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 2014 the V8 project authors. All rights reserved. 36 37 #ifndef V8_PPC_ASSEMBLER_PPC_INL_H_ 38 #define V8_PPC_ASSEMBLER_PPC_INL_H_ 39 40 #include "src/ppc/assembler-ppc.h" 41 42 #include "src/assembler.h" 43 #include "src/debug/debug.h" 44 #include "src/objects-inl.h" 45 46 namespace v8 { 47 namespace internal { 48 49 bool CpuFeatures::SupportsOptimizer() { return true; } 50 51 bool CpuFeatures::SupportsWasmSimd128() { return false; } 52 53 void RelocInfo::apply(intptr_t delta) { 54 // absolute code pointer inside code object moves with the code object. 55 if (IsInternalReference(rmode_)) { 56 // Jump table entry 57 Address target = Memory<Address>(pc_); 58 Memory<Address>(pc_) = target + delta; 59 } else { 60 // mov sequence 61 DCHECK(IsInternalReferenceEncoded(rmode_)); 62 Address target = Assembler::target_address_at(pc_, constant_pool_); 63 Assembler::set_target_address_at(pc_, constant_pool_, target + delta, 64 SKIP_ICACHE_FLUSH); 65 } 66 } 67 68 69 Address RelocInfo::target_internal_reference() { 70 if (IsInternalReference(rmode_)) { 71 // Jump table entry 72 return Memory<Address>(pc_); 73 } else { 74 // mov sequence 75 DCHECK(IsInternalReferenceEncoded(rmode_)); 76 return Assembler::target_address_at(pc_, constant_pool_); 77 } 78 } 79 80 81 Address RelocInfo::target_internal_reference_address() { 82 DCHECK(IsInternalReference(rmode_) || IsInternalReferenceEncoded(rmode_)); 83 return pc_; 84 } 85 86 87 Address RelocInfo::target_address() { 88 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_)); 89 return Assembler::target_address_at(pc_, constant_pool_); 90 } 91 92 Address RelocInfo::target_address_address() { 93 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_) || 94 IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) || 95 IsOffHeapTarget(rmode_)); 96 97 if (FLAG_enable_embedded_constant_pool && 98 Assembler::IsConstantPoolLoadStart(pc_)) { 99 // We return the PC for embedded constant pool since this function is used 100 // by the serializer and expects the address to reside within the code 101 // object. 102 return pc_; 103 } 104 105 // Read the address of the word containing the target_address in an 106 // instruction stream. 107 // The only architecture-independent user of this function is the serializer. 108 // The serializer uses it to find out how many raw bytes of instruction to 109 // output before the next target. 110 // For an instruction like LIS/ORI where the target bits are mixed into the 111 // instruction bits, the size of the target will be zero, indicating that the 112 // serializer should not step forward in memory after a target is resolved 113 // and written. 114 return pc_; 115 } 116 117 118 Address RelocInfo::constant_pool_entry_address() { 119 if (FLAG_enable_embedded_constant_pool) { 120 DCHECK(constant_pool_); 121 ConstantPoolEntry::Access access; 122 if (Assembler::IsConstantPoolLoadStart(pc_, &access)) 123 return Assembler::target_constant_pool_address_at( 124 pc_, constant_pool_, access, ConstantPoolEntry::INTPTR); 125 } 126 UNREACHABLE(); 127 } 128 129 130 int RelocInfo::target_address_size() { return Assembler::kSpecialTargetSize; } 131 132 Address Assembler::target_address_from_return_address(Address pc) { 133 // Returns the address of the call target from the return address that will 134 // be returned to after a call. 135 // Call sequence is : 136 // mov ip, @ call address 137 // mtlr ip 138 // blrl 139 // @ return address 140 int len; 141 ConstantPoolEntry::Access access; 142 if (FLAG_enable_embedded_constant_pool && 143 IsConstantPoolLoadEnd(pc - 3 * kInstrSize, &access)) { 144 len = (access == ConstantPoolEntry::OVERFLOWED) ? 2 : 1; 145 } else { 146 len = kMovInstructionsNoConstantPool; 147 } 148 return pc - (len + 2) * kInstrSize; 149 } 150 151 152 Address Assembler::return_address_from_call_start(Address pc) { 153 int len; 154 ConstantPoolEntry::Access access; 155 if (FLAG_enable_embedded_constant_pool && 156 IsConstantPoolLoadStart(pc, &access)) { 157 len = (access == ConstantPoolEntry::OVERFLOWED) ? 2 : 1; 158 } else { 159 len = kMovInstructionsNoConstantPool; 160 } 161 return pc + (len + 2) * kInstrSize; 162 } 163 164 HeapObject* RelocInfo::target_object() { 165 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 166 return HeapObject::cast(reinterpret_cast<Object*>( 167 Assembler::target_address_at(pc_, constant_pool_))); 168 } 169 170 Handle<HeapObject> RelocInfo::target_object_handle(Assembler* origin) { 171 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 172 return Handle<HeapObject>(reinterpret_cast<HeapObject**>( 173 Assembler::target_address_at(pc_, constant_pool_))); 174 } 175 176 void RelocInfo::set_target_object(Heap* heap, HeapObject* target, 177 WriteBarrierMode write_barrier_mode, 178 ICacheFlushMode icache_flush_mode) { 179 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 180 Assembler::set_target_address_at(pc_, constant_pool_, 181 reinterpret_cast<Address>(target), 182 icache_flush_mode); 183 if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != nullptr) { 184 WriteBarrierForCode(host(), this, target); 185 } 186 } 187 188 189 Address RelocInfo::target_external_reference() { 190 DCHECK(rmode_ == EXTERNAL_REFERENCE); 191 return Assembler::target_address_at(pc_, constant_pool_); 192 } 193 194 void RelocInfo::set_target_external_reference( 195 Address target, ICacheFlushMode icache_flush_mode) { 196 DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE); 197 Assembler::set_target_address_at(pc_, constant_pool_, target, 198 icache_flush_mode); 199 } 200 201 Address RelocInfo::target_runtime_entry(Assembler* origin) { 202 DCHECK(IsRuntimeEntry(rmode_)); 203 return target_address(); 204 } 205 206 void RelocInfo::set_target_runtime_entry(Address target, 207 WriteBarrierMode write_barrier_mode, 208 ICacheFlushMode icache_flush_mode) { 209 DCHECK(IsRuntimeEntry(rmode_)); 210 if (target_address() != target) 211 set_target_address(target, write_barrier_mode, icache_flush_mode); 212 } 213 214 Address RelocInfo::target_off_heap_target() { 215 DCHECK(IsOffHeapTarget(rmode_)); 216 return Assembler::target_address_at(pc_, constant_pool_); 217 } 218 219 void RelocInfo::WipeOut() { 220 DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) || 221 IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) || 222 IsInternalReference(rmode_) || IsInternalReferenceEncoded(rmode_) || 223 IsOffHeapTarget(rmode_)); 224 if (IsInternalReference(rmode_)) { 225 // Jump table entry 226 Memory<Address>(pc_) = kNullAddress; 227 } else if (IsInternalReferenceEncoded(rmode_) || IsOffHeapTarget(rmode_)) { 228 // mov sequence 229 // Currently used only by deserializer, no need to flush. 230 Assembler::set_target_address_at(pc_, constant_pool_, kNullAddress, 231 SKIP_ICACHE_FLUSH); 232 } else { 233 Assembler::set_target_address_at(pc_, constant_pool_, kNullAddress); 234 } 235 } 236 237 template <typename ObjectVisitor> 238 void RelocInfo::Visit(ObjectVisitor* visitor) { 239 RelocInfo::Mode mode = rmode(); 240 if (mode == RelocInfo::EMBEDDED_OBJECT) { 241 visitor->VisitEmbeddedPointer(host(), this); 242 } else if (RelocInfo::IsCodeTargetMode(mode)) { 243 visitor->VisitCodeTarget(host(), this); 244 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { 245 visitor->VisitExternalReference(host(), this); 246 } else if (mode == RelocInfo::INTERNAL_REFERENCE || 247 mode == RelocInfo::INTERNAL_REFERENCE_ENCODED) { 248 visitor->VisitInternalReference(host(), this); 249 } else if (IsRuntimeEntry(mode)) { 250 visitor->VisitRuntimeEntry(host(), this); 251 } else if (RelocInfo::IsOffHeapTarget(mode)) { 252 visitor->VisitOffHeapTarget(host(), this); 253 } 254 } 255 256 Operand::Operand(Register rm) : rm_(rm), rmode_(RelocInfo::NONE) {} 257 258 void Assembler::UntrackBranch() { 259 DCHECK(!trampoline_emitted_); 260 DCHECK_GT(tracked_branch_count_, 0); 261 int count = --tracked_branch_count_; 262 if (count == 0) { 263 // Reset 264 next_trampoline_check_ = kMaxInt; 265 } else { 266 next_trampoline_check_ += kTrampolineSlotsSize; 267 } 268 } 269 270 // Fetch the 32bit value from the FIXED_SEQUENCE lis/ori 271 Address Assembler::target_address_at(Address pc, Address constant_pool) { 272 if (FLAG_enable_embedded_constant_pool && constant_pool) { 273 ConstantPoolEntry::Access access; 274 if (IsConstantPoolLoadStart(pc, &access)) 275 return Memory<Address>(target_constant_pool_address_at( 276 pc, constant_pool, access, ConstantPoolEntry::INTPTR)); 277 } 278 279 Instr instr1 = instr_at(pc); 280 Instr instr2 = instr_at(pc + kInstrSize); 281 // Interpret 2 instructions generated by lis/ori 282 if (IsLis(instr1) && IsOri(instr2)) { 283 #if V8_TARGET_ARCH_PPC64 284 Instr instr4 = instr_at(pc + (3 * kInstrSize)); 285 Instr instr5 = instr_at(pc + (4 * kInstrSize)); 286 // Assemble the 64 bit value. 287 uint64_t hi = (static_cast<uint32_t>((instr1 & kImm16Mask) << 16) | 288 static_cast<uint32_t>(instr2 & kImm16Mask)); 289 uint64_t lo = (static_cast<uint32_t>((instr4 & kImm16Mask) << 16) | 290 static_cast<uint32_t>(instr5 & kImm16Mask)); 291 return static_cast<Address>((hi << 32) | lo); 292 #else 293 // Assemble the 32 bit value. 294 return static_cast<Address>(((instr1 & kImm16Mask) << 16) | 295 (instr2 & kImm16Mask)); 296 #endif 297 } 298 299 UNREACHABLE(); 300 } 301 302 303 #if V8_TARGET_ARCH_PPC64 304 const uint32_t kLoadIntptrOpcode = LD; 305 #else 306 const uint32_t kLoadIntptrOpcode = LWZ; 307 #endif 308 309 // Constant pool load sequence detection: 310 // 1) REGULAR access: 311 // load <dst>, kConstantPoolRegister + <offset> 312 // 313 // 2) OVERFLOWED access: 314 // addis <scratch>, kConstantPoolRegister, <offset_high> 315 // load <dst>, <scratch> + <offset_low> 316 bool Assembler::IsConstantPoolLoadStart(Address pc, 317 ConstantPoolEntry::Access* access) { 318 Instr instr = instr_at(pc); 319 uint32_t opcode = instr & kOpcodeMask; 320 if (GetRA(instr) != kConstantPoolRegister) return false; 321 bool overflowed = (opcode == ADDIS); 322 #ifdef DEBUG 323 if (overflowed) { 324 opcode = instr_at(pc + kInstrSize) & kOpcodeMask; 325 } 326 DCHECK(opcode == kLoadIntptrOpcode || opcode == LFD); 327 #endif 328 if (access) { 329 *access = (overflowed ? ConstantPoolEntry::OVERFLOWED 330 : ConstantPoolEntry::REGULAR); 331 } 332 return true; 333 } 334 335 336 bool Assembler::IsConstantPoolLoadEnd(Address pc, 337 ConstantPoolEntry::Access* access) { 338 Instr instr = instr_at(pc); 339 uint32_t opcode = instr & kOpcodeMask; 340 bool overflowed = false; 341 if (!(opcode == kLoadIntptrOpcode || opcode == LFD)) return false; 342 if (GetRA(instr) != kConstantPoolRegister) { 343 instr = instr_at(pc - kInstrSize); 344 opcode = instr & kOpcodeMask; 345 if ((opcode != ADDIS) || GetRA(instr) != kConstantPoolRegister) { 346 return false; 347 } 348 overflowed = true; 349 } 350 if (access) { 351 *access = (overflowed ? ConstantPoolEntry::OVERFLOWED 352 : ConstantPoolEntry::REGULAR); 353 } 354 return true; 355 } 356 357 358 int Assembler::GetConstantPoolOffset(Address pc, 359 ConstantPoolEntry::Access access, 360 ConstantPoolEntry::Type type) { 361 bool overflowed = (access == ConstantPoolEntry::OVERFLOWED); 362 #ifdef DEBUG 363 ConstantPoolEntry::Access access_check = 364 static_cast<ConstantPoolEntry::Access>(-1); 365 DCHECK(IsConstantPoolLoadStart(pc, &access_check)); 366 DCHECK(access_check == access); 367 #endif 368 int offset; 369 if (overflowed) { 370 offset = (instr_at(pc) & kImm16Mask) << 16; 371 offset += SIGN_EXT_IMM16(instr_at(pc + kInstrSize) & kImm16Mask); 372 DCHECK(!is_int16(offset)); 373 } else { 374 offset = SIGN_EXT_IMM16((instr_at(pc) & kImm16Mask)); 375 } 376 return offset; 377 } 378 379 380 void Assembler::PatchConstantPoolAccessInstruction( 381 int pc_offset, int offset, ConstantPoolEntry::Access access, 382 ConstantPoolEntry::Type type) { 383 Address pc = reinterpret_cast<Address>(buffer_) + pc_offset; 384 bool overflowed = (access == ConstantPoolEntry::OVERFLOWED); 385 CHECK(overflowed != is_int16(offset)); 386 #ifdef DEBUG 387 ConstantPoolEntry::Access access_check = 388 static_cast<ConstantPoolEntry::Access>(-1); 389 DCHECK(IsConstantPoolLoadStart(pc, &access_check)); 390 DCHECK(access_check == access); 391 #endif 392 if (overflowed) { 393 int hi_word = static_cast<int>(offset >> 16); 394 int lo_word = static_cast<int>(offset & 0xffff); 395 if (lo_word & 0x8000) hi_word++; 396 397 Instr instr1 = instr_at(pc); 398 Instr instr2 = instr_at(pc + kInstrSize); 399 instr1 &= ~kImm16Mask; 400 instr1 |= (hi_word & kImm16Mask); 401 instr2 &= ~kImm16Mask; 402 instr2 |= (lo_word & kImm16Mask); 403 instr_at_put(pc, instr1); 404 instr_at_put(pc + kInstrSize, instr2); 405 } else { 406 Instr instr = instr_at(pc); 407 instr &= ~kImm16Mask; 408 instr |= (offset & kImm16Mask); 409 instr_at_put(pc, instr); 410 } 411 } 412 413 414 Address Assembler::target_constant_pool_address_at( 415 Address pc, Address constant_pool, ConstantPoolEntry::Access access, 416 ConstantPoolEntry::Type type) { 417 Address addr = constant_pool; 418 DCHECK(addr); 419 addr += GetConstantPoolOffset(pc, access, type); 420 return addr; 421 } 422 423 424 // This sets the branch destination (which gets loaded at the call address). 425 // This is for calls and branches within generated code. The serializer 426 // has already deserialized the mov instructions etc. 427 // There is a FIXED_SEQUENCE assumption here 428 void Assembler::deserialization_set_special_target_at( 429 Address instruction_payload, Code* code, Address target) { 430 set_target_address_at(instruction_payload, 431 code ? code->constant_pool() : kNullAddress, target); 432 } 433 434 int Assembler::deserialization_special_target_size( 435 Address instruction_payload) { 436 return kSpecialTargetSize; 437 } 438 439 void Assembler::deserialization_set_target_internal_reference_at( 440 Address pc, Address target, RelocInfo::Mode mode) { 441 if (RelocInfo::IsInternalReferenceEncoded(mode)) { 442 set_target_address_at(pc, kNullAddress, target, SKIP_ICACHE_FLUSH); 443 } else { 444 Memory<Address>(pc) = target; 445 } 446 } 447 448 449 // This code assumes the FIXED_SEQUENCE of lis/ori 450 void Assembler::set_target_address_at(Address pc, Address constant_pool, 451 Address target, 452 ICacheFlushMode icache_flush_mode) { 453 if (FLAG_enable_embedded_constant_pool && constant_pool) { 454 ConstantPoolEntry::Access access; 455 if (IsConstantPoolLoadStart(pc, &access)) { 456 Memory<Address>(target_constant_pool_address_at( 457 pc, constant_pool, access, ConstantPoolEntry::INTPTR)) = target; 458 return; 459 } 460 } 461 462 Instr instr1 = instr_at(pc); 463 Instr instr2 = instr_at(pc + kInstrSize); 464 // Interpret 2 instructions generated by lis/ori 465 if (IsLis(instr1) && IsOri(instr2)) { 466 #if V8_TARGET_ARCH_PPC64 467 Instr instr4 = instr_at(pc + (3 * kInstrSize)); 468 Instr instr5 = instr_at(pc + (4 * kInstrSize)); 469 // Needs to be fixed up when mov changes to handle 64-bit values. 470 uint32_t* p = reinterpret_cast<uint32_t*>(pc); 471 uintptr_t itarget = static_cast<uintptr_t>(target); 472 473 instr5 &= ~kImm16Mask; 474 instr5 |= itarget & kImm16Mask; 475 itarget = itarget >> 16; 476 477 instr4 &= ~kImm16Mask; 478 instr4 |= itarget & kImm16Mask; 479 itarget = itarget >> 16; 480 481 instr2 &= ~kImm16Mask; 482 instr2 |= itarget & kImm16Mask; 483 itarget = itarget >> 16; 484 485 instr1 &= ~kImm16Mask; 486 instr1 |= itarget & kImm16Mask; 487 itarget = itarget >> 16; 488 489 *p = instr1; 490 *(p + 1) = instr2; 491 *(p + 3) = instr4; 492 *(p + 4) = instr5; 493 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 494 Assembler::FlushICache(p, 5 * kInstrSize); 495 } 496 #else 497 uint32_t* p = reinterpret_cast<uint32_t*>(pc); 498 uint32_t itarget = static_cast<uint32_t>(target); 499 int lo_word = itarget & kImm16Mask; 500 int hi_word = itarget >> 16; 501 instr1 &= ~kImm16Mask; 502 instr1 |= hi_word; 503 instr2 &= ~kImm16Mask; 504 instr2 |= lo_word; 505 506 *p = instr1; 507 *(p + 1) = instr2; 508 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 509 Assembler::FlushICache(p, 2 * kInstrSize); 510 } 511 #endif 512 return; 513 } 514 UNREACHABLE(); 515 } 516 } // namespace internal 517 } // namespace v8 518 519 #endif // V8_PPC_ASSEMBLER_PPC_INL_H_ 520