macro-assembler-x64.cc revision d0582a6c46733687d045e4188a1bcd0123c758a1
1// Copyright 2009 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include "v8.h" 29 30#include "bootstrapper.h" 31#include "codegen-inl.h" 32#include "assembler-x64.h" 33#include "macro-assembler-x64.h" 34#include "serialize.h" 35#include "debug.h" 36 37namespace v8 { 38namespace internal { 39 40MacroAssembler::MacroAssembler(void* buffer, int size) 41 : Assembler(buffer, size), 42 unresolved_(0), 43 generating_stub_(false), 44 allow_stub_calls_(true), 45 code_object_(Heap::undefined_value()) { 46} 47 48 49void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index) { 50 movq(destination, Operand(r13, index << kPointerSizeLog2)); 51} 52 53 54void MacroAssembler::PushRoot(Heap::RootListIndex index) { 55 push(Operand(r13, index << kPointerSizeLog2)); 56} 57 58 59void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) { 60 cmpq(with, Operand(r13, index << kPointerSizeLog2)); 61} 62 63 64void MacroAssembler::CompareRoot(Operand with, Heap::RootListIndex index) { 65 LoadRoot(kScratchRegister, index); 66 cmpq(with, kScratchRegister); 67} 68 69 70void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) { 71 CompareRoot(rsp, Heap::kStackLimitRootIndex); 72 j(below, on_stack_overflow); 73} 74 75 76static void RecordWriteHelper(MacroAssembler* masm, 77 Register object, 78 Register addr, 79 Register scratch) { 80 Label fast; 81 82 // Compute the page start address from the heap object pointer, and reuse 83 // the 'object' register for it. 84 ASSERT(is_int32(~Page::kPageAlignmentMask)); 85 masm->and_(object, 86 Immediate(static_cast<int32_t>(~Page::kPageAlignmentMask))); 87 Register page_start = object; 88 89 // Compute the bit addr in the remembered set/index of the pointer in the 90 // page. Reuse 'addr' as pointer_offset. 91 masm->subq(addr, page_start); 92 masm->shr(addr, Immediate(kPointerSizeLog2)); 93 Register pointer_offset = addr; 94 95 // If the bit offset lies beyond the normal remembered set range, it is in 96 // the extra remembered set area of a large object. 97 masm->cmpq(pointer_offset, Immediate(Page::kPageSize / kPointerSize)); 98 masm->j(less, &fast); 99 100 // Adjust 'page_start' so that addressing using 'pointer_offset' hits the 101 // extra remembered set after the large object. 102 103 // Load the array length into 'scratch'. 104 masm->movl(scratch, 105 Operand(page_start, 106 Page::kObjectStartOffset + FixedArray::kLengthOffset)); 107 Register array_length = scratch; 108 109 // Extra remembered set starts right after the large object (a FixedArray), at 110 // page_start + kObjectStartOffset + objectSize 111 // where objectSize is FixedArray::kHeaderSize + kPointerSize * array_length. 112 // Add the delta between the end of the normal RSet and the start of the 113 // extra RSet to 'page_start', so that addressing the bit using 114 // 'pointer_offset' hits the extra RSet words. 115 masm->lea(page_start, 116 Operand(page_start, array_length, times_pointer_size, 117 Page::kObjectStartOffset + FixedArray::kHeaderSize 118 - Page::kRSetEndOffset)); 119 120 // NOTE: For now, we use the bit-test-and-set (bts) x86 instruction 121 // to limit code size. We should probably evaluate this decision by 122 // measuring the performance of an equivalent implementation using 123 // "simpler" instructions 124 masm->bind(&fast); 125 masm->bts(Operand(page_start, Page::kRSetOffset), pointer_offset); 126} 127 128 129class RecordWriteStub : public CodeStub { 130 public: 131 RecordWriteStub(Register object, Register addr, Register scratch) 132 : object_(object), addr_(addr), scratch_(scratch) { } 133 134 void Generate(MacroAssembler* masm); 135 136 private: 137 Register object_; 138 Register addr_; 139 Register scratch_; 140 141#ifdef DEBUG 142 void Print() { 143 PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n", 144 object_.code(), addr_.code(), scratch_.code()); 145 } 146#endif 147 148 // Minor key encoding in 12 bits of three registers (object, address and 149 // scratch) OOOOAAAASSSS. 150 class ScratchBits : public BitField<uint32_t, 0, 4> {}; 151 class AddressBits : public BitField<uint32_t, 4, 4> {}; 152 class ObjectBits : public BitField<uint32_t, 8, 4> {}; 153 154 Major MajorKey() { return RecordWrite; } 155 156 int MinorKey() { 157 // Encode the registers. 158 return ObjectBits::encode(object_.code()) | 159 AddressBits::encode(addr_.code()) | 160 ScratchBits::encode(scratch_.code()); 161 } 162}; 163 164 165void RecordWriteStub::Generate(MacroAssembler* masm) { 166 RecordWriteHelper(masm, object_, addr_, scratch_); 167 masm->ret(0); 168} 169 170 171// Set the remembered set bit for [object+offset]. 172// object is the object being stored into, value is the object being stored. 173// If offset is zero, then the smi_index register contains the array index into 174// the elements array represented as a smi. Otherwise it can be used as a 175// scratch register. 176// All registers are clobbered by the operation. 177void MacroAssembler::RecordWrite(Register object, 178 int offset, 179 Register value, 180 Register smi_index) { 181 // First, check if a remembered set write is even needed. The tests below 182 // catch stores of Smis and stores into young gen (which does not have space 183 // for the remembered set bits. 184 Label done; 185 JumpIfSmi(value, &done); 186 187 RecordWriteNonSmi(object, offset, value, smi_index); 188 bind(&done); 189} 190 191 192void MacroAssembler::RecordWriteNonSmi(Register object, 193 int offset, 194 Register scratch, 195 Register smi_index) { 196 Label done; 197 // Test that the object address is not in the new space. We cannot 198 // set remembered set bits in the new space. 199 movq(scratch, object); 200 ASSERT(is_int32(static_cast<int64_t>(Heap::NewSpaceMask()))); 201 and_(scratch, Immediate(static_cast<int32_t>(Heap::NewSpaceMask()))); 202 movq(kScratchRegister, ExternalReference::new_space_start()); 203 cmpq(scratch, kScratchRegister); 204 j(equal, &done); 205 206 if ((offset > 0) && (offset < Page::kMaxHeapObjectSize)) { 207 // Compute the bit offset in the remembered set, leave it in 'value'. 208 lea(scratch, Operand(object, offset)); 209 ASSERT(is_int32(Page::kPageAlignmentMask)); 210 and_(scratch, Immediate(static_cast<int32_t>(Page::kPageAlignmentMask))); 211 shr(scratch, Immediate(kObjectAlignmentBits)); 212 213 // Compute the page address from the heap object pointer, leave it in 214 // 'object' (immediate value is sign extended). 215 and_(object, Immediate(~Page::kPageAlignmentMask)); 216 217 // NOTE: For now, we use the bit-test-and-set (bts) x86 instruction 218 // to limit code size. We should probably evaluate this decision by 219 // measuring the performance of an equivalent implementation using 220 // "simpler" instructions 221 bts(Operand(object, Page::kRSetOffset), scratch); 222 } else { 223 Register dst = smi_index; 224 if (offset != 0) { 225 lea(dst, Operand(object, offset)); 226 } else { 227 // array access: calculate the destination address in the same manner as 228 // KeyedStoreIC::GenerateGeneric. 229 SmiIndex index = SmiToIndex(smi_index, smi_index, kPointerSizeLog2); 230 lea(dst, Operand(object, 231 index.reg, 232 index.scale, 233 FixedArray::kHeaderSize - kHeapObjectTag)); 234 } 235 // If we are already generating a shared stub, not inlining the 236 // record write code isn't going to save us any memory. 237 if (generating_stub()) { 238 RecordWriteHelper(this, object, dst, scratch); 239 } else { 240 RecordWriteStub stub(object, dst, scratch); 241 CallStub(&stub); 242 } 243 } 244 245 bind(&done); 246} 247 248 249void MacroAssembler::Assert(Condition cc, const char* msg) { 250 if (FLAG_debug_code) Check(cc, msg); 251} 252 253 254void MacroAssembler::Check(Condition cc, const char* msg) { 255 Label L; 256 j(cc, &L); 257 Abort(msg); 258 // will not return here 259 bind(&L); 260} 261 262 263void MacroAssembler::NegativeZeroTest(Register result, 264 Register op, 265 Label* then_label) { 266 Label ok; 267 testl(result, result); 268 j(not_zero, &ok); 269 testl(op, op); 270 j(sign, then_label); 271 bind(&ok); 272} 273 274 275void MacroAssembler::Abort(const char* msg) { 276 // We want to pass the msg string like a smi to avoid GC 277 // problems, however msg is not guaranteed to be aligned 278 // properly. Instead, we pass an aligned pointer that is 279 // a proper v8 smi, but also pass the alignment difference 280 // from the real pointer as a smi. 281 intptr_t p1 = reinterpret_cast<intptr_t>(msg); 282 intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag; 283 // Note: p0 might not be a valid Smi *value*, but it has a valid Smi tag. 284 ASSERT(reinterpret_cast<Object*>(p0)->IsSmi()); 285#ifdef DEBUG 286 if (msg != NULL) { 287 RecordComment("Abort message: "); 288 RecordComment(msg); 289 } 290#endif 291 // Disable stub call restrictions to always allow calls to abort. 292 set_allow_stub_calls(true); 293 294 push(rax); 295 movq(kScratchRegister, p0, RelocInfo::NONE); 296 push(kScratchRegister); 297 movq(kScratchRegister, 298 reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(p1 - p0))), 299 RelocInfo::NONE); 300 push(kScratchRegister); 301 CallRuntime(Runtime::kAbort, 2); 302 // will not return here 303 int3(); 304} 305 306 307void MacroAssembler::CallStub(CodeStub* stub) { 308 ASSERT(allow_stub_calls()); // calls are not allowed in some stubs 309 Call(stub->GetCode(), RelocInfo::CODE_TARGET); 310} 311 312 313void MacroAssembler::StubReturn(int argc) { 314 ASSERT(argc >= 1 && generating_stub()); 315 ret((argc - 1) * kPointerSize); 316} 317 318 319void MacroAssembler::IllegalOperation(int num_arguments) { 320 if (num_arguments > 0) { 321 addq(rsp, Immediate(num_arguments * kPointerSize)); 322 } 323 LoadRoot(rax, Heap::kUndefinedValueRootIndex); 324} 325 326 327void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) { 328 CallRuntime(Runtime::FunctionForId(id), num_arguments); 329} 330 331 332void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) { 333 // If the expected number of arguments of the runtime function is 334 // constant, we check that the actual number of arguments match the 335 // expectation. 336 if (f->nargs >= 0 && f->nargs != num_arguments) { 337 IllegalOperation(num_arguments); 338 return; 339 } 340 341 Runtime::FunctionId function_id = 342 static_cast<Runtime::FunctionId>(f->stub_id); 343 RuntimeStub stub(function_id, num_arguments); 344 CallStub(&stub); 345} 346 347 348void MacroAssembler::TailCallRuntime(ExternalReference const& ext, 349 int num_arguments, 350 int result_size) { 351 // ----------- S t a t e ------------- 352 // -- rsp[0] : return address 353 // -- rsp[8] : argument num_arguments - 1 354 // ... 355 // -- rsp[8 * num_arguments] : argument 0 (receiver) 356 // ----------------------------------- 357 358 // TODO(1236192): Most runtime routines don't need the number of 359 // arguments passed in because it is constant. At some point we 360 // should remove this need and make the runtime routine entry code 361 // smarter. 362 movq(rax, Immediate(num_arguments)); 363 JumpToRuntime(ext, result_size); 364} 365 366 367void MacroAssembler::JumpToRuntime(const ExternalReference& ext, 368 int result_size) { 369 // Set the entry point and jump to the C entry runtime stub. 370 movq(rbx, ext); 371 CEntryStub ces(result_size); 372 jmp(ces.GetCode(), RelocInfo::CODE_TARGET); 373} 374 375 376void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) { 377 bool resolved; 378 Handle<Code> code = ResolveBuiltin(id, &resolved); 379 380 const char* name = Builtins::GetName(id); 381 int argc = Builtins::GetArgumentsCount(id); 382 383 movq(target, code, RelocInfo::EMBEDDED_OBJECT); 384 if (!resolved) { 385 uint32_t flags = 386 Bootstrapper::FixupFlagsArgumentsCount::encode(argc) | 387 Bootstrapper::FixupFlagsUseCodeObject::encode(true); 388 Unresolved entry = { pc_offset() - sizeof(intptr_t), flags, name }; 389 unresolved_.Add(entry); 390 } 391 addq(target, Immediate(Code::kHeaderSize - kHeapObjectTag)); 392} 393 394Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id, 395 bool* resolved) { 396 // Move the builtin function into the temporary function slot by 397 // reading it from the builtins object. NOTE: We should be able to 398 // reduce this to two instructions by putting the function table in 399 // the global object instead of the "builtins" object and by using a 400 // real register for the function. 401 movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); 402 movq(rdx, FieldOperand(rdx, GlobalObject::kBuiltinsOffset)); 403 int builtins_offset = 404 JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize); 405 movq(rdi, FieldOperand(rdx, builtins_offset)); 406 407 return Builtins::GetCode(id, resolved); 408} 409 410 411void MacroAssembler::Set(Register dst, int64_t x) { 412 if (x == 0) { 413 xor_(dst, dst); 414 } else if (is_int32(x)) { 415 movq(dst, Immediate(static_cast<int32_t>(x))); 416 } else if (is_uint32(x)) { 417 movl(dst, Immediate(static_cast<uint32_t>(x))); 418 } else { 419 movq(dst, x, RelocInfo::NONE); 420 } 421} 422 423 424void MacroAssembler::Set(const Operand& dst, int64_t x) { 425 if (x == 0) { 426 xor_(kScratchRegister, kScratchRegister); 427 movq(dst, kScratchRegister); 428 } else if (is_int32(x)) { 429 movq(dst, Immediate(static_cast<int32_t>(x))); 430 } else if (is_uint32(x)) { 431 movl(dst, Immediate(static_cast<uint32_t>(x))); 432 } else { 433 movq(kScratchRegister, x, RelocInfo::NONE); 434 movq(dst, kScratchRegister); 435 } 436} 437 438// ---------------------------------------------------------------------------- 439// Smi tagging, untagging and tag detection. 440 441static int kSmiShift = kSmiTagSize + kSmiShiftSize; 442 443void MacroAssembler::Integer32ToSmi(Register dst, Register src) { 444 ASSERT_EQ(0, kSmiTag); 445 if (!dst.is(src)) { 446 movl(dst, src); 447 } 448 shl(dst, Immediate(kSmiShift)); 449} 450 451 452void MacroAssembler::Integer32ToSmi(Register dst, 453 Register src, 454 Label* on_overflow) { 455 ASSERT_EQ(0, kSmiTag); 456 // 32-bit integer always fits in a long smi. 457 if (!dst.is(src)) { 458 movl(dst, src); 459 } 460 shl(dst, Immediate(kSmiShift)); 461} 462 463 464void MacroAssembler::Integer64PlusConstantToSmi(Register dst, 465 Register src, 466 int constant) { 467 if (dst.is(src)) { 468 addq(dst, Immediate(constant)); 469 } else { 470 lea(dst, Operand(src, constant)); 471 } 472 shl(dst, Immediate(kSmiShift)); 473} 474 475 476void MacroAssembler::SmiToInteger32(Register dst, Register src) { 477 ASSERT_EQ(0, kSmiTag); 478 if (!dst.is(src)) { 479 movq(dst, src); 480 } 481 shr(dst, Immediate(kSmiShift)); 482} 483 484 485void MacroAssembler::SmiToInteger64(Register dst, Register src) { 486 ASSERT_EQ(0, kSmiTag); 487 if (!dst.is(src)) { 488 movq(dst, src); 489 } 490 sar(dst, Immediate(kSmiShift)); 491} 492 493 494void MacroAssembler::SmiTest(Register src) { 495 testq(src, src); 496} 497 498 499void MacroAssembler::SmiCompare(Register dst, Register src) { 500 cmpq(dst, src); 501} 502 503 504void MacroAssembler::SmiCompare(Register dst, Smi* src) { 505 ASSERT(!dst.is(kScratchRegister)); 506 if (src->value() == 0) { 507 testq(dst, dst); 508 } else { 509 Move(kScratchRegister, src); 510 cmpq(dst, kScratchRegister); 511 } 512} 513 514 515void MacroAssembler::SmiCompare(const Operand& dst, Register src) { 516 cmpq(dst, src); 517} 518 519 520void MacroAssembler::SmiCompare(const Operand& dst, Smi* src) { 521 if (src->value() == 0) { 522 // Only tagged long smi to have 32-bit representation. 523 cmpq(dst, Immediate(0)); 524 } else { 525 Move(kScratchRegister, src); 526 cmpq(dst, kScratchRegister); 527 } 528} 529 530 531void MacroAssembler::PositiveSmiTimesPowerOfTwoToInteger64(Register dst, 532 Register src, 533 int power) { 534 ASSERT(power >= 0); 535 ASSERT(power < 64); 536 if (power == 0) { 537 SmiToInteger64(dst, src); 538 return; 539 } 540 if (!dst.is(src)) { 541 movq(dst, src); 542 } 543 if (power < kSmiShift) { 544 sar(dst, Immediate(kSmiShift - power)); 545 } else if (power > kSmiShift) { 546 shl(dst, Immediate(power - kSmiShift)); 547 } 548} 549 550 551Condition MacroAssembler::CheckSmi(Register src) { 552 ASSERT_EQ(0, kSmiTag); 553 testb(src, Immediate(kSmiTagMask)); 554 return zero; 555} 556 557 558Condition MacroAssembler::CheckPositiveSmi(Register src) { 559 ASSERT_EQ(0, kSmiTag); 560 movq(kScratchRegister, src); 561 rol(kScratchRegister, Immediate(1)); 562 testl(kScratchRegister, Immediate(0x03)); 563 return zero; 564} 565 566 567Condition MacroAssembler::CheckBothSmi(Register first, Register second) { 568 if (first.is(second)) { 569 return CheckSmi(first); 570 } 571 movl(kScratchRegister, first); 572 orl(kScratchRegister, second); 573 testb(kScratchRegister, Immediate(kSmiTagMask)); 574 return zero; 575} 576 577 578Condition MacroAssembler::CheckIsMinSmi(Register src) { 579 ASSERT(kSmiTag == 0 && kSmiTagSize == 1); 580 movq(kScratchRegister, src); 581 rol(kScratchRegister, Immediate(1)); 582 cmpq(kScratchRegister, Immediate(1)); 583 return equal; 584} 585 586 587Condition MacroAssembler::CheckInteger32ValidSmiValue(Register src) { 588 // A 32-bit integer value can always be converted to a smi. 589 return always; 590} 591 592 593Condition MacroAssembler::CheckUInteger32ValidSmiValue(Register src) { 594 // An unsigned 32-bit integer value is valid as long as the high bit 595 // is not set. 596 testq(src, Immediate(0x80000000)); 597 return zero; 598} 599 600 601void MacroAssembler::SmiNeg(Register dst, Register src, Label* on_smi_result) { 602 if (dst.is(src)) { 603 ASSERT(!dst.is(kScratchRegister)); 604 movq(kScratchRegister, src); 605 neg(dst); // Low 32 bits are retained as zero by negation. 606 // Test if result is zero or Smi::kMinValue. 607 cmpq(dst, kScratchRegister); 608 j(not_equal, on_smi_result); 609 movq(src, kScratchRegister); 610 } else { 611 movq(dst, src); 612 neg(dst); 613 cmpq(dst, src); 614 // If the result is zero or Smi::kMinValue, negation failed to create a smi. 615 j(not_equal, on_smi_result); 616 } 617} 618 619 620void MacroAssembler::SmiAdd(Register dst, 621 Register src1, 622 Register src2, 623 Label* on_not_smi_result) { 624 ASSERT(!dst.is(src2)); 625 if (dst.is(src1)) { 626 addq(dst, src2); 627 Label smi_result; 628 j(no_overflow, &smi_result); 629 // Restore src1. 630 subq(src1, src2); 631 jmp(on_not_smi_result); 632 bind(&smi_result); 633 } else { 634 movq(dst, src1); 635 addq(dst, src2); 636 j(overflow, on_not_smi_result); 637 } 638} 639 640 641void MacroAssembler::SmiSub(Register dst, 642 Register src1, 643 Register src2, 644 Label* on_not_smi_result) { 645 ASSERT(!dst.is(src2)); 646 if (dst.is(src1)) { 647 subq(dst, src2); 648 Label smi_result; 649 j(no_overflow, &smi_result); 650 // Restore src1. 651 addq(src1, src2); 652 jmp(on_not_smi_result); 653 bind(&smi_result); 654 } else { 655 movq(dst, src1); 656 subq(dst, src2); 657 j(overflow, on_not_smi_result); 658 } 659} 660 661 662void MacroAssembler::SmiMul(Register dst, 663 Register src1, 664 Register src2, 665 Label* on_not_smi_result) { 666 ASSERT(!dst.is(src2)); 667 ASSERT(!dst.is(kScratchRegister)); 668 ASSERT(!src1.is(kScratchRegister)); 669 ASSERT(!src2.is(kScratchRegister)); 670 671 if (dst.is(src1)) { 672 Label failure, zero_correct_result; 673 movq(kScratchRegister, src1); // Create backup for later testing. 674 SmiToInteger64(dst, src1); 675 imul(dst, src2); 676 j(overflow, &failure); 677 678 // Check for negative zero result. If product is zero, and one 679 // argument is negative, go to slow case. 680 Label correct_result; 681 testq(dst, dst); 682 j(not_zero, &correct_result); 683 684 movq(dst, kScratchRegister); 685 xor_(dst, src2); 686 j(positive, &zero_correct_result); // Result was positive zero. 687 688 bind(&failure); // Reused failure exit, restores src1. 689 movq(src1, kScratchRegister); 690 jmp(on_not_smi_result); 691 692 bind(&zero_correct_result); 693 xor_(dst, dst); 694 695 bind(&correct_result); 696 } else { 697 SmiToInteger64(dst, src1); 698 imul(dst, src2); 699 j(overflow, on_not_smi_result); 700 // Check for negative zero result. If product is zero, and one 701 // argument is negative, go to slow case. 702 Label correct_result; 703 testq(dst, dst); 704 j(not_zero, &correct_result); 705 // One of src1 and src2 is zero, the check whether the other is 706 // negative. 707 movq(kScratchRegister, src1); 708 xor_(kScratchRegister, src2); 709 j(negative, on_not_smi_result); 710 bind(&correct_result); 711 } 712} 713 714 715void MacroAssembler::SmiTryAddConstant(Register dst, 716 Register src, 717 Smi* constant, 718 Label* on_not_smi_result) { 719 // Does not assume that src is a smi. 720 ASSERT_EQ(static_cast<int>(1), static_cast<int>(kSmiTagMask)); 721 ASSERT_EQ(0, kSmiTag); 722 ASSERT(!dst.is(kScratchRegister)); 723 ASSERT(!src.is(kScratchRegister)); 724 725 JumpIfNotSmi(src, on_not_smi_result); 726 Register tmp = (dst.is(src) ? kScratchRegister : dst); 727 Move(tmp, constant); 728 addq(tmp, src); 729 j(overflow, on_not_smi_result); 730 if (dst.is(src)) { 731 movq(dst, tmp); 732 } 733} 734 735 736void MacroAssembler::SmiAddConstant(Register dst, Register src, Smi* constant) { 737 if (constant->value() == 0) { 738 if (!dst.is(src)) { 739 movq(dst, src); 740 } 741 } else if (dst.is(src)) { 742 ASSERT(!dst.is(kScratchRegister)); 743 744 Move(kScratchRegister, constant); 745 addq(dst, kScratchRegister); 746 } else { 747 Move(dst, constant); 748 addq(dst, src); 749 } 750} 751 752 753void MacroAssembler::SmiAddConstant(Register dst, 754 Register src, 755 Smi* constant, 756 Label* on_not_smi_result) { 757 if (constant->value() == 0) { 758 if (!dst.is(src)) { 759 movq(dst, src); 760 } 761 } else if (dst.is(src)) { 762 ASSERT(!dst.is(kScratchRegister)); 763 764 Move(kScratchRegister, constant); 765 addq(dst, kScratchRegister); 766 Label result_ok; 767 j(no_overflow, &result_ok); 768 subq(dst, kScratchRegister); 769 jmp(on_not_smi_result); 770 bind(&result_ok); 771 } else { 772 Move(dst, constant); 773 addq(dst, src); 774 j(overflow, on_not_smi_result); 775 } 776} 777 778 779void MacroAssembler::SmiSubConstant(Register dst, Register src, Smi* constant) { 780 if (constant->value() == 0) { 781 if (!dst.is(src)) { 782 movq(dst, src); 783 } 784 } else if (dst.is(src)) { 785 ASSERT(!dst.is(kScratchRegister)); 786 787 Move(kScratchRegister, constant); 788 subq(dst, kScratchRegister); 789 } else { 790 // Subtract by adding the negative, to do it in two operations. 791 if (constant->value() == Smi::kMinValue) { 792 Move(kScratchRegister, constant); 793 movq(dst, src); 794 subq(dst, kScratchRegister); 795 } else { 796 Move(dst, Smi::FromInt(-constant->value())); 797 addq(dst, src); 798 } 799 } 800} 801 802 803void MacroAssembler::SmiSubConstant(Register dst, 804 Register src, 805 Smi* constant, 806 Label* on_not_smi_result) { 807 if (constant->value() == 0) { 808 if (!dst.is(src)) { 809 movq(dst, src); 810 } 811 } else if (dst.is(src)) { 812 ASSERT(!dst.is(kScratchRegister)); 813 814 Move(kScratchRegister, constant); 815 subq(dst, kScratchRegister); 816 Label sub_success; 817 j(no_overflow, &sub_success); 818 addq(src, kScratchRegister); 819 jmp(on_not_smi_result); 820 bind(&sub_success); 821 } else { 822 if (constant->value() == Smi::kMinValue) { 823 Move(kScratchRegister, constant); 824 movq(dst, src); 825 subq(dst, kScratchRegister); 826 j(overflow, on_not_smi_result); 827 } else { 828 Move(dst, Smi::FromInt(-(constant->value()))); 829 addq(dst, src); 830 j(overflow, on_not_smi_result); 831 } 832 } 833} 834 835 836void MacroAssembler::SmiDiv(Register dst, 837 Register src1, 838 Register src2, 839 Label* on_not_smi_result) { 840 ASSERT(!src1.is(kScratchRegister)); 841 ASSERT(!src2.is(kScratchRegister)); 842 ASSERT(!dst.is(kScratchRegister)); 843 ASSERT(!src2.is(rax)); 844 ASSERT(!src2.is(rdx)); 845 ASSERT(!src1.is(rdx)); 846 847 // Check for 0 divisor (result is +/-Infinity). 848 Label positive_divisor; 849 testq(src2, src2); 850 j(zero, on_not_smi_result); 851 852 if (src1.is(rax)) { 853 movq(kScratchRegister, src1); 854 } 855 SmiToInteger32(rax, src1); 856 // We need to rule out dividing Smi::kMinValue by -1, since that would 857 // overflow in idiv and raise an exception. 858 // We combine this with negative zero test (negative zero only happens 859 // when dividing zero by a negative number). 860 861 // We overshoot a little and go to slow case if we divide min-value 862 // by any negative value, not just -1. 863 Label safe_div; 864 testl(rax, Immediate(0x7fffffff)); 865 j(not_zero, &safe_div); 866 testq(src2, src2); 867 if (src1.is(rax)) { 868 j(positive, &safe_div); 869 movq(src1, kScratchRegister); 870 jmp(on_not_smi_result); 871 } else { 872 j(negative, on_not_smi_result); 873 } 874 bind(&safe_div); 875 876 SmiToInteger32(src2, src2); 877 // Sign extend src1 into edx:eax. 878 cdq(); 879 idivl(src2); 880 Integer32ToSmi(src2, src2); 881 // Check that the remainder is zero. 882 testl(rdx, rdx); 883 if (src1.is(rax)) { 884 Label smi_result; 885 j(zero, &smi_result); 886 movq(src1, kScratchRegister); 887 jmp(on_not_smi_result); 888 bind(&smi_result); 889 } else { 890 j(not_zero, on_not_smi_result); 891 } 892 if (!dst.is(src1) && src1.is(rax)) { 893 movq(src1, kScratchRegister); 894 } 895 Integer32ToSmi(dst, rax); 896} 897 898 899void MacroAssembler::SmiMod(Register dst, 900 Register src1, 901 Register src2, 902 Label* on_not_smi_result) { 903 ASSERT(!dst.is(kScratchRegister)); 904 ASSERT(!src1.is(kScratchRegister)); 905 ASSERT(!src2.is(kScratchRegister)); 906 ASSERT(!src2.is(rax)); 907 ASSERT(!src2.is(rdx)); 908 ASSERT(!src1.is(rdx)); 909 ASSERT(!src1.is(src2)); 910 911 testq(src2, src2); 912 j(zero, on_not_smi_result); 913 914 if (src1.is(rax)) { 915 movq(kScratchRegister, src1); 916 } 917 SmiToInteger32(rax, src1); 918 SmiToInteger32(src2, src2); 919 920 // Test for the edge case of dividing Smi::kMinValue by -1 (will overflow). 921 Label safe_div; 922 cmpl(rax, Immediate(Smi::kMinValue)); 923 j(not_equal, &safe_div); 924 cmpl(src2, Immediate(-1)); 925 j(not_equal, &safe_div); 926 // Retag inputs and go slow case. 927 Integer32ToSmi(src2, src2); 928 if (src1.is(rax)) { 929 movq(src1, kScratchRegister); 930 } 931 jmp(on_not_smi_result); 932 bind(&safe_div); 933 934 // Sign extend eax into edx:eax. 935 cdq(); 936 idivl(src2); 937 // Restore smi tags on inputs. 938 Integer32ToSmi(src2, src2); 939 if (src1.is(rax)) { 940 movq(src1, kScratchRegister); 941 } 942 // Check for a negative zero result. If the result is zero, and the 943 // dividend is negative, go slow to return a floating point negative zero. 944 Label smi_result; 945 testl(rdx, rdx); 946 j(not_zero, &smi_result); 947 testq(src1, src1); 948 j(negative, on_not_smi_result); 949 bind(&smi_result); 950 Integer32ToSmi(dst, rdx); 951} 952 953 954void MacroAssembler::SmiNot(Register dst, Register src) { 955 ASSERT(!dst.is(kScratchRegister)); 956 ASSERT(!src.is(kScratchRegister)); 957 // Set tag and padding bits before negating, so that they are zero afterwards. 958 movl(kScratchRegister, Immediate(~0)); 959 if (dst.is(src)) { 960 xor_(dst, kScratchRegister); 961 } else { 962 lea(dst, Operand(src, kScratchRegister, times_1, 0)); 963 } 964 not_(dst); 965} 966 967 968void MacroAssembler::SmiAnd(Register dst, Register src1, Register src2) { 969 ASSERT(!dst.is(src2)); 970 if (!dst.is(src1)) { 971 movq(dst, src1); 972 } 973 and_(dst, src2); 974} 975 976 977void MacroAssembler::SmiAndConstant(Register dst, Register src, Smi* constant) { 978 if (constant->value() == 0) { 979 xor_(dst, dst); 980 } else if (dst.is(src)) { 981 ASSERT(!dst.is(kScratchRegister)); 982 Move(kScratchRegister, constant); 983 and_(dst, kScratchRegister); 984 } else { 985 Move(dst, constant); 986 and_(dst, src); 987 } 988} 989 990 991void MacroAssembler::SmiOr(Register dst, Register src1, Register src2) { 992 if (!dst.is(src1)) { 993 movq(dst, src1); 994 } 995 or_(dst, src2); 996} 997 998 999void MacroAssembler::SmiOrConstant(Register dst, Register src, Smi* constant) { 1000 if (dst.is(src)) { 1001 ASSERT(!dst.is(kScratchRegister)); 1002 Move(kScratchRegister, constant); 1003 or_(dst, kScratchRegister); 1004 } else { 1005 Move(dst, constant); 1006 or_(dst, src); 1007 } 1008} 1009 1010 1011void MacroAssembler::SmiXor(Register dst, Register src1, Register src2) { 1012 if (!dst.is(src1)) { 1013 movq(dst, src1); 1014 } 1015 xor_(dst, src2); 1016} 1017 1018 1019void MacroAssembler::SmiXorConstant(Register dst, Register src, Smi* constant) { 1020 if (dst.is(src)) { 1021 ASSERT(!dst.is(kScratchRegister)); 1022 Move(kScratchRegister, constant); 1023 xor_(dst, kScratchRegister); 1024 } else { 1025 Move(dst, constant); 1026 xor_(dst, src); 1027 } 1028} 1029 1030 1031void MacroAssembler::SmiShiftArithmeticRightConstant(Register dst, 1032 Register src, 1033 int shift_value) { 1034 ASSERT(is_uint5(shift_value)); 1035 if (shift_value > 0) { 1036 if (dst.is(src)) { 1037 sar(dst, Immediate(shift_value + kSmiShift)); 1038 shl(dst, Immediate(kSmiShift)); 1039 } else { 1040 UNIMPLEMENTED(); // Not used. 1041 } 1042 } 1043} 1044 1045 1046void MacroAssembler::SmiShiftLogicalRightConstant(Register dst, 1047 Register src, 1048 int shift_value, 1049 Label* on_not_smi_result) { 1050 // Logic right shift interprets its result as an *unsigned* number. 1051 if (dst.is(src)) { 1052 UNIMPLEMENTED(); // Not used. 1053 } else { 1054 movq(dst, src); 1055 if (shift_value == 0) { 1056 testq(dst, dst); 1057 j(negative, on_not_smi_result); 1058 } 1059 shr(dst, Immediate(shift_value + kSmiShift)); 1060 shl(dst, Immediate(kSmiShift)); 1061 } 1062} 1063 1064 1065void MacroAssembler::SmiShiftLeftConstant(Register dst, 1066 Register src, 1067 int shift_value, 1068 Label* on_not_smi_result) { 1069 if (!dst.is(src)) { 1070 movq(dst, src); 1071 } 1072 if (shift_value > 0) { 1073 shl(dst, Immediate(shift_value)); 1074 } 1075} 1076 1077 1078void MacroAssembler::SmiShiftLeft(Register dst, 1079 Register src1, 1080 Register src2, 1081 Label* on_not_smi_result) { 1082 ASSERT(!dst.is(rcx)); 1083 Label result_ok; 1084 // Untag shift amount. 1085 if (!dst.is(src1)) { 1086 movq(dst, src1); 1087 } 1088 SmiToInteger32(rcx, src2); 1089 // Shift amount specified by lower 5 bits, not six as the shl opcode. 1090 and_(rcx, Immediate(0x1f)); 1091 shl_cl(dst); 1092} 1093 1094 1095void MacroAssembler::SmiShiftLogicalRight(Register dst, 1096 Register src1, 1097 Register src2, 1098 Label* on_not_smi_result) { 1099 ASSERT(!dst.is(kScratchRegister)); 1100 ASSERT(!src1.is(kScratchRegister)); 1101 ASSERT(!src2.is(kScratchRegister)); 1102 ASSERT(!dst.is(rcx)); 1103 Label result_ok; 1104 if (src1.is(rcx) || src2.is(rcx)) { 1105 movq(kScratchRegister, rcx); 1106 } 1107 if (!dst.is(src1)) { 1108 movq(dst, src1); 1109 } 1110 SmiToInteger32(rcx, src2); 1111 orl(rcx, Immediate(kSmiShift)); 1112 shr_cl(dst); // Shift is rcx modulo 0x1f + 32. 1113 shl(dst, Immediate(kSmiShift)); 1114 testq(dst, dst); 1115 if (src1.is(rcx) || src2.is(rcx)) { 1116 Label positive_result; 1117 j(positive, &positive_result); 1118 if (src1.is(rcx)) { 1119 movq(src1, kScratchRegister); 1120 } else { 1121 movq(src2, kScratchRegister); 1122 } 1123 jmp(on_not_smi_result); 1124 bind(&positive_result); 1125 } else { 1126 j(negative, on_not_smi_result); // src2 was zero and src1 negative. 1127 } 1128} 1129 1130 1131void MacroAssembler::SmiShiftArithmeticRight(Register dst, 1132 Register src1, 1133 Register src2) { 1134 ASSERT(!dst.is(kScratchRegister)); 1135 ASSERT(!src1.is(kScratchRegister)); 1136 ASSERT(!src2.is(kScratchRegister)); 1137 ASSERT(!dst.is(rcx)); 1138 if (src1.is(rcx)) { 1139 movq(kScratchRegister, src1); 1140 } else if (src2.is(rcx)) { 1141 movq(kScratchRegister, src2); 1142 } 1143 if (!dst.is(src1)) { 1144 movq(dst, src1); 1145 } 1146 SmiToInteger32(rcx, src2); 1147 orl(rcx, Immediate(kSmiShift)); 1148 sar_cl(dst); // Shift 32 + original rcx & 0x1f. 1149 shl(dst, Immediate(kSmiShift)); 1150 if (src1.is(rcx)) { 1151 movq(src1, kScratchRegister); 1152 } else if (src2.is(rcx)) { 1153 movq(src2, kScratchRegister); 1154 } 1155} 1156 1157 1158void MacroAssembler::SelectNonSmi(Register dst, 1159 Register src1, 1160 Register src2, 1161 Label* on_not_smis) { 1162 ASSERT(!dst.is(kScratchRegister)); 1163 ASSERT(!src1.is(kScratchRegister)); 1164 ASSERT(!src2.is(kScratchRegister)); 1165 ASSERT(!dst.is(src1)); 1166 ASSERT(!dst.is(src2)); 1167 // Both operands must not be smis. 1168#ifdef DEBUG 1169 if (allow_stub_calls()) { // Check contains a stub call. 1170 Condition not_both_smis = NegateCondition(CheckBothSmi(src1, src2)); 1171 Check(not_both_smis, "Both registers were smis in SelectNonSmi."); 1172 } 1173#endif 1174 ASSERT_EQ(0, kSmiTag); 1175 ASSERT_EQ(0, Smi::FromInt(0)); 1176 movl(kScratchRegister, Immediate(kSmiTagMask)); 1177 and_(kScratchRegister, src1); 1178 testl(kScratchRegister, src2); 1179 // If non-zero then both are smis. 1180 j(not_zero, on_not_smis); 1181 1182 // Exactly one operand is a smi. 1183 ASSERT_EQ(1, static_cast<int>(kSmiTagMask)); 1184 // kScratchRegister still holds src1 & kSmiTag, which is either zero or one. 1185 subq(kScratchRegister, Immediate(1)); 1186 // If src1 is a smi, then scratch register all 1s, else it is all 0s. 1187 movq(dst, src1); 1188 xor_(dst, src2); 1189 and_(dst, kScratchRegister); 1190 // If src1 is a smi, dst holds src1 ^ src2, else it is zero. 1191 xor_(dst, src1); 1192 // If src1 is a smi, dst is src2, else it is src1, i.e., the non-smi. 1193} 1194 1195SmiIndex MacroAssembler::SmiToIndex(Register dst, 1196 Register src, 1197 int shift) { 1198 ASSERT(is_uint6(shift)); 1199 // There is a possible optimization if shift is in the range 60-63, but that 1200 // will (and must) never happen. 1201 if (!dst.is(src)) { 1202 movq(dst, src); 1203 } 1204 if (shift < kSmiShift) { 1205 sar(dst, Immediate(kSmiShift - shift)); 1206 } else { 1207 shl(dst, Immediate(shift - kSmiShift)); 1208 } 1209 return SmiIndex(dst, times_1); 1210} 1211 1212SmiIndex MacroAssembler::SmiToNegativeIndex(Register dst, 1213 Register src, 1214 int shift) { 1215 // Register src holds a positive smi. 1216 ASSERT(is_uint6(shift)); 1217 if (!dst.is(src)) { 1218 movq(dst, src); 1219 } 1220 neg(dst); 1221 if (shift < kSmiShift) { 1222 sar(dst, Immediate(kSmiShift - shift)); 1223 } else { 1224 shl(dst, Immediate(shift - kSmiShift)); 1225 } 1226 return SmiIndex(dst, times_1); 1227} 1228 1229 1230void MacroAssembler::JumpIfSmi(Register src, Label* on_smi) { 1231 ASSERT_EQ(0, kSmiTag); 1232 Condition smi = CheckSmi(src); 1233 j(smi, on_smi); 1234} 1235 1236 1237void MacroAssembler::JumpIfNotSmi(Register src, Label* on_not_smi) { 1238 Condition smi = CheckSmi(src); 1239 j(NegateCondition(smi), on_not_smi); 1240} 1241 1242 1243void MacroAssembler::JumpIfNotPositiveSmi(Register src, 1244 Label* on_not_positive_smi) { 1245 Condition positive_smi = CheckPositiveSmi(src); 1246 j(NegateCondition(positive_smi), on_not_positive_smi); 1247} 1248 1249 1250void MacroAssembler::JumpIfSmiEqualsConstant(Register src, 1251 Smi* constant, 1252 Label* on_equals) { 1253 SmiCompare(src, constant); 1254 j(equal, on_equals); 1255} 1256 1257 1258void MacroAssembler::JumpIfNotValidSmiValue(Register src, Label* on_invalid) { 1259 Condition is_valid = CheckInteger32ValidSmiValue(src); 1260 j(NegateCondition(is_valid), on_invalid); 1261} 1262 1263 1264void MacroAssembler::JumpIfUIntNotValidSmiValue(Register src, 1265 Label* on_invalid) { 1266 Condition is_valid = CheckUInteger32ValidSmiValue(src); 1267 j(NegateCondition(is_valid), on_invalid); 1268} 1269 1270 1271void MacroAssembler::JumpIfNotBothSmi(Register src1, Register src2, 1272 Label* on_not_both_smi) { 1273 Condition both_smi = CheckBothSmi(src1, src2); 1274 j(NegateCondition(both_smi), on_not_both_smi); 1275} 1276 1277 1278void MacroAssembler::Move(Register dst, Handle<Object> source) { 1279 ASSERT(!source->IsFailure()); 1280 if (source->IsSmi()) { 1281 Move(dst, Smi::cast(*source)); 1282 } else { 1283 movq(dst, source, RelocInfo::EMBEDDED_OBJECT); 1284 } 1285} 1286 1287 1288void MacroAssembler::Move(const Operand& dst, Handle<Object> source) { 1289 ASSERT(!source->IsFailure()); 1290 if (source->IsSmi()) { 1291 Move(dst, Smi::cast(*source)); 1292 } else { 1293 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT); 1294 movq(dst, kScratchRegister); 1295 } 1296} 1297 1298 1299void MacroAssembler::Cmp(Register dst, Handle<Object> source) { 1300 if (source->IsSmi()) { 1301 SmiCompare(dst, Smi::cast(*source)); 1302 } else { 1303 Move(kScratchRegister, source); 1304 cmpq(dst, kScratchRegister); 1305 } 1306} 1307 1308 1309void MacroAssembler::Cmp(const Operand& dst, Handle<Object> source) { 1310 if (source->IsSmi()) { 1311 SmiCompare(dst, Smi::cast(*source)); 1312 } else { 1313 ASSERT(source->IsHeapObject()); 1314 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT); 1315 cmpq(dst, kScratchRegister); 1316 } 1317} 1318 1319 1320void MacroAssembler::Push(Handle<Object> source) { 1321 if (source->IsSmi()) { 1322 Push(Smi::cast(*source)); 1323 } else { 1324 ASSERT(source->IsHeapObject()); 1325 movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT); 1326 push(kScratchRegister); 1327 } 1328} 1329 1330 1331void MacroAssembler::Push(Smi* source) { 1332 intptr_t smi = reinterpret_cast<intptr_t>(source); 1333 if (is_int32(smi)) { 1334 push(Immediate(static_cast<int32_t>(smi))); 1335 } else { 1336 Set(kScratchRegister, smi); 1337 push(kScratchRegister); 1338 } 1339} 1340 1341 1342void MacroAssembler::Test(const Operand& src, Smi* source) { 1343 intptr_t smi = reinterpret_cast<intptr_t>(source); 1344 if (is_int32(smi)) { 1345 testl(src, Immediate(static_cast<int32_t>(smi))); 1346 } else { 1347 Move(kScratchRegister, source); 1348 testq(src, kScratchRegister); 1349 } 1350} 1351 1352 1353void MacroAssembler::Jump(ExternalReference ext) { 1354 movq(kScratchRegister, ext); 1355 jmp(kScratchRegister); 1356} 1357 1358 1359void MacroAssembler::Jump(Address destination, RelocInfo::Mode rmode) { 1360 movq(kScratchRegister, destination, rmode); 1361 jmp(kScratchRegister); 1362} 1363 1364 1365void MacroAssembler::Jump(Handle<Code> code_object, RelocInfo::Mode rmode) { 1366 // TODO(X64): Inline this 1367 jmp(code_object, rmode); 1368} 1369 1370 1371void MacroAssembler::Call(ExternalReference ext) { 1372 movq(kScratchRegister, ext); 1373 call(kScratchRegister); 1374} 1375 1376 1377void MacroAssembler::Call(Address destination, RelocInfo::Mode rmode) { 1378 movq(kScratchRegister, destination, rmode); 1379 call(kScratchRegister); 1380} 1381 1382 1383void MacroAssembler::Call(Handle<Code> code_object, RelocInfo::Mode rmode) { 1384 ASSERT(RelocInfo::IsCodeTarget(rmode)); 1385 WriteRecordedPositions(); 1386 call(code_object, rmode); 1387} 1388 1389 1390void MacroAssembler::PushTryHandler(CodeLocation try_location, 1391 HandlerType type) { 1392 // Adjust this code if not the case. 1393 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); 1394 1395 // The pc (return address) is already on TOS. This code pushes state, 1396 // frame pointer and current handler. Check that they are expected 1397 // next on the stack, in that order. 1398 ASSERT_EQ(StackHandlerConstants::kStateOffset, 1399 StackHandlerConstants::kPCOffset - kPointerSize); 1400 ASSERT_EQ(StackHandlerConstants::kFPOffset, 1401 StackHandlerConstants::kStateOffset - kPointerSize); 1402 ASSERT_EQ(StackHandlerConstants::kNextOffset, 1403 StackHandlerConstants::kFPOffset - kPointerSize); 1404 1405 if (try_location == IN_JAVASCRIPT) { 1406 if (type == TRY_CATCH_HANDLER) { 1407 push(Immediate(StackHandler::TRY_CATCH)); 1408 } else { 1409 push(Immediate(StackHandler::TRY_FINALLY)); 1410 } 1411 push(rbp); 1412 } else { 1413 ASSERT(try_location == IN_JS_ENTRY); 1414 // The frame pointer does not point to a JS frame so we save NULL 1415 // for rbp. We expect the code throwing an exception to check rbp 1416 // before dereferencing it to restore the context. 1417 push(Immediate(StackHandler::ENTRY)); 1418 push(Immediate(0)); // NULL frame pointer. 1419 } 1420 // Save the current handler. 1421 movq(kScratchRegister, ExternalReference(Top::k_handler_address)); 1422 push(Operand(kScratchRegister, 0)); 1423 // Link this handler. 1424 movq(Operand(kScratchRegister, 0), rsp); 1425} 1426 1427 1428void MacroAssembler::Ret() { 1429 ret(0); 1430} 1431 1432 1433void MacroAssembler::FCmp() { 1434 fucomip(); 1435 ffree(0); 1436 fincstp(); 1437} 1438 1439 1440void MacroAssembler::CmpObjectType(Register heap_object, 1441 InstanceType type, 1442 Register map) { 1443 movq(map, FieldOperand(heap_object, HeapObject::kMapOffset)); 1444 CmpInstanceType(map, type); 1445} 1446 1447 1448void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { 1449 cmpb(FieldOperand(map, Map::kInstanceTypeOffset), 1450 Immediate(static_cast<int8_t>(type))); 1451} 1452 1453 1454void MacroAssembler::TryGetFunctionPrototype(Register function, 1455 Register result, 1456 Label* miss) { 1457 // Check that the receiver isn't a smi. 1458 testl(function, Immediate(kSmiTagMask)); 1459 j(zero, miss); 1460 1461 // Check that the function really is a function. 1462 CmpObjectType(function, JS_FUNCTION_TYPE, result); 1463 j(not_equal, miss); 1464 1465 // Make sure that the function has an instance prototype. 1466 Label non_instance; 1467 testb(FieldOperand(result, Map::kBitFieldOffset), 1468 Immediate(1 << Map::kHasNonInstancePrototype)); 1469 j(not_zero, &non_instance); 1470 1471 // Get the prototype or initial map from the function. 1472 movq(result, 1473 FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); 1474 1475 // If the prototype or initial map is the hole, don't return it and 1476 // simply miss the cache instead. This will allow us to allocate a 1477 // prototype object on-demand in the runtime system. 1478 CompareRoot(result, Heap::kTheHoleValueRootIndex); 1479 j(equal, miss); 1480 1481 // If the function does not have an initial map, we're done. 1482 Label done; 1483 CmpObjectType(result, MAP_TYPE, kScratchRegister); 1484 j(not_equal, &done); 1485 1486 // Get the prototype from the initial map. 1487 movq(result, FieldOperand(result, Map::kPrototypeOffset)); 1488 jmp(&done); 1489 1490 // Non-instance prototype: Fetch prototype from constructor field 1491 // in initial map. 1492 bind(&non_instance); 1493 movq(result, FieldOperand(result, Map::kConstructorOffset)); 1494 1495 // All done. 1496 bind(&done); 1497} 1498 1499 1500void MacroAssembler::SetCounter(StatsCounter* counter, int value) { 1501 if (FLAG_native_code_counters && counter->Enabled()) { 1502 movq(kScratchRegister, ExternalReference(counter)); 1503 movl(Operand(kScratchRegister, 0), Immediate(value)); 1504 } 1505} 1506 1507 1508void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) { 1509 ASSERT(value > 0); 1510 if (FLAG_native_code_counters && counter->Enabled()) { 1511 movq(kScratchRegister, ExternalReference(counter)); 1512 Operand operand(kScratchRegister, 0); 1513 if (value == 1) { 1514 incl(operand); 1515 } else { 1516 addl(operand, Immediate(value)); 1517 } 1518 } 1519} 1520 1521 1522void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) { 1523 ASSERT(value > 0); 1524 if (FLAG_native_code_counters && counter->Enabled()) { 1525 movq(kScratchRegister, ExternalReference(counter)); 1526 Operand operand(kScratchRegister, 0); 1527 if (value == 1) { 1528 decl(operand); 1529 } else { 1530 subl(operand, Immediate(value)); 1531 } 1532 } 1533} 1534 1535#ifdef ENABLE_DEBUGGER_SUPPORT 1536 1537void MacroAssembler::PushRegistersFromMemory(RegList regs) { 1538 ASSERT((regs & ~kJSCallerSaved) == 0); 1539 // Push the content of the memory location to the stack. 1540 for (int i = 0; i < kNumJSCallerSaved; i++) { 1541 int r = JSCallerSavedCode(i); 1542 if ((regs & (1 << r)) != 0) { 1543 ExternalReference reg_addr = 1544 ExternalReference(Debug_Address::Register(i)); 1545 movq(kScratchRegister, reg_addr); 1546 push(Operand(kScratchRegister, 0)); 1547 } 1548 } 1549} 1550 1551 1552void MacroAssembler::SaveRegistersToMemory(RegList regs) { 1553 ASSERT((regs & ~kJSCallerSaved) == 0); 1554 // Copy the content of registers to memory location. 1555 for (int i = 0; i < kNumJSCallerSaved; i++) { 1556 int r = JSCallerSavedCode(i); 1557 if ((regs & (1 << r)) != 0) { 1558 Register reg = { r }; 1559 ExternalReference reg_addr = 1560 ExternalReference(Debug_Address::Register(i)); 1561 movq(kScratchRegister, reg_addr); 1562 movq(Operand(kScratchRegister, 0), reg); 1563 } 1564 } 1565} 1566 1567 1568void MacroAssembler::RestoreRegistersFromMemory(RegList regs) { 1569 ASSERT((regs & ~kJSCallerSaved) == 0); 1570 // Copy the content of memory location to registers. 1571 for (int i = kNumJSCallerSaved - 1; i >= 0; i--) { 1572 int r = JSCallerSavedCode(i); 1573 if ((regs & (1 << r)) != 0) { 1574 Register reg = { r }; 1575 ExternalReference reg_addr = 1576 ExternalReference(Debug_Address::Register(i)); 1577 movq(kScratchRegister, reg_addr); 1578 movq(reg, Operand(kScratchRegister, 0)); 1579 } 1580 } 1581} 1582 1583 1584void MacroAssembler::PopRegistersToMemory(RegList regs) { 1585 ASSERT((regs & ~kJSCallerSaved) == 0); 1586 // Pop the content from the stack to the memory location. 1587 for (int i = kNumJSCallerSaved - 1; i >= 0; i--) { 1588 int r = JSCallerSavedCode(i); 1589 if ((regs & (1 << r)) != 0) { 1590 ExternalReference reg_addr = 1591 ExternalReference(Debug_Address::Register(i)); 1592 movq(kScratchRegister, reg_addr); 1593 pop(Operand(kScratchRegister, 0)); 1594 } 1595 } 1596} 1597 1598 1599void MacroAssembler::CopyRegistersFromStackToMemory(Register base, 1600 Register scratch, 1601 RegList regs) { 1602 ASSERT(!scratch.is(kScratchRegister)); 1603 ASSERT(!base.is(kScratchRegister)); 1604 ASSERT(!base.is(scratch)); 1605 ASSERT((regs & ~kJSCallerSaved) == 0); 1606 // Copy the content of the stack to the memory location and adjust base. 1607 for (int i = kNumJSCallerSaved - 1; i >= 0; i--) { 1608 int r = JSCallerSavedCode(i); 1609 if ((regs & (1 << r)) != 0) { 1610 movq(scratch, Operand(base, 0)); 1611 ExternalReference reg_addr = 1612 ExternalReference(Debug_Address::Register(i)); 1613 movq(kScratchRegister, reg_addr); 1614 movq(Operand(kScratchRegister, 0), scratch); 1615 lea(base, Operand(base, kPointerSize)); 1616 } 1617 } 1618} 1619 1620#endif // ENABLE_DEBUGGER_SUPPORT 1621 1622 1623void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) { 1624 bool resolved; 1625 Handle<Code> code = ResolveBuiltin(id, &resolved); 1626 1627 // Calls are not allowed in some stubs. 1628 ASSERT(flag == JUMP_FUNCTION || allow_stub_calls()); 1629 1630 // Rely on the assertion to check that the number of provided 1631 // arguments match the expected number of arguments. Fake a 1632 // parameter count to avoid emitting code to do the check. 1633 ParameterCount expected(0); 1634 InvokeCode(Handle<Code>(code), 1635 expected, 1636 expected, 1637 RelocInfo::CODE_TARGET, 1638 flag); 1639 1640 const char* name = Builtins::GetName(id); 1641 int argc = Builtins::GetArgumentsCount(id); 1642 // The target address for the jump is stored as an immediate at offset 1643 // kInvokeCodeAddressOffset. 1644 if (!resolved) { 1645 uint32_t flags = 1646 Bootstrapper::FixupFlagsArgumentsCount::encode(argc) | 1647 Bootstrapper::FixupFlagsUseCodeObject::encode(false); 1648 Unresolved entry = 1649 { pc_offset() - kCallTargetAddressOffset, flags, name }; 1650 unresolved_.Add(entry); 1651 } 1652} 1653 1654 1655void MacroAssembler::InvokePrologue(const ParameterCount& expected, 1656 const ParameterCount& actual, 1657 Handle<Code> code_constant, 1658 Register code_register, 1659 Label* done, 1660 InvokeFlag flag) { 1661 bool definitely_matches = false; 1662 Label invoke; 1663 if (expected.is_immediate()) { 1664 ASSERT(actual.is_immediate()); 1665 if (expected.immediate() == actual.immediate()) { 1666 definitely_matches = true; 1667 } else { 1668 movq(rax, Immediate(actual.immediate())); 1669 if (expected.immediate() == 1670 SharedFunctionInfo::kDontAdaptArgumentsSentinel) { 1671 // Don't worry about adapting arguments for built-ins that 1672 // don't want that done. Skip adaption code by making it look 1673 // like we have a match between expected and actual number of 1674 // arguments. 1675 definitely_matches = true; 1676 } else { 1677 movq(rbx, Immediate(expected.immediate())); 1678 } 1679 } 1680 } else { 1681 if (actual.is_immediate()) { 1682 // Expected is in register, actual is immediate. This is the 1683 // case when we invoke function values without going through the 1684 // IC mechanism. 1685 cmpq(expected.reg(), Immediate(actual.immediate())); 1686 j(equal, &invoke); 1687 ASSERT(expected.reg().is(rbx)); 1688 movq(rax, Immediate(actual.immediate())); 1689 } else if (!expected.reg().is(actual.reg())) { 1690 // Both expected and actual are in (different) registers. This 1691 // is the case when we invoke functions using call and apply. 1692 cmpq(expected.reg(), actual.reg()); 1693 j(equal, &invoke); 1694 ASSERT(actual.reg().is(rax)); 1695 ASSERT(expected.reg().is(rbx)); 1696 } 1697 } 1698 1699 if (!definitely_matches) { 1700 Handle<Code> adaptor = 1701 Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline)); 1702 if (!code_constant.is_null()) { 1703 movq(rdx, code_constant, RelocInfo::EMBEDDED_OBJECT); 1704 addq(rdx, Immediate(Code::kHeaderSize - kHeapObjectTag)); 1705 } else if (!code_register.is(rdx)) { 1706 movq(rdx, code_register); 1707 } 1708 1709 if (flag == CALL_FUNCTION) { 1710 Call(adaptor, RelocInfo::CODE_TARGET); 1711 jmp(done); 1712 } else { 1713 Jump(adaptor, RelocInfo::CODE_TARGET); 1714 } 1715 bind(&invoke); 1716 } 1717} 1718 1719 1720void MacroAssembler::InvokeCode(Register code, 1721 const ParameterCount& expected, 1722 const ParameterCount& actual, 1723 InvokeFlag flag) { 1724 Label done; 1725 InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag); 1726 if (flag == CALL_FUNCTION) { 1727 call(code); 1728 } else { 1729 ASSERT(flag == JUMP_FUNCTION); 1730 jmp(code); 1731 } 1732 bind(&done); 1733} 1734 1735 1736void MacroAssembler::InvokeCode(Handle<Code> code, 1737 const ParameterCount& expected, 1738 const ParameterCount& actual, 1739 RelocInfo::Mode rmode, 1740 InvokeFlag flag) { 1741 Label done; 1742 Register dummy = rax; 1743 InvokePrologue(expected, actual, code, dummy, &done, flag); 1744 if (flag == CALL_FUNCTION) { 1745 Call(code, rmode); 1746 } else { 1747 ASSERT(flag == JUMP_FUNCTION); 1748 Jump(code, rmode); 1749 } 1750 bind(&done); 1751} 1752 1753 1754void MacroAssembler::InvokeFunction(Register function, 1755 const ParameterCount& actual, 1756 InvokeFlag flag) { 1757 ASSERT(function.is(rdi)); 1758 movq(rdx, FieldOperand(function, JSFunction::kSharedFunctionInfoOffset)); 1759 movq(rsi, FieldOperand(function, JSFunction::kContextOffset)); 1760 movsxlq(rbx, 1761 FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset)); 1762 movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset)); 1763 // Advances rdx to the end of the Code object header, to the start of 1764 // the executable code. 1765 lea(rdx, FieldOperand(rdx, Code::kHeaderSize)); 1766 1767 ParameterCount expected(rbx); 1768 InvokeCode(rdx, expected, actual, flag); 1769} 1770 1771 1772void MacroAssembler::EnterFrame(StackFrame::Type type) { 1773 push(rbp); 1774 movq(rbp, rsp); 1775 push(rsi); // Context. 1776 Push(Smi::FromInt(type)); 1777 movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT); 1778 push(kScratchRegister); 1779 if (FLAG_debug_code) { 1780 movq(kScratchRegister, 1781 Factory::undefined_value(), 1782 RelocInfo::EMBEDDED_OBJECT); 1783 cmpq(Operand(rsp, 0), kScratchRegister); 1784 Check(not_equal, "code object not properly patched"); 1785 } 1786} 1787 1788 1789void MacroAssembler::LeaveFrame(StackFrame::Type type) { 1790 if (FLAG_debug_code) { 1791 Move(kScratchRegister, Smi::FromInt(type)); 1792 cmpq(Operand(rbp, StandardFrameConstants::kMarkerOffset), kScratchRegister); 1793 Check(equal, "stack frame types must match"); 1794 } 1795 movq(rsp, rbp); 1796 pop(rbp); 1797} 1798 1799 1800void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) { 1801 // Setup the frame structure on the stack. 1802 // All constants are relative to the frame pointer of the exit frame. 1803 ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize); 1804 ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize); 1805 ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize); 1806 push(rbp); 1807 movq(rbp, rsp); 1808 1809 // Reserve room for entry stack pointer and push the debug marker. 1810 ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize); 1811 push(Immediate(0)); // saved entry sp, patched before call 1812 if (mode == ExitFrame::MODE_DEBUG) { 1813 push(Immediate(0)); 1814 } else { 1815 movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT); 1816 push(kScratchRegister); 1817 } 1818 1819 // Save the frame pointer and the context in top. 1820 ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); 1821 ExternalReference context_address(Top::k_context_address); 1822 movq(r14, rax); // Backup rax before we use it. 1823 1824 movq(rax, rbp); 1825 store_rax(c_entry_fp_address); 1826 movq(rax, rsi); 1827 store_rax(context_address); 1828 1829 // Setup argv in callee-saved register r15. It is reused in LeaveExitFrame, 1830 // so it must be retained across the C-call. 1831 int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; 1832 lea(r15, Operand(rbp, r14, times_pointer_size, offset)); 1833 1834#ifdef ENABLE_DEBUGGER_SUPPORT 1835 // Save the state of all registers to the stack from the memory 1836 // location. This is needed to allow nested break points. 1837 if (mode == ExitFrame::MODE_DEBUG) { 1838 // TODO(1243899): This should be symmetric to 1839 // CopyRegistersFromStackToMemory() but it isn't! esp is assumed 1840 // correct here, but computed for the other call. Very error 1841 // prone! FIX THIS. Actually there are deeper problems with 1842 // register saving than this asymmetry (see the bug report 1843 // associated with this issue). 1844 PushRegistersFromMemory(kJSCallerSaved); 1845 } 1846#endif 1847 1848#ifdef _WIN64 1849 // Reserve space on stack for result and argument structures, if necessary. 1850 int result_stack_space = (result_size < 2) ? 0 : result_size * kPointerSize; 1851 // Reserve space for the Arguments object. The Windows 64-bit ABI 1852 // requires us to pass this structure as a pointer to its location on 1853 // the stack. The structure contains 2 values. 1854 int argument_stack_space = 2 * kPointerSize; 1855 // We also need backing space for 4 parameters, even though 1856 // we only pass one or two parameter, and it is in a register. 1857 int argument_mirror_space = 4 * kPointerSize; 1858 int total_stack_space = 1859 argument_mirror_space + argument_stack_space + result_stack_space; 1860 subq(rsp, Immediate(total_stack_space)); 1861#endif 1862 1863 // Get the required frame alignment for the OS. 1864 static const int kFrameAlignment = OS::ActivationFrameAlignment(); 1865 if (kFrameAlignment > 0) { 1866 ASSERT(IsPowerOf2(kFrameAlignment)); 1867 movq(kScratchRegister, Immediate(-kFrameAlignment)); 1868 and_(rsp, kScratchRegister); 1869 } 1870 1871 // Patch the saved entry sp. 1872 movq(Operand(rbp, ExitFrameConstants::kSPOffset), rsp); 1873} 1874 1875 1876void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode, int result_size) { 1877 // Registers: 1878 // r15 : argv 1879#ifdef ENABLE_DEBUGGER_SUPPORT 1880 // Restore the memory copy of the registers by digging them out from 1881 // the stack. This is needed to allow nested break points. 1882 if (mode == ExitFrame::MODE_DEBUG) { 1883 // It's okay to clobber register rbx below because we don't need 1884 // the function pointer after this. 1885 const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize; 1886 int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize; 1887 lea(rbx, Operand(rbp, kOffset)); 1888 CopyRegistersFromStackToMemory(rbx, rcx, kJSCallerSaved); 1889 } 1890#endif 1891 1892 // Get the return address from the stack and restore the frame pointer. 1893 movq(rcx, Operand(rbp, 1 * kPointerSize)); 1894 movq(rbp, Operand(rbp, 0 * kPointerSize)); 1895 1896 // Pop everything up to and including the arguments and the receiver 1897 // from the caller stack. 1898 lea(rsp, Operand(r15, 1 * kPointerSize)); 1899 1900 // Restore current context from top and clear it in debug mode. 1901 ExternalReference context_address(Top::k_context_address); 1902 movq(kScratchRegister, context_address); 1903 movq(rsi, Operand(kScratchRegister, 0)); 1904#ifdef DEBUG 1905 movq(Operand(kScratchRegister, 0), Immediate(0)); 1906#endif 1907 1908 // Push the return address to get ready to return. 1909 push(rcx); 1910 1911 // Clear the top frame. 1912 ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); 1913 movq(kScratchRegister, c_entry_fp_address); 1914 movq(Operand(kScratchRegister, 0), Immediate(0)); 1915} 1916 1917 1918Register MacroAssembler::CheckMaps(JSObject* object, 1919 Register object_reg, 1920 JSObject* holder, 1921 Register holder_reg, 1922 Register scratch, 1923 Label* miss) { 1924 // Make sure there's no overlap between scratch and the other 1925 // registers. 1926 ASSERT(!scratch.is(object_reg) && !scratch.is(holder_reg)); 1927 1928 // Keep track of the current object in register reg. On the first 1929 // iteration, reg is an alias for object_reg, on later iterations, 1930 // it is an alias for holder_reg. 1931 Register reg = object_reg; 1932 int depth = 1; 1933 1934 // Check the maps in the prototype chain. 1935 // Traverse the prototype chain from the object and do map checks. 1936 while (object != holder) { 1937 depth++; 1938 1939 // Only global objects and objects that do not require access 1940 // checks are allowed in stubs. 1941 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 1942 1943 JSObject* prototype = JSObject::cast(object->GetPrototype()); 1944 if (Heap::InNewSpace(prototype)) { 1945 // Get the map of the current object. 1946 movq(scratch, FieldOperand(reg, HeapObject::kMapOffset)); 1947 Cmp(scratch, Handle<Map>(object->map())); 1948 // Branch on the result of the map check. 1949 j(not_equal, miss); 1950 // Check access rights to the global object. This has to happen 1951 // after the map check so that we know that the object is 1952 // actually a global object. 1953 if (object->IsJSGlobalProxy()) { 1954 CheckAccessGlobalProxy(reg, scratch, miss); 1955 1956 // Restore scratch register to be the map of the object. 1957 // We load the prototype from the map in the scratch register. 1958 movq(scratch, FieldOperand(reg, HeapObject::kMapOffset)); 1959 } 1960 // The prototype is in new space; we cannot store a reference 1961 // to it in the code. Load it from the map. 1962 reg = holder_reg; // from now the object is in holder_reg 1963 movq(reg, FieldOperand(scratch, Map::kPrototypeOffset)); 1964 1965 } else { 1966 // Check the map of the current object. 1967 Cmp(FieldOperand(reg, HeapObject::kMapOffset), 1968 Handle<Map>(object->map())); 1969 // Branch on the result of the map check. 1970 j(not_equal, miss); 1971 // Check access rights to the global object. This has to happen 1972 // after the map check so that we know that the object is 1973 // actually a global object. 1974 if (object->IsJSGlobalProxy()) { 1975 CheckAccessGlobalProxy(reg, scratch, miss); 1976 } 1977 // The prototype is in old space; load it directly. 1978 reg = holder_reg; // from now the object is in holder_reg 1979 Move(reg, Handle<JSObject>(prototype)); 1980 } 1981 1982 // Go to the next object in the prototype chain. 1983 object = prototype; 1984 } 1985 1986 // Check the holder map. 1987 Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle<Map>(holder->map())); 1988 j(not_equal, miss); 1989 1990 // Log the check depth. 1991 LOG(IntEvent("check-maps-depth", depth)); 1992 1993 // Perform security check for access to the global object and return 1994 // the holder register. 1995 ASSERT(object == holder); 1996 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 1997 if (object->IsJSGlobalProxy()) { 1998 CheckAccessGlobalProxy(reg, scratch, miss); 1999 } 2000 return reg; 2001} 2002 2003 2004void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, 2005 Register scratch, 2006 Label* miss) { 2007 Label same_contexts; 2008 2009 ASSERT(!holder_reg.is(scratch)); 2010 ASSERT(!scratch.is(kScratchRegister)); 2011 // Load current lexical context from the stack frame. 2012 movq(scratch, Operand(rbp, StandardFrameConstants::kContextOffset)); 2013 2014 // When generating debug code, make sure the lexical context is set. 2015 if (FLAG_debug_code) { 2016 cmpq(scratch, Immediate(0)); 2017 Check(not_equal, "we should not have an empty lexical context"); 2018 } 2019 // Load the global context of the current context. 2020 int offset = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; 2021 movq(scratch, FieldOperand(scratch, offset)); 2022 movq(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset)); 2023 2024 // Check the context is a global context. 2025 if (FLAG_debug_code) { 2026 Cmp(FieldOperand(scratch, HeapObject::kMapOffset), 2027 Factory::global_context_map()); 2028 Check(equal, "JSGlobalObject::global_context should be a global context."); 2029 } 2030 2031 // Check if both contexts are the same. 2032 cmpq(scratch, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset)); 2033 j(equal, &same_contexts); 2034 2035 // Compare security tokens. 2036 // Check that the security token in the calling global object is 2037 // compatible with the security token in the receiving global 2038 // object. 2039 2040 // Check the context is a global context. 2041 if (FLAG_debug_code) { 2042 // Preserve original value of holder_reg. 2043 push(holder_reg); 2044 movq(holder_reg, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset)); 2045 CompareRoot(holder_reg, Heap::kNullValueRootIndex); 2046 Check(not_equal, "JSGlobalProxy::context() should not be null."); 2047 2048 // Read the first word and compare to global_context_map(), 2049 movq(holder_reg, FieldOperand(holder_reg, HeapObject::kMapOffset)); 2050 CompareRoot(holder_reg, Heap::kGlobalContextMapRootIndex); 2051 Check(equal, "JSGlobalObject::global_context should be a global context."); 2052 pop(holder_reg); 2053 } 2054 2055 movq(kScratchRegister, 2056 FieldOperand(holder_reg, JSGlobalProxy::kContextOffset)); 2057 int token_offset = 2058 Context::kHeaderSize + Context::SECURITY_TOKEN_INDEX * kPointerSize; 2059 movq(scratch, FieldOperand(scratch, token_offset)); 2060 cmpq(scratch, FieldOperand(kScratchRegister, token_offset)); 2061 j(not_equal, miss); 2062 2063 bind(&same_contexts); 2064} 2065 2066 2067void MacroAssembler::LoadAllocationTopHelper(Register result, 2068 Register result_end, 2069 Register scratch, 2070 AllocationFlags flags) { 2071 ExternalReference new_space_allocation_top = 2072 ExternalReference::new_space_allocation_top_address(); 2073 2074 // Just return if allocation top is already known. 2075 if ((flags & RESULT_CONTAINS_TOP) != 0) { 2076 // No use of scratch if allocation top is provided. 2077 ASSERT(scratch.is(no_reg)); 2078#ifdef DEBUG 2079 // Assert that result actually contains top on entry. 2080 movq(kScratchRegister, new_space_allocation_top); 2081 cmpq(result, Operand(kScratchRegister, 0)); 2082 Check(equal, "Unexpected allocation top"); 2083#endif 2084 return; 2085 } 2086 2087 // Move address of new object to result. Use scratch register if available. 2088 if (scratch.is(no_reg)) { 2089 movq(kScratchRegister, new_space_allocation_top); 2090 movq(result, Operand(kScratchRegister, 0)); 2091 } else { 2092 ASSERT(!scratch.is(result_end)); 2093 movq(scratch, new_space_allocation_top); 2094 movq(result, Operand(scratch, 0)); 2095 } 2096} 2097 2098 2099void MacroAssembler::UpdateAllocationTopHelper(Register result_end, 2100 Register scratch) { 2101 if (FLAG_debug_code) { 2102 testq(result_end, Immediate(kObjectAlignmentMask)); 2103 Check(zero, "Unaligned allocation in new space"); 2104 } 2105 2106 ExternalReference new_space_allocation_top = 2107 ExternalReference::new_space_allocation_top_address(); 2108 2109 // Update new top. 2110 if (result_end.is(rax)) { 2111 // rax can be stored directly to a memory location. 2112 store_rax(new_space_allocation_top); 2113 } else { 2114 // Register required - use scratch provided if available. 2115 if (scratch.is(no_reg)) { 2116 movq(kScratchRegister, new_space_allocation_top); 2117 movq(Operand(kScratchRegister, 0), result_end); 2118 } else { 2119 movq(Operand(scratch, 0), result_end); 2120 } 2121 } 2122} 2123 2124 2125void MacroAssembler::AllocateInNewSpace(int object_size, 2126 Register result, 2127 Register result_end, 2128 Register scratch, 2129 Label* gc_required, 2130 AllocationFlags flags) { 2131 ASSERT(!result.is(result_end)); 2132 2133 // Load address of new object into result. 2134 LoadAllocationTopHelper(result, result_end, scratch, flags); 2135 2136 // Calculate new top and bail out if new space is exhausted. 2137 ExternalReference new_space_allocation_limit = 2138 ExternalReference::new_space_allocation_limit_address(); 2139 lea(result_end, Operand(result, object_size)); 2140 movq(kScratchRegister, new_space_allocation_limit); 2141 cmpq(result_end, Operand(kScratchRegister, 0)); 2142 j(above, gc_required); 2143 2144 // Update allocation top. 2145 UpdateAllocationTopHelper(result_end, scratch); 2146 2147 // Tag the result if requested. 2148 if ((flags & TAG_OBJECT) != 0) { 2149 addq(result, Immediate(kHeapObjectTag)); 2150 } 2151} 2152 2153 2154void MacroAssembler::AllocateInNewSpace(int header_size, 2155 ScaleFactor element_size, 2156 Register element_count, 2157 Register result, 2158 Register result_end, 2159 Register scratch, 2160 Label* gc_required, 2161 AllocationFlags flags) { 2162 ASSERT(!result.is(result_end)); 2163 2164 // Load address of new object into result. 2165 LoadAllocationTopHelper(result, result_end, scratch, flags); 2166 2167 // Calculate new top and bail out if new space is exhausted. 2168 ExternalReference new_space_allocation_limit = 2169 ExternalReference::new_space_allocation_limit_address(); 2170 lea(result_end, Operand(result, element_count, element_size, header_size)); 2171 movq(kScratchRegister, new_space_allocation_limit); 2172 cmpq(result_end, Operand(kScratchRegister, 0)); 2173 j(above, gc_required); 2174 2175 // Update allocation top. 2176 UpdateAllocationTopHelper(result_end, scratch); 2177 2178 // Tag the result if requested. 2179 if ((flags & TAG_OBJECT) != 0) { 2180 addq(result, Immediate(kHeapObjectTag)); 2181 } 2182} 2183 2184 2185void MacroAssembler::AllocateInNewSpace(Register object_size, 2186 Register result, 2187 Register result_end, 2188 Register scratch, 2189 Label* gc_required, 2190 AllocationFlags flags) { 2191 // Load address of new object into result. 2192 LoadAllocationTopHelper(result, result_end, scratch, flags); 2193 2194 // Calculate new top and bail out if new space is exhausted. 2195 ExternalReference new_space_allocation_limit = 2196 ExternalReference::new_space_allocation_limit_address(); 2197 if (!object_size.is(result_end)) { 2198 movq(result_end, object_size); 2199 } 2200 addq(result_end, result); 2201 movq(kScratchRegister, new_space_allocation_limit); 2202 cmpq(result_end, Operand(kScratchRegister, 0)); 2203 j(above, gc_required); 2204 2205 // Update allocation top. 2206 UpdateAllocationTopHelper(result_end, scratch); 2207 2208 // Tag the result if requested. 2209 if ((flags & TAG_OBJECT) != 0) { 2210 addq(result, Immediate(kHeapObjectTag)); 2211 } 2212} 2213 2214 2215void MacroAssembler::UndoAllocationInNewSpace(Register object) { 2216 ExternalReference new_space_allocation_top = 2217 ExternalReference::new_space_allocation_top_address(); 2218 2219 // Make sure the object has no tag before resetting top. 2220 and_(object, Immediate(~kHeapObjectTagMask)); 2221 movq(kScratchRegister, new_space_allocation_top); 2222#ifdef DEBUG 2223 cmpq(object, Operand(kScratchRegister, 0)); 2224 Check(below, "Undo allocation of non allocated memory"); 2225#endif 2226 movq(Operand(kScratchRegister, 0), object); 2227} 2228 2229 2230void MacroAssembler::AllocateHeapNumber(Register result, 2231 Register scratch, 2232 Label* gc_required) { 2233 // Allocate heap number in new space. 2234 AllocateInNewSpace(HeapNumber::kSize, 2235 result, 2236 scratch, 2237 no_reg, 2238 gc_required, 2239 TAG_OBJECT); 2240 2241 // Set the map. 2242 LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex); 2243 movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister); 2244} 2245 2246 2247void MacroAssembler::LoadContext(Register dst, int context_chain_length) { 2248 if (context_chain_length > 0) { 2249 // Move up the chain of contexts to the context containing the slot. 2250 movq(dst, Operand(rsi, Context::SlotOffset(Context::CLOSURE_INDEX))); 2251 // Load the function context (which is the incoming, outer context). 2252 movq(rax, FieldOperand(rax, JSFunction::kContextOffset)); 2253 for (int i = 1; i < context_chain_length; i++) { 2254 movq(dst, Operand(dst, Context::SlotOffset(Context::CLOSURE_INDEX))); 2255 movq(dst, FieldOperand(dst, JSFunction::kContextOffset)); 2256 } 2257 // The context may be an intermediate context, not a function context. 2258 movq(dst, Operand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX))); 2259 } else { // context is the current function context. 2260 // The context may be an intermediate context, not a function context. 2261 movq(dst, Operand(rsi, Context::SlotOffset(Context::FCONTEXT_INDEX))); 2262 } 2263} 2264 2265 2266CodePatcher::CodePatcher(byte* address, int size) 2267 : address_(address), size_(size), masm_(address, size + Assembler::kGap) { 2268 // Create a new macro assembler pointing to the address of the code to patch. 2269 // The size is adjusted with kGap on order for the assembler to generate size 2270 // bytes of instructions without failing with buffer size constraints. 2271 ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap); 2272} 2273 2274 2275CodePatcher::~CodePatcher() { 2276 // Indicate that code has changed. 2277 CPU::FlushICache(address_, size_); 2278 2279 // Check that the code was patched as expected. 2280 ASSERT(masm_.pc_ == address_ + size_); 2281 ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap); 2282} 2283 2284} } // namespace v8::internal 2285