• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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_IA32
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/frame-constants.h"
13 #include "src/frames.h"
14 #include "src/heap/heap-inl.h"
15 #include "src/ic/ic.h"
16 #include "src/ic/stub-cache.h"
17 #include "src/isolate.h"
18 #include "src/objects/api-callbacks.h"
19 #include "src/regexp/jsregexp.h"
20 #include "src/regexp/regexp-macro-assembler.h"
21 #include "src/runtime/runtime.h"
22 
23 namespace v8 {
24 namespace internal {
25 
26 #define __ ACCESS_MASM(masm)
27 
Generate(MacroAssembler * masm)28 void JSEntryStub::Generate(MacroAssembler* masm) {
29   Label invoke, handler_entry, exit;
30   Label not_outermost_js, not_outermost_js_2;
31 
32   ProfileEntryHookStub::MaybeCallEntryHook(masm);
33 
34   // Set up frame.
35   __ push(ebp);
36   __ mov(ebp, esp);
37 
38   // Push marker in two places.
39   StackFrame::Type marker = type();
40   __ push(Immediate(StackFrame::TypeToMarker(marker)));  // marker
41   ExternalReference context_address =
42       ExternalReference::Create(IsolateAddressId::kContextAddress, isolate());
43   __ push(__ StaticVariable(context_address));  // context
44   // Save callee-saved registers (C calling conventions).
45   __ push(edi);
46   __ push(esi);
47   __ push(ebx);
48 
49   __ InitializeRootRegister();
50 
51   // Save copies of the top frame descriptor on the stack.
52   ExternalReference c_entry_fp =
53       ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate());
54   __ push(__ StaticVariable(c_entry_fp));
55 
56   // If this is the outermost JS call, set js_entry_sp value.
57   ExternalReference js_entry_sp =
58       ExternalReference::Create(IsolateAddressId::kJSEntrySPAddress, isolate());
59   __ cmp(__ StaticVariable(js_entry_sp), Immediate(0));
60   __ j(not_equal, &not_outermost_js, Label::kNear);
61   __ mov(__ StaticVariable(js_entry_sp), ebp);
62   __ push(Immediate(StackFrame::OUTERMOST_JSENTRY_FRAME));
63   __ jmp(&invoke, Label::kNear);
64   __ bind(&not_outermost_js);
65   __ push(Immediate(StackFrame::INNER_JSENTRY_FRAME));
66 
67   // Jump to a faked try block that does the invoke, with a faked catch
68   // block that sets the pending exception.
69   __ jmp(&invoke);
70   __ bind(&handler_entry);
71   handler_offset_ = handler_entry.pos();
72   // Caught exception: Store result (exception) in the pending exception
73   // field in the JSEnv and return a failure sentinel.
74   ExternalReference pending_exception = ExternalReference::Create(
75       IsolateAddressId::kPendingExceptionAddress, isolate());
76   __ mov(__ StaticVariable(pending_exception), eax);
77   __ mov(eax, Immediate(isolate()->factory()->exception()));
78   __ jmp(&exit);
79 
80   // Invoke: Link this frame into the handler chain.
81   __ bind(&invoke);
82   __ PushStackHandler();
83 
84   // Invoke the function by calling through JS entry trampoline builtin and
85   // pop the faked function when we return. Notice that we cannot store a
86   // reference to the trampoline code directly in this stub, because the
87   // builtin stubs may not have been generated yet.
88   __ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
89 
90   // Unlink this frame from the handler chain.
91   __ PopStackHandler();
92 
93   __ bind(&exit);
94 
95   __ VerifyRootRegister();
96 
97   // Check if the current stack frame is marked as the outermost JS frame.
98   __ pop(ebx);
99   __ cmp(ebx, Immediate(StackFrame::OUTERMOST_JSENTRY_FRAME));
100   __ j(not_equal, &not_outermost_js_2);
101   __ mov(__ StaticVariable(js_entry_sp), Immediate(0));
102   __ bind(&not_outermost_js_2);
103 
104   // Restore the top frame descriptor from the stack.
105   __ pop(__ StaticVariable(ExternalReference::Create(
106       IsolateAddressId::kCEntryFPAddress, isolate())));
107 
108   // Restore callee-saved registers (C calling conventions).
109   __ pop(ebx);
110   __ pop(esi);
111   __ pop(edi);
112   __ add(esp, Immediate(2 * kPointerSize));  // remove markers
113 
114   // Restore frame pointer and return.
115   __ pop(ebp);
116   __ ret(0);
117 }
118 
MaybeCallEntryHookDelayed(TurboAssembler * tasm,Zone * zone)119 void ProfileEntryHookStub::MaybeCallEntryHookDelayed(TurboAssembler* tasm,
120                                                      Zone* zone) {
121   if (tasm->isolate()->function_entry_hook() != nullptr) {
122     tasm->CallStubDelayed(new (zone) ProfileEntryHookStub(nullptr));
123   }
124 }
125 
MaybeCallEntryHook(MacroAssembler * masm)126 void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
127   if (masm->isolate()->function_entry_hook() != nullptr) {
128     ProfileEntryHookStub stub(masm->isolate());
129     masm->CallStub(&stub);
130   }
131 }
132 
133 
Generate(MacroAssembler * masm)134 void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
135   // Save volatile registers.
136   const int kNumSavedRegisters = 3;
137   __ push(eax);
138   __ push(ecx);
139   __ push(edx);
140 
141   // Calculate and push the original stack pointer.
142   __ lea(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
143   __ push(eax);
144 
145   // Retrieve our return address and use it to calculate the calling
146   // function's address.
147   __ mov(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
148   __ sub(eax, Immediate(Assembler::kCallInstructionLength));
149   __ push(eax);
150 
151   // Call the entry hook.
152   DCHECK_NOT_NULL(isolate()->function_entry_hook());
153   __ call(FUNCTION_ADDR(isolate()->function_entry_hook()),
154           RelocInfo::RUNTIME_ENTRY);
155   __ add(esp, Immediate(2 * kPointerSize));
156 
157   // Restore ecx.
158   __ pop(edx);
159   __ pop(ecx);
160   __ pop(eax);
161 
162   __ ret(0);
163 }
164 
165 // Generates an Operand for saving parameters after PrepareCallApiFunction.
ApiParameterOperand(int index)166 static Operand ApiParameterOperand(int index) {
167   return Operand(esp, index * kPointerSize);
168 }
169 
170 
171 // Prepares stack to put arguments (aligns and so on). Reserves
172 // space for return value if needed (assumes the return value is a handle).
173 // Arguments must be stored in ApiParameterOperand(0), ApiParameterOperand(1)
174 // etc. Saves context (esi). If space was reserved for return value then
175 // stores the pointer to the reserved slot into esi.
PrepareCallApiFunction(MacroAssembler * masm,int argc)176 static void PrepareCallApiFunction(MacroAssembler* masm, int argc) {
177   __ EnterApiExitFrame(argc);
178   if (__ emit_debug_code()) {
179     __ mov(esi, Immediate(bit_cast<int32_t>(kZapValue)));
180   }
181 }
182 
183 
184 // Calls an API function.  Allocates HandleScope, extracts returned value
185 // from handle and propagates exceptions.  Clobbers ebx, edi and
186 // caller-save registers.  Restores context.  On return removes
187 // stack_space * kPointerSize (GCed).
CallApiFunctionAndReturn(MacroAssembler * masm,Register function_address,ExternalReference thunk_ref,Operand thunk_last_arg,int stack_space,Operand * stack_space_operand,Operand return_value_operand)188 static void CallApiFunctionAndReturn(MacroAssembler* masm,
189                                      Register function_address,
190                                      ExternalReference thunk_ref,
191                                      Operand thunk_last_arg, int stack_space,
192                                      Operand* stack_space_operand,
193                                      Operand return_value_operand) {
194   Isolate* isolate = masm->isolate();
195 
196   ExternalReference next_address =
197       ExternalReference::handle_scope_next_address(isolate);
198   ExternalReference limit_address =
199       ExternalReference::handle_scope_limit_address(isolate);
200   ExternalReference level_address =
201       ExternalReference::handle_scope_level_address(isolate);
202 
203   DCHECK(edx == function_address);
204   // Allocate HandleScope in callee-save registers.
205   __ mov(ebx, __ StaticVariable(next_address));
206   __ mov(edi, __ StaticVariable(limit_address));
207   __ add(__ StaticVariable(level_address), Immediate(1));
208 
209   if (FLAG_log_timer_events) {
210     FrameScope frame(masm, StackFrame::MANUAL);
211     __ PushSafepointRegisters();
212     __ PrepareCallCFunction(1, eax);
213     __ mov(Operand(esp, 0),
214            Immediate(ExternalReference::isolate_address(isolate)));
215     __ CallCFunction(ExternalReference::log_enter_external_function(), 1);
216     __ PopSafepointRegisters();
217   }
218 
219 
220   Label profiler_disabled;
221   Label end_profiler_check;
222   __ mov(eax, Immediate(ExternalReference::is_profiling_address(isolate)));
223   __ cmpb(Operand(eax, 0), Immediate(0));
224   __ j(zero, &profiler_disabled);
225 
226   // Additional parameter is the address of the actual getter function.
227   __ mov(thunk_last_arg, function_address);
228   // Call the api function.
229   __ mov(eax, Immediate(thunk_ref));
230   __ call(eax);
231   __ jmp(&end_profiler_check);
232 
233   __ bind(&profiler_disabled);
234   // Call the api function.
235   __ call(function_address);
236   __ bind(&end_profiler_check);
237 
238   if (FLAG_log_timer_events) {
239     FrameScope frame(masm, StackFrame::MANUAL);
240     __ PushSafepointRegisters();
241     __ PrepareCallCFunction(1, eax);
242     __ mov(Operand(esp, 0),
243            Immediate(ExternalReference::isolate_address(isolate)));
244     __ CallCFunction(ExternalReference::log_leave_external_function(), 1);
245     __ PopSafepointRegisters();
246   }
247 
248   Label prologue;
249   // Load the value from ReturnValue
250   __ mov(eax, return_value_operand);
251 
252   Label promote_scheduled_exception;
253   Label delete_allocated_handles;
254   Label leave_exit_frame;
255 
256   __ bind(&prologue);
257   // No more valid handles (the result handle was the last one). Restore
258   // previous handle scope.
259   __ mov(__ StaticVariable(next_address), ebx);
260   __ sub(__ StaticVariable(level_address), Immediate(1));
261   __ Assert(above_equal, AbortReason::kInvalidHandleScopeLevel);
262   __ cmp(edi, __ StaticVariable(limit_address));
263   __ j(not_equal, &delete_allocated_handles);
264 
265   // Leave the API exit frame.
266   __ bind(&leave_exit_frame);
267   if (stack_space_operand != nullptr) {
268     __ mov(ebx, *stack_space_operand);
269   }
270   __ LeaveApiExitFrame();
271 
272   // Check if the function scheduled an exception.
273   ExternalReference scheduled_exception_address =
274       ExternalReference::scheduled_exception_address(isolate);
275   __ cmp(__ StaticVariable(scheduled_exception_address),
276          Immediate(isolate->factory()->the_hole_value()));
277   __ j(not_equal, &promote_scheduled_exception);
278 
279 #if DEBUG
280   // Check if the function returned a valid JavaScript value.
281   Label ok;
282   Register return_value = eax;
283   Register map = ecx;
284 
285   __ JumpIfSmi(return_value, &ok, Label::kNear);
286   __ mov(map, FieldOperand(return_value, HeapObject::kMapOffset));
287 
288   __ CmpInstanceType(map, LAST_NAME_TYPE);
289   __ j(below_equal, &ok, Label::kNear);
290 
291   __ CmpInstanceType(map, FIRST_JS_RECEIVER_TYPE);
292   __ j(above_equal, &ok, Label::kNear);
293 
294   __ cmp(map, isolate->factory()->heap_number_map());
295   __ j(equal, &ok, Label::kNear);
296 
297   __ cmp(return_value, isolate->factory()->undefined_value());
298   __ j(equal, &ok, Label::kNear);
299 
300   __ cmp(return_value, isolate->factory()->true_value());
301   __ j(equal, &ok, Label::kNear);
302 
303   __ cmp(return_value, isolate->factory()->false_value());
304   __ j(equal, &ok, Label::kNear);
305 
306   __ cmp(return_value, isolate->factory()->null_value());
307   __ j(equal, &ok, Label::kNear);
308 
309   __ Abort(AbortReason::kAPICallReturnedInvalidObject);
310 
311   __ bind(&ok);
312 #endif
313 
314   if (stack_space_operand != nullptr) {
315     DCHECK_EQ(0, stack_space);
316     __ pop(ecx);
317     __ add(esp, ebx);
318     __ jmp(ecx);
319   } else {
320     __ ret(stack_space * kPointerSize);
321   }
322 
323   // Re-throw by promoting a scheduled exception.
324   __ bind(&promote_scheduled_exception);
325   __ TailCallRuntime(Runtime::kPromoteScheduledException);
326 
327   // HandleScope limit has changed. Delete allocated extensions.
328   ExternalReference delete_extensions =
329       ExternalReference::delete_handle_scope_extensions();
330   __ bind(&delete_allocated_handles);
331   __ mov(__ StaticVariable(limit_address), edi);
332   __ mov(edi, eax);
333   __ mov(Operand(esp, 0),
334          Immediate(ExternalReference::isolate_address(isolate)));
335   __ mov(eax, Immediate(delete_extensions));
336   __ call(eax);
337   __ mov(eax, edi);
338   __ jmp(&leave_exit_frame);
339 }
340 
Generate(MacroAssembler * masm)341 void CallApiCallbackStub::Generate(MacroAssembler* masm) {
342   // ----------- S t a t e -------------
343   //  -- ebx                 : call_data
344   //  -- ecx                 : holder
345   //  -- edx                 : api_function_address
346   //  -- esi                 : context
347   //  --
348   //  -- esp[0]              : return address
349   //  -- esp[4]              : last argument
350   //  -- ...
351   //  -- esp[argc * 4]       : first argument
352   //  -- esp[(argc + 1) * 4] : receiver
353   // -----------------------------------
354 
355   Register call_data = ebx;
356   Register holder = ecx;
357   Register api_function_address = edx;
358   Register return_address = eax;
359 
360   typedef FunctionCallbackArguments FCA;
361 
362   STATIC_ASSERT(FCA::kArgsLength == 6);
363   STATIC_ASSERT(FCA::kNewTargetIndex == 5);
364   STATIC_ASSERT(FCA::kDataIndex == 4);
365   STATIC_ASSERT(FCA::kReturnValueOffset == 3);
366   STATIC_ASSERT(FCA::kReturnValueDefaultValueIndex == 2);
367   STATIC_ASSERT(FCA::kIsolateIndex == 1);
368   STATIC_ASSERT(FCA::kHolderIndex == 0);
369 
370   __ pop(return_address);
371 
372   // new target
373   __ PushRoot(Heap::kUndefinedValueRootIndex);
374 
375   // call data
376   __ push(call_data);
377 
378   // return value
379   __ PushRoot(Heap::kUndefinedValueRootIndex);
380   // return value default
381   __ PushRoot(Heap::kUndefinedValueRootIndex);
382   // isolate
383   __ push(Immediate(ExternalReference::isolate_address(isolate())));
384   // holder
385   __ push(holder);
386 
387   Register scratch = call_data;
388 
389   __ mov(scratch, esp);
390 
391   // push return address
392   __ push(return_address);
393 
394   // API function gets reference to the v8::Arguments. If CPU profiler
395   // is enabled wrapper function will be called and we need to pass
396   // address of the callback as additional parameter, always allocate
397   // space for it.
398   const int kApiArgc = 1 + 1;
399 
400   // Allocate the v8::Arguments structure in the arguments' space since
401   // it's not controlled by GC.
402   const int kApiStackSpace = 3;
403 
404   PrepareCallApiFunction(masm, kApiArgc + kApiStackSpace);
405 
406   // FunctionCallbackInfo::implicit_args_.
407   __ mov(ApiParameterOperand(2), scratch);
408   __ add(scratch, Immediate((argc() + FCA::kArgsLength - 1) * kPointerSize));
409   // FunctionCallbackInfo::values_.
410   __ mov(ApiParameterOperand(3), scratch);
411   // FunctionCallbackInfo::length_.
412   __ Move(ApiParameterOperand(4), Immediate(argc()));
413 
414   // v8::InvocationCallback's argument.
415   __ lea(scratch, ApiParameterOperand(2));
416   __ mov(ApiParameterOperand(0), scratch);
417 
418   ExternalReference thunk_ref = ExternalReference::invoke_function_callback();
419 
420   // Stores return the first js argument
421   int return_value_offset = 2 + FCA::kReturnValueOffset;
422   Operand return_value_operand(ebp, return_value_offset * kPointerSize);
423   const int stack_space = argc() + FCA::kArgsLength + 1;
424   Operand* stack_space_operand = nullptr;
425   CallApiFunctionAndReturn(masm, api_function_address, thunk_ref,
426                            ApiParameterOperand(1), stack_space,
427                            stack_space_operand, return_value_operand);
428 }
429 
430 
Generate(MacroAssembler * masm)431 void CallApiGetterStub::Generate(MacroAssembler* masm) {
432   // Build v8::PropertyCallbackInfo::args_ array on the stack and push property
433   // name below the exit frame to make GC aware of them.
434   STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0);
435   STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1);
436   STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2);
437   STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3);
438   STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4);
439   STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5);
440   STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6);
441   STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7);
442 
443   Register receiver = ApiGetterDescriptor::ReceiverRegister();
444   Register holder = ApiGetterDescriptor::HolderRegister();
445   Register callback = ApiGetterDescriptor::CallbackRegister();
446   Register scratch = ebx;
447   DCHECK(!AreAliased(receiver, holder, callback, scratch));
448 
449   __ pop(scratch);  // Pop return address to extend the frame.
450   __ push(receiver);
451   __ push(FieldOperand(callback, AccessorInfo::kDataOffset));
452   __ PushRoot(Heap::kUndefinedValueRootIndex);  // ReturnValue
453   // ReturnValue default value
454   __ PushRoot(Heap::kUndefinedValueRootIndex);
455   __ push(Immediate(ExternalReference::isolate_address(isolate())));
456   __ push(holder);
457   __ push(Immediate(Smi::kZero));  // should_throw_on_error -> false
458   __ push(FieldOperand(callback, AccessorInfo::kNameOffset));
459   __ push(scratch);  // Restore return address.
460 
461   // v8::PropertyCallbackInfo::args_ array and name handle.
462   const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1;
463 
464   // Allocate v8::PropertyCallbackInfo object, arguments for callback and
465   // space for optional callback address parameter (in case CPU profiler is
466   // active) in non-GCed stack space.
467   const int kApiArgc = 3 + 1;
468 
469   // Load address of v8::PropertyAccessorInfo::args_ array.
470   __ lea(scratch, Operand(esp, 2 * kPointerSize));
471 
472   PrepareCallApiFunction(masm, kApiArgc);
473   // Create v8::PropertyCallbackInfo object on the stack and initialize
474   // it's args_ field.
475   Operand info_object = ApiParameterOperand(3);
476   __ mov(info_object, scratch);
477 
478   // Name as handle.
479   __ sub(scratch, Immediate(kPointerSize));
480   __ mov(ApiParameterOperand(0), scratch);
481   // Arguments pointer.
482   __ lea(scratch, info_object);
483   __ mov(ApiParameterOperand(1), scratch);
484   // Reserve space for optional callback address parameter.
485   Operand thunk_last_arg = ApiParameterOperand(2);
486 
487   ExternalReference thunk_ref =
488       ExternalReference::invoke_accessor_getter_callback();
489 
490   __ mov(scratch, FieldOperand(callback, AccessorInfo::kJsGetterOffset));
491   Register function_address = edx;
492   __ mov(function_address,
493          FieldOperand(scratch, Foreign::kForeignAddressOffset));
494   // +3 is to skip prolog, return address and name handle.
495   Operand return_value_operand(
496       ebp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize);
497   CallApiFunctionAndReturn(masm, function_address, thunk_ref, thunk_last_arg,
498                            kStackUnwindSpace, nullptr, return_value_operand);
499 }
500 
501 #undef __
502 
503 }  // namespace internal
504 }  // namespace v8
505 
506 #endif  // V8_TARGET_ARCH_IA32
507