Index: src/compiler/js-typed-lowering.cc |
diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc |
index 1fcb0f272e7422d191e8cab62bd0cbe44bbe91e8..0d83d2baf1949a94def00b654b5abd74e10bcc47 100644 |
--- a/src/compiler/js-typed-lowering.cc |
+++ b/src/compiler/js-typed-lowering.cc |
@@ -844,6 +844,85 @@ Reduction JSTypedLowering::ReduceJSToString(Node* node) { |
} |
+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(kMachAnyTagged, 2), rconvert, |
+ rdone, control); |
+ } |
+ ReplaceWithValue(node, receiver, effect, control); |
+ return Changed(receiver); |
+} |
+ |
+ |
Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) { |
DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); |
Node* receiver = NodeProperties::GetValueInput(node, 0); |
@@ -1181,7 +1260,62 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) { |
graph()->NewNode(javascript()->ToObject(), receiver, context, |
frame_state, effect, control); |
} else { |
- return NoChange(); |
+ // 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* global_object = eglobal = graph()->NewNode( |
+ javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true), |
+ context, context, eglobal); |
+ rglobal = eglobal = |
+ graph()->NewNode(simplified()->LoadField( |
+ AccessBuilder::ForGlobalObjectGlobalProxy()), |
+ global_object, eglobal, if_global); |
+ } |
+ } |
+ |
+ control = graph()->NewNode(common()->Merge(2), if_convert, if_global); |
+ effect = |
+ graph()->NewNode(common()->EffectPhi(2), econvert, eglobal, control); |
+ receiver = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), rconvert, |
+ rglobal, control); |
} |
} |
ReplaceWithValue(node, receiver, effect, control); |
@@ -1554,10 +1688,14 @@ 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); |
+ ConvertReceiverMode const 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); |
// Check if {target} is a known JSFunction. |
if (target_type->IsConstant() && |
@@ -1566,28 +1704,27 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) { |
Handle<JSFunction>::cast(target_type->AsConstant()->Value()); |
Handle<SharedFunctionInfo> shared(function->shared(), isolate()); |
if (shared->internal_formal_parameter_count() == arity) { |
- // Check if we need to wrap the {receiver}. |
- if (is_sloppy(shared->language_mode()) && !shared->native()) { |
- if (receiver_type->Is(Type::NullOrUndefined())) { |
- // Change {receiver} to global proxy of {function}. |
- receiver = |
- jsgraph()->Constant(handle(function->global_proxy(), isolate())); |
- } else if (!receiver_type->Is(Type::Receiver())) { |
- // TODO(bmeurer): Add support for wrapping abitrary receivers. |
- return NoChange(); |
- } |
- NodeProperties::ReplaceValueInput(node, receiver, 1); |
- } |
- |
// Grab the context from the {function}. |
Node* context = |
jsgraph()->Constant(handle(function->context(), isolate())); |
NodeProperties::ReplaceContextInput(node, context); |
- CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; |
- if (p.AllowTailCalls()) { |
- flags |= CallDescriptor::kSupportsTailCalls; |
+ |
+ // 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::ReplaceEffectInput(node, effect); |
+ NodeProperties::ReplaceValueInput(node, receiver, 1); |
} |
+ |
+ // Remove the eager bailout frame state. |
NodeProperties::RemoveFrameStateInput(node, 1); |
+ |
+ // Patch {node} to a direct call. |
+ CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; |
+ if (p.AllowTailCalls()) flags |= CallDescriptor::kSupportsTailCalls; |
NodeProperties::ChangeOp(node, |
common()->Call(Linkage::GetJSCallDescriptor( |
graph()->zone(), false, 1 + arity, flags))); |
@@ -1943,6 +2080,8 @@ Reduction JSTypedLowering::Reduce(Node* node) { |
return ReduceJSToNumber(node); |
case IrOpcode::kJSToString: |
return ReduceJSToString(node); |
+ case IrOpcode::kJSToObject: |
+ return ReduceJSToObject(node); |
case IrOpcode::kJSLoadNamed: |
return ReduceJSLoadNamed(node); |
case IrOpcode::kJSLoadProperty: |