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/compiler/linkage.h"
      6 
      7 #include "src/ast/scopes.h"
      8 #include "src/code-stubs.h"
      9 #include "src/compilation-info.h"
     10 #include "src/compiler/common-operator.h"
     11 #include "src/compiler/frame.h"
     12 #include "src/compiler/node.h"
     13 #include "src/compiler/osr.h"
     14 #include "src/compiler/pipeline.h"
     15 #include "src/objects-inl.h"
     16 
     17 namespace v8 {
     18 namespace internal {
     19 namespace compiler {
     20 
     21 namespace {
     22 
     23 LinkageLocation regloc(Register reg, MachineType type) {
     24   return LinkageLocation::ForRegister(reg.code(), type);
     25 }
     26 
     27 }  // namespace
     28 
     29 
     30 std::ostream& operator<<(std::ostream& os, const CallDescriptor::Kind& k) {
     31   switch (k) {
     32     case CallDescriptor::kCallCodeObject:
     33       os << "Code";
     34       break;
     35     case CallDescriptor::kCallJSFunction:
     36       os << "JS";
     37       break;
     38     case CallDescriptor::kCallAddress:
     39       os << "Addr";
     40       break;
     41   }
     42   return os;
     43 }
     44 
     45 
     46 std::ostream& operator<<(std::ostream& os, const CallDescriptor& d) {
     47   // TODO(svenpanne) Output properties etc. and be less cryptic.
     48   return os << d.kind() << ":" << d.debug_name() << ":r" << d.ReturnCount()
     49             << "s" << d.StackParameterCount() << "i" << d.InputCount() << "f"
     50             << d.FrameStateCount() << "t" << d.SupportsTailCalls();
     51 }
     52 
     53 MachineSignature* CallDescriptor::GetMachineSignature(Zone* zone) const {
     54   size_t param_count = ParameterCount();
     55   size_t return_count = ReturnCount();
     56   MachineType* types = zone->NewArray<MachineType>(param_count + return_count);
     57   int current = 0;
     58   for (size_t i = 0; i < return_count; ++i) {
     59     types[current++] = GetReturnType(i);
     60   }
     61   for (size_t i = 0; i < param_count; ++i) {
     62     types[current++] = GetParameterType(i);
     63   }
     64   return new (zone) MachineSignature(return_count, param_count, types);
     65 }
     66 
     67 bool CallDescriptor::HasSameReturnLocationsAs(
     68     const CallDescriptor* other) const {
     69   if (ReturnCount() != other->ReturnCount()) return false;
     70   for (size_t i = 0; i < ReturnCount(); ++i) {
     71     if (GetReturnLocation(i) != other->GetReturnLocation(i)) return false;
     72   }
     73   return true;
     74 }
     75 
     76 int CallDescriptor::GetStackParameterDelta(
     77     CallDescriptor const* tail_caller) const {
     78   int callee_slots_above_sp = 0;
     79   for (size_t i = 0; i < InputCount(); ++i) {
     80     LinkageLocation operand = GetInputLocation(i);
     81     if (!operand.IsRegister()) {
     82       int new_candidate =
     83           -operand.GetLocation() + operand.GetSizeInPointers() - 1;
     84       if (new_candidate > callee_slots_above_sp) {
     85         callee_slots_above_sp = new_candidate;
     86       }
     87     }
     88   }
     89   int tail_caller_slots_above_sp = 0;
     90   if (tail_caller != nullptr) {
     91     for (size_t i = 0; i < tail_caller->InputCount(); ++i) {
     92       LinkageLocation operand = tail_caller->GetInputLocation(i);
     93       if (!operand.IsRegister()) {
     94         int new_candidate =
     95             -operand.GetLocation() + operand.GetSizeInPointers() - 1;
     96         if (new_candidate > tail_caller_slots_above_sp) {
     97           tail_caller_slots_above_sp = new_candidate;
     98         }
     99       }
    100     }
    101   }
    102   return callee_slots_above_sp - tail_caller_slots_above_sp;
    103 }
    104 
    105 bool CallDescriptor::CanTailCall(const Node* node) const {
    106   return HasSameReturnLocationsAs(CallDescriptorOf(node->op()));
    107 }
    108 
    109 int CallDescriptor::CalculateFixedFrameSize() const {
    110   switch (kind_) {
    111     case kCallJSFunction:
    112       return PushArgumentCount()
    113                  ? OptimizedBuiltinFrameConstants::kFixedSlotCount
    114                  : StandardFrameConstants::kFixedSlotCount;
    115       break;
    116     case kCallAddress:
    117       return CommonFrameConstants::kFixedSlotCountAboveFp +
    118              CommonFrameConstants::kCPSlotCount;
    119       break;
    120     case kCallCodeObject:
    121       return TypedFrameConstants::kFixedSlotCount;
    122   }
    123   UNREACHABLE();
    124   return 0;
    125 }
    126 
    127 CallDescriptor* Linkage::ComputeIncoming(Zone* zone, CompilationInfo* info) {
    128   DCHECK(!info->IsStub());
    129   if (!info->closure().is_null()) {
    130     // If we are compiling a JS function, use a JS call descriptor,
    131     // plus the receiver.
    132     SharedFunctionInfo* shared = info->closure()->shared();
    133     return GetJSCallDescriptor(zone, info->is_osr(),
    134                                1 + shared->internal_formal_parameter_count(),
    135                                CallDescriptor::kNoFlags);
    136   }
    137   return nullptr;  // TODO(titzer): ?
    138 }
    139 
    140 
    141 // static
    142 bool Linkage::NeedsFrameStateInput(Runtime::FunctionId function) {
    143   switch (function) {
    144     // Most runtime functions need a FrameState. A few chosen ones that we know
    145     // not to call into arbitrary JavaScript, not to throw, and not to lazily
    146     // deoptimize are whitelisted here and can be called without a FrameState.
    147     case Runtime::kAbort:
    148     case Runtime::kAllocateInTargetSpace:
    149     case Runtime::kConvertReceiver:
    150     case Runtime::kCreateIterResultObject:
    151     case Runtime::kDefineGetterPropertyUnchecked:  // TODO(jarin): Is it safe?
    152     case Runtime::kDefineSetterPropertyUnchecked:  // TODO(jarin): Is it safe?
    153     case Runtime::kGeneratorGetContinuation:
    154     case Runtime::kIsFunction:
    155     case Runtime::kNewClosure:
    156     case Runtime::kNewClosure_Tenured:
    157     case Runtime::kNewFunctionContext:
    158     case Runtime::kPushBlockContext:
    159     case Runtime::kPushCatchContext:
    160     case Runtime::kReThrow:
    161     case Runtime::kStringCompare:
    162     case Runtime::kStringEqual:
    163     case Runtime::kStringNotEqual:
    164     case Runtime::kStringLessThan:
    165     case Runtime::kStringLessThanOrEqual:
    166     case Runtime::kStringGreaterThan:
    167     case Runtime::kStringGreaterThanOrEqual:
    168     case Runtime::kToFastProperties:  // TODO(conradw): Is it safe?
    169     case Runtime::kTraceEnter:
    170     case Runtime::kTraceExit:
    171       return false;
    172 
    173     // Some inline intrinsics are also safe to call without a FrameState.
    174     case Runtime::kInlineClassOf:
    175     case Runtime::kInlineCreateIterResultObject:
    176     case Runtime::kInlineFixedArrayGet:
    177     case Runtime::kInlineFixedArraySet:
    178     case Runtime::kInlineGeneratorClose:
    179     case Runtime::kInlineGeneratorGetInputOrDebugPos:
    180     case Runtime::kInlineGeneratorGetResumeMode:
    181     case Runtime::kInlineIsArray:
    182     case Runtime::kInlineIsJSReceiver:
    183     case Runtime::kInlineIsRegExp:
    184     case Runtime::kInlineIsSmi:
    185     case Runtime::kInlineIsTypedArray:
    186       return false;
    187 
    188     default:
    189       break;
    190   }
    191 
    192   // For safety, default to needing a FrameState unless whitelisted.
    193   return true;
    194 }
    195 
    196 
    197 bool CallDescriptor::UsesOnlyRegisters() const {
    198   for (size_t i = 0; i < InputCount(); ++i) {
    199     if (!GetInputLocation(i).IsRegister()) return false;
    200   }
    201   for (size_t i = 0; i < ReturnCount(); ++i) {
    202     if (!GetReturnLocation(i).IsRegister()) return false;
    203   }
    204   return true;
    205 }
    206 
    207 
    208 CallDescriptor* Linkage::GetRuntimeCallDescriptor(
    209     Zone* zone, Runtime::FunctionId function_id, int js_parameter_count,
    210     Operator::Properties properties, CallDescriptor::Flags flags) {
    211   const Runtime::Function* function = Runtime::FunctionForId(function_id);
    212   const int return_count = function->result_size;
    213   const char* debug_name = function->name;
    214 
    215   if (!Linkage::NeedsFrameStateInput(function_id)) {
    216     flags = static_cast<CallDescriptor::Flags>(
    217         flags & ~CallDescriptor::kNeedsFrameState);
    218   }
    219 
    220   return GetCEntryStubCallDescriptor(zone, return_count, js_parameter_count,
    221                                      debug_name, properties, flags);
    222 }
    223 
    224 CallDescriptor* Linkage::GetCEntryStubCallDescriptor(
    225     Zone* zone, int return_count, int js_parameter_count,
    226     const char* debug_name, Operator::Properties properties,
    227     CallDescriptor::Flags flags) {
    228   const size_t function_count = 1;
    229   const size_t num_args_count = 1;
    230   const size_t context_count = 1;
    231   const size_t parameter_count = function_count +
    232                                  static_cast<size_t>(js_parameter_count) +
    233                                  num_args_count + context_count;
    234 
    235   LocationSignature::Builder locations(zone, static_cast<size_t>(return_count),
    236                                        static_cast<size_t>(parameter_count));
    237 
    238   // Add returns.
    239   if (locations.return_count_ > 0) {
    240     locations.AddReturn(regloc(kReturnRegister0, MachineType::AnyTagged()));
    241   }
    242   if (locations.return_count_ > 1) {
    243     locations.AddReturn(regloc(kReturnRegister1, MachineType::AnyTagged()));
    244   }
    245   if (locations.return_count_ > 2) {
    246     locations.AddReturn(regloc(kReturnRegister2, MachineType::AnyTagged()));
    247   }
    248 
    249   // All parameters to the runtime call go on the stack.
    250   for (int i = 0; i < js_parameter_count; i++) {
    251     locations.AddParam(LinkageLocation::ForCallerFrameSlot(
    252         i - js_parameter_count, MachineType::AnyTagged()));
    253   }
    254   // Add runtime function itself.
    255   locations.AddParam(
    256       regloc(kRuntimeCallFunctionRegister, MachineType::Pointer()));
    257 
    258   // Add runtime call argument count.
    259   locations.AddParam(
    260       regloc(kRuntimeCallArgCountRegister, MachineType::Int32()));
    261 
    262   // Add context.
    263   locations.AddParam(regloc(kContextRegister, MachineType::AnyTagged()));
    264 
    265   // The target for runtime calls is a code object.
    266   MachineType target_type = MachineType::AnyTagged();
    267   LinkageLocation target_loc =
    268       LinkageLocation::ForAnyRegister(MachineType::AnyTagged());
    269   return new (zone) CallDescriptor(     // --
    270       CallDescriptor::kCallCodeObject,  // kind
    271       target_type,                      // target MachineType
    272       target_loc,                       // target location
    273       locations.Build(),                // location_sig
    274       js_parameter_count,               // stack_parameter_count
    275       properties,                       // properties
    276       kNoCalleeSaved,                   // callee-saved
    277       kNoCalleeSaved,                   // callee-saved fp
    278       flags,                            // flags
    279       debug_name);                      // debug name
    280 }
    281 
    282 CallDescriptor* Linkage::GetJSCallDescriptor(Zone* zone, bool is_osr,
    283                                              int js_parameter_count,
    284                                              CallDescriptor::Flags flags) {
    285   const size_t return_count = 1;
    286   const size_t context_count = 1;
    287   const size_t new_target_count = 1;
    288   const size_t num_args_count = 1;
    289   const size_t parameter_count =
    290       js_parameter_count + new_target_count + num_args_count + context_count;
    291 
    292   LocationSignature::Builder locations(zone, return_count, parameter_count);
    293 
    294   // All JS calls have exactly one return value.
    295   locations.AddReturn(regloc(kReturnRegister0, MachineType::AnyTagged()));
    296 
    297   // All parameters to JS calls go on the stack.
    298   for (int i = 0; i < js_parameter_count; i++) {
    299     int spill_slot_index = i - js_parameter_count;
    300     locations.AddParam(LinkageLocation::ForCallerFrameSlot(
    301         spill_slot_index, MachineType::AnyTagged()));
    302   }
    303 
    304   // Add JavaScript call new target value.
    305   locations.AddParam(
    306       regloc(kJavaScriptCallNewTargetRegister, MachineType::AnyTagged()));
    307 
    308   // Add JavaScript call argument count.
    309   locations.AddParam(
    310       regloc(kJavaScriptCallArgCountRegister, MachineType::Int32()));
    311 
    312   // Add context.
    313   locations.AddParam(regloc(kContextRegister, MachineType::AnyTagged()));
    314 
    315   // The target for JS function calls is the JSFunction object.
    316   MachineType target_type = MachineType::AnyTagged();
    317   // When entering into an OSR function from unoptimized code the JSFunction
    318   // is not in a register, but it is on the stack in the marker spill slot.
    319   LinkageLocation target_loc =
    320       is_osr ? LinkageLocation::ForSavedCallerFunction()
    321              : regloc(kJSFunctionRegister, MachineType::AnyTagged());
    322   return new (zone) CallDescriptor(     // --
    323       CallDescriptor::kCallJSFunction,  // kind
    324       target_type,                      // target MachineType
    325       target_loc,                       // target location
    326       locations.Build(),                // location_sig
    327       js_parameter_count,               // stack_parameter_count
    328       Operator::kNoProperties,          // properties
    329       kNoCalleeSaved,                   // callee-saved
    330       kNoCalleeSaved,                   // callee-saved fp
    331       CallDescriptor::kCanUseRoots |    // flags
    332           flags,                        // flags
    333       "js-call");
    334 }
    335 
    336 // TODO(all): Add support for return representations/locations to
    337 // CallInterfaceDescriptor.
    338 // TODO(turbofan): cache call descriptors for code stub calls.
    339 CallDescriptor* Linkage::GetStubCallDescriptor(
    340     Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor,
    341     int stack_parameter_count, CallDescriptor::Flags flags,
    342     Operator::Properties properties, MachineType return_type,
    343     size_t return_count) {
    344   const int register_parameter_count = descriptor.GetRegisterParameterCount();
    345   const int js_parameter_count =
    346       register_parameter_count + stack_parameter_count;
    347   const int context_count = 1;
    348   const size_t parameter_count =
    349       static_cast<size_t>(js_parameter_count + context_count);
    350 
    351   LocationSignature::Builder locations(zone, return_count, parameter_count);
    352 
    353   // Add returns.
    354   if (locations.return_count_ > 0) {
    355     locations.AddReturn(regloc(kReturnRegister0, return_type));
    356   }
    357   if (locations.return_count_ > 1) {
    358     locations.AddReturn(regloc(kReturnRegister1, return_type));
    359   }
    360   if (locations.return_count_ > 2) {
    361     locations.AddReturn(regloc(kReturnRegister2, return_type));
    362   }
    363 
    364   // Add parameters in registers and on the stack.
    365   for (int i = 0; i < js_parameter_count; i++) {
    366     if (i < register_parameter_count) {
    367       // The first parameters go in registers.
    368       Register reg = descriptor.GetRegisterParameter(i);
    369       MachineType type = descriptor.GetParameterType(i);
    370       locations.AddParam(regloc(reg, type));
    371     } else {
    372       // The rest of the parameters go on the stack.
    373       int stack_slot = i - register_parameter_count - stack_parameter_count;
    374       locations.AddParam(LinkageLocation::ForCallerFrameSlot(
    375           stack_slot, MachineType::AnyTagged()));
    376     }
    377   }
    378   // Add context.
    379   locations.AddParam(regloc(kContextRegister, MachineType::AnyTagged()));
    380 
    381   // The target for stub calls is a code object.
    382   MachineType target_type = MachineType::AnyTagged();
    383   LinkageLocation target_loc =
    384       LinkageLocation::ForAnyRegister(MachineType::AnyTagged());
    385   return new (zone) CallDescriptor(     // --
    386       CallDescriptor::kCallCodeObject,  // kind
    387       target_type,                      // target MachineType
    388       target_loc,                       // target location
    389       locations.Build(),                // location_sig
    390       stack_parameter_count,            // stack_parameter_count
    391       properties,                       // properties
    392       kNoCalleeSaved,                   // callee-saved registers
    393       kNoCalleeSaved,                   // callee-saved fp
    394       CallDescriptor::kCanUseRoots |    // flags
    395           flags,                        // flags
    396       descriptor.DebugName(isolate));
    397 }
    398 
    399 // static
    400 CallDescriptor* Linkage::GetAllocateCallDescriptor(Zone* zone) {
    401   LocationSignature::Builder locations(zone, 1, 1);
    402 
    403   locations.AddParam(regloc(kAllocateSizeRegister, MachineType::Int32()));
    404 
    405   locations.AddReturn(regloc(kReturnRegister0, MachineType::AnyTagged()));
    406 
    407   // The target for allocate calls is a code object.
    408   MachineType target_type = MachineType::AnyTagged();
    409   LinkageLocation target_loc =
    410       LinkageLocation::ForAnyRegister(MachineType::AnyTagged());
    411   return new (zone) CallDescriptor(     // --
    412       CallDescriptor::kCallCodeObject,  // kind
    413       target_type,                      // target MachineType
    414       target_loc,                       // target location
    415       locations.Build(),                // location_sig
    416       0,                                // stack_parameter_count
    417       Operator::kNoThrow,               // properties
    418       kNoCalleeSaved,                   // callee-saved registers
    419       kNoCalleeSaved,                   // callee-saved fp
    420       CallDescriptor::kCanUseRoots,     // flags
    421       "Allocate");
    422 }
    423 
    424 // static
    425 CallDescriptor* Linkage::GetBytecodeDispatchCallDescriptor(
    426     Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor,
    427     int stack_parameter_count) {
    428   const int register_parameter_count = descriptor.GetRegisterParameterCount();
    429   const int parameter_count = register_parameter_count + stack_parameter_count;
    430 
    431   LocationSignature::Builder locations(zone, 0, parameter_count);
    432 
    433   // Add parameters in registers and on the stack.
    434   for (int i = 0; i < parameter_count; i++) {
    435     if (i < register_parameter_count) {
    436       // The first parameters go in registers.
    437       Register reg = descriptor.GetRegisterParameter(i);
    438       MachineType type = descriptor.GetParameterType(i);
    439       locations.AddParam(regloc(reg, type));
    440     } else {
    441       // The rest of the parameters go on the stack.
    442       int stack_slot = i - register_parameter_count - stack_parameter_count;
    443       locations.AddParam(LinkageLocation::ForCallerFrameSlot(
    444           stack_slot, MachineType::AnyTagged()));
    445     }
    446   }
    447 
    448   // The target for interpreter dispatches is a code entry address.
    449   MachineType target_type = MachineType::Pointer();
    450   LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type);
    451   return new (zone) CallDescriptor(            // --
    452       CallDescriptor::kCallAddress,            // kind
    453       target_type,                             // target MachineType
    454       target_loc,                              // target location
    455       locations.Build(),                       // location_sig
    456       stack_parameter_count,                   // stack_parameter_count
    457       Operator::kNoProperties,                 // properties
    458       kNoCalleeSaved,                          // callee-saved registers
    459       kNoCalleeSaved,                          // callee-saved fp
    460       CallDescriptor::kCanUseRoots |           // flags
    461           CallDescriptor::kSupportsTailCalls,  // flags
    462       descriptor.DebugName(isolate));
    463 }
    464 
    465 LinkageLocation Linkage::GetOsrValueLocation(int index) const {
    466   CHECK(incoming_->IsJSFunctionCall());
    467   int parameter_count = static_cast<int>(incoming_->JSParameterCount() - 1);
    468   int first_stack_slot = OsrHelper::FirstStackSlotIndex(parameter_count);
    469 
    470   if (index == kOsrContextSpillSlotIndex) {
    471     // Context. Use the parameter location of the context spill slot.
    472     // Parameter (arity + 2) is special for the context of the function frame.
    473     // >> context_index = target + receiver + params + new_target + #args
    474     int context_index = 1 + 1 + parameter_count + 1 + 1;
    475     return incoming_->GetInputLocation(context_index);
    476   } else if (index >= first_stack_slot) {
    477     // Local variable stored in this (callee) stack.
    478     int spill_index =
    479         index - first_stack_slot + StandardFrameConstants::kFixedSlotCount;
    480     return LinkageLocation::ForCalleeFrameSlot(spill_index,
    481                                                MachineType::AnyTagged());
    482   } else {
    483     // Parameter. Use the assigned location from the incoming call descriptor.
    484     int parameter_index = 1 + index;  // skip index 0, which is the target.
    485     return incoming_->GetInputLocation(parameter_index);
    486   }
    487 }
    488 
    489 
    490 bool Linkage::ParameterHasSecondaryLocation(int index) const {
    491   if (!incoming_->IsJSFunctionCall()) return false;
    492   LinkageLocation loc = GetParameterLocation(index);
    493   return (loc == regloc(kJSFunctionRegister, MachineType::AnyTagged()) ||
    494           loc == regloc(kContextRegister, MachineType::AnyTagged()));
    495 }
    496 
    497 LinkageLocation Linkage::GetParameterSecondaryLocation(int index) const {
    498   DCHECK(ParameterHasSecondaryLocation(index));
    499   LinkageLocation loc = GetParameterLocation(index);
    500 
    501   if (loc == regloc(kJSFunctionRegister, MachineType::AnyTagged())) {
    502     return LinkageLocation::ForCalleeFrameSlot(Frame::kJSFunctionSlot,
    503                                                MachineType::AnyTagged());
    504   } else {
    505     DCHECK(loc == regloc(kContextRegister, MachineType::AnyTagged()));
    506     return LinkageLocation::ForCalleeFrameSlot(Frame::kContextSlot,
    507                                                MachineType::AnyTagged());
    508   }
    509 }
    510 
    511 
    512 }  // namespace compiler
    513 }  // namespace internal
    514 }  // namespace v8
    515