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/ast/scopes.h"
      6 #include "src/code-stubs.h"
      7 #include "src/compiler.h"
      8 #include "src/compiler/common-operator.h"
      9 #include "src/compiler/frame.h"
     10 #include "src/compiler/linkage.h"
     11 #include "src/compiler/node.h"
     12 #include "src/compiler/osr.h"
     13 #include "src/compiler/pipeline.h"
     14 
     15 namespace v8 {
     16 namespace internal {
     17 namespace compiler {
     18 
     19 namespace {
     20 LinkageLocation regloc(Register reg) {
     21   return LinkageLocation::ForRegister(reg.code());
     22 }
     23 
     24 
     25 MachineType reptyp(Representation representation) {
     26   switch (representation.kind()) {
     27     case Representation::kInteger8:
     28       return MachineType::Int8();
     29     case Representation::kUInteger8:
     30       return MachineType::Uint8();
     31     case Representation::kInteger16:
     32       return MachineType::Int16();
     33     case Representation::kUInteger16:
     34       return MachineType::Uint16();
     35     case Representation::kInteger32:
     36       return MachineType::Int32();
     37     case Representation::kSmi:
     38     case Representation::kTagged:
     39     case Representation::kHeapObject:
     40       return MachineType::AnyTagged();
     41     case Representation::kDouble:
     42       return MachineType::Float64();
     43     case Representation::kExternal:
     44       return MachineType::Pointer();
     45     case Representation::kNone:
     46     case Representation::kNumRepresentations:
     47       break;
     48   }
     49   UNREACHABLE();
     50   return MachineType::None();
     51 }
     52 }  // namespace
     53 
     54 
     55 std::ostream& operator<<(std::ostream& os, const CallDescriptor::Kind& k) {
     56   switch (k) {
     57     case CallDescriptor::kCallCodeObject:
     58       os << "Code";
     59       break;
     60     case CallDescriptor::kCallJSFunction:
     61       os << "JS";
     62       break;
     63     case CallDescriptor::kCallAddress:
     64       os << "Addr";
     65       break;
     66     case CallDescriptor::kLazyBailout:
     67       os << "LazyBail";
     68       break;
     69   }
     70   return os;
     71 }
     72 
     73 
     74 std::ostream& operator<<(std::ostream& os, const CallDescriptor& d) {
     75   // TODO(svenpanne) Output properties etc. and be less cryptic.
     76   return os << d.kind() << ":" << d.debug_name() << ":r" << d.ReturnCount()
     77             << "s" << d.StackParameterCount() << "i" << d.InputCount() << "f"
     78             << d.FrameStateCount() << "t" << d.SupportsTailCalls();
     79 }
     80 
     81 
     82 bool CallDescriptor::HasSameReturnLocationsAs(
     83     const CallDescriptor* other) const {
     84   if (ReturnCount() != other->ReturnCount()) return false;
     85   for (size_t i = 0; i < ReturnCount(); ++i) {
     86     if (GetReturnLocation(i) != other->GetReturnLocation(i)) return false;
     87   }
     88   return true;
     89 }
     90 
     91 
     92 bool CallDescriptor::CanTailCall(const Node* node,
     93                                  int* stack_param_delta) const {
     94   CallDescriptor const* other = OpParameter<CallDescriptor const*>(node);
     95   size_t current_input = 0;
     96   size_t other_input = 0;
     97   *stack_param_delta = 0;
     98   bool more_other = true;
     99   bool more_this = true;
    100   while (more_other || more_this) {
    101     if (other_input < other->InputCount()) {
    102       if (!other->GetInputLocation(other_input).IsRegister()) {
    103         (*stack_param_delta)--;
    104       }
    105     } else {
    106       more_other = false;
    107     }
    108     if (current_input < InputCount()) {
    109       if (!GetInputLocation(current_input).IsRegister()) {
    110         (*stack_param_delta)++;
    111       }
    112     } else {
    113       more_this = false;
    114     }
    115     ++current_input;
    116     ++other_input;
    117   }
    118   return HasSameReturnLocationsAs(OpParameter<CallDescriptor const*>(node));
    119 }
    120 
    121 
    122 CallDescriptor* Linkage::ComputeIncoming(Zone* zone, CompilationInfo* info) {
    123   if (info->code_stub() != nullptr) {
    124     // Use the code stub interface descriptor.
    125     CodeStub* stub = info->code_stub();
    126     CallInterfaceDescriptor descriptor = stub->GetCallInterfaceDescriptor();
    127     return GetStubCallDescriptor(
    128         info->isolate(), zone, descriptor, stub->GetStackParameterCount(),
    129         CallDescriptor::kNoFlags, Operator::kNoProperties);
    130   }
    131   if (info->has_literal()) {
    132     // If we already have the function literal, use the number of parameters
    133     // plus the receiver.
    134     return GetJSCallDescriptor(zone, info->is_osr(),
    135                                1 + info->literal()->parameter_count(),
    136                                CallDescriptor::kNoFlags);
    137   }
    138   if (!info->closure().is_null()) {
    139     // If we are compiling a JS function, use a JS call descriptor,
    140     // plus the receiver.
    141     SharedFunctionInfo* shared = info->closure()->shared();
    142     return GetJSCallDescriptor(zone, info->is_osr(),
    143                                1 + shared->internal_formal_parameter_count(),
    144                                CallDescriptor::kNoFlags);
    145   }
    146   return nullptr;  // TODO(titzer): ?
    147 }
    148 
    149 
    150 // static
    151 int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
    152   // Most runtime functions need a FrameState. A few chosen ones that we know
    153   // not to call into arbitrary JavaScript, not to throw, and not to deoptimize
    154   // are blacklisted here and can be called without a FrameState.
    155   switch (function) {
    156     case Runtime::kAllocateInTargetSpace:
    157     case Runtime::kCreateIterResultObject:
    158     case Runtime::kDefineClassMethod:              // TODO(jarin): Is it safe?
    159     case Runtime::kDefineGetterPropertyUnchecked:  // TODO(jarin): Is it safe?
    160     case Runtime::kDefineSetterPropertyUnchecked:  // TODO(jarin): Is it safe?
    161     case Runtime::kFinalizeClassDefinition:        // TODO(conradw): Is it safe?
    162     case Runtime::kForInDone:
    163     case Runtime::kForInStep:
    164     case Runtime::kGetSuperConstructor:
    165     case Runtime::kNewClosure:
    166     case Runtime::kNewClosure_Tenured:
    167     case Runtime::kNewFunctionContext:
    168     case Runtime::kPushBlockContext:
    169     case Runtime::kPushCatchContext:
    170     case Runtime::kReThrow:
    171     case Runtime::kStringCompare:
    172     case Runtime::kStringEquals:
    173     case Runtime::kToFastProperties:  // TODO(jarin): Is it safe?
    174     case Runtime::kTraceEnter:
    175     case Runtime::kTraceExit:
    176       return 0;
    177     case Runtime::kInlineArguments:
    178     case Runtime::kInlineArgumentsLength:
    179     case Runtime::kInlineGetPrototype:
    180     case Runtime::kInlineRegExpConstructResult:
    181     case Runtime::kInlineRegExpExec:
    182     case Runtime::kInlineSubString:
    183     case Runtime::kInlineToInteger:
    184     case Runtime::kInlineToLength:
    185     case Runtime::kInlineToName:
    186     case Runtime::kInlineToNumber:
    187     case Runtime::kInlineToObject:
    188     case Runtime::kInlineToPrimitive_Number:
    189     case Runtime::kInlineToPrimitive_String:
    190     case Runtime::kInlineToPrimitive:
    191     case Runtime::kInlineToString:
    192       return 1;
    193     case Runtime::kInlineCall:
    194     case Runtime::kInlineTailCall:
    195     case Runtime::kInlineDeoptimizeNow:
    196     case Runtime::kInlineThrowNotDateError:
    197       return 2;
    198     default:
    199       break;
    200   }
    201 
    202   // Most inlined runtime functions (except the ones listed above) can be called
    203   // without a FrameState or will be lowered by JSIntrinsicLowering internally.
    204   const Runtime::Function* const f = Runtime::FunctionForId(function);
    205   if (f->intrinsic_type == Runtime::IntrinsicType::INLINE) return 0;
    206 
    207   return 1;
    208 }
    209 
    210 
    211 bool CallDescriptor::UsesOnlyRegisters() const {
    212   for (size_t i = 0; i < InputCount(); ++i) {
    213     if (!GetInputLocation(i).IsRegister()) return false;
    214   }
    215   for (size_t i = 0; i < ReturnCount(); ++i) {
    216     if (!GetReturnLocation(i).IsRegister()) return false;
    217   }
    218   return true;
    219 }
    220 
    221 
    222 CallDescriptor* Linkage::GetRuntimeCallDescriptor(
    223     Zone* zone, Runtime::FunctionId function_id, int js_parameter_count,
    224     Operator::Properties properties, CallDescriptor::Flags flags) {
    225   const size_t function_count = 1;
    226   const size_t num_args_count = 1;
    227   const size_t context_count = 1;
    228   const size_t parameter_count = function_count +
    229                                  static_cast<size_t>(js_parameter_count) +
    230                                  num_args_count + context_count;
    231 
    232   const Runtime::Function* function = Runtime::FunctionForId(function_id);
    233   const size_t return_count = static_cast<size_t>(function->result_size);
    234 
    235   LocationSignature::Builder locations(zone, return_count, parameter_count);
    236   MachineSignature::Builder types(zone, return_count, parameter_count);
    237 
    238   // Add returns.
    239   if (locations.return_count_ > 0) {
    240     locations.AddReturn(regloc(kReturnRegister0));
    241   }
    242   if (locations.return_count_ > 1) {
    243     locations.AddReturn(regloc(kReturnRegister1));
    244   }
    245   for (size_t i = 0; i < return_count; i++) {
    246     types.AddReturn(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(
    252         LinkageLocation::ForCallerFrameSlot(i - js_parameter_count));
    253     types.AddParam(MachineType::AnyTagged());
    254   }
    255   // Add runtime function itself.
    256   locations.AddParam(regloc(kRuntimeCallFunctionRegister));
    257   types.AddParam(MachineType::AnyTagged());
    258 
    259   // Add runtime call argument count.
    260   locations.AddParam(regloc(kRuntimeCallArgCountRegister));
    261   types.AddParam(MachineType::Pointer());
    262 
    263   // Add context.
    264   locations.AddParam(regloc(kContextRegister));
    265   types.AddParam(MachineType::AnyTagged());
    266 
    267   if (Linkage::FrameStateInputCount(function_id) == 0) {
    268     flags = static_cast<CallDescriptor::Flags>(
    269         flags & ~CallDescriptor::kNeedsFrameState);
    270   }
    271 
    272   // The target for runtime calls is a code object.
    273   MachineType target_type = MachineType::AnyTagged();
    274   LinkageLocation target_loc = LinkageLocation::ForAnyRegister();
    275   return new (zone) CallDescriptor(     // --
    276       CallDescriptor::kCallCodeObject,  // kind
    277       target_type,                      // target MachineType
    278       target_loc,                       // target location
    279       types.Build(),                    // machine_sig
    280       locations.Build(),                // location_sig
    281       js_parameter_count,               // stack_parameter_count
    282       properties,                       // properties
    283       kNoCalleeSaved,                   // callee-saved
    284       kNoCalleeSaved,                   // callee-saved fp
    285       flags,                            // flags
    286       function->name);                  // debug name
    287 }
    288 
    289 
    290 CallDescriptor* Linkage::GetLazyBailoutDescriptor(Zone* zone) {
    291   const size_t return_count = 0;
    292   const size_t parameter_count = 0;
    293 
    294   LocationSignature::Builder locations(zone, return_count, parameter_count);
    295   MachineSignature::Builder types(zone, return_count, parameter_count);
    296 
    297   // The target is ignored, but we need to give some values here.
    298   MachineType target_type = MachineType::AnyTagged();
    299   LinkageLocation target_loc = regloc(kJSFunctionRegister);
    300   return new (zone) CallDescriptor(      // --
    301       CallDescriptor::kLazyBailout,      // kind
    302       target_type,                       // target MachineType
    303       target_loc,                        // target location
    304       types.Build(),                     // machine_sig
    305       locations.Build(),                 // location_sig
    306       0,                                 // stack_parameter_count
    307       Operator::kNoThrow,                // properties
    308       kNoCalleeSaved,                    // callee-saved
    309       kNoCalleeSaved,                    // callee-saved fp
    310       CallDescriptor::kNeedsFrameState,  // flags
    311       "lazy-bailout");
    312 }
    313 
    314 
    315 CallDescriptor* Linkage::GetJSCallDescriptor(Zone* zone, bool is_osr,
    316                                              int js_parameter_count,
    317                                              CallDescriptor::Flags flags) {
    318   const size_t return_count = 1;
    319   const size_t context_count = 1;
    320   const size_t new_target_count = 1;
    321   const size_t num_args_count = 1;
    322   const size_t parameter_count =
    323       js_parameter_count + new_target_count + num_args_count + context_count;
    324 
    325   LocationSignature::Builder locations(zone, return_count, parameter_count);
    326   MachineSignature::Builder types(zone, return_count, parameter_count);
    327 
    328   // All JS calls have exactly one return value.
    329   locations.AddReturn(regloc(kReturnRegister0));
    330   types.AddReturn(MachineType::AnyTagged());
    331 
    332   // All parameters to JS calls go on the stack.
    333   for (int i = 0; i < js_parameter_count; i++) {
    334     int spill_slot_index = i - js_parameter_count;
    335     locations.AddParam(LinkageLocation::ForCallerFrameSlot(spill_slot_index));
    336     types.AddParam(MachineType::AnyTagged());
    337   }
    338 
    339   // Add JavaScript call new target value.
    340   locations.AddParam(regloc(kJavaScriptCallNewTargetRegister));
    341   types.AddParam(MachineType::AnyTagged());
    342 
    343   // Add JavaScript call argument count.
    344   locations.AddParam(regloc(kJavaScriptCallArgCountRegister));
    345   types.AddParam(MachineType::Int32());
    346 
    347   // Add context.
    348   locations.AddParam(regloc(kContextRegister));
    349   types.AddParam(MachineType::AnyTagged());
    350 
    351   // The target for JS function calls is the JSFunction object.
    352   MachineType target_type = MachineType::AnyTagged();
    353   // TODO(titzer): When entering into an OSR function from unoptimized code,
    354   // the JSFunction is not in a register, but it is on the stack in an
    355   // unaddressable spill slot. We hack this in the OSR prologue. Fix.
    356   LinkageLocation target_loc = regloc(kJSFunctionRegister);
    357   return new (zone) CallDescriptor(     // --
    358       CallDescriptor::kCallJSFunction,  // kind
    359       target_type,                      // target MachineType
    360       target_loc,                       // target location
    361       types.Build(),                    // machine_sig
    362       locations.Build(),                // location_sig
    363       js_parameter_count,               // stack_parameter_count
    364       Operator::kNoProperties,          // properties
    365       kNoCalleeSaved,                   // callee-saved
    366       kNoCalleeSaved,                   // callee-saved fp
    367       CallDescriptor::kCanUseRoots |    // flags
    368           flags,                        // flags
    369       "js-call");
    370 }
    371 
    372 
    373 CallDescriptor* Linkage::GetInterpreterDispatchDescriptor(Zone* zone) {
    374   MachineSignature::Builder types(zone, 0, 6);
    375   LocationSignature::Builder locations(zone, 0, 6);
    376 
    377   // Add registers for fixed parameters passed via interpreter dispatch.
    378   STATIC_ASSERT(0 == Linkage::kInterpreterAccumulatorParameter);
    379   types.AddParam(MachineType::AnyTagged());
    380   locations.AddParam(regloc(kInterpreterAccumulatorRegister));
    381 
    382   STATIC_ASSERT(1 == Linkage::kInterpreterRegisterFileParameter);
    383   types.AddParam(MachineType::Pointer());
    384   locations.AddParam(regloc(kInterpreterRegisterFileRegister));
    385 
    386   STATIC_ASSERT(2 == Linkage::kInterpreterBytecodeOffsetParameter);
    387   types.AddParam(MachineType::IntPtr());
    388   locations.AddParam(regloc(kInterpreterBytecodeOffsetRegister));
    389 
    390   STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter);
    391   types.AddParam(MachineType::AnyTagged());
    392   locations.AddParam(regloc(kInterpreterBytecodeArrayRegister));
    393 
    394   STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
    395   types.AddParam(MachineType::Pointer());
    396 #if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X87)
    397   // TODO(rmcilroy): Make the context param the one spilled to the stack once
    398   // Turbofan supports modified stack arguments in tail calls.
    399   locations.AddParam(
    400       LinkageLocation::ForCallerFrameSlot(kInterpreterDispatchTableSpillSlot));
    401 #else
    402   locations.AddParam(regloc(kInterpreterDispatchTableRegister));
    403 #endif
    404 
    405   STATIC_ASSERT(5 == Linkage::kInterpreterContextParameter);
    406   types.AddParam(MachineType::AnyTagged());
    407   locations.AddParam(regloc(kContextRegister));
    408 
    409   LinkageLocation target_loc = LinkageLocation::ForAnyRegister();
    410   return new (zone) CallDescriptor(         // --
    411       CallDescriptor::kCallCodeObject,      // kind
    412       MachineType::None(),                  // target MachineType
    413       target_loc,                           // target location
    414       types.Build(),                        // machine_sig
    415       locations.Build(),                    // location_sig
    416       0,                                    // stack_parameter_count
    417       Operator::kNoProperties,              // properties
    418       kNoCalleeSaved,                       // callee-saved registers
    419       kNoCalleeSaved,                       // callee-saved fp regs
    420       CallDescriptor::kSupportsTailCalls |  // flags
    421           CallDescriptor::kCanUseRoots,     // flags
    422       "interpreter-dispatch");
    423 }
    424 
    425 
    426 // TODO(all): Add support for return representations/locations to
    427 // CallInterfaceDescriptor.
    428 // TODO(turbofan): cache call descriptors for code stub calls.
    429 CallDescriptor* Linkage::GetStubCallDescriptor(
    430     Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor,
    431     int stack_parameter_count, CallDescriptor::Flags flags,
    432     Operator::Properties properties, MachineType return_type,
    433     size_t return_count) {
    434   const int register_parameter_count = descriptor.GetRegisterParameterCount();
    435   const int js_parameter_count =
    436       register_parameter_count + stack_parameter_count;
    437   const int context_count = 1;
    438   const size_t parameter_count =
    439       static_cast<size_t>(js_parameter_count + context_count);
    440 
    441   LocationSignature::Builder locations(zone, return_count, parameter_count);
    442   MachineSignature::Builder types(zone, return_count, parameter_count);
    443 
    444   // Add returns.
    445   if (locations.return_count_ > 0) {
    446     locations.AddReturn(regloc(kReturnRegister0));
    447   }
    448   if (locations.return_count_ > 1) {
    449     locations.AddReturn(regloc(kReturnRegister1));
    450   }
    451   for (size_t i = 0; i < return_count; i++) {
    452     types.AddReturn(return_type);
    453   }
    454 
    455   // Add parameters in registers and on the stack.
    456   for (int i = 0; i < js_parameter_count; i++) {
    457     if (i < register_parameter_count) {
    458       // The first parameters go in registers.
    459       Register reg = descriptor.GetRegisterParameter(i);
    460       Representation rep =
    461           RepresentationFromType(descriptor.GetParameterType(i));
    462       locations.AddParam(regloc(reg));
    463       types.AddParam(reptyp(rep));
    464     } else {
    465       // The rest of the parameters go on the stack.
    466       int stack_slot = i - register_parameter_count - stack_parameter_count;
    467       locations.AddParam(LinkageLocation::ForCallerFrameSlot(stack_slot));
    468       types.AddParam(MachineType::AnyTagged());
    469     }
    470   }
    471   // Add context.
    472   locations.AddParam(regloc(kContextRegister));
    473   types.AddParam(MachineType::AnyTagged());
    474 
    475   // The target for stub calls is a code object.
    476   MachineType target_type = MachineType::AnyTagged();
    477   LinkageLocation target_loc = LinkageLocation::ForAnyRegister();
    478   return new (zone) CallDescriptor(     // --
    479       CallDescriptor::kCallCodeObject,  // kind
    480       target_type,                      // target MachineType
    481       target_loc,                       // target location
    482       types.Build(),                    // machine_sig
    483       locations.Build(),                // location_sig
    484       stack_parameter_count,            // stack_parameter_count
    485       properties,                       // properties
    486       kNoCalleeSaved,                   // callee-saved registers
    487       kNoCalleeSaved,                   // callee-saved fp
    488       flags,                            // flags
    489       descriptor.DebugName(isolate));
    490 }
    491 
    492 
    493 LinkageLocation Linkage::GetOsrValueLocation(int index) const {
    494   CHECK(incoming_->IsJSFunctionCall());
    495   int parameter_count = static_cast<int>(incoming_->JSParameterCount() - 1);
    496   int first_stack_slot = OsrHelper::FirstStackSlotIndex(parameter_count);
    497 
    498   if (index == kOsrContextSpillSlotIndex) {
    499     // Context. Use the parameter location of the context spill slot.
    500     // Parameter (arity + 2) is special for the context of the function frame.
    501     // >> context_index = target + receiver + params + new_target + #args
    502     int context_index = 1 + 1 + parameter_count + 1 + 1;
    503     return incoming_->GetInputLocation(context_index);
    504   } else if (index >= first_stack_slot) {
    505     // Local variable stored in this (callee) stack.
    506     int spill_index =
    507         index - first_stack_slot + StandardFrameConstants::kFixedSlotCount;
    508     return LinkageLocation::ForCalleeFrameSlot(spill_index);
    509   } else {
    510     // Parameter. Use the assigned location from the incoming call descriptor.
    511     int parameter_index = 1 + index;  // skip index 0, which is the target.
    512     return incoming_->GetInputLocation(parameter_index);
    513   }
    514 }
    515 
    516 
    517 bool Linkage::ParameterHasSecondaryLocation(int index) const {
    518   if (incoming_->kind() != CallDescriptor::kCallJSFunction) return false;
    519   LinkageLocation loc = GetParameterLocation(index);
    520   return (loc == regloc(kJSFunctionRegister) ||
    521           loc == regloc(kContextRegister));
    522 }
    523 
    524 LinkageLocation Linkage::GetParameterSecondaryLocation(int index) const {
    525   DCHECK(ParameterHasSecondaryLocation(index));
    526   LinkageLocation loc = GetParameterLocation(index);
    527 
    528   if (loc == regloc(kJSFunctionRegister)) {
    529     return LinkageLocation::ForCalleeFrameSlot(Frame::kJSFunctionSlot);
    530   } else {
    531     DCHECK(loc == regloc(kContextRegister));
    532     return LinkageLocation::ForCalleeFrameSlot(Frame::kContextSlot);
    533   }
    534 }
    535 
    536 
    537 }  // namespace compiler
    538 }  // namespace internal
    539 }  // namespace v8
    540