diff options
Diffstat (limited to 'deps/v8/src/compiler/js-builtin-reducer.cc')
-rw-r--r-- | deps/v8/src/compiler/js-builtin-reducer.cc | 397 |
1 files changed, 343 insertions, 54 deletions
diff --git a/deps/v8/src/compiler/js-builtin-reducer.cc b/deps/v8/src/compiler/js-builtin-reducer.cc index 926bd3f715..41d4a00166 100644 --- a/deps/v8/src/compiler/js-builtin-reducer.cc +++ b/deps/v8/src/compiler/js-builtin-reducer.cc @@ -10,9 +10,9 @@ #include "src/compiler/node-matchers.h" #include "src/compiler/node-properties.h" #include "src/compiler/simplified-operator.h" +#include "src/compiler/type-cache.h" +#include "src/compiler/types.h" #include "src/objects-inl.h" -#include "src/type-cache.h" -#include "src/types.h" namespace v8 { namespace internal { @@ -275,8 +275,8 @@ Reduction JSBuiltinReducer::ReduceArrayPush(Node* node) { // here is to learn on deopt, i.e. disable Array.prototype.push inlining // for this function. if (IsFastSmiElementsKind(receiver_map->elements_kind())) { - value = effect = graph()->NewNode(simplified()->CheckTaggedSigned(), - value, effect, control); + value = effect = + graph()->NewNode(simplified()->CheckSmi(), value, effect, control); } else if (IsFastDoubleElementsKind(receiver_map->elements_kind())) { value = effect = graph()->NewNode(simplified()->CheckNumber(), value, effect, control); @@ -323,6 +323,123 @@ Reduction JSBuiltinReducer::ReduceArrayPush(Node* node) { return NoChange(); } +namespace { + +bool HasInstanceTypeWitness(Node* receiver, Node* effect, + InstanceType instance_type) { + for (Node* dominator = effect;;) { + if (dominator->opcode() == IrOpcode::kCheckMaps && + dominator->InputAt(0) == receiver) { + // Check if all maps have the given {instance_type}. + for (int i = 1; i < dominator->op()->ValueInputCount(); ++i) { + Node* const map = NodeProperties::GetValueInput(dominator, i); + Type* const map_type = NodeProperties::GetType(map); + if (!map_type->IsConstant()) return false; + Handle<Map> const map_value = + Handle<Map>::cast(map_type->AsConstant()->Value()); + if (map_value->instance_type() != instance_type) return false; + } + return true; + } + switch (dominator->opcode()) { + case IrOpcode::kStoreField: { + FieldAccess const& access = FieldAccessOf(dominator->op()); + if (access.base_is_tagged == kTaggedBase && + access.offset == HeapObject::kMapOffset) { + return false; + } + break; + } + case IrOpcode::kStoreElement: + case IrOpcode::kStoreTypedElement: + break; + default: { + DCHECK_EQ(1, dominator->op()->EffectOutputCount()); + if (dominator->op()->EffectInputCount() != 1 || + !dominator->op()->HasProperty(Operator::kNoWrite)) { + // Didn't find any appropriate CheckMaps node. + return false; + } + break; + } + } + dominator = NodeProperties::GetEffectInput(dominator); + } +} + +} // namespace + +// ES6 section 20.3.4.10 Date.prototype.getTime ( ) +Reduction JSBuiltinReducer::ReduceDateGetTime(Node* node) { + Node* receiver = NodeProperties::GetValueInput(node, 1); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + if (HasInstanceTypeWitness(receiver, effect, JS_DATE_TYPE)) { + Node* value = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForJSDateValue()), receiver, + effect, control); + ReplaceWithValue(node, value, effect, control); + return Replace(value); + } + return NoChange(); +} + +// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] ( V ) +Reduction JSBuiltinReducer::ReduceFunctionHasInstance(Node* node) { + Node* receiver = NodeProperties::GetValueInput(node, 1); + Node* object = (node->op()->ValueInputCount() >= 3) + ? NodeProperties::GetValueInput(node, 2) + : jsgraph()->UndefinedConstant(); + Node* context = NodeProperties::GetContextInput(node); + Node* frame_state = NodeProperties::GetFrameStateInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the + // stack trace doesn't contain the @@hasInstance call; we have the + // corresponding bug in the baseline case. Some massaging of the frame + // state would be necessary here. + + // Morph this {node} into a JSOrdinaryHasInstance node. + node->ReplaceInput(0, receiver); + node->ReplaceInput(1, object); + node->ReplaceInput(2, context); + node->ReplaceInput(3, frame_state); + node->ReplaceInput(4, effect); + node->ReplaceInput(5, control); + node->TrimInputCount(6); + NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); + return Changed(node); +} + +// ES6 section 18.2.2 isFinite ( number ) +Reduction JSBuiltinReducer::ReduceGlobalIsFinite(Node* node) { + JSCallReduction r(node); + if (r.InputsMatchOne(Type::PlainPrimitive())) { + // isFinite(a:plain-primitive) -> NumberEqual(a', a') + // where a' = NumberSubtract(ToNumber(a), ToNumber(a)) + Node* input = ToNumber(r.GetJSCallInput(0)); + Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, input); + Node* value = graph()->NewNode(simplified()->NumberEqual(), diff, diff); + return Replace(value); + } + return NoChange(); +} + +// ES6 section 18.2.3 isNaN ( number ) +Reduction JSBuiltinReducer::ReduceGlobalIsNaN(Node* node) { + JSCallReduction r(node); + if (r.InputsMatchOne(Type::PlainPrimitive())) { + // isNaN(a:plain-primitive) -> BooleanNot(NumberEqual(a', a')) + // where a' = ToNumber(a) + Node* input = ToNumber(r.GetJSCallInput(0)); + Node* check = graph()->NewNode(simplified()->NumberEqual(), input, input); + Node* value = graph()->NewNode(simplified()->BooleanNot(), check); + return Replace(value); + } + return NoChange(); +} + // ES6 section 20.2.2.1 Math.abs ( x ) Reduction JSBuiltinReducer::ReduceMathAbs(Node* node) { JSCallReduction r(node); @@ -737,6 +854,60 @@ Reduction JSBuiltinReducer::ReduceMathTrunc(Node* node) { return NoChange(); } +// ES6 section 20.1.2.2 Number.isFinite ( number ) +Reduction JSBuiltinReducer::ReduceNumberIsFinite(Node* node) { + JSCallReduction r(node); + if (r.InputsMatchOne(Type::Number())) { + // Number.isFinite(a:number) -> NumberEqual(a', a') + // where a' = NumberSubtract(a, a) + Node* input = r.GetJSCallInput(0); + Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, input); + Node* value = graph()->NewNode(simplified()->NumberEqual(), diff, diff); + return Replace(value); + } + return NoChange(); +} + +// ES6 section 20.1.2.3 Number.isInteger ( number ) +Reduction JSBuiltinReducer::ReduceNumberIsInteger(Node* node) { + JSCallReduction r(node); + if (r.InputsMatchOne(Type::Number())) { + // Number.isInteger(x:number) -> NumberEqual(NumberSubtract(x, x'), #0) + // where x' = NumberTrunc(x) + Node* input = r.GetJSCallInput(0); + Node* trunc = graph()->NewNode(simplified()->NumberTrunc(), input); + Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, trunc); + Node* value = graph()->NewNode(simplified()->NumberEqual(), diff, + jsgraph()->ZeroConstant()); + return Replace(value); + } + return NoChange(); +} + +// ES6 section 20.1.2.4 Number.isNaN ( number ) +Reduction JSBuiltinReducer::ReduceNumberIsNaN(Node* node) { + JSCallReduction r(node); + if (r.InputsMatchOne(Type::Number())) { + // Number.isNaN(a:number) -> BooleanNot(NumberEqual(a, a)) + Node* input = r.GetJSCallInput(0); + Node* check = graph()->NewNode(simplified()->NumberEqual(), input, input); + Node* value = graph()->NewNode(simplified()->BooleanNot(), check); + return Replace(value); + } + return NoChange(); +} + +// ES6 section 20.1.2.5 Number.isSafeInteger ( number ) +Reduction JSBuiltinReducer::ReduceNumberIsSafeInteger(Node* node) { + JSCallReduction r(node); + if (r.InputsMatchOne(type_cache_.kSafeInteger)) { + // Number.isInteger(x:safe-integer) -> #true + Node* value = jsgraph()->TrueConstant(); + return Replace(value); + } + return NoChange(); +} + // ES6 section 20.1.2.13 Number.parseInt ( string, radix ) Reduction JSBuiltinReducer::ReduceNumberParseInt(Node* node) { JSCallReduction r(node); @@ -887,51 +1058,146 @@ Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) { return NoChange(); } -namespace { +Reduction JSBuiltinReducer::ReduceStringIteratorNext(Node* node) { + Node* receiver = NodeProperties::GetValueInput(node, 1); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + Node* context = NodeProperties::GetContextInput(node); + if (HasInstanceTypeWitness(receiver, effect, JS_STRING_ITERATOR_TYPE)) { + Node* string = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForJSStringIteratorString()), + receiver, effect, control); + Node* index = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForJSStringIteratorIndex()), + receiver, effect, control); + Node* length = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForStringLength()), string, + effect, control); -bool HasInstanceTypeWitness(Node* receiver, Node* effect, - InstanceType instance_type) { - for (Node* dominator = effect;;) { - if (dominator->opcode() == IrOpcode::kCheckMaps && - dominator->InputAt(0) == receiver) { - // Check if all maps have the given {instance_type}. - for (int i = 1; i < dominator->op()->ValueInputCount(); ++i) { - Node* const map = NodeProperties::GetValueInput(dominator, i); - Type* const map_type = NodeProperties::GetType(map); - if (!map_type->IsConstant()) return false; - Handle<Map> const map_value = - Handle<Map>::cast(map_type->AsConstant()->Value()); - if (map_value->instance_type() != instance_type) return false; - } - return true; - } - switch (dominator->opcode()) { - case IrOpcode::kStoreField: { - FieldAccess const& access = FieldAccessOf(dominator->op()); - if (access.base_is_tagged == kTaggedBase && - access.offset == HeapObject::kMapOffset) { - return false; - } - break; - } - case IrOpcode::kStoreElement: - break; - default: { - DCHECK_EQ(1, dominator->op()->EffectOutputCount()); - if (dominator->op()->EffectInputCount() != 1 || - !dominator->op()->HasProperty(Operator::kNoWrite)) { - // Didn't find any appropriate CheckMaps node. - return false; + // branch0: if (index < length) + Node* check0 = + graph()->NewNode(simplified()->NumberLessThan(), index, length); + Node* branch0 = + graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control); + + Node* etrue0 = effect; + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); + Node* done_true; + Node* vtrue0; + { + done_true = jsgraph()->FalseConstant(); + Node* lead = graph()->NewNode(simplified()->StringCharCodeAt(), string, + index, if_true0); + + // branch1: if ((lead & 0xFC00) === 0xD800) + Node* check1 = graph()->NewNode( + simplified()->NumberEqual(), + graph()->NewNode(simplified()->NumberBitwiseAnd(), lead, + jsgraph()->Int32Constant(0xFC00)), + jsgraph()->Int32Constant(0xD800)); + Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), + check1, if_true0); + Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); + Node* vtrue1; + { + Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index, + jsgraph()->OneConstant()); + // branch2: if ((index + 1) < length) + Node* check2 = graph()->NewNode(simplified()->NumberLessThan(), + next_index, length); + Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kTrue), + check2, if_true1); + Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); + Node* vtrue2; + { + Node* trail = graph()->NewNode(simplified()->StringCharCodeAt(), + string, next_index, if_true2); + // branch3: if ((trail & 0xFC00) === 0xDC00) + Node* check3 = graph()->NewNode( + simplified()->NumberEqual(), + graph()->NewNode(simplified()->NumberBitwiseAnd(), trail, + jsgraph()->Int32Constant(0xFC00)), + jsgraph()->Int32Constant(0xDC00)); + Node* branch3 = graph()->NewNode(common()->Branch(BranchHint::kTrue), + check3, if_true2); + Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3); + Node* vtrue3; + { + vtrue3 = graph()->NewNode( + simplified()->NumberBitwiseOr(), +// Need to swap the order for big-endian platforms +#if V8_TARGET_BIG_ENDIAN + graph()->NewNode(simplified()->NumberShiftLeft(), lead, + jsgraph()->Int32Constant(16)), + trail); +#else + graph()->NewNode(simplified()->NumberShiftLeft(), trail, + jsgraph()->Int32Constant(16)), + lead); +#endif + } + + Node* if_false3 = graph()->NewNode(common()->IfFalse(), branch3); + Node* vfalse3 = lead; + if_true2 = graph()->NewNode(common()->Merge(2), if_true3, if_false3); + vtrue2 = + graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2), + vtrue3, vfalse3, if_true2); } - break; + + Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2); + Node* vfalse2 = lead; + if_true1 = graph()->NewNode(common()->Merge(2), if_true2, if_false2); + vtrue1 = + graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2), + vtrue2, vfalse2, if_true1); } + + Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); + Node* vfalse1 = lead; + if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); + vtrue0 = + graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2), + vtrue1, vfalse1, if_true0); + vtrue0 = graph()->NewNode( + simplified()->StringFromCodePoint(UnicodeEncoding::UTF16), vtrue0); + + // Update iterator.[[NextIndex]] + Node* char_length = etrue0 = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForStringLength()), vtrue0, + etrue0, if_true0); + index = graph()->NewNode(simplified()->NumberAdd(), index, char_length); + etrue0 = graph()->NewNode( + simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()), + receiver, index, etrue0, if_true0); } - dominator = NodeProperties::GetEffectInput(dominator); + + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); + Node* done_false; + Node* vfalse0; + { + vfalse0 = jsgraph()->UndefinedConstant(); + done_false = jsgraph()->TrueConstant(); + } + + control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); + effect = graph()->NewNode(common()->EffectPhi(2), etrue0, effect, control); + Node* value = + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + vtrue0, vfalse0, control); + Node* done = + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + done_true, done_false, control); + + value = effect = graph()->NewNode(javascript()->CreateIterResultObject(), + value, done, context, effect); + + ReplaceWithValue(node, value, effect, control); + return Replace(value); } + return NoChange(); } -} // namespace - Reduction JSBuiltinReducer::ReduceArrayBufferViewAccessor( Node* node, InstanceType instance_type, FieldAccess const& access) { Node* receiver = NodeProperties::GetValueInput(node, 1); @@ -939,27 +1205,21 @@ Reduction JSBuiltinReducer::ReduceArrayBufferViewAccessor( Node* control = NodeProperties::GetControlInput(node); if (HasInstanceTypeWitness(receiver, effect, instance_type)) { // Load the {receiver}s field. - Node* receiver_length = effect = graph()->NewNode( + Node* receiver_value = effect = graph()->NewNode( simplified()->LoadField(access), receiver, effect, control); // Check if the {receiver}s buffer was neutered. Node* receiver_buffer = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), receiver, effect, control); - Node* receiver_buffer_bitfield = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()), - receiver_buffer, effect, control); - Node* check = graph()->NewNode( - simplified()->NumberEqual(), - graph()->NewNode( - simplified()->NumberBitwiseAnd(), receiver_buffer_bitfield, - jsgraph()->Constant(JSArrayBuffer::WasNeutered::kMask)), - jsgraph()->ZeroConstant()); + Node* check = effect = + graph()->NewNode(simplified()->ArrayBufferWasNeutered(), + receiver_buffer, effect, control); // Default to zero if the {receiver}s buffer was neutered. Node* value = graph()->NewNode( - common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue), - check, receiver_length, jsgraph()->ZeroConstant()); + common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), + check, jsgraph()->ZeroConstant(), receiver_value); ReplaceWithValue(node, value, effect, control); return Replace(value); @@ -978,6 +1238,17 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { return ReduceArrayPop(node); case kArrayPush: return ReduceArrayPush(node); + case kDateGetTime: + return ReduceDateGetTime(node); + case kFunctionHasInstance: + return ReduceFunctionHasInstance(node); + break; + case kGlobalIsFinite: + reduction = ReduceGlobalIsFinite(node); + break; + case kGlobalIsNaN: + reduction = ReduceGlobalIsNaN(node); + break; case kMathAbs: reduction = ReduceMathAbs(node); break; @@ -1077,6 +1348,18 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { case kMathTrunc: reduction = ReduceMathTrunc(node); break; + case kNumberIsFinite: + reduction = ReduceNumberIsFinite(node); + break; + case kNumberIsInteger: + reduction = ReduceNumberIsInteger(node); + break; + case kNumberIsNaN: + reduction = ReduceNumberIsNaN(node); + break; + case kNumberIsSafeInteger: + reduction = ReduceNumberIsSafeInteger(node); + break; case kNumberParseInt: reduction = ReduceNumberParseInt(node); break; @@ -1087,6 +1370,8 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { return ReduceStringCharAt(node); case kStringCharCodeAt: return ReduceStringCharCodeAt(node); + case kStringIteratorNext: + return ReduceStringIteratorNext(node); case kDataViewByteLength: return ReduceArrayBufferViewAccessor( node, JS_DATA_VIEW_TYPE, @@ -1146,6 +1431,10 @@ SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const { return jsgraph()->simplified(); } +JSOperatorBuilder* JSBuiltinReducer::javascript() const { + return jsgraph()->javascript(); +} + } // namespace compiler } // namespace internal } // namespace v8 |