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::NumReservedRegisters() {
     61   return kNumReservedRegisters;
     62 }
     63 
     64 
     65 int DwVfpRegister::NumAllocatableRegisters() {
     66   return NumRegisters() - kNumReservedRegisters;
     67 }
     68 
     69 
     70 int DwVfpRegister::ToAllocationIndex(DwVfpRegister reg) {
     71   ASSERT(!reg.is(kDoubleRegZero));
     72   ASSERT(!reg.is(kScratchDoubleReg));
     73   if (reg.code() > kDoubleRegZero.code()) {
     74     return reg.code() - kNumReservedRegisters;
     75   }
     76   return reg.code();
     77 }
     78 
     79 
     80 DwVfpRegister DwVfpRegister::FromAllocationIndex(int index) {
     81   ASSERT(index >= 0 && index < NumAllocatableRegisters());
     82   ASSERT(kScratchDoubleReg.code() - kDoubleRegZero.code() ==
     83          kNumReservedRegisters - 1);
     84   if (index >= kDoubleRegZero.code()) {
     85     return from_code(index + kNumReservedRegisters);
     86   }
     87   return from_code(index);
     88 }
     89 
     90 
     91 void RelocInfo::apply(intptr_t delta) {
     92   if (RelocInfo::IsInternalReference(rmode_)) {
     93     // absolute code pointer inside code object moves with the code object.
     94     int32_t* p = reinterpret_cast<int32_t*>(pc_);
     95     *p += delta;  // relocate entry
     96   }
     97   // We do not use pc relative addressing on ARM, so there is
     98   // nothing else to do.
     99 }
    100 
    101 
    102 Address RelocInfo::target_address() {
    103   ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
    104   return Assembler::target_address_at(pc_);
    105 }
    106 
    107 
    108 Address RelocInfo::target_address_address() {
    109   ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
    110                               || rmode_ == EMBEDDED_OBJECT
    111                               || rmode_ == EXTERNAL_REFERENCE);
    112   return Assembler::target_pointer_address_at(pc_);
    113 }
    114 
    115 
    116 int RelocInfo::target_address_size() {
    117   return kPointerSize;
    118 }
    119 
    120 
    121 void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) {
    122   ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
    123   Assembler::set_target_address_at(pc_, target);
    124   if (mode == UPDATE_WRITE_BARRIER && host() != NULL && IsCodeTarget(rmode_)) {
    125     Object* target_code = Code::GetCodeFromTargetAddress(target);
    126     host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
    127         host(), this, HeapObject::cast(target_code));
    128   }
    129 }
    130 
    131 
    132 Object* RelocInfo::target_object() {
    133   ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
    134   return reinterpret_cast<Object*>(Assembler::target_address_at(pc_));
    135 }
    136 
    137 
    138 Handle<Object> RelocInfo::target_object_handle(Assembler* origin) {
    139   ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
    140   return Handle<Object>(reinterpret_cast<Object**>(
    141       Assembler::target_address_at(pc_)));
    142 }
    143 
    144 
    145 void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) {
    146   ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
    147   ASSERT(!target->IsConsString());
    148   Assembler::set_target_address_at(pc_, reinterpret_cast<Address>(target));
    149   if (mode == UPDATE_WRITE_BARRIER &&
    150       host() != NULL &&
    151       target->IsHeapObject()) {
    152     host()->GetHeap()->incremental_marking()->RecordWrite(
    153         host(), &Memory::Object_at(pc_), HeapObject::cast(target));
    154   }
    155 }
    156 
    157 
    158 Address RelocInfo::target_reference() {
    159   ASSERT(rmode_ == EXTERNAL_REFERENCE);
    160   return Assembler::target_address_at(pc_);
    161 }
    162 
    163 
    164 Address RelocInfo::target_runtime_entry(Assembler* origin) {
    165   ASSERT(IsRuntimeEntry(rmode_));
    166   return target_address();
    167 }
    168 
    169 
    170 void RelocInfo::set_target_runtime_entry(Address target,
    171                                          WriteBarrierMode mode) {
    172   ASSERT(IsRuntimeEntry(rmode_));
    173   if (target_address() != target) set_target_address(target, mode);
    174 }
    175 
    176 
    177 Handle<Cell> RelocInfo::target_cell_handle() {
    178   ASSERT(rmode_ == RelocInfo::CELL);
    179   Address address = Memory::Address_at(pc_);
    180   return Handle<Cell>(reinterpret_cast<Cell**>(address));
    181 }
    182 
    183 
    184 Cell* RelocInfo::target_cell() {
    185   ASSERT(rmode_ == RelocInfo::CELL);
    186   return Cell::FromValueAddress(Memory::Address_at(pc_));
    187 }
    188 
    189 
    190 void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) {
    191   ASSERT(rmode_ == RelocInfo::CELL);
    192   Address address = cell->address() + Cell::kValueOffset;
    193   Memory::Address_at(pc_) = address;
    194   if (mode == UPDATE_WRITE_BARRIER && host() != NULL) {
    195     // TODO(1550) We are passing NULL as a slot because cell can never be on
    196     // evacuation candidate.
    197     host()->GetHeap()->incremental_marking()->RecordWrite(
    198         host(), NULL, cell);
    199   }
    200 }
    201 
    202 
    203 static const int kNoCodeAgeSequenceLength = 3;
    204 
    205 
    206 Handle<Object> RelocInfo::code_age_stub_handle(Assembler* origin) {
    207   UNREACHABLE();  // This should never be reached on Arm.
    208   return Handle<Object>();
    209 }
    210 
    211 
    212 Code* RelocInfo::code_age_stub() {
    213   ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
    214   return Code::GetCodeFromTargetAddress(
    215       Memory::Address_at(pc_ + Assembler::kInstrSize *
    216                          (kNoCodeAgeSequenceLength - 1)));
    217 }
    218 
    219 
    220 void RelocInfo::set_code_age_stub(Code* stub) {
    221   ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
    222   Memory::Address_at(pc_ + Assembler::kInstrSize *
    223                      (kNoCodeAgeSequenceLength - 1)) =
    224       stub->instruction_start();
    225 }
    226 
    227 
    228 Address RelocInfo::call_address() {
    229   // The 2 instructions offset assumes patched debug break slot or return
    230   // sequence.
    231   ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
    232          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
    233   return Memory::Address_at(pc_ + 2 * Assembler::kInstrSize);
    234 }
    235 
    236 
    237 void RelocInfo::set_call_address(Address target) {
    238   ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
    239          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
    240   Memory::Address_at(pc_ + 2 * Assembler::kInstrSize) = target;
    241   if (host() != NULL) {
    242     Object* target_code = Code::GetCodeFromTargetAddress(target);
    243     host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
    244         host(), this, HeapObject::cast(target_code));
    245   }
    246 }
    247 
    248 
    249 Object* RelocInfo::call_object() {
    250   return *call_object_address();
    251 }
    252 
    253 
    254 void RelocInfo::set_call_object(Object* target) {
    255   *call_object_address() = target;
    256 }
    257 
    258 
    259 Object** RelocInfo::call_object_address() {
    260   ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
    261          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
    262   return reinterpret_cast<Object**>(pc_ + 2 * Assembler::kInstrSize);
    263 }
    264 
    265 
    266 void RelocInfo::WipeOut() {
    267   ASSERT(IsEmbeddedObject(rmode_) ||
    268          IsCodeTarget(rmode_) ||
    269          IsRuntimeEntry(rmode_) ||
    270          IsExternalReference(rmode_));
    271   Assembler::set_target_address_at(pc_, NULL);
    272 }
    273 
    274 
    275 bool RelocInfo::IsPatchedReturnSequence() {
    276   Instr current_instr = Assembler::instr_at(pc_);
    277   Instr next_instr = Assembler::instr_at(pc_ + Assembler::kInstrSize);
    278   // A patched return sequence is:
    279   //  ldr ip, [pc, #0]
    280   //  blx ip
    281   return ((current_instr & kLdrPCMask) == kLdrPCPattern)
    282           && ((next_instr & kBlxRegMask) == kBlxRegPattern);
    283 }
    284 
    285 
    286 bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
    287   Instr current_instr = Assembler::instr_at(pc_);
    288   return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
    289 }
    290 
    291 
    292 void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
    293   RelocInfo::Mode mode = rmode();
    294   if (mode == RelocInfo::EMBEDDED_OBJECT) {
    295     visitor->VisitEmbeddedPointer(this);
    296   } else if (RelocInfo::IsCodeTarget(mode)) {
    297     visitor->VisitCodeTarget(this);
    298   } else if (mode == RelocInfo::CELL) {
    299     visitor->VisitCell(this);
    300   } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
    301     visitor->VisitExternalReference(this);
    302   } else if (RelocInfo::IsCodeAgeSequence(mode)) {
    303     visitor->VisitCodeAgeSequence(this);
    304 #ifdef ENABLE_DEBUGGER_SUPPORT
    305   } else if (((RelocInfo::IsJSReturn(mode) &&
    306               IsPatchedReturnSequence()) ||
    307              (RelocInfo::IsDebugBreakSlot(mode) &&
    308               IsPatchedDebugBreakSlotSequence())) &&
    309              isolate->debug()->has_break_points()) {
    310     visitor->VisitDebugTarget(this);
    311 #endif
    312   } else if (RelocInfo::IsRuntimeEntry(mode)) {
    313     visitor->VisitRuntimeEntry(this);
    314   }
    315 }
    316 
    317 
    318 template<typename StaticVisitor>
    319 void RelocInfo::Visit(Heap* heap) {
    320   RelocInfo::Mode mode = rmode();
    321   if (mode == RelocInfo::EMBEDDED_OBJECT) {
    322     StaticVisitor::VisitEmbeddedPointer(heap, this);
    323   } else if (RelocInfo::IsCodeTarget(mode)) {
    324     StaticVisitor::VisitCodeTarget(heap, this);
    325   } else if (mode == RelocInfo::CELL) {
    326     StaticVisitor::VisitCell(heap, this);
    327   } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
    328     StaticVisitor::VisitExternalReference(this);
    329   } else if (RelocInfo::IsCodeAgeSequence(mode)) {
    330     StaticVisitor::VisitCodeAgeSequence(heap, this);
    331 #ifdef ENABLE_DEBUGGER_SUPPORT
    332   } else if (heap->isolate()->debug()->has_break_points() &&
    333              ((RelocInfo::IsJSReturn(mode) &&
    334               IsPatchedReturnSequence()) ||
    335              (RelocInfo::IsDebugBreakSlot(mode) &&
    336               IsPatchedDebugBreakSlotSequence()))) {
    337     StaticVisitor::VisitDebugTarget(heap, this);
    338 #endif
    339   } else if (RelocInfo::IsRuntimeEntry(mode)) {
    340     StaticVisitor::VisitRuntimeEntry(this);
    341   }
    342 }
    343 
    344 
    345 Operand::Operand(int32_t immediate, RelocInfo::Mode rmode)  {
    346   rm_ = no_reg;
    347   imm32_ = immediate;
    348   rmode_ = rmode;
    349 }
    350 
    351 
    352 Operand::Operand(const ExternalReference& f)  {
    353   rm_ = no_reg;
    354   imm32_ = reinterpret_cast<int32_t>(f.address());
    355   rmode_ = RelocInfo::EXTERNAL_REFERENCE;
    356 }
    357 
    358 
    359 Operand::Operand(Smi* value) {
    360   rm_ = no_reg;
    361   imm32_ =  reinterpret_cast<intptr_t>(value);
    362   rmode_ = RelocInfo::NONE32;
    363 }
    364 
    365 
    366 Operand::Operand(Register rm) {
    367   rm_ = rm;
    368   rs_ = no_reg;
    369   shift_op_ = LSL;
    370   shift_imm_ = 0;
    371 }
    372 
    373 
    374 bool Operand::is_reg() const {
    375   return rm_.is_valid() &&
    376          rs_.is(no_reg) &&
    377          shift_op_ == LSL &&
    378          shift_imm_ == 0;
    379 }
    380 
    381 
    382 void Assembler::CheckBuffer() {
    383   if (buffer_space() <= kGap) {
    384     GrowBuffer();
    385   }
    386   if (pc_offset() >= next_buffer_check_) {
    387     CheckConstPool(false, true);
    388   }
    389 }
    390 
    391 
    392 void Assembler::emit(Instr x) {
    393   CheckBuffer();
    394   *reinterpret_cast<Instr*>(pc_) = x;
    395   pc_ += kInstrSize;
    396 }
    397 
    398 
    399 Address Assembler::target_pointer_address_at(Address pc) {
    400   Instr instr = Memory::int32_at(pc);
    401   return pc + GetLdrRegisterImmediateOffset(instr) + kPcLoadDelta;
    402 }
    403 
    404 
    405 Address Assembler::target_address_at(Address pc) {
    406   if (IsMovW(Memory::int32_at(pc))) {
    407     ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
    408     Instruction* instr = Instruction::At(pc);
    409     Instruction* next_instr = Instruction::At(pc + kInstrSize);
    410     return reinterpret_cast<Address>(
    411         (next_instr->ImmedMovwMovtValue() << 16) |
    412         instr->ImmedMovwMovtValue());
    413   }
    414   ASSERT(IsLdrPcImmediateOffset(Memory::int32_at(pc)));
    415   return Memory::Address_at(target_pointer_address_at(pc));
    416 }
    417 
    418 
    419 Address Assembler::target_address_from_return_address(Address pc) {
    420   // Returns the address of the call target from the return address that will
    421   // be returned to after a call.
    422   // Call sequence on V7 or later is :
    423   //  movw  ip, #... @ call address low 16
    424   //  movt  ip, #... @ call address high 16
    425   //  blx   ip
    426   //                      @ return address
    427   // Or pre-V7 or cases that need frequent patching:
    428   //  ldr   ip, [pc, #...] @ call address
    429   //  blx   ip
    430   //                      @ return address
    431   Address candidate = pc - 2 * Assembler::kInstrSize;
    432   Instr candidate_instr(Memory::int32_at(candidate));
    433   if (IsLdrPcImmediateOffset(candidate_instr)) {
    434     return candidate;
    435   }
    436   candidate = pc - 3 * Assembler::kInstrSize;
    437   ASSERT(IsMovW(Memory::int32_at(candidate)) &&
    438          IsMovT(Memory::int32_at(candidate + kInstrSize)));
    439   return candidate;
    440 }
    441 
    442 
    443 Address Assembler::return_address_from_call_start(Address pc) {
    444   if (IsLdrPcImmediateOffset(Memory::int32_at(pc))) {
    445     return pc + kInstrSize * 2;
    446   } else {
    447     ASSERT(IsMovW(Memory::int32_at(pc)));
    448     ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
    449     return pc + kInstrSize * 3;
    450   }
    451 }
    452 
    453 
    454 void Assembler::deserialization_set_special_target_at(
    455     Address constant_pool_entry, Address target) {
    456   Memory::Address_at(constant_pool_entry) = target;
    457 }
    458 
    459 
    460 static Instr EncodeMovwImmediate(uint32_t immediate) {
    461   ASSERT(immediate < 0x10000);
    462   return ((immediate & 0xf000) << 4) | (immediate & 0xfff);
    463 }
    464 
    465 
    466 void Assembler::set_target_address_at(Address pc, Address target) {
    467   if (IsMovW(Memory::int32_at(pc))) {
    468     ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
    469     uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc);
    470     uint32_t immediate = reinterpret_cast<uint32_t>(target);
    471     uint32_t intermediate = instr_ptr[0];
    472     intermediate &= ~EncodeMovwImmediate(0xFFFF);
    473     intermediate |= EncodeMovwImmediate(immediate & 0xFFFF);
    474     instr_ptr[0] = intermediate;
    475     intermediate = instr_ptr[1];
    476     intermediate &= ~EncodeMovwImmediate(0xFFFF);
    477     intermediate |= EncodeMovwImmediate(immediate >> 16);
    478     instr_ptr[1] = intermediate;
    479     ASSERT(IsMovW(Memory::int32_at(pc)));
    480     ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
    481     CPU::FlushICache(pc, 2 * kInstrSize);
    482   } else {
    483     ASSERT(IsLdrPcImmediateOffset(Memory::int32_at(pc)));
    484     Memory::Address_at(target_pointer_address_at(pc)) = target;
    485     // Intuitively, we would think it is necessary to always flush the
    486     // instruction cache after patching a target address in the code as follows:
    487     //   CPU::FlushICache(pc, sizeof(target));
    488     // However, on ARM, no instruction is actually patched in the case
    489     // of embedded constants of the form:
    490     // ldr   ip, [pc, #...]
    491     // since the instruction accessing this address in the constant pool remains
    492     // unchanged.
    493   }
    494 }
    495 
    496 
    497 } }  // namespace v8::internal
    498 
    499 #endif  // V8_ARM_ASSEMBLER_ARM_INL_H_
    500