1 // Copyright 2011 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
28 #include "v8.h"
29
30 #if defined(V8_TARGET_ARCH_X64)
31
32 #include "bootstrapper.h"
33 #include "codegen.h"
34 #include "assembler-x64.h"
35 #include "macro-assembler-x64.h"
36 #include "serialize.h"
37 #include "debug.h"
38 #include "heap.h"
39
40 namespace v8 {
41 namespace internal {
42
MacroAssembler(Isolate * arg_isolate,void * buffer,int size)43 MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
44 : Assembler(arg_isolate, buffer, size),
45 generating_stub_(false),
46 allow_stub_calls_(true),
47 root_array_available_(true) {
48 if (isolate() != NULL) {
49 code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
50 isolate());
51 }
52 }
53
54
RootRegisterDelta(ExternalReference other,Isolate * isolate)55 static intptr_t RootRegisterDelta(ExternalReference other, Isolate* isolate) {
56 Address roots_register_value = kRootRegisterBias +
57 reinterpret_cast<Address>(isolate->heap()->roots_address());
58 intptr_t delta = other.address() - roots_register_value;
59 return delta;
60 }
61
62
ExternalOperand(ExternalReference target,Register scratch)63 Operand MacroAssembler::ExternalOperand(ExternalReference target,
64 Register scratch) {
65 if (root_array_available_ && !Serializer::enabled()) {
66 intptr_t delta = RootRegisterDelta(target, isolate());
67 if (is_int32(delta)) {
68 Serializer::TooLateToEnableNow();
69 return Operand(kRootRegister, static_cast<int32_t>(delta));
70 }
71 }
72 movq(scratch, target);
73 return Operand(scratch, 0);
74 }
75
76
Load(Register destination,ExternalReference source)77 void MacroAssembler::Load(Register destination, ExternalReference source) {
78 if (root_array_available_ && !Serializer::enabled()) {
79 intptr_t delta = RootRegisterDelta(source, isolate());
80 if (is_int32(delta)) {
81 Serializer::TooLateToEnableNow();
82 movq(destination, Operand(kRootRegister, static_cast<int32_t>(delta)));
83 return;
84 }
85 }
86 // Safe code.
87 if (destination.is(rax)) {
88 load_rax(source);
89 } else {
90 movq(kScratchRegister, source);
91 movq(destination, Operand(kScratchRegister, 0));
92 }
93 }
94
95
Store(ExternalReference destination,Register source)96 void MacroAssembler::Store(ExternalReference destination, Register source) {
97 if (root_array_available_ && !Serializer::enabled()) {
98 intptr_t delta = RootRegisterDelta(destination, isolate());
99 if (is_int32(delta)) {
100 Serializer::TooLateToEnableNow();
101 movq(Operand(kRootRegister, static_cast<int32_t>(delta)), source);
102 return;
103 }
104 }
105 // Safe code.
106 if (source.is(rax)) {
107 store_rax(destination);
108 } else {
109 movq(kScratchRegister, destination);
110 movq(Operand(kScratchRegister, 0), source);
111 }
112 }
113
114
LoadAddress(Register destination,ExternalReference source)115 void MacroAssembler::LoadAddress(Register destination,
116 ExternalReference source) {
117 if (root_array_available_ && !Serializer::enabled()) {
118 intptr_t delta = RootRegisterDelta(source, isolate());
119 if (is_int32(delta)) {
120 Serializer::TooLateToEnableNow();
121 lea(destination, Operand(kRootRegister, static_cast<int32_t>(delta)));
122 return;
123 }
124 }
125 // Safe code.
126 movq(destination, source);
127 }
128
129
LoadAddressSize(ExternalReference source)130 int MacroAssembler::LoadAddressSize(ExternalReference source) {
131 if (root_array_available_ && !Serializer::enabled()) {
132 // This calculation depends on the internals of LoadAddress.
133 // It's correctness is ensured by the asserts in the Call
134 // instruction below.
135 intptr_t delta = RootRegisterDelta(source, isolate());
136 if (is_int32(delta)) {
137 Serializer::TooLateToEnableNow();
138 // Operand is lea(scratch, Operand(kRootRegister, delta));
139 // Opcodes : REX.W 8D ModRM Disp8/Disp32 - 4 or 7.
140 int size = 4;
141 if (!is_int8(static_cast<int32_t>(delta))) {
142 size += 3; // Need full four-byte displacement in lea.
143 }
144 return size;
145 }
146 }
147 // Size of movq(destination, src);
148 return 10;
149 }
150
151
LoadRoot(Register destination,Heap::RootListIndex index)152 void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index) {
153 ASSERT(root_array_available_);
154 movq(destination, Operand(kRootRegister,
155 (index << kPointerSizeLog2) - kRootRegisterBias));
156 }
157
158
LoadRootIndexed(Register destination,Register variable_offset,int fixed_offset)159 void MacroAssembler::LoadRootIndexed(Register destination,
160 Register variable_offset,
161 int fixed_offset) {
162 ASSERT(root_array_available_);
163 movq(destination,
164 Operand(kRootRegister,
165 variable_offset, times_pointer_size,
166 (fixed_offset << kPointerSizeLog2) - kRootRegisterBias));
167 }
168
169
StoreRoot(Register source,Heap::RootListIndex index)170 void MacroAssembler::StoreRoot(Register source, Heap::RootListIndex index) {
171 ASSERT(root_array_available_);
172 movq(Operand(kRootRegister, (index << kPointerSizeLog2) - kRootRegisterBias),
173 source);
174 }
175
176
PushRoot(Heap::RootListIndex index)177 void MacroAssembler::PushRoot(Heap::RootListIndex index) {
178 ASSERT(root_array_available_);
179 push(Operand(kRootRegister, (index << kPointerSizeLog2) - kRootRegisterBias));
180 }
181
182
CompareRoot(Register with,Heap::RootListIndex index)183 void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) {
184 ASSERT(root_array_available_);
185 cmpq(with, Operand(kRootRegister,
186 (index << kPointerSizeLog2) - kRootRegisterBias));
187 }
188
189
CompareRoot(const Operand & with,Heap::RootListIndex index)190 void MacroAssembler::CompareRoot(const Operand& with,
191 Heap::RootListIndex index) {
192 ASSERT(root_array_available_);
193 ASSERT(!with.AddressUsesRegister(kScratchRegister));
194 LoadRoot(kScratchRegister, index);
195 cmpq(with, kScratchRegister);
196 }
197
198
RecordWriteHelper(Register object,Register addr,Register scratch)199 void MacroAssembler::RecordWriteHelper(Register object,
200 Register addr,
201 Register scratch) {
202 if (emit_debug_code()) {
203 // Check that the object is not in new space.
204 NearLabel not_in_new_space;
205 InNewSpace(object, scratch, not_equal, ¬_in_new_space);
206 Abort("new-space object passed to RecordWriteHelper");
207 bind(¬_in_new_space);
208 }
209
210 // Compute the page start address from the heap object pointer, and reuse
211 // the 'object' register for it.
212 and_(object, Immediate(~Page::kPageAlignmentMask));
213
214 // Compute number of region covering addr. See Page::GetRegionNumberForAddress
215 // method for more details.
216 shrl(addr, Immediate(Page::kRegionSizeLog2));
217 andl(addr, Immediate(Page::kPageAlignmentMask >> Page::kRegionSizeLog2));
218
219 // Set dirty mark for region.
220 bts(Operand(object, Page::kDirtyFlagOffset), addr);
221 }
222
223
RecordWrite(Register object,int offset,Register value,Register index)224 void MacroAssembler::RecordWrite(Register object,
225 int offset,
226 Register value,
227 Register index) {
228 // The compiled code assumes that record write doesn't change the
229 // context register, so we check that none of the clobbered
230 // registers are rsi.
231 ASSERT(!object.is(rsi) && !value.is(rsi) && !index.is(rsi));
232
233 // First, check if a write barrier is even needed. The tests below
234 // catch stores of smis and stores into the young generation.
235 Label done;
236 JumpIfSmi(value, &done);
237
238 RecordWriteNonSmi(object, offset, value, index);
239 bind(&done);
240
241 // Clobber all input registers when running with the debug-code flag
242 // turned on to provoke errors. This clobbering repeats the
243 // clobbering done inside RecordWriteNonSmi but it's necessary to
244 // avoid having the fast case for smis leave the registers
245 // unchanged.
246 if (emit_debug_code()) {
247 movq(object, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
248 movq(value, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
249 movq(index, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
250 }
251 }
252
253
RecordWrite(Register object,Register address,Register value)254 void MacroAssembler::RecordWrite(Register object,
255 Register address,
256 Register value) {
257 // The compiled code assumes that record write doesn't change the
258 // context register, so we check that none of the clobbered
259 // registers are rsi.
260 ASSERT(!object.is(rsi) && !value.is(rsi) && !address.is(rsi));
261
262 // First, check if a write barrier is even needed. The tests below
263 // catch stores of smis and stores into the young generation.
264 Label done;
265 JumpIfSmi(value, &done);
266
267 InNewSpace(object, value, equal, &done);
268
269 RecordWriteHelper(object, address, value);
270
271 bind(&done);
272
273 // Clobber all input registers when running with the debug-code flag
274 // turned on to provoke errors.
275 if (emit_debug_code()) {
276 movq(object, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
277 movq(address, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
278 movq(value, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
279 }
280 }
281
282
RecordWriteNonSmi(Register object,int offset,Register scratch,Register index)283 void MacroAssembler::RecordWriteNonSmi(Register object,
284 int offset,
285 Register scratch,
286 Register index) {
287 Label done;
288
289 if (emit_debug_code()) {
290 NearLabel okay;
291 JumpIfNotSmi(object, &okay);
292 Abort("MacroAssembler::RecordWriteNonSmi cannot deal with smis");
293 bind(&okay);
294
295 if (offset == 0) {
296 // index must be int32.
297 Register tmp = index.is(rax) ? rbx : rax;
298 push(tmp);
299 movl(tmp, index);
300 cmpq(tmp, index);
301 Check(equal, "Index register for RecordWrite must be untagged int32.");
302 pop(tmp);
303 }
304 }
305
306 // Test that the object address is not in the new space. We cannot
307 // update page dirty marks for new space pages.
308 InNewSpace(object, scratch, equal, &done);
309
310 // The offset is relative to a tagged or untagged HeapObject pointer,
311 // so either offset or offset + kHeapObjectTag must be a
312 // multiple of kPointerSize.
313 ASSERT(IsAligned(offset, kPointerSize) ||
314 IsAligned(offset + kHeapObjectTag, kPointerSize));
315
316 Register dst = index;
317 if (offset != 0) {
318 lea(dst, Operand(object, offset));
319 } else {
320 // array access: calculate the destination address in the same manner as
321 // KeyedStoreIC::GenerateGeneric.
322 lea(dst, FieldOperand(object,
323 index,
324 times_pointer_size,
325 FixedArray::kHeaderSize));
326 }
327 RecordWriteHelper(object, dst, scratch);
328
329 bind(&done);
330
331 // Clobber all input registers when running with the debug-code flag
332 // turned on to provoke errors.
333 if (emit_debug_code()) {
334 movq(object, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
335 movq(scratch, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
336 movq(index, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
337 }
338 }
339
Assert(Condition cc,const char * msg)340 void MacroAssembler::Assert(Condition cc, const char* msg) {
341 if (emit_debug_code()) Check(cc, msg);
342 }
343
344
AssertFastElements(Register elements)345 void MacroAssembler::AssertFastElements(Register elements) {
346 if (emit_debug_code()) {
347 NearLabel ok;
348 CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
349 Heap::kFixedArrayMapRootIndex);
350 j(equal, &ok);
351 CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
352 Heap::kFixedCOWArrayMapRootIndex);
353 j(equal, &ok);
354 Abort("JSObject with fast elements map has slow elements");
355 bind(&ok);
356 }
357 }
358
359
Check(Condition cc,const char * msg)360 void MacroAssembler::Check(Condition cc, const char* msg) {
361 NearLabel L;
362 j(cc, &L);
363 Abort(msg);
364 // will not return here
365 bind(&L);
366 }
367
368
CheckStackAlignment()369 void MacroAssembler::CheckStackAlignment() {
370 int frame_alignment = OS::ActivationFrameAlignment();
371 int frame_alignment_mask = frame_alignment - 1;
372 if (frame_alignment > kPointerSize) {
373 ASSERT(IsPowerOf2(frame_alignment));
374 NearLabel alignment_as_expected;
375 testq(rsp, Immediate(frame_alignment_mask));
376 j(zero, &alignment_as_expected);
377 // Abort if stack is not aligned.
378 int3();
379 bind(&alignment_as_expected);
380 }
381 }
382
383
NegativeZeroTest(Register result,Register op,Label * then_label)384 void MacroAssembler::NegativeZeroTest(Register result,
385 Register op,
386 Label* then_label) {
387 NearLabel ok;
388 testl(result, result);
389 j(not_zero, &ok);
390 testl(op, op);
391 j(sign, then_label);
392 bind(&ok);
393 }
394
395
Abort(const char * msg)396 void MacroAssembler::Abort(const char* msg) {
397 // We want to pass the msg string like a smi to avoid GC
398 // problems, however msg is not guaranteed to be aligned
399 // properly. Instead, we pass an aligned pointer that is
400 // a proper v8 smi, but also pass the alignment difference
401 // from the real pointer as a smi.
402 intptr_t p1 = reinterpret_cast<intptr_t>(msg);
403 intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
404 // Note: p0 might not be a valid Smi *value*, but it has a valid Smi tag.
405 ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
406 #ifdef DEBUG
407 if (msg != NULL) {
408 RecordComment("Abort message: ");
409 RecordComment(msg);
410 }
411 #endif
412 // Disable stub call restrictions to always allow calls to abort.
413 AllowStubCallsScope allow_scope(this, true);
414
415 push(rax);
416 movq(kScratchRegister, p0, RelocInfo::NONE);
417 push(kScratchRegister);
418 movq(kScratchRegister,
419 reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(p1 - p0))),
420 RelocInfo::NONE);
421 push(kScratchRegister);
422 CallRuntime(Runtime::kAbort, 2);
423 // will not return here
424 int3();
425 }
426
427
CallStub(CodeStub * stub)428 void MacroAssembler::CallStub(CodeStub* stub) {
429 ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
430 Call(stub->GetCode(), RelocInfo::CODE_TARGET);
431 }
432
433
TryCallStub(CodeStub * stub)434 MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub) {
435 ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs.
436 MaybeObject* result = stub->TryGetCode();
437 if (!result->IsFailure()) {
438 call(Handle<Code>(Code::cast(result->ToObjectUnchecked())),
439 RelocInfo::CODE_TARGET);
440 }
441 return result;
442 }
443
444
TailCallStub(CodeStub * stub)445 void MacroAssembler::TailCallStub(CodeStub* stub) {
446 ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs.
447 Jump(stub->GetCode(), RelocInfo::CODE_TARGET);
448 }
449
450
TryTailCallStub(CodeStub * stub)451 MaybeObject* MacroAssembler::TryTailCallStub(CodeStub* stub) {
452 ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs.
453 MaybeObject* result = stub->TryGetCode();
454 if (!result->IsFailure()) {
455 jmp(Handle<Code>(Code::cast(result->ToObjectUnchecked())),
456 RelocInfo::CODE_TARGET);
457 }
458 return result;
459 }
460
461
StubReturn(int argc)462 void MacroAssembler::StubReturn(int argc) {
463 ASSERT(argc >= 1 && generating_stub());
464 ret((argc - 1) * kPointerSize);
465 }
466
467
IllegalOperation(int num_arguments)468 void MacroAssembler::IllegalOperation(int num_arguments) {
469 if (num_arguments > 0) {
470 addq(rsp, Immediate(num_arguments * kPointerSize));
471 }
472 LoadRoot(rax, Heap::kUndefinedValueRootIndex);
473 }
474
475
IndexFromHash(Register hash,Register index)476 void MacroAssembler::IndexFromHash(Register hash, Register index) {
477 // The assert checks that the constants for the maximum number of digits
478 // for an array index cached in the hash field and the number of bits
479 // reserved for it does not conflict.
480 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
481 (1 << String::kArrayIndexValueBits));
482 // We want the smi-tagged index in key. Even if we subsequently go to
483 // the slow case, converting the key to a smi is always valid.
484 // key: string key
485 // hash: key's hash field, including its array index value.
486 and_(hash, Immediate(String::kArrayIndexValueMask));
487 shr(hash, Immediate(String::kHashShift));
488 // Here we actually clobber the key which will be used if calling into
489 // runtime later. However as the new key is the numeric value of a string key
490 // there is no difference in using either key.
491 Integer32ToSmi(index, hash);
492 }
493
494
CallRuntime(Runtime::FunctionId id,int num_arguments)495 void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
496 CallRuntime(Runtime::FunctionForId(id), num_arguments);
497 }
498
499
CallRuntimeSaveDoubles(Runtime::FunctionId id)500 void MacroAssembler::CallRuntimeSaveDoubles(Runtime::FunctionId id) {
501 const Runtime::Function* function = Runtime::FunctionForId(id);
502 Set(rax, function->nargs);
503 LoadAddress(rbx, ExternalReference(function, isolate()));
504 CEntryStub ces(1);
505 ces.SaveDoubles();
506 CallStub(&ces);
507 }
508
509
TryCallRuntime(Runtime::FunctionId id,int num_arguments)510 MaybeObject* MacroAssembler::TryCallRuntime(Runtime::FunctionId id,
511 int num_arguments) {
512 return TryCallRuntime(Runtime::FunctionForId(id), num_arguments);
513 }
514
515
CallRuntime(const Runtime::Function * f,int num_arguments)516 void MacroAssembler::CallRuntime(const Runtime::Function* f,
517 int num_arguments) {
518 // If the expected number of arguments of the runtime function is
519 // constant, we check that the actual number of arguments match the
520 // expectation.
521 if (f->nargs >= 0 && f->nargs != num_arguments) {
522 IllegalOperation(num_arguments);
523 return;
524 }
525
526 // TODO(1236192): Most runtime routines don't need the number of
527 // arguments passed in because it is constant. At some point we
528 // should remove this need and make the runtime routine entry code
529 // smarter.
530 Set(rax, num_arguments);
531 LoadAddress(rbx, ExternalReference(f, isolate()));
532 CEntryStub ces(f->result_size);
533 CallStub(&ces);
534 }
535
536
TryCallRuntime(const Runtime::Function * f,int num_arguments)537 MaybeObject* MacroAssembler::TryCallRuntime(const Runtime::Function* f,
538 int num_arguments) {
539 if (f->nargs >= 0 && f->nargs != num_arguments) {
540 IllegalOperation(num_arguments);
541 // Since we did not call the stub, there was no allocation failure.
542 // Return some non-failure object.
543 return HEAP->undefined_value();
544 }
545
546 // TODO(1236192): Most runtime routines don't need the number of
547 // arguments passed in because it is constant. At some point we
548 // should remove this need and make the runtime routine entry code
549 // smarter.
550 Set(rax, num_arguments);
551 LoadAddress(rbx, ExternalReference(f, isolate()));
552 CEntryStub ces(f->result_size);
553 return TryCallStub(&ces);
554 }
555
556
CallExternalReference(const ExternalReference & ext,int num_arguments)557 void MacroAssembler::CallExternalReference(const ExternalReference& ext,
558 int num_arguments) {
559 Set(rax, num_arguments);
560 LoadAddress(rbx, ext);
561
562 CEntryStub stub(1);
563 CallStub(&stub);
564 }
565
566
TailCallExternalReference(const ExternalReference & ext,int num_arguments,int result_size)567 void MacroAssembler::TailCallExternalReference(const ExternalReference& ext,
568 int num_arguments,
569 int result_size) {
570 // ----------- S t a t e -------------
571 // -- rsp[0] : return address
572 // -- rsp[8] : argument num_arguments - 1
573 // ...
574 // -- rsp[8 * num_arguments] : argument 0 (receiver)
575 // -----------------------------------
576
577 // TODO(1236192): Most runtime routines don't need the number of
578 // arguments passed in because it is constant. At some point we
579 // should remove this need and make the runtime routine entry code
580 // smarter.
581 Set(rax, num_arguments);
582 JumpToExternalReference(ext, result_size);
583 }
584
585
TryTailCallExternalReference(const ExternalReference & ext,int num_arguments,int result_size)586 MaybeObject* MacroAssembler::TryTailCallExternalReference(
587 const ExternalReference& ext, int num_arguments, int result_size) {
588 // ----------- S t a t e -------------
589 // -- rsp[0] : return address
590 // -- rsp[8] : argument num_arguments - 1
591 // ...
592 // -- rsp[8 * num_arguments] : argument 0 (receiver)
593 // -----------------------------------
594
595 // TODO(1236192): Most runtime routines don't need the number of
596 // arguments passed in because it is constant. At some point we
597 // should remove this need and make the runtime routine entry code
598 // smarter.
599 Set(rax, num_arguments);
600 return TryJumpToExternalReference(ext, result_size);
601 }
602
603
TailCallRuntime(Runtime::FunctionId fid,int num_arguments,int result_size)604 void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
605 int num_arguments,
606 int result_size) {
607 TailCallExternalReference(ExternalReference(fid, isolate()),
608 num_arguments,
609 result_size);
610 }
611
612
TryTailCallRuntime(Runtime::FunctionId fid,int num_arguments,int result_size)613 MaybeObject* MacroAssembler::TryTailCallRuntime(Runtime::FunctionId fid,
614 int num_arguments,
615 int result_size) {
616 return TryTailCallExternalReference(ExternalReference(fid, isolate()),
617 num_arguments,
618 result_size);
619 }
620
621
Offset(ExternalReference ref0,ExternalReference ref1)622 static int Offset(ExternalReference ref0, ExternalReference ref1) {
623 int64_t offset = (ref0.address() - ref1.address());
624 // Check that fits into int.
625 ASSERT(static_cast<int>(offset) == offset);
626 return static_cast<int>(offset);
627 }
628
629
PrepareCallApiFunction(int arg_stack_space)630 void MacroAssembler::PrepareCallApiFunction(int arg_stack_space) {
631 #ifdef _WIN64
632 // We need to prepare a slot for result handle on stack and put
633 // a pointer to it into 1st arg register.
634 EnterApiExitFrame(arg_stack_space + 1);
635
636 // rcx must be used to pass the pointer to the return value slot.
637 lea(rcx, StackSpaceOperand(arg_stack_space));
638 #else
639 EnterApiExitFrame(arg_stack_space);
640 #endif
641 }
642
643
TryCallApiFunctionAndReturn(ApiFunction * function,int stack_space)644 MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(
645 ApiFunction* function, int stack_space) {
646 Label empty_result;
647 Label prologue;
648 Label promote_scheduled_exception;
649 Label delete_allocated_handles;
650 Label leave_exit_frame;
651 Label write_back;
652
653 ExternalReference next_address =
654 ExternalReference::handle_scope_next_address();
655 const int kNextOffset = 0;
656 const int kLimitOffset = Offset(
657 ExternalReference::handle_scope_limit_address(),
658 next_address);
659 const int kLevelOffset = Offset(
660 ExternalReference::handle_scope_level_address(),
661 next_address);
662 ExternalReference scheduled_exception_address =
663 ExternalReference::scheduled_exception_address(isolate());
664
665 // Allocate HandleScope in callee-save registers.
666 Register prev_next_address_reg = r14;
667 Register prev_limit_reg = rbx;
668 Register base_reg = r15;
669 movq(base_reg, next_address);
670 movq(prev_next_address_reg, Operand(base_reg, kNextOffset));
671 movq(prev_limit_reg, Operand(base_reg, kLimitOffset));
672 addl(Operand(base_reg, kLevelOffset), Immediate(1));
673 // Call the api function!
674 movq(rax,
675 reinterpret_cast<int64_t>(function->address()),
676 RelocInfo::RUNTIME_ENTRY);
677 call(rax);
678
679 #ifdef _WIN64
680 // rax keeps a pointer to v8::Handle, unpack it.
681 movq(rax, Operand(rax, 0));
682 #endif
683 // Check if the result handle holds 0.
684 testq(rax, rax);
685 j(zero, &empty_result);
686 // It was non-zero. Dereference to get the result value.
687 movq(rax, Operand(rax, 0));
688 bind(&prologue);
689
690 // No more valid handles (the result handle was the last one). Restore
691 // previous handle scope.
692 subl(Operand(base_reg, kLevelOffset), Immediate(1));
693 movq(Operand(base_reg, kNextOffset), prev_next_address_reg);
694 cmpq(prev_limit_reg, Operand(base_reg, kLimitOffset));
695 j(not_equal, &delete_allocated_handles);
696 bind(&leave_exit_frame);
697
698 // Check if the function scheduled an exception.
699 movq(rsi, scheduled_exception_address);
700 Cmp(Operand(rsi, 0), FACTORY->the_hole_value());
701 j(not_equal, &promote_scheduled_exception);
702
703 LeaveApiExitFrame();
704 ret(stack_space * kPointerSize);
705
706 bind(&promote_scheduled_exception);
707 MaybeObject* result = TryTailCallRuntime(Runtime::kPromoteScheduledException,
708 0, 1);
709 if (result->IsFailure()) {
710 return result;
711 }
712
713 bind(&empty_result);
714 // It was zero; the result is undefined.
715 Move(rax, FACTORY->undefined_value());
716 jmp(&prologue);
717
718 // HandleScope limit has changed. Delete allocated extensions.
719 bind(&delete_allocated_handles);
720 movq(Operand(base_reg, kLimitOffset), prev_limit_reg);
721 movq(prev_limit_reg, rax);
722 #ifdef _WIN64
723 LoadAddress(rcx, ExternalReference::isolate_address());
724 #else
725 LoadAddress(rdi, ExternalReference::isolate_address());
726 #endif
727 LoadAddress(rax,
728 ExternalReference::delete_handle_scope_extensions(isolate()));
729 call(rax);
730 movq(rax, prev_limit_reg);
731 jmp(&leave_exit_frame);
732
733 return result;
734 }
735
736
JumpToExternalReference(const ExternalReference & ext,int result_size)737 void MacroAssembler::JumpToExternalReference(const ExternalReference& ext,
738 int result_size) {
739 // Set the entry point and jump to the C entry runtime stub.
740 LoadAddress(rbx, ext);
741 CEntryStub ces(result_size);
742 jmp(ces.GetCode(), RelocInfo::CODE_TARGET);
743 }
744
745
TryJumpToExternalReference(const ExternalReference & ext,int result_size)746 MaybeObject* MacroAssembler::TryJumpToExternalReference(
747 const ExternalReference& ext, int result_size) {
748 // Set the entry point and jump to the C entry runtime stub.
749 LoadAddress(rbx, ext);
750 CEntryStub ces(result_size);
751 return TryTailCallStub(&ces);
752 }
753
754
InvokeBuiltin(Builtins::JavaScript id,InvokeFlag flag,CallWrapper * call_wrapper)755 void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
756 InvokeFlag flag,
757 CallWrapper* call_wrapper) {
758 // Calls are not allowed in some stubs.
759 ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
760
761 // Rely on the assertion to check that the number of provided
762 // arguments match the expected number of arguments. Fake a
763 // parameter count to avoid emitting code to do the check.
764 ParameterCount expected(0);
765 GetBuiltinEntry(rdx, id);
766 InvokeCode(rdx, expected, expected, flag, call_wrapper);
767 }
768
769
GetBuiltinFunction(Register target,Builtins::JavaScript id)770 void MacroAssembler::GetBuiltinFunction(Register target,
771 Builtins::JavaScript id) {
772 // Load the builtins object into target register.
773 movq(target, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
774 movq(target, FieldOperand(target, GlobalObject::kBuiltinsOffset));
775 movq(target, FieldOperand(target,
776 JSBuiltinsObject::OffsetOfFunctionWithId(id)));
777 }
778
779
GetBuiltinEntry(Register target,Builtins::JavaScript id)780 void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
781 ASSERT(!target.is(rdi));
782 // Load the JavaScript builtin function from the builtins object.
783 GetBuiltinFunction(rdi, id);
784 movq(target, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
785 }
786
787
Set(Register dst,int64_t x)788 void MacroAssembler::Set(Register dst, int64_t x) {
789 if (x == 0) {
790 xorl(dst, dst);
791 } else if (is_uint32(x)) {
792 movl(dst, Immediate(static_cast<uint32_t>(x)));
793 } else if (is_int32(x)) {
794 movq(dst, Immediate(static_cast<int32_t>(x)));
795 } else {
796 movq(dst, x, RelocInfo::NONE);
797 }
798 }
799
Set(const Operand & dst,int64_t x)800 void MacroAssembler::Set(const Operand& dst, int64_t x) {
801 if (is_int32(x)) {
802 movq(dst, Immediate(static_cast<int32_t>(x)));
803 } else {
804 Set(kScratchRegister, x);
805 movq(dst, kScratchRegister);
806 }
807 }
808
809 // ----------------------------------------------------------------------------
810 // Smi tagging, untagging and tag detection.
811
GetSmiConstant(Smi * source)812 Register MacroAssembler::GetSmiConstant(Smi* source) {
813 int value = source->value();
814 if (value == 0) {
815 xorl(kScratchRegister, kScratchRegister);
816 return kScratchRegister;
817 }
818 if (value == 1) {
819 return kSmiConstantRegister;
820 }
821 LoadSmiConstant(kScratchRegister, source);
822 return kScratchRegister;
823 }
824
LoadSmiConstant(Register dst,Smi * source)825 void MacroAssembler::LoadSmiConstant(Register dst, Smi* source) {
826 if (emit_debug_code()) {
827 movq(dst,
828 reinterpret_cast<uint64_t>(Smi::FromInt(kSmiConstantRegisterValue)),
829 RelocInfo::NONE);
830 cmpq(dst, kSmiConstantRegister);
831 if (allow_stub_calls()) {
832 Assert(equal, "Uninitialized kSmiConstantRegister");
833 } else {
834 NearLabel ok;
835 j(equal, &ok);
836 int3();
837 bind(&ok);
838 }
839 }
840 int value = source->value();
841 if (value == 0) {
842 xorl(dst, dst);
843 return;
844 }
845 bool negative = value < 0;
846 unsigned int uvalue = negative ? -value : value;
847
848 switch (uvalue) {
849 case 9:
850 lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_8, 0));
851 break;
852 case 8:
853 xorl(dst, dst);
854 lea(dst, Operand(dst, kSmiConstantRegister, times_8, 0));
855 break;
856 case 4:
857 xorl(dst, dst);
858 lea(dst, Operand(dst, kSmiConstantRegister, times_4, 0));
859 break;
860 case 5:
861 lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_4, 0));
862 break;
863 case 3:
864 lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_2, 0));
865 break;
866 case 2:
867 lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_1, 0));
868 break;
869 case 1:
870 movq(dst, kSmiConstantRegister);
871 break;
872 case 0:
873 UNREACHABLE();
874 return;
875 default:
876 movq(dst, reinterpret_cast<uint64_t>(source), RelocInfo::NONE);
877 return;
878 }
879 if (negative) {
880 neg(dst);
881 }
882 }
883
884
Integer32ToSmi(Register dst,Register src)885 void MacroAssembler::Integer32ToSmi(Register dst, Register src) {
886 ASSERT_EQ(0, kSmiTag);
887 if (!dst.is(src)) {
888 movl(dst, src);
889 }
890 shl(dst, Immediate(kSmiShift));
891 }
892
893
Integer32ToSmiField(const Operand & dst,Register src)894 void MacroAssembler::Integer32ToSmiField(const Operand& dst, Register src) {
895 if (emit_debug_code()) {
896 testb(dst, Immediate(0x01));
897 NearLabel ok;
898 j(zero, &ok);
899 if (allow_stub_calls()) {
900 Abort("Integer32ToSmiField writing to non-smi location");
901 } else {
902 int3();
903 }
904 bind(&ok);
905 }
906 ASSERT(kSmiShift % kBitsPerByte == 0);
907 movl(Operand(dst, kSmiShift / kBitsPerByte), src);
908 }
909
910
Integer64PlusConstantToSmi(Register dst,Register src,int constant)911 void MacroAssembler::Integer64PlusConstantToSmi(Register dst,
912 Register src,
913 int constant) {
914 if (dst.is(src)) {
915 addl(dst, Immediate(constant));
916 } else {
917 leal(dst, Operand(src, constant));
918 }
919 shl(dst, Immediate(kSmiShift));
920 }
921
922
SmiToInteger32(Register dst,Register src)923 void MacroAssembler::SmiToInteger32(Register dst, Register src) {
924 ASSERT_EQ(0, kSmiTag);
925 if (!dst.is(src)) {
926 movq(dst, src);
927 }
928 shr(dst, Immediate(kSmiShift));
929 }
930
931
SmiToInteger32(Register dst,const Operand & src)932 void MacroAssembler::SmiToInteger32(Register dst, const Operand& src) {
933 movl(dst, Operand(src, kSmiShift / kBitsPerByte));
934 }
935
936
SmiToInteger64(Register dst,Register src)937 void MacroAssembler::SmiToInteger64(Register dst, Register src) {
938 ASSERT_EQ(0, kSmiTag);
939 if (!dst.is(src)) {
940 movq(dst, src);
941 }
942 sar(dst, Immediate(kSmiShift));
943 }
944
945
SmiToInteger64(Register dst,const Operand & src)946 void MacroAssembler::SmiToInteger64(Register dst, const Operand& src) {
947 movsxlq(dst, Operand(src, kSmiShift / kBitsPerByte));
948 }
949
950
SmiTest(Register src)951 void MacroAssembler::SmiTest(Register src) {
952 testq(src, src);
953 }
954
955
SmiCompare(Register smi1,Register smi2)956 void MacroAssembler::SmiCompare(Register smi1, Register smi2) {
957 if (emit_debug_code()) {
958 AbortIfNotSmi(smi1);
959 AbortIfNotSmi(smi2);
960 }
961 cmpq(smi1, smi2);
962 }
963
964
SmiCompare(Register dst,Smi * src)965 void MacroAssembler::SmiCompare(Register dst, Smi* src) {
966 if (emit_debug_code()) {
967 AbortIfNotSmi(dst);
968 }
969 Cmp(dst, src);
970 }
971
972
Cmp(Register dst,Smi * src)973 void MacroAssembler::Cmp(Register dst, Smi* src) {
974 ASSERT(!dst.is(kScratchRegister));
975 if (src->value() == 0) {
976 testq(dst, dst);
977 } else {
978 Register constant_reg = GetSmiConstant(src);
979 cmpq(dst, constant_reg);
980 }
981 }
982
983
SmiCompare(Register dst,const Operand & src)984 void MacroAssembler::SmiCompare(Register dst, const Operand& src) {
985 if (emit_debug_code()) {
986 AbortIfNotSmi(dst);
987 AbortIfNotSmi(src);
988 }
989 cmpq(dst, src);
990 }
991
992
SmiCompare(const Operand & dst,Register src)993 void MacroAssembler::SmiCompare(const Operand& dst, Register src) {
994 if (emit_debug_code()) {
995 AbortIfNotSmi(dst);
996 AbortIfNotSmi(src);
997 }
998 cmpq(dst, src);
999 }
1000
1001
SmiCompare(const Operand & dst,Smi * src)1002 void MacroAssembler::SmiCompare(const Operand& dst, Smi* src) {
1003 if (emit_debug_code()) {
1004 AbortIfNotSmi(dst);
1005 }
1006 cmpl(Operand(dst, kSmiShift / kBitsPerByte), Immediate(src->value()));
1007 }
1008
1009
Cmp(const Operand & dst,Smi * src)1010 void MacroAssembler::Cmp(const Operand& dst, Smi* src) {
1011 // The Operand cannot use the smi register.
1012 Register smi_reg = GetSmiConstant(src);
1013 ASSERT(!dst.AddressUsesRegister(smi_reg));
1014 cmpq(dst, smi_reg);
1015 }
1016
1017
SmiCompareInteger32(const Operand & dst,Register src)1018 void MacroAssembler::SmiCompareInteger32(const Operand& dst, Register src) {
1019 cmpl(Operand(dst, kSmiShift / kBitsPerByte), src);
1020 }
1021
1022
PositiveSmiTimesPowerOfTwoToInteger64(Register dst,Register src,int power)1023 void MacroAssembler::PositiveSmiTimesPowerOfTwoToInteger64(Register dst,
1024 Register src,
1025 int power) {
1026 ASSERT(power >= 0);
1027 ASSERT(power < 64);
1028 if (power == 0) {
1029 SmiToInteger64(dst, src);
1030 return;
1031 }
1032 if (!dst.is(src)) {
1033 movq(dst, src);
1034 }
1035 if (power < kSmiShift) {
1036 sar(dst, Immediate(kSmiShift - power));
1037 } else if (power > kSmiShift) {
1038 shl(dst, Immediate(power - kSmiShift));
1039 }
1040 }
1041
1042
PositiveSmiDivPowerOfTwoToInteger32(Register dst,Register src,int power)1043 void MacroAssembler::PositiveSmiDivPowerOfTwoToInteger32(Register dst,
1044 Register src,
1045 int power) {
1046 ASSERT((0 <= power) && (power < 32));
1047 if (dst.is(src)) {
1048 shr(dst, Immediate(power + kSmiShift));
1049 } else {
1050 UNIMPLEMENTED(); // Not used.
1051 }
1052 }
1053
1054
CheckSmi(Register src)1055 Condition MacroAssembler::CheckSmi(Register src) {
1056 ASSERT_EQ(0, kSmiTag);
1057 testb(src, Immediate(kSmiTagMask));
1058 return zero;
1059 }
1060
1061
CheckSmi(const Operand & src)1062 Condition MacroAssembler::CheckSmi(const Operand& src) {
1063 ASSERT_EQ(0, kSmiTag);
1064 testb(src, Immediate(kSmiTagMask));
1065 return zero;
1066 }
1067
1068
CheckNonNegativeSmi(Register src)1069 Condition MacroAssembler::CheckNonNegativeSmi(Register src) {
1070 ASSERT_EQ(0, kSmiTag);
1071 // Test that both bits of the mask 0x8000000000000001 are zero.
1072 movq(kScratchRegister, src);
1073 rol(kScratchRegister, Immediate(1));
1074 testb(kScratchRegister, Immediate(3));
1075 return zero;
1076 }
1077
1078
CheckBothSmi(Register first,Register second)1079 Condition MacroAssembler::CheckBothSmi(Register first, Register second) {
1080 if (first.is(second)) {
1081 return CheckSmi(first);
1082 }
1083 ASSERT(kSmiTag == 0 && kHeapObjectTag == 1 && kHeapObjectTagMask == 3);
1084 leal(kScratchRegister, Operand(first, second, times_1, 0));
1085 testb(kScratchRegister, Immediate(0x03));
1086 return zero;
1087 }
1088
1089
CheckBothNonNegativeSmi(Register first,Register second)1090 Condition MacroAssembler::CheckBothNonNegativeSmi(Register first,
1091 Register second) {
1092 if (first.is(second)) {
1093 return CheckNonNegativeSmi(first);
1094 }
1095 movq(kScratchRegister, first);
1096 or_(kScratchRegister, second);
1097 rol(kScratchRegister, Immediate(1));
1098 testl(kScratchRegister, Immediate(3));
1099 return zero;
1100 }
1101
1102
CheckEitherSmi(Register first,Register second,Register scratch)1103 Condition MacroAssembler::CheckEitherSmi(Register first,
1104 Register second,
1105 Register scratch) {
1106 if (first.is(second)) {
1107 return CheckSmi(first);
1108 }
1109 if (scratch.is(second)) {
1110 andl(scratch, first);
1111 } else {
1112 if (!scratch.is(first)) {
1113 movl(scratch, first);
1114 }
1115 andl(scratch, second);
1116 }
1117 testb(scratch, Immediate(kSmiTagMask));
1118 return zero;
1119 }
1120
1121
CheckIsMinSmi(Register src)1122 Condition MacroAssembler::CheckIsMinSmi(Register src) {
1123 ASSERT(!src.is(kScratchRegister));
1124 // If we overflow by subtracting one, it's the minimal smi value.
1125 cmpq(src, kSmiConstantRegister);
1126 return overflow;
1127 }
1128
1129
CheckInteger32ValidSmiValue(Register src)1130 Condition MacroAssembler::CheckInteger32ValidSmiValue(Register src) {
1131 // A 32-bit integer value can always be converted to a smi.
1132 return always;
1133 }
1134
1135
CheckUInteger32ValidSmiValue(Register src)1136 Condition MacroAssembler::CheckUInteger32ValidSmiValue(Register src) {
1137 // An unsigned 32-bit integer value is valid as long as the high bit
1138 // is not set.
1139 testl(src, src);
1140 return positive;
1141 }
1142
1143
CheckSmiToIndicator(Register dst,Register src)1144 void MacroAssembler::CheckSmiToIndicator(Register dst, Register src) {
1145 if (dst.is(src)) {
1146 andl(dst, Immediate(kSmiTagMask));
1147 } else {
1148 movl(dst, Immediate(kSmiTagMask));
1149 andl(dst, src);
1150 }
1151 }
1152
1153
CheckSmiToIndicator(Register dst,const Operand & src)1154 void MacroAssembler::CheckSmiToIndicator(Register dst, const Operand& src) {
1155 if (!(src.AddressUsesRegister(dst))) {
1156 movl(dst, Immediate(kSmiTagMask));
1157 andl(dst, src);
1158 } else {
1159 movl(dst, src);
1160 andl(dst, Immediate(kSmiTagMask));
1161 }
1162 }
1163
1164
SmiAddConstant(Register dst,Register src,Smi * constant)1165 void MacroAssembler::SmiAddConstant(Register dst, Register src, Smi* constant) {
1166 if (constant->value() == 0) {
1167 if (!dst.is(src)) {
1168 movq(dst, src);
1169 }
1170 return;
1171 } else if (dst.is(src)) {
1172 ASSERT(!dst.is(kScratchRegister));
1173 switch (constant->value()) {
1174 case 1:
1175 addq(dst, kSmiConstantRegister);
1176 return;
1177 case 2:
1178 lea(dst, Operand(src, kSmiConstantRegister, times_2, 0));
1179 return;
1180 case 4:
1181 lea(dst, Operand(src, kSmiConstantRegister, times_4, 0));
1182 return;
1183 case 8:
1184 lea(dst, Operand(src, kSmiConstantRegister, times_8, 0));
1185 return;
1186 default:
1187 Register constant_reg = GetSmiConstant(constant);
1188 addq(dst, constant_reg);
1189 return;
1190 }
1191 } else {
1192 switch (constant->value()) {
1193 case 1:
1194 lea(dst, Operand(src, kSmiConstantRegister, times_1, 0));
1195 return;
1196 case 2:
1197 lea(dst, Operand(src, kSmiConstantRegister, times_2, 0));
1198 return;
1199 case 4:
1200 lea(dst, Operand(src, kSmiConstantRegister, times_4, 0));
1201 return;
1202 case 8:
1203 lea(dst, Operand(src, kSmiConstantRegister, times_8, 0));
1204 return;
1205 default:
1206 LoadSmiConstant(dst, constant);
1207 addq(dst, src);
1208 return;
1209 }
1210 }
1211 }
1212
1213
SmiAddConstant(const Operand & dst,Smi * constant)1214 void MacroAssembler::SmiAddConstant(const Operand& dst, Smi* constant) {
1215 if (constant->value() != 0) {
1216 addl(Operand(dst, kSmiShift / kBitsPerByte), Immediate(constant->value()));
1217 }
1218 }
1219
1220
SmiSubConstant(Register dst,Register src,Smi * constant)1221 void MacroAssembler::SmiSubConstant(Register dst, Register src, Smi* constant) {
1222 if (constant->value() == 0) {
1223 if (!dst.is(src)) {
1224 movq(dst, src);
1225 }
1226 } else if (dst.is(src)) {
1227 ASSERT(!dst.is(kScratchRegister));
1228 Register constant_reg = GetSmiConstant(constant);
1229 subq(dst, constant_reg);
1230 } else {
1231 if (constant->value() == Smi::kMinValue) {
1232 LoadSmiConstant(dst, constant);
1233 // Adding and subtracting the min-value gives the same result, it only
1234 // differs on the overflow bit, which we don't check here.
1235 addq(dst, src);
1236 } else {
1237 // Subtract by adding the negation.
1238 LoadSmiConstant(dst, Smi::FromInt(-constant->value()));
1239 addq(dst, src);
1240 }
1241 }
1242 }
1243
1244
SmiAdd(Register dst,Register src1,Register src2)1245 void MacroAssembler::SmiAdd(Register dst,
1246 Register src1,
1247 Register src2) {
1248 // No overflow checking. Use only when it's known that
1249 // overflowing is impossible.
1250 ASSERT(!dst.is(src2));
1251 if (!dst.is(src1)) {
1252 movq(dst, src1);
1253 }
1254 addq(dst, src2);
1255 Assert(no_overflow, "Smi addition overflow");
1256 }
1257
1258
SmiSub(Register dst,Register src1,Register src2)1259 void MacroAssembler::SmiSub(Register dst, Register src1, Register src2) {
1260 // No overflow checking. Use only when it's known that
1261 // overflowing is impossible (e.g., subtracting two positive smis).
1262 ASSERT(!dst.is(src2));
1263 if (!dst.is(src1)) {
1264 movq(dst, src1);
1265 }
1266 subq(dst, src2);
1267 Assert(no_overflow, "Smi subtraction overflow");
1268 }
1269
1270
SmiSub(Register dst,Register src1,const Operand & src2)1271 void MacroAssembler::SmiSub(Register dst,
1272 Register src1,
1273 const Operand& src2) {
1274 // No overflow checking. Use only when it's known that
1275 // overflowing is impossible (e.g., subtracting two positive smis).
1276 if (!dst.is(src1)) {
1277 movq(dst, src1);
1278 }
1279 subq(dst, src2);
1280 Assert(no_overflow, "Smi subtraction overflow");
1281 }
1282
1283
SmiNot(Register dst,Register src)1284 void MacroAssembler::SmiNot(Register dst, Register src) {
1285 ASSERT(!dst.is(kScratchRegister));
1286 ASSERT(!src.is(kScratchRegister));
1287 // Set tag and padding bits before negating, so that they are zero afterwards.
1288 movl(kScratchRegister, Immediate(~0));
1289 if (dst.is(src)) {
1290 xor_(dst, kScratchRegister);
1291 } else {
1292 lea(dst, Operand(src, kScratchRegister, times_1, 0));
1293 }
1294 not_(dst);
1295 }
1296
1297
SmiAnd(Register dst,Register src1,Register src2)1298 void MacroAssembler::SmiAnd(Register dst, Register src1, Register src2) {
1299 ASSERT(!dst.is(src2));
1300 if (!dst.is(src1)) {
1301 movq(dst, src1);
1302 }
1303 and_(dst, src2);
1304 }
1305
1306
SmiAndConstant(Register dst,Register src,Smi * constant)1307 void MacroAssembler::SmiAndConstant(Register dst, Register src, Smi* constant) {
1308 if (constant->value() == 0) {
1309 Set(dst, 0);
1310 } else if (dst.is(src)) {
1311 ASSERT(!dst.is(kScratchRegister));
1312 Register constant_reg = GetSmiConstant(constant);
1313 and_(dst, constant_reg);
1314 } else {
1315 LoadSmiConstant(dst, constant);
1316 and_(dst, src);
1317 }
1318 }
1319
1320
SmiOr(Register dst,Register src1,Register src2)1321 void MacroAssembler::SmiOr(Register dst, Register src1, Register src2) {
1322 if (!dst.is(src1)) {
1323 ASSERT(!src1.is(src2));
1324 movq(dst, src1);
1325 }
1326 or_(dst, src2);
1327 }
1328
1329
SmiOrConstant(Register dst,Register src,Smi * constant)1330 void MacroAssembler::SmiOrConstant(Register dst, Register src, Smi* constant) {
1331 if (dst.is(src)) {
1332 ASSERT(!dst.is(kScratchRegister));
1333 Register constant_reg = GetSmiConstant(constant);
1334 or_(dst, constant_reg);
1335 } else {
1336 LoadSmiConstant(dst, constant);
1337 or_(dst, src);
1338 }
1339 }
1340
1341
SmiXor(Register dst,Register src1,Register src2)1342 void MacroAssembler::SmiXor(Register dst, Register src1, Register src2) {
1343 if (!dst.is(src1)) {
1344 ASSERT(!src1.is(src2));
1345 movq(dst, src1);
1346 }
1347 xor_(dst, src2);
1348 }
1349
1350
SmiXorConstant(Register dst,Register src,Smi * constant)1351 void MacroAssembler::SmiXorConstant(Register dst, Register src, Smi* constant) {
1352 if (dst.is(src)) {
1353 ASSERT(!dst.is(kScratchRegister));
1354 Register constant_reg = GetSmiConstant(constant);
1355 xor_(dst, constant_reg);
1356 } else {
1357 LoadSmiConstant(dst, constant);
1358 xor_(dst, src);
1359 }
1360 }
1361
1362
SmiShiftArithmeticRightConstant(Register dst,Register src,int shift_value)1363 void MacroAssembler::SmiShiftArithmeticRightConstant(Register dst,
1364 Register src,
1365 int shift_value) {
1366 ASSERT(is_uint5(shift_value));
1367 if (shift_value > 0) {
1368 if (dst.is(src)) {
1369 sar(dst, Immediate(shift_value + kSmiShift));
1370 shl(dst, Immediate(kSmiShift));
1371 } else {
1372 UNIMPLEMENTED(); // Not used.
1373 }
1374 }
1375 }
1376
1377
SmiShiftLeftConstant(Register dst,Register src,int shift_value)1378 void MacroAssembler::SmiShiftLeftConstant(Register dst,
1379 Register src,
1380 int shift_value) {
1381 if (!dst.is(src)) {
1382 movq(dst, src);
1383 }
1384 if (shift_value > 0) {
1385 shl(dst, Immediate(shift_value));
1386 }
1387 }
1388
1389
SmiShiftLeft(Register dst,Register src1,Register src2)1390 void MacroAssembler::SmiShiftLeft(Register dst,
1391 Register src1,
1392 Register src2) {
1393 ASSERT(!dst.is(rcx));
1394 NearLabel result_ok;
1395 // Untag shift amount.
1396 if (!dst.is(src1)) {
1397 movq(dst, src1);
1398 }
1399 SmiToInteger32(rcx, src2);
1400 // Shift amount specified by lower 5 bits, not six as the shl opcode.
1401 and_(rcx, Immediate(0x1f));
1402 shl_cl(dst);
1403 }
1404
1405
SmiShiftArithmeticRight(Register dst,Register src1,Register src2)1406 void MacroAssembler::SmiShiftArithmeticRight(Register dst,
1407 Register src1,
1408 Register src2) {
1409 ASSERT(!dst.is(kScratchRegister));
1410 ASSERT(!src1.is(kScratchRegister));
1411 ASSERT(!src2.is(kScratchRegister));
1412 ASSERT(!dst.is(rcx));
1413 if (src1.is(rcx)) {
1414 movq(kScratchRegister, src1);
1415 } else if (src2.is(rcx)) {
1416 movq(kScratchRegister, src2);
1417 }
1418 if (!dst.is(src1)) {
1419 movq(dst, src1);
1420 }
1421 SmiToInteger32(rcx, src2);
1422 orl(rcx, Immediate(kSmiShift));
1423 sar_cl(dst); // Shift 32 + original rcx & 0x1f.
1424 shl(dst, Immediate(kSmiShift));
1425 if (src1.is(rcx)) {
1426 movq(src1, kScratchRegister);
1427 } else if (src2.is(rcx)) {
1428 movq(src2, kScratchRegister);
1429 }
1430 }
1431
1432
SmiToIndex(Register dst,Register src,int shift)1433 SmiIndex MacroAssembler::SmiToIndex(Register dst,
1434 Register src,
1435 int shift) {
1436 ASSERT(is_uint6(shift));
1437 // There is a possible optimization if shift is in the range 60-63, but that
1438 // will (and must) never happen.
1439 if (!dst.is(src)) {
1440 movq(dst, src);
1441 }
1442 if (shift < kSmiShift) {
1443 sar(dst, Immediate(kSmiShift - shift));
1444 } else {
1445 shl(dst, Immediate(shift - kSmiShift));
1446 }
1447 return SmiIndex(dst, times_1);
1448 }
1449
SmiToNegativeIndex(Register dst,Register src,int shift)1450 SmiIndex MacroAssembler::SmiToNegativeIndex(Register dst,
1451 Register src,
1452 int shift) {
1453 // Register src holds a positive smi.
1454 ASSERT(is_uint6(shift));
1455 if (!dst.is(src)) {
1456 movq(dst, src);
1457 }
1458 neg(dst);
1459 if (shift < kSmiShift) {
1460 sar(dst, Immediate(kSmiShift - shift));
1461 } else {
1462 shl(dst, Immediate(shift - kSmiShift));
1463 }
1464 return SmiIndex(dst, times_1);
1465 }
1466
1467
AddSmiField(Register dst,const Operand & src)1468 void MacroAssembler::AddSmiField(Register dst, const Operand& src) {
1469 ASSERT_EQ(0, kSmiShift % kBitsPerByte);
1470 addl(dst, Operand(src, kSmiShift / kBitsPerByte));
1471 }
1472
1473
1474
Move(Register dst,Register src)1475 void MacroAssembler::Move(Register dst, Register src) {
1476 if (!dst.is(src)) {
1477 movq(dst, src);
1478 }
1479 }
1480
1481
Move(Register dst,Handle<Object> source)1482 void MacroAssembler::Move(Register dst, Handle<Object> source) {
1483 ASSERT(!source->IsFailure());
1484 if (source->IsSmi()) {
1485 Move(dst, Smi::cast(*source));
1486 } else {
1487 movq(dst, source, RelocInfo::EMBEDDED_OBJECT);
1488 }
1489 }
1490
1491
Move(const Operand & dst,Handle<Object> source)1492 void MacroAssembler::Move(const Operand& dst, Handle<Object> source) {
1493 ASSERT(!source->IsFailure());
1494 if (source->IsSmi()) {
1495 Move(dst, Smi::cast(*source));
1496 } else {
1497 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
1498 movq(dst, kScratchRegister);
1499 }
1500 }
1501
1502
Cmp(Register dst,Handle<Object> source)1503 void MacroAssembler::Cmp(Register dst, Handle<Object> source) {
1504 if (source->IsSmi()) {
1505 Cmp(dst, Smi::cast(*source));
1506 } else {
1507 Move(kScratchRegister, source);
1508 cmpq(dst, kScratchRegister);
1509 }
1510 }
1511
1512
Cmp(const Operand & dst,Handle<Object> source)1513 void MacroAssembler::Cmp(const Operand& dst, Handle<Object> source) {
1514 if (source->IsSmi()) {
1515 Cmp(dst, Smi::cast(*source));
1516 } else {
1517 ASSERT(source->IsHeapObject());
1518 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
1519 cmpq(dst, kScratchRegister);
1520 }
1521 }
1522
1523
Push(Handle<Object> source)1524 void MacroAssembler::Push(Handle<Object> source) {
1525 if (source->IsSmi()) {
1526 Push(Smi::cast(*source));
1527 } else {
1528 ASSERT(source->IsHeapObject());
1529 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
1530 push(kScratchRegister);
1531 }
1532 }
1533
1534
Push(Smi * source)1535 void MacroAssembler::Push(Smi* source) {
1536 intptr_t smi = reinterpret_cast<intptr_t>(source);
1537 if (is_int32(smi)) {
1538 push(Immediate(static_cast<int32_t>(smi)));
1539 } else {
1540 Register constant = GetSmiConstant(source);
1541 push(constant);
1542 }
1543 }
1544
1545
Drop(int stack_elements)1546 void MacroAssembler::Drop(int stack_elements) {
1547 if (stack_elements > 0) {
1548 addq(rsp, Immediate(stack_elements * kPointerSize));
1549 }
1550 }
1551
1552
Test(const Operand & src,Smi * source)1553 void MacroAssembler::Test(const Operand& src, Smi* source) {
1554 testl(Operand(src, kIntSize), Immediate(source->value()));
1555 }
1556
1557
Jump(ExternalReference ext)1558 void MacroAssembler::Jump(ExternalReference ext) {
1559 LoadAddress(kScratchRegister, ext);
1560 jmp(kScratchRegister);
1561 }
1562
1563
Jump(Address destination,RelocInfo::Mode rmode)1564 void MacroAssembler::Jump(Address destination, RelocInfo::Mode rmode) {
1565 movq(kScratchRegister, destination, rmode);
1566 jmp(kScratchRegister);
1567 }
1568
1569
Jump(Handle<Code> code_object,RelocInfo::Mode rmode)1570 void MacroAssembler::Jump(Handle<Code> code_object, RelocInfo::Mode rmode) {
1571 // TODO(X64): Inline this
1572 jmp(code_object, rmode);
1573 }
1574
1575
CallSize(ExternalReference ext)1576 int MacroAssembler::CallSize(ExternalReference ext) {
1577 // Opcode for call kScratchRegister is: Rex.B FF D4 (three bytes).
1578 const int kCallInstructionSize = 3;
1579 return LoadAddressSize(ext) + kCallInstructionSize;
1580 }
1581
1582
Call(ExternalReference ext)1583 void MacroAssembler::Call(ExternalReference ext) {
1584 #ifdef DEBUG
1585 int end_position = pc_offset() + CallSize(ext);
1586 #endif
1587 LoadAddress(kScratchRegister, ext);
1588 call(kScratchRegister);
1589 #ifdef DEBUG
1590 CHECK_EQ(end_position, pc_offset());
1591 #endif
1592 }
1593
1594
Call(Address destination,RelocInfo::Mode rmode)1595 void MacroAssembler::Call(Address destination, RelocInfo::Mode rmode) {
1596 #ifdef DEBUG
1597 int end_position = pc_offset() + CallSize(destination, rmode);
1598 #endif
1599 movq(kScratchRegister, destination, rmode);
1600 call(kScratchRegister);
1601 #ifdef DEBUG
1602 CHECK_EQ(pc_offset(), end_position);
1603 #endif
1604 }
1605
1606
Call(Handle<Code> code_object,RelocInfo::Mode rmode)1607 void MacroAssembler::Call(Handle<Code> code_object, RelocInfo::Mode rmode) {
1608 #ifdef DEBUG
1609 int end_position = pc_offset() + CallSize(code_object);
1610 #endif
1611 ASSERT(RelocInfo::IsCodeTarget(rmode));
1612 call(code_object, rmode);
1613 #ifdef DEBUG
1614 CHECK_EQ(end_position, pc_offset());
1615 #endif
1616 }
1617
1618
Pushad()1619 void MacroAssembler::Pushad() {
1620 push(rax);
1621 push(rcx);
1622 push(rdx);
1623 push(rbx);
1624 // Not pushing rsp or rbp.
1625 push(rsi);
1626 push(rdi);
1627 push(r8);
1628 push(r9);
1629 // r10 is kScratchRegister.
1630 push(r11);
1631 // r12 is kSmiConstantRegister.
1632 // r13 is kRootRegister.
1633 push(r14);
1634 push(r15);
1635 STATIC_ASSERT(11 == kNumSafepointSavedRegisters);
1636 // Use lea for symmetry with Popad.
1637 int sp_delta =
1638 (kNumSafepointRegisters - kNumSafepointSavedRegisters) * kPointerSize;
1639 lea(rsp, Operand(rsp, -sp_delta));
1640 }
1641
1642
Popad()1643 void MacroAssembler::Popad() {
1644 // Popad must not change the flags, so use lea instead of addq.
1645 int sp_delta =
1646 (kNumSafepointRegisters - kNumSafepointSavedRegisters) * kPointerSize;
1647 lea(rsp, Operand(rsp, sp_delta));
1648 pop(r15);
1649 pop(r14);
1650 pop(r11);
1651 pop(r9);
1652 pop(r8);
1653 pop(rdi);
1654 pop(rsi);
1655 pop(rbx);
1656 pop(rdx);
1657 pop(rcx);
1658 pop(rax);
1659 }
1660
1661
Dropad()1662 void MacroAssembler::Dropad() {
1663 addq(rsp, Immediate(kNumSafepointRegisters * kPointerSize));
1664 }
1665
1666
1667 // Order general registers are pushed by Pushad:
1668 // rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r14, r15.
1669 int MacroAssembler::kSafepointPushRegisterIndices[Register::kNumRegisters] = {
1670 0,
1671 1,
1672 2,
1673 3,
1674 -1,
1675 -1,
1676 4,
1677 5,
1678 6,
1679 7,
1680 -1,
1681 8,
1682 -1,
1683 -1,
1684 9,
1685 10
1686 };
1687
1688
StoreToSafepointRegisterSlot(Register dst,Register src)1689 void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Register src) {
1690 movq(SafepointRegisterSlot(dst), src);
1691 }
1692
1693
LoadFromSafepointRegisterSlot(Register dst,Register src)1694 void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
1695 movq(dst, SafepointRegisterSlot(src));
1696 }
1697
1698
SafepointRegisterSlot(Register reg)1699 Operand MacroAssembler::SafepointRegisterSlot(Register reg) {
1700 return Operand(rsp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
1701 }
1702
1703
PushTryHandler(CodeLocation try_location,HandlerType type)1704 void MacroAssembler::PushTryHandler(CodeLocation try_location,
1705 HandlerType type) {
1706 // Adjust this code if not the case.
1707 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
1708
1709 // The pc (return address) is already on TOS. This code pushes state,
1710 // frame pointer and current handler. Check that they are expected
1711 // next on the stack, in that order.
1712 ASSERT_EQ(StackHandlerConstants::kStateOffset,
1713 StackHandlerConstants::kPCOffset - kPointerSize);
1714 ASSERT_EQ(StackHandlerConstants::kFPOffset,
1715 StackHandlerConstants::kStateOffset - kPointerSize);
1716 ASSERT_EQ(StackHandlerConstants::kNextOffset,
1717 StackHandlerConstants::kFPOffset - kPointerSize);
1718
1719 if (try_location == IN_JAVASCRIPT) {
1720 if (type == TRY_CATCH_HANDLER) {
1721 push(Immediate(StackHandler::TRY_CATCH));
1722 } else {
1723 push(Immediate(StackHandler::TRY_FINALLY));
1724 }
1725 push(rbp);
1726 } else {
1727 ASSERT(try_location == IN_JS_ENTRY);
1728 // The frame pointer does not point to a JS frame so we save NULL
1729 // for rbp. We expect the code throwing an exception to check rbp
1730 // before dereferencing it to restore the context.
1731 push(Immediate(StackHandler::ENTRY));
1732 push(Immediate(0)); // NULL frame pointer.
1733 }
1734 // Save the current handler.
1735 Operand handler_operand =
1736 ExternalOperand(ExternalReference(Isolate::k_handler_address, isolate()));
1737 push(handler_operand);
1738 // Link this handler.
1739 movq(handler_operand, rsp);
1740 }
1741
1742
PopTryHandler()1743 void MacroAssembler::PopTryHandler() {
1744 ASSERT_EQ(0, StackHandlerConstants::kNextOffset);
1745 // Unlink this handler.
1746 Operand handler_operand =
1747 ExternalOperand(ExternalReference(Isolate::k_handler_address, isolate()));
1748 pop(handler_operand);
1749 // Remove the remaining fields.
1750 addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
1751 }
1752
1753
Throw(Register value)1754 void MacroAssembler::Throw(Register value) {
1755 // Check that stack should contain next handler, frame pointer, state and
1756 // return address in that order.
1757 STATIC_ASSERT(StackHandlerConstants::kFPOffset + kPointerSize ==
1758 StackHandlerConstants::kStateOffset);
1759 STATIC_ASSERT(StackHandlerConstants::kStateOffset + kPointerSize ==
1760 StackHandlerConstants::kPCOffset);
1761 // Keep thrown value in rax.
1762 if (!value.is(rax)) {
1763 movq(rax, value);
1764 }
1765
1766 ExternalReference handler_address(Isolate::k_handler_address, isolate());
1767 Operand handler_operand = ExternalOperand(handler_address);
1768 movq(rsp, handler_operand);
1769 // get next in chain
1770 pop(handler_operand);
1771 pop(rbp); // pop frame pointer
1772 pop(rdx); // remove state
1773
1774 // Before returning we restore the context from the frame pointer if not NULL.
1775 // The frame pointer is NULL in the exception handler of a JS entry frame.
1776 Set(rsi, 0); // Tentatively set context pointer to NULL
1777 NearLabel skip;
1778 cmpq(rbp, Immediate(0));
1779 j(equal, &skip);
1780 movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
1781 bind(&skip);
1782 ret(0);
1783 }
1784
1785
ThrowUncatchable(UncatchableExceptionType type,Register value)1786 void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
1787 Register value) {
1788 // Keep thrown value in rax.
1789 if (!value.is(rax)) {
1790 movq(rax, value);
1791 }
1792 // Fetch top stack handler.
1793 ExternalReference handler_address(Isolate::k_handler_address, isolate());
1794 Load(rsp, handler_address);
1795
1796 // Unwind the handlers until the ENTRY handler is found.
1797 NearLabel loop, done;
1798 bind(&loop);
1799 // Load the type of the current stack handler.
1800 const int kStateOffset = StackHandlerConstants::kStateOffset;
1801 cmpq(Operand(rsp, kStateOffset), Immediate(StackHandler::ENTRY));
1802 j(equal, &done);
1803 // Fetch the next handler in the list.
1804 const int kNextOffset = StackHandlerConstants::kNextOffset;
1805 movq(rsp, Operand(rsp, kNextOffset));
1806 jmp(&loop);
1807 bind(&done);
1808
1809 // Set the top handler address to next handler past the current ENTRY handler.
1810 Operand handler_operand = ExternalOperand(handler_address);
1811 pop(handler_operand);
1812
1813 if (type == OUT_OF_MEMORY) {
1814 // Set external caught exception to false.
1815 ExternalReference external_caught(
1816 Isolate::k_external_caught_exception_address, isolate());
1817 Set(rax, static_cast<int64_t>(false));
1818 Store(external_caught, rax);
1819
1820 // Set pending exception and rax to out of memory exception.
1821 ExternalReference pending_exception(Isolate::k_pending_exception_address,
1822 isolate());
1823 movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE);
1824 Store(pending_exception, rax);
1825 }
1826
1827 // Clear the context pointer.
1828 Set(rsi, 0);
1829
1830 // Restore registers from handler.
1831 STATIC_ASSERT(StackHandlerConstants::kNextOffset + kPointerSize ==
1832 StackHandlerConstants::kFPOffset);
1833 pop(rbp); // FP
1834 STATIC_ASSERT(StackHandlerConstants::kFPOffset + kPointerSize ==
1835 StackHandlerConstants::kStateOffset);
1836 pop(rdx); // State
1837
1838 STATIC_ASSERT(StackHandlerConstants::kStateOffset + kPointerSize ==
1839 StackHandlerConstants::kPCOffset);
1840 ret(0);
1841 }
1842
1843
Ret()1844 void MacroAssembler::Ret() {
1845 ret(0);
1846 }
1847
1848
Ret(int bytes_dropped,Register scratch)1849 void MacroAssembler::Ret(int bytes_dropped, Register scratch) {
1850 if (is_uint16(bytes_dropped)) {
1851 ret(bytes_dropped);
1852 } else {
1853 pop(scratch);
1854 addq(rsp, Immediate(bytes_dropped));
1855 push(scratch);
1856 ret(0);
1857 }
1858 }
1859
1860
FCmp()1861 void MacroAssembler::FCmp() {
1862 fucomip();
1863 fstp(0);
1864 }
1865
1866
CmpObjectType(Register heap_object,InstanceType type,Register map)1867 void MacroAssembler::CmpObjectType(Register heap_object,
1868 InstanceType type,
1869 Register map) {
1870 movq(map, FieldOperand(heap_object, HeapObject::kMapOffset));
1871 CmpInstanceType(map, type);
1872 }
1873
1874
CmpInstanceType(Register map,InstanceType type)1875 void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
1876 cmpb(FieldOperand(map, Map::kInstanceTypeOffset),
1877 Immediate(static_cast<int8_t>(type)));
1878 }
1879
1880
CheckMap(Register obj,Handle<Map> map,Label * fail,bool is_heap_object)1881 void MacroAssembler::CheckMap(Register obj,
1882 Handle<Map> map,
1883 Label* fail,
1884 bool is_heap_object) {
1885 if (!is_heap_object) {
1886 JumpIfSmi(obj, fail);
1887 }
1888 Cmp(FieldOperand(obj, HeapObject::kMapOffset), map);
1889 j(not_equal, fail);
1890 }
1891
1892
AbortIfNotNumber(Register object)1893 void MacroAssembler::AbortIfNotNumber(Register object) {
1894 NearLabel ok;
1895 Condition is_smi = CheckSmi(object);
1896 j(is_smi, &ok);
1897 Cmp(FieldOperand(object, HeapObject::kMapOffset),
1898 FACTORY->heap_number_map());
1899 Assert(equal, "Operand not a number");
1900 bind(&ok);
1901 }
1902
1903
AbortIfSmi(Register object)1904 void MacroAssembler::AbortIfSmi(Register object) {
1905 NearLabel ok;
1906 Condition is_smi = CheckSmi(object);
1907 Assert(NegateCondition(is_smi), "Operand is a smi");
1908 }
1909
1910
AbortIfNotSmi(Register object)1911 void MacroAssembler::AbortIfNotSmi(Register object) {
1912 Condition is_smi = CheckSmi(object);
1913 Assert(is_smi, "Operand is not a smi");
1914 }
1915
1916
AbortIfNotSmi(const Operand & object)1917 void MacroAssembler::AbortIfNotSmi(const Operand& object) {
1918 Condition is_smi = CheckSmi(object);
1919 Assert(is_smi, "Operand is not a smi");
1920 }
1921
1922
AbortIfNotString(Register object)1923 void MacroAssembler::AbortIfNotString(Register object) {
1924 testb(object, Immediate(kSmiTagMask));
1925 Assert(not_equal, "Operand is not a string");
1926 push(object);
1927 movq(object, FieldOperand(object, HeapObject::kMapOffset));
1928 CmpInstanceType(object, FIRST_NONSTRING_TYPE);
1929 pop(object);
1930 Assert(below, "Operand is not a string");
1931 }
1932
1933
AbortIfNotRootValue(Register src,Heap::RootListIndex root_value_index,const char * message)1934 void MacroAssembler::AbortIfNotRootValue(Register src,
1935 Heap::RootListIndex root_value_index,
1936 const char* message) {
1937 ASSERT(!src.is(kScratchRegister));
1938 LoadRoot(kScratchRegister, root_value_index);
1939 cmpq(src, kScratchRegister);
1940 Check(equal, message);
1941 }
1942
1943
1944
IsObjectStringType(Register heap_object,Register map,Register instance_type)1945 Condition MacroAssembler::IsObjectStringType(Register heap_object,
1946 Register map,
1947 Register instance_type) {
1948 movq(map, FieldOperand(heap_object, HeapObject::kMapOffset));
1949 movzxbl(instance_type, FieldOperand(map, Map::kInstanceTypeOffset));
1950 ASSERT(kNotStringTag != 0);
1951 testb(instance_type, Immediate(kIsNotStringMask));
1952 return zero;
1953 }
1954
1955
TryGetFunctionPrototype(Register function,Register result,Label * miss)1956 void MacroAssembler::TryGetFunctionPrototype(Register function,
1957 Register result,
1958 Label* miss) {
1959 // Check that the receiver isn't a smi.
1960 testl(function, Immediate(kSmiTagMask));
1961 j(zero, miss);
1962
1963 // Check that the function really is a function.
1964 CmpObjectType(function, JS_FUNCTION_TYPE, result);
1965 j(not_equal, miss);
1966
1967 // Make sure that the function has an instance prototype.
1968 NearLabel non_instance;
1969 testb(FieldOperand(result, Map::kBitFieldOffset),
1970 Immediate(1 << Map::kHasNonInstancePrototype));
1971 j(not_zero, &non_instance);
1972
1973 // Get the prototype or initial map from the function.
1974 movq(result,
1975 FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
1976
1977 // If the prototype or initial map is the hole, don't return it and
1978 // simply miss the cache instead. This will allow us to allocate a
1979 // prototype object on-demand in the runtime system.
1980 CompareRoot(result, Heap::kTheHoleValueRootIndex);
1981 j(equal, miss);
1982
1983 // If the function does not have an initial map, we're done.
1984 NearLabel done;
1985 CmpObjectType(result, MAP_TYPE, kScratchRegister);
1986 j(not_equal, &done);
1987
1988 // Get the prototype from the initial map.
1989 movq(result, FieldOperand(result, Map::kPrototypeOffset));
1990 jmp(&done);
1991
1992 // Non-instance prototype: Fetch prototype from constructor field
1993 // in initial map.
1994 bind(&non_instance);
1995 movq(result, FieldOperand(result, Map::kConstructorOffset));
1996
1997 // All done.
1998 bind(&done);
1999 }
2000
2001
SetCounter(StatsCounter * counter,int value)2002 void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
2003 if (FLAG_native_code_counters && counter->Enabled()) {
2004 Operand counter_operand = ExternalOperand(ExternalReference(counter));
2005 movl(counter_operand, Immediate(value));
2006 }
2007 }
2008
2009
IncrementCounter(StatsCounter * counter,int value)2010 void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) {
2011 ASSERT(value > 0);
2012 if (FLAG_native_code_counters && counter->Enabled()) {
2013 Operand counter_operand = ExternalOperand(ExternalReference(counter));
2014 if (value == 1) {
2015 incl(counter_operand);
2016 } else {
2017 addl(counter_operand, Immediate(value));
2018 }
2019 }
2020 }
2021
2022
DecrementCounter(StatsCounter * counter,int value)2023 void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
2024 ASSERT(value > 0);
2025 if (FLAG_native_code_counters && counter->Enabled()) {
2026 Operand counter_operand = ExternalOperand(ExternalReference(counter));
2027 if (value == 1) {
2028 decl(counter_operand);
2029 } else {
2030 subl(counter_operand, Immediate(value));
2031 }
2032 }
2033 }
2034
2035
2036 #ifdef ENABLE_DEBUGGER_SUPPORT
DebugBreak()2037 void MacroAssembler::DebugBreak() {
2038 ASSERT(allow_stub_calls());
2039 Set(rax, 0); // No arguments.
2040 LoadAddress(rbx, ExternalReference(Runtime::kDebugBreak, isolate()));
2041 CEntryStub ces(1);
2042 Call(ces.GetCode(), RelocInfo::DEBUG_BREAK);
2043 }
2044 #endif // ENABLE_DEBUGGER_SUPPORT
2045
2046
InvokeCode(Register code,const ParameterCount & expected,const ParameterCount & actual,InvokeFlag flag,CallWrapper * call_wrapper)2047 void MacroAssembler::InvokeCode(Register code,
2048 const ParameterCount& expected,
2049 const ParameterCount& actual,
2050 InvokeFlag flag,
2051 CallWrapper* call_wrapper) {
2052 NearLabel done;
2053 InvokePrologue(expected,
2054 actual,
2055 Handle<Code>::null(),
2056 code,
2057 &done,
2058 flag,
2059 call_wrapper);
2060 if (flag == CALL_FUNCTION) {
2061 if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(code));
2062 call(code);
2063 if (call_wrapper != NULL) call_wrapper->AfterCall();
2064 } else {
2065 ASSERT(flag == JUMP_FUNCTION);
2066 jmp(code);
2067 }
2068 bind(&done);
2069 }
2070
2071
InvokeCode(Handle<Code> code,const ParameterCount & expected,const ParameterCount & actual,RelocInfo::Mode rmode,InvokeFlag flag,CallWrapper * call_wrapper)2072 void MacroAssembler::InvokeCode(Handle<Code> code,
2073 const ParameterCount& expected,
2074 const ParameterCount& actual,
2075 RelocInfo::Mode rmode,
2076 InvokeFlag flag,
2077 CallWrapper* call_wrapper) {
2078 NearLabel done;
2079 Register dummy = rax;
2080 InvokePrologue(expected,
2081 actual,
2082 code,
2083 dummy,
2084 &done,
2085 flag,
2086 call_wrapper);
2087 if (flag == CALL_FUNCTION) {
2088 if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(code));
2089 Call(code, rmode);
2090 if (call_wrapper != NULL) call_wrapper->AfterCall();
2091 } else {
2092 ASSERT(flag == JUMP_FUNCTION);
2093 Jump(code, rmode);
2094 }
2095 bind(&done);
2096 }
2097
2098
InvokeFunction(Register function,const ParameterCount & actual,InvokeFlag flag,CallWrapper * call_wrapper)2099 void MacroAssembler::InvokeFunction(Register function,
2100 const ParameterCount& actual,
2101 InvokeFlag flag,
2102 CallWrapper* call_wrapper) {
2103 ASSERT(function.is(rdi));
2104 movq(rdx, FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
2105 movq(rsi, FieldOperand(function, JSFunction::kContextOffset));
2106 movsxlq(rbx,
2107 FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
2108 // Advances rdx to the end of the Code object header, to the start of
2109 // the executable code.
2110 movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
2111
2112 ParameterCount expected(rbx);
2113 InvokeCode(rdx, expected, actual, flag, call_wrapper);
2114 }
2115
2116
InvokeFunction(JSFunction * function,const ParameterCount & actual,InvokeFlag flag,CallWrapper * call_wrapper)2117 void MacroAssembler::InvokeFunction(JSFunction* function,
2118 const ParameterCount& actual,
2119 InvokeFlag flag,
2120 CallWrapper* call_wrapper) {
2121 ASSERT(function->is_compiled());
2122 // Get the function and setup the context.
2123 Move(rdi, Handle<JSFunction>(function));
2124 movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
2125
2126 if (V8::UseCrankshaft()) {
2127 // Since Crankshaft can recompile a function, we need to load
2128 // the Code object every time we call the function.
2129 movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
2130 ParameterCount expected(function->shared()->formal_parameter_count());
2131 InvokeCode(rdx, expected, actual, flag, call_wrapper);
2132 } else {
2133 // Invoke the cached code.
2134 Handle<Code> code(function->code());
2135 ParameterCount expected(function->shared()->formal_parameter_count());
2136 InvokeCode(code,
2137 expected,
2138 actual,
2139 RelocInfo::CODE_TARGET,
2140 flag,
2141 call_wrapper);
2142 }
2143 }
2144
2145
EnterFrame(StackFrame::Type type)2146 void MacroAssembler::EnterFrame(StackFrame::Type type) {
2147 push(rbp);
2148 movq(rbp, rsp);
2149 push(rsi); // Context.
2150 Push(Smi::FromInt(type));
2151 movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
2152 push(kScratchRegister);
2153 if (emit_debug_code()) {
2154 movq(kScratchRegister,
2155 FACTORY->undefined_value(),
2156 RelocInfo::EMBEDDED_OBJECT);
2157 cmpq(Operand(rsp, 0), kScratchRegister);
2158 Check(not_equal, "code object not properly patched");
2159 }
2160 }
2161
2162
LeaveFrame(StackFrame::Type type)2163 void MacroAssembler::LeaveFrame(StackFrame::Type type) {
2164 if (emit_debug_code()) {
2165 Move(kScratchRegister, Smi::FromInt(type));
2166 cmpq(Operand(rbp, StandardFrameConstants::kMarkerOffset), kScratchRegister);
2167 Check(equal, "stack frame types must match");
2168 }
2169 movq(rsp, rbp);
2170 pop(rbp);
2171 }
2172
2173
EnterExitFramePrologue(bool save_rax)2174 void MacroAssembler::EnterExitFramePrologue(bool save_rax) {
2175 // Setup the frame structure on the stack.
2176 // All constants are relative to the frame pointer of the exit frame.
2177 ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize);
2178 ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize);
2179 ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize);
2180 push(rbp);
2181 movq(rbp, rsp);
2182
2183 // Reserve room for entry stack pointer and push the code object.
2184 ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
2185 push(Immediate(0)); // Saved entry sp, patched before call.
2186 movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
2187 push(kScratchRegister); // Accessed from EditFrame::code_slot.
2188
2189 // Save the frame pointer and the context in top.
2190 if (save_rax) {
2191 movq(r14, rax); // Backup rax in callee-save register.
2192 }
2193
2194 Store(ExternalReference(Isolate::k_c_entry_fp_address, isolate()), rbp);
2195 Store(ExternalReference(Isolate::k_context_address, isolate()), rsi);
2196 }
2197
2198
EnterExitFrameEpilogue(int arg_stack_space,bool save_doubles)2199 void MacroAssembler::EnterExitFrameEpilogue(int arg_stack_space,
2200 bool save_doubles) {
2201 #ifdef _WIN64
2202 const int kShadowSpace = 4;
2203 arg_stack_space += kShadowSpace;
2204 #endif
2205 // Optionally save all XMM registers.
2206 if (save_doubles) {
2207 int space = XMMRegister::kNumRegisters * kDoubleSize +
2208 arg_stack_space * kPointerSize;
2209 subq(rsp, Immediate(space));
2210 int offset = -2 * kPointerSize;
2211 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; i++) {
2212 XMMRegister reg = XMMRegister::FromAllocationIndex(i);
2213 movsd(Operand(rbp, offset - ((i + 1) * kDoubleSize)), reg);
2214 }
2215 } else if (arg_stack_space > 0) {
2216 subq(rsp, Immediate(arg_stack_space * kPointerSize));
2217 }
2218
2219 // Get the required frame alignment for the OS.
2220 const int kFrameAlignment = OS::ActivationFrameAlignment();
2221 if (kFrameAlignment > 0) {
2222 ASSERT(IsPowerOf2(kFrameAlignment));
2223 ASSERT(is_int8(kFrameAlignment));
2224 and_(rsp, Immediate(-kFrameAlignment));
2225 }
2226
2227 // Patch the saved entry sp.
2228 movq(Operand(rbp, ExitFrameConstants::kSPOffset), rsp);
2229 }
2230
2231
EnterExitFrame(int arg_stack_space,bool save_doubles)2232 void MacroAssembler::EnterExitFrame(int arg_stack_space, bool save_doubles) {
2233 EnterExitFramePrologue(true);
2234
2235 // Setup argv in callee-saved register r15. It is reused in LeaveExitFrame,
2236 // so it must be retained across the C-call.
2237 int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
2238 lea(r15, Operand(rbp, r14, times_pointer_size, offset));
2239
2240 EnterExitFrameEpilogue(arg_stack_space, save_doubles);
2241 }
2242
2243
EnterApiExitFrame(int arg_stack_space)2244 void MacroAssembler::EnterApiExitFrame(int arg_stack_space) {
2245 EnterExitFramePrologue(false);
2246 EnterExitFrameEpilogue(arg_stack_space, false);
2247 }
2248
2249
LeaveExitFrame(bool save_doubles)2250 void MacroAssembler::LeaveExitFrame(bool save_doubles) {
2251 // Registers:
2252 // r15 : argv
2253 if (save_doubles) {
2254 int offset = -2 * kPointerSize;
2255 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; i++) {
2256 XMMRegister reg = XMMRegister::FromAllocationIndex(i);
2257 movsd(reg, Operand(rbp, offset - ((i + 1) * kDoubleSize)));
2258 }
2259 }
2260 // Get the return address from the stack and restore the frame pointer.
2261 movq(rcx, Operand(rbp, 1 * kPointerSize));
2262 movq(rbp, Operand(rbp, 0 * kPointerSize));
2263
2264 // Drop everything up to and including the arguments and the receiver
2265 // from the caller stack.
2266 lea(rsp, Operand(r15, 1 * kPointerSize));
2267
2268 // Push the return address to get ready to return.
2269 push(rcx);
2270
2271 LeaveExitFrameEpilogue();
2272 }
2273
2274
LeaveApiExitFrame()2275 void MacroAssembler::LeaveApiExitFrame() {
2276 movq(rsp, rbp);
2277 pop(rbp);
2278
2279 LeaveExitFrameEpilogue();
2280 }
2281
2282
LeaveExitFrameEpilogue()2283 void MacroAssembler::LeaveExitFrameEpilogue() {
2284 // Restore current context from top and clear it in debug mode.
2285 ExternalReference context_address(Isolate::k_context_address, isolate());
2286 Operand context_operand = ExternalOperand(context_address);
2287 movq(rsi, context_operand);
2288 #ifdef DEBUG
2289 movq(context_operand, Immediate(0));
2290 #endif
2291
2292 // Clear the top frame.
2293 ExternalReference c_entry_fp_address(Isolate::k_c_entry_fp_address,
2294 isolate());
2295 Operand c_entry_fp_operand = ExternalOperand(c_entry_fp_address);
2296 movq(c_entry_fp_operand, Immediate(0));
2297 }
2298
2299
CheckAccessGlobalProxy(Register holder_reg,Register scratch,Label * miss)2300 void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
2301 Register scratch,
2302 Label* miss) {
2303 Label same_contexts;
2304
2305 ASSERT(!holder_reg.is(scratch));
2306 ASSERT(!scratch.is(kScratchRegister));
2307 // Load current lexical context from the stack frame.
2308 movq(scratch, Operand(rbp, StandardFrameConstants::kContextOffset));
2309
2310 // When generating debug code, make sure the lexical context is set.
2311 if (emit_debug_code()) {
2312 cmpq(scratch, Immediate(0));
2313 Check(not_equal, "we should not have an empty lexical context");
2314 }
2315 // Load the global context of the current context.
2316 int offset = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
2317 movq(scratch, FieldOperand(scratch, offset));
2318 movq(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset));
2319
2320 // Check the context is a global context.
2321 if (emit_debug_code()) {
2322 Cmp(FieldOperand(scratch, HeapObject::kMapOffset),
2323 FACTORY->global_context_map());
2324 Check(equal, "JSGlobalObject::global_context should be a global context.");
2325 }
2326
2327 // Check if both contexts are the same.
2328 cmpq(scratch, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
2329 j(equal, &same_contexts);
2330
2331 // Compare security tokens.
2332 // Check that the security token in the calling global object is
2333 // compatible with the security token in the receiving global
2334 // object.
2335
2336 // Check the context is a global context.
2337 if (emit_debug_code()) {
2338 // Preserve original value of holder_reg.
2339 push(holder_reg);
2340 movq(holder_reg, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
2341 CompareRoot(holder_reg, Heap::kNullValueRootIndex);
2342 Check(not_equal, "JSGlobalProxy::context() should not be null.");
2343
2344 // Read the first word and compare to global_context_map(),
2345 movq(holder_reg, FieldOperand(holder_reg, HeapObject::kMapOffset));
2346 CompareRoot(holder_reg, Heap::kGlobalContextMapRootIndex);
2347 Check(equal, "JSGlobalObject::global_context should be a global context.");
2348 pop(holder_reg);
2349 }
2350
2351 movq(kScratchRegister,
2352 FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
2353 int token_offset =
2354 Context::kHeaderSize + Context::SECURITY_TOKEN_INDEX * kPointerSize;
2355 movq(scratch, FieldOperand(scratch, token_offset));
2356 cmpq(scratch, FieldOperand(kScratchRegister, token_offset));
2357 j(not_equal, miss);
2358
2359 bind(&same_contexts);
2360 }
2361
2362
LoadAllocationTopHelper(Register result,Register scratch,AllocationFlags flags)2363 void MacroAssembler::LoadAllocationTopHelper(Register result,
2364 Register scratch,
2365 AllocationFlags flags) {
2366 ExternalReference new_space_allocation_top =
2367 ExternalReference::new_space_allocation_top_address(isolate());
2368
2369 // Just return if allocation top is already known.
2370 if ((flags & RESULT_CONTAINS_TOP) != 0) {
2371 // No use of scratch if allocation top is provided.
2372 ASSERT(!scratch.is_valid());
2373 #ifdef DEBUG
2374 // Assert that result actually contains top on entry.
2375 Operand top_operand = ExternalOperand(new_space_allocation_top);
2376 cmpq(result, top_operand);
2377 Check(equal, "Unexpected allocation top");
2378 #endif
2379 return;
2380 }
2381
2382 // Move address of new object to result. Use scratch register if available,
2383 // and keep address in scratch until call to UpdateAllocationTopHelper.
2384 if (scratch.is_valid()) {
2385 LoadAddress(scratch, new_space_allocation_top);
2386 movq(result, Operand(scratch, 0));
2387 } else {
2388 Load(result, new_space_allocation_top);
2389 }
2390 }
2391
2392
UpdateAllocationTopHelper(Register result_end,Register scratch)2393 void MacroAssembler::UpdateAllocationTopHelper(Register result_end,
2394 Register scratch) {
2395 if (emit_debug_code()) {
2396 testq(result_end, Immediate(kObjectAlignmentMask));
2397 Check(zero, "Unaligned allocation in new space");
2398 }
2399
2400 ExternalReference new_space_allocation_top =
2401 ExternalReference::new_space_allocation_top_address(isolate());
2402
2403 // Update new top.
2404 if (scratch.is_valid()) {
2405 // Scratch already contains address of allocation top.
2406 movq(Operand(scratch, 0), result_end);
2407 } else {
2408 Store(new_space_allocation_top, result_end);
2409 }
2410 }
2411
2412
AllocateInNewSpace(int object_size,Register result,Register result_end,Register scratch,Label * gc_required,AllocationFlags flags)2413 void MacroAssembler::AllocateInNewSpace(int object_size,
2414 Register result,
2415 Register result_end,
2416 Register scratch,
2417 Label* gc_required,
2418 AllocationFlags flags) {
2419 if (!FLAG_inline_new) {
2420 if (emit_debug_code()) {
2421 // Trash the registers to simulate an allocation failure.
2422 movl(result, Immediate(0x7091));
2423 if (result_end.is_valid()) {
2424 movl(result_end, Immediate(0x7191));
2425 }
2426 if (scratch.is_valid()) {
2427 movl(scratch, Immediate(0x7291));
2428 }
2429 }
2430 jmp(gc_required);
2431 return;
2432 }
2433 ASSERT(!result.is(result_end));
2434
2435 // Load address of new object into result.
2436 LoadAllocationTopHelper(result, scratch, flags);
2437
2438 // Calculate new top and bail out if new space is exhausted.
2439 ExternalReference new_space_allocation_limit =
2440 ExternalReference::new_space_allocation_limit_address(isolate());
2441
2442 Register top_reg = result_end.is_valid() ? result_end : result;
2443
2444 if (!top_reg.is(result)) {
2445 movq(top_reg, result);
2446 }
2447 addq(top_reg, Immediate(object_size));
2448 j(carry, gc_required);
2449 Operand limit_operand = ExternalOperand(new_space_allocation_limit);
2450 cmpq(top_reg, limit_operand);
2451 j(above, gc_required);
2452
2453 // Update allocation top.
2454 UpdateAllocationTopHelper(top_reg, scratch);
2455
2456 if (top_reg.is(result)) {
2457 if ((flags & TAG_OBJECT) != 0) {
2458 subq(result, Immediate(object_size - kHeapObjectTag));
2459 } else {
2460 subq(result, Immediate(object_size));
2461 }
2462 } else if ((flags & TAG_OBJECT) != 0) {
2463 // Tag the result if requested.
2464 addq(result, Immediate(kHeapObjectTag));
2465 }
2466 }
2467
2468
AllocateInNewSpace(int header_size,ScaleFactor element_size,Register element_count,Register result,Register result_end,Register scratch,Label * gc_required,AllocationFlags flags)2469 void MacroAssembler::AllocateInNewSpace(int header_size,
2470 ScaleFactor element_size,
2471 Register element_count,
2472 Register result,
2473 Register result_end,
2474 Register scratch,
2475 Label* gc_required,
2476 AllocationFlags flags) {
2477 if (!FLAG_inline_new) {
2478 if (emit_debug_code()) {
2479 // Trash the registers to simulate an allocation failure.
2480 movl(result, Immediate(0x7091));
2481 movl(result_end, Immediate(0x7191));
2482 if (scratch.is_valid()) {
2483 movl(scratch, Immediate(0x7291));
2484 }
2485 // Register element_count is not modified by the function.
2486 }
2487 jmp(gc_required);
2488 return;
2489 }
2490 ASSERT(!result.is(result_end));
2491
2492 // Load address of new object into result.
2493 LoadAllocationTopHelper(result, scratch, flags);
2494
2495 // Calculate new top and bail out if new space is exhausted.
2496 ExternalReference new_space_allocation_limit =
2497 ExternalReference::new_space_allocation_limit_address(isolate());
2498
2499 // We assume that element_count*element_size + header_size does not
2500 // overflow.
2501 lea(result_end, Operand(element_count, element_size, header_size));
2502 addq(result_end, result);
2503 j(carry, gc_required);
2504 Operand limit_operand = ExternalOperand(new_space_allocation_limit);
2505 cmpq(result_end, limit_operand);
2506 j(above, gc_required);
2507
2508 // Update allocation top.
2509 UpdateAllocationTopHelper(result_end, scratch);
2510
2511 // Tag the result if requested.
2512 if ((flags & TAG_OBJECT) != 0) {
2513 addq(result, Immediate(kHeapObjectTag));
2514 }
2515 }
2516
2517
AllocateInNewSpace(Register object_size,Register result,Register result_end,Register scratch,Label * gc_required,AllocationFlags flags)2518 void MacroAssembler::AllocateInNewSpace(Register object_size,
2519 Register result,
2520 Register result_end,
2521 Register scratch,
2522 Label* gc_required,
2523 AllocationFlags flags) {
2524 if (!FLAG_inline_new) {
2525 if (emit_debug_code()) {
2526 // Trash the registers to simulate an allocation failure.
2527 movl(result, Immediate(0x7091));
2528 movl(result_end, Immediate(0x7191));
2529 if (scratch.is_valid()) {
2530 movl(scratch, Immediate(0x7291));
2531 }
2532 // object_size is left unchanged by this function.
2533 }
2534 jmp(gc_required);
2535 return;
2536 }
2537 ASSERT(!result.is(result_end));
2538
2539 // Load address of new object into result.
2540 LoadAllocationTopHelper(result, scratch, flags);
2541
2542 // Calculate new top and bail out if new space is exhausted.
2543 ExternalReference new_space_allocation_limit =
2544 ExternalReference::new_space_allocation_limit_address(isolate());
2545 if (!object_size.is(result_end)) {
2546 movq(result_end, object_size);
2547 }
2548 addq(result_end, result);
2549 j(carry, gc_required);
2550 Operand limit_operand = ExternalOperand(new_space_allocation_limit);
2551 cmpq(result_end, limit_operand);
2552 j(above, gc_required);
2553
2554 // Update allocation top.
2555 UpdateAllocationTopHelper(result_end, scratch);
2556
2557 // Tag the result if requested.
2558 if ((flags & TAG_OBJECT) != 0) {
2559 addq(result, Immediate(kHeapObjectTag));
2560 }
2561 }
2562
2563
UndoAllocationInNewSpace(Register object)2564 void MacroAssembler::UndoAllocationInNewSpace(Register object) {
2565 ExternalReference new_space_allocation_top =
2566 ExternalReference::new_space_allocation_top_address(isolate());
2567
2568 // Make sure the object has no tag before resetting top.
2569 and_(object, Immediate(~kHeapObjectTagMask));
2570 Operand top_operand = ExternalOperand(new_space_allocation_top);
2571 #ifdef DEBUG
2572 cmpq(object, top_operand);
2573 Check(below, "Undo allocation of non allocated memory");
2574 #endif
2575 movq(top_operand, object);
2576 }
2577
2578
AllocateHeapNumber(Register result,Register scratch,Label * gc_required)2579 void MacroAssembler::AllocateHeapNumber(Register result,
2580 Register scratch,
2581 Label* gc_required) {
2582 // Allocate heap number in new space.
2583 AllocateInNewSpace(HeapNumber::kSize,
2584 result,
2585 scratch,
2586 no_reg,
2587 gc_required,
2588 TAG_OBJECT);
2589
2590 // Set the map.
2591 LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex);
2592 movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
2593 }
2594
2595
AllocateTwoByteString(Register result,Register length,Register scratch1,Register scratch2,Register scratch3,Label * gc_required)2596 void MacroAssembler::AllocateTwoByteString(Register result,
2597 Register length,
2598 Register scratch1,
2599 Register scratch2,
2600 Register scratch3,
2601 Label* gc_required) {
2602 // Calculate the number of bytes needed for the characters in the string while
2603 // observing object alignment.
2604 const int kHeaderAlignment = SeqTwoByteString::kHeaderSize &
2605 kObjectAlignmentMask;
2606 ASSERT(kShortSize == 2);
2607 // scratch1 = length * 2 + kObjectAlignmentMask.
2608 lea(scratch1, Operand(length, length, times_1, kObjectAlignmentMask +
2609 kHeaderAlignment));
2610 and_(scratch1, Immediate(~kObjectAlignmentMask));
2611 if (kHeaderAlignment > 0) {
2612 subq(scratch1, Immediate(kHeaderAlignment));
2613 }
2614
2615 // Allocate two byte string in new space.
2616 AllocateInNewSpace(SeqTwoByteString::kHeaderSize,
2617 times_1,
2618 scratch1,
2619 result,
2620 scratch2,
2621 scratch3,
2622 gc_required,
2623 TAG_OBJECT);
2624
2625 // Set the map, length and hash field.
2626 LoadRoot(kScratchRegister, Heap::kStringMapRootIndex);
2627 movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
2628 Integer32ToSmi(scratch1, length);
2629 movq(FieldOperand(result, String::kLengthOffset), scratch1);
2630 movq(FieldOperand(result, String::kHashFieldOffset),
2631 Immediate(String::kEmptyHashField));
2632 }
2633
2634
AllocateAsciiString(Register result,Register length,Register scratch1,Register scratch2,Register scratch3,Label * gc_required)2635 void MacroAssembler::AllocateAsciiString(Register result,
2636 Register length,
2637 Register scratch1,
2638 Register scratch2,
2639 Register scratch3,
2640 Label* gc_required) {
2641 // Calculate the number of bytes needed for the characters in the string while
2642 // observing object alignment.
2643 const int kHeaderAlignment = SeqAsciiString::kHeaderSize &
2644 kObjectAlignmentMask;
2645 movl(scratch1, length);
2646 ASSERT(kCharSize == 1);
2647 addq(scratch1, Immediate(kObjectAlignmentMask + kHeaderAlignment));
2648 and_(scratch1, Immediate(~kObjectAlignmentMask));
2649 if (kHeaderAlignment > 0) {
2650 subq(scratch1, Immediate(kHeaderAlignment));
2651 }
2652
2653 // Allocate ascii string in new space.
2654 AllocateInNewSpace(SeqAsciiString::kHeaderSize,
2655 times_1,
2656 scratch1,
2657 result,
2658 scratch2,
2659 scratch3,
2660 gc_required,
2661 TAG_OBJECT);
2662
2663 // Set the map, length and hash field.
2664 LoadRoot(kScratchRegister, Heap::kAsciiStringMapRootIndex);
2665 movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
2666 Integer32ToSmi(scratch1, length);
2667 movq(FieldOperand(result, String::kLengthOffset), scratch1);
2668 movq(FieldOperand(result, String::kHashFieldOffset),
2669 Immediate(String::kEmptyHashField));
2670 }
2671
2672
AllocateConsString(Register result,Register scratch1,Register scratch2,Label * gc_required)2673 void MacroAssembler::AllocateConsString(Register result,
2674 Register scratch1,
2675 Register scratch2,
2676 Label* gc_required) {
2677 // Allocate heap number in new space.
2678 AllocateInNewSpace(ConsString::kSize,
2679 result,
2680 scratch1,
2681 scratch2,
2682 gc_required,
2683 TAG_OBJECT);
2684
2685 // Set the map. The other fields are left uninitialized.
2686 LoadRoot(kScratchRegister, Heap::kConsStringMapRootIndex);
2687 movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
2688 }
2689
2690
AllocateAsciiConsString(Register result,Register scratch1,Register scratch2,Label * gc_required)2691 void MacroAssembler::AllocateAsciiConsString(Register result,
2692 Register scratch1,
2693 Register scratch2,
2694 Label* gc_required) {
2695 // Allocate heap number in new space.
2696 AllocateInNewSpace(ConsString::kSize,
2697 result,
2698 scratch1,
2699 scratch2,
2700 gc_required,
2701 TAG_OBJECT);
2702
2703 // Set the map. The other fields are left uninitialized.
2704 LoadRoot(kScratchRegister, Heap::kConsAsciiStringMapRootIndex);
2705 movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
2706 }
2707
2708
2709 // Copy memory, byte-by-byte, from source to destination. Not optimized for
2710 // long or aligned copies. The contents of scratch and length are destroyed.
2711 // Destination is incremented by length, source, length and scratch are
2712 // clobbered.
2713 // A simpler loop is faster on small copies, but slower on large ones.
2714 // The cld() instruction must have been emitted, to set the direction flag(),
2715 // before calling this function.
CopyBytes(Register destination,Register source,Register length,int min_length,Register scratch)2716 void MacroAssembler::CopyBytes(Register destination,
2717 Register source,
2718 Register length,
2719 int min_length,
2720 Register scratch) {
2721 ASSERT(min_length >= 0);
2722 if (FLAG_debug_code) {
2723 cmpl(length, Immediate(min_length));
2724 Assert(greater_equal, "Invalid min_length");
2725 }
2726 Label loop, done, short_string, short_loop;
2727
2728 const int kLongStringLimit = 20;
2729 if (min_length <= kLongStringLimit) {
2730 cmpl(length, Immediate(kLongStringLimit));
2731 j(less_equal, &short_string);
2732 }
2733
2734 ASSERT(source.is(rsi));
2735 ASSERT(destination.is(rdi));
2736 ASSERT(length.is(rcx));
2737
2738 // Because source is 8-byte aligned in our uses of this function,
2739 // we keep source aligned for the rep movs operation by copying the odd bytes
2740 // at the end of the ranges.
2741 movq(scratch, length);
2742 shrl(length, Immediate(3));
2743 repmovsq();
2744 // Move remaining bytes of length.
2745 andl(scratch, Immediate(0x7));
2746 movq(length, Operand(source, scratch, times_1, -8));
2747 movq(Operand(destination, scratch, times_1, -8), length);
2748 addq(destination, scratch);
2749
2750 if (min_length <= kLongStringLimit) {
2751 jmp(&done);
2752
2753 bind(&short_string);
2754 if (min_length == 0) {
2755 testl(length, length);
2756 j(zero, &done);
2757 }
2758 lea(scratch, Operand(destination, length, times_1, 0));
2759
2760 bind(&short_loop);
2761 movb(length, Operand(source, 0));
2762 movb(Operand(destination, 0), length);
2763 incq(source);
2764 incq(destination);
2765 cmpq(destination, scratch);
2766 j(not_equal, &short_loop);
2767
2768 bind(&done);
2769 }
2770 }
2771
2772
LoadContext(Register dst,int context_chain_length)2773 void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
2774 if (context_chain_length > 0) {
2775 // Move up the chain of contexts to the context containing the slot.
2776 movq(dst, Operand(rsi, Context::SlotOffset(Context::CLOSURE_INDEX)));
2777 // Load the function context (which is the incoming, outer context).
2778 movq(dst, FieldOperand(dst, JSFunction::kContextOffset));
2779 for (int i = 1; i < context_chain_length; i++) {
2780 movq(dst, Operand(dst, Context::SlotOffset(Context::CLOSURE_INDEX)));
2781 movq(dst, FieldOperand(dst, JSFunction::kContextOffset));
2782 }
2783 // The context may be an intermediate context, not a function context.
2784 movq(dst, Operand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
2785 } else {
2786 // Slot is in the current function context. Move it into the
2787 // destination register in case we store into it (the write barrier
2788 // cannot be allowed to destroy the context in rsi).
2789 movq(dst, rsi);
2790 }
2791
2792 // We should not have found a 'with' context by walking the context chain
2793 // (i.e., the static scope chain and runtime context chain do not agree).
2794 // A variable occurring in such a scope should have slot type LOOKUP and
2795 // not CONTEXT.
2796 if (emit_debug_code()) {
2797 cmpq(dst, Operand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
2798 Check(equal, "Yo dawg, I heard you liked function contexts "
2799 "so I put function contexts in all your contexts");
2800 }
2801 }
2802
2803 #ifdef _WIN64
2804 static const int kRegisterPassedArguments = 4;
2805 #else
2806 static const int kRegisterPassedArguments = 6;
2807 #endif
2808
LoadGlobalFunction(int index,Register function)2809 void MacroAssembler::LoadGlobalFunction(int index, Register function) {
2810 // Load the global or builtins object from the current context.
2811 movq(function, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
2812 // Load the global context from the global or builtins object.
2813 movq(function, FieldOperand(function, GlobalObject::kGlobalContextOffset));
2814 // Load the function from the global context.
2815 movq(function, Operand(function, Context::SlotOffset(index)));
2816 }
2817
2818
LoadGlobalFunctionInitialMap(Register function,Register map)2819 void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
2820 Register map) {
2821 // Load the initial map. The global functions all have initial maps.
2822 movq(map, FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
2823 if (emit_debug_code()) {
2824 Label ok, fail;
2825 CheckMap(map, FACTORY->meta_map(), &fail, false);
2826 jmp(&ok);
2827 bind(&fail);
2828 Abort("Global functions must have initial map");
2829 bind(&ok);
2830 }
2831 }
2832
2833
ArgumentStackSlotsForCFunctionCall(int num_arguments)2834 int MacroAssembler::ArgumentStackSlotsForCFunctionCall(int num_arguments) {
2835 // On Windows 64 stack slots are reserved by the caller for all arguments
2836 // including the ones passed in registers, and space is always allocated for
2837 // the four register arguments even if the function takes fewer than four
2838 // arguments.
2839 // On AMD64 ABI (Linux/Mac) the first six arguments are passed in registers
2840 // and the caller does not reserve stack slots for them.
2841 ASSERT(num_arguments >= 0);
2842 #ifdef _WIN64
2843 const int kMinimumStackSlots = kRegisterPassedArguments;
2844 if (num_arguments < kMinimumStackSlots) return kMinimumStackSlots;
2845 return num_arguments;
2846 #else
2847 if (num_arguments < kRegisterPassedArguments) return 0;
2848 return num_arguments - kRegisterPassedArguments;
2849 #endif
2850 }
2851
2852
PrepareCallCFunction(int num_arguments)2853 void MacroAssembler::PrepareCallCFunction(int num_arguments) {
2854 int frame_alignment = OS::ActivationFrameAlignment();
2855 ASSERT(frame_alignment != 0);
2856 ASSERT(num_arguments >= 0);
2857
2858 // Make stack end at alignment and allocate space for arguments and old rsp.
2859 movq(kScratchRegister, rsp);
2860 ASSERT(IsPowerOf2(frame_alignment));
2861 int argument_slots_on_stack =
2862 ArgumentStackSlotsForCFunctionCall(num_arguments);
2863 subq(rsp, Immediate((argument_slots_on_stack + 1) * kPointerSize));
2864 and_(rsp, Immediate(-frame_alignment));
2865 movq(Operand(rsp, argument_slots_on_stack * kPointerSize), kScratchRegister);
2866 }
2867
2868
CallCFunction(ExternalReference function,int num_arguments)2869 void MacroAssembler::CallCFunction(ExternalReference function,
2870 int num_arguments) {
2871 LoadAddress(rax, function);
2872 CallCFunction(rax, num_arguments);
2873 }
2874
2875
CallCFunction(Register function,int num_arguments)2876 void MacroAssembler::CallCFunction(Register function, int num_arguments) {
2877 // Check stack alignment.
2878 if (emit_debug_code()) {
2879 CheckStackAlignment();
2880 }
2881
2882 call(function);
2883 ASSERT(OS::ActivationFrameAlignment() != 0);
2884 ASSERT(num_arguments >= 0);
2885 int argument_slots_on_stack =
2886 ArgumentStackSlotsForCFunctionCall(num_arguments);
2887 movq(rsp, Operand(rsp, argument_slots_on_stack * kPointerSize));
2888 }
2889
2890
CodePatcher(byte * address,int size)2891 CodePatcher::CodePatcher(byte* address, int size)
2892 : address_(address),
2893 size_(size),
2894 masm_(Isolate::Current(), address, size + Assembler::kGap) {
2895 // Create a new macro assembler pointing to the address of the code to patch.
2896 // The size is adjusted with kGap on order for the assembler to generate size
2897 // bytes of instructions without failing with buffer size constraints.
2898 ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
2899 }
2900
2901
~CodePatcher()2902 CodePatcher::~CodePatcher() {
2903 // Indicate that code has changed.
2904 CPU::FlushICache(address_, size_);
2905
2906 // Check that the code was patched as expected.
2907 ASSERT(masm_.pc_ == address_ + size_);
2908 ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
2909 }
2910
2911 } } // namespace v8::internal
2912
2913 #endif // V8_TARGET_ARCH_X64
2914