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