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/assembler-inl.h"
8#include "src/compiler/common-operator.h"
9#include "src/compiler/frame.h"
10#include "src/compiler/node.h"
11#include "src/compiler/osr.h"
12#include "src/compiler/pipeline.h"
13#include "src/macro-assembler.h"
14#include "src/optimized-compilation-info.h"
15
16namespace v8 {
17namespace internal {
18namespace compiler {
19
20namespace {
21
22inline LinkageLocation regloc(Register reg, MachineType type) {
23 return LinkageLocation::ForRegister(reg.code(), type);
24}
25
26} // namespace
27
28
29std::ostream& operator<<(std::ostream& os, const CallDescriptor::Kind& k) {
30 switch (k) {
31 case CallDescriptor::kCallCodeObject:
32 os << "Code";
33 break;
34 case CallDescriptor::kCallJSFunction:
35 os << "JS";
36 break;
37 case CallDescriptor::kCallAddress:
38 os << "Addr";
39 break;
40 case CallDescriptor::kCallWasmFunction:
41 os << "WasmFunction";
42 break;
43 case CallDescriptor::kCallWasmImportWrapper:
44 os << "WasmImportWrapper";
45 break;
46 case CallDescriptor::kCallBuiltinPointer:
47 os << "BuiltinPointer";
48 break;
49 }
50 return os;
51}
52
53
54std::ostream& operator<<(std::ostream& os, const CallDescriptor& d) {
55 // TODO(svenpanne) Output properties etc. and be less cryptic.
56 return os << d.kind() << ":" << d.debug_name() << ":r" << d.ReturnCount()
57 << "s" << d.StackParameterCount() << "i" << d.InputCount() << "f"
58 << d.FrameStateCount();
59}
60
61MachineSignature* CallDescriptor::GetMachineSignature(Zone* zone) const {
62 size_t param_count = ParameterCount();
63 size_t return_count = ReturnCount();
64 MachineType* types = zone->NewArray<MachineType>(param_count + return_count);
65 int current = 0;
66 for (size_t i = 0; i < return_count; ++i) {
67 types[current++] = GetReturnType(i);
68 }
69 for (size_t i = 0; i < param_count; ++i) {
70 types[current++] = GetParameterType(i);
71 }
72 return new (zone) MachineSignature(return_count, param_count, types);
73}
74
75bool CallDescriptor::HasSameReturnLocationsAs(
76 const CallDescriptor* other) const {
77 if (ReturnCount() != other->ReturnCount()) return false;
78 for (size_t i = 0; i < ReturnCount(); ++i) {
79 if (GetReturnLocation(i) != other->GetReturnLocation(i)) return false;
80 }
81 return true;
82}
83
84int CallDescriptor::GetFirstUnusedStackSlot() const {
85 int slots_above_sp = 0;
86 for (size_t i = 0; i < InputCount(); ++i) {
87 LinkageLocation operand = GetInputLocation(i);
88 if (!operand.IsRegister()) {
89 int new_candidate =
90 -operand.GetLocation() + operand.GetSizeInPointers() - 1;
91 if (new_candidate > slots_above_sp) {
92 slots_above_sp = new_candidate;
93 }
94 }
95 }
96 return slots_above_sp;
97}
98
99int CallDescriptor::GetStackParameterDelta(
100 CallDescriptor const* tail_caller) const {
101 int callee_slots_above_sp = GetFirstUnusedStackSlot();
102 int tail_caller_slots_above_sp = tail_caller->GetFirstUnusedStackSlot();
103 int stack_param_delta = callee_slots_above_sp - tail_caller_slots_above_sp;
104 if (kPadArguments) {
105 // Adjust stack delta when it is odd.
106 if (stack_param_delta % 2 != 0) {
107 if (callee_slots_above_sp % 2 != 0) {
108 // The delta is odd due to the callee - we will need to add one slot
109 // of padding.
110 ++stack_param_delta;
111 } else {
112 // The delta is odd because of the caller. We already have one slot of
113 // padding that we can reuse for arguments, so we will need one fewer
114 // slot.
115 --stack_param_delta;
116 }
117 }
118 }
119 return stack_param_delta;
120}
121
122int CallDescriptor::GetTaggedParameterSlots() const {
123 int result = 0;
124 for (size_t i = 0; i < InputCount(); ++i) {
125 LinkageLocation operand = GetInputLocation(i);
126 if (!operand.IsRegister() && operand.GetType().IsTagged()) {
127 ++result;
128 }
129 }
130 return result;
131}
132
133bool CallDescriptor::CanTailCall(const Node* node) const {
134 return HasSameReturnLocationsAs(CallDescriptorOf(node->op()));
135}
136
137int CallDescriptor::CalculateFixedFrameSize() const {
138 switch (kind_) {
139 case kCallJSFunction:
140 return PushArgumentCount()
141 ? OptimizedBuiltinFrameConstants::kFixedSlotCount
142 : StandardFrameConstants::kFixedSlotCount;
143 case kCallAddress:
144 return CommonFrameConstants::kFixedSlotCountAboveFp +
145 CommonFrameConstants::kCPSlotCount;
146 case kCallCodeObject:
147 case kCallBuiltinPointer:
148 return TypedFrameConstants::kFixedSlotCount;
149 case kCallWasmFunction:
150 case kCallWasmImportWrapper:
151 return WasmCompiledFrameConstants::kFixedSlotCount;
152 }
153 UNREACHABLE();
154}
155
156CallDescriptor* Linkage::ComputeIncoming(Zone* zone,
157 OptimizedCompilationInfo* info) {
158 DCHECK(!info->IsNotOptimizedFunctionOrWasmFunction());
159 if (!info->closure().is_null()) {
160 // If we are compiling a JS function, use a JS call descriptor,
161 // plus the receiver.
162 SharedFunctionInfo shared = info->closure()->shared();
163 return GetJSCallDescriptor(zone, info->is_osr(),
164 1 + shared->internal_formal_parameter_count(),
165 CallDescriptor::kCanUseRoots);
166 }
167 return nullptr; // TODO(titzer): ?
168}
169
170
171// static
172bool Linkage::NeedsFrameStateInput(Runtime::FunctionId function) {
173 switch (function) {
174 // Most runtime functions need a FrameState. A few chosen ones that we know
175 // not to call into arbitrary JavaScript, not to throw, and not to lazily
176 // deoptimize are whitelisted here and can be called without a FrameState.
177 case Runtime::kAbort:
178 case Runtime::kAllocateInOldGeneration:
179 case Runtime::kCreateIterResultObject:
180 case Runtime::kIncBlockCounter:
181 case Runtime::kIsFunction:
182 case Runtime::kNewClosure:
183 case Runtime::kNewClosure_Tenured:
184 case Runtime::kNewFunctionContext:
185 case Runtime::kPushBlockContext:
186 case Runtime::kPushCatchContext:
187 case Runtime::kReThrow:
188 case Runtime::kStringEqual:
189 case Runtime::kStringLessThan:
190 case Runtime::kStringLessThanOrEqual:
191 case Runtime::kStringGreaterThan:
192 case Runtime::kStringGreaterThanOrEqual:
193 case Runtime::kToFastProperties: // TODO(conradw): Is it safe?
194 case Runtime::kTraceEnter:
195 case Runtime::kTraceExit:
196 return false;
197
198 // Some inline intrinsics are also safe to call without a FrameState.
199 case Runtime::kInlineCreateIterResultObject:
200 case Runtime::kInlineGeneratorClose:
201 case Runtime::kInlineGeneratorGetResumeMode:
202 case Runtime::kInlineCreateJSGeneratorObject:
203 case Runtime::kInlineIsArray:
204 case Runtime::kInlineIsJSReceiver:
205 case Runtime::kInlineIsRegExp:
206 case Runtime::kInlineIsSmi:
207 case Runtime::kInlineIsTypedArray:
208 return false;
209
210 default:
211 break;
212 }
213
214 // For safety, default to needing a FrameState unless whitelisted.
215 return true;
216}
217
218
219bool CallDescriptor::UsesOnlyRegisters() const {
220 for (size_t i = 0; i < InputCount(); ++i) {
221 if (!GetInputLocation(i).IsRegister()) return false;
222 }
223 for (size_t i = 0; i < ReturnCount(); ++i) {
224 if (!GetReturnLocation(i).IsRegister()) return false;
225 }
226 return true;
227}
228
229
230CallDescriptor* Linkage::GetRuntimeCallDescriptor(
231 Zone* zone, Runtime::FunctionId function_id, int js_parameter_count,
232 Operator::Properties properties, CallDescriptor::Flags flags) {
233 const Runtime::Function* function = Runtime::FunctionForId(function_id);
234 const int return_count = function->result_size;
235 const char* debug_name = function->name;
236
237 if (!Linkage::NeedsFrameStateInput(function_id)) {
238 flags = static_cast<CallDescriptor::Flags>(
239 flags & ~CallDescriptor::kNeedsFrameState);
240 }
241
242 return GetCEntryStubCallDescriptor(zone, return_count, js_parameter_count,
243 debug_name, properties, flags);
244}
245
246CallDescriptor* Linkage::GetCEntryStubCallDescriptor(
247 Zone* zone, int return_count, int js_parameter_count,
248 const char* debug_name, Operator::Properties properties,
249 CallDescriptor::Flags flags) {
250 const size_t function_count = 1;
251 const size_t num_args_count = 1;
252 const size_t context_count = 1;
253 const size_t parameter_count = function_count +
254 static_cast<size_t>(js_parameter_count) +
255 num_args_count + context_count;
256
257 LocationSignature::Builder locations(zone, static_cast<size_t>(return_count),
258 static_cast<size_t>(parameter_count));
259
260 // Add returns.
261 if (locations.return_count_ > 0) {
262 locations.AddReturn(regloc(kReturnRegister0, MachineType::AnyTagged()));
263 }
264 if (locations.return_count_ > 1) {
265 locations.AddReturn(regloc(kReturnRegister1, MachineType::AnyTagged()));
266 }
267 if (locations.return_count_ > 2) {
268 locations.AddReturn(regloc(kReturnRegister2, MachineType::AnyTagged()));
269 }
270
271 // All parameters to the runtime call go on the stack.
272 for (int i = 0; i < js_parameter_count; i++) {
273 locations.AddParam(LinkageLocation::ForCallerFrameSlot(
274 i - js_parameter_count, MachineType::AnyTagged()));
275 }
276 // Add runtime function itself.
277 locations.AddParam(
278 regloc(kRuntimeCallFunctionRegister, MachineType::Pointer()));
279
280 // Add runtime call argument count.
281 locations.AddParam(
282 regloc(kRuntimeCallArgCountRegister, MachineType::Int32()));
283
284 // Add context.
285 locations.AddParam(regloc(kContextRegister, MachineType::AnyTagged()));
286
287 // The target for runtime calls is a code object.
288 MachineType target_type = MachineType::AnyTagged();
289 LinkageLocation target_loc =
290 LinkageLocation::ForAnyRegister(MachineType::AnyTagged());
291 return new (zone) CallDescriptor( // --
292 CallDescriptor::kCallCodeObject, // kind
293 target_type, // target MachineType
294 target_loc, // target location
295 locations.Build(), // location_sig
296 js_parameter_count, // stack_parameter_count
297 properties, // properties
298 kNoCalleeSaved, // callee-saved
299 kNoCalleeSaved, // callee-saved fp
300 flags, // flags
301 debug_name); // debug name
302}
303
304CallDescriptor* Linkage::GetJSCallDescriptor(Zone* zone, bool is_osr,
305 int js_parameter_count,
306 CallDescriptor::Flags flags) {
307 const size_t return_count = 1;
308 const size_t context_count = 1;
309 const size_t new_target_count = 1;
310 const size_t num_args_count = 1;
311 const size_t parameter_count =
312 js_parameter_count + new_target_count + num_args_count + context_count;
313
314 LocationSignature::Builder locations(zone, return_count, parameter_count);
315
316 // All JS calls have exactly one return value.
317 locations.AddReturn(regloc(kReturnRegister0, MachineType::AnyTagged()));
318
319 // All parameters to JS calls go on the stack.
320 for (int i = 0; i < js_parameter_count; i++) {
321 int spill_slot_index = i - js_parameter_count;
322 locations.AddParam(LinkageLocation::ForCallerFrameSlot(
323 spill_slot_index, MachineType::AnyTagged()));
324 }
325
326 // Add JavaScript call new target value.
327 locations.AddParam(
328 regloc(kJavaScriptCallNewTargetRegister, MachineType::AnyTagged()));
329
330 // Add JavaScript call argument count.
331 locations.AddParam(
332 regloc(kJavaScriptCallArgCountRegister, MachineType::Int32()));
333
334 // Add context.
335 locations.AddParam(regloc(kContextRegister, MachineType::AnyTagged()));
336
337 // The target for JS function calls is the JSFunction object.
338 MachineType target_type = MachineType::AnyTagged();
339 // When entering into an OSR function from unoptimized code the JSFunction
340 // is not in a register, but it is on the stack in the marker spill slot.
341 LinkageLocation target_loc =
342 is_osr ? LinkageLocation::ForSavedCallerFunction()
343 : regloc(kJSFunctionRegister, MachineType::AnyTagged());
344 return new (zone) CallDescriptor( // --
345 CallDescriptor::kCallJSFunction, // kind
346 target_type, // target MachineType
347 target_loc, // target location
348 locations.Build(), // location_sig
349 js_parameter_count, // stack_parameter_count
350 Operator::kNoProperties, // properties
351 kNoCalleeSaved, // callee-saved
352 kNoCalleeSaved, // callee-saved fp
353 flags, // flags
354 "js-call");
355}
356
357// TODO(turbofan): cache call descriptors for code stub calls.
358// TODO(jgruber): Clean up stack parameter count handling. The descriptor
359// already knows the formal stack parameter count and ideally only additional
360// stack parameters should be passed into this method. All call-sites should
361// be audited for correctness (e.g. many used to assume a stack parameter count
362// of 0).
363CallDescriptor* Linkage::GetStubCallDescriptor(
364 Zone* zone, const CallInterfaceDescriptor& descriptor,
365 int stack_parameter_count, CallDescriptor::Flags flags,
366 Operator::Properties properties, StubCallMode stub_mode) {
367 const int register_parameter_count = descriptor.GetRegisterParameterCount();
368 const int js_parameter_count =
369 register_parameter_count + stack_parameter_count;
370 const int context_count = descriptor.HasContextParameter() ? 1 : 0;
371 const size_t parameter_count =
372 static_cast<size_t>(js_parameter_count + context_count);
373
374 DCHECK_GE(stack_parameter_count, descriptor.GetStackParameterCount());
375
376 size_t return_count = descriptor.GetReturnCount();
377 LocationSignature::Builder locations(zone, return_count, parameter_count);
378
379 // Add returns.
380 if (locations.return_count_ > 0) {
381 locations.AddReturn(regloc(kReturnRegister0, descriptor.GetReturnType(0)));
382 }
383 if (locations.return_count_ > 1) {
384 locations.AddReturn(regloc(kReturnRegister1, descriptor.GetReturnType(1)));
385 }
386 if (locations.return_count_ > 2) {
387 locations.AddReturn(regloc(kReturnRegister2, descriptor.GetReturnType(2)));
388 }
389
390 // Add parameters in registers and on the stack.
391 for (int i = 0; i < js_parameter_count; i++) {
392 if (i < register_parameter_count) {
393 // The first parameters go in registers.
394 Register reg = descriptor.GetRegisterParameter(i);
395 MachineType type = descriptor.GetParameterType(i);
396 locations.AddParam(regloc(reg, type));
397 } else {
398 // The rest of the parameters go on the stack.
399 int stack_slot = i - register_parameter_count - stack_parameter_count;
400 locations.AddParam(LinkageLocation::ForCallerFrameSlot(
401 stack_slot, MachineType::AnyTagged()));
402 }
403 }
404 // Add context.
405 if (context_count) {
406 locations.AddParam(regloc(kContextRegister, MachineType::AnyTagged()));
407 }
408
409 // The target for stub calls depends on the requested mode.
410 CallDescriptor::Kind kind;
411 MachineType target_type;
412 switch (stub_mode) {
413 case StubCallMode::kCallCodeObject:
414 kind = CallDescriptor::kCallCodeObject;
415 target_type = MachineType::AnyTagged();
416 break;
417 case StubCallMode::kCallWasmRuntimeStub:
418 kind = CallDescriptor::kCallWasmFunction;
419 target_type = MachineType::Pointer();
420 break;
421 case StubCallMode::kCallBuiltinPointer:
422 kind = CallDescriptor::kCallBuiltinPointer;
423 target_type = MachineType::AnyTagged();
424 break;
425 }
426
427 LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type);
428 return new (zone) CallDescriptor( // --
429 kind, // kind
430 target_type, // target MachineType
431 target_loc, // target location
432 locations.Build(), // location_sig
433 stack_parameter_count, // stack_parameter_count
434 properties, // properties
435 kNoCalleeSaved, // callee-saved registers
436 kNoCalleeSaved, // callee-saved fp
437 CallDescriptor::kCanUseRoots | flags, // flags
438 descriptor.DebugName(), // debug name
439 descriptor.allocatable_registers());
440}
441
442// static
443CallDescriptor* Linkage::GetBytecodeDispatchCallDescriptor(
444 Zone* zone, const CallInterfaceDescriptor& descriptor,
445 int stack_parameter_count) {
446 const int register_parameter_count = descriptor.GetRegisterParameterCount();
447 const int parameter_count = register_parameter_count + stack_parameter_count;
448
449 DCHECK_EQ(descriptor.GetReturnCount(), 1);
450 LocationSignature::Builder locations(zone, 1, parameter_count);
451
452 locations.AddReturn(regloc(kReturnRegister0, descriptor.GetReturnType(0)));
453
454 // Add parameters in registers and on the stack.
455 for (int i = 0; i < parameter_count; i++) {
456 if (i < register_parameter_count) {
457 // The first parameters go in registers.
458 Register reg = descriptor.GetRegisterParameter(i);
459 MachineType type = descriptor.GetParameterType(i);
460 locations.AddParam(regloc(reg, type));
461 } else {
462 // The rest of the parameters go on the stack.
463 int stack_slot = i - register_parameter_count - stack_parameter_count;
464 locations.AddParam(LinkageLocation::ForCallerFrameSlot(
465 stack_slot, MachineType::AnyTagged()));
466 }
467 }
468
469 // The target for interpreter dispatches is a code entry address.
470 MachineType target_type = MachineType::Pointer();
471 LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type);
472 const CallDescriptor::Flags kFlags =
473 CallDescriptor::kCanUseRoots | CallDescriptor::kFixedTargetRegister;
474 return new (zone) CallDescriptor( // --
475 CallDescriptor::kCallAddress, // kind
476 target_type, // target MachineType
477 target_loc, // target location
478 locations.Build(), // location_sig
479 stack_parameter_count, // stack_parameter_count
480 Operator::kNoProperties, // properties
481 kNoCalleeSaved, // callee-saved registers
482 kNoCalleeSaved, // callee-saved fp
483 kFlags, // flags
484 descriptor.DebugName());
485}
486
487LinkageLocation Linkage::GetOsrValueLocation(int index) const {
488 CHECK(incoming_->IsJSFunctionCall());
489 int parameter_count = static_cast<int>(incoming_->JSParameterCount() - 1);
490 int first_stack_slot = OsrHelper::FirstStackSlotIndex(parameter_count);
491
492 if (index == kOsrContextSpillSlotIndex) {
493 // Context. Use the parameter location of the context spill slot.
494 // Parameter (arity + 2) is special for the context of the function frame.
495 // >> context_index = target + receiver + params + new_target + #args
496 int context_index = 1 + 1 + parameter_count + 1 + 1;
497 return incoming_->GetInputLocation(context_index);
498 } else if (index >= first_stack_slot) {
499 // Local variable stored in this (callee) stack.
500 int spill_index =
501 index - first_stack_slot + StandardFrameConstants::kFixedSlotCount;
502 return LinkageLocation::ForCalleeFrameSlot(spill_index,
503 MachineType::AnyTagged());
504 } else {
505 // Parameter. Use the assigned location from the incoming call descriptor.
506 int parameter_index = 1 + index; // skip index 0, which is the target.
507 return incoming_->GetInputLocation(parameter_index);
508 }
509}
510
511namespace {
512inline bool IsTaggedReg(const LinkageLocation& loc, Register reg) {
513 return loc.IsRegister() && loc.AsRegister() == reg.code() &&
514 loc.GetType().representation() ==
515 MachineRepresentation::kTaggedPointer;
516}
517} // namespace
518
519bool Linkage::ParameterHasSecondaryLocation(int index) const {
520 // TODO(titzer): this should be configurable, not call-type specific.
521 if (incoming_->IsJSFunctionCall()) {
522 LinkageLocation loc = GetParameterLocation(index);
523 return IsTaggedReg(loc, kJSFunctionRegister) ||
524 IsTaggedReg(loc, kContextRegister);
525 }
526 if (incoming_->IsWasmFunctionCall()) {
527 LinkageLocation loc = GetParameterLocation(index);
528 return IsTaggedReg(loc, kWasmInstanceRegister);
529 }
530 return false;
531}
532
533LinkageLocation Linkage::GetParameterSecondaryLocation(int index) const {
534 // TODO(titzer): these constants are necessary due to offset/slot# mismatch
535 static const int kJSContextSlot = 2 + StandardFrameConstants::kCPSlotCount;
536 static const int kJSFunctionSlot = 3 + StandardFrameConstants::kCPSlotCount;
537 static const int kWasmInstanceSlot = 3 + StandardFrameConstants::kCPSlotCount;
538
539 DCHECK(ParameterHasSecondaryLocation(index));
540 LinkageLocation loc = GetParameterLocation(index);
541
542 // TODO(titzer): this should be configurable, not call-type specific.
543 if (incoming_->IsJSFunctionCall()) {
544 if (IsTaggedReg(loc, kJSFunctionRegister)) {
545 return LinkageLocation::ForCalleeFrameSlot(kJSFunctionSlot,
546 MachineType::AnyTagged());
547 } else {
548 DCHECK(IsTaggedReg(loc, kContextRegister));
549 return LinkageLocation::ForCalleeFrameSlot(kJSContextSlot,
550 MachineType::AnyTagged());
551 }
552 }
553 if (incoming_->IsWasmFunctionCall()) {
554 DCHECK(IsTaggedReg(loc, kWasmInstanceRegister));
555 return LinkageLocation::ForCalleeFrameSlot(kWasmInstanceSlot,
556 MachineType::AnyTagged());
557 }
558 UNREACHABLE();
559 return LinkageLocation::ForCalleeFrameSlot(0, MachineType::AnyTagged());
560}
561
562
563} // namespace compiler
564} // namespace internal
565} // namespace v8
566