}
+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<SelectParameters>(op);
+}
+
+
size_t hash_value(OutputFrameStateCombine const& sc) {
return base::hash_combine(sc.kind_, sc.parameter_);
}
}
+const Operator* CommonOperatorBuilder::Select(MachineType type,
+ BranchHint hint) {
+ return new (zone())
+ Operator1<SelectParameters>(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<MachineType>(IrOpcode::kPhi, Operator::kPure,
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 {
const Operator* NumberConstant(volatile double);
const Operator* HeapConstant(const Unique<HeapObject>&);
+ const Operator* Select(MachineType, BranchHint = BranchHint::kNone);
const Operator* Phi(MachineType type, int arguments);
const Operator* EffectPhi(int arguments);
const Operator* ValueEffect(int arguments);
case IrOpcode::kLoop:
case IrOpcode::kMerge:
return ReduceMerge(node);
+ case IrOpcode::kSelect:
+ return ReduceSelect(node);
case IrOpcode::kPhi:
case IrOpcode::kEffectPhi:
return ReducePhi(node);
}
}
+ // 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> object =
+ HeapObjectMatcher<Object>(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();
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;
Lower##x(node); \
break;
DECLARE_CASE(Branch)
+ DECLARE_CASE(Select)
JS_OP_LIST(DECLARE_CASE)
#undef DECLARE_CASE
default:
}
+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);
Handle<ExternalArray>::cast(handle(array->elements()));
Node* pointer = jsgraph()->IntPtrConstant(
bit_cast<intptr_t>(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(
Handle<ExternalArray>::cast(handle(array->elements()));
Node* pointer = jsgraph()->IntPtrConstant(
bit_cast<intptr_t>(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(
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
};
typedef IntMatcher<uint32_t, IrOpcode::kInt32Constant> Uint32Matcher;
typedef IntMatcher<int64_t, IrOpcode::kInt64Constant> Int64Matcher;
typedef IntMatcher<uint64_t, IrOpcode::kInt64Constant> 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.
typedef BinopMatcher<Uint32Matcher, Uint32Matcher> Uint32BinopMatcher;
typedef BinopMatcher<Int64Matcher, Int64Matcher> Int64BinopMatcher;
typedef BinopMatcher<Uint64Matcher, Uint64Matcher> Uint64BinopMatcher;
+typedef BinopMatcher<IntPtrMatcher, IntPtrMatcher> IntPtrBinopMatcher;
+typedef BinopMatcher<UintPtrMatcher, UintPtrMatcher> UintPtrBinopMatcher;
typedef BinopMatcher<Float64Matcher, Float64Matcher> Float64BinopMatcher;
typedef BinopMatcher<NumberMatcher, NumberMatcher> NumberBinopMatcher;
V(HeapConstant)
#define INNER_OP_LIST(V) \
+ V(Select) \
V(Phi) \
V(EffectPhi) \
V(ValueEffect) \
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<MachineType>(changer_->TypeFromUpperBound(upper) | output);
+ SetOutput(node, output_type);
+
+ if (lower()) {
+ // Update the select operator.
+ SelectParameters p = SelectParametersOf(node->op());
+ MachineType type = static_cast<MachineType>(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<MachineType>((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) {
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);
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<float>::quiet_NaN();
+ return bit_cast<intptr_t>(&dummy);
+ }
+ case kRepFloat64: {
+ static const double dummy = std::numeric_limits<double>::quiet_NaN();
+ return bit_cast<intptr_t>(&dummy);
+ }
+ case kRepBit:
+ case kRepWord8:
+ case kRepWord16:
+ case kRepWord32: {
+ static const int32_t dummy = 0;
+ return bit_cast<intptr_t>(&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<intptr_t>(&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);
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<float>::quiet_NaN());
- } else if (output_type & kRepFloat64) {
- undefined =
- jsgraph()->Float64Constant(std::numeric_limits<double>::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<float>::quiet_NaN());
+ } else if (output_type & kRepFloat64) {
+ undefined = jsgraph()->Float64Constant(
+ std::numeric_limits<double>::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);
+ }
}
}
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);
+ }
}
}
node->ReplaceInput(1, jsgraph()->SmiConstant(EQUAL));
}
-
} // namespace compiler
} // namespace internal
} // namespace v8
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);
}
+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);
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);
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);
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::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));
}
+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);
TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArray) {
const size_t kLength = 17;
- uint8_t backing_store[kLength * 8];
+ double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
- NewArrayBuffer(backing_store, arraysize(backing_store));
+ NewArrayBuffer(backing_store, sizeof(backing_store));
VectorSlotPair feedback(Handle<TypeFeedbackVector>::null(),
FeedbackVectorICSlot::Invalid());
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
- EXPECT_THAT(
- r.replacement(),
- IsLoadElement(AccessBuilder::ForTypedArrayElement(type, true),
- IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
- key, IsNumberConstant(static_cast<double>(kLength)),
- effect));
+ EXPECT_THAT(r.replacement(),
+ IsLoadElement(
+ AccessBuilder::ForTypedArrayElement(type, true),
+ IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
+ key, IsNumberConstant(array->length()->Number()), effect));
}
}
TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) {
const size_t kLength = 17;
- uint8_t backing_store[kLength * 8];
+ double backing_store[kLength];
Handle<JSArrayBuffer> 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<JSTypedArray> array =
IsStoreElement(
AccessBuilder::ForTypedArrayElement(type, true),
IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
- key, IsNumberConstant(static_cast<double>(kLength)),
- value, effect, control));
+ key, IsNumberConstant(array->length()->Number()), value,
+ effect, control));
}
}
}
};
+class IsSelectMatcher FINAL : public NodeMatcher {
+ public:
+ IsSelectMatcher(const Matcher<MachineType>& type_matcher,
+ const Matcher<Node*>& value0_matcher,
+ const Matcher<Node*>& value1_matcher,
+ const Matcher<Node*>& 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<MachineType>(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<MachineType> type_matcher_;
+ const Matcher<Node*> value0_matcher_;
+ const Matcher<Node*> value1_matcher_;
+ const Matcher<Node*> value2_matcher_;
+};
+
+
class IsPhiMatcher FINAL : public NodeMatcher {
public:
IsPhiMatcher(const Matcher<MachineType>& type_matcher,
}
+Matcher<Node*> IsSelect(const Matcher<MachineType>& type_matcher,
+ const Matcher<Node*>& value0_matcher,
+ const Matcher<Node*>& value1_matcher,
+ const Matcher<Node*>& value2_matcher) {
+ return MakeMatcher(new IsSelectMatcher(type_matcher, value0_matcher,
+ value1_matcher, value2_matcher));
+}
+
+
Matcher<Node*> IsPhi(const Matcher<MachineType>& type_matcher,
const Matcher<Node*>& value0_matcher,
const Matcher<Node*>& value1_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)
Matcher<Node*> IsInt32Constant(const Matcher<int32_t>& value_matcher);
Matcher<Node*> IsInt64Constant(const Matcher<int64_t>& value_matcher);
Matcher<Node*> IsNumberConstant(const Matcher<double>& value_matcher);
+Matcher<Node*> IsSelect(const Matcher<MachineType>& type_matcher,
+ const Matcher<Node*>& value0_matcher,
+ const Matcher<Node*>& value1_matcher,
+ const Matcher<Node*>& value2_matcher);
Matcher<Node*> IsPhi(const Matcher<MachineType>& type_matcher,
const Matcher<Node*>& value0_matcher,
const Matcher<Node*>& value1_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsNumberSubtract(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
+Matcher<Node*> IsNumberMultiply(const Matcher<Node*>& lhs_matcher,
+ const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsLoadField(const Matcher<FieldAccess>& access_matcher,
const Matcher<Node*>& base_matcher,
const Matcher<Node*>& effect_matcher,