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