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