Skip to content

Commit

Permalink
[turbofan] Optimize certain chains of Branch into a Switch.
Browse files Browse the repository at this point in the history
This adds a new ControlFlowOptimizer that - for now - recognizes chains
of Branches generated by the SwitchBuilder for a subset of javascript
switches into Switch nodes. Those Switch nodes are then lowered to
either table or lookup switches.

Also rename Case to IfValue (and introduce IfDefault) for consistency.

BUG=v8:3872
LOG=n

Review URL: https://codereview.chromium.org/931623002

Cr-Commit-Position: refs/heads/master@{#26691}
  • Loading branch information
bmeurer authored and Commit bot committed Feb 17, 2015
1 parent 66ca91b commit acd9c46
Show file tree
Hide file tree
Showing 39 changed files with 1,122 additions and 168 deletions.
2 changes: 2 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,8 @@ source_set("v8_base") {
"src/compiler/control-builders.cc",
"src/compiler/control-builders.h",
"src/compiler/control-equivalence.h",
"src/compiler/control-flow-optimizer.cc",
"src/compiler/control-flow-optimizer.h",
"src/compiler/control-reducer.cc",
"src/compiler/control-reducer.h",
"src/compiler/diamond.h",
Expand Down
45 changes: 31 additions & 14 deletions src/compiler/arm/code-generator-arm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
AssembleArchJump(i.InputRpo(0));
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
case kArchSwitch:
AssembleArchSwitch(instr);
case kArchLookupSwitch:
AssembleArchLookupSwitch(instr);
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
case kArchTableSwitch:
AssembleArchTableSwitch(instr);
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
case kArchNop:
Expand Down Expand Up @@ -741,18 +745,6 @@ void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
}


void CodeGenerator::AssembleArchSwitch(Instruction* instr) {
ArmOperandConverter i(this, instr);
int const kNumLabels = static_cast<int>(instr->InputCount() - 1);
__ BlockConstPoolFor(kNumLabels + 2);
__ ldr(pc, MemOperand(pc, i.InputRegister(0), LSL, 2));
__ nop();
for (int index = 0; index < kNumLabels; ++index) {
__ dd(GetLabel(i.InputRpo(index + 1)));
}
}


// Assembles boolean materializations after an instruction.
void CodeGenerator::AssembleArchBoolean(Instruction* instr,
FlagsCondition condition) {
Expand All @@ -768,6 +760,31 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
}


void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
ArmOperandConverter i(this, instr);
Register input = i.InputRegister(0);
for (size_t index = 2; index < instr->InputCount(); index += 2) {
__ cmp(input, Operand(i.InputInt32(index + 0)));
__ b(eq, GetLabel(i.InputRpo(index + 1)));
}
AssembleArchJump(i.InputRpo(1));
}


void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
ArmOperandConverter i(this, instr);
Register input = i.InputRegister(0);
size_t const case_count = instr->InputCount() - 2;
__ cmp(input, Operand(case_count));
__ BlockConstPoolFor(case_count + 2);
__ ldr(pc, MemOperand(pc, input, LSL, 2), lo);
__ b(GetLabel(i.InputRpo(1)));
for (size_t index = 0; index < case_count; ++index) {
__ dd(GetLabel(i.InputRpo(index + 2)));
}
}


void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
isolate(), deoptimization_id, Deoptimizer::LAZY);
Expand Down
61 changes: 61 additions & 0 deletions src/compiler/arm/instruction-selector-arm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,67 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
}


void InstructionSelector::VisitSwitch(Node* node, BasicBlock* default_branch,
BasicBlock** case_branches,
int32_t* case_values, size_t case_count,
int32_t min_value, int32_t max_value) {
ArmOperandGenerator g(this);
InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
InstructionOperand default_operand = g.Label(default_branch);

// Note that {value_range} can be 0 if {min_value} is -2^31 and {max_value}
// is 2^31-1, so don't assume that it's non-zero below.
size_t value_range =
1u + bit_cast<uint32_t>(max_value) - bit_cast<uint32_t>(min_value);

// Determine whether to issue an ArchTableSwitch or an ArchLookupSwitch
// instruction.
size_t table_space_cost = 4 + value_range;
size_t table_time_cost = 3;
size_t lookup_space_cost = 3 + 2 * case_count;
size_t lookup_time_cost = case_count;
if (case_count > 0 &&
table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost &&
min_value > std::numeric_limits<int32_t>::min()) {
InstructionOperand index_operand = value_operand;
if (min_value) {
index_operand = g.TempRegister();
Emit(kArmSub | AddressingModeField::encode(kMode_Operand2_I),
index_operand, value_operand, g.TempImmediate(min_value));
}
size_t input_count = 2 + value_range;
auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
inputs[0] = index_operand;
std::fill(&inputs[1], &inputs[input_count], default_operand);
for (size_t index = 0; index < case_count; ++index) {
size_t value = case_values[index] - min_value;
BasicBlock* branch = case_branches[index];
DCHECK_LE(0u, value);
DCHECK_LT(value + 2, input_count);
inputs[value + 2] = g.Label(branch);
}
Emit(kArchTableSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
->MarkAsControl();
return;
}

// Generate a sequence of conditional jumps.
size_t input_count = 2 + case_count * 2;
auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
inputs[0] = value_operand;
inputs[1] = default_operand;
for (size_t index = 0; index < case_count; ++index) {
int32_t value = case_values[index];
BasicBlock* branch = case_branches[index];
inputs[index * 2 + 2 + 0] = g.TempImmediate(value);
inputs[index * 2 + 2 + 1] = g.Label(branch);
}
Emit(kArchLookupSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
->MarkAsControl();
}


void InstructionSelector::VisitWord32Equal(Node* const node) {
FlagsContinuation cont(kEqual, node);
Int32BinopMatcher m(node);
Expand Down
55 changes: 37 additions & 18 deletions src/compiler/arm64/code-generator-arm64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kArchJmp:
AssembleArchJump(i.InputRpo(0));
break;
case kArchSwitch:
AssembleArchSwitch(instr);
case kArchTableSwitch:
AssembleArchTableSwitch(instr);
break;
case kArchLookupSwitch:
AssembleArchLookupSwitch(instr);
break;
case kArchNop:
// don't emit code for nops.
Expand Down Expand Up @@ -841,22 +844,6 @@ void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
}


void CodeGenerator::AssembleArchSwitch(Instruction* instr) {
Arm64OperandConverter i(this, instr);
UseScratchRegisterScope scope(masm());
Register reg = i.InputRegister(0);
Register tmp = scope.AcquireX();
Label table;
__ Adr(tmp, &table);
__ Add(tmp, tmp, Operand(reg, LSL, 2));
__ Br(tmp);
__ Bind(&table);
for (size_t index = 1; index < instr->InputCount(); ++index) {
__ B(GetLabel(i.InputRpo(index)));
}
}


// Assemble boolean materializations after this instruction.
void CodeGenerator::AssembleArchBoolean(Instruction* instr,
FlagsCondition condition) {
Expand All @@ -871,6 +858,38 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
}


void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
Arm64OperandConverter i(this, instr);
Register input = i.InputRegister32(0);
for (size_t index = 2; index < instr->InputCount(); index += 2) {
__ Cmp(input, i.InputInt32(index + 0));
__ B(eq, GetLabel(i.InputRpo(index + 1)));
}
AssembleArchJump(i.InputRpo(1));
}


void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
Arm64OperandConverter i(this, instr);
UseScratchRegisterScope scope(masm());
Register input = i.InputRegister32(0);
Register temp = scope.AcquireX();
size_t const case_count = instr->InputCount() - 2;
Label table;
__ Cmp(input, case_count);
__ B(hs, GetLabel(i.InputRpo(1)));
__ Adr(temp, &table);
__ Add(temp, temp, Operand(input, UXTW, 2));
__ Br(temp);
__ StartBlockPools();
__ Bind(&table);
for (size_t index = 0; index < case_count; ++index) {
__ B(GetLabel(i.InputRpo(index + 2)));
}
__ EndBlockPools();
}


void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
isolate(), deoptimization_id, Deoptimizer::LAZY);
Expand Down
61 changes: 61 additions & 0 deletions src/compiler/arm64/instruction-selector-arm64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,67 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
}


void InstructionSelector::VisitSwitch(Node* node, BasicBlock* default_branch,
BasicBlock** case_branches,
int32_t* case_values, size_t case_count,
int32_t min_value, int32_t max_value) {
Arm64OperandGenerator g(this);
InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
InstructionOperand default_operand = g.Label(default_branch);

// Note that {value_range} can be 0 if {min_value} is -2^31 and {max_value}
// is 2^31-1, so don't assume that it's non-zero below.
size_t value_range =
1u + bit_cast<uint32_t>(max_value) - bit_cast<uint32_t>(min_value);

// Determine whether to issue an ArchTableSwitch or an ArchLookupSwitch
// instruction.
size_t table_space_cost = 4 + value_range;
size_t table_time_cost = 3;
size_t lookup_space_cost = 3 + 2 * case_count;
size_t lookup_time_cost = case_count;
if (case_count > 0 &&
table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost &&
min_value > std::numeric_limits<int32_t>::min()) {
InstructionOperand index_operand = value_operand;
if (min_value) {
index_operand = g.TempRegister();
Emit(kArm64Sub32, index_operand, value_operand,
g.TempImmediate(min_value));
}
size_t input_count = 2 + value_range;
auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
inputs[0] = index_operand;
std::fill(&inputs[1], &inputs[input_count], default_operand);
for (size_t index = 0; index < case_count; ++index) {
size_t value = case_values[index] - min_value;
BasicBlock* branch = case_branches[index];
DCHECK_LE(0u, value);
DCHECK_LT(value + 2, input_count);
inputs[value + 2] = g.Label(branch);
}
Emit(kArchTableSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
->MarkAsControl();
return;
}

// Generate a sequence of conditional jumps.
size_t input_count = 2 + case_count * 2;
auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
inputs[0] = value_operand;
inputs[1] = default_operand;
for (size_t index = 0; index < case_count; ++index) {
int32_t value = case_values[index];
BasicBlock* branch = case_branches[index];
inputs[index * 2 + 2 + 0] = g.TempImmediate(value);
inputs[index * 2 + 2 + 1] = g.Label(branch);
}
Emit(kArchLookupSwitch, 0, nullptr, input_count, inputs, 0, nullptr)
->MarkAsControl();
}


void InstructionSelector::VisitWord32Equal(Node* const node) {
Node* const user = node;
FlagsContinuation cont(kEqual, node);
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/code-generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ class CodeGenerator FINAL : public GapResolver::Assembler {

void AssembleArchInstruction(Instruction* instr);
void AssembleArchJump(BasicBlock::RpoNumber target);
void AssembleArchSwitch(Instruction* instr);
void AssembleArchBranch(Instruction* instr, BranchInfo* branch);
void AssembleArchBoolean(Instruction* instr, FlagsCondition condition);
void AssembleArchLookupSwitch(Instruction* instr);
void AssembleArchTableSwitch(Instruction* instr);

void AssembleDeoptimizerCall(int deoptimization_id);

Expand Down
21 changes: 8 additions & 13 deletions src/compiler/common-operator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@ BranchHint BranchHintOf(const Operator* const op) {
}


size_t CaseIndexOf(const Operator* const op) {
DCHECK_EQ(IrOpcode::kCase, op->opcode());
return OpParameter<size_t>(op);
}


bool operator==(SelectParameters const& lhs, SelectParameters const& rhs) {
return lhs.type() == rhs.type() && lhs.hint() == rhs.hint();
}
Expand Down Expand Up @@ -121,6 +115,7 @@ size_t ProjectionIndexOf(const Operator* const op) {
V(End, Operator::kKontrol, 0, 0, 1, 0, 0, 0) \
V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfDefault, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(Throw, Operator::kFoldable, 1, 1, 1, 0, 0, 1) \
V(Return, Operator::kNoThrow, 1, 1, 1, 0, 0, 1) \
V(OsrNormalEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
Expand Down Expand Up @@ -256,20 +251,20 @@ const Operator* CommonOperatorBuilder::Branch(BranchHint hint) {


const Operator* CommonOperatorBuilder::Switch(size_t control_output_count) {
DCHECK_GE(control_output_count, 2u); // Disallow trivial switches.
DCHECK_GE(control_output_count, 3u); // Disallow trivial switches.
return new (zone()) Operator( // --
IrOpcode::kSwitch, Operator::kKontrol, // opcode
"Switch", // name
1, 0, 1, 0, 0, control_output_count); // counts
}


const Operator* CommonOperatorBuilder::Case(size_t index) {
return new (zone()) Operator1<size_t>( // --
IrOpcode::kCase, Operator::kKontrol, // opcode
"Case", // name
0, 0, 1, 0, 0, 1, // counts
index); // parameter
const Operator* CommonOperatorBuilder::IfValue(int32_t index) {
return new (zone()) Operator1<int32_t>( // --
IrOpcode::kIfValue, Operator::kKontrol, // opcode
"IfValue", // name
0, 0, 1, 0, 0, 1, // counts
index); // parameter
}


Expand Down
6 changes: 2 additions & 4 deletions src/compiler/common-operator.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ std::ostream& operator<<(std::ostream&, BranchHint);
BranchHint BranchHintOf(const Operator* const);


size_t CaseIndexOf(const Operator* const);


class SelectParameters FINAL {
public:
explicit SelectParameters(MachineType type,
Expand Down Expand Up @@ -176,7 +173,8 @@ class CommonOperatorBuilder FINAL : public ZoneObject {
const Operator* IfTrue();
const Operator* IfFalse();
const Operator* Switch(size_t control_output_count);
const Operator* Case(size_t index);
const Operator* IfValue(int32_t value);
const Operator* IfDefault();
const Operator* Throw();
const Operator* Return();

Expand Down
Loading

0 comments on commit acd9c46

Please sign in to comment.