Home | History | Annotate | Download | only in arm
      1 // Copyright (c) 1994-2006 Sun Microsystems Inc.
      2 // All Rights Reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions
      6 // are met:
      7 //
      8 // - Redistributions of source code must retain the above copyright notice,
      9 // this list of conditions and the following disclaimer.
     10 //
     11 // - Redistribution in binary form must reproduce the above copyright
     12 // notice, this list of conditions and the following disclaimer in the
     13 // documentation and/or other materials provided with the
     14 // distribution.
     15 //
     16 // - Neither the name of Sun Microsystems or the names of contributors may
     17 // be used to endorse or promote products derived from this software without
     18 // specific prior written permission.
     19 //
     20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     23 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     24 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     25 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     26 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     27 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     29 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
     31 // OF THE POSSIBILITY OF SUCH DAMAGE.
     32 
     33 // The original source code covered by the above license above has been modified
     34 // significantly by Google Inc.
     35 // Copyright 2012 the V8 project authors. All rights reserved.
     36 
     37 #ifndef V8_ARM_ASSEMBLER_ARM_INL_H_
     38 #define V8_ARM_ASSEMBLER_ARM_INL_H_
     39 
     40 #include "arm/assembler-arm.h"
     41 
     42 #include "cpu.h"
     43 #include "debug.h"
     44 
     45 
     46 namespace v8 {
     47 namespace internal {
     48 
     49 
     50 int Register::NumAllocatableRegisters() {
     51   return kMaxNumAllocatableRegisters;
     52 }
     53 
     54 
     55 int DwVfpRegister::NumRegisters() {
     56   return CpuFeatures::IsSupported(VFP32DREGS) ? 32 : 16;
     57 }
     58 
     59 
     60 int DwVfpRegister::NumAllocatableRegisters() {
     61   return NumRegisters() - kNumReservedRegisters;
     62 }
     63 
     64 
     65 int DwVfpRegister::ToAllocationIndex(DwVfpRegister reg) {
     66   ASSERT(!reg.is(kDoubleRegZero));
     67   ASSERT(!reg.is(kScratchDoubleReg));
     68   if (reg.code() > kDoubleRegZero.code()) {
     69     return reg.code() - kNumReservedRegisters;
     70   }
     71   return reg.code();
     72 }
     73 
     74 
     75 DwVfpRegister DwVfpRegister::FromAllocationIndex(int index) {
     76   ASSERT(index >= 0 && index < NumAllocatableRegisters());
     77   ASSERT(kScratchDoubleReg.code() - kDoubleRegZero.code() ==
     78          kNumReservedRegisters - 1);
     79   if (index >= kDoubleRegZero.code()) {
     80     return from_code(index + kNumReservedRegisters);
     81   }
     82   return from_code(index);
     83 }
     84 
     85 
     86 void RelocInfo::apply(intptr_t delta) {
     87   if (RelocInfo::IsInternalReference(rmode_)) {
     88     // absolute code pointer inside code object moves with the code object.
     89     int32_t* p = reinterpret_cast<int32_t*>(pc_);
     90     *p += delta;  // relocate entry
     91   }
     92   // We do not use pc relative addressing on ARM, so there is
     93   // nothing else to do.
     94 }
     95 
     96 
     97 Address RelocInfo::target_address() {
     98   ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
     99   return Assembler::target_address_at(pc_);
    100 }
    101 
    102 
    103 Address RelocInfo::target_address_address() {
    104   ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
    105                               || rmode_ == EMBEDDED_OBJECT
    106                               || rmode_ == EXTERNAL_REFERENCE);
    107   return reinterpret_cast<Address>(Assembler::target_pointer_address_at(pc_));
    108 }
    109 
    110 
    111 int RelocInfo::target_address_size() {
    112   return kPointerSize;
    113 }
    114 
    115 
    116 void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) {
    117   ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
    118   Assembler::set_target_address_at(pc_, target);
    119   if (mode == UPDATE_WRITE_BARRIER && host() != NULL && IsCodeTarget(rmode_)) {
    120     Object* target_code = Code::GetCodeFromTargetAddress(target);
    121     host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
    122         host(), this, HeapObject::cast(target_code));
    123   }
    124 }
    125 
    126 
    127 Object* RelocInfo::target_object() {
    128   ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
    129   return reinterpret_cast<Object*>(Assembler::target_pointer_at(pc_));
    130 }
    131 
    132 
    133 Handle<Object> RelocInfo::target_object_handle(Assembler* origin) {
    134   ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
    135   return Handle<Object>(reinterpret_cast<Object**>(
    136       Assembler::target_pointer_at(pc_)));
    137 }
    138 
    139 
    140 Object** RelocInfo::target_object_address() {
    141   // Provide a "natural pointer" to the embedded object,
    142   // which can be de-referenced during heap iteration.
    143   ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
    144   reconstructed_obj_ptr_ =
    145       reinterpret_cast<Object*>(Assembler::target_pointer_at(pc_));
    146   return &reconstructed_obj_ptr_;
    147 }
    148 
    149 
    150 void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) {
    151   ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
    152   ASSERT(!target->IsConsString());
    153   Assembler::set_target_pointer_at(pc_, reinterpret_cast<Address>(target));
    154   if (mode == UPDATE_WRITE_BARRIER &&
    155       host() != NULL &&
    156       target->IsHeapObject()) {
    157     host()->GetHeap()->incremental_marking()->RecordWrite(
    158         host(), &Memory::Object_at(pc_), HeapObject::cast(target));
    159   }
    160 }
    161 
    162 
    163 Address* RelocInfo::target_reference_address() {
    164   ASSERT(rmode_ == EXTERNAL_REFERENCE);
    165   reconstructed_adr_ptr_ = Assembler::target_address_at(pc_);
    166   return &reconstructed_adr_ptr_;
    167 }
    168 
    169 
    170 Address RelocInfo::target_runtime_entry(Assembler* origin) {
    171   ASSERT(IsRuntimeEntry(rmode_));
    172   return target_address();
    173 }
    174 
    175 
    176 void RelocInfo::set_target_runtime_entry(Address target,
    177                                          WriteBarrierMode mode) {
    178   ASSERT(IsRuntimeEntry(rmode_));
    179   if (target_address() != target) set_target_address(target, mode);
    180 }
    181 
    182 
    183 Handle<Cell> RelocInfo::target_cell_handle() {
    184   ASSERT(rmode_ == RelocInfo::CELL);
    185   Address address = Memory::Address_at(pc_);
    186   return Handle<Cell>(reinterpret_cast<Cell**>(address));
    187 }
    188 
    189 
    190 Cell* RelocInfo::target_cell() {
    191   ASSERT(rmode_ == RelocInfo::CELL);
    192   return Cell::FromValueAddress(Memory::Address_at(pc_));
    193 }
    194 
    195 
    196 void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) {
    197   ASSERT(rmode_ == RelocInfo::CELL);
    198   Address address = cell->address() + Cell::kValueOffset;
    199   Memory::Address_at(pc_) = address;
    200   if (mode == UPDATE_WRITE_BARRIER && host() != NULL) {
    201     // TODO(1550) We are passing NULL as a slot because cell can never be on
    202     // evacuation candidate.
    203     host()->GetHeap()->incremental_marking()->RecordWrite(
    204         host(), NULL, cell);
    205   }
    206 }
    207 
    208 
    209 static const int kNoCodeAgeSequenceLength = 3;
    210 
    211 Code* RelocInfo::code_age_stub() {
    212   ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
    213   return Code::GetCodeFromTargetAddress(
    214       Memory::Address_at(pc_ + Assembler::kInstrSize *
    215                          (kNoCodeAgeSequenceLength - 1)));
    216 }
    217 
    218 
    219 void RelocInfo::set_code_age_stub(Code* stub) {
    220   ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
    221   Memory::Address_at(pc_ + Assembler::kInstrSize *
    222                      (kNoCodeAgeSequenceLength - 1)) =
    223       stub->instruction_start();
    224 }
    225 
    226 
    227 Address RelocInfo::call_address() {
    228   // The 2 instructions offset assumes patched debug break slot or return
    229   // sequence.
    230   ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
    231          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
    232   return Memory::Address_at(pc_ + 2 * Assembler::kInstrSize);
    233 }
    234 
    235 
    236 void RelocInfo::set_call_address(Address target) {
    237   ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
    238          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
    239   Memory::Address_at(pc_ + 2 * Assembler::kInstrSize) = target;
    240   if (host() != NULL) {
    241     Object* target_code = Code::GetCodeFromTargetAddress(target);
    242     host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
    243         host(), this, HeapObject::cast(target_code));
    244   }
    245 }
    246 
    247 
    248 Object* RelocInfo::call_object() {
    249   return *call_object_address();
    250 }
    251 
    252 
    253 void RelocInfo::set_call_object(Object* target) {
    254   *call_object_address() = target;
    255 }
    256 
    257 
    258 Object** RelocInfo::call_object_address() {
    259   ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
    260          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
    261   return reinterpret_cast<Object**>(pc_ + 2 * Assembler::kInstrSize);
    262 }
    263 
    264 
    265 bool RelocInfo::IsPatchedReturnSequence() {
    266   Instr current_instr = Assembler::instr_at(pc_);
    267   Instr next_instr = Assembler::instr_at(pc_ + Assembler::kInstrSize);
    268   // A patched return sequence is:
    269   //  ldr ip, [pc, #0]
    270   //  blx ip
    271   return ((current_instr & kLdrPCMask) == kLdrPCPattern)
    272           && ((next_instr & kBlxRegMask) == kBlxRegPattern);
    273 }
    274 
    275 
    276 bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
    277   Instr current_instr = Assembler::instr_at(pc_);
    278   return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
    279 }
    280 
    281 
    282 void RelocInfo::Visit(ObjectVisitor* visitor) {
    283   RelocInfo::Mode mode = rmode();
    284   if (mode == RelocInfo::EMBEDDED_OBJECT) {
    285     visitor->VisitEmbeddedPointer(this);
    286   } else if (RelocInfo::IsCodeTarget(mode)) {
    287     visitor->VisitCodeTarget(this);
    288   } else if (mode == RelocInfo::CELL) {
    289     visitor->VisitCell(this);
    290   } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
    291     visitor->VisitExternalReference(this);
    292   } else if (RelocInfo::IsCodeAgeSequence(mode)) {
    293     visitor->VisitCodeAgeSequence(this);
    294 #ifdef ENABLE_DEBUGGER_SUPPORT
    295   // TODO(isolates): Get a cached isolate below.
    296   } else if (((RelocInfo::IsJSReturn(mode) &&
    297               IsPatchedReturnSequence()) ||
    298              (RelocInfo::IsDebugBreakSlot(mode) &&
    299               IsPatchedDebugBreakSlotSequence())) &&
    300              Isolate::Current()->debug()->has_break_points()) {
    301     visitor->VisitDebugTarget(this);
    302 #endif
    303   } else if (RelocInfo::IsRuntimeEntry(mode)) {
    304     visitor->VisitRuntimeEntry(this);
    305   }
    306 }
    307 
    308 
    309 template<typename StaticVisitor>
    310 void RelocInfo::Visit(Heap* heap) {
    311   RelocInfo::Mode mode = rmode();
    312   if (mode == RelocInfo::EMBEDDED_OBJECT) {
    313     StaticVisitor::VisitEmbeddedPointer(heap, this);
    314   } else if (RelocInfo::IsCodeTarget(mode)) {
    315     StaticVisitor::VisitCodeTarget(heap, this);
    316   } else if (mode == RelocInfo::CELL) {
    317     StaticVisitor::VisitCell(heap, this);
    318   } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
    319     StaticVisitor::VisitExternalReference(this);
    320   } else if (RelocInfo::IsCodeAgeSequence(mode)) {
    321     StaticVisitor::VisitCodeAgeSequence(heap, this);
    322 #ifdef ENABLE_DEBUGGER_SUPPORT
    323   } else if (heap->isolate()->debug()->has_break_points() &&
    324              ((RelocInfo::IsJSReturn(mode) &&
    325               IsPatchedReturnSequence()) ||
    326              (RelocInfo::IsDebugBreakSlot(mode) &&
    327               IsPatchedDebugBreakSlotSequence()))) {
    328     StaticVisitor::VisitDebugTarget(heap, this);
    329 #endif
    330   } else if (RelocInfo::IsRuntimeEntry(mode)) {
    331     StaticVisitor::VisitRuntimeEntry(this);
    332   }
    333 }
    334 
    335 
    336 Operand::Operand(int32_t immediate, RelocInfo::Mode rmode)  {
    337   rm_ = no_reg;
    338   imm32_ = immediate;
    339   rmode_ = rmode;
    340 }
    341 
    342 
    343 Operand::Operand(const ExternalReference& f)  {
    344   rm_ = no_reg;
    345   imm32_ = reinterpret_cast<int32_t>(f.address());
    346   rmode_ = RelocInfo::EXTERNAL_REFERENCE;
    347 }
    348 
    349 
    350 Operand::Operand(Smi* value) {
    351   rm_ = no_reg;
    352   imm32_ =  reinterpret_cast<intptr_t>(value);
    353   rmode_ = RelocInfo::NONE32;
    354 }
    355 
    356 
    357 Operand::Operand(Register rm) {
    358   rm_ = rm;
    359   rs_ = no_reg;
    360   shift_op_ = LSL;
    361   shift_imm_ = 0;
    362 }
    363 
    364 
    365 bool Operand::is_reg() const {
    366   return rm_.is_valid() &&
    367          rs_.is(no_reg) &&
    368          shift_op_ == LSL &&
    369          shift_imm_ == 0;
    370 }
    371 
    372 
    373 void Assembler::CheckBuffer() {
    374   if (buffer_space() <= kGap) {
    375     GrowBuffer();
    376   }
    377   if (pc_offset() >= next_buffer_check_) {
    378     CheckConstPool(false, true);
    379   }
    380 }
    381 
    382 
    383 void Assembler::emit(Instr x) {
    384   CheckBuffer();
    385   *reinterpret_cast<Instr*>(pc_) = x;
    386   pc_ += kInstrSize;
    387 }
    388 
    389 
    390 Address Assembler::target_pointer_address_at(Address pc) {
    391   Address target_pc = pc;
    392   Instr instr = Memory::int32_at(target_pc);
    393   // If we have a bx instruction, the instruction before the bx is
    394   // what we need to patch.
    395   static const int32_t kBxInstMask = 0x0ffffff0;
    396   static const int32_t kBxInstPattern = 0x012fff10;
    397   if ((instr & kBxInstMask) == kBxInstPattern) {
    398     target_pc -= kInstrSize;
    399     instr = Memory::int32_at(target_pc);
    400   }
    401 
    402   // With a blx instruction, the instruction before is what needs to be patched.
    403   if ((instr & kBlxRegMask) == kBlxRegPattern) {
    404     target_pc -= kInstrSize;
    405     instr = Memory::int32_at(target_pc);
    406   }
    407 
    408   ASSERT(IsLdrPcImmediateOffset(instr));
    409   int offset = instr & 0xfff;  // offset_12 is unsigned
    410   if ((instr & (1 << 23)) == 0) offset = -offset;  // U bit defines offset sign
    411   // Verify that the constant pool comes after the instruction referencing it.
    412   ASSERT(offset >= -4);
    413   return target_pc + offset + 8;
    414 }
    415 
    416 
    417 Address Assembler::target_pointer_at(Address pc) {
    418   if (IsMovW(Memory::int32_at(pc))) {
    419     ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
    420     Instruction* instr = Instruction::At(pc);
    421     Instruction* next_instr = Instruction::At(pc + kInstrSize);
    422     return reinterpret_cast<Address>(
    423         (next_instr->ImmedMovwMovtValue() << 16) |
    424         instr->ImmedMovwMovtValue());
    425   }
    426   return Memory::Address_at(target_pointer_address_at(pc));
    427 }
    428 
    429 
    430 Address Assembler::target_address_from_return_address(Address pc) {
    431   // Returns the address of the call target from the return address that will
    432   // be returned to after a call.
    433   // Call sequence on V7 or later is :
    434   //  movw  ip, #... @ call address low 16
    435   //  movt  ip, #... @ call address high 16
    436   //  blx   ip
    437   //                      @ return address
    438   // Or pre-V7 or cases that need frequent patching:
    439   //  ldr   ip, [pc, #...] @ call address
    440   //  blx   ip
    441   //                      @ return address
    442   Address candidate = pc - 2 * Assembler::kInstrSize;
    443   Instr candidate_instr(Memory::int32_at(candidate));
    444   if (IsLdrPcImmediateOffset(candidate_instr)) {
    445     return candidate;
    446   }
    447   candidate = pc - 3 * Assembler::kInstrSize;
    448   ASSERT(IsMovW(Memory::int32_at(candidate)) &&
    449          IsMovT(Memory::int32_at(candidate + kInstrSize)));
    450   return candidate;
    451 }
    452 
    453 
    454 Address Assembler::return_address_from_call_start(Address pc) {
    455   if (IsLdrPcImmediateOffset(Memory::int32_at(pc))) {
    456     return pc + kInstrSize * 2;
    457   } else {
    458     ASSERT(IsMovW(Memory::int32_at(pc)));
    459     ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
    460     return pc + kInstrSize * 3;
    461   }
    462 }
    463 
    464 
    465 void Assembler::deserialization_set_special_target_at(
    466     Address constant_pool_entry, Address target) {
    467   Memory::Address_at(constant_pool_entry) = target;
    468 }
    469 
    470 
    471 void Assembler::set_external_target_at(Address constant_pool_entry,
    472                                        Address target) {
    473   Memory::Address_at(constant_pool_entry) = target;
    474 }
    475 
    476 
    477 static Instr EncodeMovwImmediate(uint32_t immediate) {
    478   ASSERT(immediate < 0x10000);
    479   return ((immediate & 0xf000) << 4) | (immediate & 0xfff);
    480 }
    481 
    482 
    483 void Assembler::set_target_pointer_at(Address pc, Address target) {
    484   if (IsMovW(Memory::int32_at(pc))) {
    485     ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
    486     uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc);
    487     uint32_t immediate = reinterpret_cast<uint32_t>(target);
    488     uint32_t intermediate = instr_ptr[0];
    489     intermediate &= ~EncodeMovwImmediate(0xFFFF);
    490     intermediate |= EncodeMovwImmediate(immediate & 0xFFFF);
    491     instr_ptr[0] = intermediate;
    492     intermediate = instr_ptr[1];
    493     intermediate &= ~EncodeMovwImmediate(0xFFFF);
    494     intermediate |= EncodeMovwImmediate(immediate >> 16);
    495     instr_ptr[1] = intermediate;
    496     ASSERT(IsMovW(Memory::int32_at(pc)));
    497     ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
    498     CPU::FlushICache(pc, 2 * kInstrSize);
    499   } else {
    500     ASSERT(IsLdrPcImmediateOffset(Memory::int32_at(pc)));
    501     Memory::Address_at(target_pointer_address_at(pc)) = target;
    502     // Intuitively, we would think it is necessary to always flush the
    503     // instruction cache after patching a target address in the code as follows:
    504     //   CPU::FlushICache(pc, sizeof(target));
    505     // However, on ARM, no instruction is actually patched in the case
    506     // of embedded constants of the form:
    507     // ldr   ip, [pc, #...]
    508     // since the instruction accessing this address in the constant pool remains
    509     // unchanged.
    510   }
    511 }
    512 
    513 
    514 Address Assembler::target_address_at(Address pc) {
    515   return target_pointer_at(pc);
    516 }
    517 
    518 
    519 void Assembler::set_target_address_at(Address pc, Address target) {
    520   set_target_pointer_at(pc, target);
    521 }
    522 
    523 
    524 } }  // namespace v8::internal
    525 
    526 #endif  // V8_ARM_ASSEMBLER_ARM_INL_H_
    527