Home | History | Annotate | Download | only in ppc
      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