1// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/globals.h" // Needed here to get TARGET_ARCH_RISCV*.
6#if defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64)
7
8#include "vm/instructions.h"
9#include "vm/instructions_riscv.h"
10
11#include "vm/constants.h"
12#include "vm/cpu.h"
13#include "vm/object.h"
14#include "vm/object_store.h"
15#include "vm/reverse_pc_lookup_cache.h"
16
17namespace dart {
18
19CallPattern::CallPattern(uword pc, const Code& code)
20 : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
21 target_code_pool_index_(-1) {
22 ASSERT(code.ContainsInstructionAt(pc));
23 // [lui,add,]lx CODE_REG, ##(pp)
24 // xxxxxxxx lx ra, ##(CODE_REG)
25 // xxxx jalr ra
26
27 // Last instruction: jalr ra.
28 ASSERT(*reinterpret_cast<uint16_t*>(pc - 2) == 0x9082);
29 Register reg;
30 InstructionPattern::DecodeLoadWordFromPool(pc - 6, &reg,
31 &target_code_pool_index_);
32 ASSERT(reg == CODE_REG);
33}
34
35ICCallPattern::ICCallPattern(uword pc, const Code& code)
36 : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
37 target_pool_index_(-1),
38 data_pool_index_(-1) {
39 ASSERT(code.ContainsInstructionAt(pc));
40 // [lui,add,]lx IC_DATA_REG, ##(pp)
41 // [lui,add,]lx CODE_REG, ##(pp)
42 // xxxxxxxx lx ra, ##(CODE_REG)
43 // xxxx jalr ra
44
45 // Last instruction: jalr ra.
46 ASSERT(*reinterpret_cast<uint16_t*>(pc - 2) == 0x9082);
47
48 Register reg;
49 uword data_load_end = InstructionPattern::DecodeLoadWordFromPool(
50 pc - 6, &reg, &target_pool_index_);
51 ASSERT(reg == CODE_REG);
52
53 InstructionPattern::DecodeLoadWordFromPool(data_load_end, &reg,
54 &data_pool_index_);
55 ASSERT(reg == IC_DATA_REG);
56}
57
58NativeCallPattern::NativeCallPattern(uword pc, const Code& code)
59 : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
60 end_(pc),
61 native_function_pool_index_(-1),
62 target_code_pool_index_(-1) {
63 ASSERT(code.ContainsInstructionAt(pc));
64 // [lui,add,]lx t5, ##(pp)
65 // [lui,add,]lx CODE_REG, ##(pp)
66 // xxxxxxxx lx ra, ##(CODE_REG)
67 // xxxx jalr ra
68
69 // Last instruction: jalr ra.
70 ASSERT(*reinterpret_cast<uint16_t*>(pc - 2) == 0x9082);
71
72 Register reg;
73 uword native_function_load_end = InstructionPattern::DecodeLoadWordFromPool(
74 pc - 6, &reg, &target_code_pool_index_);
75 ASSERT(reg == CODE_REG);
76 InstructionPattern::DecodeLoadWordFromPool(native_function_load_end, &reg,
77 &native_function_pool_index_);
78 ASSERT(reg == T5);
79}
80
81CodePtr NativeCallPattern::target() const {
82 return static_cast<CodePtr>(object_pool_.ObjectAt(target_code_pool_index_));
83}
84
85void NativeCallPattern::set_target(const Code& target) const {
86 object_pool_.SetObjectAt(target_code_pool_index_, target);
87 // No need to flush the instruction cache, since the code is not modified.
88}
89
90NativeFunction NativeCallPattern::native_function() const {
91 return reinterpret_cast<NativeFunction>(
92 object_pool_.RawValueAt(native_function_pool_index_));
93}
94
95void NativeCallPattern::set_native_function(NativeFunction func) const {
96 object_pool_.SetRawValueAt(native_function_pool_index_,
97 reinterpret_cast<uword>(func));
98}
99
100// Decodes a load sequence ending at 'end' (the last instruction of the load
101// sequence is the instruction before the one at end). Returns a pointer to
102// the first instruction in the sequence. Returns the register being loaded
103// and the loaded immediate value in the output parameters 'reg' and 'value'
104// respectively.
105uword InstructionPattern::DecodeLoadWordImmediate(uword end,
106 Register* reg,
107 intptr_t* value) {
108 UNIMPLEMENTED();
109 return 0;
110}
111
112static bool DecodeLoadX(uword end,
113 Register* dst,
114 Register* base,
115 intptr_t* offset,
116 intptr_t* length) {
117 Instr instr(LoadUnaligned(reinterpret_cast<uint32_t*>(end - 4)));
118#if XLEN == 32
119 if (instr.opcode() == LOAD && instr.funct3() == LW) {
120#elif XLEN == 64
121 if (instr.opcode() == LOAD && instr.funct3() == LD) {
122#endif
123 *dst = instr.rd();
124 *base = instr.rs1();
125 *offset = instr.itype_imm();
126 *length = 4;
127 return true;
128 }
129
130 CInstr cinstr(*reinterpret_cast<uint16_t*>(end - 2));
131#if XLEN == 32
132 if (cinstr.opcode() == C_LW) {
133#elif XLEN == 64
134 if (cinstr.opcode() == C_LD) {
135#endif
136 *dst = cinstr.rdp();
137 *base = cinstr.rs1p();
138#if XLEN == 32
139 *offset = cinstr.mem4_imm();
140#elif XLEN == 64
141 *offset = cinstr.mem8_imm();
142#endif
143 *length = 2;
144 return true;
145 }
146
147 return false;
148}
149
150static bool DecodeLUI(uword end,
151 Register* dst,
152 intptr_t* imm,
153 intptr_t* length) {
154 Instr instr(LoadUnaligned(reinterpret_cast<uint32_t*>(end - 4)));
155 if (instr.opcode() == LUI) {
156 *dst = instr.rd();
157 *imm = instr.utype_imm();
158 *length = 4;
159 return true;
160 }
161
162 CInstr cinstr(*reinterpret_cast<uint16_t*>(end - 2));
163 if (cinstr.opcode() == C_LUI) {
164 *dst = cinstr.rd();
165 *imm = cinstr.u_imm();
166 *length = 2;
167 return true;
168 }
169
170 return false;
171}
172
173// See comment in instructions_arm64.h
174uword InstructionPattern::DecodeLoadWordFromPool(uword end,
175 Register* reg,
176 intptr_t* index) {
177 // [c.]lx dst, offset(pp)
178 // or
179 // [c.]lui dst, hi
180 // c.add dst, dst, pp
181 // [c.]lx dst, lo(dst)
182
183 Register base;
184 intptr_t lo, length;
185 if (!DecodeLoadX(end, reg, &base, &lo, &length)) {
186 UNREACHABLE();
187 }
188
189 if (base == PP) {
190 // PP is untagged on RISCV.
191 *index = ObjectPool::IndexFromOffset(lo - kHeapObjectTag);
192 return end - length;
193 }
194 ASSERT(base == *reg);
195 end -= length;
196
197 CInstr add_instr(*reinterpret_cast<uint16_t*>(end - 2));
198 ASSERT(add_instr.opcode() ==
199 C_MV); // Not C_ADD, which extends past the opcode proper.
200 ASSERT(add_instr.rd() == base);
201 ASSERT(add_instr.rs1() == base);
202 ASSERT(add_instr.rs2() == PP);
203 end -= 2;
204
205 Register dst;
206 intptr_t hi;
207 if (!DecodeLUI(end, &dst, &hi, &length)) {
208 UNREACHABLE();
209 }
210 ASSERT(dst == base);
211 // PP is untagged on RISC-V.
212 *index = ObjectPool::IndexFromOffset(hi + lo - kHeapObjectTag);
213 return end - length;
214}
215
216bool DecodeLoadObjectFromPoolOrThread(uword pc, const Code& code, Object* obj) {
217 ASSERT(code.ContainsInstructionAt(pc));
218 uint16_t parcel = *reinterpret_cast<uint16_t*>(pc);
219 if (IsCInstruction(parcel)) {
220 CInstr instr(parcel);
221#if XLEN == 32
222 if (instr.opcode() == C_LW) {
223 intptr_t offset = instr.mem4_imm();
224#elif XLEN == 64
225 if (instr.opcode() == C_LD) {
226 intptr_t offset = instr.mem8_imm();
227#endif
228 if (instr.rs1p() == PP) {
229 // PP is untagged on RISC-V.
230 if (!Utils::IsAligned(offset, kWordSize)) {
231 return false; // Being used as argument register A5.
232 }
233 intptr_t index = ObjectPool::IndexFromOffset(offset - kHeapObjectTag);
234 return ObjectAtPoolIndex(code, index, obj);
235 } else if (instr.rs1p() == THR) {
236 return Thread::ObjectAtOffset(offset, obj);
237 }
238 }
239 } else {
240 Instr instr(LoadUnaligned(reinterpret_cast<uint32_t*>(pc)));
241#if XLEN == 32
242 if (instr.opcode() == LOAD && instr.funct3() == LW) {
243#elif XLEN == 64
244 if (instr.opcode() == LOAD && instr.funct3() == LD) {
245#endif
246 intptr_t offset = instr.itype_imm();
247 if (instr.rs1() == PP) {
248 // PP is untagged on RISC-V.
249 if (!Utils::IsAligned(offset, kWordSize)) {
250 return false; // Being used as argument register A5.
251 }
252 intptr_t index = ObjectPool::IndexFromOffset(offset - kHeapObjectTag);
253 return ObjectAtPoolIndex(code, index, obj);
254 } else if (instr.rs1() == THR) {
255 return Thread::ObjectAtOffset(offset, obj);
256 }
257 }
258 if ((instr.opcode() == OPIMM) && (instr.funct3() == ADDI) &&
259 (instr.rs1() == NULL_REG)) {
260 if (instr.itype_imm() == 0) {
261 *obj = Object::null();
262 return true;
263 }
264 if (instr.itype_imm() == kTrueOffsetFromNull) {
265 *obj = Object::bool_true().ptr();
266 return true;
267 }
268 if (instr.itype_imm() == kFalseOffsetFromNull) {
269 *obj = Object::bool_false().ptr();
270 return true;
271 }
272 }
273 }
274
275 // TODO(riscv): Loads with offsets beyond 12 bits.
276 return false;
277}
278
279// Encodes a load sequence ending at 'end'. Encodes a fixed length two
280// instruction load from the pool pointer in PP using the destination
281// register reg as a temporary for the base address.
282// Assumes that the location has already been validated for patching.
283void InstructionPattern::EncodeLoadWordFromPoolFixed(uword end,
284 int32_t offset) {
285 UNIMPLEMENTED();
286}
287
288CodePtr CallPattern::TargetCode() const {
289 return static_cast<CodePtr>(object_pool_.ObjectAt(target_code_pool_index_));
290}
291
292void CallPattern::SetTargetCode(const Code& target) const {
293 object_pool_.SetObjectAt(target_code_pool_index_, target);
294 // No need to flush the instruction cache, since the code is not modified.
295}
296
297ObjectPtr ICCallPattern::Data() const {
298 return object_pool_.ObjectAt(data_pool_index_);
299}
300
301void ICCallPattern::SetData(const Object& data) const {
302 ASSERT(data.IsArray() || data.IsICData() || data.IsMegamorphicCache());
303 object_pool_.SetObjectAt(data_pool_index_, data);
304}
305
306CodePtr ICCallPattern::TargetCode() const {
307 return static_cast<CodePtr>(object_pool_.ObjectAt(target_pool_index_));
308}
309
310void ICCallPattern::SetTargetCode(const Code& target) const {
311 object_pool_.SetObjectAt(target_pool_index_, target);
312 // No need to flush the instruction cache, since the code is not modified.
313}
314
315SwitchableCallPatternBase::SwitchableCallPatternBase(
316 const ObjectPool& object_pool)
317 : object_pool_(object_pool), data_pool_index_(-1), target_pool_index_(-1) {}
318
319ObjectPtr SwitchableCallPatternBase::data() const {
320 return object_pool_.ObjectAt(data_pool_index_);
321}
322
323void SwitchableCallPatternBase::SetData(const Object& data) const {
324 ASSERT(!Object::Handle(object_pool_.ObjectAt(data_pool_index_)).IsCode());
325 object_pool_.SetObjectAt(data_pool_index_, data);
326}
327
328SwitchableCallPattern::SwitchableCallPattern(uword pc, const Code& code)
329 : SwitchableCallPatternBase(ObjectPool::Handle(code.GetObjectPool())) {
330 ASSERT(code.ContainsInstructionAt(pc));
331 UNIMPLEMENTED();
332}
333
334uword SwitchableCallPattern::target_entry() const {
335 return Code::Handle(Code::RawCast(object_pool_.ObjectAt(target_pool_index_)))
336 .MonomorphicEntryPoint();
337}
338
339void SwitchableCallPattern::SetTarget(const Code& target) const {
340 ASSERT(Object::Handle(object_pool_.ObjectAt(target_pool_index_)).IsCode());
341 object_pool_.SetObjectAt(target_pool_index_, target);
342}
343
344BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc)
345 : SwitchableCallPatternBase(ObjectPool::Handle(
346 IsolateGroup::Current()->object_store()->global_object_pool())) {
347 // [lui,add,]lx RA, ##(pp)
348 // [lui,add,]lx IC_DATA_REG, ##(pp)
349 // xxxx jalr RA
350
351 // Last instruction: jalr ra.
352 ASSERT(*reinterpret_cast<uint16_t*>(pc - 2) == 0x9082);
353
354 Register reg;
355 uword target_load_end = InstructionPattern::DecodeLoadWordFromPool(
356 pc - 2, &reg, &data_pool_index_);
357 ASSERT_EQUAL(reg, IC_DATA_REG);
358
359 InstructionPattern::DecodeLoadWordFromPool(target_load_end, &reg,
360 &target_pool_index_);
361 ASSERT_EQUAL(reg, RA);
362}
363
364uword BareSwitchableCallPattern::target_entry() const {
365 return object_pool_.RawValueAt(target_pool_index_);
366}
367
368void BareSwitchableCallPattern::SetTarget(const Code& target) const {
369 ASSERT(object_pool_.TypeAt(target_pool_index_) ==
370 ObjectPool::EntryType::kImmediate);
371 object_pool_.SetRawValueAt(target_pool_index_,
372 target.MonomorphicEntryPoint());
373}
374
375ReturnPattern::ReturnPattern(uword pc) : pc_(pc) {}
376
377bool ReturnPattern::IsValid() const {
378 return *reinterpret_cast<uint16_t*>(pc_) == 0x8082;
379}
380
381bool PcRelativeCallPattern::IsValid() const {
382 Instr aupic(LoadUnaligned(reinterpret_cast<uint32_t*>(pc_)));
383 if (aupic.opcode() != AUIPC) return false;
384 Instr jalr(LoadUnaligned(reinterpret_cast<uint32_t*>(pc_ + 4)));
385 if (jalr.opcode() != JALR) return false;
386 if (aupic.rd() != jalr.rs1()) return false;
387 if (jalr.rd() != RA) return false;
388 return true;
389}
390
391bool PcRelativeTailCallPattern::IsValid() const {
392 Instr aupic(LoadUnaligned(reinterpret_cast<uint32_t*>(pc_)));
393 if (aupic.opcode() != AUIPC) return false;
394 Instr jr(LoadUnaligned(reinterpret_cast<uint32_t*>(pc_ + 4)));
395 if (jr.opcode() != JALR) return false;
396 if (aupic.rd() != jr.rs1()) return false;
397 if (jr.rd() != ZR) return false;
398 return true;
399}
400
401void PcRelativeTrampolineJumpPattern::Initialize() {
402 StoreUnaligned(reinterpret_cast<uint32_t*>(pc_),
403 EncodeOpcode(AUIPC) | EncodeRd(TMP) | EncodeUTypeImm(0));
404 StoreUnaligned(reinterpret_cast<uint32_t*>(pc_ + 4),
405 EncodeOpcode(JALR) | EncodeFunct3(F3_0) | EncodeRd(ZR) |
406 EncodeRs1(TMP) | EncodeITypeImm(0));
407}
408
409intptr_t TypeTestingStubCallPattern::GetSubtypeTestCachePoolIndex() {
410 // Calls to the type testing stubs look like:
411 // lx s4, ...
412 // lx Rn, idx(pp)
413 // jalr s4
414 // where Rn = TypeTestABI::kSubtypeTestCacheReg.
415
416 // Ensure the caller of the type testing stub (whose return address is [pc_])
417 // branched via `jalr s3` or a pc-relative call.
418 if (*reinterpret_cast<uint16_t*>(pc_ - 2) == 0x9982) { // jalr s3
419 // indirect call
420 // xxxx c.jalr s3
421 Register reg;
422 intptr_t pool_index = -1;
423 InstructionPattern::DecodeLoadWordFromPool(pc_ - 2, &reg, &pool_index);
424 ASSERT_EQUAL(reg, TypeTestABI::kSubtypeTestCacheReg);
425 return pool_index;
426 } else {
427 ASSERT(FLAG_precompiled_mode);
428 // pc-relative call
429 // xxxxxxxx aupic ra, hi
430 // xxxxxxxx jalr ra, lo
431 Instr jalr(LoadUnaligned(reinterpret_cast<uint32_t*>(pc_ - 4)));
432 ASSERT(jalr.opcode() == JALR);
433 Instr auipc(LoadUnaligned(reinterpret_cast<uint32_t*>(pc_ - 8)));
434 ASSERT(auipc.opcode() == AUIPC);
435
436 Register reg;
437 intptr_t pool_index = -1;
438 InstructionPattern::DecodeLoadWordFromPool(pc_ - 8, &reg, &pool_index);
439 ASSERT_EQUAL(reg, TypeTestABI::kSubtypeTestCacheReg);
440 return pool_index;
441 }
442}
443
444} // namespace dart
445
446#endif // defined TARGET_ARCH_RISCV
447

source code of dart_sdk/runtime/vm/instructions_riscv.cc