summaryrefslogtreecommitdiff
path: root/chromium/v8/src/compiler/js-typed-lowering.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/v8/src/compiler/js-typed-lowering.cc')
-rw-r--r--chromium/v8/src/compiler/js-typed-lowering.cc1820
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