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