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.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
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 lookup_space_cost = 3 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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 &&
sw.min_value > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) {
sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand;
if (sw.min_value) {
if (sw.min_value()) {
index_operand = g.TempRegister();
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.
return EmitTableSwitch(sw, index_operand);

View File

@ -2349,20 +2349,20 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
// Emit either ArchTableSwitch or ArchLookupSwitch.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
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 lookup_space_cost = 3 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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 &&
sw.min_value > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) {
sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand;
if (sw.min_value) {
if (sw.min_value()) {
index_operand = g.TempRegister();
Emit(kArm64Sub32, index_operand, value_operand,
g.TempImmediate(sw.min_value));
g.TempImmediate(sw.min_value()));
}
// Generate a table lookup.
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++) {
Node* if_value = projections[i];
DCHECK_EQ(IrOpcode::kIfValue, if_value->opcode());
int32_t value_index = OpParameter<int32_t>(if_value->op());
if (value_index == mswitched.Value()) {
const IfValueParameters& p = IfValueParametersOf(if_value->op());
if (p.value() == mswitched.Value()) {
matched = true;
Replace(if_value, control);
break;

View File

@ -391,6 +391,26 @@ ZoneVector<MachineType> const* MachineTypesOf(Operator const* op) {
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) \
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
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
}
const Operator* CommonOperatorBuilder::IfValue(int32_t index) {
return new (zone()) Operator1<int32_t>( // --
const Operator* CommonOperatorBuilder::IfValue(int32_t index,
int32_t comparison_order) {
return new (zone()) Operator1<IfValueParameters>( // --
IrOpcode::kIfValue, Operator::kKontrol, // opcode
"IfValue", // name
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*);
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,
// including JavaScript, mid-level, and low-level.
class V8_EXPORT_PRIVATE CommonOperatorBuilder final
@ -395,7 +419,7 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
const Operator* IfSuccess();
const Operator* IfException();
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* Throw();
const Operator* Deoptimize(DeoptimizeKind kind, DeoptimizeReason reason,

View File

@ -83,6 +83,7 @@ bool ControlFlowOptimizer::TryBuildSwitch(Node* node) {
Node* if_false;
Node* if_true;
int32_t order = 1;
while (true) {
BranchMatcher matcher(branch);
DCHECK(matcher.Matched());
@ -109,7 +110,7 @@ bool ControlFlowOptimizer::TryBuildSwitch(Node* node) {
branch->NullAllInputs();
if_true->ReplaceInput(0, node);
}
NodeProperties::ChangeOp(if_true, common()->IfValue(value));
NodeProperties::ChangeOp(if_true, common()->IfValue(value, order++));
if_false->NullAllInputs();
Enqueue(if_true);
@ -128,7 +129,7 @@ bool ControlFlowOptimizer::TryBuildSwitch(Node* node) {
node->ReplaceInput(0, index);
NodeProperties::ChangeOp(node, common()->Switch(values.size() + 1));
if_true->ReplaceInput(0, node);
NodeProperties::ChangeOp(if_true, common()->IfValue(value));
NodeProperties::ChangeOp(if_true, common()->IfValue(value, order++));
Enqueue(if_true);
if_false->ReplaceInput(0, node);
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.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
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 lookup_space_cost = 3 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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 &&
sw.min_value > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) {
sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand;
if (sw.min_value) {
if (sw.min_value()) {
index_operand = g.TempRegister();
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.
return EmitTableSwitch(sw, index_operand);

View File

@ -15,15 +15,52 @@ namespace v8 {
namespace internal {
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.
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
class SwitchInfo {
public:
SwitchInfo(ZoneVector<CaseInfo>& cases, int32_t min_value, int32_t max_value,
BasicBlock* default_branch)
: cases_(cases),
min_value_(min_value),
max_value_(min_value),
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

View File

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

View File

@ -26,7 +26,7 @@ struct CallBuffer; // TODO(bmeurer): Remove this.
class FlagsContinuation;
class Linkage;
class OperandGenerator;
struct SwitchInfo;
class SwitchInfo;
class StateObjectDeduplicator;
// 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.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
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 lookup_space_cost = 2 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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 &&
sw.min_value > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) {
sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand;
if (sw.min_value) {
if (sw.min_value()) {
index_operand = g.TempRegister();
Emit(kMipsSub, index_operand, value_operand,
g.TempImmediate(sw.min_value));
g.TempImmediate(sw.min_value()));
}
// Generate a table lookup.
return EmitTableSwitch(sw, index_operand);

View File

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

View File

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

View File

@ -2095,20 +2095,20 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
// Emit either ArchTableSwitch or ArchLookupSwitch.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
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 lookup_space_cost = 3 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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 &&
sw.min_value > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) {
sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range() <= kMaxTableSwitchValueRange) {
InstructionOperand index_operand = value_operand;
if (sw.min_value) {
if (sw.min_value()) {
index_operand = g.TempRegister();
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
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.
if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
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 lookup_space_cost = 3 + 2 * sw.case_count;
size_t lookup_time_cost = sw.case_count;
if (sw.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 &&
sw.min_value > std::numeric_limits<int32_t>::min() &&
sw.value_range <= kMaxTableSwitchValueRange) {
sw.min_value() > std::numeric_limits<int32_t>::min() &&
sw.value_range() <= kMaxTableSwitchValueRange) {
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
// index.
Emit(kX64Lea32 | AddressingModeField::encode(kMode_MRI), index_operand,
value_operand, g.TempImmediate(-sw.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);

View File

@ -238,10 +238,11 @@ TEST_F(CommonOperatorTest, Switch) {
TEST_F(CommonOperatorTest, IfValue) {
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(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->EffectInputCount());
EXPECT_EQ(1, op->ControlInputCount());
@ -250,6 +251,7 @@ TEST_F(CommonOperatorTest, IfValue) {
EXPECT_EQ(0, op->EffectOutputCount());
EXPECT_EQ(1, op->ControlOutputCount());
}
}
}

View File

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

View File

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

View File

@ -5,6 +5,7 @@
#ifndef 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/simplified-operator.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*> IsSwitch(const Matcher<Node*>& value_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);
Matcher<Node*> IsIfDefault(const Matcher<Node*>& control_matcher);
Matcher<Node*> IsBeginRegion(const Matcher<Node*>& effect_matcher);