V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
code-stubs-x64.cc
1 // Copyright 2013 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 #if V8_TARGET_ARCH_X64
6 
7 #include "src/api-arguments-inl.h"
8 #include "src/bootstrapper.h"
9 #include "src/code-stubs.h"
10 #include "src/counters.h"
11 #include "src/double.h"
12 #include "src/frame-constants.h"
13 #include "src/frames.h"
14 #include "src/ic/ic.h"
15 #include "src/ic/stub-cache.h"
16 #include "src/isolate.h"
17 #include "src/macro-assembler.h"
18 #include "src/objects-inl.h"
19 #include "src/objects/api-callbacks.h"
20 #include "src/objects/regexp-match-info.h"
21 #include "src/regexp/jsregexp.h"
22 #include "src/regexp/regexp-macro-assembler.h"
23 #include "src/runtime/runtime.h"
24 
25 namespace v8 {
26 namespace internal {
27 
28 #define __ ACCESS_MASM(masm)
29 
30 void JSEntryStub::Generate(MacroAssembler* masm) {
31  Label invoke, handler_entry, exit;
32  Label not_outermost_js, not_outermost_js_2;
33 
34  { // NOLINT. Scope block confuses linter.
35  NoRootArrayScope uninitialized_root_register(masm);
36  // Set up frame.
37  __ pushq(rbp);
38  __ movp(rbp, rsp);
39 
40  // Push the stack frame type.
41  __ Push(Immediate(StackFrame::TypeToMarker(type()))); // context slot
42  ExternalReference context_address =
43  ExternalReference::Create(IsolateAddressId::kContextAddress, isolate());
44  __ Load(kScratchRegister, context_address);
45  __ Push(kScratchRegister); // context
46  // Save callee-saved registers (X64/X32/Win64 calling conventions).
47  __ pushq(r12);
48  __ pushq(r13);
49  __ pushq(r14);
50  __ pushq(r15);
51 #ifdef _WIN64
52  __ pushq(rdi); // Only callee save in Win64 ABI, argument in AMD64 ABI.
53  __ pushq(rsi); // Only callee save in Win64 ABI, argument in AMD64 ABI.
54 #endif
55  __ pushq(rbx);
56 
57 #ifdef _WIN64
58  // On Win64 XMM6-XMM15 are callee-save
59  __ subp(rsp, Immediate(EntryFrameConstants::kXMMRegistersBlockSize));
60  __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 0), xmm6);
61  __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 1), xmm7);
62  __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 2), xmm8);
63  __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 3), xmm9);
64  __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 4), xmm10);
65  __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 5), xmm11);
66  __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 6), xmm12);
67  __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 7), xmm13);
68  __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 8), xmm14);
69  __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 9), xmm15);
70 #endif
71 
72  __ InitializeRootRegister();
73  }
74 
75  // Save copies of the top frame descriptor on the stack.
76  ExternalReference c_entry_fp =
77  ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate());
78  {
79  Operand c_entry_fp_operand = masm->ExternalReferenceAsOperand(c_entry_fp);
80  __ Push(c_entry_fp_operand);
81  }
82 
83  // If this is the outermost JS call, set js_entry_sp value.
84  ExternalReference js_entry_sp =
85  ExternalReference::Create(IsolateAddressId::kJSEntrySPAddress, isolate());
86  __ Load(rax, js_entry_sp);
87  __ testp(rax, rax);
88  __ j(not_zero, &not_outermost_js);
89  __ Push(Immediate(StackFrame::OUTERMOST_JSENTRY_FRAME));
90  __ movp(rax, rbp);
91  __ Store(js_entry_sp, rax);
92  Label cont;
93  __ jmp(&cont);
94  __ bind(&not_outermost_js);
95  __ Push(Immediate(StackFrame::INNER_JSENTRY_FRAME));
96  __ bind(&cont);
97 
98  // Jump to a faked try block that does the invoke, with a faked catch
99  // block that sets the pending exception.
100  __ jmp(&invoke);
101  __ bind(&handler_entry);
102  handler_offset_ = handler_entry.pos();
103  // Caught exception: Store result (exception) in the pending exception
104  // field in the JSEnv and return a failure sentinel.
105  ExternalReference pending_exception = ExternalReference::Create(
106  IsolateAddressId::kPendingExceptionAddress, isolate());
107  __ Store(pending_exception, rax);
108  __ LoadRoot(rax, RootIndex::kException);
109  __ jmp(&exit);
110 
111  // Invoke: Link this frame into the handler chain.
112  __ bind(&invoke);
113  __ PushStackHandler();
114 
115  // Invoke the function by calling through JS entry trampoline builtin and
116  // pop the faked function when we return. We load the address from an
117  // external reference instead of inlining the call target address directly
118  // in the code, because the builtin stubs may not have been generated yet
119  // at the time this code is generated.
120  __ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
121 
122  // Unlink this frame from the handler chain.
123  __ PopStackHandler();
124 
125  __ bind(&exit);
126  // Check if the current stack frame is marked as the outermost JS frame.
127  __ Pop(rbx);
128  __ cmpp(rbx, Immediate(StackFrame::OUTERMOST_JSENTRY_FRAME));
129  __ j(not_equal, &not_outermost_js_2);
130  __ Move(kScratchRegister, js_entry_sp);
131  __ movp(Operand(kScratchRegister, 0), Immediate(0));
132  __ bind(&not_outermost_js_2);
133 
134  // Restore the top frame descriptor from the stack.
135  {
136  Operand c_entry_fp_operand = masm->ExternalReferenceAsOperand(c_entry_fp);
137  __ Pop(c_entry_fp_operand);
138  }
139 
140  // Restore callee-saved registers (X64 conventions).
141 #ifdef _WIN64
142  // On Win64 XMM6-XMM15 are callee-save
143  __ movdqu(xmm6, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 0));
144  __ movdqu(xmm7, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 1));
145  __ movdqu(xmm8, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 2));
146  __ movdqu(xmm9, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 3));
147  __ movdqu(xmm10, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 4));
148  __ movdqu(xmm11, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 5));
149  __ movdqu(xmm12, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 6));
150  __ movdqu(xmm13, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 7));
151  __ movdqu(xmm14, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 8));
152  __ movdqu(xmm15, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 9));
153  __ addp(rsp, Immediate(EntryFrameConstants::kXMMRegistersBlockSize));
154 #endif
155 
156  __ popq(rbx);
157 #ifdef _WIN64
158  // Callee save on in Win64 ABI, arguments/volatile in AMD64 ABI.
159  __ popq(rsi);
160  __ popq(rdi);
161 #endif
162  __ popq(r15);
163  __ popq(r14);
164  __ popq(r13);
165  __ popq(r12);
166  __ addp(rsp, Immediate(2 * kPointerSize)); // remove markers
167 
168  // Restore frame pointer and return.
169  __ popq(rbp);
170  __ ret(0);
171 }
172 
173 static int Offset(ExternalReference ref0, ExternalReference ref1) {
174  int64_t offset = (ref0.address() - ref1.address());
175  // Check that fits into int.
176  DCHECK(static_cast<int>(offset) == offset);
177  return static_cast<int>(offset);
178 }
179 
180 // Prepares stack to put arguments (aligns and so on). WIN64 calling convention
181 // requires to put the pointer to the return value slot into rcx (rcx must be
182 // preserverd until CallApiFunctionAndReturn). Clobbers rax. Allocates
183 // arg_stack_space * kPointerSize inside the exit frame (not GCed) accessible
184 // via StackSpaceOperand.
185 static void PrepareCallApiFunction(MacroAssembler* masm, int arg_stack_space) {
186  __ EnterApiExitFrame(arg_stack_space);
187 }
188 
189 
190 // Calls an API function. Allocates HandleScope, extracts returned value
191 // from handle and propagates exceptions. Clobbers r14, r15, rbx and
192 // caller-save registers. Restores context. On return removes
193 // stack_space * kPointerSize (GCed).
194 static void CallApiFunctionAndReturn(MacroAssembler* masm,
195  Register function_address,
196  ExternalReference thunk_ref,
197  Register thunk_last_arg, int stack_space,
198  Operand* stack_space_operand,
199  Operand return_value_operand) {
200  Label prologue;
201  Label promote_scheduled_exception;
202  Label delete_allocated_handles;
203  Label leave_exit_frame;
204  Label write_back;
205 
206  Isolate* isolate = masm->isolate();
207  Factory* factory = isolate->factory();
208  ExternalReference next_address =
209  ExternalReference::handle_scope_next_address(isolate);
210  const int kNextOffset = 0;
211  const int kLimitOffset = Offset(
212  ExternalReference::handle_scope_limit_address(isolate), next_address);
213  const int kLevelOffset = Offset(
214  ExternalReference::handle_scope_level_address(isolate), next_address);
215  ExternalReference scheduled_exception_address =
216  ExternalReference::scheduled_exception_address(isolate);
217 
218  DCHECK(rdx == function_address || r8 == function_address);
219  // Allocate HandleScope in callee-save registers.
220  Register prev_next_address_reg = r14;
221  Register prev_limit_reg = rbx;
222  Register base_reg = r15;
223  __ Move(base_reg, next_address);
224  __ movp(prev_next_address_reg, Operand(base_reg, kNextOffset));
225  __ movp(prev_limit_reg, Operand(base_reg, kLimitOffset));
226  __ addl(Operand(base_reg, kLevelOffset), Immediate(1));
227 
228  if (FLAG_log_timer_events) {
229  FrameScope frame(masm, StackFrame::MANUAL);
230  __ PushSafepointRegisters();
231  __ PrepareCallCFunction(1);
232  __ LoadAddress(arg_reg_1, ExternalReference::isolate_address(isolate));
233  __ CallCFunction(ExternalReference::log_enter_external_function(), 1);
234  __ PopSafepointRegisters();
235  }
236 
237  Label profiler_disabled;
238  Label end_profiler_check;
239  __ Move(rax, ExternalReference::is_profiling_address(isolate));
240  __ cmpb(Operand(rax, 0), Immediate(0));
241  __ j(zero, &profiler_disabled);
242 
243  // Third parameter is the address of the actual getter function.
244  __ Move(thunk_last_arg, function_address);
245  __ Move(rax, thunk_ref);
246  __ jmp(&end_profiler_check);
247 
248  __ bind(&profiler_disabled);
249  // Call the api function!
250  __ Move(rax, function_address);
251 
252  __ bind(&end_profiler_check);
253 
254  // Call the api function!
255  __ call(rax);
256 
257  if (FLAG_log_timer_events) {
258  FrameScope frame(masm, StackFrame::MANUAL);
259  __ PushSafepointRegisters();
260  __ PrepareCallCFunction(1);
261  __ LoadAddress(arg_reg_1, ExternalReference::isolate_address(isolate));
262  __ CallCFunction(ExternalReference::log_leave_external_function(), 1);
263  __ PopSafepointRegisters();
264  }
265 
266  // Load the value from ReturnValue
267  __ movp(rax, return_value_operand);
268  __ bind(&prologue);
269 
270  // No more valid handles (the result handle was the last one). Restore
271  // previous handle scope.
272  __ subl(Operand(base_reg, kLevelOffset), Immediate(1));
273  __ movp(Operand(base_reg, kNextOffset), prev_next_address_reg);
274  __ cmpp(prev_limit_reg, Operand(base_reg, kLimitOffset));
275  __ j(not_equal, &delete_allocated_handles);
276 
277  // Leave the API exit frame.
278  __ bind(&leave_exit_frame);
279  if (stack_space_operand != nullptr) {
280  __ movp(rbx, *stack_space_operand);
281  }
282  __ LeaveApiExitFrame();
283 
284  // Check if the function scheduled an exception.
285  __ Move(rdi, scheduled_exception_address);
286  __ Cmp(Operand(rdi, 0), factory->the_hole_value());
287  __ j(not_equal, &promote_scheduled_exception);
288 
289 #if DEBUG
290  // Check if the function returned a valid JavaScript value.
291  Label ok;
292  Register return_value = rax;
293  Register map = rcx;
294 
295  __ JumpIfSmi(return_value, &ok, Label::kNear);
296  __ movp(map, FieldOperand(return_value, HeapObject::kMapOffset));
297 
298  __ CmpInstanceType(map, LAST_NAME_TYPE);
299  __ j(below_equal, &ok, Label::kNear);
300 
301  __ CmpInstanceType(map, FIRST_JS_RECEIVER_TYPE);
302  __ j(above_equal, &ok, Label::kNear);
303 
304  __ CompareRoot(map, RootIndex::kHeapNumberMap);
305  __ j(equal, &ok, Label::kNear);
306 
307  __ CompareRoot(return_value, RootIndex::kUndefinedValue);
308  __ j(equal, &ok, Label::kNear);
309 
310  __ CompareRoot(return_value, RootIndex::kTrueValue);
311  __ j(equal, &ok, Label::kNear);
312 
313  __ CompareRoot(return_value, RootIndex::kFalseValue);
314  __ j(equal, &ok, Label::kNear);
315 
316  __ CompareRoot(return_value, RootIndex::kNullValue);
317  __ j(equal, &ok, Label::kNear);
318 
319  __ Abort(AbortReason::kAPICallReturnedInvalidObject);
320 
321  __ bind(&ok);
322 #endif
323 
324  if (stack_space_operand != nullptr) {
325  DCHECK_EQ(stack_space, 0);
326  __ PopReturnAddressTo(rcx);
327  __ addq(rsp, rbx);
328  __ jmp(rcx);
329  } else {
330  __ ret(stack_space * kPointerSize);
331  }
332 
333  // Re-throw by promoting a scheduled exception.
334  __ bind(&promote_scheduled_exception);
335  __ TailCallRuntime(Runtime::kPromoteScheduledException);
336 
337  // HandleScope limit has changed. Delete allocated extensions.
338  __ bind(&delete_allocated_handles);
339  __ movp(Operand(base_reg, kLimitOffset), prev_limit_reg);
340  __ movp(prev_limit_reg, rax);
341  __ LoadAddress(arg_reg_1, ExternalReference::isolate_address(isolate));
342  __ LoadAddress(rax, ExternalReference::delete_handle_scope_extensions());
343  __ call(rax);
344  __ movp(rax, prev_limit_reg);
345  __ jmp(&leave_exit_frame);
346 }
347 
348 void CallApiCallbackStub::Generate(MacroAssembler* masm) {
349  // ----------- S t a t e -------------
350  // -- rbx : call_data
351  // -- rcx : holder
352  // -- rdx : api_function_address
353  // -- rsi : context
354  // -- rax : number of arguments if argc is a register
355  // -- rsp[0] : return address
356  // -- rsp[8] : last argument
357  // -- ...
358  // -- rsp[argc * 8] : first argument
359  // -- rsp[(argc + 1) * 8] : receiver
360  // -----------------------------------
361 
362  Register call_data = rbx;
363  Register holder = rcx;
364  Register api_function_address = rdx;
365  Register return_address = r8;
366 
367  typedef FunctionCallbackArguments FCA;
368 
369  STATIC_ASSERT(FCA::kArgsLength == 6);
370  STATIC_ASSERT(FCA::kNewTargetIndex == 5);
371  STATIC_ASSERT(FCA::kDataIndex == 4);
372  STATIC_ASSERT(FCA::kReturnValueOffset == 3);
373  STATIC_ASSERT(FCA::kReturnValueDefaultValueIndex == 2);
374  STATIC_ASSERT(FCA::kIsolateIndex == 1);
375  STATIC_ASSERT(FCA::kHolderIndex == 0);
376 
377  __ PopReturnAddressTo(return_address);
378 
379  // new target
380  __ PushRoot(RootIndex::kUndefinedValue);
381 
382  // call data
383  __ Push(call_data);
384 
385  // return value
386  __ PushRoot(RootIndex::kUndefinedValue);
387  // return value default
388  __ PushRoot(RootIndex::kUndefinedValue);
389  // isolate
390  Register scratch = call_data;
391  __ Move(scratch, ExternalReference::isolate_address(masm->isolate()));
392  __ Push(scratch);
393  // holder
394  __ Push(holder);
395 
396  int argc = this->argc();
397 
398  __ movp(scratch, rsp);
399  // Push return address back on stack.
400  __ PushReturnAddressFrom(return_address);
401 
402  // Allocate the v8::Arguments structure in the arguments' space since
403  // it's not controlled by GC.
404  const int kApiStackSpace = 3;
405 
406  PrepareCallApiFunction(masm, kApiStackSpace);
407 
408  // FunctionCallbackInfo::implicit_args_.
409  __ movp(StackSpaceOperand(0), scratch);
410  __ addp(scratch, Immediate((argc + FCA::kArgsLength - 1) * kPointerSize));
411  // FunctionCallbackInfo::values_.
412  __ movp(StackSpaceOperand(1), scratch);
413  // FunctionCallbackInfo::length_.
414  __ Set(StackSpaceOperand(2), argc);
415 
416 #if defined(__MINGW64__) || defined(_WIN64)
417  Register arguments_arg = rcx;
418  Register callback_arg = rdx;
419 #else
420  Register arguments_arg = rdi;
421  Register callback_arg = rsi;
422 #endif
423 
424  // It's okay if api_function_address == callback_arg
425  // but not arguments_arg
426  DCHECK(api_function_address != arguments_arg);
427 
428  // v8::InvocationCallback's argument.
429  __ leap(arguments_arg, StackSpaceOperand(0));
430 
431  ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
432 
433  // Accessor for FunctionCallbackInfo and first js arg.
434  StackArgumentsAccessor args_from_rbp(rbp, FCA::kArgsLength + 1,
435  ARGUMENTS_DONT_CONTAIN_RECEIVER);
436  Operand return_value_operand = args_from_rbp.GetArgumentOperand(
437  FCA::kArgsLength - FCA::kReturnValueOffset);
438  const int stack_space = argc + FCA::kArgsLength + 1;
439  Operand* stack_space_operand = nullptr;
440  CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, callback_arg,
441  stack_space, stack_space_operand,
442  return_value_operand);
443 }
444 
445 
446 void CallApiGetterStub::Generate(MacroAssembler* masm) {
447 #if defined(__MINGW64__) || defined(_WIN64)
448  Register getter_arg = r8;
449  Register accessor_info_arg = rdx;
450  Register name_arg = rcx;
451 #else
452  Register getter_arg = rdx;
453  Register accessor_info_arg = rsi;
454  Register name_arg = rdi;
455 #endif
456  Register api_function_address = r8;
457  Register receiver = ApiGetterDescriptor::ReceiverRegister();
458  Register holder = ApiGetterDescriptor::HolderRegister();
459  Register callback = ApiGetterDescriptor::CallbackRegister();
460  Register scratch = rax;
461  DCHECK(!AreAliased(receiver, holder, callback, scratch));
462 
463  // Build v8::PropertyCallbackInfo::args_ array on the stack and push property
464  // name below the exit frame to make GC aware of them.
465  STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0);
466  STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1);
467  STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2);
468  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3);
469  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4);
470  STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5);
471  STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6);
472  STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7);
473 
474  // Insert additional parameters into the stack frame above return address.
475  __ PopReturnAddressTo(scratch);
476  __ Push(receiver);
477  __ Push(FieldOperand(callback, AccessorInfo::kDataOffset));
478  __ LoadRoot(kScratchRegister, RootIndex::kUndefinedValue);
479  __ Push(kScratchRegister); // return value
480  __ Push(kScratchRegister); // return value default
481  __ PushAddress(ExternalReference::isolate_address(isolate()));
482  __ Push(holder);
483  __ Push(Smi::zero()); // should_throw_on_error -> false
484  __ Push(FieldOperand(callback, AccessorInfo::kNameOffset));
485  __ PushReturnAddressFrom(scratch);
486 
487  // v8::PropertyCallbackInfo::args_ array and name handle.
488  const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1;
489 
490  // Allocate v8::PropertyCallbackInfo in non-GCed stack space.
491  const int kArgStackSpace = 1;
492 
493  // Load address of v8::PropertyAccessorInfo::args_ array.
494  __ leap(scratch, Operand(rsp, 2 * kPointerSize));
495 
496  PrepareCallApiFunction(masm, kArgStackSpace);
497  // Create v8::PropertyCallbackInfo object on the stack and initialize
498  // it's args_ field.
499  Operand info_object = StackSpaceOperand(0);
500  __ movp(info_object, scratch);
501 
502  __ leap(name_arg, Operand(scratch, -kPointerSize));
503  // The context register (rsi) has been saved in PrepareCallApiFunction and
504  // could be used to pass arguments.
505  __ leap(accessor_info_arg, info_object);
506 
507  ExternalReference thunk_ref =
508  ExternalReference::invoke_accessor_getter_callback();
509 
510  // It's okay if api_function_address == getter_arg
511  // but not accessor_info_arg or name_arg
512  DCHECK(api_function_address != accessor_info_arg);
513  DCHECK(api_function_address != name_arg);
514  __ movp(scratch, FieldOperand(callback, AccessorInfo::kJsGetterOffset));
515  __ movp(api_function_address,
516  FieldOperand(scratch, Foreign::kForeignAddressOffset));
517 
518  // +3 is to skip prolog, return address and name handle.
519  Operand return_value_operand(
520  rbp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
521  CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, getter_arg,
522  kStackUnwindSpace, nullptr, return_value_operand);
523 }
524 
525 #undef __
526 
527 } // namespace internal
528 } // namespace v8
529 
530 #endif // V8_TARGET_ARCH_X64
Definition: libplatform.h:13