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_CODEGEN_ARM_ASSEMBLER_ARM_INL_H_ 38#define V8_CODEGEN_ARM_ASSEMBLER_ARM_INL_H_ 39 40#include "src/codegen/arm/assembler-arm.h" 41 42#include "src/codegen/assembler.h" 43#include "src/debug/debug.h" 44#include "src/objects/objects-inl.h" 45#include "src/objects/smi.h" 46 47namespace v8 { 48namespace internal { 49 50bool CpuFeatures::SupportsOptimizer() { return true; } 51 52int DoubleRegister::SupportedRegisterCount() { 53 return CpuFeatures::IsSupported(VFP32DREGS) ? 32 : 16; 54} 55 56void RelocInfo::apply(intptr_t delta) { 57 if (RelocInfo::IsInternalReference(rmode_)) { 58 // absolute code pointer inside code object moves with the code object. 59 int32_t* p = reinterpret_cast<int32_t*>(pc_); 60 *p += delta; // relocate entry 61 } else if (RelocInfo::IsRelativeCodeTarget(rmode_)) { 62 Instruction* branch = Instruction::At(pc_); 63 int32_t branch_offset = branch->GetBranchOffset() - delta; 64 branch->SetBranchOffset(branch_offset); 65 } 66} 67 68Address RelocInfo::target_address() { 69 DCHECK(IsCodeTargetMode(rmode_) || IsRuntimeEntry(rmode_) || 70 IsWasmCall(rmode_)); 71 return Assembler::target_address_at(pc_, constant_pool_); 72} 73 74Address RelocInfo::target_address_address() { 75 DCHECK(HasTargetAddressAddress()); 76 if (Assembler::IsMovW(Memory<int32_t>(pc_))) { 77 return pc_; 78 } else if (Assembler::IsLdrPcImmediateOffset(Memory<int32_t>(pc_))) { 79 return constant_pool_entry_address(); 80 } else { 81 DCHECK(Assembler::IsBOrBlPcImmediateOffset(Memory<int32_t>(pc_))); 82 DCHECK(IsRelativeCodeTarget(rmode_)); 83 return pc_; 84 } 85} 86 87Address RelocInfo::constant_pool_entry_address() { 88 DCHECK(IsInConstantPool()); 89 return Assembler::constant_pool_entry_address(pc_, constant_pool_); 90} 91 92int RelocInfo::target_address_size() { return kPointerSize; } 93 94HeapObject RelocInfo::target_object(PtrComprCageBase cage_base) { 95 DCHECK(IsCodeTarget(rmode_) || IsFullEmbeddedObject(rmode_) || 96 IsDataEmbeddedObject(rmode_)); 97 if (IsDataEmbeddedObject(rmode_)) { 98 return HeapObject::cast(Object(ReadUnalignedValue<Address>(pc_))); 99 } 100 return HeapObject::cast( 101 Object(Assembler::target_address_at(pc_, constant_pool_))); 102} 103 104Handle<HeapObject> RelocInfo::target_object_handle(Assembler* origin) { 105 if (IsCodeTarget(rmode_) || IsFullEmbeddedObject(rmode_)) { 106 return Handle<HeapObject>(reinterpret_cast<Address*>( 107 Assembler::target_address_at(pc_, constant_pool_))); 108 } else if (IsDataEmbeddedObject(rmode_)) { 109 return Handle<HeapObject>::cast(ReadUnalignedValue<Handle<Object>>(pc_)); 110 } 111 DCHECK(IsRelativeCodeTarget(rmode_)); 112 return origin->relative_code_target_object_handle_at(pc_); 113} 114 115void RelocInfo::set_target_object(Heap* heap, HeapObject target, 116 WriteBarrierMode write_barrier_mode, 117 ICacheFlushMode icache_flush_mode) { 118 DCHECK(IsCodeTarget(rmode_) || IsFullEmbeddedObject(rmode_) || 119 IsDataEmbeddedObject(rmode_)); 120 if (IsDataEmbeddedObject(rmode_)) { 121 WriteUnalignedValue(pc_, target.ptr()); 122 // No need to flush icache since no instructions were changed. 123 } else { 124 Assembler::set_target_address_at(pc_, constant_pool_, target.ptr(), 125 icache_flush_mode); 126 } 127 if (write_barrier_mode == UPDATE_WRITE_BARRIER && !host().is_null() && 128 !FLAG_disable_write_barriers) { 129 WriteBarrierForCode(host(), this, target); 130 } 131} 132 133Address RelocInfo::target_external_reference() { 134 DCHECK(rmode_ == EXTERNAL_REFERENCE); 135 return Assembler::target_address_at(pc_, constant_pool_); 136} 137 138void RelocInfo::set_target_external_reference( 139 Address target, ICacheFlushMode icache_flush_mode) { 140 DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE); 141 Assembler::set_target_address_at(pc_, constant_pool_, target, 142 icache_flush_mode); 143} 144 145Address RelocInfo::target_internal_reference() { 146 DCHECK(rmode_ == INTERNAL_REFERENCE); 147 return Memory<Address>(pc_); 148} 149 150Address RelocInfo::target_internal_reference_address() { 151 DCHECK(rmode_ == INTERNAL_REFERENCE); 152 return pc_; 153} 154 155Address RelocInfo::target_runtime_entry(Assembler* origin) { 156 DCHECK(IsRuntimeEntry(rmode_)); 157 return target_address(); 158} 159 160void RelocInfo::set_target_runtime_entry(Address target, 161 WriteBarrierMode write_barrier_mode, 162 ICacheFlushMode icache_flush_mode) { 163 DCHECK(IsRuntimeEntry(rmode_)); 164 if (target_address() != target) 165 set_target_address(target, write_barrier_mode, icache_flush_mode); 166} 167 168Address RelocInfo::target_off_heap_target() { 169 DCHECK(IsOffHeapTarget(rmode_)); 170 return Assembler::target_address_at(pc_, constant_pool_); 171} 172 173void RelocInfo::WipeOut() { 174 DCHECK(IsFullEmbeddedObject(rmode_) || IsCodeTarget(rmode_) || 175 IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) || 176 IsInternalReference(rmode_) || IsOffHeapTarget(rmode_)); 177 if (IsInternalReference(rmode_)) { 178 Memory<Address>(pc_) = kNullAddress; 179 } else { 180 Assembler::set_target_address_at(pc_, constant_pool_, kNullAddress); 181 } 182} 183 184Handle<Code> Assembler::relative_code_target_object_handle_at( 185 Address pc) const { 186 Instruction* branch = Instruction::At(pc); 187 int code_target_index = branch->GetBranchOffset() / kInstrSize; 188 return GetCodeTarget(code_target_index); 189} 190 191Operand Operand::Zero() { return Operand(static_cast<int32_t>(0)); } 192 193Operand::Operand(const ExternalReference& f) 194 : rmode_(RelocInfo::EXTERNAL_REFERENCE) { 195 value_.immediate = static_cast<int32_t>(f.address()); 196} 197 198Operand::Operand(Smi value) : rmode_(RelocInfo::NO_INFO) { 199 value_.immediate = static_cast<intptr_t>(value.ptr()); 200} 201 202Operand::Operand(Register rm) : rm_(rm), shift_op_(LSL), shift_imm_(0) {} 203 204void Assembler::CheckBuffer() { 205 if (V8_UNLIKELY(buffer_space() <= kGap)) { 206 GrowBuffer(); 207 } 208 MaybeCheckConstPool(); 209} 210 211void Assembler::emit(Instr x) { 212 CheckBuffer(); 213 *reinterpret_cast<Instr*>(pc_) = x; 214 pc_ += kInstrSize; 215} 216 217void Assembler::deserialization_set_special_target_at( 218 Address constant_pool_entry, Code code, Address target) { 219 DCHECK(!Builtins::IsIsolateIndependentBuiltin(code)); 220 Memory<Address>(constant_pool_entry) = target; 221} 222 223int Assembler::deserialization_special_target_size(Address location) { 224 return kSpecialTargetSize; 225} 226 227void Assembler::deserialization_set_target_internal_reference_at( 228 Address pc, Address target, RelocInfo::Mode mode) { 229 Memory<Address>(pc) = target; 230} 231 232bool Assembler::is_constant_pool_load(Address pc) { 233 return IsLdrPcImmediateOffset(Memory<int32_t>(pc)); 234} 235 236Address Assembler::constant_pool_entry_address(Address pc, 237 Address constant_pool) { 238 DCHECK(Assembler::IsLdrPcImmediateOffset(Memory<int32_t>(pc))); 239 Instr instr = Memory<int32_t>(pc); 240 return pc + GetLdrRegisterImmediateOffset(instr) + Instruction::kPcLoadDelta; 241} 242 243Address Assembler::target_address_at(Address pc, Address constant_pool) { 244 if (is_constant_pool_load(pc)) { 245 // This is a constant pool lookup. Return the value in the constant pool. 246 return Memory<Address>(constant_pool_entry_address(pc, constant_pool)); 247 } else if (CpuFeatures::IsSupported(ARMv7) && IsMovW(Memory<int32_t>(pc))) { 248 // This is an movw / movt immediate load. Return the immediate. 249 DCHECK(IsMovW(Memory<int32_t>(pc)) && 250 IsMovT(Memory<int32_t>(pc + kInstrSize))); 251 Instruction* movw_instr = Instruction::At(pc); 252 Instruction* movt_instr = Instruction::At(pc + kInstrSize); 253 return static_cast<Address>((movt_instr->ImmedMovwMovtValue() << 16) | 254 movw_instr->ImmedMovwMovtValue()); 255 } else if (IsMovImmed(Memory<int32_t>(pc))) { 256 // This is an mov / orr immediate load. Return the immediate. 257 DCHECK(IsMovImmed(Memory<int32_t>(pc)) && 258 IsOrrImmed(Memory<int32_t>(pc + kInstrSize)) && 259 IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)) && 260 IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize))); 261 Instr mov_instr = instr_at(pc); 262 Instr orr_instr_1 = instr_at(pc + kInstrSize); 263 Instr orr_instr_2 = instr_at(pc + 2 * kInstrSize); 264 Instr orr_instr_3 = instr_at(pc + 3 * kInstrSize); 265 Address ret = static_cast<Address>( 266 DecodeShiftImm(mov_instr) | DecodeShiftImm(orr_instr_1) | 267 DecodeShiftImm(orr_instr_2) | DecodeShiftImm(orr_instr_3)); 268 return ret; 269 } else { 270 Instruction* branch = Instruction::At(pc); 271 int32_t delta = branch->GetBranchOffset(); 272 return pc + delta + Instruction::kPcLoadDelta; 273 } 274} 275 276void Assembler::set_target_address_at(Address pc, Address constant_pool, 277 Address target, 278 ICacheFlushMode icache_flush_mode) { 279 if (is_constant_pool_load(pc)) { 280 // This is a constant pool lookup. Update the entry in the constant pool. 281 Memory<Address>(constant_pool_entry_address(pc, constant_pool)) = target; 282 // Intuitively, we would think it is necessary to always flush the 283 // instruction cache after patching a target address in the code as follows: 284 // FlushInstructionCache(pc, sizeof(target)); 285 // However, on ARM, no instruction is actually patched in the case 286 // of embedded constants of the form: 287 // ldr ip, [pp, #...] 288 // since the instruction accessing this address in the constant pool remains 289 // unchanged. 290 } else if (CpuFeatures::IsSupported(ARMv7) && IsMovW(Memory<int32_t>(pc))) { 291 // This is an movw / movt immediate load. Patch the immediate embedded in 292 // the instructions. 293 DCHECK(IsMovW(Memory<int32_t>(pc))); 294 DCHECK(IsMovT(Memory<int32_t>(pc + kInstrSize))); 295 uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc); 296 uint32_t immediate = static_cast<uint32_t>(target); 297 instr_ptr[0] = PatchMovwImmediate(instr_ptr[0], immediate & 0xFFFF); 298 instr_ptr[1] = PatchMovwImmediate(instr_ptr[1], immediate >> 16); 299 DCHECK(IsMovW(Memory<int32_t>(pc))); 300 DCHECK(IsMovT(Memory<int32_t>(pc + kInstrSize))); 301 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 302 FlushInstructionCache(pc, 2 * kInstrSize); 303 } 304 } else if (IsMovImmed(Memory<int32_t>(pc))) { 305 // This is an mov / orr immediate load. Patch the immediate embedded in 306 // the instructions. 307 DCHECK(IsMovImmed(Memory<int32_t>(pc)) && 308 IsOrrImmed(Memory<int32_t>(pc + kInstrSize)) && 309 IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)) && 310 IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize))); 311 uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc); 312 uint32_t immediate = static_cast<uint32_t>(target); 313 instr_ptr[0] = PatchShiftImm(instr_ptr[0], immediate & kImm8Mask); 314 instr_ptr[1] = PatchShiftImm(instr_ptr[1], immediate & (kImm8Mask << 8)); 315 instr_ptr[2] = PatchShiftImm(instr_ptr[2], immediate & (kImm8Mask << 16)); 316 instr_ptr[3] = PatchShiftImm(instr_ptr[3], immediate & (kImm8Mask << 24)); 317 DCHECK(IsMovImmed(Memory<int32_t>(pc)) && 318 IsOrrImmed(Memory<int32_t>(pc + kInstrSize)) && 319 IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)) && 320 IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize))); 321 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 322 FlushInstructionCache(pc, 4 * kInstrSize); 323 } 324 } else { 325 intptr_t branch_offset = target - pc - Instruction::kPcLoadDelta; 326 Instruction* branch = Instruction::At(pc); 327 branch->SetBranchOffset(branch_offset); 328 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 329 FlushInstructionCache(pc, kInstrSize); 330 } 331 } 332} 333 334EnsureSpace::EnsureSpace(Assembler* assembler) { assembler->CheckBuffer(); } 335 336template <typename T> 337bool UseScratchRegisterScope::CanAcquireVfp() const { 338 VfpRegList* available = assembler_->GetScratchVfpRegisterList(); 339 DCHECK_NOT_NULL(available); 340 for (int index = 0; index < T::kNumRegisters; index++) { 341 T reg = T::from_code(index); 342 uint64_t mask = reg.ToVfpRegList(); 343 if ((*available & mask) == mask) { 344 return true; 345 } 346 } 347 return false; 348} 349 350template <typename T> 351T UseScratchRegisterScope::AcquireVfp() { 352 VfpRegList* available = assembler_->GetScratchVfpRegisterList(); 353 DCHECK_NOT_NULL(available); 354 for (int index = 0; index < T::kNumRegisters; index++) { 355 T reg = T::from_code(index); 356 uint64_t mask = reg.ToVfpRegList(); 357 if ((*available & mask) == mask) { 358 *available &= ~mask; 359 return reg; 360 } 361 } 362 UNREACHABLE(); 363} 364 365} // namespace internal 366} // namespace v8 367 368#endif // V8_CODEGEN_ARM_ASSEMBLER_ARM_INL_H_ 369