[turbofan] Factor out common switch-related code in instruction selectors.

R=bmeurer@chromium.org
BUG=

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

Cr-Commit-Position: refs/heads/master@{#27468}
This commit is contained in:
titzer 2015-03-26 02:08:45 -07:00 committed by Commit bot
parent 2f3a42f9a1
commit a6940f7aa3
10 changed files with 159 additions and 319 deletions

View File

@ -1271,62 +1271,31 @@ 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) {
void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
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;
// Emit either ArchTableSwitch or ArchLookupSwitch.
size_t table_space_cost = 4 + sw.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 &&
size_t lookup_space_cost = 3 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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()) {
sw.min_value > std::numeric_limits<int32_t>::min()) {
InstructionOperand index_operand = value_operand;
if (min_value) {
if (sw.min_value) {
index_operand = g.TempRegister();
Emit(kArmSub | AddressingModeField::encode(kMode_Operand2_I),
index_operand, value_operand, g.TempImmediate(min_value));
index_operand, value_operand, g.TempImmediate(sw.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);
return;
// Generate a table lookup.
return EmitTableSwitch(sw, index_operand);
}
// 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);
return EmitLookupSwitch(sw, value_operand);
}

View File

@ -1443,62 +1443,31 @@ 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) {
void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
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;
// Emit either ArchTableSwitch or ArchLookupSwitch.
size_t table_space_cost = 4 + sw.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 &&
size_t lookup_space_cost = 3 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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()) {
sw.min_value > std::numeric_limits<int32_t>::min()) {
InstructionOperand index_operand = value_operand;
if (min_value) {
if (sw.min_value) {
index_operand = g.TempRegister();
Emit(kArm64Sub32, index_operand, value_operand,
g.TempImmediate(min_value));
g.TempImmediate(sw.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);
return;
// Generate a table lookup.
return EmitTableSwitch(sw, index_operand);
}
// 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);
return EmitLookupSwitch(sw, value_operand);
}

View File

@ -991,62 +991,31 @@ 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) {
void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
IA32OperandGenerator 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;
// Emit either ArchTableSwitch or ArchLookupSwitch.
size_t table_space_cost = 4 + sw.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 > 4 &&
size_t lookup_space_cost = 3 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.case_count > 4 &&
table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost &&
min_value > std::numeric_limits<int32_t>::min()) {
sw.min_value > std::numeric_limits<int32_t>::min()) {
InstructionOperand index_operand = value_operand;
if (min_value) {
if (sw.min_value) {
index_operand = g.TempRegister();
Emit(kIA32Lea | AddressingModeField::encode(kMode_MRI), index_operand,
value_operand, g.TempImmediate(-min_value));
value_operand, g.TempImmediate(-sw.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);
return;
// Generate a table lookup.
return EmitTableSwitch(sw, index_operand);
}
// 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);
return EmitLookupSwitch(sw, value_operand);
}

View File

@ -15,6 +15,17 @@ namespace v8 {
namespace internal {
namespace compiler {
// Helper struct containing data about a table or lookup switch.
struct SwitchInfo {
int32_t min_value; // minimum value of {case_values}
int32_t max_value; // maximum value of {case_values}
size_t value_range; // |max_value - min_value| + 1
size_t case_count; // number of cases
int32_t* case_values; // actual case values, unsorted
BasicBlock** case_branches; // basic blocks corresponding to case values
BasicBlock* default_branch; // default branch target
};
// A helper class for the instruction selector that simplifies construction of
// Operands. This class implements a base for architecture-specific helpers.
class OperandGenerator {

View File

@ -522,27 +522,32 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
}
case BasicBlock::kSwitch: {
DCHECK_EQ(IrOpcode::kSwitch, input->opcode());
SwitchInfo sw;
// Last successor must be Default.
BasicBlock* default_branch = block->successors().back();
DCHECK_EQ(IrOpcode::kIfDefault, default_branch->front()->opcode());
sw.default_branch = block->successors().back();
DCHECK_EQ(IrOpcode::kIfDefault, sw.default_branch->front()->opcode());
// All other successors must be cases.
size_t case_count = block->SuccessorCount() - 1;
DCHECK_LE(1u, case_count);
BasicBlock** case_branches = &block->successors().front();
sw.case_count = block->SuccessorCount() - 1;
DCHECK_LE(1u, sw.case_count);
sw.case_branches = &block->successors().front();
// Determine case values and their min/max.
int32_t* case_values = zone()->NewArray<int32_t>(case_count);
int32_t min_value = std::numeric_limits<int32_t>::max();
int32_t max_value = std::numeric_limits<int32_t>::min();
for (size_t index = 0; index < case_count; ++index) {
BasicBlock* branch = case_branches[index];
sw.case_values = zone()->NewArray<int32_t>(sw.case_count);
sw.min_value = std::numeric_limits<int32_t>::max();
sw.max_value = std::numeric_limits<int32_t>::min();
for (size_t index = 0; index < sw.case_count; ++index) {
BasicBlock* branch = sw.case_branches[index];
int32_t value = OpParameter<int32_t>(branch->front()->op());
case_values[index] = value;
if (min_value > value) min_value = value;
if (max_value < value) max_value = value;
sw.case_values[index] = value;
if (sw.min_value > value) sw.min_value = value;
if (sw.max_value < value) sw.max_value = value;
}
DCHECK_LE(min_value, max_value);
return VisitSwitch(input, default_branch, case_branches, case_values,
case_count, min_value, max_value);
DCHECK_LE(sw.min_value, sw.max_value);
// 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.
sw.value_range = 1u + bit_cast<uint32_t>(sw.max_value) -
bit_cast<uint32_t>(sw.min_value);
return VisitSwitch(input, sw);
}
case BasicBlock::kReturn: {
// If the result itself is a return, return its input.
@ -813,6 +818,43 @@ void InstructionSelector::VisitLoadStackPointer(Node* node) {
Emit(kArchStackPointer, g.DefineAsRegister(node));
}
void InstructionSelector::EmitTableSwitch(const SwitchInfo& sw,
InstructionOperand& index_operand) {
OperandGenerator g(this);
size_t input_count = 2 + sw.value_range;
auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
inputs[0] = index_operand;
InstructionOperand default_operand = g.Label(sw.default_branch);
std::fill(&inputs[1], &inputs[input_count], default_operand);
for (size_t index = 0; index < sw.case_count; ++index) {
size_t value = sw.case_values[index] - sw.min_value;
BasicBlock* branch = sw.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);
}
void InstructionSelector::EmitLookupSwitch(const SwitchInfo& sw,
InstructionOperand& value_operand) {
OperandGenerator g(this);
size_t input_count = 2 + sw.case_count * 2;
auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
inputs[0] = value_operand;
inputs[1] = g.Label(sw.default_branch);
for (size_t index = 0; index < sw.case_count; ++index) {
int32_t value = sw.case_values[index];
BasicBlock* branch = sw.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);
}
#endif // V8_TURBOFAN_BACKEND
// 32 bit targets do not implement the following instructions.

View File

@ -22,6 +22,7 @@ class BasicBlock;
struct CallBuffer; // TODO(bmeurer): Remove this.
class FlagsContinuation;
class Linkage;
struct SwitchInfo;
typedef ZoneVector<InstructionOperand> InstructionOperandVector;
@ -134,6 +135,10 @@ class InstructionSelector FINAL {
private:
friend class OperandGenerator;
void EmitTableSwitch(const SwitchInfo& sw, InstructionOperand& index_operand);
void EmitLookupSwitch(const SwitchInfo& sw,
InstructionOperand& value_operand);
// Inform the instruction selection that {node} was just defined.
void MarkAsDefined(Node* node);
@ -201,9 +206,7 @@ class InstructionSelector FINAL {
void VisitCall(Node* call, BasicBlock* handler);
void VisitGoto(BasicBlock* target);
void VisitBranch(Node* input, BasicBlock* tbranch, BasicBlock* fbranch);
void 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);
void VisitSwitch(Node* node, const SwitchInfo& sw);
void VisitDeoptimize(Node* value);
void VisitReturn(Node* value);
void VisitThrow(Node* value);

View File

@ -771,61 +771,31 @@ 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) {
void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
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 = 9 + value_range;
// Emit either ArchTableSwitch or ArchLookupSwitch.
size_t table_space_cost = 9 + sw.value_range;
size_t table_time_cost = 3;
size_t lookup_space_cost = 2 + 2 * case_count;
size_t lookup_time_cost = case_count;
if (case_count > 0 &&
size_t lookup_space_cost = 2 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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()) {
sw.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));
Emit(kMipsSub, index_operand, value_operand,
g.TempImmediate(sw.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);
return;
// Generate a table lookup.
return EmitTableSwitch(sw, index_operand);
}
// 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);
return EmitLookupSwitch(sw, value_operand);
}

View File

@ -955,62 +955,31 @@ 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) {
void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
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 = 10 + 2 * value_range;
// Emit either ArchTableSwitch or ArchLookupSwitch.
size_t table_space_cost = 10 + 2 * sw.value_range;
size_t table_time_cost = 3;
size_t lookup_space_cost = 2 + 2 * case_count;
size_t lookup_time_cost = case_count;
if (case_count > 0 &&
size_t lookup_space_cost = 2 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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()) {
sw.min_value > std::numeric_limits<int32_t>::min()) {
InstructionOperand index_operand = value_operand;
if (min_value) {
if (sw.min_value) {
index_operand = g.TempRegister();
Emit(kMips64Sub, index_operand, value_operand,
g.TempImmediate(min_value));
g.TempImmediate(sw.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);
return;
// Generate a table lookup.
return EmitTableSwitch(sw, index_operand);
}
// 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);
return EmitLookupSwitch(sw, value_operand);
}

View File

@ -1249,62 +1249,31 @@ 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) {
void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
PPCOperandGenerator 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;
// Emit either ArchTableSwitch or ArchLookupSwitch.
size_t table_space_cost = 4 + sw.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 &&
size_t lookup_space_cost = 3 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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()) {
sw.min_value > std::numeric_limits<int32_t>::min()) {
InstructionOperand index_operand = value_operand;
if (min_value) {
if (sw.min_value) {
index_operand = g.TempRegister();
Emit(kPPC_Sub32, index_operand, value_operand,
g.TempImmediate(min_value));
g.TempImmediate(sw.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);
return;
// Generate a table lookup.
return EmitTableSwitch(sw, index_operand);
}
// 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);
return EmitLookupSwitch(sw, value_operand);
}

View File

@ -1220,65 +1220,34 @@ 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) {
void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
X64OperandGenerator 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;
// Emit either ArchTableSwitch or ArchLookupSwitch.
size_t table_space_cost = 4 + sw.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 > 4 &&
size_t lookup_space_cost = 3 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.case_count > 4 &&
table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost &&
min_value > std::numeric_limits<int32_t>::min()) {
sw.min_value > std::numeric_limits<int32_t>::min()) {
InstructionOperand index_operand = g.TempRegister();
if (min_value) {
if (sw.min_value) {
// The leal automatically zero extends, so result is a valid 64-bit index.
Emit(kX64Lea32 | AddressingModeField::encode(kMode_MRI), index_operand,
value_operand, g.TempImmediate(-min_value));
value_operand, g.TempImmediate(-sw.min_value));
} else {
// Zero extend, because we use it as 64-bit index into the jump table.
Emit(kX64Movl, index_operand, value_operand);
}
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);
return;
// Generate a table lookup.
return EmitTableSwitch(sw, index_operand);
}
// 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);
return EmitLookupSwitch(sw, value_operand);
}