diff options
Diffstat (limited to 'deps/v8/src/compiler/js-typed-lowering.cc')
-rw-r--r-- | deps/v8/src/compiler/js-typed-lowering.cc | 1377 |
1 files changed, 967 insertions, 410 deletions
diff --git a/deps/v8/src/compiler/js-typed-lowering.cc b/deps/v8/src/compiler/js-typed-lowering.cc index f221577104..5e0712a7f1 100644 --- a/deps/v8/src/compiler/js-typed-lowering.cc +++ b/deps/v8/src/compiler/js-typed-lowering.cc @@ -12,31 +12,14 @@ #include "src/compiler/node-properties.h" #include "src/compiler/operator-properties.h" #include "src/compiler/state-values-utils.h" +#include "src/type-cache.h" #include "src/types.h" namespace v8 { namespace internal { namespace compiler { -// TODO(turbofan): js-typed-lowering improvements possible -// - immediately put in type bounds for all new nodes -// - relax effects from generic but not-side-effecting operations - - -JSTypedLowering::JSTypedLowering(Editor* editor, - CompilationDependencies* dependencies, - Flags flags, JSGraph* jsgraph, Zone* zone) - : AdvancedReducer(editor), - dependencies_(dependencies), - flags_(flags), - jsgraph_(jsgraph) { - for (size_t k = 0; k < arraysize(shifted_int32_ranges_); ++k) { - double min = kMinInt / (1 << k); - double max = kMaxInt / (1 << k); - shifted_int32_ranges_[k] = Type::Range(min, max, graph()->zone()); - } -} - +namespace { // A helper class to construct inline allocations on the simplified operator // level. This keeps track of the effect chain for initial stores on a newly @@ -50,10 +33,11 @@ class AllocationBuilder final { control_(control) {} // Primitive allocation of static size. - void Allocate(int size) { + void Allocate(int size, PretenureFlag pretenure = NOT_TENURED) { effect_ = graph()->NewNode(common()->BeginRegion(), effect_); - allocation_ = graph()->NewNode( - simplified()->Allocate(), jsgraph()->Constant(size), effect_, control_); + allocation_ = + graph()->NewNode(simplified()->Allocate(pretenure), + jsgraph()->Constant(size), effect_, control_); effect_ = allocation_; } @@ -63,9 +47,21 @@ class AllocationBuilder final { value, effect_, control_); } + // Primitive store into an element. + void Store(ElementAccess const& access, Node* index, Node* value) { + effect_ = graph()->NewNode(simplified()->StoreElement(access), allocation_, + index, value, effect_, control_); + } + // Compound allocation of a FixedArray. - void AllocateArray(int length, Handle<Map> map) { - Allocate(FixedArray::SizeFor(length)); + void AllocateArray(int length, Handle<Map> map, + PretenureFlag pretenure = NOT_TENURED) { + DCHECK(map->instance_type() == FIXED_ARRAY_TYPE || + map->instance_type() == FIXED_DOUBLE_ARRAY_TYPE); + int size = (map->instance_type() == FIXED_ARRAY_TYPE) + ? FixedArray::SizeFor(length) + : FixedDoubleArray::SizeFor(length); + Allocate(size, pretenure); Store(AccessBuilder::ForMap(), map); Store(AccessBuilder::ForFixedArrayLength(), jsgraph()->Constant(length)); } @@ -100,6 +96,8 @@ class AllocationBuilder final { Node* control_; }; +} // namespace + // A helper class to simplify the process of reducing a single binop node with a // JSOperator. This class manages the rewriting of context, control, and effect @@ -220,7 +218,16 @@ class JSBinopReduction final { return ChangeToPureOperator(op, false, type); } - bool IsStrong() { return is_strong(OpParameter<LanguageMode>(node_)); } + // TODO(turbofan): Strong mode should be killed soonish! + bool IsStrong() const { + if (node_->opcode() == IrOpcode::kJSLessThan || + node_->opcode() == IrOpcode::kJSLessThanOrEqual || + node_->opcode() == IrOpcode::kJSGreaterThan || + node_->opcode() == IrOpcode::kJSGreaterThanOrEqual) { + return is_strong(OpParameter<LanguageMode>(node_)); + } + return is_strong(BinaryOperationParametersOf(node_->op()).language_mode()); + } bool LeftInputIs(Type* t) { return left_type()->Is(t); } @@ -377,8 +384,8 @@ class JSBinopReduction final { // Wire conversions to existing {IfException} continuation. Node* exception_merge = if_exception; Node* exception_value = - graph()->NewNode(common()->Phi(kMachAnyTagged, 2), left_exception, - right_exception, exception_merge); + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + left_exception, right_exception, exception_merge); Node* exception_effect = graph()->NewNode(common()->EffectPhi(2), left_exception, right_exception, exception_merge); @@ -417,7 +424,34 @@ class JSBinopReduction final { }; +// TODO(turbofan): js-typed-lowering improvements possible +// - immediately put in type bounds for all new nodes +// - relax effects from generic but not-side-effecting operations + + +JSTypedLowering::JSTypedLowering(Editor* editor, + CompilationDependencies* dependencies, + Flags flags, JSGraph* jsgraph, Zone* zone) + : AdvancedReducer(editor), + dependencies_(dependencies), + flags_(flags), + jsgraph_(jsgraph), + true_type_(Type::Constant(factory()->true_value(), graph()->zone())), + false_type_(Type::Constant(factory()->false_value(), graph()->zone())), + the_hole_type_( + Type::Constant(factory()->the_hole_value(), graph()->zone())), + type_cache_(TypeCache::Get()) { + for (size_t k = 0; k < arraysize(shifted_int32_ranges_); ++k) { + double min = kMinInt / (1 << k); + double max = kMaxInt / (1 << k); + shifted_int32_ranges_[k] = Type::Range(min, max, graph()->zone()); + } +} + + Reduction JSTypedLowering::ReduceJSAdd(Node* node) { + if (flags() & kDisableBinaryOpReduction) return NoChange(); + JSBinopReduction r(this, node); if (r.BothInputsAre(Type::Number())) { // JSAdd(x:number, y:number) => NumberAdd(x, y) @@ -448,6 +482,8 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) { Reduction JSTypedLowering::ReduceJSModulus(Node* node) { + if (flags() & kDisableBinaryOpReduction) return NoChange(); + JSBinopReduction r(this, node); if (r.BothInputsAre(Type::Number())) { // JSModulus(x:number, x:number) => NumberModulus(x, y) @@ -460,6 +496,8 @@ Reduction JSTypedLowering::ReduceJSModulus(Node* node) { Reduction JSTypedLowering::ReduceNumberBinop(Node* node, const Operator* numberOp) { + if (flags() & kDisableBinaryOpReduction) return NoChange(); + JSBinopReduction r(this, node); if (r.IsStrong() || numberOp == simplified()->NumberModulus()) { if (r.BothInputsAre(Type::Number())) { @@ -474,6 +512,8 @@ Reduction JSTypedLowering::ReduceNumberBinop(Node* node, Reduction JSTypedLowering::ReduceInt32Binop(Node* node, const Operator* intOp) { + if (flags() & kDisableBinaryOpReduction) return NoChange(); + JSBinopReduction r(this, node); if (r.IsStrong()) { if (r.BothInputsAre(Type::Number())) { @@ -492,6 +532,8 @@ Reduction JSTypedLowering::ReduceInt32Binop(Node* node, const Operator* intOp) { Reduction JSTypedLowering::ReduceUI32Shift(Node* node, Signedness left_signedness, const Operator* shift_op) { + if (flags() & kDisableBinaryOpReduction) return NoChange(); + JSBinopReduction r(this, node); if (r.IsStrong()) { if (r.BothInputsAre(Type::Number())) { @@ -508,6 +550,8 @@ Reduction JSTypedLowering::ReduceUI32Shift(Node* node, Reduction JSTypedLowering::ReduceJSComparison(Node* node) { + if (flags() & kDisableBinaryOpReduction) return NoChange(); + JSBinopReduction r(this, node); if (r.BothInputsAre(Type::String())) { // If both inputs are definitely strings, perform a string comparison. @@ -579,6 +623,8 @@ Reduction JSTypedLowering::ReduceJSComparison(Node* node) { Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) { + if (flags() & kDisableBinaryOpReduction) return NoChange(); + JSBinopReduction r(this, node); if (r.BothInputsAre(Type::Number())) { @@ -620,6 +666,8 @@ Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) { Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) { + if (flags() & kDisableBinaryOpReduction) return NoChange(); + JSBinopReduction r(this, node); if (r.left() == r.right()) { // x === x is always true if x != NaN @@ -638,6 +686,10 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) { return Replace(replacement); } } + if (r.OneInputIs(the_hole_type_)) { + return r.ChangeToPureOperator(simplified()->ReferenceEqual(the_hole_type_), + invert); + } if (r.OneInputIs(Type::Undefined())) { return r.ChangeToPureOperator( simplified()->ReferenceEqual(Type::Undefined()), invert); @@ -674,40 +726,6 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) { } -Reduction JSTypedLowering::ReduceJSUnaryNot(Node* node) { - Node* const input = node->InputAt(0); - Type* const input_type = NodeProperties::GetType(input); - if (input_type->Is(Type::Boolean())) { - // JSUnaryNot(x:boolean) => BooleanNot(x) - RelaxEffectsAndControls(node); - node->TrimInputCount(1); - NodeProperties::ChangeOp(node, simplified()->BooleanNot()); - return Changed(node); - } else if (input_type->Is(Type::OrderedNumber())) { - // JSUnaryNot(x:number) => NumberEqual(x,#0) - RelaxEffectsAndControls(node); - node->ReplaceInput(1, jsgraph()->ZeroConstant()); - node->TrimInputCount(2); - NodeProperties::ChangeOp(node, simplified()->NumberEqual()); - return Changed(node); - } else if (input_type->Is(Type::String())) { - // JSUnaryNot(x:string) => NumberEqual(x.length,#0) - FieldAccess const access = AccessBuilder::ForStringLength(); - // It is safe for the load to be effect-free (i.e. not linked into effect - // chain) because we assume String::length to be immutable. - Node* length = graph()->NewNode(simplified()->LoadField(access), input, - graph()->start(), graph()->start()); - ReplaceWithValue(node, node, length); - node->ReplaceInput(0, length); - node->ReplaceInput(1, jsgraph()->ZeroConstant()); - node->TrimInputCount(2); - NodeProperties::ChangeOp(node, simplified()->NumberEqual()); - return Changed(node); - } - return NoChange(); -} - - Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) { Node* const input = node->InputAt(0); Type* const input_type = NodeProperties::GetType(input); @@ -747,6 +765,21 @@ Reduction JSTypedLowering::ReduceJSToNumberInput(Node* input) { if (result.Changed()) return result; return Changed(input); // JSToNumber(JSToNumber(x)) => JSToNumber(x) } + // Check for ToNumber truncation of signaling NaN to undefined mapping. + if (input->opcode() == IrOpcode::kSelect) { + Node* check = NodeProperties::GetValueInput(input, 0); + Node* vtrue = NodeProperties::GetValueInput(input, 1); + Type* vtrue_type = NodeProperties::GetType(vtrue); + Node* vfalse = NodeProperties::GetValueInput(input, 2); + Type* vfalse_type = NodeProperties::GetType(vfalse); + if (vtrue_type->Is(Type::Undefined()) && vfalse_type->Is(Type::Number())) { + if (check->opcode() == IrOpcode::kNumberIsHoleNaN && + check->InputAt(0) == vfalse) { + // JSToNumber(Select(NumberIsHoleNaN(x), y:undefined, x:number)) => x + return Replace(vfalse); + } + } + } // Check if we have a cached conversion. Type* input_type = NodeProperties::GetType(input); if (input_type->Is(Type::Number())) { @@ -812,10 +845,10 @@ Reduction JSTypedLowering::ReduceJSToStringInput(Node* input) { return Changed(input); // JSToString(x:string) => x } if (input_type->Is(Type::Boolean())) { - return Replace( - graph()->NewNode(common()->Select(kMachAnyTagged), input, - jsgraph()->HeapConstant(factory()->true_string()), - jsgraph()->HeapConstant(factory()->false_string()))); + return Replace(graph()->NewNode( + common()->Select(MachineRepresentation::kTagged), input, + jsgraph()->HeapConstant(factory()->true_string()), + jsgraph()->HeapConstant(factory()->false_string()))); } if (input_type->Is(Type::Undefined())) { return Replace(jsgraph()->HeapConstant(factory()->undefined_string())); @@ -911,8 +944,9 @@ Reduction JSTypedLowering::ReduceJSToObject(Node* node) { control = graph()->NewNode(common()->Merge(2), if_convert, if_done); effect = graph()->NewNode(common()->EffectPhi(2), econvert, edone, control); - receiver = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), rconvert, - rdone, control); + receiver = + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + rconvert, rdone, control); } ReplaceWithValue(node, receiver, effect, control); return Changed(receiver); @@ -935,6 +969,27 @@ Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) { ReplaceWithValue(node, value, effect); return Replace(value); } + // Optimize "prototype" property of functions. + if (name.is_identical_to(factory()->prototype_string()) && + receiver_type->IsConstant() && + receiver_type->AsConstant()->Value()->IsJSFunction()) { + // TODO(turbofan): This lowering might not kick in if we ever lower + // the C++ accessor for "prototype" in an earlier optimization pass. + Handle<JSFunction> function = + Handle<JSFunction>::cast(receiver_type->AsConstant()->Value()); + if (function->has_initial_map()) { + // We need to add a code dependency on the initial map of the {function} + // in order to be notified about changes to the "prototype" of {function}, + // so it doesn't make sense to continue unless deoptimization is enabled. + if (!(flags() & kDeoptimizationEnabled)) return NoChange(); + Handle<Map> initial_map(function->initial_map(), isolate()); + dependencies()->AssumeInitialMapCantChange(initial_map); + Node* value = + jsgraph()->Constant(handle(initial_map->prototype(), isolate())); + ReplaceWithValue(node, value); + return Replace(value); + } + } return NoChange(); } @@ -950,7 +1005,8 @@ Reduction JSTypedLowering::ReduceJSLoadProperty(Node* node) { if (!array->GetBuffer()->was_neutered()) { array->GetBuffer()->set_is_neuterable(false); BufferAccess const access(array->type()); - size_t const k = ElementSizeLog2Of(access.machine_type()); + size_t const k = + ElementSizeLog2Of(access.machine_type().representation()); double const byte_length = array->byte_length()->Number(); CHECK_LT(k, arraysize(shifted_int32_ranges_)); if (key_type->Is(shifted_int32_ranges_[k]) && byte_length <= kMaxInt) { @@ -996,7 +1052,8 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) { if (!array->GetBuffer()->was_neutered()) { array->GetBuffer()->set_is_neuterable(false); BufferAccess const access(array->type()); - size_t const k = ElementSizeLog2Of(access.machine_type()); + size_t const k = + ElementSizeLog2Of(access.machine_type().representation()); double const byte_length = array->byte_length()->Number(); CHECK_LT(k, arraysize(shifted_int32_ranges_)); if (access.external_array_type() != kExternalUint8ClampedArray && @@ -1022,14 +1079,6 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) { frame_state_for_to_number, effect, control); } } - // For integer-typed arrays, convert to the integer type. - if (TypeOf(access.machine_type()) == kTypeInt32 && - !value_type->Is(Type::Signed32())) { - value = graph()->NewNode(simplified()->NumberToInt32(), value); - } else if (TypeOf(access.machine_type()) == kTypeUint32 && - !value_type->Is(Type::Unsigned32())) { - value = graph()->NewNode(simplified()->NumberToUint32(), value); - } // Check if we can avoid the bounds check. if (key_type->Min() >= 0 && key_type->Max() < array->length_value()) { RelaxControls(node); @@ -1067,126 +1116,185 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) { Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) { DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode()); + Node* const context = NodeProperties::GetContextInput(node); + Node* const frame_state = NodeProperties::GetFrameStateInput(node, 0); // If deoptimization is disabled, we cannot optimize. - if (!(flags() & kDeoptimizationEnabled)) return NoChange(); + if (!(flags() & kDeoptimizationEnabled) || + (flags() & kDisableBinaryOpReduction)) { + return NoChange(); + } + + // If we are in a try block, don't optimize since the runtime call + // in the proxy case can throw. + if (NodeProperties::IsExceptionalCall(node)) return NoChange(); JSBinopReduction r(this, node); Node* effect = r.effect(); Node* control = r.control(); - if (r.right_type()->IsConstant() && - r.right_type()->AsConstant()->Value()->IsJSFunction()) { - Handle<JSFunction> function = - Handle<JSFunction>::cast(r.right_type()->AsConstant()->Value()); - Handle<SharedFunctionInfo> shared(function->shared(), isolate()); - if (!function->map()->has_non_instance_prototype()) { - JSFunction::EnsureHasInitialMap(function); - DCHECK(function->has_initial_map()); - Handle<Map> initial_map(function->initial_map(), isolate()); - this->dependencies()->AssumeInitialMapCantChange(initial_map); - Node* prototype = - jsgraph()->Constant(handle(initial_map->prototype(), isolate())); + if (!r.right_type()->IsConstant() || + !r.right_type()->AsConstant()->Value()->IsJSFunction()) { + return NoChange(); + } - Node* if_is_smi = nullptr; - Node* e_is_smi = nullptr; - // If the left hand side is an object, no smi check is needed. - if (r.left_type()->Maybe(Type::TaggedSigned())) { - Node* is_smi = graph()->NewNode(simplified()->ObjectIsSmi(), r.left()); - Node* branch_is_smi = graph()->NewNode( - common()->Branch(BranchHint::kFalse), is_smi, control); - if_is_smi = graph()->NewNode(common()->IfTrue(), branch_is_smi); - e_is_smi = effect; - control = graph()->NewNode(common()->IfFalse(), branch_is_smi); - } + Handle<JSFunction> function = + Handle<JSFunction>::cast(r.right_type()->AsConstant()->Value()); + Handle<SharedFunctionInfo> shared(function->shared(), isolate()); - Node* object_map = effect = - graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), - r.left(), effect, control); - - // Loop through the {object}s prototype chain looking for the {prototype}. - Node* loop = control = - graph()->NewNode(common()->Loop(2), control, control); - - Node* loop_effect = effect = - graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); - - Node* loop_object_map = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), - object_map, r.left(), loop); - - - Node* object_prototype = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForMapPrototype()), - loop_object_map, loop_effect, control); - - // Check if object prototype is equal to function prototype. - Node* eq_proto = - graph()->NewNode(simplified()->ReferenceEqual(r.right_type()), - object_prototype, prototype); - Node* branch_eq_proto = graph()->NewNode( - common()->Branch(BranchHint::kFalse), eq_proto, control); - Node* if_eq_proto = graph()->NewNode(common()->IfTrue(), branch_eq_proto); - Node* e_eq_proto = effect; - - control = graph()->NewNode(common()->IfFalse(), branch_eq_proto); - - // If not, check if object prototype is the null prototype. - Node* null_proto = - graph()->NewNode(simplified()->ReferenceEqual(r.right_type()), - object_prototype, jsgraph()->NullConstant()); - Node* branch_null_proto = graph()->NewNode( - common()->Branch(BranchHint::kFalse), null_proto, control); - Node* if_null_proto = - graph()->NewNode(common()->IfTrue(), branch_null_proto); - Node* e_null_proto = effect; - - control = graph()->NewNode(common()->IfFalse(), branch_null_proto); - Node* load_object_map = effect = - graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), - object_prototype, effect, control); - // Close the loop. - loop_effect->ReplaceInput(1, effect); - loop_object_map->ReplaceInput(1, load_object_map); - loop->ReplaceInput(1, control); - - control = - graph()->NewNode(common()->Merge(2), if_eq_proto, if_null_proto); - effect = graph()->NewNode(common()->EffectPhi(2), e_eq_proto, - e_null_proto, control); - - - Node* result = graph()->NewNode(common()->Phi(kTypeBool, 2), - jsgraph()->TrueConstant(), - jsgraph()->FalseConstant(), control); - - if (if_is_smi != nullptr) { - DCHECK(e_is_smi != nullptr); - control = graph()->NewNode(common()->Merge(2), if_is_smi, control); - effect = - graph()->NewNode(common()->EffectPhi(2), e_is_smi, effect, control); - result = graph()->NewNode(common()->Phi(kTypeBool, 2), - jsgraph()->FalseConstant(), result, control); - } - ReplaceWithValue(node, result, effect, control); - return Changed(result); - } + if (!function->IsConstructor() || + function->map()->has_non_instance_prototype()) { + return NoChange(); } - return NoChange(); + JSFunction::EnsureHasInitialMap(function); + DCHECK(function->has_initial_map()); + Handle<Map> initial_map(function->initial_map(), isolate()); + this->dependencies()->AssumeInitialMapCantChange(initial_map); + Node* prototype = + jsgraph()->Constant(handle(initial_map->prototype(), isolate())); + + Node* if_is_smi = nullptr; + Node* e_is_smi = nullptr; + // If the left hand side is an object, no smi check is needed. + if (r.left_type()->Maybe(Type::TaggedSigned())) { + Node* is_smi = graph()->NewNode(simplified()->ObjectIsSmi(), r.left()); + Node* branch_is_smi = + graph()->NewNode(common()->Branch(BranchHint::kFalse), is_smi, control); + if_is_smi = graph()->NewNode(common()->IfTrue(), branch_is_smi); + e_is_smi = effect; + control = graph()->NewNode(common()->IfFalse(), branch_is_smi); + } + + Node* object_map = effect = + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), + r.left(), effect, control); + + // Loop through the {object}s prototype chain looking for the {prototype}. + Node* loop = control = graph()->NewNode(common()->Loop(2), control, control); + + Node* loop_effect = effect = + graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); + + Node* loop_object_map = + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + object_map, r.left(), loop); + + // Check if the lhs needs access checks. + Node* map_bit_field = effect = + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMapBitField()), + loop_object_map, loop_effect, control); + int is_access_check_needed_bit = 1 << Map::kIsAccessCheckNeeded; + Node* is_access_check_needed_num = + graph()->NewNode(simplified()->NumberBitwiseAnd(), map_bit_field, + jsgraph()->Uint32Constant(is_access_check_needed_bit)); + Node* is_access_check_needed = + graph()->NewNode(machine()->Word32Equal(), is_access_check_needed_num, + jsgraph()->Uint32Constant(is_access_check_needed_bit)); + + Node* branch_is_access_check_needed = graph()->NewNode( + common()->Branch(BranchHint::kFalse), is_access_check_needed, control); + Node* if_is_access_check_needed = + graph()->NewNode(common()->IfTrue(), branch_is_access_check_needed); + Node* e_is_access_check_needed = effect; + + control = + graph()->NewNode(common()->IfFalse(), branch_is_access_check_needed); + + // Check if the lhs is a proxy. + Node* map_instance_type = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForMapInstanceType()), + loop_object_map, loop_effect, control); + Node* is_proxy = graph()->NewNode(machine()->Word32Equal(), map_instance_type, + jsgraph()->Uint32Constant(JS_PROXY_TYPE)); + Node* branch_is_proxy = + graph()->NewNode(common()->Branch(BranchHint::kFalse), is_proxy, control); + Node* if_is_proxy = graph()->NewNode(common()->IfTrue(), branch_is_proxy); + Node* e_is_proxy = effect; + + + Node* runtime_has_in_proto_chain = control = graph()->NewNode( + common()->Merge(2), if_is_access_check_needed, if_is_proxy); + effect = graph()->NewNode(common()->EffectPhi(2), e_is_access_check_needed, + e_is_proxy, control); + + // If we need an access check or the object is a Proxy, make a runtime call + // to finish the lowering. + Node* bool_result_runtime_has_in_proto_chain_case = graph()->NewNode( + javascript()->CallRuntime(Runtime::kHasInPrototypeChain, 2), r.left(), + prototype, context, frame_state, effect, control); + + control = graph()->NewNode(common()->IfFalse(), branch_is_proxy); + + Node* object_prototype = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForMapPrototype()), + loop_object_map, loop_effect, control); + + // Check if object prototype is equal to function prototype. + Node* eq_proto = + graph()->NewNode(simplified()->ReferenceEqual(r.right_type()), + object_prototype, prototype); + Node* branch_eq_proto = + graph()->NewNode(common()->Branch(BranchHint::kFalse), eq_proto, control); + Node* if_eq_proto = graph()->NewNode(common()->IfTrue(), branch_eq_proto); + Node* e_eq_proto = effect; + + control = graph()->NewNode(common()->IfFalse(), branch_eq_proto); + + // If not, check if object prototype is the null prototype. + Node* null_proto = + graph()->NewNode(simplified()->ReferenceEqual(r.right_type()), + object_prototype, jsgraph()->NullConstant()); + Node* branch_null_proto = graph()->NewNode( + common()->Branch(BranchHint::kFalse), null_proto, control); + Node* if_null_proto = graph()->NewNode(common()->IfTrue(), branch_null_proto); + Node* e_null_proto = effect; + + control = graph()->NewNode(common()->IfFalse(), branch_null_proto); + Node* load_object_map = effect = + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), + object_prototype, effect, control); + // Close the loop. + loop_effect->ReplaceInput(1, effect); + loop_object_map->ReplaceInput(1, load_object_map); + loop->ReplaceInput(1, control); + + control = graph()->NewNode(common()->Merge(3), runtime_has_in_proto_chain, + if_eq_proto, if_null_proto); + effect = graph()->NewNode(common()->EffectPhi(3), + bool_result_runtime_has_in_proto_chain_case, + e_eq_proto, e_null_proto, control); + + Node* result = graph()->NewNode( + common()->Phi(MachineRepresentation::kTagged, 3), + bool_result_runtime_has_in_proto_chain_case, jsgraph()->TrueConstant(), + jsgraph()->FalseConstant(), control); + + if (if_is_smi != nullptr) { + DCHECK_NOT_NULL(e_is_smi); + control = graph()->NewNode(common()->Merge(2), if_is_smi, control); + effect = + graph()->NewNode(common()->EffectPhi(2), e_is_smi, effect, control); + result = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + jsgraph()->FalseConstant(), result, control); + } + + ReplaceWithValue(node, result, effect, control); + return Changed(result); } Reduction JSTypedLowering::ReduceJSLoadContext(Node* node) { DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); ContextAccess const& access = ContextAccessOf(node->op()); - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = graph()->start(); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = graph()->start(); for (size_t i = 0; i < access.depth(); ++i) { - node->ReplaceInput( - 0, graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX)), - NodeProperties::GetValueInput(node, 0), effect, control)); + Node* previous = effect = graph()->NewNode( + simplified()->LoadField( + AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX)), + NodeProperties::GetValueInput(node, 0), effect, control); + node->ReplaceInput(0, previous); } node->ReplaceInput(1, effect); node->ReplaceInput(2, control); @@ -1200,16 +1308,17 @@ Reduction JSTypedLowering::ReduceJSLoadContext(Node* node) { Reduction JSTypedLowering::ReduceJSStoreContext(Node* node) { DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode()); ContextAccess const& access = ContextAccessOf(node->op()); - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = graph()->start(); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = graph()->start(); for (size_t i = 0; i < access.depth(); ++i) { - node->ReplaceInput( - 0, graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX)), - NodeProperties::GetValueInput(node, 0), effect, control)); + Node* previous = effect = graph()->NewNode( + simplified()->LoadField( + AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX)), + NodeProperties::GetValueInput(node, 0), effect, control); + node->ReplaceInput(0, previous); } node->RemoveInput(2); + node->ReplaceInput(2, effect); NodeProperties::ChangeOp( node, simplified()->StoreField(AccessBuilder::ForContextSlot(access.index()))); @@ -1237,13 +1346,12 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) { isolate()); receiver = jsgraph()->Constant(global_proxy); } else { - Node* global_object = effect = graph()->NewNode( - javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true), + Node* native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), context, context, effect); - receiver = effect = - graph()->NewNode(simplified()->LoadField( - AccessBuilder::ForJSGlobalObjectGlobalProxy()), - global_object, effect, control); + receiver = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::GLOBAL_PROXY_INDEX, true), + native_context, native_context, effect); } } else if (!receiver_type->Maybe(Type::NullOrUndefined()) || mode == ConvertReceiverMode::kNotNullOrUndefined) { @@ -1292,21 +1400,21 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) { isolate()); rglobal = jsgraph()->Constant(global_proxy); } else { - Node* global_object = eglobal = graph()->NewNode( - javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true), + Node* native_context = eglobal = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), context, context, eglobal); rglobal = eglobal = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForJSGlobalObjectGlobalProxy()), - global_object, eglobal, if_global); + javascript()->LoadContext(0, Context::GLOBAL_PROXY_INDEX, true), + native_context, native_context, eglobal); } } control = graph()->NewNode(common()->Merge(2), if_convert, if_global); effect = graph()->NewNode(common()->EffectPhi(2), econvert, eglobal, control); - receiver = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), rconvert, - rglobal, control); + receiver = + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + rconvert, rglobal, control); } } ReplaceWithValue(node, receiver, effect, control); @@ -1316,6 +1424,75 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) { namespace { +// Maximum instance size for which allocations will be inlined. +const int kMaxInlineInstanceSize = 64 * kPointerSize; + + +// Checks whether allocation using the given constructor can be inlined. +bool IsAllocationInlineable(Handle<JSFunction> constructor) { + // TODO(bmeurer): Further relax restrictions on inlining, i.e. + // instance type and maybe instance size (inobject properties + // are limited anyways by the runtime). + return constructor->has_initial_map() && + constructor->initial_map()->instance_type() == JS_OBJECT_TYPE && + constructor->initial_map()->instance_size() < kMaxInlineInstanceSize; +} + +} // namespace + + +Reduction JSTypedLowering::ReduceJSCreate(Node* node) { + DCHECK_EQ(IrOpcode::kJSCreate, node->opcode()); + Node* const target = NodeProperties::GetValueInput(node, 0); + Type* const target_type = NodeProperties::GetType(target); + Node* const new_target = NodeProperties::GetValueInput(node, 1); + Node* const effect = NodeProperties::GetEffectInput(node); + // TODO(turbofan): Add support for NewTarget passed to JSCreate. + if (target != new_target) return NoChange(); + // Extract constructor function. + if (target_type->IsConstant() && + target_type->AsConstant()->Value()->IsJSFunction()) { + Handle<JSFunction> constructor = + Handle<JSFunction>::cast(target_type->AsConstant()->Value()); + DCHECK(constructor->IsConstructor()); + // Force completion of inobject slack tracking before + // generating code to finalize the instance size. + constructor->CompleteInobjectSlackTrackingIfActive(); + + // TODO(bmeurer): We fall back to the runtime in case we cannot inline + // the allocation here, which is sort of expensive. We should think about + // a soft fallback to some NewObjectCodeStub. + if (IsAllocationInlineable(constructor)) { + // Compute instance size from initial map of {constructor}. + Handle<Map> initial_map(constructor->initial_map(), isolate()); + int const instance_size = initial_map->instance_size(); + + // Add a dependency on the {initial_map} to make sure that this code is + // deoptimized whenever the {initial_map} of the {constructor} changes. + dependencies()->AssumeInitialMapCantChange(initial_map); + + // Emit code to allocate the JSObject instance for the {constructor}. + AllocationBuilder a(jsgraph(), effect, graph()->start()); + a.Allocate(instance_size); + a.Store(AccessBuilder::ForMap(), initial_map); + a.Store(AccessBuilder::ForJSObjectProperties(), + jsgraph()->EmptyFixedArrayConstant()); + a.Store(AccessBuilder::ForJSObjectElements(), + jsgraph()->EmptyFixedArrayConstant()); + for (int i = 0; i < initial_map->GetInObjectProperties(); ++i) { + a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i), + jsgraph()->UndefinedConstant()); + } + a.FinishAndChange(node); + return Changed(node); + } + } + return NoChange(); +} + + +namespace { + // Retrieves the frame state holding actual argument values. Node* GetArgumentsFrameState(Node* frame_state) { Node* const outer_state = frame_state->InputAt(kFrameStateOuterStateInput); @@ -1337,124 +1514,302 @@ Reduction JSTypedLowering::ReduceJSCreateArguments(Node* node) { // Use the ArgumentsAccessStub for materializing both mapped and unmapped // arguments object, but only for non-inlined (i.e. outermost) frames. - if (p.type() != CreateArgumentsParameters::kRestArray && - outer_state->opcode() != IrOpcode::kFrameState) { - Handle<SharedFunctionInfo> shared; + if (outer_state->opcode() != IrOpcode::kFrameState) { Isolate* isolate = jsgraph()->isolate(); - if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); - bool unmapped = p.type() == CreateArgumentsParameters::kUnmappedArguments; - Callable callable = CodeFactory::ArgumentsAccess( - isolate, unmapped, shared->has_duplicate_parameters()); - CallDescriptor* desc = Linkage::GetStubCallDescriptor( - isolate, graph()->zone(), callable.descriptor(), 0, - CallDescriptor::kNeedsFrameState); - const Operator* new_op = common()->Call(desc); int parameter_count = state_info.parameter_count() - 1; int parameter_offset = parameter_count * kPointerSize; int offset = StandardFrameConstants::kCallerSPOffset + parameter_offset; - Node* stub_code = jsgraph()->HeapConstant(callable.code()); Node* parameter_pointer = graph()->NewNode( machine()->IntAdd(), graph()->NewNode(machine()->LoadFramePointer()), jsgraph()->IntPtrConstant(offset)); - node->InsertInput(graph()->zone(), 0, stub_code); - node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(parameter_count)); - node->InsertInput(graph()->zone(), 3, parameter_pointer); - NodeProperties::ChangeOp(node, new_op); - return Changed(node); + + if (p.type() != CreateArgumentsParameters::kRestArray) { + Handle<SharedFunctionInfo> shared; + if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); + bool unmapped = p.type() == CreateArgumentsParameters::kUnmappedArguments; + Callable callable = CodeFactory::ArgumentsAccess( + isolate, unmapped, shared->has_duplicate_parameters()); + CallDescriptor* desc = Linkage::GetStubCallDescriptor( + isolate, graph()->zone(), callable.descriptor(), 0, + CallDescriptor::kNeedsFrameState); + const Operator* new_op = common()->Call(desc); + Node* stub_code = jsgraph()->HeapConstant(callable.code()); + node->InsertInput(graph()->zone(), 0, stub_code); + node->InsertInput(graph()->zone(), 2, + jsgraph()->Constant(parameter_count)); + node->InsertInput(graph()->zone(), 3, parameter_pointer); + NodeProperties::ChangeOp(node, new_op); + return Changed(node); + } else { + Callable callable = CodeFactory::RestArgumentsAccess(isolate); + CallDescriptor* desc = Linkage::GetStubCallDescriptor( + isolate, graph()->zone(), callable.descriptor(), 0, + CallDescriptor::kNeedsFrameState); + const Operator* new_op = common()->Call(desc); + Node* stub_code = jsgraph()->HeapConstant(callable.code()); + node->InsertInput(graph()->zone(), 0, stub_code); + node->ReplaceInput(1, jsgraph()->Constant(parameter_count)); + node->InsertInput(graph()->zone(), 2, parameter_pointer); + node->InsertInput(graph()->zone(), 3, + jsgraph()->Constant(p.start_index())); + NodeProperties::ChangeOp(node, new_op); + return Changed(node); + } + } else if (outer_state->opcode() == IrOpcode::kFrameState) { + // Use inline allocation for all mapped arguments objects within inlined + // (i.e. non-outermost) frames, independent of the object size. + if (p.type() == CreateArgumentsParameters::kMappedArguments) { + Handle<SharedFunctionInfo> shared; + if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); + Node* const callee = NodeProperties::GetValueInput(node, 0); + Node* const control = NodeProperties::GetControlInput(node); + Node* const context = NodeProperties::GetContextInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + // TODO(mstarzinger): Duplicate parameters are not handled yet. + if (shared->has_duplicate_parameters()) return NoChange(); + // Choose the correct frame state and frame state info depending on + // whether there conceptually is an arguments adaptor frame in the call + // chain. + Node* const args_state = GetArgumentsFrameState(frame_state); + FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state); + // Prepare element backing store to be used by arguments object. + bool has_aliased_arguments = false; + Node* const elements = AllocateAliasedArguments( + effect, control, args_state, context, shared, &has_aliased_arguments); + effect = elements->op()->EffectOutputCount() > 0 ? elements : effect; + // Load the arguments object map from the current native context. + Node* const load_native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, effect); + Node* const load_arguments_map = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForContextSlot( + has_aliased_arguments ? Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX + : Context::SLOPPY_ARGUMENTS_MAP_INDEX)), + load_native_context, effect, control); + // Actually allocate and initialize the arguments object. + AllocationBuilder a(jsgraph(), effect, control); + Node* properties = jsgraph()->EmptyFixedArrayConstant(); + int length = args_state_info.parameter_count() - 1; // Minus receiver. + STATIC_ASSERT(Heap::kSloppyArgumentsObjectSize == 5 * kPointerSize); + a.Allocate(Heap::kSloppyArgumentsObjectSize); + a.Store(AccessBuilder::ForMap(), load_arguments_map); + a.Store(AccessBuilder::ForJSObjectProperties(), properties); + a.Store(AccessBuilder::ForJSObjectElements(), elements); + a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length)); + a.Store(AccessBuilder::ForArgumentsCallee(), callee); + RelaxControls(node); + a.FinishAndChange(node); + return Changed(node); + } else if (p.type() == CreateArgumentsParameters::kUnmappedArguments) { + // Use inline allocation for all unmapped arguments objects within inlined + // (i.e. non-outermost) frames, independent of the object size. + Node* const control = NodeProperties::GetControlInput(node); + Node* const context = NodeProperties::GetContextInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + // Choose the correct frame state and frame state info depending on + // whether there conceptually is an arguments adaptor frame in the call + // chain. + Node* const args_state = GetArgumentsFrameState(frame_state); + FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state); + // Prepare element backing store to be used by arguments object. + Node* const elements = AllocateArguments(effect, control, args_state); + effect = elements->op()->EffectOutputCount() > 0 ? elements : effect; + // Load the arguments object map from the current native context. + Node* const load_native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, effect); + Node* const load_arguments_map = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForContextSlot( + Context::STRICT_ARGUMENTS_MAP_INDEX)), + load_native_context, effect, control); + // Actually allocate and initialize the arguments object. + AllocationBuilder a(jsgraph(), effect, control); + Node* properties = jsgraph()->EmptyFixedArrayConstant(); + int length = args_state_info.parameter_count() - 1; // Minus receiver. + STATIC_ASSERT(Heap::kStrictArgumentsObjectSize == 4 * kPointerSize); + a.Allocate(Heap::kStrictArgumentsObjectSize); + a.Store(AccessBuilder::ForMap(), load_arguments_map); + a.Store(AccessBuilder::ForJSObjectProperties(), properties); + a.Store(AccessBuilder::ForJSObjectElements(), elements); + a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length)); + RelaxControls(node); + a.FinishAndChange(node); + return Changed(node); + } else if (p.type() == CreateArgumentsParameters::kRestArray) { + // Use inline allocation for all unmapped arguments objects within inlined + // (i.e. non-outermost) frames, independent of the object size. + Node* const control = NodeProperties::GetControlInput(node); + Node* const context = NodeProperties::GetContextInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + // Choose the correct frame state and frame state info depending on + // whether there conceptually is an arguments adaptor frame in the call + // chain. + Node* const args_state = GetArgumentsFrameState(frame_state); + FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state); + // Prepare element backing store to be used by the rest array. + Node* const elements = + AllocateRestArguments(effect, control, args_state, p.start_index()); + effect = elements->op()->EffectOutputCount() > 0 ? elements : effect; + // Load the JSArray object map from the current native context. + Node* const load_native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, effect); + Node* const load_jsarray_map = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForContextSlot( + Context::JS_ARRAY_FAST_ELEMENTS_MAP_INDEX)), + load_native_context, effect, control); + // Actually allocate and initialize the jsarray. + AllocationBuilder a(jsgraph(), effect, control); + Node* properties = jsgraph()->EmptyFixedArrayConstant(); + + // -1 to minus receiver + int argument_count = args_state_info.parameter_count() - 1; + int length = std::max(0, argument_count - p.start_index()); + STATIC_ASSERT(JSArray::kSize == 4 * kPointerSize); + a.Allocate(JSArray::kSize); + a.Store(AccessBuilder::ForMap(), load_jsarray_map); + a.Store(AccessBuilder::ForJSObjectProperties(), properties); + a.Store(AccessBuilder::ForJSObjectElements(), elements); + a.Store(AccessBuilder::ForJSArrayLength(FAST_ELEMENTS), + jsgraph()->Constant(length)); + RelaxControls(node); + a.FinishAndChange(node); + return Changed(node); + } } - // Use inline allocation for all mapped arguments objects within inlined - // (i.e. non-outermost) frames, independent of the object size. - if (p.type() == CreateArgumentsParameters::kMappedArguments && - outer_state->opcode() == IrOpcode::kFrameState) { - Handle<SharedFunctionInfo> shared; - if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); - Node* const callee = NodeProperties::GetValueInput(node, 0); - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = NodeProperties::GetControlInput(node); - Node* const context = NodeProperties::GetContextInput(node); - // TODO(mstarzinger): Duplicate parameters are not handled yet. - if (shared->has_duplicate_parameters()) return NoChange(); - // Choose the correct frame state and frame state info depending on whether - // there conceptually is an arguments adaptor frame in the call chain. - Node* const args_state = GetArgumentsFrameState(frame_state); - FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state); - // Prepare element backing store to be used by arguments object. - bool has_aliased_arguments = false; - Node* const elements = AllocateAliasedArguments( - effect, control, args_state, context, shared, &has_aliased_arguments); - // Load the arguments object map from the current native context. - Node* const load_global_object = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX)), - context, effect, control); - Node* const load_native_context = - graph()->NewNode(simplified()->LoadField( - AccessBuilder::ForJSGlobalObjectNativeContext()), - load_global_object, effect, control); - Node* const load_arguments_map = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForContextSlot( - has_aliased_arguments ? Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX - : Context::SLOPPY_ARGUMENTS_MAP_INDEX)), - load_native_context, effect, control); - // Actually allocate and initialize the arguments object. - AllocationBuilder a(jsgraph(), effect, control); - Node* properties = jsgraph()->EmptyFixedArrayConstant(); - int length = args_state_info.parameter_count() - 1; // Minus receiver. - STATIC_ASSERT(Heap::kSloppyArgumentsObjectSize == 5 * kPointerSize); - a.Allocate(Heap::kSloppyArgumentsObjectSize); - a.Store(AccessBuilder::ForMap(), load_arguments_map); - a.Store(AccessBuilder::ForJSObjectProperties(), properties); - a.Store(AccessBuilder::ForJSObjectElements(), elements); - a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length)); - a.Store(AccessBuilder::ForArgumentsCallee(), callee); - RelaxControls(node); - a.FinishAndChange(node); - return Changed(node); + return NoChange(); +} + + +Reduction JSTypedLowering::ReduceNewArray(Node* node, Node* length, + int capacity, + Handle<AllocationSite> site) { + DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode()); + Node* context = NodeProperties::GetContextInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + // Extract transition and tenuring feedback from the {site} and add + // appropriate code dependencies on the {site} if deoptimization is + // enabled. + PretenureFlag pretenure = site->GetPretenureMode(); + ElementsKind elements_kind = site->GetElementsKind(); + DCHECK(IsFastElementsKind(elements_kind)); + if (flags() & kDeoptimizationEnabled) { + dependencies()->AssumeTenuringDecision(site); + dependencies()->AssumeTransitionStable(site); + } + + // Retrieve the initial map for the array from the appropriate native context. + Node* native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, effect); + Node* js_array_map = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::ArrayMapIndex(elements_kind), true), + native_context, native_context, effect); + + // Setup elements and properties. + Node* elements; + if (capacity == 0) { + elements = jsgraph()->EmptyFixedArrayConstant(); + } else { + elements = effect = + AllocateElements(effect, control, elements_kind, capacity, pretenure); + } + Node* properties = jsgraph()->EmptyFixedArrayConstant(); + + // Perform the allocation of the actual JSArray object. + AllocationBuilder a(jsgraph(), effect, control); + a.Allocate(JSArray::kSize, pretenure); + a.Store(AccessBuilder::ForMap(), js_array_map); + a.Store(AccessBuilder::ForJSObjectProperties(), properties); + a.Store(AccessBuilder::ForJSObjectElements(), elements); + a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length); + RelaxControls(node); + a.FinishAndChange(node); + return Changed(node); +} + + +Reduction JSTypedLowering::ReduceJSCreateArray(Node* node) { + DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode()); + CreateArrayParameters const& p = CreateArrayParametersOf(node->op()); + Node* target = NodeProperties::GetValueInput(node, 0); + Node* new_target = NodeProperties::GetValueInput(node, 1); + + // TODO(bmeurer): Optimize the subclassing case. + if (target != new_target) return NoChange(); + + // Check if we have a feedback {site} on the {node}. + Handle<AllocationSite> site = p.site(); + if (p.site().is_null()) return NoChange(); + + // Attempt to inline calls to the Array constructor for the relevant cases + // where either no arguments are provided, or exactly one unsigned number + // argument is given. + if (site->CanInlineCall()) { + if (p.arity() == 0) { + Node* length = jsgraph()->ZeroConstant(); + int capacity = JSArray::kPreallocatedArrayElements; + return ReduceNewArray(node, length, capacity, site); + } else if (p.arity() == 1) { + Node* length = NodeProperties::GetValueInput(node, 2); + Type* length_type = NodeProperties::GetType(length); + if (length_type->Is(type_cache_.kElementLoopUnrollType)) { + int capacity = static_cast<int>(length_type->Max()); + return ReduceNewArray(node, length, capacity, site); + } + } } - // Use inline allocation for all unmapped arguments objects within inlined - // (i.e. non-outermost) frames, independent of the object size. - if (p.type() == CreateArgumentsParameters::kUnmappedArguments && - outer_state->opcode() == IrOpcode::kFrameState) { - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = NodeProperties::GetControlInput(node); - Node* const context = NodeProperties::GetContextInput(node); - // Choose the correct frame state and frame state info depending on whether - // there conceptually is an arguments adaptor frame in the call chain. - Node* const args_state = GetArgumentsFrameState(frame_state); - FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state); - // Prepare element backing store to be used by arguments object. - Node* const elements = AllocateArguments(effect, control, args_state); - // Load the arguments object map from the current native context. - Node* const load_global_object = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX)), - context, effect, control); - Node* const load_native_context = - graph()->NewNode(simplified()->LoadField( - AccessBuilder::ForJSGlobalObjectNativeContext()), - load_global_object, effect, control); - Node* const load_arguments_map = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForContextSlot(Context::STRICT_ARGUMENTS_MAP_INDEX)), - load_native_context, effect, control); - // Actually allocate and initialize the arguments object. - AllocationBuilder a(jsgraph(), effect, control); - Node* properties = jsgraph()->EmptyFixedArrayConstant(); - int length = args_state_info.parameter_count() - 1; // Minus receiver. - STATIC_ASSERT(Heap::kStrictArgumentsObjectSize == 4 * kPointerSize); - a.Allocate(Heap::kStrictArgumentsObjectSize); - a.Store(AccessBuilder::ForMap(), load_arguments_map); - a.Store(AccessBuilder::ForJSObjectProperties(), properties); - a.Store(AccessBuilder::ForJSObjectElements(), elements); - a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length)); - RelaxControls(node); - a.FinishAndChange(node); + // Reduce {node} to the appropriate ArrayConstructorStub backend. + // Note that these stubs "behave" like JSFunctions, which means they + // expect a receiver on the stack, which they remove. We just push + // undefined for the receiver. + ElementsKind elements_kind = site->GetElementsKind(); + AllocationSiteOverrideMode override_mode = + (AllocationSite::GetMode(elements_kind) == TRACK_ALLOCATION_SITE) + ? DISABLE_ALLOCATION_SITES + : DONT_OVERRIDE; + if (p.arity() == 0) { + ArrayNoArgumentConstructorStub stub(isolate(), elements_kind, + override_mode); + CallDescriptor* desc = Linkage::GetStubCallDescriptor( + isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 1, + CallDescriptor::kNeedsFrameState); + node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode())); + node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site)); + node->InsertInput(graph()->zone(), 3, jsgraph()->UndefinedConstant()); + NodeProperties::ChangeOp(node, common()->Call(desc)); + return Changed(node); + } else if (p.arity() == 1) { + // TODO(bmeurer): Optimize for the 0 length non-holey case? + ArraySingleArgumentConstructorStub stub( + isolate(), GetHoleyElementsKind(elements_kind), override_mode); + CallDescriptor* desc = Linkage::GetStubCallDescriptor( + isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 2, + CallDescriptor::kNeedsFrameState); + node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode())); + node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site)); + node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(1)); + node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant()); + NodeProperties::ChangeOp(node, common()->Call(desc)); + return Changed(node); + } else { + int const arity = static_cast<int>(p.arity()); + ArrayNArgumentsConstructorStub stub(isolate(), elements_kind, + override_mode); + CallDescriptor* desc = Linkage::GetStubCallDescriptor( + isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), + arity + 1, CallDescriptor::kNeedsFrameState); + node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode())); + node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site)); + node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(arity)); + node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant()); + NodeProperties::ChangeOp(node, common()->Call(desc)); return Changed(node); } - - return NoChange(); } @@ -1484,11 +1839,43 @@ Reduction JSTypedLowering::ReduceJSCreateClosure(Node* node) { } +Reduction JSTypedLowering::ReduceJSCreateIterResultObject(Node* node) { + DCHECK_EQ(IrOpcode::kJSCreateIterResultObject, node->opcode()); + Node* value = NodeProperties::GetValueInput(node, 0); + Node* done = NodeProperties::GetValueInput(node, 1); + Node* context = NodeProperties::GetContextInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + + // Load the JSIteratorResult map for the {context}. + Node* native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, effect); + Node* iterator_result_map = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::ITERATOR_RESULT_MAP_INDEX, true), + native_context, native_context, effect); + + // Emit code to allocate the JSIteratorResult instance. + AllocationBuilder a(jsgraph(), effect, graph()->start()); + a.Allocate(JSIteratorResult::kSize); + a.Store(AccessBuilder::ForMap(), iterator_result_map); + a.Store(AccessBuilder::ForJSObjectProperties(), + jsgraph()->EmptyFixedArrayConstant()); + a.Store(AccessBuilder::ForJSObjectElements(), + jsgraph()->EmptyFixedArrayConstant()); + a.Store(AccessBuilder::ForJSIteratorResultValue(), value); + a.Store(AccessBuilder::ForJSIteratorResultDone(), done); + STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize); + a.FinishAndChange(node); + return Changed(node); +} + + Reduction JSTypedLowering::ReduceJSCreateLiteralArray(Node* node) { DCHECK_EQ(IrOpcode::kJSCreateLiteralArray, node->opcode()); - HeapObjectMatcher mconst(NodeProperties::GetValueInput(node, 2)); - int length = Handle<FixedArray>::cast(mconst.Value())->length(); - int flags = OpParameter<int>(node->op()); + CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op()); + Handle<FixedArray> const constants = Handle<FixedArray>::cast(p.constant()); + int const length = constants->length(); + int const flags = p.flags(); // Use the FastCloneShallowArrayStub only for shallow boilerplates up to the // initial length limit for arrays with "fast" elements kind. @@ -1505,7 +1892,11 @@ Reduction JSTypedLowering::ReduceJSCreateLiteralArray(Node* node) { : CallDescriptor::kNoFlags); const Operator* new_op = common()->Call(desc); Node* stub_code = jsgraph()->HeapConstant(callable.code()); + Node* literal_index = jsgraph()->SmiConstant(p.index()); + Node* constant_elements = jsgraph()->HeapConstant(constants); node->InsertInput(graph()->zone(), 0, stub_code); + node->InsertInput(graph()->zone(), 2, literal_index); + node->InsertInput(graph()->zone(), 3, constant_elements); NodeProperties::ChangeOp(node, new_op); return Changed(node); } @@ -1516,10 +1907,11 @@ Reduction JSTypedLowering::ReduceJSCreateLiteralArray(Node* node) { Reduction JSTypedLowering::ReduceJSCreateLiteralObject(Node* node) { DCHECK_EQ(IrOpcode::kJSCreateLiteralObject, node->opcode()); - HeapObjectMatcher mconst(NodeProperties::GetValueInput(node, 2)); + CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op()); + Handle<FixedArray> const constants = Handle<FixedArray>::cast(p.constant()); // Constants are pairs, see ObjectLiteral::properties_count(). - int length = Handle<FixedArray>::cast(mconst.Value())->length() / 2; - int flags = OpParameter<int>(node->op()); + int const length = constants->length() / 2; + int const flags = p.flags(); // Use the FastCloneShallowObjectStub only for shallow boilerplates without // elements up to the number of properties that the stubs can handle. @@ -1534,8 +1926,13 @@ Reduction JSTypedLowering::ReduceJSCreateLiteralObject(Node* node) { : CallDescriptor::kNoFlags); const Operator* new_op = common()->Call(desc); Node* stub_code = jsgraph()->HeapConstant(callable.code()); - node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(flags)); + Node* literal_index = jsgraph()->SmiConstant(p.index()); + Node* literal_flags = jsgraph()->SmiConstant(flags); + Node* constant_elements = jsgraph()->HeapConstant(constants); node->InsertInput(graph()->zone(), 0, stub_code); + node->InsertInput(graph()->zone(), 2, literal_index); + node->InsertInput(graph()->zone(), 3, constant_elements); + node->InsertInput(graph()->zone(), 4, literal_flags); NodeProperties::ChangeOp(node, new_op); return Changed(node); } @@ -1549,22 +1946,16 @@ Reduction JSTypedLowering::ReduceJSCreateFunctionContext(Node* node) { int slot_count = OpParameter<int>(node->op()); Node* const closure = NodeProperties::GetValueInput(node, 0); - // The closure can be NumberConstant(0) if the closure is global code - // (rather than a function). We exclude that case here. - // TODO(jarin) Find a better way to check that the closure is a function. - // Use inline allocation for function contexts up to a size limit. - if (slot_count < kFunctionContextAllocationLimit && - closure->opcode() != IrOpcode::kNumberConstant) { + if (slot_count < kFunctionContextAllocationLimit) { // JSCreateFunctionContext[slot_count < limit]](fun) - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = NodeProperties::GetControlInput(node); - Node* const context = NodeProperties::GetContextInput(node); - Node* const extension = jsgraph()->ZeroConstant(); - Node* const load = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX)), - context, effect, control); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + Node* context = NodeProperties::GetContextInput(node); + Node* extension = jsgraph()->TheHoleConstant(); + Node* native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, effect); AllocationBuilder a(jsgraph(), effect, control); STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered. int context_length = slot_count + Context::MIN_CONTEXT_SLOTS; @@ -1572,7 +1963,8 @@ Reduction JSTypedLowering::ReduceJSCreateFunctionContext(Node* node) { a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure); a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context); a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension); - a.Store(AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX), load); + a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX), + native_context); for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) { a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->UndefinedConstant()); } @@ -1601,38 +1993,53 @@ Reduction JSTypedLowering::ReduceJSCreateFunctionContext(Node* node) { Reduction JSTypedLowering::ReduceJSCreateWithContext(Node* node) { DCHECK_EQ(IrOpcode::kJSCreateWithContext, node->opcode()); - Node* const input = NodeProperties::GetValueInput(node, 0); - Node* const closure = NodeProperties::GetValueInput(node, 1); - Type* input_type = NodeProperties::GetType(input); + Node* object = NodeProperties::GetValueInput(node, 0); + Node* closure = NodeProperties::GetValueInput(node, 1); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + Node* context = NodeProperties::GetContextInput(node); + Node* native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, effect); + AllocationBuilder a(jsgraph(), effect, control); + STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered. + a.AllocateArray(Context::MIN_CONTEXT_SLOTS, factory()->with_context_map()); + a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure); + a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context); + a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), object); + a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX), + native_context); + RelaxControls(node); + a.FinishAndChange(node); + return Changed(node); +} - // The closure can be NumberConstant(0) if the closure is global code - // (rather than a function). We exclude that case here. - // TODO(jarin) Find a better way to check that the closure is a function. - - // Use inline allocation for with contexts for regular objects. - if (input_type->Is(Type::Receiver()) && - closure->opcode() != IrOpcode::kNumberConstant) { - // JSCreateWithContext(o:receiver, fun) - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = NodeProperties::GetControlInput(node); - Node* const context = NodeProperties::GetContextInput(node); - Node* const load = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX)), - context, effect, control); - AllocationBuilder a(jsgraph(), effect, control); - STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered. - a.AllocateArray(Context::MIN_CONTEXT_SLOTS, factory()->with_context_map()); - a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure); - a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context); - a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), input); - a.Store(AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX), load); - RelaxControls(node); - a.FinishAndChange(node); - return Changed(node); - } - return NoChange(); +Reduction JSTypedLowering::ReduceJSCreateCatchContext(Node* node) { + DCHECK_EQ(IrOpcode::kJSCreateCatchContext, node->opcode()); + Handle<String> name = OpParameter<Handle<String>>(node); + Node* exception = NodeProperties::GetValueInput(node, 0); + Node* closure = NodeProperties::GetValueInput(node, 1); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + Node* context = NodeProperties::GetContextInput(node); + Node* native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, effect); + AllocationBuilder a(jsgraph(), effect, control); + STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered. + a.AllocateArray(Context::MIN_CONTEXT_SLOTS + 1, + factory()->catch_context_map()); + a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure); + a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context); + a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), name); + a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX), + native_context); + a.Store(AccessBuilder::ForContextSlot(Context::THROWN_OBJECT_INDEX), + exception); + RelaxControls(node); + a.FinishAndChange(node); + return Changed(node); } @@ -1642,31 +2049,26 @@ Reduction JSTypedLowering::ReduceJSCreateBlockContext(Node* node) { int context_length = scope_info->ContextLength(); Node* const closure = NodeProperties::GetValueInput(node, 0); - // The closure can be NumberConstant(0) if the closure is global code - // (rather than a function). We exclude that case here. - // TODO(jarin) Find a better way to check that the closure is a function. - // Use inline allocation for block contexts up to a size limit. - if (context_length < kBlockContextAllocationLimit && - closure->opcode() != IrOpcode::kNumberConstant) { + if (context_length < kBlockContextAllocationLimit) { // JSCreateBlockContext[scope[length < limit]](fun) - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = NodeProperties::GetControlInput(node); - Node* const context = NodeProperties::GetContextInput(node); - Node* const extension = jsgraph()->Constant(scope_info); - Node* const load = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX)), - context, effect, control); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + Node* context = NodeProperties::GetContextInput(node); + Node* extension = jsgraph()->Constant(scope_info); + Node* native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, effect); AllocationBuilder a(jsgraph(), effect, control); STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered. a.AllocateArray(context_length, factory()->block_context_map()); a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure); a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context); a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension); - a.Store(AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX), load); + a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX), + native_context); for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) { - a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->TheHoleConstant()); + a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->UndefinedConstant()); } RelaxControls(node); a.FinishAndChange(node); @@ -1677,6 +2079,66 @@ Reduction JSTypedLowering::ReduceJSCreateBlockContext(Node* node) { } +Reduction JSTypedLowering::ReduceJSCallConstruct(Node* node) { + DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode()); + CallConstructParameters const& p = CallConstructParametersOf(node->op()); + DCHECK_LE(2u, p.arity()); + int const arity = static_cast<int>(p.arity() - 2); + Node* target = NodeProperties::GetValueInput(node, 0); + Type* target_type = NodeProperties::GetType(target); + Node* new_target = NodeProperties::GetValueInput(node, arity + 1); + + // Check if {target} is a known JSFunction. + if (target_type->IsConstant() && + target_type->AsConstant()->Value()->IsJSFunction()) { + Handle<JSFunction> function = + Handle<JSFunction>::cast(target_type->AsConstant()->Value()); + Handle<SharedFunctionInfo> shared(function->shared(), isolate()); + + // Remove the eager bailout frame state. + NodeProperties::RemoveFrameStateInput(node, 1); + + // Patch {node} to an indirect call via the {function}s construct stub. + Callable callable(handle(shared->construct_stub(), isolate()), + ConstructStubDescriptor(isolate())); + node->RemoveInput(arity + 1); + node->InsertInput(graph()->zone(), 0, + jsgraph()->HeapConstant(callable.code())); + node->InsertInput(graph()->zone(), 2, new_target); + node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(arity)); + node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant()); + node->InsertInput(graph()->zone(), 5, jsgraph()->UndefinedConstant()); + NodeProperties::ChangeOp( + node, common()->Call(Linkage::GetStubCallDescriptor( + isolate(), graph()->zone(), callable.descriptor(), 1 + arity, + CallDescriptor::kNeedsFrameState))); + return Changed(node); + } + + // Check if {target} is a JSFunction. + if (target_type->Is(Type::Function())) { + // Remove the eager bailout frame state. + NodeProperties::RemoveFrameStateInput(node, 1); + + // Patch {node} to an indirect call via the ConstructFunction builtin. + Callable callable = CodeFactory::ConstructFunction(isolate()); + node->RemoveInput(arity + 1); + node->InsertInput(graph()->zone(), 0, + jsgraph()->HeapConstant(callable.code())); + node->InsertInput(graph()->zone(), 2, new_target); + node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(arity)); + node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant()); + NodeProperties::ChangeOp( + node, common()->Call(Linkage::GetStubCallDescriptor( + isolate(), graph()->zone(), callable.descriptor(), 1 + arity, + CallDescriptor::kNeedsFrameState))); + return Changed(node); + } + + return NoChange(); +} + + Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) { DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); @@ -1708,8 +2170,10 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) { // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). if (IsClassConstructor(shared->kind())) return NoChange(); - // Grab the context from the {function}. - Node* context = jsgraph()->Constant(handle(function->context(), isolate())); + // Load the context from the {target}. + Node* context = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target, + effect, control); NodeProperties::ReplaceContextInput(node, context); // Check if we need to convert the {receiver}. @@ -1718,10 +2182,12 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) { receiver = effect = graph()->NewNode(javascript()->ConvertReceiver(convert_mode), receiver, context, frame_state, effect, control); - NodeProperties::ReplaceEffectInput(node, effect); NodeProperties::ReplaceValueInput(node, receiver, 1); } + // Update the effect dependency for the {node}. + NodeProperties::ReplaceEffectInput(node, effect); + // Remove the eager bailout frame state. NodeProperties::RemoveFrameStateInput(node, 1); @@ -1731,12 +2197,14 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) { flags |= CallDescriptor::kSupportsTailCalls; } + Node* new_target = jsgraph()->UndefinedConstant(); + Node* argument_count = jsgraph()->Int32Constant(arity); if (shared->internal_formal_parameter_count() == arity || shared->internal_formal_parameter_count() == SharedFunctionInfo::kDontAdaptArgumentsSentinel) { // Patch {node} to a direct call. - node->InsertInput(graph()->zone(), arity + 2, - jsgraph()->Int32Constant(arity)); + node->InsertInput(graph()->zone(), arity + 2, new_target); + node->InsertInput(graph()->zone(), arity + 3, argument_count); NodeProperties::ChangeOp(node, common()->Call(Linkage::GetJSCallDescriptor( graph()->zone(), false, 1 + arity, flags))); @@ -1745,9 +2213,10 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) { Callable callable = CodeFactory::ArgumentAdaptor(isolate()); node->InsertInput(graph()->zone(), 0, jsgraph()->HeapConstant(callable.code())); - node->InsertInput(graph()->zone(), 2, jsgraph()->Int32Constant(arity)); + node->InsertInput(graph()->zone(), 2, new_target); + node->InsertInput(graph()->zone(), 3, argument_count); node->InsertInput( - graph()->zone(), 3, + graph()->zone(), 4, jsgraph()->Int32Constant(shared->internal_formal_parameter_count())); NodeProperties::ChangeOp( node, common()->Call(Linkage::GetStubCallDescriptor( @@ -1882,8 +2351,8 @@ Reduction JSTypedLowering::ReduceJSForInPrepare(Node* node) { etrue0 = graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_true0); cache_array_true0 = - graph()->NewNode(common()->Phi(kMachAnyTagged, 2), cache_array_true1, - cache_array_false1, if_true0); + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + cache_array_true1, cache_array_false1, if_true0); cache_type_true0 = cache_type; } @@ -1895,35 +2364,24 @@ Reduction JSTypedLowering::ReduceJSForInPrepare(Node* node) { Node* efalse0; { // FixedArray case. - Node* receiver_instance_type = efalse0 = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForMapInstanceType()), - receiver_map, effect, if_false0); - - STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); - cache_type_false0 = graph()->NewNode( - common()->Select(kMachAnyTagged, BranchHint::kFalse), - graph()->NewNode(machine()->Uint32LessThanOrEqual(), - receiver_instance_type, - jsgraph()->Uint32Constant(LAST_JS_PROXY_TYPE)), - jsgraph()->ZeroConstant(), // Zero indicagtes proxy. - jsgraph()->OneConstant()); // One means slow check. - + cache_type_false0 = jsgraph()->OneConstant(); // Smi means slow check cache_array_false0 = cache_type; cache_length_false0 = efalse0 = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), - cache_array_false0, efalse0, if_false0); + cache_array_false0, effect, if_false0); } control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); Node* cache_array = - graph()->NewNode(common()->Phi(kMachAnyTagged, 2), cache_array_true0, - cache_array_false0, control); + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + cache_array_true0, cache_array_false0, control); Node* cache_length = - graph()->NewNode(common()->Phi(kMachAnyTagged, 2), cache_length_true0, - cache_length_false0, control); - cache_type = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), - cache_type_true0, cache_type_false0, control); + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + cache_length_true0, cache_length_false0, control); + cache_type = + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + cache_type_true0, cache_type_false0, control); for (auto edge : node->use_edges()) { Node* const use = edge.from(); @@ -2036,8 +2494,8 @@ Reduction JSTypedLowering::ReduceJSForInNext(Node* node) { if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); efalse0 = graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0); - vfalse0 = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), vtrue1, - vfalse1, if_false0); + vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + vtrue1, vfalse1, if_false0); } control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); @@ -2047,7 +2505,8 @@ Reduction JSTypedLowering::ReduceJSForInNext(Node* node) { node->ReplaceInput(1, vfalse0); node->ReplaceInput(2, control); node->TrimInputCount(3); - NodeProperties::ChangeOp(node, common()->Phi(kMachAnyTagged, 2)); + NodeProperties::ChangeOp(node, + common()->Phi(MachineRepresentation::kTagged, 2)); return Changed(node); } @@ -2060,6 +2519,36 @@ Reduction JSTypedLowering::ReduceJSForInStep(Node* node) { } +Reduction JSTypedLowering::ReduceSelect(Node* node) { + DCHECK_EQ(IrOpcode::kSelect, node->opcode()); + Node* const condition = NodeProperties::GetValueInput(node, 0); + Type* const condition_type = NodeProperties::GetType(condition); + Node* const vtrue = NodeProperties::GetValueInput(node, 1); + Type* const vtrue_type = NodeProperties::GetType(vtrue); + Node* const vfalse = NodeProperties::GetValueInput(node, 2); + Type* const vfalse_type = NodeProperties::GetType(vfalse); + if (condition_type->Is(true_type_)) { + // Select(condition:true, vtrue, vfalse) => vtrue + return Replace(vtrue); + } + if (condition_type->Is(false_type_)) { + // Select(condition:false, vtrue, vfalse) => vfalse + return Replace(vfalse); + } + if (vtrue_type->Is(true_type_) && vfalse_type->Is(false_type_)) { + // Select(condition, vtrue:true, vfalse:false) => condition + return Replace(condition); + } + if (vtrue_type->Is(false_type_) && vfalse_type->Is(true_type_)) { + // Select(condition, vtrue:false, vfalse:true) => BooleanNot(condition) + node->TrimInputCount(1); + NodeProperties::ChangeOp(node, simplified()->BooleanNot()); + return Changed(node); + } + return NoChange(); +} + + Reduction JSTypedLowering::Reduce(Node* node) { // Check if the output type is a singleton. In that case we already know the // result value and can simply replace the node if it's eliminable. @@ -2129,8 +2618,6 @@ Reduction JSTypedLowering::Reduce(Node* node) { return ReduceNumberBinop(node, simplified()->NumberDivide()); case IrOpcode::kJSModulus: return ReduceJSModulus(node); - case IrOpcode::kJSUnaryNot: - return ReduceJSUnaryNot(node); case IrOpcode::kJSToBoolean: return ReduceJSToBoolean(node); case IrOpcode::kJSToNumber: @@ -2153,10 +2640,16 @@ Reduction JSTypedLowering::Reduce(Node* node) { return ReduceJSStoreContext(node); case IrOpcode::kJSConvertReceiver: return ReduceJSConvertReceiver(node); + case IrOpcode::kJSCreate: + return ReduceJSCreate(node); case IrOpcode::kJSCreateArguments: return ReduceJSCreateArguments(node); + case IrOpcode::kJSCreateArray: + return ReduceJSCreateArray(node); case IrOpcode::kJSCreateClosure: return ReduceJSCreateClosure(node); + case IrOpcode::kJSCreateIterResultObject: + return ReduceJSCreateIterResultObject(node); case IrOpcode::kJSCreateLiteralArray: return ReduceJSCreateLiteralArray(node); case IrOpcode::kJSCreateLiteralObject: @@ -2165,8 +2658,12 @@ Reduction JSTypedLowering::Reduce(Node* node) { return ReduceJSCreateFunctionContext(node); case IrOpcode::kJSCreateWithContext: return ReduceJSCreateWithContext(node); + case IrOpcode::kJSCreateCatchContext: + return ReduceJSCreateCatchContext(node); case IrOpcode::kJSCreateBlockContext: return ReduceJSCreateBlockContext(node); + case IrOpcode::kJSCallConstruct: + return ReduceJSCallConstruct(node); case IrOpcode::kJSCallFunction: return ReduceJSCallFunction(node); case IrOpcode::kJSForInDone: @@ -2177,6 +2674,8 @@ Reduction JSTypedLowering::Reduce(Node* node) { return ReduceJSForInPrepare(node); case IrOpcode::kJSForInStep: return ReduceJSForInStep(node); + case IrOpcode::kSelect: + return ReduceSelect(node); default: break; } @@ -2202,13 +2701,43 @@ Node* JSTypedLowering::AllocateArguments(Node* effect, Node* control, // Prepare an iterator over argument values recorded in the frame state. Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); StateValuesAccess parameters_access(parameters); - auto paratemers_it = ++parameters_access.begin(); + auto parameters_it = ++parameters_access.begin(); // Actually allocate the backing store. AllocationBuilder a(jsgraph(), effect, control); a.AllocateArray(argument_count, factory()->fixed_array_map()); - for (int i = 0; i < argument_count; ++i, ++paratemers_it) { - a.Store(AccessBuilder::ForFixedArraySlot(i), (*paratemers_it).node); + for (int i = 0; i < argument_count; ++i, ++parameters_it) { + a.Store(AccessBuilder::ForFixedArraySlot(i), (*parameters_it).node); + } + return a.Finish(); +} + + +// Helper that allocates a FixedArray holding argument values recorded in the +// given {frame_state}. Serves as backing store for JSCreateArguments nodes. +Node* JSTypedLowering::AllocateRestArguments(Node* effect, Node* control, + Node* frame_state, + int start_index) { + FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); + int argument_count = state_info.parameter_count() - 1; // Minus receiver. + int num_elements = std::max(0, argument_count - start_index); + if (num_elements == 0) return jsgraph()->EmptyFixedArrayConstant(); + + // Prepare an iterator over argument values recorded in the frame state. + Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); + StateValuesAccess parameters_access(parameters); + auto parameters_it = ++parameters_access.begin(); + + // Skip unused arguments. + for (int i = 0; i < start_index; i++) { + ++parameters_it; + } + + // Actually allocate the backing store. + AllocationBuilder a(jsgraph(), effect, control); + a.AllocateArray(num_elements, factory()->fixed_array_map()); + for (int i = 0; i < num_elements; ++i, ++parameters_it) { + a.Store(AccessBuilder::ForFixedArraySlot(i), (*parameters_it).node); } return a.Finish(); } @@ -2254,7 +2783,7 @@ Node* JSTypedLowering::AllocateAliasedArguments( Node* arguments = aa.Finish(); // Actually allocate the backing store. - AllocationBuilder a(jsgraph(), effect, control); + AllocationBuilder a(jsgraph(), arguments, control); a.AllocateArray(mapped_count + 2, factory()->sloppy_arguments_elements_map()); a.Store(AccessBuilder::ForFixedArraySlot(0), context); a.Store(AccessBuilder::ForFixedArraySlot(1), arguments); @@ -2266,6 +2795,34 @@ Node* JSTypedLowering::AllocateAliasedArguments( } +Node* JSTypedLowering::AllocateElements(Node* effect, Node* control, + ElementsKind elements_kind, + int capacity, PretenureFlag pretenure) { + DCHECK_LE(1, capacity); + DCHECK_LE(capacity, JSArray::kInitialMaxFastElementArray); + + Handle<Map> elements_map = IsFastDoubleElementsKind(elements_kind) + ? factory()->fixed_double_array_map() + : factory()->fixed_array_map(); + ElementAccess access = IsFastDoubleElementsKind(elements_kind) + ? AccessBuilder::ForFixedDoubleArrayElement() + : AccessBuilder::ForFixedArrayElement(); + Node* value = + IsFastDoubleElementsKind(elements_kind) + ? jsgraph()->Float64Constant(bit_cast<double>(kHoleNanInt64)) + : jsgraph()->TheHoleConstant(); + + // Actually allocate the backing store. + AllocationBuilder a(jsgraph(), effect, control); + a.AllocateArray(capacity, elements_map, pretenure); + for (int i = 0; i < capacity; ++i) { + Node* index = jsgraph()->Constant(i); + a.Store(access, index, value); + } + return a.Finish(); +} + + Factory* JSTypedLowering::factory() const { return jsgraph()->factory(); } |