diff options
Diffstat (limited to 'chromium/v8/src/compiler/js-typed-lowering.cc')
-rw-r--r-- | chromium/v8/src/compiler/js-typed-lowering.cc | 1820 |
1 files changed, 1445 insertions, 375 deletions
diff --git a/chromium/v8/src/compiler/js-typed-lowering.cc b/chromium/v8/src/compiler/js-typed-lowering.cc index 7c25afcfafa..a31aa0070c6 100644 --- a/chromium/v8/src/compiler/js-typed-lowering.cc +++ b/chromium/v8/src/compiler/js-typed-lowering.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "src/code-factory.h" +#include "src/compilation-dependencies.h" #include "src/compiler/access-builder.h" #include "src/compiler/js-graph.h" #include "src/compiler/js-typed-lowering.h" @@ -10,44 +11,33 @@ #include "src/compiler/node-matchers.h" #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, JSGraph* jsgraph, Zone* zone) - : AdvancedReducer(editor), jsgraph_(jsgraph), simplified_(graph()->zone()) { - 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 // allocated object and also provides helpers for commonly allocated objects. class AllocationBuilder final { public: - AllocationBuilder(JSGraph* jsgraph, SimplifiedOperatorBuilder* simplified, - Node* effect, Node* control) + AllocationBuilder(JSGraph* jsgraph, Node* effect, Node* control) : jsgraph_(jsgraph), - simplified_(simplified), allocation_(nullptr), effect_(effect), control_(control) {} // Primitive allocation of static size. - void Allocate(int size) { - allocation_ = graph()->NewNode( - simplified()->Allocate(), jsgraph()->Constant(size), effect_, control_); + void Allocate(int size, PretenureFlag pretenure = NOT_TENURED) { + effect_ = graph()->NewNode(common()->BeginRegion(), effect_); + allocation_ = + graph()->NewNode(simplified()->Allocate(pretenure), + jsgraph()->Constant(size), effect_, control_); effect_ = allocation_; } @@ -57,12 +47,23 @@ 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(graph()->zone()), - jsgraph()->Constant(length)); + Store(AccessBuilder::ForFixedArrayLength(), jsgraph()->Constant(length)); } // Compound store of a constant into a field. @@ -70,22 +71,33 @@ class AllocationBuilder final { Store(access, jsgraph()->Constant(value)); } - Node* allocation() const { return allocation_; } - Node* effect() const { return effect_; } + void FinishAndChange(Node* node) { + NodeProperties::SetType(allocation_, NodeProperties::GetType(node)); + node->ReplaceInput(0, allocation_); + node->ReplaceInput(1, effect_); + node->TrimInputCount(2); + NodeProperties::ChangeOp(node, common()->FinishRegion()); + } + + Node* Finish() { + return graph()->NewNode(common()->FinishRegion(), allocation_, effect_); + } protected: JSGraph* jsgraph() { return jsgraph_; } Graph* graph() { return jsgraph_->graph(); } - SimplifiedOperatorBuilder* simplified() { return simplified_; } + CommonOperatorBuilder* common() { return jsgraph_->common(); } + SimplifiedOperatorBuilder* simplified() { return jsgraph_->simplified(); } private: JSGraph* const jsgraph_; - SimplifiedOperatorBuilder* simplified_; Node* allocation_; Node* effect_; 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 @@ -135,11 +147,6 @@ class JSBinopReduction final { node_->ReplaceInput(1, ConvertToUI32(right(), right_signedness)); } - void ConvertInputsToString() { - node_->ReplaceInput(0, ConvertToString(left())); - node_->ReplaceInput(1, ConvertToString(right())); - } - void SwapInputs() { Node* l = left(); Node* r = right(); @@ -211,13 +218,24 @@ 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 OneInputIs(Type* t) { return left_type()->Is(t) || right_type()->Is(t); } + bool LeftInputIs(Type* t) { return left_type()->Is(t); } - bool BothInputsAre(Type* t) { - return left_type()->Is(t) && right_type()->Is(t); - } + bool RightInputIs(Type* t) { return right_type()->Is(t); } + + bool OneInputIs(Type* t) { return LeftInputIs(t) || RightInputIs(t); } + + bool BothInputsAre(Type* t) { return LeftInputIs(t) && RightInputIs(t); } bool OneInputCannotBe(Type* t) { return !left_type()->Maybe(t) || !right_type()->Maybe(t); @@ -247,16 +265,6 @@ class JSBinopReduction final { JSTypedLowering* lowering_; // The containing lowering instance. Node* node_; // The original node. - Node* ConvertToString(Node* node) { - // Avoid introducing too many eager ToString() operations. - Reduction reduced = lowering_->ReduceJSToStringInput(node); - if (reduced.Changed()) return reduced.replacement(); - Node* n = graph()->NewNode(javascript()->ToString(), node, context(), - effect(), control()); - update_effect(n); - return n; - } - Node* CreateFrameStateForLeftInput(Node* frame_state) { FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); @@ -376,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); @@ -416,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) @@ -447,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) @@ -459,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())) { @@ -473,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())) { @@ -491,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())) { @@ -507,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. @@ -578,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())) { @@ -587,18 +634,40 @@ Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) { return r.ChangeToStringComparisonOperator(simplified()->StringEqual(), invert); } + if (r.BothInputsAre(Type::Boolean())) { + return r.ChangeToPureOperator(simplified()->ReferenceEqual(Type::Boolean()), + invert); + } if (r.BothInputsAre(Type::Receiver())) { return r.ChangeToPureOperator( simplified()->ReferenceEqual(Type::Receiver()), invert); } - // TODO(turbofan): js-typed-lowering of Equal(undefined) - // TODO(turbofan): js-typed-lowering of Equal(null) - // TODO(turbofan): js-typed-lowering of Equal(boolean) + if (r.OneInputIs(Type::NullOrUndefined())) { + Callable const callable = CodeFactory::CompareNilIC(isolate(), kNullValue); + CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( + isolate(), graph()->zone(), callable.descriptor(), 0, + CallDescriptor::kNeedsFrameState, node->op()->properties()); + node->RemoveInput(r.LeftInputIs(Type::NullOrUndefined()) ? 0 : 1); + node->InsertInput(graph()->zone(), 0, + jsgraph()->HeapConstant(callable.code())); + NodeProperties::ChangeOp(node, common()->Call(desc)); + if (invert) { + // Insert an boolean not to invert the value. + Node* value = graph()->NewNode(simplified()->BooleanNot(), node); + node->ReplaceUses(value); + // Note: ReplaceUses() smashes all uses, so smash it back here. + value->ReplaceInput(0, node); + return Replace(value); + } + return Changed(node); + } return NoChange(); } 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 @@ -617,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); @@ -653,46 +726,13 @@ 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(graph()->zone()); - // 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); + Node* const effect = NodeProperties::GetEffectInput(node); if (input_type->Is(Type::Boolean())) { // JSToBoolean(x:boolean) => x - ReplaceWithValue(node, input); + ReplaceWithValue(node, input, effect); return Replace(input); } else if (input_type->Is(Type::OrderedNumber())) { // JSToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x,#0)) @@ -704,11 +744,9 @@ Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) { return Changed(node); } else if (input_type->Is(Type::String())) { // JSToBoolean(x:string) => NumberLessThan(#0,x.length) - FieldAccess const access = AccessBuilder::ForStringLength(graph()->zone()); - // 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. + FieldAccess const access = AccessBuilder::ForStringLength(); Node* length = graph()->NewNode(simplified()->LoadField(access), input, - graph()->start(), graph()->start()); + effect, graph()->start()); ReplaceWithValue(node, node, length); node->ReplaceInput(0, jsgraph()->ZeroConstant()); node->ReplaceInput(1, length); @@ -727,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())) { @@ -791,13 +844,18 @@ Reduction JSTypedLowering::ReduceJSToStringInput(Node* input) { if (input_type->Is(Type::String())) { return Changed(input); // JSToString(x:string) => x } + if (input_type->Is(Type::Boolean())) { + 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())); } if (input_type->Is(Type::Null())) { return Replace(jsgraph()->HeapConstant(factory()->null_string())); } - // TODO(turbofan): js-typed-lowering of ToString(x:boolean) // TODO(turbofan): js-typed-lowering of ToString(x:number) return NoChange(); } @@ -815,16 +873,83 @@ Reduction JSTypedLowering::ReduceJSToString(Node* node) { } -Reduction JSTypedLowering::ReduceJSLoadGlobal(Node* node) { - // Optimize global constants like "undefined", "Infinity", and "NaN". - Handle<Name> name = LoadGlobalParametersOf(node->op()).name(); - Handle<Object> constant_value = factory()->GlobalConstantFor(name); - if (!constant_value.is_null()) { - Node* constant = jsgraph()->Constant(constant_value); - ReplaceWithValue(node, constant); - return Replace(constant); +Reduction JSTypedLowering::ReduceJSToObject(Node* node) { + DCHECK_EQ(IrOpcode::kJSToObject, node->opcode()); + Node* receiver = NodeProperties::GetValueInput(node, 0); + Type* receiver_type = NodeProperties::GetType(receiver); + Node* context = NodeProperties::GetContextInput(node); + Node* frame_state = NodeProperties::GetFrameStateInput(node, 0); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + if (!receiver_type->Is(Type::Receiver())) { + // TODO(bmeurer/mstarzinger): Add support for lowering inside try blocks. + if (receiver_type->Maybe(Type::NullOrUndefined()) && + NodeProperties::IsExceptionalCall(node)) { + // ToObject throws for null or undefined inputs. + return NoChange(); + } + + // Check whether {receiver} is a Smi. + Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); + Node* branch0 = + graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control); + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); + Node* etrue0 = effect; + + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); + Node* efalse0 = effect; + + // Determine the instance type of {receiver}. + Node* receiver_map = efalse0 = + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), + receiver, efalse0, if_false0); + Node* receiver_instance_type = efalse0 = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForMapInstanceType()), + receiver_map, efalse0, if_false0); + + // Check whether {receiver} is a spec object. + STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); + Node* check1 = + graph()->NewNode(machine()->Uint32LessThanOrEqual(), + jsgraph()->Uint32Constant(FIRST_JS_RECEIVER_TYPE), + receiver_instance_type); + Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue), + check1, if_false0); + Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); + Node* etrue1 = efalse0; + + Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); + Node* efalse1 = efalse0; + + // Convert {receiver} using the ToObjectStub. + Node* if_convert = + graph()->NewNode(common()->Merge(2), if_true0, if_false1); + Node* econvert = + graph()->NewNode(common()->EffectPhi(2), etrue0, efalse1, if_convert); + Node* rconvert; + { + Callable callable = CodeFactory::ToObject(isolate()); + CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( + isolate(), graph()->zone(), callable.descriptor(), 0, + CallDescriptor::kNeedsFrameState, node->op()->properties()); + rconvert = econvert = graph()->NewNode( + common()->Call(desc), jsgraph()->HeapConstant(callable.code()), + receiver, context, frame_state, econvert, if_convert); + } + + // The {receiver} is already a spec object. + Node* if_done = if_true1; + Node* edone = etrue1; + Node* rdone = receiver; + + control = graph()->NewNode(common()->Merge(2), if_convert, if_done); + effect = graph()->NewNode(common()->EffectPhi(2), econvert, edone, control); + receiver = + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + rconvert, rdone, control); } - return NoChange(); + ReplaceWithValue(node, receiver, effect, control); + return Changed(receiver); } @@ -834,17 +959,37 @@ Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) { Type* receiver_type = NodeProperties::GetType(receiver); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); - Handle<Name> name = LoadNamedParametersOf(node->op()).name(); + Handle<Name> name = NamedAccessOf(node->op()).name(); // Optimize "length" property of strings. if (name.is_identical_to(factory()->length_string()) && receiver_type->Is(Type::String())) { - Node* value = effect = - graph()->NewNode(simplified()->LoadField( - AccessBuilder::ForStringLength(graph()->zone())), - receiver, effect, control); + Node* value = effect = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, + effect, control); 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(); } @@ -860,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) { @@ -906,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 && @@ -932,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); @@ -975,17 +1114,187 @@ 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) || + (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()) { + return NoChange(); + } + + Handle<JSFunction> function = + Handle<JSFunction>::cast(r.right_type()->AsConstant()->Value()); + Handle<SharedFunctionInfo> shared(function->shared(), isolate()); + + if (!function->IsConstructor() || + function->map()->has_non_instance_prototype()) { + 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); @@ -999,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()))); @@ -1016,124 +1326,184 @@ Reduction JSTypedLowering::ReduceJSStoreContext(Node* node) { } -Reduction JSTypedLowering::ReduceJSLoadDynamicGlobal(Node* node) { - DCHECK_EQ(IrOpcode::kJSLoadDynamicGlobal, node->opcode()); - DynamicGlobalAccess const& access = DynamicGlobalAccessOf(node->op()); - Node* const vector = NodeProperties::GetValueInput(node, 0); - Node* const context = NodeProperties::GetContextInput(node); - Node* const state1 = NodeProperties::GetFrameStateInput(node, 0); - Node* const state2 = NodeProperties::GetFrameStateInput(node, 1); - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = NodeProperties::GetControlInput(node); - if (access.RequiresFullCheck()) return NoChange(); - - // Perform checks whether the fast mode applies, by looking for any extension - // object which might shadow the optimistic declaration. - uint32_t bitset = access.check_bitset(); - Node* check_true = control; - Node* check_false = graph()->NewNode(common()->Merge(0)); - for (int depth = 0; bitset != 0; bitset >>= 1, depth++) { - if ((bitset & 1) == 0) continue; - Node* load = graph()->NewNode( - javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false), - context, context, effect); - Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), - load, jsgraph()->ZeroConstant()); - Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, - check_true); - Node* if_true = graph()->NewNode(common()->IfTrue(), branch); - Node* if_false = graph()->NewNode(common()->IfFalse(), branch); - check_false->AppendInput(graph()->zone(), if_false); - NodeProperties::ChangeOp(check_false, - common()->Merge(check_false->InputCount())); - check_true = if_true; - } - - // Fast case, because variable is not shadowed. Perform global object load. - Node* global = graph()->NewNode( - javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true), context, - context, effect); - Node* fast = graph()->NewNode( - javascript()->LoadGlobal(access.name(), access.feedback(), - access.typeof_mode()), - context, global, vector, context, state1, state2, global, check_true); - - // Slow case, because variable potentially shadowed. Perform dynamic lookup. - uint32_t check_bitset = DynamicGlobalAccess::kFullCheckRequired; - Node* slow = graph()->NewNode( - javascript()->LoadDynamicGlobal(access.name(), check_bitset, - access.feedback(), access.typeof_mode()), - vector, context, context, state1, state2, effect, check_false); - - // Replace value, effect and control uses accordingly. - Node* new_control = - graph()->NewNode(common()->Merge(2), check_true, check_false); - Node* new_effect = - graph()->NewNode(common()->EffectPhi(2), fast, slow, new_control); - Node* new_value = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), fast, - slow, new_control); - ReplaceWithValue(node, new_value, new_effect, new_control); - return Changed(new_value); +Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) { + DCHECK_EQ(IrOpcode::kJSConvertReceiver, node->opcode()); + ConvertReceiverMode mode = ConvertReceiverModeOf(node->op()); + Node* receiver = NodeProperties::GetValueInput(node, 0); + Type* receiver_type = NodeProperties::GetType(receiver); + Node* context = NodeProperties::GetContextInput(node); + Type* context_type = NodeProperties::GetType(context); + Node* frame_state = NodeProperties::GetFrameStateInput(node, 0); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + if (!receiver_type->Is(Type::Receiver())) { + if (receiver_type->Is(Type::NullOrUndefined()) || + mode == ConvertReceiverMode::kNullOrUndefined) { + if (context_type->IsConstant()) { + Handle<JSObject> global_proxy( + Handle<Context>::cast(context_type->AsConstant()->Value()) + ->global_proxy(), + isolate()); + receiver = jsgraph()->Constant(global_proxy); + } else { + Node* native_context = effect = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, effect); + 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) { + receiver = effect = + graph()->NewNode(javascript()->ToObject(), receiver, context, + frame_state, effect, control); + } else { + // Check {receiver} for undefined. + Node* check0 = + graph()->NewNode(simplified()->ReferenceEqual(receiver_type), + receiver, jsgraph()->UndefinedConstant()); + Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse), + check0, control); + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); + + // Check {receiver} for null. + Node* check1 = + graph()->NewNode(simplified()->ReferenceEqual(receiver_type), + receiver, jsgraph()->NullConstant()); + Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), + check1, if_false0); + Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); + Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); + + // Convert {receiver} using ToObject. + Node* if_convert = if_false1; + Node* econvert = effect; + Node* rconvert; + { + rconvert = econvert = + graph()->NewNode(javascript()->ToObject(), receiver, context, + frame_state, econvert, if_convert); + } + + // Replace {receiver} with global proxy of {context}. + Node* if_global = + graph()->NewNode(common()->Merge(2), if_true0, if_true1); + Node* eglobal = effect; + Node* rglobal; + { + if (context_type->IsConstant()) { + Handle<JSObject> global_proxy( + Handle<Context>::cast(context_type->AsConstant()->Value()) + ->global_proxy(), + isolate()); + rglobal = jsgraph()->Constant(global_proxy); + } else { + Node* native_context = eglobal = graph()->NewNode( + javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), + context, context, eglobal); + rglobal = eglobal = graph()->NewNode( + 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(MachineRepresentation::kTagged, 2), + rconvert, rglobal, control); + } + } + ReplaceWithValue(node, receiver, effect, control); + return Changed(receiver); } -Reduction JSTypedLowering::ReduceJSLoadDynamicContext(Node* node) { - DCHECK_EQ(IrOpcode::kJSLoadDynamicContext, node->opcode()); - DynamicContextAccess const& access = DynamicContextAccessOf(node->op()); - ContextAccess const& context_access = access.context_access(); - Node* const context = NodeProperties::GetContextInput(node); - Node* const state = NodeProperties::GetFrameStateInput(node, 0); +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); - Node* const control = NodeProperties::GetControlInput(node); - if (access.RequiresFullCheck()) return NoChange(); - - // Perform checks whether the fast mode applies, by looking for any extension - // object which might shadow the optimistic declaration. - uint32_t bitset = access.check_bitset(); - Node* check_true = control; - Node* check_false = graph()->NewNode(common()->Merge(0)); - for (int depth = 0; bitset != 0; bitset >>= 1, depth++) { - if ((bitset & 1) == 0) continue; - Node* load = graph()->NewNode( - javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false), - context, context, effect); - Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), - load, jsgraph()->ZeroConstant()); - Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, - check_true); - Node* if_true = graph()->NewNode(common()->IfTrue(), branch); - Node* if_false = graph()->NewNode(common()->IfFalse(), branch); - check_false->AppendInput(graph()->zone(), if_false); - NodeProperties::ChangeOp(check_false, - common()->Merge(check_false->InputCount())); - check_true = if_true; - } - - // Fast case, because variable is not shadowed. Perform context slot load. - Node* fast = - graph()->NewNode(javascript()->LoadContext(context_access.depth(), - context_access.index(), false), - context, context, effect); - - // Slow case, because variable potentially shadowed. Perform dynamic lookup. - uint32_t check_bitset = DynamicContextAccess::kFullCheckRequired; - Node* slow = - graph()->NewNode(javascript()->LoadDynamicContext( - access.name(), check_bitset, context_access.depth(), - context_access.index()), - context, context, state, effect, check_false); - - // Replace value, effect and control uses accordingly. - Node* new_control = - graph()->NewNode(common()->Merge(2), check_true, check_false); - Node* new_effect = - graph()->NewNode(common()->EffectPhi(2), fast, slow, new_control); - Node* new_value = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), fast, - slow, new_control); - ReplaceWithValue(node, new_value, new_effect, new_control); - return Changed(new_value); + // 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); + FrameStateInfo outer_state_info = OpParameter<FrameStateInfo>(outer_state); + return outer_state_info.type() == FrameStateType::kArgumentsAdaptor + ? outer_state + : frame_state; } +} // namespace + Reduction JSTypedLowering::ReduceJSCreateArguments(Node* node) { DCHECK_EQ(IrOpcode::kJSCreateArguments, node->opcode()); @@ -1144,36 +1514,305 @@ 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); + } } 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); + } + } + } + + // 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); + } +} + + Reduction JSTypedLowering::ReduceJSCreateClosure(Node* node) { DCHECK_EQ(IrOpcode::kJSCreateClosure, node->opcode()); CreateClosureParameters const& p = CreateClosureParametersOf(node->op()); @@ -1200,18 +1839,50 @@ 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. // TODO(rossberg): Teach strong mode to FastCloneShallowArrayStub. if ((flags & ArrayLiteral::kShallowElements) != 0 && (flags & ArrayLiteral::kIsStrong) == 0 && - length < JSObject::kInitialMaxFastElementArray) { + length < JSArray::kInitialMaxFastElementArray) { Isolate* isolate = jsgraph()->isolate(); Callable callable = CodeFactory::FastCloneShallowArray(isolate); CallDescriptor* desc = Linkage::GetStubCallDescriptor( @@ -1221,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); } @@ -1232,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. @@ -1250,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); } @@ -1260,74 +1941,200 @@ Reduction JSTypedLowering::ReduceJSCreateLiteralObject(Node* node) { } -Reduction JSTypedLowering::ReduceJSCreateWithContext(Node* node) { - DCHECK_EQ(IrOpcode::kJSCreateWithContext, node->opcode()); - Node* const input = NodeProperties::GetValueInput(node, 0); - Type* input_type = NodeProperties::GetType(input); - if (FLAG_turbo_allocate && input_type->Is(Type::Receiver())) { - // JSCreateWithContext(o:receiver, f) - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = NodeProperties::GetControlInput(node); - Node* const closure = NodeProperties::GetValueInput(node, 1); - 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(), simplified(), effect, control); +Reduction JSTypedLowering::ReduceJSCreateFunctionContext(Node* node) { + DCHECK_EQ(IrOpcode::kJSCreateFunctionContext, node->opcode()); + int slot_count = OpParameter<int>(node->op()); + Node* const closure = NodeProperties::GetValueInput(node, 0); + + // Use inline allocation for function contexts up to a size limit. + if (slot_count < kFunctionContextAllocationLimit) { + // JSCreateFunctionContext[slot_count < limit]](fun) + 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. - a.AllocateArray(Context::MIN_CONTEXT_SLOTS, factory()->with_context_map()); + int context_length = slot_count + Context::MIN_CONTEXT_SLOTS; + a.AllocateArray(context_length, factory()->function_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); - // TODO(mstarzinger): We could mutate {node} into the allocation instead. - NodeProperties::SetType(a.allocation(), NodeProperties::GetType(node)); - ReplaceWithValue(node, node, a.effect()); - node->ReplaceInput(0, a.allocation()); - node->ReplaceInput(1, a.effect()); - node->TrimInputCount(2); - NodeProperties::ChangeOp(node, common()->Finish(1)); + a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension); + 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()); + } + RelaxControls(node); + a.FinishAndChange(node); + return Changed(node); + } + + // Use the FastNewContextStub only for function contexts up maximum size. + if (slot_count <= FastNewContextStub::kMaximumSlots) { + Isolate* isolate = jsgraph()->isolate(); + Callable callable = CodeFactory::FastNewContext(isolate, slot_count); + CallDescriptor* desc = Linkage::GetStubCallDescriptor( + isolate, graph()->zone(), callable.descriptor(), 0, + CallDescriptor::kNoFlags); + const Operator* new_op = common()->Call(desc); + Node* stub_code = jsgraph()->HeapConstant(callable.code()); + node->InsertInput(graph()->zone(), 0, stub_code); + NodeProperties::ChangeOp(node, new_op); return Changed(node); } + return NoChange(); } +Reduction JSTypedLowering::ReduceJSCreateWithContext(Node* node) { + DCHECK_EQ(IrOpcode::kJSCreateWithContext, node->opcode()); + 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); +} + + +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); +} + + Reduction JSTypedLowering::ReduceJSCreateBlockContext(Node* node) { DCHECK_EQ(IrOpcode::kJSCreateBlockContext, node->opcode()); Handle<ScopeInfo> scope_info = OpParameter<Handle<ScopeInfo>>(node); int context_length = scope_info->ContextLength(); - if (FLAG_turbo_allocate && context_length < kBlockContextAllocationLimit) { - // JSCreateBlockContext(s:scope[length < limit], f) - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = NodeProperties::GetControlInput(node); - Node* const closure = NodeProperties::GetValueInput(node, 1); - 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); - AllocationBuilder a(jsgraph(), simplified(), effect, control); + Node* const closure = NodeProperties::GetValueInput(node, 0); + + // Use inline allocation for block contexts up to a size limit. + if (context_length < kBlockContextAllocationLimit) { + // JSCreateBlockContext[scope[length < limit]](fun) + 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()); } - // TODO(mstarzinger): We could mutate {node} into the allocation instead. - NodeProperties::SetType(a.allocation(), NodeProperties::GetType(node)); - ReplaceWithValue(node, node, a.effect()); - node->ReplaceInput(0, a.allocation()); - node->ReplaceInput(1, a.effect()); - node->TrimInputCount(2); - NodeProperties::ChangeOp(node, common()->Finish(1)); + RelaxControls(node); + a.FinishAndChange(node); + return Changed(node); + } + + return NoChange(); +} + + +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(); } @@ -1336,32 +2143,121 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) { DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); int const arity = static_cast<int>(p.arity() - 2); - Node* const function = NodeProperties::GetValueInput(node, 0); - Type* const function_type = NodeProperties::GetType(function); - Node* const receiver = NodeProperties::GetValueInput(node, 1); - Type* const receiver_type = NodeProperties::GetType(receiver); - Node* const effect = NodeProperties::GetEffectInput(node); - Node* const control = NodeProperties::GetControlInput(node); - - // Check that {function} is actually a JSFunction with the correct arity. - if (function_type->IsFunction() && - function_type->AsFunction()->Arity() == arity) { - // Check that the {receiver} doesn't need to be wrapped. - if (receiver_type->Is(Type::ReceiverOrUndefined())) { - Node* const context = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), - function, effect, control); - NodeProperties::ReplaceContextInput(node, context); - CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; - if (is_strict(p.language_mode())) { - flags |= CallDescriptor::kSupportsTailCalls; - } + ConvertReceiverMode convert_mode = p.convert_mode(); + Node* target = NodeProperties::GetValueInput(node, 0); + Type* target_type = NodeProperties::GetType(target); + Node* receiver = NodeProperties::GetValueInput(node, 1); + Type* receiver_type = NodeProperties::GetType(receiver); + Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + // Try to infer receiver {convert_mode} from {receiver} type. + if (receiver_type->Is(Type::NullOrUndefined())) { + convert_mode = ConvertReceiverMode::kNullOrUndefined; + } else if (!receiver_type->Maybe(Type::NullOrUndefined())) { + convert_mode = ConvertReceiverMode::kNotNullOrUndefined; + } + + // 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()); + + // Class constructors are callable, but [[Call]] will raise an exception. + // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). + if (IsClassConstructor(shared->kind())) return NoChange(); + + // 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}. + if (is_sloppy(shared->language_mode()) && !shared->native() && + !receiver_type->Is(Type::Receiver())) { + receiver = effect = + graph()->NewNode(javascript()->ConvertReceiver(convert_mode), + receiver, context, frame_state, effect, control); + 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); + + // Compute flags for the call. + CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; + if (p.tail_call_mode() == TailCallMode::kAllow) { + 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, new_target); + node->InsertInput(graph()->zone(), arity + 3, argument_count); NodeProperties::ChangeOp(node, common()->Call(Linkage::GetJSCallDescriptor( graph()->zone(), false, 1 + arity, flags))); - return Changed(node); + } else { + // Patch {node} to an indirect call via the ArgumentsAdaptorTrampoline. + Callable callable = CodeFactory::ArgumentAdaptor(isolate()); + node->InsertInput(graph()->zone(), 0, + jsgraph()->HeapConstant(callable.code())); + node->InsertInput(graph()->zone(), 2, new_target); + node->InsertInput(graph()->zone(), 3, argument_count); + node->InsertInput( + graph()->zone(), 4, + jsgraph()->Int32Constant(shared->internal_formal_parameter_count())); + NodeProperties::ChangeOp( + node, common()->Call(Linkage::GetStubCallDescriptor( + isolate(), graph()->zone(), callable.descriptor(), + 1 + arity, flags))); } + 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); + + // Compute flags for the call. + CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; + if (p.tail_call_mode() == TailCallMode::kAllow) { + flags |= CallDescriptor::kSupportsTailCalls; + } + + // Patch {node} to an indirect call via the CallFunction builtin. + Callable callable = CodeFactory::CallFunction(isolate(), convert_mode); + node->InsertInput(graph()->zone(), 0, + jsgraph()->HeapConstant(callable.code())); + node->InsertInput(graph()->zone(), 2, jsgraph()->Int32Constant(arity)); + NodeProperties::ChangeOp( + node, common()->Call(Linkage::GetStubCallDescriptor( + isolate(), graph()->zone(), callable.descriptor(), 1 + arity, + flags))); + return Changed(node); + } + + // Maybe we did at least learn something about the {receiver}. + if (p.convert_mode() != convert_mode) { + NodeProperties::ChangeOp( + node, + javascript()->CallFunction(p.arity(), p.language_mode(), p.feedback(), + convert_mode, p.tail_call_mode())); + return Changed(node); + } + return NoChange(); } @@ -1414,9 +2310,9 @@ Reduction JSTypedLowering::ReduceJSForInPrepare(Node* node) { Node* cache_type_enum_length = etrue0 = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForMapBitField3()), cache_type, effect, if_true0); - cache_length_true0 = - graph()->NewNode(machine()->Word32And(), cache_type_enum_length, - jsgraph()->Uint32Constant(Map::EnumLengthBits::kMask)); + cache_length_true0 = graph()->NewNode( + simplified()->NumberBitwiseAnd(), cache_type_enum_length, + jsgraph()->Int32Constant(Map::EnumLengthBits::kMask)); Node* check1 = graph()->NewNode(machine()->Word32Equal(), cache_length_true0, @@ -1455,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; } @@ -1468,36 +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(graph()->zone())), - cache_array_false0, efalse0, if_false0); + simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), + 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(); @@ -1610,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); @@ -1621,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); } @@ -1634,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. @@ -1681,11 +2596,11 @@ Reduction JSTypedLowering::Reduce(Node* node) { case IrOpcode::kJSGreaterThanOrEqual: return ReduceJSComparison(node); case IrOpcode::kJSBitwiseOr: - return ReduceInt32Binop(node, machine()->Word32Or()); + return ReduceInt32Binop(node, simplified()->NumberBitwiseOr()); case IrOpcode::kJSBitwiseXor: - return ReduceInt32Binop(node, machine()->Word32Xor()); + return ReduceInt32Binop(node, simplified()->NumberBitwiseXor()); case IrOpcode::kJSBitwiseAnd: - return ReduceInt32Binop(node, machine()->Word32And()); + return ReduceInt32Binop(node, simplified()->NumberBitwiseAnd()); case IrOpcode::kJSShiftLeft: return ReduceUI32Shift(node, kSigned, simplified()->NumberShiftLeft()); case IrOpcode::kJSShiftRight: @@ -1703,42 +2618,52 @@ 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: return ReduceJSToNumber(node); case IrOpcode::kJSToString: return ReduceJSToString(node); - case IrOpcode::kJSLoadGlobal: - return ReduceJSLoadGlobal(node); + case IrOpcode::kJSToObject: + return ReduceJSToObject(node); case IrOpcode::kJSLoadNamed: return ReduceJSLoadNamed(node); case IrOpcode::kJSLoadProperty: return ReduceJSLoadProperty(node); case IrOpcode::kJSStoreProperty: return ReduceJSStoreProperty(node); + case IrOpcode::kJSInstanceOf: + return ReduceJSInstanceOf(node); case IrOpcode::kJSLoadContext: return ReduceJSLoadContext(node); case IrOpcode::kJSStoreContext: return ReduceJSStoreContext(node); - case IrOpcode::kJSLoadDynamicGlobal: - return ReduceJSLoadDynamicGlobal(node); - case IrOpcode::kJSLoadDynamicContext: - return ReduceJSLoadDynamicContext(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: return ReduceJSCreateLiteralObject(node); + case IrOpcode::kJSCreateFunctionContext: + 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: @@ -1749,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; } @@ -1763,6 +2690,139 @@ Node* JSTypedLowering::Word32Shl(Node* const lhs, int32_t const rhs) { } +// Helper that allocates a FixedArray holding argument values recorded in the +// given {frame_state}. Serves as backing store for JSCreateArguments nodes. +Node* JSTypedLowering::AllocateArguments(Node* effect, Node* control, + Node* frame_state) { + FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); + int argument_count = state_info.parameter_count() - 1; // Minus receiver. + if (argument_count == 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(); + + // 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, ++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(); +} + + +// Helper that allocates a FixedArray serving as a parameter map for values +// recorded in the given {frame_state}. Some elements map to slots within the +// given {context}. Serves as backing store for JSCreateArguments nodes. +Node* JSTypedLowering::AllocateAliasedArguments( + Node* effect, Node* control, Node* frame_state, Node* context, + Handle<SharedFunctionInfo> shared, bool* has_aliased_arguments) { + FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); + int argument_count = state_info.parameter_count() - 1; // Minus receiver. + if (argument_count == 0) return jsgraph()->EmptyFixedArrayConstant(); + + // If there is no aliasing, the arguments object elements are not special in + // any way, we can just return an unmapped backing store instead. + int parameter_count = shared->internal_formal_parameter_count(); + if (parameter_count == 0) { + return AllocateArguments(effect, control, frame_state); + } + + // Calculate number of argument values being aliased/mapped. + int mapped_count = Min(argument_count, parameter_count); + *has_aliased_arguments = true; + + // 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(); + + // The unmapped argument values recorded in the frame state are stored yet + // another indirection away and then linked into the parameter map below, + // whereas mapped argument values are replaced with a hole instead. + AllocationBuilder aa(jsgraph(), effect, control); + aa.AllocateArray(argument_count, factory()->fixed_array_map()); + for (int i = 0; i < mapped_count; ++i, ++paratemers_it) { + aa.Store(AccessBuilder::ForFixedArraySlot(i), jsgraph()->TheHoleConstant()); + } + for (int i = mapped_count; i < argument_count; ++i, ++paratemers_it) { + aa.Store(AccessBuilder::ForFixedArraySlot(i), (*paratemers_it).node); + } + Node* arguments = aa.Finish(); + + // Actually allocate the backing store. + 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); + for (int i = 0; i < mapped_count; ++i) { + int idx = Context::MIN_CONTEXT_SLOTS + parameter_count - 1 - i; + a.Store(AccessBuilder::ForFixedArraySlot(i + 2), jsgraph()->Constant(idx)); + } + return a.Finish(); +} + + +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(); } @@ -1782,10 +2842,20 @@ CommonOperatorBuilder* JSTypedLowering::common() const { } +SimplifiedOperatorBuilder* JSTypedLowering::simplified() const { + return jsgraph()->simplified(); +} + + MachineOperatorBuilder* JSTypedLowering::machine() const { return jsgraph()->machine(); } + +CompilationDependencies* JSTypedLowering::dependencies() const { + return dependencies_; +} + } // namespace compiler } // namespace internal } // namespace v8 |