Home | History | Annotate | Download | only in x64
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef V8_X64_ASSEMBLER_X64_INL_H_
      6 #define V8_X64_ASSEMBLER_X64_INL_H_
      7 
      8 #include "src/x64/assembler-x64.h"
      9 
     10 #include "src/base/cpu.h"
     11 #include "src/debug.h"
     12 #include "src/v8memory.h"
     13 
     14 namespace v8 {
     15 namespace internal {
     16 
     17 bool CpuFeatures::SupportsCrankshaft() { return true; }
     18 
     19 
     20 // -----------------------------------------------------------------------------
     21 // Implementation of Assembler
     22 
     23 
     24 static const byte kCallOpcode = 0xE8;
     25 // The length of pushq(rbp), movp(rbp, rsp), Push(rsi) and Push(rdi).
     26 static const int kNoCodeAgeSequenceLength = kPointerSize == kInt64Size ? 6 : 17;
     27 
     28 
     29 void Assembler::emitl(uint32_t x) {
     30   Memory::uint32_at(pc_) = x;
     31   pc_ += sizeof(uint32_t);
     32 }
     33 
     34 
     35 void Assembler::emitp(void* x, RelocInfo::Mode rmode) {
     36   uintptr_t value = reinterpret_cast<uintptr_t>(x);
     37   Memory::uintptr_at(pc_) = value;
     38   if (!RelocInfo::IsNone(rmode)) {
     39     RecordRelocInfo(rmode, value);
     40   }
     41   pc_ += sizeof(uintptr_t);
     42 }
     43 
     44 
     45 void Assembler::emitq(uint64_t x) {
     46   Memory::uint64_at(pc_) = x;
     47   pc_ += sizeof(uint64_t);
     48 }
     49 
     50 
     51 void Assembler::emitw(uint16_t x) {
     52   Memory::uint16_at(pc_) = x;
     53   pc_ += sizeof(uint16_t);
     54 }
     55 
     56 
     57 void Assembler::emit_code_target(Handle<Code> target,
     58                                  RelocInfo::Mode rmode,
     59                                  TypeFeedbackId ast_id) {
     60   DCHECK(RelocInfo::IsCodeTarget(rmode) ||
     61       rmode == RelocInfo::CODE_AGE_SEQUENCE);
     62   if (rmode == RelocInfo::CODE_TARGET && !ast_id.IsNone()) {
     63     RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, ast_id.ToInt());
     64   } else {
     65     RecordRelocInfo(rmode);
     66   }
     67   int current = code_targets_.length();
     68   if (current > 0 && code_targets_.last().is_identical_to(target)) {
     69     // Optimization if we keep jumping to the same code target.
     70     emitl(current - 1);
     71   } else {
     72     code_targets_.Add(target);
     73     emitl(current);
     74   }
     75 }
     76 
     77 
     78 void Assembler::emit_runtime_entry(Address entry, RelocInfo::Mode rmode) {
     79   DCHECK(RelocInfo::IsRuntimeEntry(rmode));
     80   RecordRelocInfo(rmode);
     81   emitl(static_cast<uint32_t>(entry - isolate()->code_range()->start()));
     82 }
     83 
     84 
     85 void Assembler::emit_rex_64(Register reg, Register rm_reg) {
     86   emit(0x48 | reg.high_bit() << 2 | rm_reg.high_bit());
     87 }
     88 
     89 
     90 void Assembler::emit_rex_64(XMMRegister reg, Register rm_reg) {
     91   emit(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3);
     92 }
     93 
     94 
     95 void Assembler::emit_rex_64(Register reg, XMMRegister rm_reg) {
     96   emit(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3);
     97 }
     98 
     99 
    100 void Assembler::emit_rex_64(Register reg, const Operand& op) {
    101   emit(0x48 | reg.high_bit() << 2 | op.rex_);
    102 }
    103 
    104 
    105 void Assembler::emit_rex_64(XMMRegister reg, const Operand& op) {
    106   emit(0x48 | (reg.code() & 0x8) >> 1 | op.rex_);
    107 }
    108 
    109 
    110 void Assembler::emit_rex_64(Register rm_reg) {
    111   DCHECK_EQ(rm_reg.code() & 0xf, rm_reg.code());
    112   emit(0x48 | rm_reg.high_bit());
    113 }
    114 
    115 
    116 void Assembler::emit_rex_64(const Operand& op) {
    117   emit(0x48 | op.rex_);
    118 }
    119 
    120 
    121 void Assembler::emit_rex_32(Register reg, Register rm_reg) {
    122   emit(0x40 | reg.high_bit() << 2 | rm_reg.high_bit());
    123 }
    124 
    125 
    126 void Assembler::emit_rex_32(Register reg, const Operand& op) {
    127   emit(0x40 | reg.high_bit() << 2  | op.rex_);
    128 }
    129 
    130 
    131 void Assembler::emit_rex_32(Register rm_reg) {
    132   emit(0x40 | rm_reg.high_bit());
    133 }
    134 
    135 
    136 void Assembler::emit_rex_32(const Operand& op) {
    137   emit(0x40 | op.rex_);
    138 }
    139 
    140 
    141 void Assembler::emit_optional_rex_32(Register reg, Register rm_reg) {
    142   byte rex_bits = reg.high_bit() << 2 | rm_reg.high_bit();
    143   if (rex_bits != 0) emit(0x40 | rex_bits);
    144 }
    145 
    146 
    147 void Assembler::emit_optional_rex_32(Register reg, const Operand& op) {
    148   byte rex_bits =  reg.high_bit() << 2 | op.rex_;
    149   if (rex_bits != 0) emit(0x40 | rex_bits);
    150 }
    151 
    152 
    153 void Assembler::emit_optional_rex_32(XMMRegister reg, const Operand& op) {
    154   byte rex_bits =  (reg.code() & 0x8) >> 1 | op.rex_;
    155   if (rex_bits != 0) emit(0x40 | rex_bits);
    156 }
    157 
    158 
    159 void Assembler::emit_optional_rex_32(XMMRegister reg, XMMRegister base) {
    160   byte rex_bits =  (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3;
    161   if (rex_bits != 0) emit(0x40 | rex_bits);
    162 }
    163 
    164 
    165 void Assembler::emit_optional_rex_32(XMMRegister reg, Register base) {
    166   byte rex_bits =  (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3;
    167   if (rex_bits != 0) emit(0x40 | rex_bits);
    168 }
    169 
    170 
    171 void Assembler::emit_optional_rex_32(Register reg, XMMRegister base) {
    172   byte rex_bits =  (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3;
    173   if (rex_bits != 0) emit(0x40 | rex_bits);
    174 }
    175 
    176 
    177 void Assembler::emit_optional_rex_32(Register rm_reg) {
    178   if (rm_reg.high_bit()) emit(0x41);
    179 }
    180 
    181 
    182 void Assembler::emit_optional_rex_32(const Operand& op) {
    183   if (op.rex_ != 0) emit(0x40 | op.rex_);
    184 }
    185 
    186 
    187 Address Assembler::target_address_at(Address pc,
    188                                      ConstantPoolArray* constant_pool) {
    189   return Memory::int32_at(pc) + pc + 4;
    190 }
    191 
    192 
    193 void Assembler::set_target_address_at(Address pc,
    194                                       ConstantPoolArray* constant_pool,
    195                                       Address target,
    196                                       ICacheFlushMode icache_flush_mode) {
    197   Memory::int32_at(pc) = static_cast<int32_t>(target - pc - 4);
    198   if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
    199     CpuFeatures::FlushICache(pc, sizeof(int32_t));
    200   }
    201 }
    202 
    203 
    204 Address Assembler::target_address_from_return_address(Address pc) {
    205   return pc - kCallTargetAddressOffset;
    206 }
    207 
    208 
    209 Address Assembler::break_address_from_return_address(Address pc) {
    210   return pc - Assembler::kPatchDebugBreakSlotReturnOffset;
    211 }
    212 
    213 
    214 Handle<Object> Assembler::code_target_object_handle_at(Address pc) {
    215   return code_targets_[Memory::int32_at(pc)];
    216 }
    217 
    218 
    219 Address Assembler::runtime_entry_at(Address pc) {
    220   return Memory::int32_at(pc) + isolate()->code_range()->start();
    221 }
    222 
    223 // -----------------------------------------------------------------------------
    224 // Implementation of RelocInfo
    225 
    226 // The modes possibly affected by apply must be in kApplyMask.
    227 void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) {
    228   bool flush_icache = icache_flush_mode != SKIP_ICACHE_FLUSH;
    229   if (IsInternalReference(rmode_)) {
    230     // absolute code pointer inside code object moves with the code object.
    231     Memory::Address_at(pc_) += static_cast<int32_t>(delta);
    232     if (flush_icache) CpuFeatures::FlushICache(pc_, sizeof(Address));
    233   } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) {
    234     Memory::int32_at(pc_) -= static_cast<int32_t>(delta);
    235     if (flush_icache) CpuFeatures::FlushICache(pc_, sizeof(int32_t));
    236   } else if (rmode_ == CODE_AGE_SEQUENCE) {
    237     if (*pc_ == kCallOpcode) {
    238       int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
    239       *p -= static_cast<int32_t>(delta);  // Relocate entry.
    240       if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t));
    241     }
    242   }
    243 }
    244 
    245 
    246 Address RelocInfo::target_address() {
    247   DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
    248   return Assembler::target_address_at(pc_, host_);
    249 }
    250 
    251 
    252 Address RelocInfo::target_address_address() {
    253   DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
    254                               || rmode_ == EMBEDDED_OBJECT
    255                               || rmode_ == EXTERNAL_REFERENCE);
    256   return reinterpret_cast<Address>(pc_);
    257 }
    258 
    259 
    260 Address RelocInfo::constant_pool_entry_address() {
    261   UNREACHABLE();
    262   return NULL;
    263 }
    264 
    265 
    266 int RelocInfo::target_address_size() {
    267   if (IsCodedSpecially()) {
    268     return Assembler::kSpecialTargetSize;
    269   } else {
    270     return kPointerSize;
    271   }
    272 }
    273 
    274 
    275 void RelocInfo::set_target_address(Address target,
    276                                    WriteBarrierMode write_barrier_mode,
    277                                    ICacheFlushMode icache_flush_mode) {
    278   DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
    279   Assembler::set_target_address_at(pc_, host_, target, icache_flush_mode);
    280   if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != NULL &&
    281       IsCodeTarget(rmode_)) {
    282     Object* target_code = Code::GetCodeFromTargetAddress(target);
    283     host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
    284         host(), this, HeapObject::cast(target_code));
    285   }
    286 }
    287 
    288 
    289 Object* RelocInfo::target_object() {
    290   DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
    291   return Memory::Object_at(pc_);
    292 }
    293 
    294 
    295 Handle<Object> RelocInfo::target_object_handle(Assembler* origin) {
    296   DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
    297   if (rmode_ == EMBEDDED_OBJECT) {
    298     return Memory::Object_Handle_at(pc_);
    299   } else {
    300     return origin->code_target_object_handle_at(pc_);
    301   }
    302 }
    303 
    304 
    305 Address RelocInfo::target_reference() {
    306   DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE);
    307   return Memory::Address_at(pc_);
    308 }
    309 
    310 
    311 void RelocInfo::set_target_object(Object* target,
    312                                   WriteBarrierMode write_barrier_mode,
    313                                   ICacheFlushMode icache_flush_mode) {
    314   DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
    315   Memory::Object_at(pc_) = target;
    316   if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
    317     CpuFeatures::FlushICache(pc_, sizeof(Address));
    318   }
    319   if (write_barrier_mode == UPDATE_WRITE_BARRIER &&
    320       host() != NULL &&
    321       target->IsHeapObject()) {
    322     host()->GetHeap()->incremental_marking()->RecordWrite(
    323         host(), &Memory::Object_at(pc_), HeapObject::cast(target));
    324   }
    325 }
    326 
    327 
    328 Address RelocInfo::target_runtime_entry(Assembler* origin) {
    329   DCHECK(IsRuntimeEntry(rmode_));
    330   return origin->runtime_entry_at(pc_);
    331 }
    332 
    333 
    334 void RelocInfo::set_target_runtime_entry(Address target,
    335                                          WriteBarrierMode write_barrier_mode,
    336                                          ICacheFlushMode icache_flush_mode) {
    337   DCHECK(IsRuntimeEntry(rmode_));
    338   if (target_address() != target) {
    339     set_target_address(target, write_barrier_mode, icache_flush_mode);
    340   }
    341 }
    342 
    343 
    344 Handle<Cell> RelocInfo::target_cell_handle() {
    345   DCHECK(rmode_ == RelocInfo::CELL);
    346   Address address = Memory::Address_at(pc_);
    347   return Handle<Cell>(reinterpret_cast<Cell**>(address));
    348 }
    349 
    350 
    351 Cell* RelocInfo::target_cell() {
    352   DCHECK(rmode_ == RelocInfo::CELL);
    353   return Cell::FromValueAddress(Memory::Address_at(pc_));
    354 }
    355 
    356 
    357 void RelocInfo::set_target_cell(Cell* cell,
    358                                 WriteBarrierMode write_barrier_mode,
    359                                 ICacheFlushMode icache_flush_mode) {
    360   DCHECK(rmode_ == RelocInfo::CELL);
    361   Address address = cell->address() + Cell::kValueOffset;
    362   Memory::Address_at(pc_) = address;
    363   if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
    364     CpuFeatures::FlushICache(pc_, sizeof(Address));
    365   }
    366   if (write_barrier_mode == UPDATE_WRITE_BARRIER &&
    367       host() != NULL) {
    368     // TODO(1550) We are passing NULL as a slot because cell can never be on
    369     // evacuation candidate.
    370     host()->GetHeap()->incremental_marking()->RecordWrite(
    371         host(), NULL, cell);
    372   }
    373 }
    374 
    375 
    376 void RelocInfo::WipeOut() {
    377   if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_)) {
    378     Memory::Address_at(pc_) = NULL;
    379   } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) {
    380     // Effectively write zero into the relocation.
    381     Assembler::set_target_address_at(pc_, host_, pc_ + sizeof(int32_t));
    382   } else {
    383     UNREACHABLE();
    384   }
    385 }
    386 
    387 
    388 bool RelocInfo::IsPatchedReturnSequence() {
    389   // The recognized call sequence is:
    390   //  movq(kScratchRegister, address); call(kScratchRegister);
    391   // It only needs to be distinguished from a return sequence
    392   //  movq(rsp, rbp); pop(rbp); ret(n); int3 *6
    393   // The 11th byte is int3 (0xCC) in the return sequence and
    394   // REX.WB (0x48+register bit) for the call sequence.
    395   return pc_[Assembler::kMoveAddressIntoScratchRegisterInstructionLength] !=
    396          0xCC;
    397 }
    398 
    399 
    400 bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
    401   return !Assembler::IsNop(pc());
    402 }
    403 
    404 
    405 Handle<Object> RelocInfo::code_age_stub_handle(Assembler* origin) {
    406   DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
    407   DCHECK(*pc_ == kCallOpcode);
    408   return origin->code_target_object_handle_at(pc_ + 1);
    409 }
    410 
    411 
    412 Code* RelocInfo::code_age_stub() {
    413   DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
    414   DCHECK(*pc_ == kCallOpcode);
    415   return Code::GetCodeFromTargetAddress(
    416       Assembler::target_address_at(pc_ + 1, host_));
    417 }
    418 
    419 
    420 void RelocInfo::set_code_age_stub(Code* stub,
    421                                   ICacheFlushMode icache_flush_mode) {
    422   DCHECK(*pc_ == kCallOpcode);
    423   DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
    424   Assembler::set_target_address_at(pc_ + 1, host_, stub->instruction_start(),
    425                                    icache_flush_mode);
    426 }
    427 
    428 
    429 Address RelocInfo::call_address() {
    430   DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
    431          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
    432   return Memory::Address_at(
    433       pc_ + Assembler::kRealPatchReturnSequenceAddressOffset);
    434 }
    435 
    436 
    437 void RelocInfo::set_call_address(Address target) {
    438   DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
    439          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
    440   Memory::Address_at(pc_ + Assembler::kRealPatchReturnSequenceAddressOffset) =
    441       target;
    442   CpuFeatures::FlushICache(
    443       pc_ + Assembler::kRealPatchReturnSequenceAddressOffset, sizeof(Address));
    444   if (host() != NULL) {
    445     Object* target_code = Code::GetCodeFromTargetAddress(target);
    446     host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
    447         host(), this, HeapObject::cast(target_code));
    448   }
    449 }
    450 
    451 
    452 Object* RelocInfo::call_object() {
    453   return *call_object_address();
    454 }
    455 
    456 
    457 void RelocInfo::set_call_object(Object* target) {
    458   *call_object_address() = target;
    459 }
    460 
    461 
    462 Object** RelocInfo::call_object_address() {
    463   DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
    464          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
    465   return reinterpret_cast<Object**>(
    466       pc_ + Assembler::kPatchReturnSequenceAddressOffset);
    467 }
    468 
    469 
    470 void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
    471   RelocInfo::Mode mode = rmode();
    472   if (mode == RelocInfo::EMBEDDED_OBJECT) {
    473     visitor->VisitEmbeddedPointer(this);
    474     CpuFeatures::FlushICache(pc_, sizeof(Address));
    475   } else if (RelocInfo::IsCodeTarget(mode)) {
    476     visitor->VisitCodeTarget(this);
    477   } else if (mode == RelocInfo::CELL) {
    478     visitor->VisitCell(this);
    479   } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
    480     visitor->VisitExternalReference(this);
    481     CpuFeatures::FlushICache(pc_, sizeof(Address));
    482   } else if (RelocInfo::IsCodeAgeSequence(mode)) {
    483     visitor->VisitCodeAgeSequence(this);
    484   } else if (((RelocInfo::IsJSReturn(mode) &&
    485               IsPatchedReturnSequence()) ||
    486              (RelocInfo::IsDebugBreakSlot(mode) &&
    487               IsPatchedDebugBreakSlotSequence())) &&
    488              isolate->debug()->has_break_points()) {
    489     visitor->VisitDebugTarget(this);
    490   } else if (RelocInfo::IsRuntimeEntry(mode)) {
    491     visitor->VisitRuntimeEntry(this);
    492   }
    493 }
    494 
    495 
    496 template<typename StaticVisitor>
    497 void RelocInfo::Visit(Heap* heap) {
    498   RelocInfo::Mode mode = rmode();
    499   if (mode == RelocInfo::EMBEDDED_OBJECT) {
    500     StaticVisitor::VisitEmbeddedPointer(heap, this);
    501     CpuFeatures::FlushICache(pc_, sizeof(Address));
    502   } else if (RelocInfo::IsCodeTarget(mode)) {
    503     StaticVisitor::VisitCodeTarget(heap, this);
    504   } else if (mode == RelocInfo::CELL) {
    505     StaticVisitor::VisitCell(heap, this);
    506   } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
    507     StaticVisitor::VisitExternalReference(this);
    508     CpuFeatures::FlushICache(pc_, sizeof(Address));
    509   } else if (RelocInfo::IsCodeAgeSequence(mode)) {
    510     StaticVisitor::VisitCodeAgeSequence(heap, this);
    511   } else if (heap->isolate()->debug()->has_break_points() &&
    512              ((RelocInfo::IsJSReturn(mode) &&
    513               IsPatchedReturnSequence()) ||
    514              (RelocInfo::IsDebugBreakSlot(mode) &&
    515               IsPatchedDebugBreakSlotSequence()))) {
    516     StaticVisitor::VisitDebugTarget(heap, this);
    517   } else if (RelocInfo::IsRuntimeEntry(mode)) {
    518     StaticVisitor::VisitRuntimeEntry(this);
    519   }
    520 }
    521 
    522 
    523 // -----------------------------------------------------------------------------
    524 // Implementation of Operand
    525 
    526 void Operand::set_modrm(int mod, Register rm_reg) {
    527   DCHECK(is_uint2(mod));
    528   buf_[0] = mod << 6 | rm_reg.low_bits();
    529   // Set REX.B to the high bit of rm.code().
    530   rex_ |= rm_reg.high_bit();
    531 }
    532 
    533 
    534 void Operand::set_sib(ScaleFactor scale, Register index, Register base) {
    535   DCHECK(len_ == 1);
    536   DCHECK(is_uint2(scale));
    537   // Use SIB with no index register only for base rsp or r12. Otherwise we
    538   // would skip the SIB byte entirely.
    539   DCHECK(!index.is(rsp) || base.is(rsp) || base.is(r12));
    540   buf_[1] = (scale << 6) | (index.low_bits() << 3) | base.low_bits();
    541   rex_ |= index.high_bit() << 1 | base.high_bit();
    542   len_ = 2;
    543 }
    544 
    545 void Operand::set_disp8(int disp) {
    546   DCHECK(is_int8(disp));
    547   DCHECK(len_ == 1 || len_ == 2);
    548   int8_t* p = reinterpret_cast<int8_t*>(&buf_[len_]);
    549   *p = disp;
    550   len_ += sizeof(int8_t);
    551 }
    552 
    553 void Operand::set_disp32(int disp) {
    554   DCHECK(len_ == 1 || len_ == 2);
    555   int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]);
    556   *p = disp;
    557   len_ += sizeof(int32_t);
    558 }
    559 
    560 
    561 } }  // namespace v8::internal
    562 
    563 #endif  // V8_X64_ASSEMBLER_X64_INL_H_
    564