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