79bcb45447
This is a reland of a72b2f88a8
Original change's description:
> [arm] Restrict grouping pushes before a TailCall to registers only
>
> We optimize parallel moves performed before a TailCall by grouping adjacent
> pushes. This way, we may use a single instruction to push multiple registers at
> once. However, we also have support for pushing immediates and stack slots for
> which the benefit is questionnable therefore this patch removes support for
> them.
>
> Concerning immediate pushes, it looks like a mistake since we do not have
> support for this case in `AssembleMove` so this patch removes it. Furthermore,
> if we add a test for this case, we see that a `push ip` instruction is
> generated, effectively pushing whatever was in `ip` at the time instead of
> pushing a constant.
>
> Concerning stack slot pushes, we generate a more or less equivalent sequence of
> instructions.
>
> Finally, grouping floating point pushes is not used anywhere so this patch
> removes support for this also.
>
> Bug: v8:6553
> Change-Id: I9b820d33361fc442dd813f66e1f96cda41009110
> Reviewed-on: https://chromium-review.googlesource.com/567191
> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
> Commit-Queue: Pierre Langlois <pierre.langlois@arm.com>
> Cr-Commit-Position: refs/heads/master@{#46718}
Bug: v8:6553
Change-Id: Ib9a55dae7cc5db6185d163c56088ff23426d04bb
Reviewed-on: https://chromium-review.googlesource.com/576087
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Pierre Langlois <pierre.langlois@arm.com>
Cr-Commit-Position: refs/heads/master@{#46754}
200 lines
7.8 KiB
C++
200 lines
7.8 KiB
C++
// Copyright 2017 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "src/codegen.h"
|
|
#include "src/compilation-info.h"
|
|
#include "src/compiler/code-generator.h"
|
|
#include "src/compiler/instruction.h"
|
|
#include "src/compiler/linkage.h"
|
|
#include "src/isolate.h"
|
|
|
|
#include "test/cctest/cctest.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace compiler {
|
|
|
|
class CodeGeneratorTester : public InitializedHandleScope {
|
|
public:
|
|
CodeGeneratorTester()
|
|
: zone_(main_isolate()->allocator(), ZONE_NAME),
|
|
info_(ArrayVector("test"), main_isolate(), &zone_,
|
|
Code::ComputeFlags(Code::STUB)),
|
|
descriptor_(Linkage::GetJSCallDescriptor(&zone_, false, 0,
|
|
CallDescriptor::kNoFlags)),
|
|
linkage_(descriptor_),
|
|
blocks_(&zone_),
|
|
sequence_(main_isolate(), &zone_, &blocks_),
|
|
frame_(descriptor_->CalculateFixedFrameSize()),
|
|
generator_(&zone_, &frame_, &linkage_, &sequence_, &info_,
|
|
base::Optional<OsrHelper>(), kNoSourcePosition) {
|
|
info_.set_prologue_offset(generator_.tasm()->pc_offset());
|
|
}
|
|
|
|
enum PushTypeFlag {
|
|
kRegisterPush = CodeGenerator::kRegisterPush,
|
|
kStackSlotPush = CodeGenerator::kStackSlotPush,
|
|
kScalarPush = CodeGenerator::kScalarPush
|
|
};
|
|
|
|
void CheckAssembleTailCallGaps(Instruction* instr,
|
|
int first_unused_stack_slot,
|
|
CodeGeneratorTester::PushTypeFlag push_type) {
|
|
generator_.AssembleTailCallBeforeGap(instr, first_unused_stack_slot);
|
|
#if defined(V8_TARGET_ARCH_ARM)
|
|
// Only folding register pushes is supported on ARM.
|
|
bool supported = ((push_type & CodeGenerator::kRegisterPush) == push_type);
|
|
#elif defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_IA32) || \
|
|
defined(V8_TARGET_ARCH_X87) || defined(V8_TARGET_ARCH_S390X) || \
|
|
defined(V8_TARGET_ARCH_PPC64)
|
|
bool supported = ((push_type & CodeGenerator::kScalarPush) == push_type);
|
|
#else
|
|
bool supported = false;
|
|
#endif
|
|
if (supported) {
|
|
// Architectures supporting folding adjacent pushes should now have
|
|
// resolved all moves.
|
|
for (const auto& move :
|
|
*instr->parallel_moves()[Instruction::FIRST_GAP_POSITION]) {
|
|
CHECK(move->IsEliminated());
|
|
}
|
|
}
|
|
generator_.AssembleGaps(instr);
|
|
generator_.AssembleTailCallAfterGap(instr, first_unused_stack_slot);
|
|
}
|
|
|
|
Handle<Code> Finalize() {
|
|
generator_.FinishCode();
|
|
generator_.safepoints()->Emit(generator_.tasm(),
|
|
frame_.GetTotalFrameSlotCount());
|
|
return generator_.FinalizeCode();
|
|
}
|
|
|
|
void Disassemble() {
|
|
HandleScope scope(main_isolate());
|
|
Handle<Code> code = Finalize();
|
|
if (FLAG_print_code) {
|
|
code->Print();
|
|
}
|
|
}
|
|
|
|
Zone* zone() { return &zone_; }
|
|
|
|
private:
|
|
Zone zone_;
|
|
CompilationInfo info_;
|
|
CallDescriptor* descriptor_;
|
|
Linkage linkage_;
|
|
ZoneVector<InstructionBlock*> blocks_;
|
|
InstructionSequence sequence_;
|
|
Frame frame_;
|
|
CodeGenerator generator_;
|
|
};
|
|
|
|
TEST(AssembleTailCallGap) {
|
|
const RegisterConfiguration* conf = RegisterConfiguration::Turbofan();
|
|
|
|
// This test assumes at least 4 registers are allocatable.
|
|
CHECK(conf->num_allocatable_general_registers() >= 4);
|
|
|
|
auto r0 = AllocatedOperand(LocationOperand::REGISTER,
|
|
MachineRepresentation::kTagged,
|
|
conf->GetAllocatableGeneralCode(0));
|
|
auto r1 = AllocatedOperand(LocationOperand::REGISTER,
|
|
MachineRepresentation::kTagged,
|
|
conf->GetAllocatableGeneralCode(1));
|
|
auto r2 = AllocatedOperand(LocationOperand::REGISTER,
|
|
MachineRepresentation::kTagged,
|
|
conf->GetAllocatableGeneralCode(2));
|
|
auto r3 = AllocatedOperand(LocationOperand::REGISTER,
|
|
MachineRepresentation::kTagged,
|
|
conf->GetAllocatableGeneralCode(3));
|
|
|
|
auto slot_minus_4 = AllocatedOperand(LocationOperand::STACK_SLOT,
|
|
MachineRepresentation::kTagged, -4);
|
|
auto slot_minus_3 = AllocatedOperand(LocationOperand::STACK_SLOT,
|
|
MachineRepresentation::kTagged, -3);
|
|
auto slot_minus_2 = AllocatedOperand(LocationOperand::STACK_SLOT,
|
|
MachineRepresentation::kTagged, -2);
|
|
auto slot_minus_1 = AllocatedOperand(LocationOperand::STACK_SLOT,
|
|
MachineRepresentation::kTagged, -1);
|
|
|
|
// Avoid slot 0 for architectures which use it store the return address.
|
|
int first_slot = V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK ? 1 : 0;
|
|
auto slot_0 = AllocatedOperand(LocationOperand::STACK_SLOT,
|
|
MachineRepresentation::kTagged, first_slot);
|
|
auto slot_1 =
|
|
AllocatedOperand(LocationOperand::STACK_SLOT,
|
|
MachineRepresentation::kTagged, first_slot + 1);
|
|
auto slot_2 =
|
|
AllocatedOperand(LocationOperand::STACK_SLOT,
|
|
MachineRepresentation::kTagged, first_slot + 2);
|
|
auto slot_3 =
|
|
AllocatedOperand(LocationOperand::STACK_SLOT,
|
|
MachineRepresentation::kTagged, first_slot + 3);
|
|
|
|
// These tests all generate series of moves that the code generator should
|
|
// detect as adjacent pushes. Depending on the architecture, we make sure
|
|
// these moves get eliminated.
|
|
// Also, disassembling with `--print-code` is useful when debugging.
|
|
|
|
{
|
|
// Generate a series of register pushes only.
|
|
CodeGeneratorTester c;
|
|
Instruction* instr = Instruction::New(c.zone(), kArchNop);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(r3, slot_0);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(r2, slot_1);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(r1, slot_2);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(r0, slot_3);
|
|
|
|
c.CheckAssembleTailCallGaps(instr, first_slot + 4,
|
|
CodeGeneratorTester::kRegisterPush);
|
|
c.Disassemble();
|
|
}
|
|
|
|
{
|
|
// Generate a series of stack pushes only.
|
|
CodeGeneratorTester c;
|
|
Instruction* instr = Instruction::New(c.zone(), kArchNop);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(slot_minus_4, slot_0);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(slot_minus_3, slot_1);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(slot_minus_2, slot_2);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(slot_minus_1, slot_3);
|
|
|
|
c.CheckAssembleTailCallGaps(instr, first_slot + 4,
|
|
CodeGeneratorTester::kStackSlotPush);
|
|
c.Disassemble();
|
|
}
|
|
|
|
{
|
|
// Generate a mix of stack and register pushes.
|
|
CodeGeneratorTester c;
|
|
Instruction* instr = Instruction::New(c.zone(), kArchNop);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(slot_minus_2, slot_0);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(r1, slot_1);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(slot_minus_1, slot_2);
|
|
instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone())
|
|
->AddMove(r0, slot_3);
|
|
|
|
c.CheckAssembleTailCallGaps(instr, first_slot + 4,
|
|
CodeGeneratorTester::kScalarPush);
|
|
c.Disassemble();
|
|
}
|
|
}
|
|
|
|
} // namespace compiler
|
|
} // namespace internal
|
|
} // namespace v8
|