V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
code-stubs-ppc.cc
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 #if V8_TARGET_ARCH_PPC
6 
7 #include "src/api-arguments-inl.h"
8 #include "src/assembler-inl.h"
9 #include "src/base/bits.h"
10 #include "src/bootstrapper.h"
11 #include "src/code-stubs.h"
12 #include "src/double.h"
13 #include "src/frame-constants.h"
14 #include "src/frames.h"
15 #include "src/ic/ic.h"
16 #include "src/ic/stub-cache.h"
17 #include "src/isolate.h"
18 #include "src/macro-assembler.h"
19 #include "src/objects/api-callbacks.h"
20 #include "src/regexp/jsregexp.h"
21 #include "src/regexp/regexp-macro-assembler.h"
22 #include "src/runtime/runtime.h"
23 
24 #include "src/ppc/code-stubs-ppc.h" // Cannot be the first include.
25 
26 namespace v8 {
27 namespace internal {
28 
29 #define __ ACCESS_MASM(masm)
30 
31 void JSEntryStub::Generate(MacroAssembler* masm) {
32  // r3: code entry
33  // r4: function
34  // r5: receiver
35  // r6: argc
36  // [sp+0]: argv
37 
38  Label invoke, handler_entry, exit;
39 
40 // Called from C
41  __ function_descriptor();
42 
43  {
44  NoRootArrayScope no_root_array(masm);
45 
46  // PPC LINUX ABI:
47  // preserve LR in pre-reserved slot in caller's frame
48  __ mflr(r0);
49  __ StoreP(r0, MemOperand(sp, kStackFrameLRSlot * kPointerSize));
50 
51  // Save callee saved registers on the stack.
52  __ MultiPush(kCalleeSaved);
53 
54  // Save callee-saved double registers.
55  __ MultiPushDoubles(kCalleeSavedDoubles);
56  // Set up the reserved register for 0.0.
57  __ LoadDoubleLiteral(kDoubleRegZero, Double(0.0), r0);
58 
59  __ InitializeRootRegister();
60  }
61 
62  // Push a frame with special values setup to mark it as an entry frame.
63  // r3: code entry
64  // r4: function
65  // r5: receiver
66  // r6: argc
67  // r7: argv
68  __ li(r0, Operand(-1)); // Push a bad frame pointer to fail if it is used.
69  __ push(r0);
70  if (FLAG_enable_embedded_constant_pool) {
71  __ li(kConstantPoolRegister, Operand::Zero());
72  __ push(kConstantPoolRegister);
73  }
74  StackFrame::Type marker = type();
75  __ mov(r0, Operand(StackFrame::TypeToMarker(marker)));
76  __ push(r0);
77  __ push(r0);
78  // Save copies of the top frame descriptor on the stack.
79  __ mov(r8, Operand(ExternalReference::Create(
80  IsolateAddressId::kCEntryFPAddress, isolate())));
81  __ LoadP(r0, MemOperand(r8));
82  __ push(r0);
83 
84  // Set up frame pointer for the frame to be pushed.
85  __ addi(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
86 
87  // If this is the outermost JS call, set js_entry_sp value.
88  Label non_outermost_js;
89  ExternalReference js_entry_sp =
90  ExternalReference::Create(IsolateAddressId::kJSEntrySPAddress, isolate());
91  __ mov(r8, Operand(js_entry_sp));
92  __ LoadP(r9, MemOperand(r8));
93  __ cmpi(r9, Operand::Zero());
94  __ bne(&non_outermost_js);
95  __ StoreP(fp, MemOperand(r8));
96  __ mov(ip, Operand(StackFrame::OUTERMOST_JSENTRY_FRAME));
97  Label cont;
98  __ b(&cont);
99  __ bind(&non_outermost_js);
100  __ mov(ip, Operand(StackFrame::INNER_JSENTRY_FRAME));
101  __ bind(&cont);
102  __ push(ip); // frame-type
103 
104  // Jump to a faked try block that does the invoke, with a faked catch
105  // block that sets the pending exception.
106  __ b(&invoke);
107 
108  __ bind(&handler_entry);
109  handler_offset_ = handler_entry.pos();
110  // Caught exception: Store result (exception) in the pending exception
111  // field in the JSEnv and return a failure sentinel. Coming in here the
112  // fp will be invalid because the PushStackHandler below sets it to 0 to
113  // signal the existence of the JSEntry frame.
114  __ mov(ip, Operand(ExternalReference::Create(
115  IsolateAddressId::kPendingExceptionAddress, isolate())));
116 
117  __ StoreP(r3, MemOperand(ip));
118  __ LoadRoot(r3, RootIndex::kException);
119  __ b(&exit);
120 
121  // Invoke: Link this frame into the handler chain.
122  __ bind(&invoke);
123  // Must preserve r3-r7.
124  __ PushStackHandler();
125  // If an exception not caught by another handler occurs, this handler
126  // returns control to the code after the b(&invoke) above, which
127  // restores all kCalleeSaved registers (including cp and fp) to their
128  // saved values before returning a failure to C.
129 
130  // Invoke the function by calling through JS entry trampoline builtin.
131  // Notice that we cannot store a reference to the trampoline code directly in
132  // this stub, because runtime stubs are not traversed when doing GC.
133 
134  // Expected registers by Builtins::JSEntryTrampoline
135  // r3: code entry
136  // r4: function
137  // r5: receiver
138  // r6: argc
139  // r7: argv
140  __ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
141 
142  // Unlink this frame from the handler chain.
143  __ PopStackHandler();
144 
145  __ bind(&exit); // r3 holds result
146  // Check if the current stack frame is marked as the outermost JS frame.
147  Label non_outermost_js_2;
148  __ pop(r8);
149  __ cmpi(r8, Operand(StackFrame::OUTERMOST_JSENTRY_FRAME));
150  __ bne(&non_outermost_js_2);
151  __ mov(r9, Operand::Zero());
152  __ mov(r8, Operand(js_entry_sp));
153  __ StoreP(r9, MemOperand(r8));
154  __ bind(&non_outermost_js_2);
155 
156  // Restore the top frame descriptors from the stack.
157  __ pop(r6);
158  __ mov(ip, Operand(ExternalReference::Create(
159  IsolateAddressId::kCEntryFPAddress, isolate())));
160  __ StoreP(r6, MemOperand(ip));
161 
162  // Reset the stack to the callee saved registers.
163  __ addi(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
164 
165  // Restore callee-saved double registers.
166  __ MultiPopDoubles(kCalleeSavedDoubles);
167 
168  // Restore callee-saved registers.
169  __ MultiPop(kCalleeSaved);
170 
171  // Return
172  __ LoadP(r0, MemOperand(sp, kStackFrameLRSlot * kPointerSize));
173  __ mtlr(r0);
174  __ blr();
175 }
176 
177 // This stub is paired with DirectCEntryStub::GenerateCall
178 void DirectCEntryStub::Generate(MacroAssembler* masm) {
179  // Place the return address on the stack, making the call
180  // GC safe. The RegExp backend also relies on this.
181  __ mflr(r0);
182  __ StoreP(r0, MemOperand(sp, kStackFrameExtraParamSlot * kPointerSize));
183  __ Call(ip); // Call the C++ function.
184  __ LoadP(r0, MemOperand(sp, kStackFrameExtraParamSlot * kPointerSize));
185  __ mtlr(r0);
186  __ blr();
187 }
188 
189 
190 void DirectCEntryStub::GenerateCall(MacroAssembler* masm, Register target) {
191  if (FLAG_embedded_builtins) {
192  if (masm->root_array_available() &&
193  isolate()->ShouldLoadConstantsFromRootList()) {
194  // This is basically an inlined version of Call(Handle<Code>) that loads
195  // the code object into lr instead of ip.
196  DCHECK_NE(ip, target);
197  __ IndirectLoadConstant(ip, GetCode());
198  __ addi(r0, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
199  __ Move(ip, target);
200  __ Call(r0);
201  return;
202  }
203  }
204  if (ABI_USES_FUNCTION_DESCRIPTORS) {
205  // AIX/PPC64BE Linux use a function descriptor.
206  __ LoadP(ToRegister(ABI_TOC_REGISTER), MemOperand(target, kPointerSize));
207  __ LoadP(ip, MemOperand(target, 0)); // Instruction address
208  } else {
209  // ip needs to be set for DirectCEentryStub::Generate, and also
210  // for ABI_CALL_VIA_IP.
211  __ Move(ip, target);
212  }
213 
214  intptr_t code = reinterpret_cast<intptr_t>(GetCode().location());
215  __ mov(r0, Operand(code, RelocInfo::CODE_TARGET));
216  __ Call(r0); // Call the stub.
217 }
218 
219 static int AddressOffset(ExternalReference ref0, ExternalReference ref1) {
220  return ref0.address() - ref1.address();
221 }
222 
223 
224 // Calls an API function. Allocates HandleScope, extracts returned value
225 // from handle and propagates exceptions. Restores context. stack_space
226 // - space to be unwound on exit (includes the call JS arguments space and
227 // the additional space allocated for the fast call).
228 static void CallApiFunctionAndReturn(MacroAssembler* masm,
229  Register function_address,
230  ExternalReference thunk_ref,
231  int stack_space,
232  MemOperand* stack_space_operand,
233  MemOperand return_value_operand) {
234  Isolate* isolate = masm->isolate();
235  ExternalReference next_address =
236  ExternalReference::handle_scope_next_address(isolate);
237  const int kNextOffset = 0;
238  const int kLimitOffset = AddressOffset(
239  ExternalReference::handle_scope_limit_address(isolate), next_address);
240  const int kLevelOffset = AddressOffset(
241  ExternalReference::handle_scope_level_address(isolate), next_address);
242 
243  // Additional parameter is the address of the actual callback.
244  DCHECK(function_address == r4 || function_address == r5);
245  Register scratch = r6;
246 
247  __ Move(scratch, ExternalReference::is_profiling_address(isolate));
248  __ lbz(scratch, MemOperand(scratch, 0));
249  __ cmpi(scratch, Operand::Zero());
250 
251  if (CpuFeatures::IsSupported(ISELECT)) {
252  __ Move(scratch, thunk_ref);
253  __ isel(eq, scratch, function_address, scratch);
254  } else {
255  Label profiler_disabled;
256  Label end_profiler_check;
257  __ beq(&profiler_disabled);
258  __ Move(scratch, thunk_ref);
259  __ b(&end_profiler_check);
260  __ bind(&profiler_disabled);
261  __ mr(scratch, function_address);
262  __ bind(&end_profiler_check);
263  }
264 
265  // Allocate HandleScope in callee-save registers.
266  // r17 - next_address
267  // r14 - next_address->kNextOffset
268  // r15 - next_address->kLimitOffset
269  // r16 - next_address->kLevelOffset
270  __ Move(r17, next_address);
271  __ LoadP(r14, MemOperand(r17, kNextOffset));
272  __ LoadP(r15, MemOperand(r17, kLimitOffset));
273  __ lwz(r16, MemOperand(r17, kLevelOffset));
274  __ addi(r16, r16, Operand(1));
275  __ stw(r16, MemOperand(r17, kLevelOffset));
276 
277  if (FLAG_log_timer_events) {
278  FrameScope frame(masm, StackFrame::MANUAL);
279  __ PushSafepointRegisters();
280  __ PrepareCallCFunction(1, r3);
281  __ Move(r3, ExternalReference::isolate_address(isolate));
282  __ CallCFunction(ExternalReference::log_enter_external_function(), 1);
283  __ PopSafepointRegisters();
284  }
285 
286  // Native call returns to the DirectCEntry stub which redirects to the
287  // return address pushed on stack (could have moved after GC).
288  // DirectCEntry stub itself is generated early and never moves.
289  DirectCEntryStub stub(isolate);
290  stub.GenerateCall(masm, scratch);
291 
292  if (FLAG_log_timer_events) {
293  FrameScope frame(masm, StackFrame::MANUAL);
294  __ PushSafepointRegisters();
295  __ PrepareCallCFunction(1, r3);
296  __ Move(r3, ExternalReference::isolate_address(isolate));
297  __ CallCFunction(ExternalReference::log_leave_external_function(), 1);
298  __ PopSafepointRegisters();
299  }
300 
301  Label promote_scheduled_exception;
302  Label delete_allocated_handles;
303  Label leave_exit_frame;
304  Label return_value_loaded;
305 
306  // load value from ReturnValue
307  __ LoadP(r3, return_value_operand);
308  __ bind(&return_value_loaded);
309  // No more valid handles (the result handle was the last one). Restore
310  // previous handle scope.
311  __ StoreP(r14, MemOperand(r17, kNextOffset));
312  if (__ emit_debug_code()) {
313  __ lwz(r4, MemOperand(r17, kLevelOffset));
314  __ cmp(r4, r16);
315  __ Check(eq, AbortReason::kUnexpectedLevelAfterReturnFromApiCall);
316  }
317  __ subi(r16, r16, Operand(1));
318  __ stw(r16, MemOperand(r17, kLevelOffset));
319  __ LoadP(r0, MemOperand(r17, kLimitOffset));
320  __ cmp(r15, r0);
321  __ bne(&delete_allocated_handles);
322 
323  // Leave the API exit frame.
324  __ bind(&leave_exit_frame);
325  // LeaveExitFrame expects unwind space to be in a register.
326  if (stack_space_operand != nullptr) {
327  __ lwz(r14, *stack_space_operand);
328  } else {
329  __ mov(r14, Operand(stack_space));
330  }
331  __ LeaveExitFrame(false, r14, stack_space_operand != nullptr);
332 
333  // Check if the function scheduled an exception.
334  __ LoadRoot(r14, RootIndex::kTheHoleValue);
335  __ Move(r15, ExternalReference::scheduled_exception_address(isolate));
336  __ LoadP(r15, MemOperand(r15));
337  __ cmp(r14, r15);
338  __ bne(&promote_scheduled_exception);
339 
340  __ blr();
341 
342  // Re-throw by promoting a scheduled exception.
343  __ bind(&promote_scheduled_exception);
344  __ TailCallRuntime(Runtime::kPromoteScheduledException);
345 
346  // HandleScope limit has changed. Delete allocated extensions.
347  __ bind(&delete_allocated_handles);
348  __ StoreP(r15, MemOperand(r17, kLimitOffset));
349  __ mr(r14, r3);
350  __ PrepareCallCFunction(1, r15);
351  __ Move(r3, ExternalReference::isolate_address(isolate));
352  __ CallCFunction(ExternalReference::delete_handle_scope_extensions(), 1);
353  __ mr(r3, r14);
354  __ b(&leave_exit_frame);
355 }
356 
357 void CallApiCallbackStub::Generate(MacroAssembler* masm) {
358  // ----------- S t a t e -------------
359  // -- r7 : call_data
360  // -- r5 : holder
361  // -- r4 : api_function_address
362  // -- cp : context
363  // --
364  // -- sp[0] : last argument
365  // -- ...
366  // -- sp[(argc - 1)* 4] : first argument
367  // -- sp[argc * 4] : receiver
368  // -----------------------------------
369 
370  Register call_data = r7;
371  Register holder = r5;
372  Register api_function_address = r4;
373 
374  typedef FunctionCallbackArguments FCA;
375 
376  STATIC_ASSERT(FCA::kArgsLength == 6);
377  STATIC_ASSERT(FCA::kNewTargetIndex == 5);
378  STATIC_ASSERT(FCA::kDataIndex == 4);
379  STATIC_ASSERT(FCA::kReturnValueOffset == 3);
380  STATIC_ASSERT(FCA::kReturnValueDefaultValueIndex == 2);
381  STATIC_ASSERT(FCA::kIsolateIndex == 1);
382  STATIC_ASSERT(FCA::kHolderIndex == 0);
383 
384  // new target
385  __ PushRoot(RootIndex::kUndefinedValue);
386 
387  // call data
388  __ push(call_data);
389 
390  Register scratch = call_data;
391  __ LoadRoot(scratch, RootIndex::kUndefinedValue);
392  // return value
393  __ push(scratch);
394  // return value default
395  __ push(scratch);
396  // isolate
397  __ Move(scratch, ExternalReference::isolate_address(masm->isolate()));
398  __ push(scratch);
399  // holder
400  __ push(holder);
401 
402  // Prepare arguments.
403  __ mr(scratch, sp);
404 
405  // Allocate the v8::Arguments structure in the arguments' space since
406  // it's not controlled by GC.
407  // PPC LINUX ABI:
408  //
409  // Create 4 extra slots on stack:
410  // [0] space for DirectCEntryStub's LR save
411  // [1-3] FunctionCallbackInfo
412  const int kApiStackSpace = 4;
413  const int kFunctionCallbackInfoOffset =
414  (kStackFrameExtraParamSlot + 1) * kPointerSize;
415 
416  FrameScope frame_scope(masm, StackFrame::MANUAL);
417  __ EnterExitFrame(false, kApiStackSpace);
418 
419  DCHECK(api_function_address != r3 && scratch != r3);
420  // r3 = FunctionCallbackInfo&
421  // Arguments is after the return address.
422  __ addi(r3, sp, Operand(kFunctionCallbackInfoOffset));
423  // FunctionCallbackInfo::implicit_args_
424  __ StoreP(scratch, MemOperand(r3, 0 * kPointerSize));
425  // FunctionCallbackInfo::values_
426  __ addi(ip, scratch, Operand((FCA::kArgsLength - 1 + argc()) * kPointerSize));
427  __ StoreP(ip, MemOperand(r3, 1 * kPointerSize));
428  // FunctionCallbackInfo::length_ = argc
429  __ li(ip, Operand(argc()));
430  __ stw(ip, MemOperand(r3, 2 * kPointerSize));
431 
432  ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
433 
434  AllowExternalCallThatCantCauseGC scope(masm);
435  // Stores return the first js argument
436  int return_value_offset = 2 + FCA::kReturnValueOffset;
437  MemOperand return_value_operand(fp, return_value_offset * kPointerSize);
438  const int stack_space = argc() + FCA::kArgsLength + 1;
439  MemOperand* stack_space_operand = nullptr;
440  CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, stack_space,
441  stack_space_operand, return_value_operand);
442 }
443 
444 
445 void CallApiGetterStub::Generate(MacroAssembler* masm) {
446  int arg0Slot = 0;
447  int accessorInfoSlot = 0;
448  int apiStackSpace = 0;
449  // Build v8::PropertyCallbackInfo::args_ array on the stack and push property
450  // name below the exit frame to make GC aware of them.
451  STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0);
452  STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1);
453  STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2);
454  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3);
455  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4);
456  STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5);
457  STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6);
458  STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7);
459 
460  Register receiver = ApiGetterDescriptor::ReceiverRegister();
461  Register holder = ApiGetterDescriptor::HolderRegister();
462  Register callback = ApiGetterDescriptor::CallbackRegister();
463  Register scratch = r7;
464  DCHECK(!AreAliased(receiver, holder, callback, scratch));
465 
466  Register api_function_address = r5;
467 
468  __ push(receiver);
469  // Push data from AccessorInfo.
470  __ LoadP(scratch, FieldMemOperand(callback, AccessorInfo::kDataOffset));
471  __ push(scratch);
472  __ LoadRoot(scratch, RootIndex::kUndefinedValue);
473  __ Push(scratch, scratch);
474  __ Move(scratch, ExternalReference::isolate_address(isolate()));
475  __ Push(scratch, holder);
476  __ Push(Smi::zero()); // should_throw_on_error -> false
477  __ LoadP(scratch, FieldMemOperand(callback, AccessorInfo::kNameOffset));
478  __ push(scratch);
479 
480  // v8::PropertyCallbackInfo::args_ array and name handle.
481  const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1;
482 
483  // Load address of v8::PropertyAccessorInfo::args_ array and name handle.
484  __ mr(r3, sp); // r3 = Handle<Name>
485  __ addi(r4, r3, Operand(1 * kPointerSize)); // r4 = v8::PCI::args_
486 
487 // If ABI passes Handles (pointer-sized struct) in a register:
488 //
489 // Create 2 extra slots on stack:
490 // [0] space for DirectCEntryStub's LR save
491 // [1] AccessorInfo&
492 //
493 // Otherwise:
494 //
495 // Create 3 extra slots on stack:
496 // [0] space for DirectCEntryStub's LR save
497 // [1] copy of Handle (first arg)
498 // [2] AccessorInfo&
499  if (ABI_PASSES_HANDLES_IN_REGS) {
500  accessorInfoSlot = kStackFrameExtraParamSlot + 1;
501  apiStackSpace = 2;
502  } else {
503  arg0Slot = kStackFrameExtraParamSlot + 1;
504  accessorInfoSlot = arg0Slot + 1;
505  apiStackSpace = 3;
506  }
507 
508  FrameScope frame_scope(masm, StackFrame::MANUAL);
509  __ EnterExitFrame(false, apiStackSpace);
510 
511  if (!ABI_PASSES_HANDLES_IN_REGS) {
512  // pass 1st arg by reference
513  __ StoreP(r3, MemOperand(sp, arg0Slot * kPointerSize));
514  __ addi(r3, sp, Operand(arg0Slot * kPointerSize));
515  }
516 
517  // Create v8::PropertyCallbackInfo object on the stack and initialize
518  // it's args_ field.
519  __ StoreP(r4, MemOperand(sp, accessorInfoSlot * kPointerSize));
520  __ addi(r4, sp, Operand(accessorInfoSlot * kPointerSize));
521  // r4 = v8::PropertyCallbackInfo&
522 
523  ExternalReference thunk_ref =
524  ExternalReference::invoke_accessor_getter_callback();
525 
526  __ LoadP(scratch, FieldMemOperand(callback, AccessorInfo::kJsGetterOffset));
527  __ LoadP(api_function_address,
528  FieldMemOperand(scratch, Foreign::kForeignAddressOffset));
529 
530  // +3 is to skip prolog, return address and name handle.
531  MemOperand return_value_operand(
532  fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
533  CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
534  kStackUnwindSpace, nullptr, return_value_operand);
535 }
536 
537 #undef __
538 } // namespace internal
539 } // namespace v8
540 
541 #endif // V8_TARGET_ARCH_PPC
Definition: libplatform.h:13