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 
regloc(Register reg,MachineType type)23 LinkageLocation regloc(Register reg, MachineType type) {
24   return LinkageLocation::ForRegister(reg.code(), type);
25 }
26 
27 }  // namespace
28 
29 
operator <<(std::ostream & os,const CallDescriptor::Kind & k)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 
operator <<(std::ostream & os,const CallDescriptor & d)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 
GetMachineSignature(Zone * zone) const53 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 
HasSameReturnLocationsAs(const CallDescriptor * other) const67 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 
GetStackParameterDelta(CallDescriptor const * tail_caller) const76 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 
CanTailCall(const Node * node) const105 bool CallDescriptor::CanTailCall(const Node* node) const {
106   return HasSameReturnLocationsAs(CallDescriptorOf(node->op()));
107 }
108 
CalculateFixedFrameSize() const109 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 
ComputeIncoming(Zone * zone,CompilationInfo * info)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
NeedsFrameStateInput(Runtime::FunctionId function)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 
UsesOnlyRegisters() const197 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 
GetRuntimeCallDescriptor(Zone * zone,Runtime::FunctionId function_id,int js_parameter_count,Operator::Properties properties,CallDescriptor::Flags flags)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 
GetCEntryStubCallDescriptor(Zone * zone,int return_count,int js_parameter_count,const char * debug_name,Operator::Properties properties,CallDescriptor::Flags flags)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 
GetJSCallDescriptor(Zone * zone,bool is_osr,int js_parameter_count,CallDescriptor::Flags flags)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.
GetStubCallDescriptor(Isolate * isolate,Zone * zone,const CallInterfaceDescriptor & descriptor,int stack_parameter_count,CallDescriptor::Flags flags,Operator::Properties properties,MachineType return_type,size_t return_count)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
GetAllocateCallDescriptor(Zone * zone)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
GetBytecodeDispatchCallDescriptor(Isolate * isolate,Zone * zone,const CallInterfaceDescriptor & descriptor,int stack_parameter_count)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 
GetOsrValueLocation(int index) const465 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 
ParameterHasSecondaryLocation(int index) const490 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 
GetParameterSecondaryLocation(int index) const497 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