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, ¬_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(¬_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, ¬_outermost_js_2);
101 __ mov(__ StaticVariable(js_entry_sp), Immediate(0));
102 __ bind(¬_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