| // Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | #include "vm/globals.h"  // Needed here to get TARGET_ARCH_RISCV*. | 
 | #if defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64) | 
 |  | 
 | #include "vm/instructions.h" | 
 | #include "vm/instructions_riscv.h" | 
 |  | 
 | #include "vm/constants.h" | 
 | #include "vm/cpu.h" | 
 | #include "vm/object.h" | 
 | #include "vm/object_store.h" | 
 | #include "vm/reverse_pc_lookup_cache.h" | 
 |  | 
 | namespace dart { | 
 |  | 
 | CallPattern::CallPattern(uword pc, const Code& code) | 
 |     : object_pool_(ObjectPool::Handle(code.GetObjectPool())), | 
 |       target_code_pool_index_(-1) { | 
 |   ASSERT(code.ContainsInstructionAt(pc)); | 
 |   //          [lui,add,]lx CODE_REG, ##(pp) | 
 |   // xxxxxxxx lx ra, ##(CODE_REG) | 
 |   //     xxxx jalr ra | 
 |  | 
 |   // Last instruction: jalr ra. | 
 |   ASSERT(*reinterpret_cast<uint16_t*>(pc - 2) == 0x9082); | 
 |   Register reg; | 
 |   InstructionPattern::DecodeLoadWordFromPool(pc - 6, ®, | 
 |                                              &target_code_pool_index_); | 
 |   ASSERT(reg == CODE_REG); | 
 | } | 
 |  | 
 | ICCallPattern::ICCallPattern(uword pc, const Code& code) | 
 |     : object_pool_(ObjectPool::Handle(code.GetObjectPool())), | 
 |       target_pool_index_(-1), | 
 |       data_pool_index_(-1) { | 
 |   ASSERT(code.ContainsInstructionAt(pc)); | 
 |   //          [lui,add,]lx IC_DATA_REG, ##(pp) | 
 |   //          [lui,add,]lx CODE_REG, ##(pp) | 
 |   // xxxxxxxx lx ra, ##(CODE_REG) | 
 |   //     xxxx jalr ra | 
 |  | 
 |   // Last instruction: jalr ra. | 
 |   ASSERT(*reinterpret_cast<uint16_t*>(pc - 2) == 0x9082); | 
 |  | 
 |   Register reg; | 
 |   uword data_load_end = InstructionPattern::DecodeLoadWordFromPool( | 
 |       pc - 6, ®, &target_pool_index_); | 
 |   ASSERT(reg == CODE_REG); | 
 |  | 
 |   InstructionPattern::DecodeLoadWordFromPool(data_load_end, ®, | 
 |                                              &data_pool_index_); | 
 |   ASSERT(reg == IC_DATA_REG); | 
 | } | 
 |  | 
 | NativeCallPattern::NativeCallPattern(uword pc, const Code& code) | 
 |     : object_pool_(ObjectPool::Handle(code.GetObjectPool())), | 
 |       end_(pc), | 
 |       native_function_pool_index_(-1), | 
 |       target_code_pool_index_(-1) { | 
 |   ASSERT(code.ContainsInstructionAt(pc)); | 
 |   //          [lui,add,]lx t5, ##(pp) | 
 |   //          [lui,add,]lx CODE_REG, ##(pp) | 
 |   // xxxxxxxx lx ra, ##(CODE_REG) | 
 |   //     xxxx jalr ra | 
 |  | 
 |   // Last instruction: jalr ra. | 
 |   ASSERT(*reinterpret_cast<uint16_t*>(pc - 2) == 0x9082); | 
 |  | 
 |   Register reg; | 
 |   uword native_function_load_end = InstructionPattern::DecodeLoadWordFromPool( | 
 |       pc - 6, ®, &target_code_pool_index_); | 
 |   ASSERT(reg == CODE_REG); | 
 |   InstructionPattern::DecodeLoadWordFromPool(native_function_load_end, ®, | 
 |                                              &native_function_pool_index_); | 
 |   ASSERT(reg == T5); | 
 | } | 
 |  | 
 | CodePtr NativeCallPattern::target() const { | 
 |   return static_cast<CodePtr>(object_pool_.ObjectAt(target_code_pool_index_)); | 
 | } | 
 |  | 
 | void NativeCallPattern::set_target(const Code& target) const { | 
 |   object_pool_.SetObjectAt(target_code_pool_index_, target); | 
 |   // No need to flush the instruction cache, since the code is not modified. | 
 | } | 
 |  | 
 | NativeFunction NativeCallPattern::native_function() const { | 
 |   return reinterpret_cast<NativeFunction>( | 
 |       object_pool_.RawValueAt(native_function_pool_index_)); | 
 | } | 
 |  | 
 | void NativeCallPattern::set_native_function(NativeFunction func) const { | 
 |   object_pool_.SetRawValueAt(native_function_pool_index_, | 
 |                              reinterpret_cast<uword>(func)); | 
 | } | 
 |  | 
 | // Decodes a load sequence ending at 'end' (the last instruction of the load | 
 | // sequence is the instruction before the one at end).  Returns a pointer to | 
 | // the first instruction in the sequence.  Returns the register being loaded | 
 | // and the loaded immediate value in the output parameters 'reg' and 'value' | 
 | // respectively. | 
 | uword InstructionPattern::DecodeLoadWordImmediate(uword end, | 
 |                                                   Register* reg, | 
 |                                                   intptr_t* value) { | 
 |   UNIMPLEMENTED(); | 
 |   return 0; | 
 | } | 
 |  | 
 | static bool DecodeLoadX(uword end, | 
 |                         Register* dst, | 
 |                         Register* base, | 
 |                         intptr_t* offset, | 
 |                         intptr_t* length) { | 
 |   Instr instr(*reinterpret_cast<uint32_t*>(end - 4)); | 
 | #if XLEN == 32 | 
 |   if (instr.opcode() == LOAD && instr.funct3() == LW) { | 
 | #elif XLEN == 64 | 
 |   if (instr.opcode() == LOAD && instr.funct3() == LD) { | 
 | #endif | 
 |     *dst = instr.rd(); | 
 |     *base = instr.rs1(); | 
 |     *offset = instr.itype_imm(); | 
 |     *length = 4; | 
 |     return true; | 
 |   } | 
 |  | 
 |   CInstr cinstr(*reinterpret_cast<uint16_t*>(end - 2)); | 
 | #if XLEN == 32 | 
 |   if (cinstr.opcode() == C_LW) { | 
 | #elif XLEN == 64 | 
 |   if (cinstr.opcode() == C_LD) { | 
 | #endif | 
 |     *dst = cinstr.rdp(); | 
 |     *base = cinstr.rs1p(); | 
 | #if XLEN == 32 | 
 |     *offset = cinstr.mem4_imm(); | 
 | #elif XLEN == 64 | 
 |     *offset = cinstr.mem8_imm(); | 
 | #endif | 
 |     *length = 2; | 
 |     return true; | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | static bool DecodeLUI(uword end, | 
 |                       Register* dst, | 
 |                       intptr_t* imm, | 
 |                       intptr_t* length) { | 
 |   Instr instr(*reinterpret_cast<uint32_t*>(end - 4)); | 
 |   if (instr.opcode() == LUI) { | 
 |     *dst = instr.rd(); | 
 |     *imm = instr.utype_imm(); | 
 |     *length = 4; | 
 |     return true; | 
 |   } | 
 |  | 
 |   CInstr cinstr(*reinterpret_cast<uint16_t*>(end - 2)); | 
 |   if (cinstr.opcode() == C_LUI) { | 
 |     *dst = cinstr.rd(); | 
 |     *imm = cinstr.u_imm(); | 
 |     *length = 2; | 
 |     return true; | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | // See comment in instructions_arm64.h | 
 | uword InstructionPattern::DecodeLoadWordFromPool(uword end, | 
 |                                                  Register* reg, | 
 |                                                  intptr_t* index) { | 
 |   // [c.]lx dst, offset(pp) | 
 |   // or | 
 |   // [c.]lui dst, hi | 
 |   // c.add dst, dst, pp | 
 |   // [c.]lx dst, lo(dst) | 
 |  | 
 |   Register base; | 
 |   intptr_t lo, length; | 
 |   if (!DecodeLoadX(end, reg, &base, &lo, &length)) { | 
 |     UNREACHABLE(); | 
 |   } | 
 |  | 
 |   if (base == PP) { | 
 |     // PP is untagged on RISCV. | 
 |     *index = ObjectPool::IndexFromOffset(lo - kHeapObjectTag); | 
 |     return end - length; | 
 |   } | 
 |   ASSERT(base == *reg); | 
 |   end -= length; | 
 |  | 
 |   CInstr add_instr(*reinterpret_cast<uint16_t*>(end - 2)); | 
 |   ASSERT(add_instr.opcode() == | 
 |          C_MV);  // Not C_ADD, which extends past the opcode proper. | 
 |   ASSERT(add_instr.rd() == base); | 
 |   ASSERT(add_instr.rs1() == base); | 
 |   ASSERT(add_instr.rs2() == PP); | 
 |   end -= 2; | 
 |  | 
 |   Register dst; | 
 |   intptr_t hi; | 
 |   if (!DecodeLUI(end, &dst, &hi, &length)) { | 
 |     UNREACHABLE(); | 
 |   } | 
 |   ASSERT(dst == base); | 
 |   // PP is untagged on RISC-V. | 
 |   *index = ObjectPool::IndexFromOffset(hi + lo - kHeapObjectTag); | 
 |   return end - length; | 
 | } | 
 |  | 
 | bool DecodeLoadObjectFromPoolOrThread(uword pc, const Code& code, Object* obj) { | 
 |   ASSERT(code.ContainsInstructionAt(pc)); | 
 |   uint16_t parcel = *reinterpret_cast<uint16_t*>(pc); | 
 |   if (IsCInstruction(parcel)) { | 
 |     CInstr instr(parcel); | 
 | #if XLEN == 32 | 
 |     if (instr.opcode() == C_LW) { | 
 |       intptr_t offset = instr.mem4_imm(); | 
 | #elif XLEN == 64 | 
 |     if (instr.opcode() == C_LD) { | 
 |       intptr_t offset = instr.mem8_imm(); | 
 | #endif | 
 |       if (instr.rs1p() == PP) { | 
 |         // PP is untagged on RISC-V. | 
 |         if (!Utils::IsAligned(offset, kWordSize)) { | 
 |           return false;  // Being used as argument register A5. | 
 |         } | 
 |         intptr_t index = ObjectPool::IndexFromOffset(offset - kHeapObjectTag); | 
 |         return ObjectAtPoolIndex(code, index, obj); | 
 |       } else if (instr.rs1p() == THR) { | 
 |         return Thread::ObjectAtOffset(offset, obj); | 
 |       } | 
 |     } | 
 |   } else { | 
 |     Instr instr(*reinterpret_cast<uint32_t*>(pc)); | 
 | #if XLEN == 32 | 
 |     if (instr.opcode() == LOAD && instr.funct3() == LW) { | 
 | #elif XLEN == 64 | 
 |     if (instr.opcode() == LOAD && instr.funct3() == LD) { | 
 | #endif | 
 |       intptr_t offset = instr.itype_imm(); | 
 |       if (instr.rs1() == PP) { | 
 |         // PP is untagged on RISC-V. | 
 |         if (!Utils::IsAligned(offset, kWordSize)) { | 
 |           return false;  // Being used as argument register A5. | 
 |         } | 
 |         intptr_t index = ObjectPool::IndexFromOffset(offset - kHeapObjectTag); | 
 |         return ObjectAtPoolIndex(code, index, obj); | 
 |       } else if (instr.rs1() == THR) { | 
 |         return Thread::ObjectAtOffset(offset, obj); | 
 |       } | 
 |     } | 
 |     if ((instr.opcode() == OPIMM) && (instr.funct3() == ADDI) && | 
 |         (instr.rs1() == NULL_REG)) { | 
 |       if (instr.itype_imm() == 0) { | 
 |         *obj = Object::null(); | 
 |         return true; | 
 |       } | 
 |       if (instr.itype_imm() == kTrueOffsetFromNull) { | 
 |         *obj = Object::bool_true().ptr(); | 
 |         return true; | 
 |       } | 
 |       if (instr.itype_imm() == kFalseOffsetFromNull) { | 
 |         *obj = Object::bool_false().ptr(); | 
 |         return true; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // TODO(riscv): Loads with offsets beyond 12 bits. | 
 |   return false; | 
 | } | 
 |  | 
 | // Encodes a load sequence ending at 'end'. Encodes a fixed length two | 
 | // instruction load from the pool pointer in PP using the destination | 
 | // register reg as a temporary for the base address. | 
 | // Assumes that the location has already been validated for patching. | 
 | void InstructionPattern::EncodeLoadWordFromPoolFixed(uword end, | 
 |                                                      int32_t offset) { | 
 |   UNIMPLEMENTED(); | 
 | } | 
 |  | 
 | CodePtr CallPattern::TargetCode() const { | 
 |   return static_cast<CodePtr>(object_pool_.ObjectAt(target_code_pool_index_)); | 
 | } | 
 |  | 
 | void CallPattern::SetTargetCode(const Code& target) const { | 
 |   object_pool_.SetObjectAt(target_code_pool_index_, target); | 
 |   // No need to flush the instruction cache, since the code is not modified. | 
 | } | 
 |  | 
 | ObjectPtr ICCallPattern::Data() const { | 
 |   return object_pool_.ObjectAt(data_pool_index_); | 
 | } | 
 |  | 
 | void ICCallPattern::SetData(const Object& data) const { | 
 |   ASSERT(data.IsArray() || data.IsICData() || data.IsMegamorphicCache()); | 
 |   object_pool_.SetObjectAt(data_pool_index_, data); | 
 | } | 
 |  | 
 | CodePtr ICCallPattern::TargetCode() const { | 
 |   return static_cast<CodePtr>(object_pool_.ObjectAt(target_pool_index_)); | 
 | } | 
 |  | 
 | void ICCallPattern::SetTargetCode(const Code& target) const { | 
 |   object_pool_.SetObjectAt(target_pool_index_, target); | 
 |   // No need to flush the instruction cache, since the code is not modified. | 
 | } | 
 |  | 
 | SwitchableCallPatternBase::SwitchableCallPatternBase( | 
 |     const ObjectPool& object_pool) | 
 |     : object_pool_(object_pool), data_pool_index_(-1), target_pool_index_(-1) {} | 
 |  | 
 | ObjectPtr SwitchableCallPatternBase::data() const { | 
 |   return object_pool_.ObjectAt(data_pool_index_); | 
 | } | 
 |  | 
 | void SwitchableCallPatternBase::SetData(const Object& data) const { | 
 |   ASSERT(!Object::Handle(object_pool_.ObjectAt(data_pool_index_)).IsCode()); | 
 |   object_pool_.SetObjectAt(data_pool_index_, data); | 
 | } | 
 |  | 
 | SwitchableCallPattern::SwitchableCallPattern(uword pc, const Code& code) | 
 |     : SwitchableCallPatternBase(ObjectPool::Handle(code.GetObjectPool())) { | 
 |   ASSERT(code.ContainsInstructionAt(pc)); | 
 |   UNIMPLEMENTED(); | 
 | } | 
 |  | 
 | uword SwitchableCallPattern::target_entry() const { | 
 |   return Code::Handle(Code::RawCast(object_pool_.ObjectAt(target_pool_index_))) | 
 |       .MonomorphicEntryPoint(); | 
 | } | 
 |  | 
 | void SwitchableCallPattern::SetTarget(const Code& target) const { | 
 |   ASSERT(Object::Handle(object_pool_.ObjectAt(target_pool_index_)).IsCode()); | 
 |   object_pool_.SetObjectAt(target_pool_index_, target); | 
 | } | 
 |  | 
 | BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc) | 
 |     : SwitchableCallPatternBase(ObjectPool::Handle( | 
 |           IsolateGroup::Current()->object_store()->global_object_pool())) { | 
 |   //      [lui,add,]lx RA, ##(pp) | 
 |   //      [lui,add,]lx IC_DATA_REG, ##(pp) | 
 |   // xxxx jalr RA | 
 |  | 
 |   // Last instruction: jalr ra. | 
 |   ASSERT(*reinterpret_cast<uint16_t*>(pc - 2) == 0x9082); | 
 |  | 
 |   Register reg; | 
 |   uword target_load_end = InstructionPattern::DecodeLoadWordFromPool( | 
 |       pc - 2, ®, &data_pool_index_); | 
 |   ASSERT_EQUAL(reg, IC_DATA_REG); | 
 |  | 
 |   InstructionPattern::DecodeLoadWordFromPool(target_load_end, ®, | 
 |                                              &target_pool_index_); | 
 |   ASSERT_EQUAL(reg, RA); | 
 | } | 
 |  | 
 | uword BareSwitchableCallPattern::target_entry() const { | 
 |   return object_pool_.RawValueAt(target_pool_index_); | 
 | } | 
 |  | 
 | void BareSwitchableCallPattern::SetTarget(const Code& target) const { | 
 |   ASSERT(object_pool_.TypeAt(target_pool_index_) == | 
 |          ObjectPool::EntryType::kImmediate); | 
 |   object_pool_.SetRawValueAt(target_pool_index_, | 
 |                              target.MonomorphicEntryPoint()); | 
 | } | 
 |  | 
 | ReturnPattern::ReturnPattern(uword pc) : pc_(pc) {} | 
 |  | 
 | bool ReturnPattern::IsValid() const { | 
 |   return *reinterpret_cast<uint16_t*>(pc_) == 0x8082; | 
 | } | 
 |  | 
 | bool PcRelativeCallPattern::IsValid() const { | 
 |   Instr aupic(*reinterpret_cast<uint32_t*>(pc_)); | 
 |   if (aupic.opcode() != AUIPC) return false; | 
 |   Instr jalr(*reinterpret_cast<uint32_t*>(pc_ + 4)); | 
 |   if (jalr.opcode() != JALR) return false; | 
 |   if (aupic.rd() != jalr.rs1()) return false; | 
 |   if (jalr.rd() != RA) return false; | 
 |   return true; | 
 | } | 
 |  | 
 | bool PcRelativeTailCallPattern::IsValid() const { | 
 |   Instr aupic(*reinterpret_cast<uint32_t*>(pc_)); | 
 |   if (aupic.opcode() != AUIPC) return false; | 
 |   Instr jr(*reinterpret_cast<uint32_t*>(pc_ + 4)); | 
 |   if (jr.opcode() != JALR) return false; | 
 |   if (aupic.rd() != jr.rs1()) return false; | 
 |   if (jr.rd() != ZR) return false; | 
 |   return true; | 
 | } | 
 |  | 
 | void PcRelativeTrampolineJumpPattern::Initialize() { | 
 |   UNREACHABLE(); | 
 | } | 
 |  | 
 | int32_t PcRelativeTrampolineJumpPattern::distance() { | 
 |   UNREACHABLE(); | 
 |   return 0; | 
 | } | 
 |  | 
 | void PcRelativeTrampolineJumpPattern::set_distance(int32_t distance) { | 
 |   UNREACHABLE(); | 
 | } | 
 |  | 
 | bool PcRelativeTrampolineJumpPattern::IsValid() const { | 
 |   UNREACHABLE(); | 
 |   return false; | 
 | } | 
 |  | 
 | intptr_t TypeTestingStubCallPattern::GetSubtypeTestCachePoolIndex() { | 
 |   // Calls to the type testing stubs look like: | 
 |   //   lx s4, ... | 
 |   //   lx Rn, idx(pp) | 
 |   //   jalr s4 | 
 |   // where Rn = TypeTestABI::kSubtypeTestCacheReg. | 
 |  | 
 |   // Ensure the caller of the type testing stub (whose return address is [pc_]) | 
 |   // branched via `blr R9` or a pc-relative call. | 
 |   if (*reinterpret_cast<uint16_t*>(pc_ - 2) == 0x9a02) {  // jalr s4 | 
 |     // indirect call | 
 |     //     xxxx c.jalr s4 | 
 |     Register reg; | 
 |     intptr_t pool_index = -1; | 
 |     InstructionPattern::DecodeLoadWordFromPool(pc_ - 2, ®, &pool_index); | 
 |     ASSERT_EQUAL(reg, TypeTestABI::kSubtypeTestCacheReg); | 
 |     return pool_index; | 
 |   } else { | 
 |     ASSERT(FLAG_precompiled_mode); | 
 |     // pc-relative call | 
 |     // xxxxxxxx aupic ra, hi | 
 |     // xxxxxxxx jalr ra, lo | 
 |     Instr jalr(*reinterpret_cast<uint32_t*>(pc_ - 4)); | 
 |     ASSERT(jalr.opcode() == JALR); | 
 |     Instr auipc(*reinterpret_cast<uint32_t*>(pc_ - 8)); | 
 |     ASSERT(auipc.opcode() == AUIPC); | 
 |  | 
 |     Register reg; | 
 |     intptr_t pool_index = -1; | 
 |     InstructionPattern::DecodeLoadWordFromPool(pc_ - 8, ®, &pool_index); | 
 |     ASSERT_EQUAL(reg, TypeTestABI::kSubtypeTestCacheReg); | 
 |     return pool_index; | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace dart | 
 |  | 
 | #endif  // defined TARGET_ARCH_RISCV |