Reland "[turbofan] Preserve order of compares in switches"

This is a reland of b8bc26d099

Original change's description:
> [turbofan] Preserve order of compares in switches
> 
> This CL makes sure that control flow optimization does
> not change the order of switches that ultimately get
> lowered to a series of comparisons anyway.
> 
> Bug: v8:7326
> Change-Id: If004de6b71a7e9504d37754c847ca108a64e49db
> Reviewed-on: https://chromium-review.googlesource.com/941952
> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
> Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
> Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#51679}

Bug: v8:7326
Change-Id: Ifbe61dece499c98bbd49fa3ae9b99ccf4e955ddc
Reviewed-on: https://chromium-review.googlesource.com/945770
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51691}
This commit is contained in:
Sigurd Schneider 2018-03-02 11:54:30 +01:00 committed by Commit Bot
parent 767c954972
commit 2daca1c6a8
19 changed files with 228 additions and 153 deletions

View File

@ -1960,20 +1960,20 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
// Emit either ArchTableSwitch or ArchLookupSwitch. // Emit either ArchTableSwitch or ArchLookupSwitch.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) { if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
static const size_t kMaxTableSwitchValueRange = 2 << 16; static const size_t kMaxTableSwitchValueRange = 2 << 16;
size_t table_space_cost = 4 + sw.value_range; size_t table_space_cost = 4 + sw.value_range();
size_t table_time_cost = 3; size_t table_time_cost = 3;
size_t lookup_space_cost = 3 + 2 * sw.case_count; size_t lookup_space_cost = 3 + 2 * sw.case_count();
size_t lookup_time_cost = sw.case_count; size_t lookup_time_cost = sw.case_count();
if (sw.case_count > 0 && if (sw.case_count() > 0 &&
table_space_cost + 3 * table_time_cost <= table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost && lookup_space_cost + 3 * lookup_time_cost &&
sw.min_value > std::numeric_limits<int32_t>::min() && sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) { sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand; InstructionOperand index_operand = value_operand;
if (sw.min_value) { if (sw.min_value()) {
index_operand = g.TempRegister(); index_operand = g.TempRegister();
Emit(kArmSub | AddressingModeField::encode(kMode_Operand2_I), Emit(kArmSub | AddressingModeField::encode(kMode_Operand2_I),
index_operand, value_operand, g.TempImmediate(sw.min_value)); index_operand, value_operand, g.TempImmediate(sw.min_value()));
} }
// Generate a table lookup. // Generate a table lookup.
return EmitTableSwitch(sw, index_operand); return EmitTableSwitch(sw, index_operand);

View File

@ -2349,20 +2349,20 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
// Emit either ArchTableSwitch or ArchLookupSwitch. // Emit either ArchTableSwitch or ArchLookupSwitch.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) { if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
static const size_t kMaxTableSwitchValueRange = 2 << 16; static const size_t kMaxTableSwitchValueRange = 2 << 16;
size_t table_space_cost = 4 + sw.value_range; size_t table_space_cost = 4 + sw.value_range();
size_t table_time_cost = 3; size_t table_time_cost = 3;
size_t lookup_space_cost = 3 + 2 * sw.case_count; size_t lookup_space_cost = 3 + 2 * sw.case_count();
size_t lookup_time_cost = sw.case_count; size_t lookup_time_cost = sw.case_count();
if (sw.case_count > 0 && if (sw.case_count() > 0 &&
table_space_cost + 3 * table_time_cost <= table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost && lookup_space_cost + 3 * lookup_time_cost &&
sw.min_value > std::numeric_limits<int32_t>::min() && sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) { sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand; InstructionOperand index_operand = value_operand;
if (sw.min_value) { if (sw.min_value()) {
index_operand = g.TempRegister(); index_operand = g.TempRegister();
Emit(kArm64Sub32, index_operand, value_operand, Emit(kArm64Sub32, index_operand, value_operand,
g.TempImmediate(sw.min_value)); g.TempImmediate(sw.min_value()));
} }
// Generate a table lookup. // Generate a table lookup.
return EmitTableSwitch(sw, index_operand); return EmitTableSwitch(sw, index_operand);

View File

@ -438,8 +438,8 @@ Reduction CommonOperatorReducer::ReduceSwitch(Node* node) {
for (size_t i = 0; i < projection_count - 1; i++) { for (size_t i = 0; i < projection_count - 1; i++) {
Node* if_value = projections[i]; Node* if_value = projections[i];
DCHECK_EQ(IrOpcode::kIfValue, if_value->opcode()); DCHECK_EQ(IrOpcode::kIfValue, if_value->opcode());
int32_t value_index = OpParameter<int32_t>(if_value->op()); const IfValueParameters& p = IfValueParametersOf(if_value->op());
if (value_index == mswitched.Value()) { if (p.value() == mswitched.Value()) {
matched = true; matched = true;
Replace(if_value, control); Replace(if_value, control);
break; break;

View File

@ -391,6 +391,26 @@ ZoneVector<MachineType> const* MachineTypesOf(Operator const* op) {
return OpParameter<TypedObjectStateInfo>(op).machine_types(); return OpParameter<TypedObjectStateInfo>(op).machine_types();
} }
V8_EXPORT_PRIVATE bool operator==(IfValueParameters const& l,
IfValueParameters const& r) {
return l.value() == r.value() && r.comparison_order() == r.comparison_order();
}
size_t hash_value(IfValueParameters const& p) {
return base::hash_combine(p.value(), p.comparison_order());
}
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
IfValueParameters const& p) {
out << p.value() << " (order " << p.comparison_order() << ")";
return out;
}
IfValueParameters const& IfValueParametersOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kIfValue);
return OpParameter<IfValueParameters>(op);
}
#define COMMON_CACHED_OP_LIST(V) \ #define COMMON_CACHED_OP_LIST(V) \
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \ V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
V(Unreachable, Operator::kFoldable, 0, 1, 1, 1, 1, 0) \ V(Unreachable, Operator::kFoldable, 0, 1, 1, 1, 1, 0) \
@ -994,13 +1014,13 @@ const Operator* CommonOperatorBuilder::Switch(size_t control_output_count) {
1, 0, 1, 0, 0, control_output_count); // counts 1, 0, 1, 0, 0, control_output_count); // counts
} }
const Operator* CommonOperatorBuilder::IfValue(int32_t index,
const Operator* CommonOperatorBuilder::IfValue(int32_t index) { int32_t comparison_order) {
return new (zone()) Operator1<int32_t>( // -- return new (zone()) Operator1<IfValueParameters>( // --
IrOpcode::kIfValue, Operator::kKontrol, // opcode IrOpcode::kIfValue, Operator::kKontrol, // opcode
"IfValue", // name "IfValue", // name
0, 0, 1, 0, 0, 1, // counts 0, 0, 1, 0, 0, 1, // counts
index); // parameter IfValueParameters(index, comparison_order)); // parameter
} }

View File

@ -376,6 +376,30 @@ uint32_t ObjectIdOf(Operator const*);
MachineRepresentation DeadValueRepresentationOf(Operator const*); MachineRepresentation DeadValueRepresentationOf(Operator const*);
class IfValueParameters final {
public:
IfValueParameters(int32_t value, int32_t comparison_order)
: value_(value), comparison_order_(comparison_order) {}
int32_t value() const { return value_; }
int32_t comparison_order() const { return comparison_order_; }
private:
int32_t value_;
int32_t comparison_order_;
};
V8_EXPORT_PRIVATE bool operator==(IfValueParameters const&,
IfValueParameters const&);
size_t hash_value(IfValueParameters const&);
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&,
IfValueParameters const&);
V8_EXPORT_PRIVATE IfValueParameters const& IfValueParametersOf(
const Operator* op) WARN_UNUSED_RESULT;
// Interface for building common operators that can be used at any level of IR, // Interface for building common operators that can be used at any level of IR,
// including JavaScript, mid-level, and low-level. // including JavaScript, mid-level, and low-level.
class V8_EXPORT_PRIVATE CommonOperatorBuilder final class V8_EXPORT_PRIVATE CommonOperatorBuilder final
@ -395,7 +419,7 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
const Operator* IfSuccess(); const Operator* IfSuccess();
const Operator* IfException(); const Operator* IfException();
const Operator* Switch(size_t control_output_count); const Operator* Switch(size_t control_output_count);
const Operator* IfValue(int32_t value); const Operator* IfValue(int32_t value, int32_t order = 0);
const Operator* IfDefault(); const Operator* IfDefault();
const Operator* Throw(); const Operator* Throw();
const Operator* Deoptimize(DeoptimizeKind kind, DeoptimizeReason reason, const Operator* Deoptimize(DeoptimizeKind kind, DeoptimizeReason reason,

View File

@ -83,6 +83,7 @@ bool ControlFlowOptimizer::TryBuildSwitch(Node* node) {
Node* if_false; Node* if_false;
Node* if_true; Node* if_true;
int32_t order = 1;
while (true) { while (true) {
BranchMatcher matcher(branch); BranchMatcher matcher(branch);
DCHECK(matcher.Matched()); DCHECK(matcher.Matched());
@ -109,7 +110,7 @@ bool ControlFlowOptimizer::TryBuildSwitch(Node* node) {
branch->NullAllInputs(); branch->NullAllInputs();
if_true->ReplaceInput(0, node); if_true->ReplaceInput(0, node);
} }
NodeProperties::ChangeOp(if_true, common()->IfValue(value)); NodeProperties::ChangeOp(if_true, common()->IfValue(value, order++));
if_false->NullAllInputs(); if_false->NullAllInputs();
Enqueue(if_true); Enqueue(if_true);
@ -128,7 +129,7 @@ bool ControlFlowOptimizer::TryBuildSwitch(Node* node) {
node->ReplaceInput(0, index); node->ReplaceInput(0, index);
NodeProperties::ChangeOp(node, common()->Switch(values.size() + 1)); NodeProperties::ChangeOp(node, common()->Switch(values.size() + 1));
if_true->ReplaceInput(0, node); if_true->ReplaceInput(0, node);
NodeProperties::ChangeOp(if_true, common()->IfValue(value)); NodeProperties::ChangeOp(if_true, common()->IfValue(value, order++));
Enqueue(if_true); Enqueue(if_true);
if_false->ReplaceInput(0, node); if_false->ReplaceInput(0, node);
NodeProperties::ChangeOp(if_false, common()->IfDefault()); NodeProperties::ChangeOp(if_false, common()->IfDefault());

View File

@ -1406,20 +1406,20 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
// Emit either ArchTableSwitch or ArchLookupSwitch. // Emit either ArchTableSwitch or ArchLookupSwitch.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) { if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
static const size_t kMaxTableSwitchValueRange = 2 << 16; static const size_t kMaxTableSwitchValueRange = 2 << 16;
size_t table_space_cost = 4 + sw.value_range; size_t table_space_cost = 4 + sw.value_range();
size_t table_time_cost = 3; size_t table_time_cost = 3;
size_t lookup_space_cost = 3 + 2 * sw.case_count; size_t lookup_space_cost = 3 + 2 * sw.case_count();
size_t lookup_time_cost = sw.case_count; size_t lookup_time_cost = sw.case_count();
if (sw.case_count > 4 && if (sw.case_count() > 4 &&
table_space_cost + 3 * table_time_cost <= table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost && lookup_space_cost + 3 * lookup_time_cost &&
sw.min_value > std::numeric_limits<int32_t>::min() && sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) { sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand; InstructionOperand index_operand = value_operand;
if (sw.min_value) { if (sw.min_value()) {
index_operand = g.TempRegister(); index_operand = g.TempRegister();
Emit(kIA32Lea | AddressingModeField::encode(kMode_MRI), index_operand, Emit(kIA32Lea | AddressingModeField::encode(kMode_MRI), index_operand,
value_operand, g.TempImmediate(-sw.min_value)); value_operand, g.TempImmediate(-sw.min_value()));
} }
// Generate a table lookup. // Generate a table lookup.
return EmitTableSwitch(sw, index_operand); return EmitTableSwitch(sw, index_operand);

View File

@ -15,15 +15,52 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
struct CaseInfo {
int32_t value; // The case value.
int32_t order; // The order for lowering to comparisons (less means earlier).
BasicBlock* branch; // The basic blocks corresponding to the case value.
};
inline bool operator<(const CaseInfo& l, const CaseInfo& r) {
return l.order < r.order;
}
// Helper struct containing data about a table or lookup switch. // Helper struct containing data about a table or lookup switch.
struct SwitchInfo { class SwitchInfo {
int32_t min_value; // minimum value of {case_values} public:
int32_t max_value; // maximum value of {case_values} SwitchInfo(ZoneVector<CaseInfo>& cases, int32_t min_value, int32_t max_value,
size_t value_range; // |max_value - min_value| + 1 BasicBlock* default_branch)
size_t case_count; // number of cases : cases_(cases),
int32_t* case_values; // actual case values, unsorted min_value_(min_value),
BasicBlock** case_branches; // basic blocks corresponding to case values max_value_(min_value),
BasicBlock* default_branch; // default branch target default_branch_(default_branch) {
if (cases.size() != 0) {
DCHECK_LE(min_value, 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.
value_range_ =
1u + bit_cast<uint32_t>(max_value) - bit_cast<uint32_t>(min_value);
} else {
value_range_ = 0;
}
}
int32_t min_value() const { return min_value_; }
int32_t max_value() const { return max_value_; }
size_t value_range() const { return value_range_; }
size_t case_count() const { return cases_.size(); }
const CaseInfo& GetCase(size_t i) const {
DCHECK_LT(i, cases_.size());
return cases_[i];
}
BasicBlock* default_branch() const { return default_branch_; }
private:
const ZoneVector<CaseInfo>& cases_;
int32_t min_value_; // minimum value of {cases_}
int32_t max_value_; // maximum value of {cases_}
size_t value_range_; // |max_value - min_value| + 1
BasicBlock* default_branch_;
}; };
// A helper class for the instruction selector that simplifies construction of // A helper class for the instruction selector that simplifies construction of

View File

@ -1023,33 +1023,24 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
} }
case BasicBlock::kSwitch: { case BasicBlock::kSwitch: {
DCHECK_EQ(IrOpcode::kSwitch, input->opcode()); DCHECK_EQ(IrOpcode::kSwitch, input->opcode());
SwitchInfo sw; // Last successor must be {IfDefault}.
// Last successor must be Default. BasicBlock* default_branch = block->successors().back();
sw.default_branch = block->successors().back(); DCHECK_EQ(IrOpcode::kIfDefault, default_branch->front()->opcode());
DCHECK_EQ(IrOpcode::kIfDefault, sw.default_branch->front()->opcode()); // All other successors must be {IfValue}s.
// All other successors must be cases. int32_t min_value = std::numeric_limits<int32_t>::max();
sw.case_count = block->SuccessorCount() - 1; int32_t max_value = std::numeric_limits<int32_t>::min();
sw.case_branches = &block->successors().front(); size_t case_count = block->SuccessorCount() - 1;
// Determine case values and their min/max. ZoneVector<CaseInfo> cases(case_count, zone());
sw.case_values = zone()->NewArray<int32_t>(sw.case_count); for (size_t i = 0; i < case_count; ++i) {
sw.min_value = std::numeric_limits<int32_t>::max(); BasicBlock* branch = block->SuccessorAt(i);
sw.max_value = std::numeric_limits<int32_t>::min(); const IfValueParameters& p = IfValueParametersOf(branch->front()->op());
for (size_t index = 0; index < sw.case_count; ++index) { cases[i] = CaseInfo{p.value(), p.comparison_order(), branch};
BasicBlock* branch = sw.case_branches[index]; if (min_value > p.value()) min_value = p.value();
int32_t value = OpParameter<int32_t>(branch->front()->op()); if (max_value < p.value()) max_value = p.value();
sw.case_values[index] = value;
if (sw.min_value > value) sw.min_value = value;
if (sw.max_value < value) sw.max_value = value;
}
if (sw.case_count != 0) {
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);
} else {
sw.value_range = 0;
} }
// Ensure that comparison order of if-cascades is preserved.
std::stable_sort(cases.begin(), cases.end());
SwitchInfo sw(cases, min_value, max_value, default_branch);
return VisitSwitch(input, sw); return VisitSwitch(input, sw);
} }
case BasicBlock::kReturn: { case BasicBlock::kReturn: {
@ -1934,18 +1925,18 @@ void InstructionSelector::VisitFloat64Tanh(Node* node) {
void InstructionSelector::EmitTableSwitch(const SwitchInfo& sw, void InstructionSelector::EmitTableSwitch(const SwitchInfo& sw,
InstructionOperand& index_operand) { InstructionOperand& index_operand) {
OperandGenerator g(this); OperandGenerator g(this);
size_t input_count = 2 + sw.value_range; size_t input_count = 2 + sw.value_range();
DCHECK_LE(sw.value_range, std::numeric_limits<size_t>::max() - 2); DCHECK_LE(sw.value_range(), std::numeric_limits<size_t>::max() - 2);
auto* inputs = zone()->NewArray<InstructionOperand>(input_count); auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
inputs[0] = index_operand; inputs[0] = index_operand;
InstructionOperand default_operand = g.Label(sw.default_branch); InstructionOperand default_operand = g.Label(sw.default_branch());
std::fill(&inputs[1], &inputs[input_count], default_operand); std::fill(&inputs[1], &inputs[input_count], default_operand);
for (size_t index = 0; index < sw.case_count; ++index) { for (size_t index = 0; index < sw.case_count(); ++index) {
size_t value = sw.case_values[index] - sw.min_value; const CaseInfo& c = sw.GetCase(index);
BasicBlock* branch = sw.case_branches[index]; size_t value = c.value - sw.min_value();
DCHECK_LE(0u, value); DCHECK_LE(0u, value);
DCHECK_LT(value + 2, input_count); DCHECK_LT(value + 2, input_count);
inputs[value + 2] = g.Label(branch); inputs[value + 2] = g.Label(c.branch);
} }
Emit(kArchTableSwitch, 0, nullptr, input_count, inputs, 0, nullptr); Emit(kArchTableSwitch, 0, nullptr, input_count, inputs, 0, nullptr);
} }
@ -1954,16 +1945,15 @@ void InstructionSelector::EmitTableSwitch(const SwitchInfo& sw,
void InstructionSelector::EmitLookupSwitch(const SwitchInfo& sw, void InstructionSelector::EmitLookupSwitch(const SwitchInfo& sw,
InstructionOperand& value_operand) { InstructionOperand& value_operand) {
OperandGenerator g(this); OperandGenerator g(this);
size_t input_count = 2 + sw.case_count * 2; size_t input_count = 2 + sw.case_count() * 2;
DCHECK_LE(sw.case_count, (std::numeric_limits<size_t>::max() - 2) / 2); DCHECK_LE(sw.case_count(), (std::numeric_limits<size_t>::max() - 2) / 2);
auto* inputs = zone()->NewArray<InstructionOperand>(input_count); auto* inputs = zone()->NewArray<InstructionOperand>(input_count);
inputs[0] = value_operand; inputs[0] = value_operand;
inputs[1] = g.Label(sw.default_branch); inputs[1] = g.Label(sw.default_branch());
for (size_t index = 0; index < sw.case_count; ++index) { for (size_t index = 0; index < sw.case_count(); ++index) {
int32_t value = sw.case_values[index]; const CaseInfo& c = sw.GetCase(index);
BasicBlock* branch = sw.case_branches[index]; inputs[index * 2 + 2 + 0] = g.TempImmediate(c.value);
inputs[index * 2 + 2 + 0] = g.TempImmediate(value); inputs[index * 2 + 2 + 1] = g.Label(c.branch);
inputs[index * 2 + 2 + 1] = g.Label(branch);
} }
Emit(kArchLookupSwitch, 0, nullptr, input_count, inputs, 0, nullptr); Emit(kArchLookupSwitch, 0, nullptr, input_count, inputs, 0, nullptr);
} }

View File

@ -26,7 +26,7 @@ struct CallBuffer; // TODO(bmeurer): Remove this.
class FlagsContinuation; class FlagsContinuation;
class Linkage; class Linkage;
class OperandGenerator; class OperandGenerator;
struct SwitchInfo; class SwitchInfo;
class StateObjectDeduplicator; class StateObjectDeduplicator;
// This struct connects nodes of parameters which are going to be pushed on the // This struct connects nodes of parameters which are going to be pushed on the

View File

@ -1582,20 +1582,20 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
// Emit either ArchTableSwitch or ArchLookupSwitch. // Emit either ArchTableSwitch or ArchLookupSwitch.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) { if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
static const size_t kMaxTableSwitchValueRange = 2 << 16; static const size_t kMaxTableSwitchValueRange = 2 << 16;
size_t table_space_cost = 9 + sw.value_range; size_t table_space_cost = 9 + sw.value_range();
size_t table_time_cost = 3; size_t table_time_cost = 3;
size_t lookup_space_cost = 2 + 2 * sw.case_count; size_t lookup_space_cost = 2 + 2 * sw.case_count();
size_t lookup_time_cost = sw.case_count; size_t lookup_time_cost = sw.case_count();
if (sw.case_count > 0 && if (sw.case_count() > 0 &&
table_space_cost + 3 * table_time_cost <= table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost && lookup_space_cost + 3 * lookup_time_cost &&
sw.min_value > std::numeric_limits<int32_t>::min() && sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) { sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand; InstructionOperand index_operand = value_operand;
if (sw.min_value) { if (sw.min_value()) {
index_operand = g.TempRegister(); index_operand = g.TempRegister();
Emit(kMipsSub, index_operand, value_operand, Emit(kMipsSub, index_operand, value_operand,
g.TempImmediate(sw.min_value)); g.TempImmediate(sw.min_value()));
} }
// Generate a table lookup. // Generate a table lookup.
return EmitTableSwitch(sw, index_operand); return EmitTableSwitch(sw, index_operand);

View File

@ -2198,20 +2198,20 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
// Emit either ArchTableSwitch or ArchLookupSwitch. // Emit either ArchTableSwitch or ArchLookupSwitch.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) { if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
static const size_t kMaxTableSwitchValueRange = 2 << 16; static const size_t kMaxTableSwitchValueRange = 2 << 16;
size_t table_space_cost = 10 + 2 * sw.value_range; size_t table_space_cost = 10 + 2 * sw.value_range();
size_t table_time_cost = 3; size_t table_time_cost = 3;
size_t lookup_space_cost = 2 + 2 * sw.case_count; size_t lookup_space_cost = 2 + 2 * sw.case_count();
size_t lookup_time_cost = sw.case_count; size_t lookup_time_cost = sw.case_count();
if (sw.case_count > 0 && if (sw.case_count() > 0 &&
table_space_cost + 3 * table_time_cost <= table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost && lookup_space_cost + 3 * lookup_time_cost &&
sw.min_value > std::numeric_limits<int32_t>::min() && sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) { sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand; InstructionOperand index_operand = value_operand;
if (sw.min_value) { if (sw.min_value()) {
index_operand = g.TempRegister(); index_operand = g.TempRegister();
Emit(kMips64Sub, index_operand, value_operand, Emit(kMips64Sub, index_operand, value_operand,
g.TempImmediate(sw.min_value)); g.TempImmediate(sw.min_value()));
} }
// Generate a table lookup. // Generate a table lookup.
return EmitTableSwitch(sw, index_operand); return EmitTableSwitch(sw, index_operand);

View File

@ -1720,20 +1720,20 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
// Emit either ArchTableSwitch or ArchLookupSwitch. // Emit either ArchTableSwitch or ArchLookupSwitch.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) { if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
static const size_t kMaxTableSwitchValueRange = 2 << 16; static const size_t kMaxTableSwitchValueRange = 2 << 16;
size_t table_space_cost = 4 + sw.value_range; size_t table_space_cost = 4 + sw.value_range();
size_t table_time_cost = 3; size_t table_time_cost = 3;
size_t lookup_space_cost = 3 + 2 * sw.case_count; size_t lookup_space_cost = 3 + 2 * sw.case_count();
size_t lookup_time_cost = sw.case_count; size_t lookup_time_cost = sw.case_count();
if (sw.case_count > 0 && if (sw.case_count() > 0 &&
table_space_cost + 3 * table_time_cost <= table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost && lookup_space_cost + 3 * lookup_time_cost &&
sw.min_value > std::numeric_limits<int32_t>::min() && sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) { sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand; InstructionOperand index_operand = value_operand;
if (sw.min_value) { if (sw.min_value()) {
index_operand = g.TempRegister(); index_operand = g.TempRegister();
Emit(kPPC_Sub, index_operand, value_operand, Emit(kPPC_Sub, index_operand, value_operand,
g.TempImmediate(sw.min_value)); g.TempImmediate(sw.min_value()));
} }
// Generate a table lookup. // Generate a table lookup.
return EmitTableSwitch(sw, index_operand); return EmitTableSwitch(sw, index_operand);

View File

@ -2095,20 +2095,20 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
// Emit either ArchTableSwitch or ArchLookupSwitch. // Emit either ArchTableSwitch or ArchLookupSwitch.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) { if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
static const size_t kMaxTableSwitchValueRange = 2 << 16; static const size_t kMaxTableSwitchValueRange = 2 << 16;
size_t table_space_cost = 4 + sw.value_range; size_t table_space_cost = 4 + sw.value_range();
size_t table_time_cost = 3; size_t table_time_cost = 3;
size_t lookup_space_cost = 3 + 2 * sw.case_count; size_t lookup_space_cost = 3 + 2 * sw.case_count();
size_t lookup_time_cost = sw.case_count; size_t lookup_time_cost = sw.case_count();
if (sw.case_count > 0 && if (sw.case_count() > 0 &&
table_space_cost + 3 * table_time_cost <= table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost && lookup_space_cost + 3 * lookup_time_cost &&
sw.min_value > std::numeric_limits<int32_t>::min() && sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) { sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand; InstructionOperand index_operand = value_operand;
if (sw.min_value) { if (sw.min_value()) {
index_operand = g.TempRegister(); index_operand = g.TempRegister();
Emit(kS390_Lay | AddressingModeField::encode(kMode_MRI), index_operand, Emit(kS390_Lay | AddressingModeField::encode(kMode_MRI), index_operand,
value_operand, g.TempImmediate(-sw.min_value)); value_operand, g.TempImmediate(-sw.min_value()));
} }
#if V8_TARGET_ARCH_S390X #if V8_TARGET_ARCH_S390X
InstructionOperand index_operand_zero_ext = g.TempRegister(); InstructionOperand index_operand_zero_ext = g.TempRegister();

View File

@ -1937,21 +1937,21 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
// Emit either ArchTableSwitch or ArchLookupSwitch. // Emit either ArchTableSwitch or ArchLookupSwitch.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) { if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
static const size_t kMaxTableSwitchValueRange = 2 << 16; static const size_t kMaxTableSwitchValueRange = 2 << 16;
size_t table_space_cost = 4 + sw.value_range; size_t table_space_cost = 4 + sw.value_range();
size_t table_time_cost = 3; size_t table_time_cost = 3;
size_t lookup_space_cost = 3 + 2 * sw.case_count; size_t lookup_space_cost = 3 + 2 * sw.case_count();
size_t lookup_time_cost = sw.case_count; size_t lookup_time_cost = sw.case_count();
if (sw.case_count > 4 && if (sw.case_count() > 4 &&
table_space_cost + 3 * table_time_cost <= table_space_cost + 3 * table_time_cost <=
lookup_space_cost + 3 * lookup_time_cost && lookup_space_cost + 3 * lookup_time_cost &&
sw.min_value > std::numeric_limits<int32_t>::min() && sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) { sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = g.TempRegister(); InstructionOperand index_operand = g.TempRegister();
if (sw.min_value) { if (sw.min_value()) {
// The leal automatically zero extends, so result is a valid 64-bit // The leal automatically zero extends, so result is a valid 64-bit
// index. // index.
Emit(kX64Lea32 | AddressingModeField::encode(kMode_MRI), index_operand, Emit(kX64Lea32 | AddressingModeField::encode(kMode_MRI), index_operand,
value_operand, g.TempImmediate(-sw.min_value)); value_operand, g.TempImmediate(-sw.min_value()));
} else { } else {
// Zero extend, because we use it as 64-bit index into the jump table. // Zero extend, because we use it as 64-bit index into the jump table.
Emit(kX64Movl, index_operand, value_operand); Emit(kX64Movl, index_operand, value_operand);

View File

@ -238,10 +238,11 @@ TEST_F(CommonOperatorTest, Switch) {
TEST_F(CommonOperatorTest, IfValue) { TEST_F(CommonOperatorTest, IfValue) {
TRACED_FOREACH(int32_t, value, kInt32Values) { TRACED_FOREACH(int32_t, value, kInt32Values) {
const Operator* const op = common()->IfValue(value); TRACED_FOREACH(int32_t, order, kInt32Values) {
const Operator* const op = common()->IfValue(value, order);
EXPECT_EQ(IrOpcode::kIfValue, op->opcode()); EXPECT_EQ(IrOpcode::kIfValue, op->opcode());
EXPECT_EQ(Operator::kKontrol, op->properties()); EXPECT_EQ(Operator::kKontrol, op->properties());
EXPECT_EQ(value, OpParameter<int32_t>(op)); EXPECT_EQ(IfValueParameters(value, order), IfValueParametersOf(op));
EXPECT_EQ(0, op->ValueInputCount()); EXPECT_EQ(0, op->ValueInputCount());
EXPECT_EQ(0, op->EffectInputCount()); EXPECT_EQ(0, op->EffectInputCount());
EXPECT_EQ(1, op->ControlInputCount()); EXPECT_EQ(1, op->ControlInputCount());
@ -251,6 +252,7 @@ TEST_F(CommonOperatorTest, IfValue) {
EXPECT_EQ(1, op->ControlOutputCount()); EXPECT_EQ(1, op->ControlOutputCount());
} }
} }
}
TEST_F(CommonOperatorTest, Select) { TEST_F(CommonOperatorTest, Select) {

View File

@ -57,9 +57,10 @@ TEST_F(ControlFlowOptimizerTest, BuildSwitch1) {
graph()->SetEnd(graph()->NewNode(common()->End(1), merge)); graph()->SetEnd(graph()->NewNode(common()->End(1), merge));
Optimize(); Optimize();
Capture<Node*> switch_capture; Capture<Node*> switch_capture;
EXPECT_THAT(end(), EXPECT_THAT(
IsEnd(IsMerge(IsIfValue(0, CaptureEq(&switch_capture)), end(), IsEnd(IsMerge(
IsIfValue(1, CaptureEq(&switch_capture)), IsIfValue(IfValueParameters(0, 1), CaptureEq(&switch_capture)),
IsIfValue(IfValueParameters(1, 2), CaptureEq(&switch_capture)),
IsIfDefault(AllOf(CaptureEq(&switch_capture), IsIfDefault(AllOf(CaptureEq(&switch_capture),
IsSwitch(index, start())))))); IsSwitch(index, start()))))));
} }
@ -89,9 +90,9 @@ TEST_F(ControlFlowOptimizerTest, BuildSwitch2) {
Optimize(); Optimize();
Capture<Node*> switch_capture; Capture<Node*> switch_capture;
EXPECT_THAT( EXPECT_THAT(
end(), end(), IsEnd(IsMerge(
IsEnd(IsMerge(IsIfValue(0, CaptureEq(&switch_capture)), IsIfValue(IfValueParameters(0, 1), CaptureEq(&switch_capture)),
IsIfValue(1, CaptureEq(&switch_capture)), IsIfValue(IfValueParameters(1, 2), CaptureEq(&switch_capture)),
IsIfDefault(AllOf(CaptureEq(&switch_capture), IsIfDefault(AllOf(CaptureEq(&switch_capture),
IsSwitch(index, IsIfSuccess(index))))))); IsSwitch(index, IsIfSuccess(index)))))));
} }

View File

@ -135,7 +135,7 @@ class IsSwitchMatcher final : public TestNodeMatcher {
class IsIfValueMatcher final : public TestNodeMatcher { class IsIfValueMatcher final : public TestNodeMatcher {
public: public:
IsIfValueMatcher(const Matcher<int32_t>& value_matcher, IsIfValueMatcher(const Matcher<IfValueParameters>& value_matcher,
const Matcher<Node*>& control_matcher) const Matcher<Node*>& control_matcher)
: TestNodeMatcher(IrOpcode::kIfValue), : TestNodeMatcher(IrOpcode::kIfValue),
value_matcher_(value_matcher), value_matcher_(value_matcher),
@ -152,14 +152,14 @@ class IsIfValueMatcher final : public TestNodeMatcher {
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final { bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
return (TestNodeMatcher::MatchAndExplain(node, listener) && return (TestNodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(OpParameter<int32_t>(node->op()), "value", PrintMatchAndExplain(IfValueParametersOf(node->op()), "value",
value_matcher_, listener) && value_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node), PrintMatchAndExplain(NodeProperties::GetControlInput(node),
"control", control_matcher_, listener)); "control", control_matcher_, listener));
} }
private: private:
const Matcher<int32_t> value_matcher_; const Matcher<IfValueParameters> value_matcher_;
const Matcher<Node*> control_matcher_; const Matcher<Node*> control_matcher_;
}; };
@ -1526,8 +1526,7 @@ Matcher<Node*> IsSwitch(const Matcher<Node*>& value_matcher,
return MakeMatcher(new IsSwitchMatcher(value_matcher, control_matcher)); return MakeMatcher(new IsSwitchMatcher(value_matcher, control_matcher));
} }
Matcher<Node*> IsIfValue(const Matcher<IfValueParameters>& value_matcher,
Matcher<Node*> IsIfValue(const Matcher<int32_t>& value_matcher,
const Matcher<Node*>& control_matcher) { const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsIfValueMatcher(value_matcher, control_matcher)); return MakeMatcher(new IsIfValueMatcher(value_matcher, control_matcher));
} }

View File

@ -5,6 +5,7 @@
#ifndef V8_UNITTESTS_COMPILER_NODE_TEST_UTILS_H_ #ifndef V8_UNITTESTS_COMPILER_NODE_TEST_UTILS_H_
#define V8_UNITTESTS_COMPILER_NODE_TEST_UTILS_H_ #define V8_UNITTESTS_COMPILER_NODE_TEST_UTILS_H_
#include "src/compiler/common-operator.h"
#include "src/compiler/machine-operator.h" #include "src/compiler/machine-operator.h"
#include "src/compiler/simplified-operator.h" #include "src/compiler/simplified-operator.h"
#include "src/machine-type.h" #include "src/machine-type.h"
@ -68,7 +69,7 @@ Matcher<Node*> IsIfFalse(const Matcher<Node*>& control_matcher);
Matcher<Node*> IsIfSuccess(const Matcher<Node*>& control_matcher); Matcher<Node*> IsIfSuccess(const Matcher<Node*>& control_matcher);
Matcher<Node*> IsSwitch(const Matcher<Node*>& value_matcher, Matcher<Node*> IsSwitch(const Matcher<Node*>& value_matcher,
const Matcher<Node*>& control_matcher); const Matcher<Node*>& control_matcher);
Matcher<Node*> IsIfValue(const Matcher<int32_t>& value_matcher, Matcher<Node*> IsIfValue(const Matcher<IfValueParameters>& value_matcher,
const Matcher<Node*>& control_matcher); const Matcher<Node*>& control_matcher);
Matcher<Node*> IsIfDefault(const Matcher<Node*>& control_matcher); Matcher<Node*> IsIfDefault(const Matcher<Node*>& control_matcher);
Matcher<Node*> IsBeginRegion(const Matcher<Node*>& effect_matcher); Matcher<Node*> IsBeginRegion(const Matcher<Node*>& effect_matcher);