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