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:
parent
767c954972
commit
2daca1c6a8
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)))))));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user