Home | History | Annotate | Download | only in compiler
      1 // Copyright 2014 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/code-factory.h"
      6 #include "src/code-stubs.h"
      7 #include "src/compiler/common-operator.h"
      8 #include "src/compiler/graph-inl.h"
      9 #include "src/compiler/js-generic-lowering.h"
     10 #include "src/compiler/machine-operator.h"
     11 #include "src/compiler/node-aux-data-inl.h"
     12 #include "src/compiler/node-properties-inl.h"
     13 #include "src/unique.h"
     14 
     15 namespace v8 {
     16 namespace internal {
     17 namespace compiler {
     18 
     19 JSGenericLowering::JSGenericLowering(CompilationInfo* info, JSGraph* jsgraph)
     20     : info_(info),
     21       jsgraph_(jsgraph),
     22       linkage_(new (jsgraph->zone()) Linkage(info)) {}
     23 
     24 
     25 void JSGenericLowering::PatchOperator(Node* node, const Operator* op) {
     26   node->set_op(op);
     27 }
     28 
     29 
     30 void JSGenericLowering::PatchInsertInput(Node* node, int index, Node* input) {
     31   node->InsertInput(zone(), index, input);
     32 }
     33 
     34 
     35 Node* JSGenericLowering::SmiConstant(int32_t immediate) {
     36   return jsgraph()->SmiConstant(immediate);
     37 }
     38 
     39 
     40 Node* JSGenericLowering::Int32Constant(int immediate) {
     41   return jsgraph()->Int32Constant(immediate);
     42 }
     43 
     44 
     45 Node* JSGenericLowering::CodeConstant(Handle<Code> code) {
     46   return jsgraph()->HeapConstant(code);
     47 }
     48 
     49 
     50 Node* JSGenericLowering::FunctionConstant(Handle<JSFunction> function) {
     51   return jsgraph()->HeapConstant(function);
     52 }
     53 
     54 
     55 Node* JSGenericLowering::ExternalConstant(ExternalReference ref) {
     56   return jsgraph()->ExternalConstant(ref);
     57 }
     58 
     59 
     60 Reduction JSGenericLowering::Reduce(Node* node) {
     61   switch (node->opcode()) {
     62 #define DECLARE_CASE(x) \
     63   case IrOpcode::k##x:  \
     64     Lower##x(node);     \
     65     break;
     66     DECLARE_CASE(Branch)
     67     JS_OP_LIST(DECLARE_CASE)
     68 #undef DECLARE_CASE
     69     default:
     70       // Nothing to see.
     71       return NoChange();
     72   }
     73   return Changed(node);
     74 }
     75 
     76 
     77 #define REPLACE_BINARY_OP_IC_CALL(op, token)                             \
     78   void JSGenericLowering::Lower##op(Node* node) {                        \
     79     ReplaceWithStubCall(node, CodeFactory::BinaryOpIC(isolate(), token), \
     80                         CallDescriptor::kPatchableCallSiteWithNop);      \
     81   }
     82 REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr, Token::BIT_OR)
     83 REPLACE_BINARY_OP_IC_CALL(JSBitwiseXor, Token::BIT_XOR)
     84 REPLACE_BINARY_OP_IC_CALL(JSBitwiseAnd, Token::BIT_AND)
     85 REPLACE_BINARY_OP_IC_CALL(JSShiftLeft, Token::SHL)
     86 REPLACE_BINARY_OP_IC_CALL(JSShiftRight, Token::SAR)
     87 REPLACE_BINARY_OP_IC_CALL(JSShiftRightLogical, Token::SHR)
     88 REPLACE_BINARY_OP_IC_CALL(JSAdd, Token::ADD)
     89 REPLACE_BINARY_OP_IC_CALL(JSSubtract, Token::SUB)
     90 REPLACE_BINARY_OP_IC_CALL(JSMultiply, Token::MUL)
     91 REPLACE_BINARY_OP_IC_CALL(JSDivide, Token::DIV)
     92 REPLACE_BINARY_OP_IC_CALL(JSModulus, Token::MOD)
     93 #undef REPLACE_BINARY_OP_IC_CALL
     94 
     95 
     96 #define REPLACE_COMPARE_IC_CALL(op, token, pure)  \
     97   void JSGenericLowering::Lower##op(Node* node) { \
     98     ReplaceWithCompareIC(node, token, pure);      \
     99   }
    100 REPLACE_COMPARE_IC_CALL(JSEqual, Token::EQ, false)
    101 REPLACE_COMPARE_IC_CALL(JSNotEqual, Token::NE, false)
    102 REPLACE_COMPARE_IC_CALL(JSStrictEqual, Token::EQ_STRICT, true)
    103 REPLACE_COMPARE_IC_CALL(JSStrictNotEqual, Token::NE_STRICT, true)
    104 REPLACE_COMPARE_IC_CALL(JSLessThan, Token::LT, false)
    105 REPLACE_COMPARE_IC_CALL(JSGreaterThan, Token::GT, false)
    106 REPLACE_COMPARE_IC_CALL(JSLessThanOrEqual, Token::LTE, false)
    107 REPLACE_COMPARE_IC_CALL(JSGreaterThanOrEqual, Token::GTE, false)
    108 #undef REPLACE_COMPARE_IC_CALL
    109 
    110 
    111 #define REPLACE_RUNTIME_CALL(op, fun)             \
    112   void JSGenericLowering::Lower##op(Node* node) { \
    113     ReplaceWithRuntimeCall(node, fun);            \
    114   }
    115 REPLACE_RUNTIME_CALL(JSTypeOf, Runtime::kTypeof)
    116 REPLACE_RUNTIME_CALL(JSCreate, Runtime::kAbort)
    117 REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext)
    118 REPLACE_RUNTIME_CALL(JSCreateCatchContext, Runtime::kPushCatchContext)
    119 REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
    120 REPLACE_RUNTIME_CALL(JSCreateBlockContext, Runtime::kPushBlockContext)
    121 REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
    122 REPLACE_RUNTIME_CALL(JSCreateGlobalContext, Runtime::kAbort)
    123 #undef REPLACE_RUNTIME
    124 
    125 
    126 #define REPLACE_UNIMPLEMENTED(op) \
    127   void JSGenericLowering::Lower##op(Node* node) { UNIMPLEMENTED(); }
    128 REPLACE_UNIMPLEMENTED(JSToName)
    129 REPLACE_UNIMPLEMENTED(JSYield)
    130 REPLACE_UNIMPLEMENTED(JSDebugger)
    131 #undef REPLACE_UNIMPLEMENTED
    132 
    133 
    134 static CallDescriptor::Flags FlagsForNode(Node* node) {
    135   CallDescriptor::Flags result = CallDescriptor::kNoFlags;
    136   if (OperatorProperties::HasFrameStateInput(node->op())) {
    137     result |= CallDescriptor::kNeedsFrameState;
    138   }
    139   return result;
    140 }
    141 
    142 
    143 void JSGenericLowering::ReplaceWithCompareIC(Node* node, Token::Value token,
    144                                              bool pure) {
    145   Callable callable = CodeFactory::CompareIC(isolate(), token);
    146   bool has_frame_state = OperatorProperties::HasFrameStateInput(node->op());
    147   CallDescriptor* desc_compare = linkage()->GetStubCallDescriptor(
    148       callable.descriptor(), 0,
    149       CallDescriptor::kPatchableCallSiteWithNop | FlagsForNode(node));
    150   NodeVector inputs(zone());
    151   inputs.reserve(node->InputCount() + 1);
    152   inputs.push_back(CodeConstant(callable.code()));
    153   inputs.push_back(NodeProperties::GetValueInput(node, 0));
    154   inputs.push_back(NodeProperties::GetValueInput(node, 1));
    155   inputs.push_back(NodeProperties::GetContextInput(node));
    156   if (pure) {
    157     // A pure (strict) comparison doesn't have an effect, control or frame
    158     // state.  But for the graph, we need to add control and effect inputs.
    159     DCHECK(!has_frame_state);
    160     inputs.push_back(graph()->start());
    161     inputs.push_back(graph()->start());
    162   } else {
    163     DCHECK(has_frame_state == FLAG_turbo_deoptimization);
    164     if (FLAG_turbo_deoptimization) {
    165       inputs.push_back(NodeProperties::GetFrameStateInput(node));
    166     }
    167     inputs.push_back(NodeProperties::GetEffectInput(node));
    168     inputs.push_back(NodeProperties::GetControlInput(node));
    169   }
    170   Node* compare =
    171       graph()->NewNode(common()->Call(desc_compare),
    172                        static_cast<int>(inputs.size()), &inputs.front());
    173 
    174   node->ReplaceInput(0, compare);
    175   node->ReplaceInput(1, SmiConstant(token));
    176 
    177   if (has_frame_state) {
    178     // Remove the frame state from inputs.
    179     node->RemoveInput(NodeProperties::FirstFrameStateIndex(node));
    180   }
    181 
    182   ReplaceWithRuntimeCall(node, Runtime::kBooleanize);
    183 }
    184 
    185 
    186 void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable,
    187                                             CallDescriptor::Flags flags) {
    188   CallDescriptor* desc = linkage()->GetStubCallDescriptor(
    189       callable.descriptor(), 0, flags | FlagsForNode(node));
    190   Node* stub_code = CodeConstant(callable.code());
    191   PatchInsertInput(node, 0, stub_code);
    192   PatchOperator(node, common()->Call(desc));
    193 }
    194 
    195 
    196 void JSGenericLowering::ReplaceWithBuiltinCall(Node* node,
    197                                                Builtins::JavaScript id,
    198                                                int nargs) {
    199   Callable callable =
    200       CodeFactory::CallFunction(isolate(), nargs - 1, NO_CALL_FUNCTION_FLAGS);
    201   CallDescriptor* desc =
    202       linkage()->GetStubCallDescriptor(callable.descriptor(), nargs);
    203   // TODO(mstarzinger): Accessing the builtins object this way prevents sharing
    204   // of code across native contexts. Fix this by loading from given context.
    205   Handle<JSFunction> function(
    206       JSFunction::cast(info()->context()->builtins()->javascript_builtin(id)));
    207   Node* stub_code = CodeConstant(callable.code());
    208   Node* function_node = FunctionConstant(function);
    209   PatchInsertInput(node, 0, stub_code);
    210   PatchInsertInput(node, 1, function_node);
    211   PatchOperator(node, common()->Call(desc));
    212 }
    213 
    214 
    215 void JSGenericLowering::ReplaceWithRuntimeCall(Node* node,
    216                                                Runtime::FunctionId f,
    217                                                int nargs_override) {
    218   Operator::Properties properties = node->op()->properties();
    219   const Runtime::Function* fun = Runtime::FunctionForId(f);
    220   int nargs = (nargs_override < 0) ? fun->nargs : nargs_override;
    221   CallDescriptor* desc =
    222       linkage()->GetRuntimeCallDescriptor(f, nargs, properties);
    223   Node* ref = ExternalConstant(ExternalReference(f, isolate()));
    224   Node* arity = Int32Constant(nargs);
    225   if (!centrystub_constant_.is_set()) {
    226     centrystub_constant_.set(CodeConstant(CEntryStub(isolate(), 1).GetCode()));
    227   }
    228   PatchInsertInput(node, 0, centrystub_constant_.get());
    229   PatchInsertInput(node, nargs + 1, ref);
    230   PatchInsertInput(node, nargs + 2, arity);
    231   PatchOperator(node, common()->Call(desc));
    232 }
    233 
    234 
    235 void JSGenericLowering::LowerBranch(Node* node) {
    236   if (!info()->is_typing_enabled()) {
    237     // TODO(mstarzinger): If typing is enabled then simplified lowering will
    238     // have inserted the correct ChangeBoolToBit, otherwise we need to perform
    239     // poor-man's representation inference here and insert manual change.
    240     Node* test = graph()->NewNode(machine()->WordEqual(), node->InputAt(0),
    241                                   jsgraph()->TrueConstant());
    242     node->ReplaceInput(0, test);
    243   }
    244 }
    245 
    246 
    247 void JSGenericLowering::LowerJSUnaryNot(Node* node) {
    248   Callable callable = CodeFactory::ToBoolean(
    249       isolate(), ToBooleanStub::RESULT_AS_INVERSE_ODDBALL);
    250   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
    251 }
    252 
    253 
    254 void JSGenericLowering::LowerJSToBoolean(Node* node) {
    255   Callable callable =
    256       CodeFactory::ToBoolean(isolate(), ToBooleanStub::RESULT_AS_ODDBALL);
    257   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
    258 }
    259 
    260 
    261 void JSGenericLowering::LowerJSToNumber(Node* node) {
    262   Callable callable = CodeFactory::ToNumber(isolate());
    263   ReplaceWithStubCall(node, callable, CallDescriptor::kNoFlags);
    264 }
    265 
    266 
    267 void JSGenericLowering::LowerJSToString(Node* node) {
    268   ReplaceWithBuiltinCall(node, Builtins::TO_STRING, 1);
    269 }
    270 
    271 
    272 void JSGenericLowering::LowerJSToObject(Node* node) {
    273   ReplaceWithBuiltinCall(node, Builtins::TO_OBJECT, 1);
    274 }
    275 
    276 
    277 void JSGenericLowering::LowerJSLoadProperty(Node* node) {
    278   Callable callable = CodeFactory::KeyedLoadIC(isolate());
    279   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
    280 }
    281 
    282 
    283 void JSGenericLowering::LowerJSLoadNamed(Node* node) {
    284   LoadNamedParameters p = OpParameter<LoadNamedParameters>(node);
    285   Callable callable = CodeFactory::LoadIC(isolate(), p.contextual_mode);
    286   PatchInsertInput(node, 1, jsgraph()->HeapConstant(p.name));
    287   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
    288 }
    289 
    290 
    291 void JSGenericLowering::LowerJSStoreProperty(Node* node) {
    292   StrictMode strict_mode = OpParameter<StrictMode>(node);
    293   Callable callable = CodeFactory::KeyedStoreIC(isolate(), strict_mode);
    294   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
    295 }
    296 
    297 
    298 void JSGenericLowering::LowerJSStoreNamed(Node* node) {
    299   StoreNamedParameters params = OpParameter<StoreNamedParameters>(node);
    300   Callable callable = CodeFactory::StoreIC(isolate(), params.strict_mode);
    301   PatchInsertInput(node, 1, jsgraph()->HeapConstant(params.name));
    302   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
    303 }
    304 
    305 
    306 void JSGenericLowering::LowerJSDeleteProperty(Node* node) {
    307   StrictMode strict_mode = OpParameter<StrictMode>(node);
    308   PatchInsertInput(node, 2, SmiConstant(strict_mode));
    309   ReplaceWithBuiltinCall(node, Builtins::DELETE, 3);
    310 }
    311 
    312 
    313 void JSGenericLowering::LowerJSHasProperty(Node* node) {
    314   ReplaceWithBuiltinCall(node, Builtins::IN, 2);
    315 }
    316 
    317 
    318 void JSGenericLowering::LowerJSInstanceOf(Node* node) {
    319   InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>(
    320       InstanceofStub::kReturnTrueFalseObject |
    321       InstanceofStub::kArgsInRegisters);
    322   InstanceofStub stub(isolate(), flags);
    323   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
    324   CallDescriptor* desc = linkage()->GetStubCallDescriptor(d, 0);
    325   Node* stub_code = CodeConstant(stub.GetCode());
    326   PatchInsertInput(node, 0, stub_code);
    327   PatchOperator(node, common()->Call(desc));
    328 }
    329 
    330 
    331 void JSGenericLowering::LowerJSLoadContext(Node* node) {
    332   ContextAccess access = OpParameter<ContextAccess>(node);
    333   // TODO(mstarzinger): Use simplified operators instead of machine operators
    334   // here so that load/store optimization can be applied afterwards.
    335   for (int i = 0; i < access.depth(); ++i) {
    336     node->ReplaceInput(
    337         0, graph()->NewNode(
    338                machine()->Load(kMachAnyTagged),
    339                NodeProperties::GetValueInput(node, 0),
    340                Int32Constant(Context::SlotOffset(Context::PREVIOUS_INDEX)),
    341                NodeProperties::GetEffectInput(node)));
    342   }
    343   node->ReplaceInput(1, Int32Constant(Context::SlotOffset(access.index())));
    344   PatchOperator(node, machine()->Load(kMachAnyTagged));
    345 }
    346 
    347 
    348 void JSGenericLowering::LowerJSStoreContext(Node* node) {
    349   ContextAccess access = OpParameter<ContextAccess>(node);
    350   // TODO(mstarzinger): Use simplified operators instead of machine operators
    351   // here so that load/store optimization can be applied afterwards.
    352   for (int i = 0; i < access.depth(); ++i) {
    353     node->ReplaceInput(
    354         0, graph()->NewNode(
    355                machine()->Load(kMachAnyTagged),
    356                NodeProperties::GetValueInput(node, 0),
    357                Int32Constant(Context::SlotOffset(Context::PREVIOUS_INDEX)),
    358                NodeProperties::GetEffectInput(node)));
    359   }
    360   node->ReplaceInput(2, NodeProperties::GetValueInput(node, 1));
    361   node->ReplaceInput(1, Int32Constant(Context::SlotOffset(access.index())));
    362   PatchOperator(node, machine()->Store(StoreRepresentation(kMachAnyTagged,
    363                                                            kFullWriteBarrier)));
    364 }
    365 
    366 
    367 void JSGenericLowering::LowerJSCallConstruct(Node* node) {
    368   int arity = OpParameter<int>(node);
    369   CallConstructStub stub(isolate(), NO_CALL_CONSTRUCTOR_FLAGS);
    370   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
    371   CallDescriptor* desc =
    372       linkage()->GetStubCallDescriptor(d, arity, FlagsForNode(node));
    373   Node* stub_code = CodeConstant(stub.GetCode());
    374   Node* construct = NodeProperties::GetValueInput(node, 0);
    375   PatchInsertInput(node, 0, stub_code);
    376   PatchInsertInput(node, 1, Int32Constant(arity - 1));
    377   PatchInsertInput(node, 2, construct);
    378   PatchInsertInput(node, 3, jsgraph()->UndefinedConstant());
    379   PatchOperator(node, common()->Call(desc));
    380 }
    381 
    382 
    383 void JSGenericLowering::LowerJSCallFunction(Node* node) {
    384   CallParameters p = OpParameter<CallParameters>(node);
    385   CallFunctionStub stub(isolate(), p.arity - 2, p.flags);
    386   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
    387   CallDescriptor* desc =
    388       linkage()->GetStubCallDescriptor(d, p.arity - 1, FlagsForNode(node));
    389   Node* stub_code = CodeConstant(stub.GetCode());
    390   PatchInsertInput(node, 0, stub_code);
    391   PatchOperator(node, common()->Call(desc));
    392 }
    393 
    394 
    395 void JSGenericLowering::LowerJSCallRuntime(Node* node) {
    396   Runtime::FunctionId function = OpParameter<Runtime::FunctionId>(node);
    397   int arity = OperatorProperties::GetValueInputCount(node->op());
    398   ReplaceWithRuntimeCall(node, function, arity);
    399 }
    400 
    401 }  // namespace compiler
    402 }  // namespace internal
    403 }  // namespace v8
    404