Chromium Code Reviews| 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) { |
|
Michael Starzinger
2015/10/30 09:55:01
nit: Can we haz unit test for ReduceJSToObject?
Benedikt Meurer
2015/10/30 10:00:49
Of course :-)
|
| + 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: |