blob: 104ccb8c155f5e36c04bb550d603fcfcf1c5a6fa [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"
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000034#include "serialize.h"
ager@chromium.orgeadaf222009-06-16 09:43:10 +000035#include "debug.h"
kasperl@chromium.org71affb52009-05-26 05:44:31 +000036
37namespace v8 {
38namespace internal {
39
40MacroAssembler::MacroAssembler(void* buffer, int size)
41 : Assembler(buffer, size),
42 unresolved_(0),
43 generating_stub_(false),
44 allow_stub_calls_(true),
45 code_object_(Heap::undefined_value()) {
46}
47
ager@chromium.orge2902be2009-06-08 12:21:35 +000048
ager@chromium.org18ad94b2009-09-02 08:22:29 +000049void MacroAssembler::LoadRoot(Register destination,
50 Heap::RootListIndex index) {
51 movq(destination, Operand(r13, index << kPointerSizeLog2));
52}
53
54
55void MacroAssembler::PushRoot(Heap::RootListIndex index) {
56 push(Operand(r13, index << kPointerSizeLog2));
57}
58
59
60void MacroAssembler::CompareRoot(Register with,
61 Heap::RootListIndex index) {
62 cmpq(with, Operand(r13, index << kPointerSizeLog2));
63}
64
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000065
66static void RecordWriteHelper(MacroAssembler* masm,
67 Register object,
68 Register addr,
69 Register scratch) {
70 Label fast;
71
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000072 // Compute the page start address from the heap object pointer, and reuse
73 // the 'object' register for it.
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000074 ASSERT(is_int32(~Page::kPageAlignmentMask));
75 masm->and_(object,
76 Immediate(static_cast<int32_t>(~Page::kPageAlignmentMask)));
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000077 Register page_start = object;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000078
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000079 // Compute the bit addr in the remembered set/index of the pointer in the
80 // page. Reuse 'addr' as pointer_offset.
81 masm->subq(addr, page_start);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000082 masm->shr(addr, Immediate(kPointerSizeLog2));
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000083 Register pointer_offset = addr;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000084
85 // If the bit offset lies beyond the normal remembered set range, it is in
86 // the extra remembered set area of a large object.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000087 masm->cmpq(pointer_offset, Immediate(Page::kPageSize / kPointerSize));
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000088 masm->j(less, &fast);
89
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000090 // Adjust 'page_start' so that addressing using 'pointer_offset' hits the
91 // extra remembered set after the large object.
92
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000093 // Load the array length into 'scratch'.
94 masm->movl(scratch,
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000095 Operand(page_start,
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000096 Page::kObjectStartOffset + FixedArray::kLengthOffset));
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000097 Register array_length = scratch;
98
99 // Extra remembered set starts right after the large object (a FixedArray), at
100 // page_start + kObjectStartOffset + objectSize
101 // where objectSize is FixedArray::kHeaderSize + kPointerSize * array_length.
102 // Add the delta between the end of the normal RSet and the start of the
103 // extra RSet to 'page_start', so that addressing the bit using
104 // 'pointer_offset' hits the extra RSet words.
105 masm->lea(page_start,
106 Operand(page_start, array_length, times_pointer_size,
107 Page::kObjectStartOffset + FixedArray::kHeaderSize
108 - Page::kRSetEndOffset));
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000109
110 // NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
111 // to limit code size. We should probably evaluate this decision by
112 // measuring the performance of an equivalent implementation using
113 // "simpler" instructions
114 masm->bind(&fast);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000115 masm->bts(Operand(page_start, Page::kRSetOffset), pointer_offset);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000116}
117
118
119class RecordWriteStub : public CodeStub {
120 public:
121 RecordWriteStub(Register object, Register addr, Register scratch)
122 : object_(object), addr_(addr), scratch_(scratch) { }
123
124 void Generate(MacroAssembler* masm);
125
126 private:
127 Register object_;
128 Register addr_;
129 Register scratch_;
130
131#ifdef DEBUG
132 void Print() {
133 PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
134 object_.code(), addr_.code(), scratch_.code());
135 }
136#endif
137
138 // Minor key encoding in 12 bits of three registers (object, address and
139 // scratch) OOOOAAAASSSS.
140 class ScratchBits: public BitField<uint32_t, 0, 4> {};
141 class AddressBits: public BitField<uint32_t, 4, 4> {};
142 class ObjectBits: public BitField<uint32_t, 8, 4> {};
143
144 Major MajorKey() { return RecordWrite; }
145
146 int MinorKey() {
147 // Encode the registers.
148 return ObjectBits::encode(object_.code()) |
149 AddressBits::encode(addr_.code()) |
150 ScratchBits::encode(scratch_.code());
151 }
152};
153
154
155void RecordWriteStub::Generate(MacroAssembler* masm) {
156 RecordWriteHelper(masm, object_, addr_, scratch_);
157 masm->ret(0);
158}
159
160
161// Set the remembered set bit for [object+offset].
162// object is the object being stored into, value is the object being stored.
163// If offset is zero, then the scratch register contains the array index into
164// the elements array represented as a Smi.
165// All registers are clobbered by the operation.
166void MacroAssembler::RecordWrite(Register object,
167 int offset,
168 Register value,
169 Register scratch) {
170 // First, check if a remembered set write is even needed. The tests below
171 // catch stores of Smis and stores into young gen (which does not have space
172 // for the remembered set bits.
173 Label done;
174
175 // Test that the object address is not in the new space. We cannot
176 // set remembered set bits in the new space.
177 movq(value, object);
178 ASSERT(is_int32(static_cast<int64_t>(Heap::NewSpaceMask())));
179 and_(value, Immediate(static_cast<int32_t>(Heap::NewSpaceMask())));
180 movq(kScratchRegister, ExternalReference::new_space_start());
181 cmpq(value, kScratchRegister);
182 j(equal, &done);
183
184 if ((offset > 0) && (offset < Page::kMaxHeapObjectSize)) {
185 // Compute the bit offset in the remembered set, leave it in 'value'.
186 lea(value, Operand(object, offset));
187 ASSERT(is_int32(Page::kPageAlignmentMask));
188 and_(value, Immediate(static_cast<int32_t>(Page::kPageAlignmentMask)));
189 shr(value, Immediate(kObjectAlignmentBits));
190
191 // Compute the page address from the heap object pointer, leave it in
192 // 'object' (immediate value is sign extended).
193 and_(object, Immediate(~Page::kPageAlignmentMask));
194
195 // NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
196 // to limit code size. We should probably evaluate this decision by
197 // measuring the performance of an equivalent implementation using
198 // "simpler" instructions
199 bts(Operand(object, Page::kRSetOffset), value);
200 } else {
201 Register dst = scratch;
202 if (offset != 0) {
203 lea(dst, Operand(object, offset));
204 } else {
205 // array access: calculate the destination address in the same manner as
206 // KeyedStoreIC::GenerateGeneric. Multiply a smi by 4 to get an offset
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000207 // into an array of pointers.
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000208 lea(dst, Operand(object, dst, times_half_pointer_size,
209 FixedArray::kHeaderSize - kHeapObjectTag));
210 }
211 // If we are already generating a shared stub, not inlining the
212 // record write code isn't going to save us any memory.
213 if (generating_stub()) {
214 RecordWriteHelper(this, object, dst, value);
215 } else {
216 RecordWriteStub stub(object, dst, value);
217 CallStub(&stub);
218 }
219 }
220
221 bind(&done);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000222}
223
224
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000225void MacroAssembler::Assert(Condition cc, const char* msg) {
226 if (FLAG_debug_code) Check(cc, msg);
227}
228
229
230void MacroAssembler::Check(Condition cc, const char* msg) {
231 Label L;
232 j(cc, &L);
233 Abort(msg);
234 // will not return here
235 bind(&L);
236}
237
238
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000239void MacroAssembler::NegativeZeroTest(Register result,
240 Register op,
241 Label* then_label) {
242 Label ok;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000243 testl(result, result);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000244 j(not_zero, &ok);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000245 testl(op, op);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000246 j(sign, then_label);
247 bind(&ok);
248}
249
250
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000251void MacroAssembler::Abort(const char* msg) {
252 // We want to pass the msg string like a smi to avoid GC
253 // problems, however msg is not guaranteed to be aligned
254 // properly. Instead, we pass an aligned pointer that is
255 // a proper v8 smi, but also pass the alignment difference
256 // from the real pointer as a smi.
257 intptr_t p1 = reinterpret_cast<intptr_t>(msg);
258 intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
259 // Note: p0 might not be a valid Smi *value*, but it has a valid Smi tag.
260 ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
261#ifdef DEBUG
262 if (msg != NULL) {
263 RecordComment("Abort message: ");
264 RecordComment(msg);
265 }
266#endif
267 push(rax);
268 movq(kScratchRegister, p0, RelocInfo::NONE);
269 push(kScratchRegister);
270 movq(kScratchRegister,
271 reinterpret_cast<intptr_t>(Smi::FromInt(p1 - p0)),
272 RelocInfo::NONE);
273 push(kScratchRegister);
274 CallRuntime(Runtime::kAbort, 2);
275 // will not return here
276}
277
278
279void MacroAssembler::CallStub(CodeStub* stub) {
280 ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000281 Call(stub->GetCode(), RelocInfo::CODE_TARGET);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000282}
283
284
285void MacroAssembler::StubReturn(int argc) {
286 ASSERT(argc >= 1 && generating_stub());
287 ret((argc - 1) * kPointerSize);
288}
289
290
291void MacroAssembler::IllegalOperation(int num_arguments) {
292 if (num_arguments > 0) {
293 addq(rsp, Immediate(num_arguments * kPointerSize));
294 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000295 LoadRoot(rax, Heap::kUndefinedValueRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000296}
297
298
299void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
300 CallRuntime(Runtime::FunctionForId(id), num_arguments);
301}
302
303
304void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) {
305 // If the expected number of arguments of the runtime function is
306 // constant, we check that the actual number of arguments match the
307 // expectation.
308 if (f->nargs >= 0 && f->nargs != num_arguments) {
309 IllegalOperation(num_arguments);
310 return;
311 }
312
313 Runtime::FunctionId function_id =
314 static_cast<Runtime::FunctionId>(f->stub_id);
315 RuntimeStub stub(function_id, num_arguments);
316 CallStub(&stub);
317}
318
319
320void MacroAssembler::TailCallRuntime(ExternalReference const& ext,
ager@chromium.orga1645e22009-09-09 19:27:10 +0000321 int num_arguments,
322 int result_size) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000323 // ----------- S t a t e -------------
324 // -- rsp[0] : return address
325 // -- rsp[8] : argument num_arguments - 1
326 // ...
327 // -- rsp[8 * num_arguments] : argument 0 (receiver)
328 // -----------------------------------
329
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000330 // TODO(1236192): Most runtime routines don't need the number of
331 // arguments passed in because it is constant. At some point we
332 // should remove this need and make the runtime routine entry code
333 // smarter.
334 movq(rax, Immediate(num_arguments));
ager@chromium.orga1645e22009-09-09 19:27:10 +0000335 JumpToBuiltin(ext, result_size);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000336}
337
338
ager@chromium.orga1645e22009-09-09 19:27:10 +0000339void MacroAssembler::JumpToBuiltin(const ExternalReference& ext,
340 int result_size) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000341 // Set the entry point and jump to the C entry runtime stub.
342 movq(rbx, ext);
ager@chromium.orga1645e22009-09-09 19:27:10 +0000343 CEntryStub ces(result_size);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000344 movq(kScratchRegister, ces.GetCode(), RelocInfo::CODE_TARGET);
345 jmp(kScratchRegister);
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000346}
347
ager@chromium.orge2902be2009-06-08 12:21:35 +0000348
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000349void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
350 bool resolved;
351 Handle<Code> code = ResolveBuiltin(id, &resolved);
352
353 const char* name = Builtins::GetName(id);
354 int argc = Builtins::GetArgumentsCount(id);
355
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000356 movq(target, code, RelocInfo::EMBEDDED_OBJECT);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000357 if (!resolved) {
358 uint32_t flags =
359 Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
360 Bootstrapper::FixupFlagsIsPCRelative::encode(false) |
361 Bootstrapper::FixupFlagsUseCodeObject::encode(true);
362 Unresolved entry = { pc_offset() - sizeof(intptr_t), flags, name };
363 unresolved_.Add(entry);
364 }
365 addq(target, Immediate(Code::kHeaderSize - kHeapObjectTag));
366}
367
368
369Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
370 bool* resolved) {
371 // Move the builtin function into the temporary function slot by
372 // reading it from the builtins object. NOTE: We should be able to
373 // reduce this to two instructions by putting the function table in
374 // the global object instead of the "builtins" object and by using a
375 // real register for the function.
376 movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
377 movq(rdx, FieldOperand(rdx, GlobalObject::kBuiltinsOffset));
378 int builtins_offset =
379 JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
380 movq(rdi, FieldOperand(rdx, builtins_offset));
381
382
383 return Builtins::GetCode(id, resolved);
384}
385
386
ager@chromium.orge2902be2009-06-08 12:21:35 +0000387void MacroAssembler::Set(Register dst, int64_t x) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000388 if (x == 0) {
389 xor_(dst, dst);
390 } else if (is_int32(x)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000391 movq(dst, Immediate(x));
392 } else if (is_uint32(x)) {
393 movl(dst, Immediate(x));
394 } else {
395 movq(dst, x, RelocInfo::NONE);
396 }
397}
398
399
400void MacroAssembler::Set(const Operand& dst, int64_t x) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000401 if (x == 0) {
402 xor_(kScratchRegister, kScratchRegister);
403 movq(dst, kScratchRegister);
404 } else if (is_int32(x)) {
405 movq(dst, Immediate(x));
ager@chromium.orge2902be2009-06-08 12:21:35 +0000406 } else if (is_uint32(x)) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000407 movl(dst, Immediate(x));
ager@chromium.orge2902be2009-06-08 12:21:35 +0000408 } else {
409 movq(kScratchRegister, x, RelocInfo::NONE);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000410 movq(dst, kScratchRegister);
ager@chromium.orge2902be2009-06-08 12:21:35 +0000411 }
ager@chromium.orge2902be2009-06-08 12:21:35 +0000412}
413
414
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000415bool MacroAssembler::IsUnsafeSmi(Smi* value) {
416 return false;
417}
418
419void MacroAssembler::LoadUnsafeSmi(Register dst, Smi* source) {
420 UNIMPLEMENTED();
421}
422
423
424void MacroAssembler::Move(Register dst, Handle<Object> source) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000425 ASSERT(!source->IsFailure());
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000426 if (source->IsSmi()) {
427 if (IsUnsafeSmi(source)) {
428 LoadUnsafeSmi(dst, source);
429 } else {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000430 int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
431 movq(dst, Immediate(smi));
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000432 }
433 } else {
434 movq(dst, source, RelocInfo::EMBEDDED_OBJECT);
435 }
436}
437
438
439void MacroAssembler::Move(const Operand& dst, Handle<Object> source) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000440 if (source->IsSmi()) {
441 int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
442 movq(dst, Immediate(smi));
443 } else {
444 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
445 movq(dst, kScratchRegister);
446 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000447}
448
449
450void MacroAssembler::Cmp(Register dst, Handle<Object> source) {
451 Move(kScratchRegister, source);
452 cmpq(dst, kScratchRegister);
453}
454
455
ager@chromium.org3e875802009-06-29 08:26:34 +0000456void MacroAssembler::Cmp(const Operand& dst, Handle<Object> source) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000457 if (source->IsSmi()) {
458 if (IsUnsafeSmi(source)) {
459 LoadUnsafeSmi(kScratchRegister, source);
460 cmpl(dst, kScratchRegister);
461 } else {
462 // For smi-comparison, it suffices to compare the low 32 bits.
463 int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
464 cmpl(dst, Immediate(smi));
465 }
466 } else {
467 ASSERT(source->IsHeapObject());
468 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
469 cmpq(dst, kScratchRegister);
470 }
ager@chromium.org3e875802009-06-29 08:26:34 +0000471}
472
473
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000474void MacroAssembler::Push(Handle<Object> source) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000475 if (source->IsSmi()) {
476 if (IsUnsafeSmi(source)) {
477 LoadUnsafeSmi(kScratchRegister, source);
478 push(kScratchRegister);
479 } else {
480 int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
481 push(Immediate(smi));
482 }
483 } else {
484 ASSERT(source->IsHeapObject());
485 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
486 push(kScratchRegister);
487 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000488}
489
490
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000491void MacroAssembler::Push(Smi* source) {
492 if (IsUnsafeSmi(source)) {
493 LoadUnsafeSmi(kScratchRegister, source);
494 push(kScratchRegister);
495 } else {
496 int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(source));
497 push(Immediate(smi));
498 }
499}
500
501
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000502void MacroAssembler::Jump(ExternalReference ext) {
503 movq(kScratchRegister, ext);
504 jmp(kScratchRegister);
505}
506
507
508void MacroAssembler::Jump(Address destination, RelocInfo::Mode rmode) {
509 movq(kScratchRegister, destination, rmode);
510 jmp(kScratchRegister);
511}
512
513
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000514void MacroAssembler::Jump(Handle<Code> code_object, RelocInfo::Mode rmode) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000515 ASSERT(RelocInfo::IsCodeTarget(rmode));
516 movq(kScratchRegister, code_object, rmode);
ager@chromium.org3e875802009-06-29 08:26:34 +0000517#ifdef DEBUG
518 Label target;
519 bind(&target);
520#endif
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000521 jmp(kScratchRegister);
ager@chromium.org3e875802009-06-29 08:26:34 +0000522#ifdef DEBUG
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000523 ASSERT_EQ(kPatchReturnSequenceLength,
ager@chromium.org3e875802009-06-29 08:26:34 +0000524 SizeOfCodeGeneratedSince(&target) + kPointerSize);
525#endif
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000526}
527
528
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000529void MacroAssembler::Call(ExternalReference ext) {
530 movq(kScratchRegister, ext);
531 call(kScratchRegister);
532}
533
534
535void MacroAssembler::Call(Address destination, RelocInfo::Mode rmode) {
536 movq(kScratchRegister, destination, rmode);
537 call(kScratchRegister);
538}
539
540
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000541void MacroAssembler::Call(Handle<Code> code_object, RelocInfo::Mode rmode) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000542 ASSERT(RelocInfo::IsCodeTarget(rmode));
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000543 WriteRecordedPositions();
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000544 movq(kScratchRegister, code_object, rmode);
545#ifdef DEBUG
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000546 // Patch target is kPointer size bytes *before* target label.
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000547 Label target;
548 bind(&target);
549#endif
550 call(kScratchRegister);
551#ifdef DEBUG
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000552 ASSERT_EQ(kPatchReturnSequenceLength,
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000553 SizeOfCodeGeneratedSince(&target) + kPointerSize);
554#endif
555}
556
557
ager@chromium.orge2902be2009-06-08 12:21:35 +0000558void MacroAssembler::PushTryHandler(CodeLocation try_location,
559 HandlerType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000560 // Adjust this code if not the case.
561 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
562
563 // The pc (return address) is already on TOS. This code pushes state,
564 // frame pointer and current handler. Check that they are expected
565 // next on the stack, in that order.
ager@chromium.orge2902be2009-06-08 12:21:35 +0000566 ASSERT_EQ(StackHandlerConstants::kStateOffset,
567 StackHandlerConstants::kPCOffset - kPointerSize);
ager@chromium.orge2902be2009-06-08 12:21:35 +0000568 ASSERT_EQ(StackHandlerConstants::kFPOffset,
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000569 StackHandlerConstants::kStateOffset - kPointerSize);
570 ASSERT_EQ(StackHandlerConstants::kNextOffset,
ager@chromium.orge2902be2009-06-08 12:21:35 +0000571 StackHandlerConstants::kFPOffset - kPointerSize);
572
573 if (try_location == IN_JAVASCRIPT) {
574 if (type == TRY_CATCH_HANDLER) {
575 push(Immediate(StackHandler::TRY_CATCH));
576 } else {
577 push(Immediate(StackHandler::TRY_FINALLY));
578 }
ager@chromium.orge2902be2009-06-08 12:21:35 +0000579 push(rbp);
ager@chromium.orge2902be2009-06-08 12:21:35 +0000580 } else {
581 ASSERT(try_location == IN_JS_ENTRY);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000582 // The frame pointer does not point to a JS frame so we save NULL
583 // for rbp. We expect the code throwing an exception to check rbp
584 // before dereferencing it to restore the context.
ager@chromium.orge2902be2009-06-08 12:21:35 +0000585 push(Immediate(StackHandler::ENTRY));
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000586 push(Immediate(0)); // NULL frame pointer.
ager@chromium.orge2902be2009-06-08 12:21:35 +0000587 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000588 // Save the current handler.
ager@chromium.orge2902be2009-06-08 12:21:35 +0000589 movq(kScratchRegister, ExternalReference(Top::k_handler_address));
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000590 push(Operand(kScratchRegister, 0));
ager@chromium.orge2902be2009-06-08 12:21:35 +0000591 // Link this handler.
592 movq(Operand(kScratchRegister, 0), rsp);
593}
594
595
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000596void MacroAssembler::Ret() {
597 ret(0);
598}
599
600
ager@chromium.org3e875802009-06-29 08:26:34 +0000601void MacroAssembler::FCmp() {
602 fcompp();
603 push(rax);
604 fnstsw_ax();
ager@chromium.org532c4972009-09-01 16:23:26 +0000605 if (CpuFeatures::IsSupported(CpuFeatures::SAHF)) {
606 sahf();
607 } else {
608 shrl(rax, Immediate(8));
609 and_(rax, Immediate(0xFF));
610 push(rax);
611 popfq();
612 }
ager@chromium.org3e875802009-06-29 08:26:34 +0000613 pop(rax);
614}
615
616
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000617void MacroAssembler::CmpObjectType(Register heap_object,
618 InstanceType type,
619 Register map) {
620 movq(map, FieldOperand(heap_object, HeapObject::kMapOffset));
621 CmpInstanceType(map, type);
622}
623
624
625void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
626 cmpb(FieldOperand(map, Map::kInstanceTypeOffset),
627 Immediate(static_cast<int8_t>(type)));
628}
629
630
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000631void MacroAssembler::TryGetFunctionPrototype(Register function,
632 Register result,
633 Label* miss) {
634 // Check that the receiver isn't a smi.
635 testl(function, Immediate(kSmiTagMask));
636 j(zero, miss);
637
638 // Check that the function really is a function.
639 CmpObjectType(function, JS_FUNCTION_TYPE, result);
640 j(not_equal, miss);
641
642 // Make sure that the function has an instance prototype.
643 Label non_instance;
644 testb(FieldOperand(result, Map::kBitFieldOffset),
645 Immediate(1 << Map::kHasNonInstancePrototype));
646 j(not_zero, &non_instance);
647
648 // Get the prototype or initial map from the function.
649 movq(result,
650 FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
651
652 // If the prototype or initial map is the hole, don't return it and
653 // simply miss the cache instead. This will allow us to allocate a
654 // prototype object on-demand in the runtime system.
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000655 CompareRoot(result, Heap::kTheHoleValueRootIndex);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000656 j(equal, miss);
657
658 // If the function does not have an initial map, we're done.
659 Label done;
660 CmpObjectType(result, MAP_TYPE, kScratchRegister);
661 j(not_equal, &done);
662
663 // Get the prototype from the initial map.
664 movq(result, FieldOperand(result, Map::kPrototypeOffset));
665 jmp(&done);
666
667 // Non-instance prototype: Fetch prototype from constructor field
668 // in initial map.
669 bind(&non_instance);
670 movq(result, FieldOperand(result, Map::kConstructorOffset));
671
672 // All done.
673 bind(&done);
674}
675
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000676
677void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
678 if (FLAG_native_code_counters && counter->Enabled()) {
679 movq(kScratchRegister, ExternalReference(counter));
680 movl(Operand(kScratchRegister, 0), Immediate(value));
681 }
682}
683
684
685void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) {
686 ASSERT(value > 0);
687 if (FLAG_native_code_counters && counter->Enabled()) {
688 movq(kScratchRegister, ExternalReference(counter));
689 Operand operand(kScratchRegister, 0);
690 if (value == 1) {
691 incl(operand);
692 } else {
693 addl(operand, Immediate(value));
694 }
695 }
696}
697
698
699void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
700 ASSERT(value > 0);
701 if (FLAG_native_code_counters && counter->Enabled()) {
702 movq(kScratchRegister, ExternalReference(counter));
703 Operand operand(kScratchRegister, 0);
704 if (value == 1) {
705 decl(operand);
706 } else {
707 subl(operand, Immediate(value));
708 }
709 }
710}
711
712
713#ifdef ENABLE_DEBUGGER_SUPPORT
714
715void MacroAssembler::PushRegistersFromMemory(RegList regs) {
716 ASSERT((regs & ~kJSCallerSaved) == 0);
717 // Push the content of the memory location to the stack.
718 for (int i = 0; i < kNumJSCallerSaved; i++) {
719 int r = JSCallerSavedCode(i);
720 if ((regs & (1 << r)) != 0) {
721 ExternalReference reg_addr =
722 ExternalReference(Debug_Address::Register(i));
723 movq(kScratchRegister, reg_addr);
724 push(Operand(kScratchRegister, 0));
725 }
726 }
727}
728
729void MacroAssembler::SaveRegistersToMemory(RegList regs) {
730 ASSERT((regs & ~kJSCallerSaved) == 0);
731 // Copy the content of registers to memory location.
732 for (int i = 0; i < kNumJSCallerSaved; i++) {
733 int r = JSCallerSavedCode(i);
734 if ((regs & (1 << r)) != 0) {
735 Register reg = { r };
736 ExternalReference reg_addr =
737 ExternalReference(Debug_Address::Register(i));
738 movq(kScratchRegister, reg_addr);
739 movq(Operand(kScratchRegister, 0), reg);
740 }
741 }
742}
743
744
745void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
746 ASSERT((regs & ~kJSCallerSaved) == 0);
747 // Copy the content of memory location to registers.
748 for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
749 int r = JSCallerSavedCode(i);
750 if ((regs & (1 << r)) != 0) {
751 Register reg = { r };
752 ExternalReference reg_addr =
753 ExternalReference(Debug_Address::Register(i));
754 movq(kScratchRegister, reg_addr);
755 movq(reg, Operand(kScratchRegister, 0));
756 }
757 }
758}
759
760
761void MacroAssembler::PopRegistersToMemory(RegList regs) {
762 ASSERT((regs & ~kJSCallerSaved) == 0);
763 // Pop the content from the stack to the memory location.
764 for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
765 int r = JSCallerSavedCode(i);
766 if ((regs & (1 << r)) != 0) {
767 ExternalReference reg_addr =
768 ExternalReference(Debug_Address::Register(i));
769 movq(kScratchRegister, reg_addr);
770 pop(Operand(kScratchRegister, 0));
771 }
772 }
773}
774
775
776void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
777 Register scratch,
778 RegList regs) {
779 ASSERT(!scratch.is(kScratchRegister));
780 ASSERT(!base.is(kScratchRegister));
781 ASSERT(!base.is(scratch));
782 ASSERT((regs & ~kJSCallerSaved) == 0);
783 // Copy the content of the stack to the memory location and adjust base.
784 for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
785 int r = JSCallerSavedCode(i);
786 if ((regs & (1 << r)) != 0) {
787 movq(scratch, Operand(base, 0));
788 ExternalReference reg_addr =
789 ExternalReference(Debug_Address::Register(i));
790 movq(kScratchRegister, reg_addr);
791 movq(Operand(kScratchRegister, 0), scratch);
792 lea(base, Operand(base, kPointerSize));
793 }
794 }
795}
796
797#endif // ENABLE_DEBUGGER_SUPPORT
798
799
ager@chromium.org3e875802009-06-29 08:26:34 +0000800void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
801 bool resolved;
802 Handle<Code> code = ResolveBuiltin(id, &resolved);
803
804 // Calls are not allowed in some stubs.
805 ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
806
807 // Rely on the assertion to check that the number of provided
808 // arguments match the expected number of arguments. Fake a
809 // parameter count to avoid emitting code to do the check.
810 ParameterCount expected(0);
811 InvokeCode(Handle<Code>(code), expected, expected,
812 RelocInfo::CODE_TARGET, flag);
813
814 const char* name = Builtins::GetName(id);
815 int argc = Builtins::GetArgumentsCount(id);
816 // The target address for the jump is stored as an immediate at offset
817 // kInvokeCodeAddressOffset.
818 if (!resolved) {
819 uint32_t flags =
820 Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
kasperl@chromium.org68ac0092009-07-09 06:00:35 +0000821 Bootstrapper::FixupFlagsIsPCRelative::encode(false) |
ager@chromium.org3e875802009-06-29 08:26:34 +0000822 Bootstrapper::FixupFlagsUseCodeObject::encode(false);
823 Unresolved entry =
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000824 { pc_offset() - kPatchReturnSequenceLength, flags, name };
ager@chromium.org3e875802009-06-29 08:26:34 +0000825 unresolved_.Add(entry);
826 }
827}
828
829
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000830void MacroAssembler::InvokePrologue(const ParameterCount& expected,
831 const ParameterCount& actual,
832 Handle<Code> code_constant,
833 Register code_register,
834 Label* done,
835 InvokeFlag flag) {
836 bool definitely_matches = false;
837 Label invoke;
838 if (expected.is_immediate()) {
839 ASSERT(actual.is_immediate());
840 if (expected.immediate() == actual.immediate()) {
841 definitely_matches = true;
842 } else {
843 movq(rax, Immediate(actual.immediate()));
844 if (expected.immediate() ==
845 SharedFunctionInfo::kDontAdaptArgumentsSentinel) {
846 // Don't worry about adapting arguments for built-ins that
847 // don't want that done. Skip adaption code by making it look
848 // like we have a match between expected and actual number of
849 // arguments.
850 definitely_matches = true;
851 } else {
852 movq(rbx, Immediate(expected.immediate()));
853 }
854 }
855 } else {
856 if (actual.is_immediate()) {
857 // Expected is in register, actual is immediate. This is the
858 // case when we invoke function values without going through the
859 // IC mechanism.
860 cmpq(expected.reg(), Immediate(actual.immediate()));
861 j(equal, &invoke);
862 ASSERT(expected.reg().is(rbx));
863 movq(rax, Immediate(actual.immediate()));
864 } else if (!expected.reg().is(actual.reg())) {
865 // Both expected and actual are in (different) registers. This
866 // is the case when we invoke functions using call and apply.
867 cmpq(expected.reg(), actual.reg());
868 j(equal, &invoke);
869 ASSERT(actual.reg().is(rax));
870 ASSERT(expected.reg().is(rbx));
871 }
872 }
873
874 if (!definitely_matches) {
875 Handle<Code> adaptor =
876 Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
877 if (!code_constant.is_null()) {
878 movq(rdx, code_constant, RelocInfo::EMBEDDED_OBJECT);
879 addq(rdx, Immediate(Code::kHeaderSize - kHeapObjectTag));
880 } else if (!code_register.is(rdx)) {
881 movq(rdx, code_register);
882 }
883
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000884 if (flag == CALL_FUNCTION) {
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000885 Call(adaptor, RelocInfo::CODE_TARGET);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000886 jmp(done);
887 } else {
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000888 Jump(adaptor, RelocInfo::CODE_TARGET);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000889 }
890 bind(&invoke);
891 }
892}
893
894
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000895void MacroAssembler::InvokeCode(Register code,
896 const ParameterCount& expected,
897 const ParameterCount& actual,
898 InvokeFlag flag) {
899 Label done;
900 InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag);
901 if (flag == CALL_FUNCTION) {
902 call(code);
903 } else {
904 ASSERT(flag == JUMP_FUNCTION);
905 jmp(code);
906 }
907 bind(&done);
908}
909
910
911void MacroAssembler::InvokeCode(Handle<Code> code,
912 const ParameterCount& expected,
913 const ParameterCount& actual,
914 RelocInfo::Mode rmode,
915 InvokeFlag flag) {
916 Label done;
917 Register dummy = rax;
918 InvokePrologue(expected, actual, code, dummy, &done, flag);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000919 if (flag == CALL_FUNCTION) {
ager@chromium.org3e875802009-06-29 08:26:34 +0000920 Call(code, rmode);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000921 } else {
922 ASSERT(flag == JUMP_FUNCTION);
ager@chromium.org3e875802009-06-29 08:26:34 +0000923 Jump(code, rmode);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000924 }
925 bind(&done);
926}
927
928
929void MacroAssembler::InvokeFunction(Register function,
930 const ParameterCount& actual,
931 InvokeFlag flag) {
932 ASSERT(function.is(rdi));
933 movq(rdx, FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
934 movq(rsi, FieldOperand(function, JSFunction::kContextOffset));
ager@chromium.org3e875802009-06-29 08:26:34 +0000935 movsxlq(rbx,
936 FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000937 movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000938 // Advances rdx to the end of the Code object header, to the start of
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000939 // the executable code.
940 lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
941
942 ParameterCount expected(rbx);
943 InvokeCode(rdx, expected, actual, flag);
944}
945
946
947void MacroAssembler::EnterFrame(StackFrame::Type type) {
948 push(rbp);
949 movq(rbp, rsp);
950 push(rsi); // Context.
951 push(Immediate(Smi::FromInt(type)));
952 movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
953 push(kScratchRegister);
954 if (FLAG_debug_code) {
955 movq(kScratchRegister,
956 Factory::undefined_value(),
957 RelocInfo::EMBEDDED_OBJECT);
958 cmpq(Operand(rsp, 0), kScratchRegister);
959 Check(not_equal, "code object not properly patched");
960 }
961}
962
963
964void MacroAssembler::LeaveFrame(StackFrame::Type type) {
965 if (FLAG_debug_code) {
966 movq(kScratchRegister, Immediate(Smi::FromInt(type)));
967 cmpq(Operand(rbp, StandardFrameConstants::kMarkerOffset), kScratchRegister);
968 Check(equal, "stack frame types must match");
969 }
970 movq(rsp, rbp);
971 pop(rbp);
972}
973
974
975
ager@chromium.orga1645e22009-09-09 19:27:10 +0000976void MacroAssembler::EnterExitFrame(StackFrame::Type type, int result_size) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000977 ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG);
978
979 // Setup the frame structure on the stack.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000980 // All constants are relative to the frame pointer of the exit frame.
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000981 ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize);
982 ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize);
983 ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize);
984 push(rbp);
985 movq(rbp, rsp);
986
987 // Reserve room for entry stack pointer and push the debug marker.
988 ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
989 push(Immediate(0)); // saved entry sp, patched before call
990 push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0));
991
992 // Save the frame pointer and the context in top.
993 ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
994 ExternalReference context_address(Top::k_context_address);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000995 movq(r14, rax); // Backup rax before we use it.
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000996
997 movq(rax, rbp);
998 store_rax(c_entry_fp_address);
999 movq(rax, rsi);
1000 store_rax(context_address);
1001
1002 // Setup argv in callee-saved register r15. It is reused in LeaveExitFrame,
1003 // so it must be retained across the C-call.
1004 int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001005 lea(r15, Operand(rbp, r14, times_pointer_size, offset));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001006
1007#ifdef ENABLE_DEBUGGER_SUPPORT
1008 // Save the state of all registers to the stack from the memory
1009 // location. This is needed to allow nested break points.
1010 if (type == StackFrame::EXIT_DEBUG) {
1011 // TODO(1243899): This should be symmetric to
1012 // CopyRegistersFromStackToMemory() but it isn't! esp is assumed
1013 // correct here, but computed for the other call. Very error
1014 // prone! FIX THIS. Actually there are deeper problems with
1015 // register saving than this asymmetry (see the bug report
1016 // associated with this issue).
1017 PushRegistersFromMemory(kJSCallerSaved);
1018 }
1019#endif
1020
ager@chromium.orga1645e22009-09-09 19:27:10 +00001021#ifdef _WIN64
1022 // Reserve space on stack for result and argument structures, if necessary.
1023 int result_stack_space = (result_size < 2) ? 0 : result_size * kPointerSize;
1024 // Reserve space for the Arguments object. The Windows 64-bit ABI
1025 // requires us to pass this structure as a pointer to its location on
1026 // the stack. The structure contains 2 values.
1027 int argument_stack_space = 2 * kPointerSize;
1028 // We also need backing space for 4 parameters, even though
1029 // we only pass one or two parameter, and it is in a register.
1030 int argument_mirror_space = 4 * kPointerSize;
1031 int total_stack_space =
1032 argument_mirror_space + argument_stack_space + result_stack_space;
1033 subq(rsp, Immediate(total_stack_space));
1034#endif
1035
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001036 // Get the required frame alignment for the OS.
1037 static const int kFrameAlignment = OS::ActivationFrameAlignment();
1038 if (kFrameAlignment > 0) {
1039 ASSERT(IsPowerOf2(kFrameAlignment));
1040 movq(kScratchRegister, Immediate(-kFrameAlignment));
1041 and_(rsp, kScratchRegister);
1042 }
1043
1044 // Patch the saved entry sp.
1045 movq(Operand(rbp, ExitFrameConstants::kSPOffset), rsp);
1046}
1047
1048
ager@chromium.orga1645e22009-09-09 19:27:10 +00001049void MacroAssembler::LeaveExitFrame(StackFrame::Type type, int result_size) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001050 // Registers:
1051 // r15 : argv
1052#ifdef ENABLE_DEBUGGER_SUPPORT
1053 // Restore the memory copy of the registers by digging them out from
1054 // the stack. This is needed to allow nested break points.
1055 if (type == StackFrame::EXIT_DEBUG) {
ager@chromium.orga1645e22009-09-09 19:27:10 +00001056 // It's okay to clobber register rbx below because we don't need
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001057 // the function pointer after this.
1058 const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
1059 int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize;
1060 lea(rbx, Operand(rbp, kOffset));
1061 CopyRegistersFromStackToMemory(rbx, rcx, kJSCallerSaved);
1062 }
1063#endif
1064
1065 // Get the return address from the stack and restore the frame pointer.
1066 movq(rcx, Operand(rbp, 1 * kPointerSize));
1067 movq(rbp, Operand(rbp, 0 * kPointerSize));
1068
ager@chromium.orga1645e22009-09-09 19:27:10 +00001069#ifdef _WIN64
1070 // If return value is on the stack, pop it to registers.
1071 if (result_size > 1) {
1072 ASSERT_EQ(2, result_size);
1073 // Position above 4 argument mirrors and arguments object.
1074 movq(rax, Operand(rsp, 6 * kPointerSize));
1075 movq(rdx, Operand(rsp, 7 * kPointerSize));
1076 }
1077#endif
1078
1079 // Pop everything up to and including the arguments and the receiver
1080 // from the caller stack.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001081 lea(rsp, Operand(r15, 1 * kPointerSize));
1082
1083 // Restore current context from top and clear it in debug mode.
1084 ExternalReference context_address(Top::k_context_address);
1085 movq(kScratchRegister, context_address);
1086 movq(rsi, Operand(kScratchRegister, 0));
1087#ifdef DEBUG
1088 movq(Operand(kScratchRegister, 0), Immediate(0));
1089#endif
1090
1091 // Push the return address to get ready to return.
1092 push(rcx);
1093
1094 // Clear the top frame.
1095 ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
1096 movq(kScratchRegister, c_entry_fp_address);
1097 movq(Operand(kScratchRegister, 0), Immediate(0));
1098}
1099
1100
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001101Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
1102 JSObject* holder, Register holder_reg,
1103 Register scratch,
1104 Label* miss) {
1105 // Make sure there's no overlap between scratch and the other
1106 // registers.
1107 ASSERT(!scratch.is(object_reg) && !scratch.is(holder_reg));
1108
1109 // Keep track of the current object in register reg. On the first
1110 // iteration, reg is an alias for object_reg, on later iterations,
1111 // it is an alias for holder_reg.
1112 Register reg = object_reg;
1113 int depth = 1;
1114
1115 // Check the maps in the prototype chain.
1116 // Traverse the prototype chain from the object and do map checks.
1117 while (object != holder) {
1118 depth++;
1119
1120 // Only global objects and objects that do not require access
1121 // checks are allowed in stubs.
1122 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
1123
1124 JSObject* prototype = JSObject::cast(object->GetPrototype());
1125 if (Heap::InNewSpace(prototype)) {
1126 // Get the map of the current object.
1127 movq(scratch, FieldOperand(reg, HeapObject::kMapOffset));
1128 Cmp(scratch, Handle<Map>(object->map()));
1129 // Branch on the result of the map check.
1130 j(not_equal, miss);
1131 // Check access rights to the global object. This has to happen
1132 // after the map check so that we know that the object is
1133 // actually a global object.
1134 if (object->IsJSGlobalProxy()) {
1135 CheckAccessGlobalProxy(reg, scratch, miss);
1136
1137 // Restore scratch register to be the map of the object.
1138 // We load the prototype from the map in the scratch register.
1139 movq(scratch, FieldOperand(reg, HeapObject::kMapOffset));
1140 }
1141 // The prototype is in new space; we cannot store a reference
1142 // to it in the code. Load it from the map.
1143 reg = holder_reg; // from now the object is in holder_reg
1144 movq(reg, FieldOperand(scratch, Map::kPrototypeOffset));
1145
1146 } else {
1147 // Check the map of the current object.
1148 Cmp(FieldOperand(reg, HeapObject::kMapOffset),
1149 Handle<Map>(object->map()));
1150 // Branch on the result of the map check.
1151 j(not_equal, miss);
1152 // Check access rights to the global object. This has to happen
1153 // after the map check so that we know that the object is
1154 // actually a global object.
1155 if (object->IsJSGlobalProxy()) {
1156 CheckAccessGlobalProxy(reg, scratch, miss);
1157 }
1158 // The prototype is in old space; load it directly.
1159 reg = holder_reg; // from now the object is in holder_reg
1160 Move(reg, Handle<JSObject>(prototype));
1161 }
1162
1163 // Go to the next object in the prototype chain.
1164 object = prototype;
1165 }
1166
1167 // Check the holder map.
1168 Cmp(FieldOperand(reg, HeapObject::kMapOffset),
1169 Handle<Map>(holder->map()));
1170 j(not_equal, miss);
1171
1172 // Log the check depth.
1173 LOG(IntEvent("check-maps-depth", depth));
1174
1175 // Perform security check for access to the global object and return
1176 // the holder register.
1177 ASSERT(object == holder);
1178 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
1179 if (object->IsJSGlobalProxy()) {
1180 CheckAccessGlobalProxy(reg, scratch, miss);
1181 }
1182 return reg;
1183}
1184
1185
1186
1187
1188void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
1189 Register scratch,
1190 Label* miss) {
1191 Label same_contexts;
1192
1193 ASSERT(!holder_reg.is(scratch));
1194 ASSERT(!scratch.is(kScratchRegister));
1195 // Load current lexical context from the stack frame.
1196 movq(scratch, Operand(rbp, StandardFrameConstants::kContextOffset));
1197
1198 // When generating debug code, make sure the lexical context is set.
1199 if (FLAG_debug_code) {
1200 cmpq(scratch, Immediate(0));
1201 Check(not_equal, "we should not have an empty lexical context");
1202 }
1203 // Load the global context of the current context.
1204 int offset = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
1205 movq(scratch, FieldOperand(scratch, offset));
1206 movq(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset));
1207
1208 // Check the context is a global context.
1209 if (FLAG_debug_code) {
1210 Cmp(FieldOperand(scratch, HeapObject::kMapOffset),
1211 Factory::global_context_map());
1212 Check(equal, "JSGlobalObject::global_context should be a global context.");
1213 }
1214
1215 // Check if both contexts are the same.
1216 cmpq(scratch, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
1217 j(equal, &same_contexts);
1218
1219 // Compare security tokens.
1220 // Check that the security token in the calling global object is
1221 // compatible with the security token in the receiving global
1222 // object.
1223
1224 // Check the context is a global context.
1225 if (FLAG_debug_code) {
1226 // Preserve original value of holder_reg.
1227 push(holder_reg);
1228 movq(holder_reg, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001229 CompareRoot(holder_reg, Heap::kNullValueRootIndex);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001230 Check(not_equal, "JSGlobalProxy::context() should not be null.");
1231
1232 // Read the first word and compare to global_context_map(),
1233 movq(holder_reg, FieldOperand(holder_reg, HeapObject::kMapOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001234 CompareRoot(holder_reg, Heap::kGlobalContextMapRootIndex);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001235 Check(equal, "JSGlobalObject::global_context should be a global context.");
1236 pop(holder_reg);
1237 }
1238
1239 movq(kScratchRegister,
1240 FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
1241 int token_offset = Context::kHeaderSize +
1242 Context::SECURITY_TOKEN_INDEX * kPointerSize;
1243 movq(scratch, FieldOperand(scratch, token_offset));
1244 cmpq(scratch, FieldOperand(kScratchRegister, token_offset));
1245 j(not_equal, miss);
1246
1247 bind(&same_contexts);
1248}
1249
1250
ager@chromium.orga1645e22009-09-09 19:27:10 +00001251void MacroAssembler::LoadAllocationTopHelper(Register result,
1252 Register result_end,
1253 Register scratch,
1254 AllocationFlags flags) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001255 ExternalReference new_space_allocation_top =
1256 ExternalReference::new_space_allocation_top_address();
1257
1258 // Just return if allocation top is already known.
ager@chromium.orga1645e22009-09-09 19:27:10 +00001259 if ((flags & RESULT_CONTAINS_TOP) != 0) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001260 // No use of scratch if allocation top is provided.
1261 ASSERT(scratch.is(no_reg));
ager@chromium.orga1645e22009-09-09 19:27:10 +00001262#ifdef DEBUG
1263 // Assert that result actually contains top on entry.
1264 movq(kScratchRegister, new_space_allocation_top);
1265 cmpq(result, Operand(kScratchRegister, 0));
1266 Check(equal, "Unexpected allocation top");
1267#endif
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001268 return;
1269 }
1270
1271 // Move address of new object to result. Use scratch register if available.
1272 if (scratch.is(no_reg)) {
1273 movq(kScratchRegister, new_space_allocation_top);
1274 movq(result, Operand(kScratchRegister, 0));
1275 } else {
1276 ASSERT(!scratch.is(result_end));
1277 movq(scratch, new_space_allocation_top);
1278 movq(result, Operand(scratch, 0));
1279 }
1280}
1281
1282
1283void MacroAssembler::UpdateAllocationTopHelper(Register result_end,
1284 Register scratch) {
1285 ExternalReference new_space_allocation_top =
1286 ExternalReference::new_space_allocation_top_address();
1287
1288 // Update new top.
1289 if (result_end.is(rax)) {
1290 // rax can be stored directly to a memory location.
1291 store_rax(new_space_allocation_top);
1292 } else {
1293 // Register required - use scratch provided if available.
1294 if (scratch.is(no_reg)) {
1295 movq(kScratchRegister, new_space_allocation_top);
1296 movq(Operand(kScratchRegister, 0), result_end);
1297 } else {
1298 movq(Operand(scratch, 0), result_end);
1299 }
1300 }
1301}
1302
1303
ager@chromium.orga1645e22009-09-09 19:27:10 +00001304void MacroAssembler::AllocateObjectInNewSpace(int object_size,
1305 Register result,
1306 Register result_end,
1307 Register scratch,
1308 Label* gc_required,
1309 AllocationFlags flags) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001310 ASSERT(!result.is(result_end));
1311
1312 // Load address of new object into result.
ager@chromium.orga1645e22009-09-09 19:27:10 +00001313 LoadAllocationTopHelper(result, result_end, scratch, flags);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001314
1315 // Calculate new top and bail out if new space is exhausted.
1316 ExternalReference new_space_allocation_limit =
1317 ExternalReference::new_space_allocation_limit_address();
1318 lea(result_end, Operand(result, object_size));
1319 movq(kScratchRegister, new_space_allocation_limit);
1320 cmpq(result_end, Operand(kScratchRegister, 0));
1321 j(above, gc_required);
1322
1323 // Update allocation top.
1324 UpdateAllocationTopHelper(result_end, scratch);
ager@chromium.orga1645e22009-09-09 19:27:10 +00001325
1326 // Tag the result if requested.
1327 if ((flags & TAG_OBJECT) != 0) {
1328 addq(result, Immediate(kHeapObjectTag));
1329 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001330}
1331
1332
ager@chromium.orga1645e22009-09-09 19:27:10 +00001333void MacroAssembler::AllocateObjectInNewSpace(int header_size,
1334 ScaleFactor element_size,
1335 Register element_count,
1336 Register result,
1337 Register result_end,
1338 Register scratch,
1339 Label* gc_required,
1340 AllocationFlags flags) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001341 ASSERT(!result.is(result_end));
1342
1343 // Load address of new object into result.
ager@chromium.orga1645e22009-09-09 19:27:10 +00001344 LoadAllocationTopHelper(result, result_end, scratch, flags);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001345
1346 // Calculate new top and bail out if new space is exhausted.
1347 ExternalReference new_space_allocation_limit =
1348 ExternalReference::new_space_allocation_limit_address();
1349 lea(result_end, Operand(result, element_count, element_size, header_size));
1350 movq(kScratchRegister, new_space_allocation_limit);
1351 cmpq(result_end, Operand(kScratchRegister, 0));
1352 j(above, gc_required);
1353
1354 // Update allocation top.
1355 UpdateAllocationTopHelper(result_end, scratch);
ager@chromium.orga1645e22009-09-09 19:27:10 +00001356
1357 // Tag the result if requested.
1358 if ((flags & TAG_OBJECT) != 0) {
1359 addq(result, Immediate(kHeapObjectTag));
1360 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001361}
1362
1363
ager@chromium.orga1645e22009-09-09 19:27:10 +00001364void MacroAssembler::AllocateObjectInNewSpace(Register object_size,
1365 Register result,
1366 Register result_end,
1367 Register scratch,
1368 Label* gc_required,
1369 AllocationFlags flags) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001370 // Load address of new object into result.
ager@chromium.orga1645e22009-09-09 19:27:10 +00001371 LoadAllocationTopHelper(result, result_end, scratch, flags);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001372
1373 // Calculate new top and bail out if new space is exhausted.
1374 ExternalReference new_space_allocation_limit =
1375 ExternalReference::new_space_allocation_limit_address();
1376 if (!object_size.is(result_end)) {
1377 movq(result_end, object_size);
1378 }
1379 addq(result_end, result);
1380 movq(kScratchRegister, new_space_allocation_limit);
1381 cmpq(result_end, Operand(kScratchRegister, 0));
1382 j(above, gc_required);
1383
1384 // Update allocation top.
1385 UpdateAllocationTopHelper(result_end, scratch);
ager@chromium.orga1645e22009-09-09 19:27:10 +00001386
1387 // Tag the result if requested.
1388 if ((flags & TAG_OBJECT) != 0) {
1389 addq(result, Immediate(kHeapObjectTag));
1390 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001391}
1392
1393
1394void MacroAssembler::UndoAllocationInNewSpace(Register object) {
1395 ExternalReference new_space_allocation_top =
1396 ExternalReference::new_space_allocation_top_address();
1397
1398 // Make sure the object has no tag before resetting top.
1399 and_(object, Immediate(~kHeapObjectTagMask));
1400 movq(kScratchRegister, new_space_allocation_top);
1401#ifdef DEBUG
1402 cmpq(object, Operand(kScratchRegister, 0));
1403 Check(below, "Undo allocation of non allocated memory");
1404#endif
1405 movq(Operand(kScratchRegister, 0), object);
1406}
1407
1408
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001409} } // namespace v8::internal