Commit 97a4a21a authored by balazs.kilvady's avatar balazs.kilvady Committed by Commit bot

MIPS: [turbofan] Optimize certain chains of Branch into a Switch.

Port acd9c46c

Original commit message:
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/931263002

Cr-Commit-Position: refs/heads/master@{#26707}
parent e22c41eb
......@@ -428,8 +428,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kArchJmp:
AssembleArchJump(i.InputRpo(0));
break;
case kArchSwitch:
AssembleArchSwitch(instr);
case kArchLookupSwitch:
AssembleArchLookupSwitch(instr);
break;
case kArchTableSwitch:
AssembleArchTableSwitch(instr);
break;
case kArchNop:
// don't emit code for nops.
......@@ -805,28 +808,6 @@ void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
}
void CodeGenerator::AssembleArchSwitch(Instruction* instr) {
MipsOperandConverter i(this, instr);
int const kNumLabels = static_cast<int>(instr->InputCount() - 1);
v8::internal::Assembler::BlockTrampolinePoolScope block_trampoline_pool(
masm());
Label here;
__ bal(&here);
__ nop(); // Branch delay slot nop.
__ bind(&here);
__ sll(at, i.InputRegister(0), 2);
__ addu(at, at, ra);
__ lw(at, MemOperand(at, 5 * v8::internal::Assembler::kInstrSize));
__ jr(at);
__ nop(); // Branch delay slot 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) {
......@@ -904,6 +885,37 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
}
void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
MipsOperandConverter i(this, instr);
Register input = i.InputRegister(0);
for (size_t index = 2; index < instr->InputCount(); index += 2) {
__ Branch(GetLabel(i.InputRpo(index + 1)), eq, input,
Operand(i.InputInt32(index + 0)));
}
AssembleArchJump(i.InputRpo(1));
}
void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
MipsOperandConverter i(this, instr);
Register input = i.InputRegister(0);
size_t const case_count = instr->InputCount() - 2;
Label here;
__ Branch(GetLabel(i.InputRpo(1)), hs, input, Operand(case_count));
__ bal(&here);
__ nop(); // Branch delay slot nop.
__ bind(&here);
__ sll(at, input, 2);
__ addu(at, at, ra);
__ lw(at, MemOperand(at, 5 * v8::internal::Assembler::kInstrSize));
__ jr(at);
__ nop(); // Branch delay slot nop.
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);
......
......@@ -746,6 +746,66 @@ 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) {
MipsOperandGenerator 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(kMipsSub, 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);
......
......@@ -428,8 +428,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kArchJmp:
AssembleArchJump(i.InputRpo(0));
break;
case kArchSwitch:
AssembleArchSwitch(instr);
case kArchLookupSwitch:
AssembleArchLookupSwitch(instr);
break;
case kArchTableSwitch:
AssembleArchTableSwitch(instr);
break;
case kArchNop:
// don't emit code for nops.
......@@ -919,32 +922,6 @@ void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
}
void CodeGenerator::AssembleArchSwitch(Instruction* instr) {
MipsOperandConverter i(this, instr);
int const kNumLabels = static_cast<int>(instr->InputCount() - 1);
v8::internal::Assembler::BlockTrampolinePoolScope block_trampoline_pool(
masm());
Label here;
// Ensure that dd-ed labels goes to 8 byte aligned addresses.
if ((masm()->pc_offset() & 7) == 0) {
__ nop();
}
__ bal(&here);
__ nop(); // Branch delay slot nop.
__ bind(&here);
__ dsll(at, i.InputRegister(0), 3);
__ daddu(at, at, ra);
__ ld(at, MemOperand(at, 5 * v8::internal::Assembler::kInstrSize));
__ jr(at);
__ nop(); // Branch delay slot 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) {
......@@ -1063,6 +1040,42 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
}
void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
MipsOperandConverter i(this, instr);
Register input = i.InputRegister(0);
for (size_t index = 2; index < instr->InputCount(); index += 2) {
__ Branch(GetLabel(i.InputRpo(index + 1)), eq, input,
Operand(i.InputInt32(index + 0)));
}
AssembleArchJump(i.InputRpo(1));
}
void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
MipsOperandConverter i(this, instr);
Register input = i.InputRegister(0);
size_t const case_count = instr->InputCount() - 2;
Label here;
__ Branch(GetLabel(i.InputRpo(1)), hs, input, Operand(case_count));
// Ensure that dd-ed labels goes to 8 byte aligned addresses.
if ((masm()->pc_offset() & 7) == 0) {
__ nop();
}
__ bal(&here);
__ nop(); // Branch delay slot nop.
__ bind(&here);
__ dsll(at, input, 3);
__ daddu(at, at, ra);
__ ld(at, MemOperand(at, 5 * v8::internal::Assembler::kInstrSize));
__ jr(at);
__ nop(); // Branch delay slot nop.
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);
......
......@@ -967,6 +967,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) {
Mips64OperandGenerator 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(kMips64Sub, 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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment