blob: 099a46103c864f3bcf52c0e01a3d3de4db5c7fa2 [file] [log] [blame]
ager@chromium.org5ec48922009-05-05 07:25:34 +00001// Copyright 2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
kasperl@chromium.org71affb52009-05-26 05:44:31 +000028#include "v8.h"
29
30#include "bootstrapper.h"
31#include "codegen-inl.h"
ager@chromium.orgeadaf222009-06-16 09:43:10 +000032#include "assembler-x64.h"
ager@chromium.orge2902be2009-06-08 12:21:35 +000033#include "macro-assembler-x64.h"
ager@chromium.orgeadaf222009-06-16 09:43:10 +000034#include "debug.h"
kasperl@chromium.org71affb52009-05-26 05:44:31 +000035
36namespace v8 {
37namespace internal {
38
39MacroAssembler::MacroAssembler(void* buffer, int size)
40 : Assembler(buffer, size),
41 unresolved_(0),
42 generating_stub_(false),
43 allow_stub_calls_(true),
44 code_object_(Heap::undefined_value()) {
45}
46
ager@chromium.orge2902be2009-06-08 12:21:35 +000047
ager@chromium.org5aa501c2009-06-23 07:57:28 +000048// TODO(x64): For now, the write barrier is disabled on x64 and we
49// therefore generate no code. This should be fixed when the write
50// barrier is enabled.
51void MacroAssembler::RecordWrite(Register object, int offset,
52 Register value, Register scratch) {
53}
54
55
ager@chromium.orgeadaf222009-06-16 09:43:10 +000056void MacroAssembler::Assert(Condition cc, const char* msg) {
57 if (FLAG_debug_code) Check(cc, msg);
58}
59
60
61void MacroAssembler::Check(Condition cc, const char* msg) {
62 Label L;
63 j(cc, &L);
64 Abort(msg);
65 // will not return here
66 bind(&L);
67}
68
69
ager@chromium.org5aa501c2009-06-23 07:57:28 +000070void MacroAssembler::NegativeZeroTest(Register result,
71 Register op,
72 Label* then_label) {
73 Label ok;
74 testq(result, result);
75 j(not_zero, &ok);
76 testq(op, op);
77 j(sign, then_label);
78 bind(&ok);
79}
80
81
ager@chromium.orgeadaf222009-06-16 09:43:10 +000082void MacroAssembler::Abort(const char* msg) {
83 // We want to pass the msg string like a smi to avoid GC
84 // problems, however msg is not guaranteed to be aligned
85 // properly. Instead, we pass an aligned pointer that is
86 // a proper v8 smi, but also pass the alignment difference
87 // from the real pointer as a smi.
88 intptr_t p1 = reinterpret_cast<intptr_t>(msg);
89 intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
90 // Note: p0 might not be a valid Smi *value*, but it has a valid Smi tag.
91 ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
92#ifdef DEBUG
93 if (msg != NULL) {
94 RecordComment("Abort message: ");
95 RecordComment(msg);
96 }
97#endif
98 push(rax);
99 movq(kScratchRegister, p0, RelocInfo::NONE);
100 push(kScratchRegister);
101 movq(kScratchRegister,
102 reinterpret_cast<intptr_t>(Smi::FromInt(p1 - p0)),
103 RelocInfo::NONE);
104 push(kScratchRegister);
105 CallRuntime(Runtime::kAbort, 2);
106 // will not return here
107}
108
109
110void MacroAssembler::CallStub(CodeStub* stub) {
111 ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
112 movq(kScratchRegister, stub->GetCode(), RelocInfo::CODE_TARGET);
113 call(kScratchRegister);
114}
115
116
117void MacroAssembler::StubReturn(int argc) {
118 ASSERT(argc >= 1 && generating_stub());
119 ret((argc - 1) * kPointerSize);
120}
121
122
123void MacroAssembler::IllegalOperation(int num_arguments) {
124 if (num_arguments > 0) {
125 addq(rsp, Immediate(num_arguments * kPointerSize));
126 }
127 movq(rax, Factory::undefined_value(), RelocInfo::EMBEDDED_OBJECT);
128}
129
130
131void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
132 CallRuntime(Runtime::FunctionForId(id), num_arguments);
133}
134
135
136void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) {
137 // If the expected number of arguments of the runtime function is
138 // constant, we check that the actual number of arguments match the
139 // expectation.
140 if (f->nargs >= 0 && f->nargs != num_arguments) {
141 IllegalOperation(num_arguments);
142 return;
143 }
144
145 Runtime::FunctionId function_id =
146 static_cast<Runtime::FunctionId>(f->stub_id);
147 RuntimeStub stub(function_id, num_arguments);
148 CallStub(&stub);
149}
150
151
152void MacroAssembler::TailCallRuntime(ExternalReference const& ext,
153 int num_arguments) {
154 // TODO(1236192): Most runtime routines don't need the number of
155 // arguments passed in because it is constant. At some point we
156 // should remove this need and make the runtime routine entry code
157 // smarter.
158 movq(rax, Immediate(num_arguments));
159 JumpToBuiltin(ext);
160}
161
162
163void MacroAssembler::JumpToBuiltin(const ExternalReference& ext) {
164 // Set the entry point and jump to the C entry runtime stub.
165 movq(rbx, ext);
166 CEntryStub ces;
167 movq(kScratchRegister, ces.GetCode(), RelocInfo::CODE_TARGET);
168 jmp(kScratchRegister);
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000169}
170
ager@chromium.orge2902be2009-06-08 12:21:35 +0000171
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000172void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
173 bool resolved;
174 Handle<Code> code = ResolveBuiltin(id, &resolved);
175
176 const char* name = Builtins::GetName(id);
177 int argc = Builtins::GetArgumentsCount(id);
178
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000179 movq(target, code, RelocInfo::EMBEDDED_OBJECT);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000180 if (!resolved) {
181 uint32_t flags =
182 Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
183 Bootstrapper::FixupFlagsIsPCRelative::encode(false) |
184 Bootstrapper::FixupFlagsUseCodeObject::encode(true);
185 Unresolved entry = { pc_offset() - sizeof(intptr_t), flags, name };
186 unresolved_.Add(entry);
187 }
188 addq(target, Immediate(Code::kHeaderSize - kHeapObjectTag));
189}
190
191
192Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
193 bool* resolved) {
194 // Move the builtin function into the temporary function slot by
195 // reading it from the builtins object. NOTE: We should be able to
196 // reduce this to two instructions by putting the function table in
197 // the global object instead of the "builtins" object and by using a
198 // real register for the function.
199 movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
200 movq(rdx, FieldOperand(rdx, GlobalObject::kBuiltinsOffset));
201 int builtins_offset =
202 JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
203 movq(rdi, FieldOperand(rdx, builtins_offset));
204
205
206 return Builtins::GetCode(id, resolved);
207}
208
209
ager@chromium.orge2902be2009-06-08 12:21:35 +0000210void MacroAssembler::Set(Register dst, int64_t x) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000211 if (x == 0) {
212 xor_(dst, dst);
213 } else if (is_int32(x)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000214 movq(dst, Immediate(x));
215 } else if (is_uint32(x)) {
216 movl(dst, Immediate(x));
217 } else {
218 movq(dst, x, RelocInfo::NONE);
219 }
220}
221
222
223void MacroAssembler::Set(const Operand& dst, int64_t x) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000224 if (x == 0) {
225 xor_(kScratchRegister, kScratchRegister);
226 movq(dst, kScratchRegister);
227 } else if (is_int32(x)) {
228 movq(dst, Immediate(x));
ager@chromium.orge2902be2009-06-08 12:21:35 +0000229 } else if (is_uint32(x)) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000230 movl(dst, Immediate(x));
ager@chromium.orge2902be2009-06-08 12:21:35 +0000231 } else {
232 movq(kScratchRegister, x, RelocInfo::NONE);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000233 movq(dst, kScratchRegister);
ager@chromium.orge2902be2009-06-08 12:21:35 +0000234 }
ager@chromium.orge2902be2009-06-08 12:21:35 +0000235}
236
237
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000238bool MacroAssembler::IsUnsafeSmi(Smi* value) {
239 return false;
240}
241
242void MacroAssembler::LoadUnsafeSmi(Register dst, Smi* source) {
243 UNIMPLEMENTED();
244}
245
246
247void MacroAssembler::Move(Register dst, Handle<Object> source) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000248 ASSERT(!source->IsFailure());
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000249 if (source->IsSmi()) {
250 if (IsUnsafeSmi(source)) {
251 LoadUnsafeSmi(dst, source);
252 } else {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000253 int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
254 movq(dst, Immediate(smi));
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000255 }
256 } else {
257 movq(dst, source, RelocInfo::EMBEDDED_OBJECT);
258 }
259}
260
261
262void MacroAssembler::Move(const Operand& dst, Handle<Object> source) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000263 if (source->IsSmi()) {
264 int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
265 movq(dst, Immediate(smi));
266 } else {
267 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
268 movq(dst, kScratchRegister);
269 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000270}
271
272
273void MacroAssembler::Cmp(Register dst, Handle<Object> source) {
274 Move(kScratchRegister, source);
275 cmpq(dst, kScratchRegister);
276}
277
278
ager@chromium.org3e875802009-06-29 08:26:34 +0000279void MacroAssembler::Cmp(const Operand& dst, Handle<Object> source) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000280 if (source->IsSmi()) {
281 if (IsUnsafeSmi(source)) {
282 LoadUnsafeSmi(kScratchRegister, source);
283 cmpl(dst, kScratchRegister);
284 } else {
285 // For smi-comparison, it suffices to compare the low 32 bits.
286 int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
287 cmpl(dst, Immediate(smi));
288 }
289 } else {
290 ASSERT(source->IsHeapObject());
291 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
292 cmpq(dst, kScratchRegister);
293 }
ager@chromium.org3e875802009-06-29 08:26:34 +0000294}
295
296
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000297void MacroAssembler::Push(Handle<Object> source) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000298 if (source->IsSmi()) {
299 if (IsUnsafeSmi(source)) {
300 LoadUnsafeSmi(kScratchRegister, source);
301 push(kScratchRegister);
302 } else {
303 int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
304 push(Immediate(smi));
305 }
306 } else {
307 ASSERT(source->IsHeapObject());
308 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
309 push(kScratchRegister);
310 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000311}
312
313
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000314void MacroAssembler::Jump(ExternalReference ext) {
315 movq(kScratchRegister, ext);
316 jmp(kScratchRegister);
317}
318
319
320void MacroAssembler::Jump(Address destination, RelocInfo::Mode rmode) {
321 movq(kScratchRegister, destination, rmode);
322 jmp(kScratchRegister);
323}
324
325
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000326void MacroAssembler::Jump(Handle<Code> code_object, RelocInfo::Mode rmode) {
327 WriteRecordedPositions();
328 ASSERT(RelocInfo::IsCodeTarget(rmode));
329 movq(kScratchRegister, code_object, rmode);
ager@chromium.org3e875802009-06-29 08:26:34 +0000330#ifdef DEBUG
331 Label target;
332 bind(&target);
333#endif
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000334 jmp(kScratchRegister);
ager@chromium.org3e875802009-06-29 08:26:34 +0000335#ifdef DEBUG
336 ASSERT_EQ(kTargetAddrToReturnAddrDist,
337 SizeOfCodeGeneratedSince(&target) + kPointerSize);
338#endif
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000339}
340
341
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000342void MacroAssembler::Call(ExternalReference ext) {
343 movq(kScratchRegister, ext);
344 call(kScratchRegister);
345}
346
347
348void MacroAssembler::Call(Address destination, RelocInfo::Mode rmode) {
349 movq(kScratchRegister, destination, rmode);
350 call(kScratchRegister);
351}
352
353
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000354void MacroAssembler::Call(Handle<Code> code_object, RelocInfo::Mode rmode) {
355 WriteRecordedPositions();
356 ASSERT(RelocInfo::IsCodeTarget(rmode));
357 movq(kScratchRegister, code_object, rmode);
358#ifdef DEBUG
359 Label target;
360 bind(&target);
361#endif
362 call(kScratchRegister);
363#ifdef DEBUG
364 ASSERT_EQ(kTargetAddrToReturnAddrDist,
365 SizeOfCodeGeneratedSince(&target) + kPointerSize);
366#endif
367}
368
369
ager@chromium.orge2902be2009-06-08 12:21:35 +0000370void MacroAssembler::PushTryHandler(CodeLocation try_location,
371 HandlerType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000372 // Adjust this code if not the case.
373 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
374
375 // The pc (return address) is already on TOS. This code pushes state,
376 // frame pointer and current handler. Check that they are expected
377 // next on the stack, in that order.
ager@chromium.orge2902be2009-06-08 12:21:35 +0000378 ASSERT_EQ(StackHandlerConstants::kStateOffset,
379 StackHandlerConstants::kPCOffset - kPointerSize);
ager@chromium.orge2902be2009-06-08 12:21:35 +0000380 ASSERT_EQ(StackHandlerConstants::kFPOffset,
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000381 StackHandlerConstants::kStateOffset - kPointerSize);
382 ASSERT_EQ(StackHandlerConstants::kNextOffset,
ager@chromium.orge2902be2009-06-08 12:21:35 +0000383 StackHandlerConstants::kFPOffset - kPointerSize);
384
385 if (try_location == IN_JAVASCRIPT) {
386 if (type == TRY_CATCH_HANDLER) {
387 push(Immediate(StackHandler::TRY_CATCH));
388 } else {
389 push(Immediate(StackHandler::TRY_FINALLY));
390 }
ager@chromium.orge2902be2009-06-08 12:21:35 +0000391 push(rbp);
ager@chromium.orge2902be2009-06-08 12:21:35 +0000392 } else {
393 ASSERT(try_location == IN_JS_ENTRY);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000394 // The frame pointer does not point to a JS frame so we save NULL
395 // for rbp. We expect the code throwing an exception to check rbp
396 // before dereferencing it to restore the context.
ager@chromium.orge2902be2009-06-08 12:21:35 +0000397 push(Immediate(StackHandler::ENTRY));
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000398 push(Immediate(0)); // NULL frame pointer.
ager@chromium.orge2902be2009-06-08 12:21:35 +0000399 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000400 // Save the current handler.
ager@chromium.orge2902be2009-06-08 12:21:35 +0000401 movq(kScratchRegister, ExternalReference(Top::k_handler_address));
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000402 push(Operand(kScratchRegister, 0));
ager@chromium.orge2902be2009-06-08 12:21:35 +0000403 // Link this handler.
404 movq(Operand(kScratchRegister, 0), rsp);
405}
406
407
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000408void MacroAssembler::Ret() {
409 ret(0);
410}
411
412
ager@chromium.org3e875802009-06-29 08:26:34 +0000413void MacroAssembler::FCmp() {
414 fcompp();
415 push(rax);
416 fnstsw_ax();
417 // TODO(X64): Check that sahf is safe to use, using CPUProbe.
418 sahf();
419 pop(rax);
420}
421
422
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000423void MacroAssembler::CmpObjectType(Register heap_object,
424 InstanceType type,
425 Register map) {
426 movq(map, FieldOperand(heap_object, HeapObject::kMapOffset));
427 CmpInstanceType(map, type);
428}
429
430
431void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
432 cmpb(FieldOperand(map, Map::kInstanceTypeOffset),
433 Immediate(static_cast<int8_t>(type)));
434}
435
436
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000437void MacroAssembler::TryGetFunctionPrototype(Register function,
438 Register result,
439 Label* miss) {
440 // Check that the receiver isn't a smi.
441 testl(function, Immediate(kSmiTagMask));
442 j(zero, miss);
443
444 // Check that the function really is a function.
445 CmpObjectType(function, JS_FUNCTION_TYPE, result);
446 j(not_equal, miss);
447
448 // Make sure that the function has an instance prototype.
449 Label non_instance;
450 testb(FieldOperand(result, Map::kBitFieldOffset),
451 Immediate(1 << Map::kHasNonInstancePrototype));
452 j(not_zero, &non_instance);
453
454 // Get the prototype or initial map from the function.
455 movq(result,
456 FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
457
458 // If the prototype or initial map is the hole, don't return it and
459 // simply miss the cache instead. This will allow us to allocate a
460 // prototype object on-demand in the runtime system.
461 Cmp(result, Factory::the_hole_value());
462 j(equal, miss);
463
464 // If the function does not have an initial map, we're done.
465 Label done;
466 CmpObjectType(result, MAP_TYPE, kScratchRegister);
467 j(not_equal, &done);
468
469 // Get the prototype from the initial map.
470 movq(result, FieldOperand(result, Map::kPrototypeOffset));
471 jmp(&done);
472
473 // Non-instance prototype: Fetch prototype from constructor field
474 // in initial map.
475 bind(&non_instance);
476 movq(result, FieldOperand(result, Map::kConstructorOffset));
477
478 // All done.
479 bind(&done);
480}
481
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000482
483void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
484 if (FLAG_native_code_counters && counter->Enabled()) {
485 movq(kScratchRegister, ExternalReference(counter));
486 movl(Operand(kScratchRegister, 0), Immediate(value));
487 }
488}
489
490
491void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) {
492 ASSERT(value > 0);
493 if (FLAG_native_code_counters && counter->Enabled()) {
494 movq(kScratchRegister, ExternalReference(counter));
495 Operand operand(kScratchRegister, 0);
496 if (value == 1) {
497 incl(operand);
498 } else {
499 addl(operand, Immediate(value));
500 }
501 }
502}
503
504
505void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
506 ASSERT(value > 0);
507 if (FLAG_native_code_counters && counter->Enabled()) {
508 movq(kScratchRegister, ExternalReference(counter));
509 Operand operand(kScratchRegister, 0);
510 if (value == 1) {
511 decl(operand);
512 } else {
513 subl(operand, Immediate(value));
514 }
515 }
516}
517
518
519#ifdef ENABLE_DEBUGGER_SUPPORT
520
521void MacroAssembler::PushRegistersFromMemory(RegList regs) {
522 ASSERT((regs & ~kJSCallerSaved) == 0);
523 // Push the content of the memory location to the stack.
524 for (int i = 0; i < kNumJSCallerSaved; i++) {
525 int r = JSCallerSavedCode(i);
526 if ((regs & (1 << r)) != 0) {
527 ExternalReference reg_addr =
528 ExternalReference(Debug_Address::Register(i));
529 movq(kScratchRegister, reg_addr);
530 push(Operand(kScratchRegister, 0));
531 }
532 }
533}
534
535void MacroAssembler::SaveRegistersToMemory(RegList regs) {
536 ASSERT((regs & ~kJSCallerSaved) == 0);
537 // Copy the content of registers to memory location.
538 for (int i = 0; i < kNumJSCallerSaved; i++) {
539 int r = JSCallerSavedCode(i);
540 if ((regs & (1 << r)) != 0) {
541 Register reg = { r };
542 ExternalReference reg_addr =
543 ExternalReference(Debug_Address::Register(i));
544 movq(kScratchRegister, reg_addr);
545 movq(Operand(kScratchRegister, 0), reg);
546 }
547 }
548}
549
550
551void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
552 ASSERT((regs & ~kJSCallerSaved) == 0);
553 // Copy the content of memory location to registers.
554 for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
555 int r = JSCallerSavedCode(i);
556 if ((regs & (1 << r)) != 0) {
557 Register reg = { r };
558 ExternalReference reg_addr =
559 ExternalReference(Debug_Address::Register(i));
560 movq(kScratchRegister, reg_addr);
561 movq(reg, Operand(kScratchRegister, 0));
562 }
563 }
564}
565
566
567void MacroAssembler::PopRegistersToMemory(RegList regs) {
568 ASSERT((regs & ~kJSCallerSaved) == 0);
569 // Pop the content from the stack to the memory location.
570 for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
571 int r = JSCallerSavedCode(i);
572 if ((regs & (1 << r)) != 0) {
573 ExternalReference reg_addr =
574 ExternalReference(Debug_Address::Register(i));
575 movq(kScratchRegister, reg_addr);
576 pop(Operand(kScratchRegister, 0));
577 }
578 }
579}
580
581
582void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
583 Register scratch,
584 RegList regs) {
585 ASSERT(!scratch.is(kScratchRegister));
586 ASSERT(!base.is(kScratchRegister));
587 ASSERT(!base.is(scratch));
588 ASSERT((regs & ~kJSCallerSaved) == 0);
589 // Copy the content of the stack to the memory location and adjust base.
590 for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
591 int r = JSCallerSavedCode(i);
592 if ((regs & (1 << r)) != 0) {
593 movq(scratch, Operand(base, 0));
594 ExternalReference reg_addr =
595 ExternalReference(Debug_Address::Register(i));
596 movq(kScratchRegister, reg_addr);
597 movq(Operand(kScratchRegister, 0), scratch);
598 lea(base, Operand(base, kPointerSize));
599 }
600 }
601}
602
603#endif // ENABLE_DEBUGGER_SUPPORT
604
605
ager@chromium.org3e875802009-06-29 08:26:34 +0000606void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
607 bool resolved;
608 Handle<Code> code = ResolveBuiltin(id, &resolved);
609
610 // Calls are not allowed in some stubs.
611 ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
612
613 // Rely on the assertion to check that the number of provided
614 // arguments match the expected number of arguments. Fake a
615 // parameter count to avoid emitting code to do the check.
616 ParameterCount expected(0);
617 InvokeCode(Handle<Code>(code), expected, expected,
618 RelocInfo::CODE_TARGET, flag);
619
620 const char* name = Builtins::GetName(id);
621 int argc = Builtins::GetArgumentsCount(id);
622 // The target address for the jump is stored as an immediate at offset
623 // kInvokeCodeAddressOffset.
624 if (!resolved) {
625 uint32_t flags =
626 Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000627 Bootstrapper::FixupFlagsIsPCRelative::encode(false) |
ager@chromium.org3e875802009-06-29 08:26:34 +0000628 Bootstrapper::FixupFlagsUseCodeObject::encode(false);
629 Unresolved entry =
630 { pc_offset() - kTargetAddrToReturnAddrDist, flags, name };
631 unresolved_.Add(entry);
632 }
633}
634
635
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000636void MacroAssembler::InvokePrologue(const ParameterCount& expected,
637 const ParameterCount& actual,
638 Handle<Code> code_constant,
639 Register code_register,
640 Label* done,
641 InvokeFlag flag) {
642 bool definitely_matches = false;
643 Label invoke;
644 if (expected.is_immediate()) {
645 ASSERT(actual.is_immediate());
646 if (expected.immediate() == actual.immediate()) {
647 definitely_matches = true;
648 } else {
649 movq(rax, Immediate(actual.immediate()));
650 if (expected.immediate() ==
651 SharedFunctionInfo::kDontAdaptArgumentsSentinel) {
652 // Don't worry about adapting arguments for built-ins that
653 // don't want that done. Skip adaption code by making it look
654 // like we have a match between expected and actual number of
655 // arguments.
656 definitely_matches = true;
657 } else {
658 movq(rbx, Immediate(expected.immediate()));
659 }
660 }
661 } else {
662 if (actual.is_immediate()) {
663 // Expected is in register, actual is immediate. This is the
664 // case when we invoke function values without going through the
665 // IC mechanism.
666 cmpq(expected.reg(), Immediate(actual.immediate()));
667 j(equal, &invoke);
668 ASSERT(expected.reg().is(rbx));
669 movq(rax, Immediate(actual.immediate()));
670 } else if (!expected.reg().is(actual.reg())) {
671 // Both expected and actual are in (different) registers. This
672 // is the case when we invoke functions using call and apply.
673 cmpq(expected.reg(), actual.reg());
674 j(equal, &invoke);
675 ASSERT(actual.reg().is(rax));
676 ASSERT(expected.reg().is(rbx));
677 }
678 }
679
680 if (!definitely_matches) {
681 Handle<Code> adaptor =
682 Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
683 if (!code_constant.is_null()) {
684 movq(rdx, code_constant, RelocInfo::EMBEDDED_OBJECT);
685 addq(rdx, Immediate(Code::kHeaderSize - kHeapObjectTag));
686 } else if (!code_register.is(rdx)) {
687 movq(rdx, code_register);
688 }
689
690 movq(kScratchRegister, adaptor, RelocInfo::CODE_TARGET);
691 if (flag == CALL_FUNCTION) {
692 call(kScratchRegister);
693 jmp(done);
694 } else {
695 jmp(kScratchRegister);
696 }
697 bind(&invoke);
698 }
699}
700
701
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000702void MacroAssembler::InvokeCode(Register code,
703 const ParameterCount& expected,
704 const ParameterCount& actual,
705 InvokeFlag flag) {
706 Label done;
707 InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag);
708 if (flag == CALL_FUNCTION) {
709 call(code);
710 } else {
711 ASSERT(flag == JUMP_FUNCTION);
712 jmp(code);
713 }
714 bind(&done);
715}
716
717
718void MacroAssembler::InvokeCode(Handle<Code> code,
719 const ParameterCount& expected,
720 const ParameterCount& actual,
721 RelocInfo::Mode rmode,
722 InvokeFlag flag) {
723 Label done;
724 Register dummy = rax;
725 InvokePrologue(expected, actual, code, dummy, &done, flag);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000726 if (flag == CALL_FUNCTION) {
ager@chromium.org3e875802009-06-29 08:26:34 +0000727 Call(code, rmode);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000728 } else {
729 ASSERT(flag == JUMP_FUNCTION);
ager@chromium.org3e875802009-06-29 08:26:34 +0000730 Jump(code, rmode);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000731 }
732 bind(&done);
733}
734
735
736void MacroAssembler::InvokeFunction(Register function,
737 const ParameterCount& actual,
738 InvokeFlag flag) {
739 ASSERT(function.is(rdi));
740 movq(rdx, FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
741 movq(rsi, FieldOperand(function, JSFunction::kContextOffset));
ager@chromium.org3e875802009-06-29 08:26:34 +0000742 movsxlq(rbx,
743 FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000744 movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000745 // Advances rdx to the end of the Code object header, to the start of
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000746 // the executable code.
747 lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
748
749 ParameterCount expected(rbx);
750 InvokeCode(rdx, expected, actual, flag);
751}
752
753
754void MacroAssembler::EnterFrame(StackFrame::Type type) {
755 push(rbp);
756 movq(rbp, rsp);
757 push(rsi); // Context.
758 push(Immediate(Smi::FromInt(type)));
759 movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
760 push(kScratchRegister);
761 if (FLAG_debug_code) {
762 movq(kScratchRegister,
763 Factory::undefined_value(),
764 RelocInfo::EMBEDDED_OBJECT);
765 cmpq(Operand(rsp, 0), kScratchRegister);
766 Check(not_equal, "code object not properly patched");
767 }
768}
769
770
771void MacroAssembler::LeaveFrame(StackFrame::Type type) {
772 if (FLAG_debug_code) {
773 movq(kScratchRegister, Immediate(Smi::FromInt(type)));
774 cmpq(Operand(rbp, StandardFrameConstants::kMarkerOffset), kScratchRegister);
775 Check(equal, "stack frame types must match");
776 }
777 movq(rsp, rbp);
778 pop(rbp);
779}
780
781
782
783void MacroAssembler::EnterExitFrame(StackFrame::Type type) {
784 ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG);
785
786 // Setup the frame structure on the stack.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000787 // All constants are relative to the frame pointer of the exit frame.
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000788 ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize);
789 ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize);
790 ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize);
791 push(rbp);
792 movq(rbp, rsp);
793
794 // Reserve room for entry stack pointer and push the debug marker.
795 ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
796 push(Immediate(0)); // saved entry sp, patched before call
797 push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0));
798
799 // Save the frame pointer and the context in top.
800 ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
801 ExternalReference context_address(Top::k_context_address);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000802 movq(r14, rax); // Backup rax before we use it.
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000803
804 movq(rax, rbp);
805 store_rax(c_entry_fp_address);
806 movq(rax, rsi);
807 store_rax(context_address);
808
809 // Setup argv in callee-saved register r15. It is reused in LeaveExitFrame,
810 // so it must be retained across the C-call.
811 int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000812 lea(r15, Operand(rbp, r14, times_pointer_size, offset));
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000813
814#ifdef ENABLE_DEBUGGER_SUPPORT
815 // Save the state of all registers to the stack from the memory
816 // location. This is needed to allow nested break points.
817 if (type == StackFrame::EXIT_DEBUG) {
818 // TODO(1243899): This should be symmetric to
819 // CopyRegistersFromStackToMemory() but it isn't! esp is assumed
820 // correct here, but computed for the other call. Very error
821 // prone! FIX THIS. Actually there are deeper problems with
822 // register saving than this asymmetry (see the bug report
823 // associated with this issue).
824 PushRegistersFromMemory(kJSCallerSaved);
825 }
826#endif
827
828 // Reserve space for two arguments: argc and argv
829 subq(rsp, Immediate(2 * kPointerSize));
830
831 // Get the required frame alignment for the OS.
832 static const int kFrameAlignment = OS::ActivationFrameAlignment();
833 if (kFrameAlignment > 0) {
834 ASSERT(IsPowerOf2(kFrameAlignment));
835 movq(kScratchRegister, Immediate(-kFrameAlignment));
836 and_(rsp, kScratchRegister);
837 }
838
839 // Patch the saved entry sp.
840 movq(Operand(rbp, ExitFrameConstants::kSPOffset), rsp);
841}
842
843
844void MacroAssembler::LeaveExitFrame(StackFrame::Type type) {
845 // Registers:
846 // r15 : argv
847#ifdef ENABLE_DEBUGGER_SUPPORT
848 // Restore the memory copy of the registers by digging them out from
849 // the stack. This is needed to allow nested break points.
850 if (type == StackFrame::EXIT_DEBUG) {
851 // It's okay to clobber register ebx below because we don't need
852 // the function pointer after this.
853 const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
854 int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize;
855 lea(rbx, Operand(rbp, kOffset));
856 CopyRegistersFromStackToMemory(rbx, rcx, kJSCallerSaved);
857 }
858#endif
859
860 // Get the return address from the stack and restore the frame pointer.
861 movq(rcx, Operand(rbp, 1 * kPointerSize));
862 movq(rbp, Operand(rbp, 0 * kPointerSize));
863
864 // Pop the arguments and the receiver from the caller stack.
865 lea(rsp, Operand(r15, 1 * kPointerSize));
866
867 // Restore current context from top and clear it in debug mode.
868 ExternalReference context_address(Top::k_context_address);
869 movq(kScratchRegister, context_address);
870 movq(rsi, Operand(kScratchRegister, 0));
871#ifdef DEBUG
872 movq(Operand(kScratchRegister, 0), Immediate(0));
873#endif
874
875 // Push the return address to get ready to return.
876 push(rcx);
877
878 // Clear the top frame.
879 ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
880 movq(kScratchRegister, c_entry_fp_address);
881 movq(Operand(kScratchRegister, 0), Immediate(0));
882}
883
884
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000885} } // namespace v8::internal