| // Copyright 2014 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/code-factory.h" |
| #include "src/code-stubs.h" |
| #include "src/compiler/common-operator.h" |
| #include "src/compiler/js-generic-lowering.h" |
| #include "src/compiler/js-graph.h" |
| #include "src/compiler/machine-operator.h" |
| #include "src/compiler/node-matchers.h" |
| #include "src/compiler/node-properties.h" |
| #include "src/compiler/operator-properties.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| static CallDescriptor::Flags AdjustFrameStatesForCall(Node* node) { |
| int count = OperatorProperties::GetFrameStateInputCount(node->op()); |
| if (count > 1) { |
| int index = NodeProperties::FirstFrameStateIndex(node) + 1; |
| do { |
| node->RemoveInput(index); |
| } while (--count > 1); |
| } |
| return count > 0 ? CallDescriptor::kNeedsFrameState |
| : CallDescriptor::kNoFlags; |
| } |
| |
| |
| JSGenericLowering::JSGenericLowering(bool is_typing_enabled, JSGraph* jsgraph) |
| : is_typing_enabled_(is_typing_enabled), jsgraph_(jsgraph) {} |
| |
| |
| JSGenericLowering::~JSGenericLowering() {} |
| |
| |
| Reduction JSGenericLowering::Reduce(Node* node) { |
| switch (node->opcode()) { |
| #define DECLARE_CASE(x) \ |
| case IrOpcode::k##x: \ |
| Lower##x(node); \ |
| break; |
| JS_OP_LIST(DECLARE_CASE) |
| #undef DECLARE_CASE |
| case IrOpcode::kBranch: |
| // TODO(mstarzinger): If typing is enabled then simplified lowering will |
| // have inserted the correct ChangeBoolToBit, otherwise we need to perform |
| // poor-man's representation inference here and insert manual change. |
| if (!is_typing_enabled_) { |
| Node* condition = node->InputAt(0); |
| Node* test = graph()->NewNode(machine()->WordEqual(), condition, |
| jsgraph()->TrueConstant()); |
| node->ReplaceInput(0, test); |
| } |
| // Fall-through. |
| default: |
| // Nothing to see. |
| return NoChange(); |
| } |
| return Changed(node); |
| } |
| |
| |
| #define REPLACE_BINARY_OP_IC_CALL(Op, token) \ |
| void JSGenericLowering::Lower##Op(Node* node) { \ |
| BinaryOperationParameters const& p = \ |
| BinaryOperationParametersOf(node->op()); \ |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); \ |
| ReplaceWithStubCall(node, \ |
| CodeFactory::BinaryOpIC(isolate(), token, \ |
| strength(p.language_mode())), \ |
| CallDescriptor::kPatchableCallSiteWithNop | flags); \ |
| } |
| REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr, Token::BIT_OR) |
| REPLACE_BINARY_OP_IC_CALL(JSBitwiseXor, Token::BIT_XOR) |
| REPLACE_BINARY_OP_IC_CALL(JSBitwiseAnd, Token::BIT_AND) |
| REPLACE_BINARY_OP_IC_CALL(JSShiftLeft, Token::SHL) |
| REPLACE_BINARY_OP_IC_CALL(JSShiftRight, Token::SAR) |
| REPLACE_BINARY_OP_IC_CALL(JSShiftRightLogical, Token::SHR) |
| REPLACE_BINARY_OP_IC_CALL(JSAdd, Token::ADD) |
| REPLACE_BINARY_OP_IC_CALL(JSSubtract, Token::SUB) |
| REPLACE_BINARY_OP_IC_CALL(JSMultiply, Token::MUL) |
| REPLACE_BINARY_OP_IC_CALL(JSDivide, Token::DIV) |
| REPLACE_BINARY_OP_IC_CALL(JSModulus, Token::MOD) |
| #undef REPLACE_BINARY_OP_IC_CALL |
| |
| |
| // These ops are not language mode dependent; we arbitrarily pass Strength::WEAK |
| // here. |
| #define REPLACE_COMPARE_IC_CALL(op, token) \ |
| void JSGenericLowering::Lower##op(Node* node) { \ |
| ReplaceWithCompareIC(node, token, Strength::WEAK); \ |
| } |
| REPLACE_COMPARE_IC_CALL(JSEqual, Token::EQ) |
| REPLACE_COMPARE_IC_CALL(JSNotEqual, Token::NE) |
| REPLACE_COMPARE_IC_CALL(JSStrictEqual, Token::EQ_STRICT) |
| REPLACE_COMPARE_IC_CALL(JSStrictNotEqual, Token::NE_STRICT) |
| #undef REPLACE_COMPARE_IC_CALL |
| |
| |
| #define REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(op, token) \ |
| void JSGenericLowering::Lower##op(Node* node) { \ |
| ReplaceWithCompareIC(node, token, \ |
| strength(OpParameter<LanguageMode>(node))); \ |
| } |
| REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(JSLessThan, Token::LT) |
| REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(JSGreaterThan, Token::GT) |
| REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(JSLessThanOrEqual, Token::LTE) |
| REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(JSGreaterThanOrEqual, Token::GTE) |
| #undef REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE |
| |
| |
| #define REPLACE_RUNTIME_CALL(op, fun) \ |
| void JSGenericLowering::Lower##op(Node* node) { \ |
| ReplaceWithRuntimeCall(node, fun); \ |
| } |
| REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext) |
| REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext) |
| REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext) |
| REPLACE_RUNTIME_CALL(JSConvertReceiver, Runtime::kConvertReceiver) |
| #undef REPLACE_RUNTIME |
| |
| |
| static CallDescriptor::Flags FlagsForNode(Node* node) { |
| CallDescriptor::Flags result = CallDescriptor::kNoFlags; |
| if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) { |
| result |= CallDescriptor::kNeedsFrameState; |
| } |
| return result; |
| } |
| |
| |
| void JSGenericLowering::ReplaceWithCompareIC(Node* node, Token::Value token, |
| Strength str) { |
| Callable callable = CodeFactory::CompareIC(isolate(), token, str); |
| |
| // Create a new call node asking a CompareIC for help. |
| NodeVector inputs(zone()); |
| inputs.reserve(node->InputCount() + 1); |
| inputs.push_back(jsgraph()->HeapConstant(callable.code())); |
| inputs.push_back(NodeProperties::GetValueInput(node, 0)); |
| inputs.push_back(NodeProperties::GetValueInput(node, 1)); |
| inputs.push_back(NodeProperties::GetContextInput(node)); |
| // Some comparisons (StrictEqual) don't have an effect, control or frame |
| // state inputs, so handle those cases here. |
| if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) { |
| inputs.push_back(NodeProperties::GetFrameStateInput(node, 0)); |
| } |
| Node* effect = (node->op()->EffectInputCount() > 0) |
| ? NodeProperties::GetEffectInput(node) |
| : graph()->start(); |
| inputs.push_back(effect); |
| Node* control = (node->op()->ControlInputCount() > 0) |
| ? NodeProperties::GetControlInput(node) |
| : graph()->start(); |
| inputs.push_back(control); |
| CallDescriptor* desc_compare = Linkage::GetStubCallDescriptor( |
| isolate(), zone(), callable.descriptor(), 0, |
| CallDescriptor::kPatchableCallSiteWithNop | FlagsForNode(node), |
| Operator::kNoProperties, kMachIntPtr); |
| Node* compare = |
| graph()->NewNode(common()->Call(desc_compare), |
| static_cast<int>(inputs.size()), &inputs.front()); |
| |
| // Decide how the return value from the above CompareIC can be converted into |
| // a JavaScript boolean oddball depending on the given token. |
| Node* false_value = jsgraph()->FalseConstant(); |
| Node* true_value = jsgraph()->TrueConstant(); |
| const Operator* op = nullptr; |
| switch (token) { |
| case Token::EQ: // a == 0 |
| case Token::EQ_STRICT: |
| op = machine()->WordEqual(); |
| break; |
| case Token::NE: // a != 0 becomes !(a == 0) |
| case Token::NE_STRICT: |
| op = machine()->WordEqual(); |
| std::swap(true_value, false_value); |
| break; |
| case Token::LT: // a < 0 |
| op = machine()->IntLessThan(); |
| break; |
| case Token::GT: // a > 0 becomes !(a <= 0) |
| op = machine()->IntLessThanOrEqual(); |
| std::swap(true_value, false_value); |
| break; |
| case Token::LTE: // a <= 0 |
| op = machine()->IntLessThanOrEqual(); |
| break; |
| case Token::GTE: // a >= 0 becomes !(a < 0) |
| op = machine()->IntLessThan(); |
| std::swap(true_value, false_value); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| Node* booleanize = graph()->NewNode(op, compare, jsgraph()->ZeroConstant()); |
| |
| // Finally patch the original node to select a boolean. |
| NodeProperties::ReplaceUses(node, node, compare, compare, compare); |
| node->TrimInputCount(3); |
| node->ReplaceInput(0, booleanize); |
| node->ReplaceInput(1, true_value); |
| node->ReplaceInput(2, false_value); |
| NodeProperties::ChangeOp(node, common()->Select(kMachAnyTagged)); |
| } |
| |
| |
| void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable, |
| CallDescriptor::Flags flags) { |
| Operator::Properties properties = node->op()->properties(); |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), zone(), callable.descriptor(), 0, flags, properties); |
| Node* stub_code = jsgraph()->HeapConstant(callable.code()); |
| node->InsertInput(zone(), 0, stub_code); |
| NodeProperties::ChangeOp(node, common()->Call(desc)); |
| } |
| |
| |
| void JSGenericLowering::ReplaceWithRuntimeCall(Node* node, |
| Runtime::FunctionId f, |
| int nargs_override) { |
| Operator::Properties properties = node->op()->properties(); |
| const Runtime::Function* fun = Runtime::FunctionForId(f); |
| int nargs = (nargs_override < 0) ? fun->nargs : nargs_override; |
| CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor( |
| zone(), f, nargs, properties, CallDescriptor::kNeedsFrameState); |
| Node* ref = jsgraph()->ExternalConstant(ExternalReference(f, isolate())); |
| Node* arity = jsgraph()->Int32Constant(nargs); |
| node->InsertInput(zone(), 0, jsgraph()->CEntryStubConstant(fun->result_size)); |
| node->InsertInput(zone(), nargs + 1, ref); |
| node->InsertInput(zone(), nargs + 2, arity); |
| NodeProperties::ChangeOp(node, common()->Call(desc)); |
| } |
| |
| |
| void JSGenericLowering::LowerJSTypeOf(Node* node) { |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| Callable callable = CodeFactory::Typeof(isolate()); |
| ReplaceWithStubCall(node, callable, flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSToBoolean(Node* node) { |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| Callable callable = CodeFactory::ToBoolean(isolate()); |
| ReplaceWithStubCall(node, callable, |
| CallDescriptor::kPatchableCallSite | flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSToNumber(Node* node) { |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| Callable callable = CodeFactory::ToNumber(isolate()); |
| ReplaceWithStubCall(node, callable, flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSToString(Node* node) { |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| Callable callable = CodeFactory::ToString(isolate()); |
| ReplaceWithStubCall(node, callable, flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSToName(Node* node) { |
| ReplaceWithRuntimeCall(node, Runtime::kToName); |
| } |
| |
| |
| void JSGenericLowering::LowerJSToObject(Node* node) { |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| Callable callable = CodeFactory::ToObject(isolate()); |
| ReplaceWithStubCall(node, callable, flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSLoadProperty(Node* node) { |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| const PropertyAccess& p = PropertyAccessOf(node->op()); |
| Callable callable = CodeFactory::KeyedLoadICInOptimizedCode( |
| isolate(), p.language_mode(), UNINITIALIZED); |
| node->InsertInput(zone(), 2, jsgraph()->SmiConstant(p.feedback().index())); |
| ReplaceWithStubCall(node, callable, flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSLoadNamed(Node* node) { |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| NamedAccess const& p = NamedAccessOf(node->op()); |
| Callable callable = CodeFactory::LoadICInOptimizedCode( |
| isolate(), NOT_INSIDE_TYPEOF, p.language_mode(), UNINITIALIZED); |
| node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.name())); |
| node->InsertInput(zone(), 2, jsgraph()->SmiConstant(p.feedback().index())); |
| ReplaceWithStubCall(node, callable, flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSLoadGlobal(Node* node) { |
| Node* context = NodeProperties::GetContextInput(node); |
| Node* effect = NodeProperties::GetEffectInput(node); |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| const LoadGlobalParameters& p = LoadGlobalParametersOf(node->op()); |
| Callable callable = CodeFactory::LoadICInOptimizedCode( |
| isolate(), p.typeof_mode(), SLOPPY, UNINITIALIZED); |
| // Load global object from the context. |
| Node* native_context = |
| graph()->NewNode(machine()->Load(kMachAnyTagged), context, |
| jsgraph()->IntPtrConstant( |
| Context::SlotOffset(Context::NATIVE_CONTEXT_INDEX)), |
| effect, graph()->start()); |
| Node* global = graph()->NewNode( |
| machine()->Load(kMachAnyTagged), native_context, |
| jsgraph()->IntPtrConstant(Context::SlotOffset(Context::EXTENSION_INDEX)), |
| effect, graph()->start()); |
| node->InsertInput(zone(), 0, global); |
| node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.name())); |
| node->InsertInput(zone(), 2, jsgraph()->SmiConstant(p.feedback().index())); |
| ReplaceWithStubCall(node, callable, flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSStoreProperty(Node* node) { |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| PropertyAccess const& p = PropertyAccessOf(node->op()); |
| LanguageMode language_mode = p.language_mode(); |
| Callable callable = CodeFactory::KeyedStoreICInOptimizedCode( |
| isolate(), language_mode, UNINITIALIZED); |
| DCHECK(p.feedback().index() != -1); |
| node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.feedback().index())); |
| ReplaceWithStubCall(node, callable, |
| CallDescriptor::kPatchableCallSite | flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSStoreNamed(Node* node) { |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| NamedAccess const& p = NamedAccessOf(node->op()); |
| Callable callable = CodeFactory::StoreICInOptimizedCode( |
| isolate(), p.language_mode(), UNINITIALIZED); |
| node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.name())); |
| DCHECK(p.feedback().index() != -1); |
| node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.feedback().index())); |
| ReplaceWithStubCall(node, callable, |
| CallDescriptor::kPatchableCallSite | flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSStoreGlobal(Node* node) { |
| Node* context = NodeProperties::GetContextInput(node); |
| Node* effect = NodeProperties::GetEffectInput(node); |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| const StoreGlobalParameters& p = StoreGlobalParametersOf(node->op()); |
| Callable callable = CodeFactory::StoreICInOptimizedCode( |
| isolate(), p.language_mode(), UNINITIALIZED); |
| // Load global object from the context. |
| Node* native_context = |
| graph()->NewNode(machine()->Load(kMachAnyTagged), context, |
| jsgraph()->IntPtrConstant( |
| Context::SlotOffset(Context::NATIVE_CONTEXT_INDEX)), |
| effect, graph()->start()); |
| Node* global = graph()->NewNode( |
| machine()->Load(kMachAnyTagged), native_context, |
| jsgraph()->IntPtrConstant(Context::SlotOffset(Context::EXTENSION_INDEX)), |
| effect, graph()->start()); |
| node->InsertInput(zone(), 0, global); |
| node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.name())); |
| DCHECK(p.feedback().index() != -1); |
| node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.feedback().index())); |
| ReplaceWithStubCall(node, callable, |
| CallDescriptor::kPatchableCallSite | flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSDeleteProperty(Node* node) { |
| LanguageMode language_mode = OpParameter<LanguageMode>(node); |
| ReplaceWithRuntimeCall(node, is_strict(language_mode) |
| ? Runtime::kDeleteProperty_Strict |
| : Runtime::kDeleteProperty_Sloppy); |
| } |
| |
| |
| void JSGenericLowering::LowerJSHasProperty(Node* node) { |
| ReplaceWithRuntimeCall(node, Runtime::kHasProperty); |
| } |
| |
| |
| void JSGenericLowering::LowerJSInstanceOf(Node* node) { |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| Callable callable = CodeFactory::InstanceOf(isolate()); |
| ReplaceWithStubCall(node, callable, flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSLoadContext(Node* node) { |
| const ContextAccess& access = ContextAccessOf(node->op()); |
| for (size_t i = 0; i < access.depth(); ++i) { |
| node->ReplaceInput( |
| 0, graph()->NewNode(machine()->Load(kMachAnyTagged), |
| NodeProperties::GetValueInput(node, 0), |
| jsgraph()->Int32Constant( |
| Context::SlotOffset(Context::PREVIOUS_INDEX)), |
| NodeProperties::GetEffectInput(node), |
| graph()->start())); |
| } |
| node->ReplaceInput(1, jsgraph()->Int32Constant(Context::SlotOffset( |
| static_cast<int>(access.index())))); |
| node->AppendInput(zone(), graph()->start()); |
| NodeProperties::ChangeOp(node, machine()->Load(kMachAnyTagged)); |
| } |
| |
| |
| void JSGenericLowering::LowerJSStoreContext(Node* node) { |
| const ContextAccess& access = ContextAccessOf(node->op()); |
| for (size_t i = 0; i < access.depth(); ++i) { |
| node->ReplaceInput( |
| 0, graph()->NewNode(machine()->Load(kMachAnyTagged), |
| NodeProperties::GetValueInput(node, 0), |
| jsgraph()->Int32Constant( |
| Context::SlotOffset(Context::PREVIOUS_INDEX)), |
| NodeProperties::GetEffectInput(node), |
| graph()->start())); |
| } |
| node->ReplaceInput(2, NodeProperties::GetValueInput(node, 1)); |
| node->ReplaceInput(1, jsgraph()->Int32Constant(Context::SlotOffset( |
| static_cast<int>(access.index())))); |
| NodeProperties::ChangeOp(node, machine()->Store(StoreRepresentation( |
| kMachAnyTagged, kFullWriteBarrier))); |
| } |
| |
| |
| void JSGenericLowering::LowerJSLoadDynamic(Node* node) { |
| const DynamicAccess& access = DynamicAccessOf(node->op()); |
| Runtime::FunctionId function_id = |
| (access.typeof_mode() == NOT_INSIDE_TYPEOF) |
| ? Runtime::kLoadLookupSlot |
| : Runtime::kLoadLookupSlotNoReferenceError; |
| Node* projection = graph()->NewNode(common()->Projection(0), node); |
| NodeProperties::ReplaceUses(node, projection, node, node, node); |
| node->RemoveInput(NodeProperties::FirstValueIndex(node)); |
| node->InsertInput(zone(), 1, jsgraph()->Constant(access.name())); |
| ReplaceWithRuntimeCall(node, function_id); |
| projection->ReplaceInput(0, node); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCreate(Node* node) { |
| ReplaceWithRuntimeCall(node, Runtime::kNewObject); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCreateArguments(Node* node) { |
| const CreateArgumentsParameters& p = CreateArgumentsParametersOf(node->op()); |
| switch (p.type()) { |
| case CreateArgumentsParameters::kMappedArguments: |
| ReplaceWithRuntimeCall(node, Runtime::kNewSloppyArguments_Generic); |
| break; |
| case CreateArgumentsParameters::kUnmappedArguments: |
| ReplaceWithRuntimeCall(node, Runtime::kNewStrictArguments_Generic); |
| break; |
| case CreateArgumentsParameters::kRestArray: |
| UNIMPLEMENTED(); |
| break; |
| } |
| } |
| |
| |
| void JSGenericLowering::LowerJSCreateArray(Node* node) { |
| CreateArrayParameters const& p = CreateArrayParametersOf(node->op()); |
| int const arity = static_cast<int>(p.arity()); |
| Node* new_target = node->InputAt(1); |
| // TODO(turbofan): We embed the AllocationSite from the Operator at this |
| // point, which we should not do once we want to both consume the feedback |
| // but at the same time shared the optimized code across native contexts, |
| // as the AllocationSite is associated with a single native context (it's |
| // stored in the type feedback vector after all). Once we go for cross |
| // context code generation, we should somehow find a way to get to the |
| // allocation site for the actual native context at runtime. |
| Node* type_info = p.site().is_null() ? jsgraph()->UndefinedConstant() |
| : jsgraph()->HeapConstant(p.site()); |
| node->RemoveInput(1); |
| node->InsertInput(zone(), 1 + arity, new_target); |
| node->InsertInput(zone(), 2 + arity, type_info); |
| ReplaceWithRuntimeCall(node, Runtime::kNewArray, arity + 3); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCreateClosure(Node* node) { |
| CreateClosureParameters p = CreateClosureParametersOf(node->op()); |
| node->InsertInput(zone(), 0, jsgraph()->HeapConstant(p.shared_info())); |
| ReplaceWithRuntimeCall(node, (p.pretenure() == TENURED) |
| ? Runtime::kNewClosure_Tenured |
| : Runtime::kNewClosure); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCreateLiteralArray(Node* node) { |
| CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op()); |
| node->InsertInput(zone(), 1, jsgraph()->SmiConstant(p.index())); |
| node->InsertInput(zone(), 2, jsgraph()->HeapConstant(p.constant())); |
| node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags())); |
| ReplaceWithRuntimeCall(node, Runtime::kCreateArrayLiteral); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCreateLiteralObject(Node* node) { |
| CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op()); |
| node->InsertInput(zone(), 1, jsgraph()->SmiConstant(p.index())); |
| node->InsertInput(zone(), 2, jsgraph()->HeapConstant(p.constant())); |
| node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags())); |
| ReplaceWithRuntimeCall(node, Runtime::kCreateObjectLiteral); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCreateLiteralRegExp(Node* node) { |
| CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op()); |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| Callable callable = CodeFactory::FastCloneRegExp(isolate()); |
| Node* literal_index = jsgraph()->SmiConstant(p.index()); |
| Node* literal_flags = jsgraph()->SmiConstant(p.flags()); |
| Node* pattern = jsgraph()->HeapConstant(p.constant()); |
| node->InsertInput(graph()->zone(), 1, literal_index); |
| node->InsertInput(graph()->zone(), 2, pattern); |
| node->InsertInput(graph()->zone(), 3, literal_flags); |
| ReplaceWithStubCall(node, callable, flags); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCreateCatchContext(Node* node) { |
| Handle<String> name = OpParameter<Handle<String>>(node); |
| node->InsertInput(zone(), 0, jsgraph()->HeapConstant(name)); |
| ReplaceWithRuntimeCall(node, Runtime::kPushCatchContext); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCreateBlockContext(Node* node) { |
| Handle<ScopeInfo> scope_info = OpParameter<Handle<ScopeInfo>>(node); |
| node->InsertInput(zone(), 0, jsgraph()->HeapConstant(scope_info)); |
| ReplaceWithRuntimeCall(node, Runtime::kPushBlockContext); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCreateScriptContext(Node* node) { |
| Handle<ScopeInfo> scope_info = OpParameter<Handle<ScopeInfo>>(node); |
| node->InsertInput(zone(), 1, jsgraph()->HeapConstant(scope_info)); |
| ReplaceWithRuntimeCall(node, Runtime::kNewScriptContext); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCallConstruct(Node* node) { |
| CallConstructParameters const& p = CallConstructParametersOf(node->op()); |
| int const arg_count = static_cast<int>(p.arity() - 2); |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| Callable callable = CodeFactory::Construct(isolate()); |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), zone(), callable.descriptor(), arg_count + 1, flags); |
| Node* stub_code = jsgraph()->HeapConstant(callable.code()); |
| Node* stub_arity = jsgraph()->Int32Constant(arg_count); |
| Node* new_target = node->InputAt(arg_count + 1); |
| Node* receiver = jsgraph()->UndefinedConstant(); |
| node->RemoveInput(arg_count + 1); // Drop new target. |
| node->InsertInput(zone(), 0, stub_code); |
| node->InsertInput(zone(), 2, new_target); |
| node->InsertInput(zone(), 3, stub_arity); |
| node->InsertInput(zone(), 4, receiver); |
| NodeProperties::ChangeOp(node, common()->Call(desc)); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCallFunction(Node* node) { |
| CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
| int const arg_count = static_cast<int>(p.arity() - 2); |
| ConvertReceiverMode const mode = p.convert_mode(); |
| Callable callable = CodeFactory::Call(isolate(), mode); |
| CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); |
| if (p.tail_call_mode() == TailCallMode::kAllow) { |
| flags |= CallDescriptor::kSupportsTailCalls; |
| } |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), zone(), callable.descriptor(), arg_count + 1, flags); |
| Node* stub_code = jsgraph()->HeapConstant(callable.code()); |
| Node* stub_arity = jsgraph()->Int32Constant(arg_count); |
| node->InsertInput(zone(), 0, stub_code); |
| node->InsertInput(zone(), 2, stub_arity); |
| NodeProperties::ChangeOp(node, common()->Call(desc)); |
| } |
| |
| |
| void JSGenericLowering::LowerJSCallRuntime(Node* node) { |
| const CallRuntimeParameters& p = CallRuntimeParametersOf(node->op()); |
| AdjustFrameStatesForCall(node); |
| ReplaceWithRuntimeCall(node, p.id(), static_cast<int>(p.arity())); |
| } |
| |
| |
| void JSGenericLowering::LowerJSForInDone(Node* node) { |
| ReplaceWithRuntimeCall(node, Runtime::kForInDone); |
| } |
| |
| |
| void JSGenericLowering::LowerJSForInNext(Node* node) { |
| ReplaceWithRuntimeCall(node, Runtime::kForInNext); |
| } |
| |
| |
| void JSGenericLowering::LowerJSForInPrepare(Node* node) { |
| Node* object = NodeProperties::GetValueInput(node, 0); |
| Node* context = NodeProperties::GetContextInput(node); |
| Node* effect = NodeProperties::GetEffectInput(node); |
| Node* control = NodeProperties::GetControlInput(node); |
| Node* frame_state = NodeProperties::GetFrameStateInput(node, 0); |
| |
| // Get the set of properties to enumerate. |
| Runtime::Function const* function = |
| Runtime::FunctionForId(Runtime::kGetPropertyNamesFast); |
| CallDescriptor const* descriptor = Linkage::GetRuntimeCallDescriptor( |
| zone(), function->function_id, 1, Operator::kNoProperties, |
| CallDescriptor::kNeedsFrameState); |
| Node* cache_type = effect = graph()->NewNode( |
| common()->Call(descriptor), |
| jsgraph()->CEntryStubConstant(function->result_size), object, |
| jsgraph()->ExternalConstant(function->function_id), |
| jsgraph()->Int32Constant(1), context, frame_state, effect, control); |
| control = graph()->NewNode(common()->IfSuccess(), cache_type); |
| |
| Node* object_map = effect = graph()->NewNode( |
| machine()->Load(kMachAnyTagged), object, |
| jsgraph()->IntPtrConstant(HeapObject::kMapOffset - kHeapObjectTag), |
| effect, control); |
| Node* cache_type_map = effect = graph()->NewNode( |
| machine()->Load(kMachAnyTagged), cache_type, |
| jsgraph()->IntPtrConstant(HeapObject::kMapOffset - kHeapObjectTag), |
| effect, control); |
| Node* meta_map = jsgraph()->HeapConstant(isolate()->factory()->meta_map()); |
| |
| // If we got a map from the GetPropertyNamesFast runtime call, we can do a |
| // fast modification check. Otherwise, we got a fixed array, and we have to |
| // perform a slow check on every iteration. |
| Node* check0 = |
| graph()->NewNode(machine()->WordEqual(), cache_type_map, meta_map); |
| Node* branch0 = |
| graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control); |
| |
| Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
| Node* cache_array_true0; |
| Node* cache_length_true0; |
| Node* cache_type_true0; |
| Node* etrue0; |
| { |
| // Enum cache case. |
| Node* cache_type_enum_length = etrue0 = graph()->NewNode( |
| machine()->Load(kMachUint32), cache_type, |
| jsgraph()->IntPtrConstant(Map::kBitField3Offset - kHeapObjectTag), |
| effect, if_true0); |
| cache_type_enum_length = |
| graph()->NewNode(machine()->Word32And(), cache_type_enum_length, |
| jsgraph()->Uint32Constant(Map::EnumLengthBits::kMask)); |
| |
| Node* check1 = |
| graph()->NewNode(machine()->Word32Equal(), cache_type_enum_length, |
| jsgraph()->Int32Constant(0)); |
| Node* branch1 = |
| graph()->NewNode(common()->Branch(BranchHint::kTrue), check1, if_true0); |
| |
| Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| Node* cache_array_true1; |
| Node* etrue1; |
| { |
| // No properties to enumerate. |
| cache_array_true1 = |
| jsgraph()->HeapConstant(isolate()->factory()->empty_fixed_array()); |
| etrue1 = etrue0; |
| } |
| |
| Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
| Node* cache_array_false1; |
| Node* efalse1; |
| { |
| // Load the enumeration cache from the instance descriptors of {object}. |
| Node* object_map_descriptors = efalse1 = graph()->NewNode( |
| machine()->Load(kMachAnyTagged), object_map, |
| jsgraph()->IntPtrConstant(Map::kDescriptorsOffset - kHeapObjectTag), |
| etrue0, if_false1); |
| Node* object_map_enum_cache = efalse1 = graph()->NewNode( |
| machine()->Load(kMachAnyTagged), object_map_descriptors, |
| jsgraph()->IntPtrConstant(DescriptorArray::kEnumCacheOffset - |
| kHeapObjectTag), |
| efalse1, if_false1); |
| cache_array_false1 = efalse1 = graph()->NewNode( |
| machine()->Load(kMachAnyTagged), object_map_enum_cache, |
| jsgraph()->IntPtrConstant( |
| DescriptorArray::kEnumCacheBridgeCacheOffset - kHeapObjectTag), |
| efalse1, if_false1); |
| } |
| |
| if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
| 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); |
| |
| cache_length_true0 = graph()->NewNode( |
| machine()->WordShl(), |
| machine()->Is64() |
| ? graph()->NewNode(machine()->ChangeUint32ToUint64(), |
| cache_type_enum_length) |
| : cache_type_enum_length, |
| jsgraph()->Int32Constant(kSmiShiftSize + kSmiTagSize)); |
| cache_type_true0 = cache_type; |
| } |
| |
| Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
| Node* cache_array_false0; |
| Node* cache_length_false0; |
| Node* cache_type_false0; |
| Node* efalse0; |
| { |
| // FixedArray case. |
| Node* object_instance_type = efalse0 = graph()->NewNode( |
| machine()->Load(kMachUint8), object_map, |
| jsgraph()->IntPtrConstant(Map::kInstanceTypeOffset - kHeapObjectTag), |
| effect, if_false0); |
| |
| Node* check1 = |
| graph()->NewNode(machine()->Word32Equal(), object_instance_type, |
| jsgraph()->Uint32Constant(JS_PROXY_TYPE)); |
| Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| check1, if_false0); |
| |
| Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| Node* cache_type_true1 = jsgraph()->ZeroConstant(); // Zero indicates proxy |
| |
| Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
| Node* cache_type_false1 = jsgraph()->OneConstant(); // One means slow check |
| |
| if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
| cache_type_false0 = |
| graph()->NewNode(common()->Phi(kMachAnyTagged, 2), cache_type_true1, |
| cache_type_false1, if_false0); |
| |
| cache_array_false0 = cache_type; |
| cache_length_false0 = efalse0 = graph()->NewNode( |
| machine()->Load(kMachAnyTagged), cache_array_false0, |
| jsgraph()->IntPtrConstant(FixedArray::kLengthOffset - kHeapObjectTag), |
| efalse0, 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); |
| 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); |
| |
| for (auto edge : node->use_edges()) { |
| if (NodeProperties::IsEffectEdge(edge)) { |
| edge.UpdateTo(effect); |
| } else if (NodeProperties::IsControlEdge(edge)) { |
| Node* const use = edge.from(); |
| if (use->opcode() == IrOpcode::kIfSuccess) { |
| use->ReplaceUses(control); |
| use->Kill(); |
| } else if (use->opcode() == IrOpcode::kIfException) { |
| edge.UpdateTo(cache_type_true0); |
| } else { |
| UNREACHABLE(); |
| } |
| } else { |
| Node* const use = edge.from(); |
| DCHECK(NodeProperties::IsValueEdge(edge)); |
| DCHECK_EQ(IrOpcode::kProjection, use->opcode()); |
| switch (ProjectionIndexOf(use->op())) { |
| case 0: |
| use->ReplaceUses(cache_type); |
| break; |
| case 1: |
| use->ReplaceUses(cache_array); |
| break; |
| case 2: |
| use->ReplaceUses(cache_length); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| use->Kill(); |
| } |
| } |
| } |
| |
| |
| void JSGenericLowering::LowerJSForInStep(Node* node) { |
| ReplaceWithRuntimeCall(node, Runtime::kForInStep); |
| } |
| |
| |
| void JSGenericLowering::LowerJSLoadMessage(Node* node) { |
| ExternalReference message_address = |
| ExternalReference::address_of_pending_message_obj(isolate()); |
| node->RemoveInput(NodeProperties::FirstContextIndex(node)); |
| node->InsertInput(zone(), 0, jsgraph()->ExternalConstant(message_address)); |
| node->InsertInput(zone(), 1, jsgraph()->IntPtrConstant(0)); |
| NodeProperties::ChangeOp(node, machine()->Load(kMachAnyTagged)); |
| } |
| |
| |
| void JSGenericLowering::LowerJSStoreMessage(Node* node) { |
| ExternalReference message_address = |
| ExternalReference::address_of_pending_message_obj(isolate()); |
| node->RemoveInput(NodeProperties::FirstContextIndex(node)); |
| node->InsertInput(zone(), 0, jsgraph()->ExternalConstant(message_address)); |
| node->InsertInput(zone(), 1, jsgraph()->IntPtrConstant(0)); |
| StoreRepresentation representation(kMachAnyTagged, kNoWriteBarrier); |
| NodeProperties::ChangeOp(node, machine()->Store(representation)); |
| } |
| |
| |
| void JSGenericLowering::LowerJSYield(Node* node) { UNIMPLEMENTED(); } |
| |
| |
| void JSGenericLowering::LowerJSStackCheck(Node* node) { |
| Node* effect = NodeProperties::GetEffectInput(node); |
| Node* control = NodeProperties::GetControlInput(node); |
| |
| Node* limit = graph()->NewNode( |
| machine()->Load(kMachPtr), |
| jsgraph()->ExternalConstant( |
| ExternalReference::address_of_stack_limit(isolate())), |
| jsgraph()->IntPtrConstant(0), effect, control); |
| Node* pointer = graph()->NewNode(machine()->LoadStackPointer()); |
| |
| Node* check = graph()->NewNode(machine()->UintLessThan(), limit, pointer); |
| Node* branch = |
| graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
| |
| Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
| Node* etrue = effect; |
| |
| Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
| NodeProperties::ReplaceControlInput(node, if_false); |
| Node* efalse = node; |
| |
| Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); |
| Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge); |
| |
| // Wire the new diamond into the graph, {node} can still throw. |
| NodeProperties::ReplaceUses(node, node, ephi, node, node); |
| NodeProperties::ReplaceEffectInput(ephi, efalse, 1); |
| |
| // TODO(mstarzinger): This iteration cuts out the IfSuccess projection from |
| // the node and places it inside the diamond. Come up with a helper method! |
| for (Node* use : node->uses()) { |
| if (use->opcode() == IrOpcode::kIfSuccess) { |
| use->ReplaceUses(merge); |
| merge->ReplaceInput(1, use); |
| } |
| } |
| |
| // Turn the stack check into a runtime call. |
| ReplaceWithRuntimeCall(node, Runtime::kStackGuard); |
| } |
| |
| |
| Zone* JSGenericLowering::zone() const { return graph()->zone(); } |
| |
| |
| Isolate* JSGenericLowering::isolate() const { return jsgraph()->isolate(); } |
| |
| |
| Graph* JSGenericLowering::graph() const { return jsgraph()->graph(); } |
| |
| |
| CommonOperatorBuilder* JSGenericLowering::common() const { |
| return jsgraph()->common(); |
| } |
| |
| |
| MachineOperatorBuilder* JSGenericLowering::machine() const { |
| return jsgraph()->machine(); |
| } |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |