From de088f207cb8f7aa5c638497fca08ebaaa30dd08 Mon Sep 17 00:00:00 2001 From: "bmeurer@chromium.org" Date: Wed, 29 Oct 2014 14:16:32 +0000 Subject: [PATCH] [turbofan] Introduce new Select operator to improve bounds checking. TEST=mjsunit,unittests R=dcarney@chromium.org Review URL: https://codereview.chromium.org/691513002 Cr-Commit-Position: refs/heads/master@{#24980} git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24980 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/compiler/common-operator.cc | 34 +++ src/compiler/common-operator.h | 25 ++ src/compiler/control-reducer.cc | 31 +++ src/compiler/js-generic-lowering.cc | 18 ++ src/compiler/js-typed-lowering.cc | 4 +- src/compiler/machine-type.h | 2 + src/compiler/node-matchers.h | 9 + src/compiler/opcodes.h | 1 + src/compiler/simplified-lowering.cc | 274 ++++++++++++++++----- src/compiler/simplified-lowering.h | 2 +- src/compiler/typer.cc | 5 + src/compiler/verifier.cc | 6 + test/mjsunit/asm/int16array-outofbounds.js | 14 +- .../unittests/compiler/common-operator-unittest.cc | 31 ++- .../compiler/js-typed-lowering-unittest.cc | 23 +- test/unittests/compiler/node-test-utils.cc | 56 +++++ test/unittests/compiler/node-test-utils.h | 6 + 17 files changed, 460 insertions(+), 81 deletions(-) diff --git a/src/compiler/common-operator.cc b/src/compiler/common-operator.cc index c9dea87..f1ac90a 100644 --- a/src/compiler/common-operator.cc +++ b/src/compiler/common-operator.cc @@ -50,6 +50,32 @@ BranchHint BranchHintOf(const Operator* const op) { } +bool operator==(SelectParameters const& lhs, SelectParameters const& rhs) { + return lhs.type() == rhs.type() && lhs.hint() == rhs.hint(); +} + + +bool operator!=(SelectParameters const& lhs, SelectParameters const& rhs) { + return !(lhs == rhs); +} + + +size_t hash_value(SelectParameters const& p) { + return base::hash_combine(p.type(), p.hint()); +} + + +std::ostream& operator<<(std::ostream& os, SelectParameters const& p) { + return os << p.type() << "|" << p.hint(); +} + + +SelectParameters const& SelectParametersOf(const Operator* const op) { + DCHECK_EQ(IrOpcode::kSelect, op->opcode()); + return OpParameter(op); +} + + size_t hash_value(OutputFrameStateCombine const& sc) { return base::hash_combine(sc.kind_, sc.parameter_); } @@ -219,6 +245,14 @@ const Operator* CommonOperatorBuilder::HeapConstant( } +const Operator* CommonOperatorBuilder::Select(MachineType type, + BranchHint hint) { + return new (zone()) + Operator1(IrOpcode::kSelect, Operator::kPure, 3, 1, + "Select", SelectParameters(type, hint)); +} + + const Operator* CommonOperatorBuilder::Phi(MachineType type, int arguments) { DCHECK(arguments > 0); // Disallow empty phis. return new (zone()) Operator1(IrOpcode::kPhi, Operator::kPure, diff --git a/src/compiler/common-operator.h b/src/compiler/common-operator.h index 31c3d12..355269c 100644 --- a/src/compiler/common-operator.h +++ b/src/compiler/common-operator.h @@ -33,6 +33,30 @@ std::ostream& operator<<(std::ostream&, BranchHint); BranchHint BranchHintOf(const Operator* const); +class SelectParameters FINAL { + public: + explicit SelectParameters(MachineType type, + BranchHint hint = BranchHint::kNone) + : type_(type), hint_(hint) {} + + MachineType type() const { return type_; } + BranchHint hint() const { return hint_; } + + private: + const MachineType type_; + const BranchHint hint_; +}; + +bool operator==(SelectParameters const&, SelectParameters const&); +bool operator!=(SelectParameters const&, SelectParameters const&); + +size_t hash_value(SelectParameters const& p); + +std::ostream& operator<<(std::ostream&, SelectParameters const& p); + +SelectParameters const& SelectParametersOf(const Operator* const); + + // Flag that describes how to combine the current environment with // the output of a node to obtain a framestate for lazy bailout. class OutputFrameStateCombine { @@ -157,6 +181,7 @@ class CommonOperatorBuilder FINAL : public ZoneObject { const Operator* NumberConstant(volatile double); const Operator* HeapConstant(const Unique&); + const Operator* Select(MachineType, BranchHint = BranchHint::kNone); const Operator* Phi(MachineType type, int arguments); const Operator* EffectPhi(int arguments); const Operator* ValueEffect(int arguments); diff --git a/src/compiler/control-reducer.cc b/src/compiler/control-reducer.cc index 533fb23..b060764 100644 --- a/src/compiler/control-reducer.cc +++ b/src/compiler/control-reducer.cc @@ -322,6 +322,8 @@ class ControlReducerImpl { case IrOpcode::kLoop: case IrOpcode::kMerge: return ReduceMerge(node); + case IrOpcode::kSelect: + return ReduceSelect(node); case IrOpcode::kPhi: case IrOpcode::kEffectPhi: return ReducePhi(node); @@ -330,6 +332,32 @@ class ControlReducerImpl { } } + // Reduce redundant selects. + Node* ReduceSelect(Node* const node) { + Node* const cond = node->InputAt(0); + Node* const tvalue = node->InputAt(1); + Node* const fvalue = node->InputAt(2); + if (tvalue == fvalue) return tvalue; + switch (cond->opcode()) { + case IrOpcode::kInt32Constant: + return Int32Matcher(cond).Is(0) ? fvalue : tvalue; + case IrOpcode::kInt64Constant: + return Int64Matcher(cond).Is(0) ? fvalue : tvalue; + case IrOpcode::kNumberConstant: + return NumberMatcher(cond).Is(0) ? fvalue : tvalue; + case IrOpcode::kHeapConstant: { + Handle object = + HeapObjectMatcher(cond).Value().handle(); + if (object->IsTrue()) return tvalue; + if (object->IsFalse()) return fvalue; + break; + } + default: + break; + } + return node; + } + // Reduce redundant phis. Node* ReducePhi(Node* node) { int n = node->InputCount(); @@ -406,6 +434,9 @@ class ControlReducerImpl { case IrOpcode::kInt32Constant: is_true = !Int32Matcher(cond).Is(0); break; + case IrOpcode::kInt64Constant: + is_true = !Int64Matcher(cond).Is(0); + break; case IrOpcode::kNumberConstant: is_true = !NumberMatcher(cond).Is(0); break; diff --git a/src/compiler/js-generic-lowering.cc b/src/compiler/js-generic-lowering.cc index c16aa82..11979ec 100644 --- a/src/compiler/js-generic-lowering.cc +++ b/src/compiler/js-generic-lowering.cc @@ -64,6 +64,7 @@ Reduction JSGenericLowering::Reduce(Node* node) { Lower##x(node); \ break; DECLARE_CASE(Branch) + DECLARE_CASE(Select) JS_OP_LIST(DECLARE_CASE) #undef DECLARE_CASE default: @@ -241,6 +242,23 @@ void JSGenericLowering::LowerBranch(Node* node) { } +void JSGenericLowering::LowerSelect(Node* node) { + // TODO(bmeurer): This should probably be moved into a separate file. + SelectParameters const& p = SelectParametersOf(node->op()); + Node* branch = graph()->NewNode(common()->Branch(p.hint()), node->InputAt(0), + graph()->start()); + Node* if_true = graph()->NewNode(common()->IfTrue(), branch); + Node* vtrue = node->InputAt(1); + Node* if_false = graph()->NewNode(common()->IfFalse(), branch); + Node* vfalse = node->InputAt(2); + Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); + node->set_op(common()->Phi(p.type(), 2)); + node->ReplaceInput(0, vtrue); + node->ReplaceInput(1, vfalse); + node->ReplaceInput(2, merge); +} + + void JSGenericLowering::LowerJSUnaryNot(Node* node) { Callable callable = CodeFactory::ToBoolean( isolate(), ToBooleanStub::RESULT_AS_INVERSE_ODDBALL); diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc index e4d25bb..535e585 100644 --- a/src/compiler/js-typed-lowering.cc +++ b/src/compiler/js-typed-lowering.cc @@ -612,7 +612,7 @@ Reduction JSTypedLowering::ReduceJSLoadProperty(Node* node) { Handle::cast(handle(array->elements())); Node* pointer = jsgraph()->IntPtrConstant( bit_cast(elements->external_pointer())); - Node* length = jsgraph()->Constant(byte_length / array->element_size()); + Node* length = jsgraph()->Constant(array->length()->Number()); Node* effect = NodeProperties::GetEffectInput(node); Node* load = graph()->NewNode( simplified()->LoadElement( @@ -647,7 +647,7 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) { Handle::cast(handle(array->elements())); Node* pointer = jsgraph()->IntPtrConstant( bit_cast(elements->external_pointer())); - Node* length = jsgraph()->Constant(byte_length / array->element_size()); + Node* length = jsgraph()->Constant(array->length()->Number()); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); Node* store = graph()->NewNode( diff --git a/src/compiler/machine-type.h b/src/compiler/machine-type.h index 12fdd11..4c38a6d 100644 --- a/src/compiler/machine-type.h +++ b/src/compiler/machine-type.h @@ -50,6 +50,8 @@ enum MachineType { kMachUint32 = kRepWord32 | kTypeUint32, kMachInt64 = kRepWord64 | kTypeInt64, kMachUint64 = kRepWord64 | kTypeUint64, + kMachIntPtr = (kPointerSize == 4) ? kMachInt32 : kMachInt64, + kMachUintPtr = (kPointerSize == 4) ? kMachUint32 : kMachUint64, kMachPtr = (kPointerSize == 4) ? kRepWord32 : kRepWord64, kMachAnyTagged = kRepTagged | kTypeAny }; diff --git a/src/compiler/node-matchers.h b/src/compiler/node-matchers.h index 5803a00..8c124ba 100644 --- a/src/compiler/node-matchers.h +++ b/src/compiler/node-matchers.h @@ -81,6 +81,13 @@ typedef IntMatcher Int32Matcher; typedef IntMatcher Uint32Matcher; typedef IntMatcher Int64Matcher; typedef IntMatcher Uint64Matcher; +#if V8_HOST_ARCH_32_BIT +typedef Int32Matcher IntPtrMatcher; +typedef Uint32Matcher UintPtrMatcher; +#else +typedef Int64Matcher IntPtrMatcher; +typedef Uint64Matcher UintPtrMatcher; +#endif // A pattern matcher for floating point constants. @@ -138,6 +145,8 @@ typedef BinopMatcher Int32BinopMatcher; typedef BinopMatcher Uint32BinopMatcher; typedef BinopMatcher Int64BinopMatcher; typedef BinopMatcher Uint64BinopMatcher; +typedef BinopMatcher IntPtrBinopMatcher; +typedef BinopMatcher UintPtrBinopMatcher; typedef BinopMatcher Float64BinopMatcher; typedef BinopMatcher NumberBinopMatcher; diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index 4853110..1d20685 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -33,6 +33,7 @@ V(HeapConstant) #define INNER_OP_LIST(V) \ + V(Select) \ V(Phi) \ V(EffectPhi) \ V(ValueEffect) \ diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index 35707f2..43a0d52 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -299,6 +299,85 @@ class RepresentationSelector { void VisitInt64Cmp(Node* node) { VisitBinop(node, kMachInt64, kRepBit); } void VisitUint64Cmp(Node* node) { VisitBinop(node, kMachUint64, kRepBit); } + // Helper for handling selects. + // TODO(turbofan): Share some code with VisitPhi() below? + void VisitSelect(Node* node, MachineTypeUnion use, + SimplifiedLowering* lowering) { + ProcessInput(node, 0, kRepBit); + + // Selects adapt to the output representation their uses demand, pushing + // representation changes to their inputs. + Type* upper = NodeProperties::GetBounds(node).upper; + MachineType output = kMachNone; + MachineType propagate = kMachNone; + + if (upper->Is(Type::Signed32()) || upper->Is(Type::Unsigned32())) { + // legal = kRepTagged | kRepFloat64 | kRepWord32; + if ((use & kRepMask) == kRepTagged) { + // only tagged uses. + output = kRepTagged; + propagate = kRepTagged; + } else if ((use & kRepMask) == kRepFloat64) { + // only float64 uses. + output = kRepFloat64; + propagate = kRepFloat64; + } else { + // multiple uses. + output = kRepWord32; + propagate = kRepWord32; + } + } else if (upper->Is(Type::Boolean())) { + // legal = kRepTagged | kRepBit; + if ((use & kRepMask) == kRepTagged) { + // only tagged uses. + output = kRepTagged; + propagate = kRepTagged; + } else { + // multiple uses. + output = kRepBit; + propagate = kRepBit; + } + } else if (upper->Is(Type::Number())) { + // legal = kRepTagged | kRepFloat64; + if ((use & kRepMask) == kRepTagged) { + // only tagged uses. + output = kRepTagged; + propagate = kRepTagged; + } else { + // multiple uses. + output = kRepFloat64; + propagate = kRepFloat64; + } + } else { + // legal = kRepTagged; + output = kRepTagged; + propagate = kRepTagged; + } + + MachineType output_type = + static_cast(changer_->TypeFromUpperBound(upper) | output); + SetOutput(node, output_type); + + if (lower()) { + // Update the select operator. + SelectParameters p = SelectParametersOf(node->op()); + MachineType type = static_cast(output_type); + if (type != p.type()) { + node->set_op(lowering->common()->Select(type, p.hint())); + } + + // Convert inputs to the output representation of this select. + ProcessInput(node, 1, output_type); + ProcessInput(node, 2, output_type); + } else { + // Propagate {use} of the select to value inputs. + MachineType use_type = + static_cast((use & kTypeMask) | propagate); + ProcessInput(node, 1, use_type); + ProcessInput(node, 2, use_type); + } + } + // Helper for handling phis. void VisitPhi(Node* node, MachineTypeUnion use, SimplifiedLowering* lowering) { @@ -481,6 +560,8 @@ class RepresentationSelector { ProcessInput(node, 0, kRepBit); Enqueue(NodeProperties::GetControlInput(node, 0)); break; + case IrOpcode::kSelect: + return VisitSelect(node, use, lowering); case IrOpcode::kPhi: return VisitPhi(node, use, lowering); @@ -1057,25 +1138,67 @@ void SimplifiedLowering::DoStoreField(Node* node) { Node* SimplifiedLowering::ComputeIndex(const ElementAccess& access, - Node* index) { - int element_size = ElementSizeOf(access.machine_type); + Node* const key) { + Node* index = key; + const int element_size = ElementSizeOf(access.machine_type); if (element_size != 1) { - index = graph()->NewNode(machine()->Int32Mul(), - jsgraph()->Int32Constant(element_size), index); + index = graph()->NewNode(machine()->Int32Mul(), index, + jsgraph()->Int32Constant(element_size)); + } + const int fixed_offset = access.header_size - access.tag(); + if (fixed_offset != 0) { + index = graph()->NewNode(machine()->Int32Add(), index, + jsgraph()->Int32Constant(fixed_offset)); + } + // TODO(bmeurer): 64-Bit + // if (machine()->Is64()) { + // index = graph()->NewNode(machine()->ChangeInt32ToInt64(), index); + // } + return index; +} + + +namespace { + +intptr_t AddressForOutOfBoundsLoad(MachineType type) { + switch (RepresentationOf(type)) { + case kRepFloat32: { + static const float dummy = std::numeric_limits::quiet_NaN(); + return bit_cast(&dummy); + } + case kRepFloat64: { + static const double dummy = std::numeric_limits::quiet_NaN(); + return bit_cast(&dummy); + } + case kRepBit: + case kRepWord8: + case kRepWord16: + case kRepWord32: { + static const int32_t dummy = 0; + return bit_cast(&dummy); + } + default: + break; } - int fixed_offset = access.header_size - access.tag(); - if (fixed_offset == 0) return index; - return graph()->NewNode(machine()->Int32Add(), index, - jsgraph()->Int32Constant(fixed_offset)); + UNREACHABLE(); + return 0; } +intptr_t AddressForOutOfBoundsStore() { + static volatile double dummy = 0; + return bit_cast(&dummy); +} + +} // namespace + + void SimplifiedLowering::DoLoadElement(Node* node, MachineType output_type) { const ElementAccess& access = ElementAccessOf(node->op()); const Operator* op = machine()->Load(access.machine_type); Node* key = node->InputAt(1); - Node* effect = node->InputAt(3); Node* index = ComputeIndex(access, key); + Node* effect = node->InputAt(3); if (access.bounds_check == kNoBoundsCheck) { DCHECK_EQ(access.machine_type, output_type); node->set_op(op); @@ -1087,54 +1210,68 @@ void SimplifiedLowering::DoLoadElement(Node* node, MachineType output_type) { Node* base = node->InputAt(0); Node* length = node->InputAt(2); - Node* check = graph()->NewNode(machine()->Uint32LessThan(), key, length); - Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, - graph()->start()); - - Node* if_true = graph()->NewNode(common()->IfTrue(), branch); - Node* load = graph()->NewNode(op, base, index, effect, if_true); - Node* result = load; - if (output_type & kRepTagged) { - // TODO(turbofan): This is ugly as hell! - SimplifiedOperatorBuilder simplified(graph()->zone()); - RepresentationChanger changer(jsgraph(), &simplified, - graph()->zone()->isolate()); - result = changer.GetTaggedRepresentationFor(result, access.machine_type); - } - Node* if_false = graph()->NewNode(common()->IfFalse(), branch); - Node* undefined; - if (output_type & kRepTagged) { - DCHECK(!(access.machine_type & kRepTagged)); - undefined = jsgraph()->UndefinedConstant(); - } else if (output_type & kRepFloat32) { - undefined = - jsgraph()->Float32Constant(std::numeric_limits::quiet_NaN()); - } else if (output_type & kRepFloat64) { - undefined = - jsgraph()->Float64Constant(std::numeric_limits::quiet_NaN()); + IntPtrMatcher mbase(base); + if (mbase.HasValue() && (output_type & kRepTagged) == 0) { + Node* select = graph()->NewNode( + common()->Select(kMachIntPtr, BranchHint::kTrue), check, index, + jsgraph()->IntPtrConstant(AddressForOutOfBoundsLoad(output_type) - + mbase.Value())); + + node->set_op(op); + node->ReplaceInput(1, select); + node->ReplaceInput(2, effect); + node->ReplaceInput(3, graph()->start()); } else { - undefined = jsgraph()->Int32Constant(0); - } - - Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); - Node* phi = graph()->NewNode(common()->EffectPhi(2), load, effect, merge); + Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), + check, graph()->start()); + + Node* if_true = graph()->NewNode(common()->IfTrue(), branch); + Node* load = graph()->NewNode(op, base, index, effect, if_true); + Node* result = load; + if (output_type & kRepTagged) { + // TODO(turbofan): This is ugly as hell! + SimplifiedOperatorBuilder simplified(graph()->zone()); + RepresentationChanger changer(jsgraph(), &simplified, + graph()->zone()->isolate()); + result = + changer.GetTaggedRepresentationFor(result, access.machine_type); + } - // Replace effect uses of node with the effect phi. - for (UseIter i = node->uses().begin(); i != node->uses().end();) { - if (NodeProperties::IsEffectEdge(i.edge())) { - i = i.UpdateToAndIncrement(phi); + Node* if_false = graph()->NewNode(common()->IfFalse(), branch); + Node* undefined; + if (output_type & kRepTagged) { + DCHECK_EQ(0, access.machine_type & kRepTagged); + undefined = jsgraph()->UndefinedConstant(); + } else if (output_type & kRepFloat32) { + undefined = + jsgraph()->Float32Constant(std::numeric_limits::quiet_NaN()); + } else if (output_type & kRepFloat64) { + undefined = jsgraph()->Float64Constant( + std::numeric_limits::quiet_NaN()); } else { - ++i; + undefined = jsgraph()->Int32Constant(0); } - } - node->set_op(common()->Phi(output_type, 2)); - node->ReplaceInput(0, result); - node->ReplaceInput(1, undefined); - node->ReplaceInput(2, merge); - node->TrimInputCount(3); + Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); + Node* phi = graph()->NewNode(common()->EffectPhi(2), load, effect, merge); + + // Replace effect uses of node with the effect phi. + for (UseIter i = node->uses().begin(); i != node->uses().end();) { + if (NodeProperties::IsEffectEdge(i.edge())) { + i = i.UpdateToAndIncrement(phi); + } else { + ++i; + } + } + + node->set_op(common()->Phi(output_type, 2)); + node->ReplaceInput(0, result); + node->ReplaceInput(1, undefined); + node->ReplaceInput(2, merge); + node->TrimInputCount(3); + } } } @@ -1159,23 +1296,35 @@ void SimplifiedLowering::DoStoreElement(Node* node) { Node* value = node->InputAt(3); Node* effect = node->InputAt(4); Node* control = node->InputAt(5); - Node* check = graph()->NewNode(machine()->Uint32LessThan(), key, length); - Node* branch = - graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); - Node* if_true = graph()->NewNode(common()->IfTrue(), branch); - Node* store = graph()->NewNode(op, base, index, value, effect, if_true); + IntPtrMatcher mbase(base); + if (mbase.HasValue()) { + Node* select = graph()->NewNode( + common()->Select(kMachIntPtr, BranchHint::kTrue), check, index, + jsgraph()->IntPtrConstant(AddressForOutOfBoundsStore() - + mbase.Value())); + + node->set_op(op); + node->ReplaceInput(1, select); + node->RemoveInput(2); + } else { + Node* branch = + graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); + + Node* if_true = graph()->NewNode(common()->IfTrue(), branch); + Node* store = graph()->NewNode(op, base, index, value, effect, if_true); - Node* if_false = graph()->NewNode(common()->IfFalse(), branch); + Node* if_false = graph()->NewNode(common()->IfFalse(), branch); - Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); + Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); - node->set_op(common()->EffectPhi(2)); - node->ReplaceInput(0, store); - node->ReplaceInput(1, effect); - node->ReplaceInput(2, merge); - node->TrimInputCount(3); + node->set_op(common()->EffectPhi(2)); + node->ReplaceInput(0, store); + node->ReplaceInput(1, effect); + node->ReplaceInput(2, merge); + node->TrimInputCount(3); + } } } @@ -1375,7 +1524,6 @@ void SimplifiedLowering::DoStringLessThanOrEqual(Node* node) { node->ReplaceInput(1, jsgraph()->SmiConstant(EQUAL)); } - } // namespace compiler } // namespace internal } // namespace v8 diff --git a/src/compiler/simplified-lowering.h b/src/compiler/simplified-lowering.h index d4d99d3..0e8d5aa 100644 --- a/src/compiler/simplified-lowering.h +++ b/src/compiler/simplified-lowering.h @@ -40,7 +40,7 @@ class SimplifiedLowering FINAL { Node* IsTagged(Node* node); Node* Untag(Node* node); Node* OffsetMinusTagConstant(int32_t offset); - Node* ComputeIndex(const ElementAccess& access, Node* index); + Node* ComputeIndex(const ElementAccess& access, Node* const key); Node* StringComparison(Node* node, bool requires_ordering); Node* Int32Div(Node* const node); Node* Int32Mod(Node* const node); diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index b4a62a1..45004af 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -535,6 +535,11 @@ Bounds Typer::Visitor::TypeExternalConstant(Node* node) { } +Bounds Typer::Visitor::TypeSelect(Node* node) { + return Bounds::Either(Operand(node, 1), Operand(node, 2), zone()); +} + + Bounds Typer::Visitor::TypePhi(Node* node) { int arity = OperatorProperties::GetValueInputCount(node->op()); Bounds bounds = Operand(node, 0); diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 0bae1ef..42b582c 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -310,6 +310,12 @@ GenericGraphVisit::Control Verifier::Visitor::Pre(Node* node) { CheckUpperIs(node, Type::Any()); break; } + case IrOpcode::kSelect: { + CHECK_EQ(0, effect_count); + CHECK_EQ(0, control_count); + CHECK_EQ(3, value_count); + break; + } case IrOpcode::kPhi: { // Phi input count matches parent control node. CHECK_EQ(0, effect_count); diff --git a/test/mjsunit/asm/int16array-outofbounds.js b/test/mjsunit/asm/int16array-outofbounds.js index 18020aa..7982c00 100644 --- a/test/mjsunit/asm/int16array-outofbounds.js +++ b/test/mjsunit/asm/int16array-outofbounds.js @@ -10,16 +10,28 @@ function Module(stdlib, foreign, heap) { i = MEM16[i >> 1] | 0; return i; } + function loadm1() { + return MEM16[-1] | 0; + } function store(i, v) { i = i|0; v = v|0; MEM16[i >> 1] = v; } - return { load: load, store: store }; + function storem1(v) { + v = v|0; + MEM16[-1] = v; + } + return {load: load, loadm1: loadm1, store: store, storem1: storem1}; } var m = Module(this, {}, new ArrayBuffer(2)); +m.store(-1000, 4); +assertEquals(0, m.load(-1000)); +assertEquals(0, m.loadm1()); +m.storem1(1); +assertEquals(0, m.loadm1()); m.store(0, 32767); for (var i = 1; i < 64; ++i) { m.store(i * 2 * 32 * 1024, i); diff --git a/test/unittests/compiler/common-operator-unittest.cc b/test/unittests/compiler/common-operator-unittest.cc index fdf73c9..802d78c 100644 --- a/test/unittests/compiler/common-operator-unittest.cc +++ b/test/unittests/compiler/common-operator-unittest.cc @@ -156,16 +156,19 @@ const double kDoubleValues[] = {-std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN(), std::numeric_limits::signaling_NaN()}; + +const BranchHint kHints[] = {BranchHint::kNone, BranchHint::kTrue, + BranchHint::kFalse}; + } // namespace TEST_F(CommonOperatorTest, Branch) { - static const BranchHint kHints[] = {BranchHint::kNone, BranchHint::kTrue, - BranchHint::kFalse}; TRACED_FOREACH(BranchHint, hint, kHints) { const Operator* const op = common()->Branch(hint); EXPECT_EQ(IrOpcode::kBranch, op->opcode()); EXPECT_EQ(Operator::kFoldable, op->properties()); + EXPECT_EQ(hint, BranchHintOf(op)); EXPECT_EQ(1, OperatorProperties::GetValueInputCount(op)); EXPECT_EQ(0, OperatorProperties::GetEffectInputCount(op)); EXPECT_EQ(1, OperatorProperties::GetControlInputCount(op)); @@ -177,6 +180,30 @@ TEST_F(CommonOperatorTest, Branch) { } +TEST_F(CommonOperatorTest, Select) { + static const MachineType kTypes[] = { + kMachInt8, kMachUint8, kMachInt16, kMachUint16, + kMachInt32, kMachUint32, kMachInt64, kMachUint64, + kMachFloat32, kMachFloat64, kMachAnyTagged}; + TRACED_FOREACH(MachineType, type, kTypes) { + TRACED_FOREACH(BranchHint, hint, kHints) { + const Operator* const op = common()->Select(type, hint); + EXPECT_EQ(IrOpcode::kSelect, op->opcode()); + EXPECT_EQ(Operator::kPure, op->properties()); + EXPECT_EQ(type, SelectParametersOf(op).type()); + EXPECT_EQ(hint, SelectParametersOf(op).hint()); + EXPECT_EQ(3, OperatorProperties::GetValueInputCount(op)); + EXPECT_EQ(0, OperatorProperties::GetEffectInputCount(op)); + EXPECT_EQ(0, OperatorProperties::GetControlInputCount(op)); + EXPECT_EQ(3, OperatorProperties::GetTotalInputCount(op)); + EXPECT_EQ(1, OperatorProperties::GetValueOutputCount(op)); + EXPECT_EQ(0, OperatorProperties::GetEffectOutputCount(op)); + EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op)); + } + } +} + + TEST_F(CommonOperatorTest, Float32Constant) { TRACED_FOREACH(float, value, kFloatValues) { const Operator* op = common()->Float32Constant(value); diff --git a/test/unittests/compiler/js-typed-lowering-unittest.cc b/test/unittests/compiler/js-typed-lowering-unittest.cc index af32d94..539785d 100644 --- a/test/unittests/compiler/js-typed-lowering-unittest.cc +++ b/test/unittests/compiler/js-typed-lowering-unittest.cc @@ -114,9 +114,9 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithOrderedNumberAndBoolean) { TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArray) { const size_t kLength = 17; - uint8_t backing_store[kLength * 8]; + double backing_store[kLength]; Handle buffer = - NewArrayBuffer(backing_store, arraysize(backing_store)); + NewArrayBuffer(backing_store, sizeof(backing_store)); VectorSlotPair feedback(Handle::null(), FeedbackVectorICSlot::Invalid()); TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) { @@ -138,12 +138,11 @@ TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArray) { Reduction r = Reduce(node); ASSERT_TRUE(r.Changed()); - EXPECT_THAT( - r.replacement(), - IsLoadElement(AccessBuilder::ForTypedArrayElement(type, true), - IsIntPtrConstant(bit_cast(&backing_store[0])), - key, IsNumberConstant(static_cast(kLength)), - effect)); + EXPECT_THAT(r.replacement(), + IsLoadElement( + AccessBuilder::ForTypedArrayElement(type, true), + IsIntPtrConstant(bit_cast(&backing_store[0])), + key, IsNumberConstant(array->length()->Number()), effect)); } } @@ -154,9 +153,9 @@ TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArray) { TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) { const size_t kLength = 17; - uint8_t backing_store[kLength * 8]; + double backing_store[kLength]; Handle buffer = - NewArrayBuffer(backing_store, arraysize(backing_store)); + NewArrayBuffer(backing_store, sizeof(backing_store)); TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) { TRACED_FOREACH(StrictMode, strict_mode, kStrictModes) { Handle array = @@ -182,8 +181,8 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) { IsStoreElement( AccessBuilder::ForTypedArrayElement(type, true), IsIntPtrConstant(bit_cast(&backing_store[0])), - key, IsNumberConstant(static_cast(kLength)), - value, effect, control)); + key, IsNumberConstant(array->length()->Number()), value, + effect, control)); } } } diff --git a/test/unittests/compiler/node-test-utils.cc b/test/unittests/compiler/node-test-utils.cc index 6f00c37..a7308f9 100644 --- a/test/unittests/compiler/node-test-utils.cc +++ b/test/unittests/compiler/node-test-utils.cc @@ -208,6 +208,52 @@ class IsConstantMatcher FINAL : public NodeMatcher { }; +class IsSelectMatcher FINAL : public NodeMatcher { + public: + IsSelectMatcher(const Matcher& type_matcher, + const Matcher& value0_matcher, + const Matcher& value1_matcher, + const Matcher& value2_matcher) + : NodeMatcher(IrOpcode::kSelect), + type_matcher_(type_matcher), + value0_matcher_(value0_matcher), + value1_matcher_(value1_matcher), + value2_matcher_(value2_matcher) {} + + virtual void DescribeTo(std::ostream* os) const OVERRIDE { + NodeMatcher::DescribeTo(os); + *os << " whose type ("; + type_matcher_.DescribeTo(os); + *os << "), value0 ("; + value0_matcher_.DescribeTo(os); + *os << "), value1 ("; + value1_matcher_.DescribeTo(os); + *os << ") and value2 ("; + value2_matcher_.DescribeTo(os); + *os << ")"; + } + + virtual bool MatchAndExplain(Node* node, + MatchResultListener* listener) const OVERRIDE { + return (NodeMatcher::MatchAndExplain(node, listener) && + PrintMatchAndExplain(OpParameter(node), "type", + type_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0), + "value0", value0_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1), + "value1", value1_matcher_, listener) && + PrintMatchAndExplain(NodeProperties::GetValueInput(node, 2), + "value2", value2_matcher_, listener)); + } + + private: + const Matcher type_matcher_; + const Matcher value0_matcher_; + const Matcher value1_matcher_; + const Matcher value2_matcher_; +}; + + class IsPhiMatcher FINAL : public NodeMatcher { public: IsPhiMatcher(const Matcher& type_matcher, @@ -765,6 +811,15 @@ Matcher IsNumberConstant(const Matcher& value_matcher) { } +Matcher IsSelect(const Matcher& type_matcher, + const Matcher& value0_matcher, + const Matcher& value1_matcher, + const Matcher& value2_matcher) { + return MakeMatcher(new IsSelectMatcher(type_matcher, value0_matcher, + value1_matcher, value2_matcher)); +} + + Matcher IsPhi(const Matcher& type_matcher, const Matcher& value0_matcher, const Matcher& value1_matcher, @@ -857,6 +912,7 @@ Matcher IsStore(const Matcher& rep_matcher, IS_BINOP_MATCHER(NumberEqual) IS_BINOP_MATCHER(NumberLessThan) IS_BINOP_MATCHER(NumberSubtract) +IS_BINOP_MATCHER(NumberMultiply) IS_BINOP_MATCHER(Word32And) IS_BINOP_MATCHER(Word32Sar) IS_BINOP_MATCHER(Word32Shl) diff --git a/test/unittests/compiler/node-test-utils.h b/test/unittests/compiler/node-test-utils.h index f277a10..da05a10 100644 --- a/test/unittests/compiler/node-test-utils.h +++ b/test/unittests/compiler/node-test-utils.h @@ -48,6 +48,10 @@ Matcher IsFloat64Constant(const Matcher& value_matcher); Matcher IsInt32Constant(const Matcher& value_matcher); Matcher IsInt64Constant(const Matcher& value_matcher); Matcher IsNumberConstant(const Matcher& value_matcher); +Matcher IsSelect(const Matcher& type_matcher, + const Matcher& value0_matcher, + const Matcher& value1_matcher, + const Matcher& value2_matcher); Matcher IsPhi(const Matcher& type_matcher, const Matcher& value0_matcher, const Matcher& value1_matcher, @@ -69,6 +73,8 @@ Matcher IsNumberLessThan(const Matcher& lhs_matcher, const Matcher& rhs_matcher); Matcher IsNumberSubtract(const Matcher& lhs_matcher, const Matcher& rhs_matcher); +Matcher IsNumberMultiply(const Matcher& lhs_matcher, + const Matcher& rhs_matcher); Matcher IsLoadField(const Matcher& access_matcher, const Matcher& base_matcher, const Matcher& effect_matcher, -- 2.7.4