Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/riscv64/inst/mod.rs
1693 views
1
//! This module defines riscv64-specific machine instruction types.
2
3
use super::lower::isle::generated_code::{VecAMode, VecElementWidth, VecOpMasking};
4
use crate::binemit::{Addend, CodeOffset, Reloc};
5
pub use crate::ir::condcodes::IntCC;
6
use crate::ir::types::{self, F16, F32, F64, F128, I8, I8X16, I16, I32, I64, I128};
7
8
pub use crate::ir::{ExternalName, MemFlags, Type};
9
use crate::isa::{CallConv, FunctionAlignment};
10
use crate::machinst::*;
11
use crate::{CodegenError, CodegenResult, settings};
12
13
pub use crate::ir::condcodes::FloatCC;
14
15
use alloc::vec::Vec;
16
use regalloc2::RegClass;
17
use smallvec::{SmallVec, smallvec};
18
use std::boxed::Box;
19
use std::fmt::Write;
20
use std::string::{String, ToString};
21
22
pub mod regs;
23
pub use self::regs::*;
24
pub mod imms;
25
pub use self::imms::*;
26
pub mod args;
27
pub use self::args::*;
28
pub mod emit;
29
pub use self::emit::*;
30
pub mod vector;
31
pub use self::vector::*;
32
pub mod encode;
33
pub use self::encode::*;
34
pub mod unwind;
35
36
use crate::isa::riscv64::abi::Riscv64MachineDeps;
37
38
#[cfg(test)]
39
mod emit_tests;
40
41
use std::fmt::{Display, Formatter};
42
43
pub(crate) type VecU8 = Vec<u8>;
44
45
//=============================================================================
46
// Instructions (top level): definition
47
48
pub use crate::isa::riscv64::lower::isle::generated_code::{
49
AluOPRRI, AluOPRRR, AtomicOP, CSR, CsrImmOP, CsrRegOP, FClassResult, FFlagsException, FRM,
50
FpuOPRR, FpuOPRRR, FpuOPRRRR, LoadOP, MInst as Inst, StoreOP,
51
};
52
use crate::isa::riscv64::lower::isle::generated_code::{CjOp, MInst, VecAluOpRRImm5, VecAluOpRRR};
53
54
/// Additional information for `return_call[_ind]` instructions, left out of
55
/// line to lower the size of the `Inst` enum.
56
#[derive(Clone, Debug)]
57
pub struct ReturnCallInfo<T> {
58
pub dest: T,
59
pub uses: CallArgList,
60
pub new_stack_arg_size: u32,
61
}
62
63
/// A conditional branch target.
64
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
65
pub enum CondBrTarget {
66
/// An unresolved reference to a Label, as passed into
67
/// `lower_branch_group()`.
68
Label(MachLabel),
69
/// No jump; fall through to the next instruction.
70
Fallthrough,
71
}
72
73
impl CondBrTarget {
74
/// Return the target's label, if it is a label-based target.
75
pub(crate) fn as_label(self) -> Option<MachLabel> {
76
match self {
77
CondBrTarget::Label(l) => Some(l),
78
_ => None,
79
}
80
}
81
82
pub(crate) fn is_fallthrouh(&self) -> bool {
83
self == &CondBrTarget::Fallthrough
84
}
85
}
86
87
impl Display for CondBrTarget {
88
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
89
match self {
90
CondBrTarget::Label(l) => write!(f, "{}", l.to_string()),
91
CondBrTarget::Fallthrough => write!(f, "0"),
92
}
93
}
94
}
95
96
pub(crate) fn enc_auipc(rd: Writable<Reg>, imm: Imm20) -> u32 {
97
let x = 0b0010111 | reg_to_gpr_num(rd.to_reg()) << 7 | imm.bits() << 12;
98
x
99
}
100
101
pub(crate) fn enc_jalr(rd: Writable<Reg>, base: Reg, offset: Imm12) -> u32 {
102
let x = 0b1100111
103
| reg_to_gpr_num(rd.to_reg()) << 7
104
| 0b000 << 12
105
| reg_to_gpr_num(base) << 15
106
| offset.bits() << 20;
107
x
108
}
109
110
/// rd and src must have the same length.
111
pub(crate) fn gen_moves(rd: &[Writable<Reg>], src: &[Reg]) -> SmallInstVec<Inst> {
112
assert!(rd.len() == src.len());
113
assert!(rd.len() > 0);
114
let mut insts = SmallInstVec::new();
115
for (dst, src) in rd.iter().zip(src.iter()) {
116
let ty = Inst::canonical_type_for_rc(dst.to_reg().class());
117
insts.push(Inst::gen_move(*dst, *src, ty));
118
}
119
insts
120
}
121
122
impl Inst {
123
/// RISC-V can have multiple instruction sizes. 2 bytes for compressed
124
/// instructions, 4 for regular instructions, 6 and 8 byte instructions
125
/// are also being considered.
126
const UNCOMPRESSED_INSTRUCTION_SIZE: i32 = 4;
127
128
#[inline]
129
pub(crate) fn load_imm12(rd: Writable<Reg>, imm: Imm12) -> Inst {
130
Inst::AluRRImm12 {
131
alu_op: AluOPRRI::Addi,
132
rd,
133
rs: zero_reg(),
134
imm12: imm,
135
}
136
}
137
138
/// Immediates can be loaded using lui and addi instructions.
139
fn load_const_imm(rd: Writable<Reg>, value: u64) -> Option<SmallInstVec<Inst>> {
140
Inst::generate_imm(value).map(|(imm20, imm12)| {
141
let mut insts = SmallVec::new();
142
143
let imm20_is_zero = imm20.as_i32() == 0;
144
let imm12_is_zero = imm12.as_i16() == 0;
145
146
let rs = if !imm20_is_zero {
147
insts.push(Inst::Lui { rd, imm: imm20 });
148
rd.to_reg()
149
} else {
150
zero_reg()
151
};
152
153
// We also need to emit the addi if the value is 0, otherwise we just
154
// won't produce any instructions.
155
if !imm12_is_zero || (imm20_is_zero && imm12_is_zero) {
156
insts.push(Inst::AluRRImm12 {
157
alu_op: AluOPRRI::Addi,
158
rd,
159
rs,
160
imm12,
161
})
162
}
163
164
insts
165
})
166
}
167
168
pub(crate) fn load_constant_u32(rd: Writable<Reg>, value: u64) -> SmallInstVec<Inst> {
169
let insts = Inst::load_const_imm(rd, value);
170
insts.unwrap_or_else(|| {
171
smallvec![Inst::LoadInlineConst {
172
rd,
173
ty: I32,
174
imm: value
175
}]
176
})
177
}
178
179
pub fn load_constant_u64(rd: Writable<Reg>, value: u64) -> SmallInstVec<Inst> {
180
let insts = Inst::load_const_imm(rd, value);
181
insts.unwrap_or_else(|| {
182
smallvec![Inst::LoadInlineConst {
183
rd,
184
ty: I64,
185
imm: value
186
}]
187
})
188
}
189
190
pub(crate) fn construct_auipc_and_jalr(
191
link: Option<Writable<Reg>>,
192
tmp: Writable<Reg>,
193
offset: i64,
194
) -> [Inst; 2] {
195
Inst::generate_imm(offset as u64)
196
.map(|(imm20, imm12)| {
197
let a = Inst::Auipc {
198
rd: tmp,
199
imm: imm20,
200
};
201
let b = Inst::Jalr {
202
rd: link.unwrap_or(writable_zero_reg()),
203
base: tmp.to_reg(),
204
offset: imm12,
205
};
206
[a, b]
207
})
208
.expect("code range is too big.")
209
}
210
211
/// Generic constructor for a load (zero-extending where appropriate).
212
pub fn gen_load(into_reg: Writable<Reg>, mem: AMode, ty: Type, flags: MemFlags) -> Inst {
213
if ty.is_vector() {
214
Inst::VecLoad {
215
eew: VecElementWidth::from_type(ty),
216
to: into_reg,
217
from: VecAMode::UnitStride { base: mem },
218
flags,
219
mask: VecOpMasking::Disabled,
220
vstate: VState::from_type(ty),
221
}
222
} else {
223
Inst::Load {
224
rd: into_reg,
225
op: LoadOP::from_type(ty),
226
from: mem,
227
flags,
228
}
229
}
230
}
231
232
/// Generic constructor for a store.
233
pub fn gen_store(mem: AMode, from_reg: Reg, ty: Type, flags: MemFlags) -> Inst {
234
if ty.is_vector() {
235
Inst::VecStore {
236
eew: VecElementWidth::from_type(ty),
237
to: VecAMode::UnitStride { base: mem },
238
from: from_reg,
239
flags,
240
mask: VecOpMasking::Disabled,
241
vstate: VState::from_type(ty),
242
}
243
} else {
244
Inst::Store {
245
src: from_reg,
246
op: StoreOP::from_type(ty),
247
to: mem,
248
flags,
249
}
250
}
251
}
252
}
253
254
//=============================================================================
255
256
fn vec_mask_operands(mask: &mut VecOpMasking, collector: &mut impl OperandVisitor) {
257
match mask {
258
VecOpMasking::Enabled { reg } => {
259
collector.reg_fixed_use(reg, pv_reg(0).into());
260
}
261
VecOpMasking::Disabled => {}
262
}
263
}
264
fn vec_mask_late_operands(mask: &mut VecOpMasking, collector: &mut impl OperandVisitor) {
265
match mask {
266
VecOpMasking::Enabled { reg } => {
267
collector.reg_fixed_late_use(reg, pv_reg(0).into());
268
}
269
VecOpMasking::Disabled => {}
270
}
271
}
272
273
fn riscv64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
274
match inst {
275
Inst::Nop0 | Inst::Nop4 => {}
276
Inst::BrTable {
277
index, tmp1, tmp2, ..
278
} => {
279
collector.reg_use(index);
280
collector.reg_early_def(tmp1);
281
collector.reg_early_def(tmp2);
282
}
283
Inst::Auipc { rd, .. } => collector.reg_def(rd),
284
Inst::Lui { rd, .. } => collector.reg_def(rd),
285
Inst::Fli { rd, .. } => collector.reg_def(rd),
286
Inst::LoadInlineConst { rd, .. } => collector.reg_def(rd),
287
Inst::AluRRR { rd, rs1, rs2, .. } => {
288
collector.reg_use(rs1);
289
collector.reg_use(rs2);
290
collector.reg_def(rd);
291
}
292
Inst::FpuRRR { rd, rs1, rs2, .. } => {
293
collector.reg_use(rs1);
294
collector.reg_use(rs2);
295
collector.reg_def(rd);
296
}
297
Inst::AluRRImm12 { rd, rs, .. } => {
298
collector.reg_use(rs);
299
collector.reg_def(rd);
300
}
301
Inst::CsrReg { rd, rs, .. } => {
302
collector.reg_use(rs);
303
collector.reg_def(rd);
304
}
305
Inst::CsrImm { rd, .. } => {
306
collector.reg_def(rd);
307
}
308
Inst::Load { rd, from, .. } => {
309
from.get_operands(collector);
310
collector.reg_def(rd);
311
}
312
Inst::Store { to, src, .. } => {
313
to.get_operands(collector);
314
collector.reg_use(src);
315
}
316
317
Inst::Args { args } => {
318
for ArgPair { vreg, preg } in args {
319
collector.reg_fixed_def(vreg, *preg);
320
}
321
}
322
Inst::Rets { rets } => {
323
for RetPair { vreg, preg } in rets {
324
collector.reg_fixed_use(vreg, *preg);
325
}
326
}
327
Inst::Ret { .. } => {}
328
329
Inst::Extend { rd, rn, .. } => {
330
collector.reg_use(rn);
331
collector.reg_def(rd);
332
}
333
Inst::Call { info, .. } => {
334
let CallInfo { uses, defs, .. } = &mut **info;
335
for CallArgPair { vreg, preg } in uses {
336
collector.reg_fixed_use(vreg, *preg);
337
}
338
for CallRetPair { vreg, location } in defs {
339
match location {
340
RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),
341
RetLocation::Stack(..) => collector.any_def(vreg),
342
}
343
}
344
collector.reg_clobbers(info.clobbers);
345
if let Some(try_call_info) = &mut info.try_call_info {
346
try_call_info.collect_operands(collector);
347
}
348
}
349
Inst::CallInd { info } => {
350
let CallInfo {
351
dest, uses, defs, ..
352
} = &mut **info;
353
collector.reg_use(dest);
354
for CallArgPair { vreg, preg } in uses {
355
collector.reg_fixed_use(vreg, *preg);
356
}
357
for CallRetPair { vreg, location } in defs {
358
match location {
359
RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),
360
RetLocation::Stack(..) => collector.any_def(vreg),
361
}
362
}
363
collector.reg_clobbers(info.clobbers);
364
if let Some(try_call_info) = &mut info.try_call_info {
365
try_call_info.collect_operands(collector);
366
}
367
}
368
Inst::ReturnCall { info } => {
369
for CallArgPair { vreg, preg } in &mut info.uses {
370
collector.reg_fixed_use(vreg, *preg);
371
}
372
}
373
Inst::ReturnCallInd { info } => {
374
// TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
375
// This shouldn't be a fixed register constraint.
376
collector.reg_fixed_use(&mut info.dest, x_reg(5));
377
378
for CallArgPair { vreg, preg } in &mut info.uses {
379
collector.reg_fixed_use(vreg, *preg);
380
}
381
}
382
Inst::Jal { .. } => {
383
// JAL technically has a rd register, but we currently always
384
// hardcode it to x0.
385
}
386
Inst::CondBr {
387
kind: IntegerCompare { rs1, rs2, .. },
388
..
389
} => {
390
collector.reg_use(rs1);
391
collector.reg_use(rs2);
392
}
393
Inst::LoadExtNameGot { rd, .. }
394
| Inst::LoadExtNameNear { rd, .. }
395
| Inst::LoadExtNameFar { rd, .. } => {
396
collector.reg_def(rd);
397
}
398
Inst::ElfTlsGetAddr { rd, .. } => {
399
// x10 is a0 which is both the first argument and the first return value.
400
collector.reg_fixed_def(rd, a0());
401
let mut clobbers =
402
Riscv64MachineDeps::get_regs_clobbered_by_call(CallConv::SystemV, false);
403
clobbers.remove(px_reg(10));
404
collector.reg_clobbers(clobbers);
405
}
406
Inst::LoadAddr { rd, mem } => {
407
mem.get_operands(collector);
408
collector.reg_early_def(rd);
409
}
410
411
Inst::Mov { rd, rm, .. } => {
412
collector.reg_use(rm);
413
collector.reg_def(rd);
414
}
415
Inst::MovFromPReg { rd, rm } => {
416
debug_assert!([px_reg(2), px_reg(8)].contains(rm));
417
collector.reg_def(rd);
418
}
419
Inst::Fence { .. } => {}
420
Inst::EBreak => {}
421
Inst::Udf { .. } => {}
422
Inst::FpuRR { rd, rs, .. } => {
423
collector.reg_use(rs);
424
collector.reg_def(rd);
425
}
426
Inst::FpuRRRR {
427
rd, rs1, rs2, rs3, ..
428
} => {
429
collector.reg_use(rs1);
430
collector.reg_use(rs2);
431
collector.reg_use(rs3);
432
collector.reg_def(rd);
433
}
434
435
Inst::Jalr { rd, base, .. } => {
436
collector.reg_use(base);
437
collector.reg_def(rd);
438
}
439
Inst::Atomic { rd, addr, src, .. } => {
440
collector.reg_use(addr);
441
collector.reg_use(src);
442
collector.reg_def(rd);
443
}
444
Inst::Select {
445
dst,
446
condition: IntegerCompare { rs1, rs2, .. },
447
x,
448
y,
449
..
450
} => {
451
// Mark the condition registers as late use so that they don't overlap with the destination
452
// register. We may potentially write to the destination register before evaluating the
453
// condition.
454
collector.reg_late_use(rs1);
455
collector.reg_late_use(rs2);
456
457
for reg in x.regs_mut() {
458
collector.reg_use(reg);
459
}
460
for reg in y.regs_mut() {
461
collector.reg_use(reg);
462
}
463
464
// If there's more than one destination register then use
465
// `reg_early_def` to prevent destination registers from overlapping
466
// with any operands. This ensures that the lowering doesn't have to
467
// deal with a situation such as when the input registers need to be
468
// swapped when moved to the destination.
469
//
470
// When there's only one destination register though don't use an
471
// early def because once the register is written no other inputs
472
// are read so it's ok for the destination to overlap the sources.
473
// The condition registers are already marked as late use so they
474
// won't overlap with the destination.
475
match dst.regs_mut() {
476
[reg] => collector.reg_def(reg),
477
regs => {
478
for d in regs {
479
collector.reg_early_def(d);
480
}
481
}
482
}
483
}
484
Inst::AtomicCas {
485
offset,
486
t0,
487
dst,
488
e,
489
addr,
490
v,
491
..
492
} => {
493
collector.reg_use(offset);
494
collector.reg_use(e);
495
collector.reg_use(addr);
496
collector.reg_use(v);
497
collector.reg_early_def(t0);
498
collector.reg_early_def(dst);
499
}
500
501
Inst::RawData { .. } => {}
502
Inst::AtomicStore { src, p, .. } => {
503
collector.reg_use(src);
504
collector.reg_use(p);
505
}
506
Inst::AtomicLoad { rd, p, .. } => {
507
collector.reg_use(p);
508
collector.reg_def(rd);
509
}
510
Inst::AtomicRmwLoop {
511
offset,
512
dst,
513
p,
514
x,
515
t0,
516
..
517
} => {
518
collector.reg_use(offset);
519
collector.reg_use(p);
520
collector.reg_use(x);
521
collector.reg_early_def(t0);
522
collector.reg_early_def(dst);
523
}
524
Inst::TrapIf { rs1, rs2, .. } => {
525
collector.reg_use(rs1);
526
collector.reg_use(rs2);
527
}
528
Inst::Unwind { .. } => {}
529
Inst::DummyUse { reg } => {
530
collector.reg_use(reg);
531
}
532
Inst::Popcnt {
533
sum, step, rs, tmp, ..
534
} => {
535
collector.reg_use(rs);
536
collector.reg_early_def(tmp);
537
collector.reg_early_def(step);
538
collector.reg_early_def(sum);
539
}
540
Inst::Cltz {
541
sum, step, tmp, rs, ..
542
} => {
543
collector.reg_use(rs);
544
collector.reg_early_def(tmp);
545
collector.reg_early_def(step);
546
collector.reg_early_def(sum);
547
}
548
Inst::Brev8 {
549
rs,
550
rd,
551
step,
552
tmp,
553
tmp2,
554
..
555
} => {
556
collector.reg_use(rs);
557
collector.reg_early_def(step);
558
collector.reg_early_def(tmp);
559
collector.reg_early_def(tmp2);
560
collector.reg_early_def(rd);
561
}
562
Inst::StackProbeLoop { .. } => {
563
// StackProbeLoop has a tmp register and StackProbeLoop used at gen_prologue.
564
// t3 will do the job. (t3 is caller-save register and not used directly by compiler like writable_spilltmp_reg)
565
// gen_prologue is called at emit stage.
566
// no need let reg alloc know.
567
}
568
Inst::VecAluRRRR {
569
op,
570
vd,
571
vd_src,
572
vs1,
573
vs2,
574
mask,
575
..
576
} => {
577
debug_assert_eq!(vd_src.class(), RegClass::Vector);
578
debug_assert_eq!(vd.to_reg().class(), RegClass::Vector);
579
debug_assert_eq!(vs2.class(), RegClass::Vector);
580
debug_assert_eq!(vs1.class(), op.vs1_regclass());
581
582
collector.reg_late_use(vs1);
583
collector.reg_late_use(vs2);
584
collector.reg_use(vd_src);
585
collector.reg_reuse_def(vd, 2); // `vd` == `vd_src`.
586
vec_mask_late_operands(mask, collector);
587
}
588
Inst::VecAluRRRImm5 {
589
op,
590
vd,
591
vd_src,
592
vs2,
593
mask,
594
..
595
} => {
596
debug_assert_eq!(vd_src.class(), RegClass::Vector);
597
debug_assert_eq!(vd.to_reg().class(), RegClass::Vector);
598
debug_assert_eq!(vs2.class(), RegClass::Vector);
599
600
// If the operation forbids source/destination overlap we need to
601
// ensure that the source and destination registers are different.
602
if op.forbids_overlaps(mask) {
603
collector.reg_late_use(vs2);
604
collector.reg_use(vd_src);
605
collector.reg_reuse_def(vd, 1); // `vd` == `vd_src`.
606
vec_mask_late_operands(mask, collector);
607
} else {
608
collector.reg_use(vs2);
609
collector.reg_use(vd_src);
610
collector.reg_reuse_def(vd, 1); // `vd` == `vd_src`.
611
vec_mask_operands(mask, collector);
612
}
613
}
614
Inst::VecAluRRR {
615
op,
616
vd,
617
vs1,
618
vs2,
619
mask,
620
..
621
} => {
622
debug_assert_eq!(vd.to_reg().class(), RegClass::Vector);
623
debug_assert_eq!(vs2.class(), RegClass::Vector);
624
debug_assert_eq!(vs1.class(), op.vs1_regclass());
625
626
collector.reg_use(vs1);
627
collector.reg_use(vs2);
628
629
// If the operation forbids source/destination overlap, then we must
630
// register it as an early_def. This encodes the constraint that
631
// these must not overlap.
632
if op.forbids_overlaps(mask) {
633
collector.reg_early_def(vd);
634
} else {
635
collector.reg_def(vd);
636
}
637
638
vec_mask_operands(mask, collector);
639
}
640
Inst::VecAluRRImm5 {
641
op, vd, vs2, mask, ..
642
} => {
643
debug_assert_eq!(vd.to_reg().class(), RegClass::Vector);
644
debug_assert_eq!(vs2.class(), RegClass::Vector);
645
646
collector.reg_use(vs2);
647
648
// If the operation forbids source/destination overlap, then we must
649
// register it as an early_def. This encodes the constraint that
650
// these must not overlap.
651
if op.forbids_overlaps(mask) {
652
collector.reg_early_def(vd);
653
} else {
654
collector.reg_def(vd);
655
}
656
657
vec_mask_operands(mask, collector);
658
}
659
Inst::VecAluRR {
660
op, vd, vs, mask, ..
661
} => {
662
debug_assert_eq!(vd.to_reg().class(), op.dst_regclass());
663
debug_assert_eq!(vs.class(), op.src_regclass());
664
665
collector.reg_use(vs);
666
667
// If the operation forbids source/destination overlap, then we must
668
// register it as an early_def. This encodes the constraint that
669
// these must not overlap.
670
if op.forbids_overlaps(mask) {
671
collector.reg_early_def(vd);
672
} else {
673
collector.reg_def(vd);
674
}
675
676
vec_mask_operands(mask, collector);
677
}
678
Inst::VecAluRImm5 { op, vd, mask, .. } => {
679
debug_assert_eq!(vd.to_reg().class(), RegClass::Vector);
680
debug_assert!(!op.forbids_overlaps(mask));
681
682
collector.reg_def(vd);
683
vec_mask_operands(mask, collector);
684
}
685
Inst::VecSetState { rd, .. } => {
686
collector.reg_def(rd);
687
}
688
Inst::VecLoad { to, from, mask, .. } => {
689
from.get_operands(collector);
690
collector.reg_def(to);
691
vec_mask_operands(mask, collector);
692
}
693
Inst::VecStore { to, from, mask, .. } => {
694
to.get_operands(collector);
695
collector.reg_use(from);
696
vec_mask_operands(mask, collector);
697
}
698
Inst::EmitIsland { .. } => {}
699
Inst::LabelAddress { dst, .. } => {
700
collector.reg_def(dst);
701
}
702
}
703
}
704
705
impl MachInst for Inst {
706
type LabelUse = LabelUse;
707
type ABIMachineSpec = Riscv64MachineDeps;
708
709
// https://github.com/riscv/riscv-isa-manual/issues/850
710
// all zero will cause invalid opcode.
711
const TRAP_OPCODE: &'static [u8] = &[0; 4];
712
713
fn gen_dummy_use(reg: Reg) -> Self {
714
Inst::DummyUse { reg }
715
}
716
717
fn canonical_type_for_rc(rc: RegClass) -> Type {
718
match rc {
719
regalloc2::RegClass::Int => I64,
720
regalloc2::RegClass::Float => F64,
721
regalloc2::RegClass::Vector => I8X16,
722
}
723
}
724
725
fn is_safepoint(&self) -> bool {
726
match self {
727
Inst::Call { .. } | Inst::CallInd { .. } => true,
728
_ => false,
729
}
730
}
731
732
fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
733
riscv64_get_operands(self, collector);
734
}
735
736
fn is_move(&self) -> Option<(Writable<Reg>, Reg)> {
737
match self {
738
Inst::Mov { rd, rm, .. } => Some((*rd, *rm)),
739
_ => None,
740
}
741
}
742
743
fn is_included_in_clobbers(&self) -> bool {
744
match self {
745
&Inst::Args { .. } => false,
746
_ => true,
747
}
748
}
749
750
fn is_trap(&self) -> bool {
751
match self {
752
Self::Udf { .. } => true,
753
_ => false,
754
}
755
}
756
757
fn is_args(&self) -> bool {
758
match self {
759
Self::Args { .. } => true,
760
_ => false,
761
}
762
}
763
764
fn call_type(&self) -> CallType {
765
match self {
766
Inst::Call { .. } | Inst::CallInd { .. } | Inst::ElfTlsGetAddr { .. } => {
767
CallType::Regular
768
}
769
770
Inst::ReturnCall { .. } | Inst::ReturnCallInd { .. } => CallType::TailCall,
771
772
_ => CallType::None,
773
}
774
}
775
776
fn is_term(&self) -> MachTerminator {
777
match self {
778
&Inst::Jal { .. } => MachTerminator::Branch,
779
&Inst::CondBr { .. } => MachTerminator::Branch,
780
&Inst::Jalr { .. } => MachTerminator::Branch,
781
&Inst::Rets { .. } => MachTerminator::Ret,
782
&Inst::BrTable { .. } => MachTerminator::Branch,
783
&Inst::ReturnCall { .. } | &Inst::ReturnCallInd { .. } => MachTerminator::RetCall,
784
&Inst::Call { ref info } if info.try_call_info.is_some() => MachTerminator::Branch,
785
&Inst::CallInd { ref info } if info.try_call_info.is_some() => MachTerminator::Branch,
786
_ => MachTerminator::None,
787
}
788
}
789
790
fn is_mem_access(&self) -> bool {
791
panic!("TODO FILL ME OUT")
792
}
793
794
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {
795
let x = Inst::Mov {
796
rd: to_reg,
797
rm: from_reg,
798
ty,
799
};
800
x
801
}
802
803
fn gen_nop(preferred_size: usize) -> Inst {
804
if preferred_size == 0 {
805
return Inst::Nop0;
806
}
807
// We can't give a NOP (or any insn) < 4 bytes.
808
assert!(preferred_size >= 4);
809
Inst::Nop4
810
}
811
812
fn rc_for_type(ty: Type) -> CodegenResult<(&'static [RegClass], &'static [Type])> {
813
match ty {
814
I8 => Ok((&[RegClass::Int], &[I8])),
815
I16 => Ok((&[RegClass::Int], &[I16])),
816
I32 => Ok((&[RegClass::Int], &[I32])),
817
I64 => Ok((&[RegClass::Int], &[I64])),
818
F16 => Ok((&[RegClass::Float], &[F16])),
819
F32 => Ok((&[RegClass::Float], &[F32])),
820
F64 => Ok((&[RegClass::Float], &[F64])),
821
// FIXME(#8312): Add support for Q extension
822
F128 | I128 => Ok((&[RegClass::Int, RegClass::Int], &[I64, I64])),
823
_ if ty.is_vector() => {
824
debug_assert!(ty.bits() <= 512);
825
826
// Here we only need to return a SIMD type with the same size as `ty`.
827
// We use these types for spills and reloads, so prefer types with lanes <= 31
828
// since that fits in the immediate field of `vsetivli`.
829
const SIMD_TYPES: [[Type; 1]; 6] = [
830
[types::I8X2],
831
[types::I8X4],
832
[types::I8X8],
833
[types::I8X16],
834
[types::I16X16],
835
[types::I32X16],
836
];
837
let idx = (ty.bytes().ilog2() - 1) as usize;
838
let ty = &SIMD_TYPES[idx][..];
839
840
Ok((&[RegClass::Vector], ty))
841
}
842
_ => Err(CodegenError::Unsupported(format!(
843
"Unexpected SSA-value type: {ty}"
844
))),
845
}
846
}
847
848
fn gen_jump(target: MachLabel) -> Inst {
849
Inst::Jal { label: target }
850
}
851
852
fn worst_case_size() -> CodeOffset {
853
// Our worst case size is determined by the riscv64_worst_case_instruction_size test
854
84
855
}
856
857
fn ref_type_regclass(_settings: &settings::Flags) -> RegClass {
858
RegClass::Int
859
}
860
861
fn function_alignment() -> FunctionAlignment {
862
FunctionAlignment {
863
minimum: 2,
864
preferred: 4,
865
}
866
}
867
}
868
869
//=============================================================================
870
// Pretty-printing of instructions.
871
pub fn reg_name(reg: Reg) -> String {
872
match reg.to_real_reg() {
873
Some(real) => match real.class() {
874
RegClass::Int => match real.hw_enc() {
875
0 => "zero".into(),
876
1 => "ra".into(),
877
2 => "sp".into(),
878
3 => "gp".into(),
879
4 => "tp".into(),
880
5..=7 => format!("t{}", real.hw_enc() - 5),
881
8 => "fp".into(),
882
9 => "s1".into(),
883
10..=17 => format!("a{}", real.hw_enc() - 10),
884
18..=27 => format!("s{}", real.hw_enc() - 16),
885
28..=31 => format!("t{}", real.hw_enc() - 25),
886
_ => unreachable!(),
887
},
888
RegClass::Float => match real.hw_enc() {
889
0..=7 => format!("ft{}", real.hw_enc() - 0),
890
8..=9 => format!("fs{}", real.hw_enc() - 8),
891
10..=17 => format!("fa{}", real.hw_enc() - 10),
892
18..=27 => format!("fs{}", real.hw_enc() - 16),
893
28..=31 => format!("ft{}", real.hw_enc() - 20),
894
_ => unreachable!(),
895
},
896
RegClass::Vector => format!("v{}", real.hw_enc()),
897
},
898
None => {
899
format!("{reg:?}")
900
}
901
}
902
}
903
904
fn pretty_print_try_call(info: &TryCallInfo) -> String {
905
format!(
906
"; j {:?}; catch [{}]",
907
info.continuation,
908
info.pretty_print_dests()
909
)
910
}
911
912
impl Inst {
913
fn print_with_state(&self, _state: &mut EmitState) -> String {
914
let format_reg = |reg: Reg| -> String { reg_name(reg) };
915
916
let format_vec_amode = |amode: &VecAMode| -> String {
917
match amode {
918
VecAMode::UnitStride { base } => base.to_string(),
919
}
920
};
921
922
let format_mask = |mask: &VecOpMasking| -> String {
923
match mask {
924
VecOpMasking::Enabled { reg } => format!(",{}.t", format_reg(*reg)),
925
VecOpMasking::Disabled => format!(""),
926
}
927
};
928
929
let format_regs = |regs: &[Reg]| -> String {
930
let mut x = if regs.len() > 1 {
931
String::from("[")
932
} else {
933
String::default()
934
};
935
regs.iter().for_each(|i| {
936
x.push_str(format_reg(*i).as_str());
937
if *i != *regs.last().unwrap() {
938
x.push_str(",");
939
}
940
});
941
if regs.len() > 1 {
942
x.push_str("]");
943
}
944
x
945
};
946
let format_labels = |labels: &[MachLabel]| -> String {
947
if labels.len() == 0 {
948
return String::from("[_]");
949
}
950
let mut x = String::from("[");
951
labels.iter().for_each(|l| {
952
x.push_str(
953
format!(
954
"{:?}{}",
955
l,
956
if l != labels.last().unwrap() { "," } else { "" },
957
)
958
.as_str(),
959
);
960
});
961
x.push_str("]");
962
x
963
};
964
965
fn format_frm(rounding_mode: FRM) -> String {
966
format!(",{}", rounding_mode.to_static_str())
967
}
968
969
match self {
970
&Inst::Nop0 => {
971
format!("##zero length nop")
972
}
973
&Inst::Nop4 => {
974
format!("##fixed 4-size nop")
975
}
976
&Inst::StackProbeLoop {
977
guard_size,
978
probe_count,
979
tmp,
980
} => {
981
let tmp = format_reg(tmp.to_reg());
982
format!(
983
"inline_stack_probe##guard_size={guard_size} probe_count={probe_count} tmp={tmp}"
984
)
985
}
986
&Inst::AtomicStore { src, ty, p } => {
987
let src = format_reg(src);
988
let p = format_reg(p);
989
format!("atomic_store.{ty} {src},({p})")
990
}
991
&Inst::DummyUse { reg } => {
992
let reg = format_reg(reg);
993
format!("dummy_use {reg}")
994
}
995
996
&Inst::AtomicLoad { rd, ty, p } => {
997
let p = format_reg(p);
998
let rd = format_reg(rd.to_reg());
999
format!("atomic_load.{ty} {rd},({p})")
1000
}
1001
&Inst::AtomicRmwLoop {
1002
offset,
1003
op,
1004
dst,
1005
ty,
1006
p,
1007
x,
1008
t0,
1009
} => {
1010
let offset = format_reg(offset);
1011
let p = format_reg(p);
1012
let x = format_reg(x);
1013
let t0 = format_reg(t0.to_reg());
1014
let dst = format_reg(dst.to_reg());
1015
format!("atomic_rmw.{ty} {op} {dst},{x},({p})##t0={t0} offset={offset}")
1016
}
1017
1018
&Inst::RawData { ref data } => match data.len() {
1019
4 => {
1020
let mut bytes = [0; 4];
1021
for i in 0..bytes.len() {
1022
bytes[i] = data[i];
1023
}
1024
format!(".4byte 0x{:x}", u32::from_le_bytes(bytes))
1025
}
1026
8 => {
1027
let mut bytes = [0; 8];
1028
for i in 0..bytes.len() {
1029
bytes[i] = data[i];
1030
}
1031
format!(".8byte 0x{:x}", u64::from_le_bytes(bytes))
1032
}
1033
_ => {
1034
format!(".data {data:?}")
1035
}
1036
},
1037
&Inst::Unwind { ref inst } => {
1038
format!("unwind {inst:?}")
1039
}
1040
&Inst::Brev8 {
1041
rs,
1042
ty,
1043
step,
1044
tmp,
1045
tmp2,
1046
rd,
1047
} => {
1048
let rs = format_reg(rs);
1049
let step = format_reg(step.to_reg());
1050
let tmp = format_reg(tmp.to_reg());
1051
let tmp2 = format_reg(tmp2.to_reg());
1052
let rd = format_reg(rd.to_reg());
1053
format!("brev8 {rd},{rs}##tmp={tmp} tmp2={tmp2} step={step} ty={ty}")
1054
}
1055
&Inst::Popcnt {
1056
sum,
1057
step,
1058
rs,
1059
tmp,
1060
ty,
1061
} => {
1062
let rs = format_reg(rs);
1063
let tmp = format_reg(tmp.to_reg());
1064
let step = format_reg(step.to_reg());
1065
let sum = format_reg(sum.to_reg());
1066
format!("popcnt {sum},{rs}##ty={ty} tmp={tmp} step={step}")
1067
}
1068
&Inst::Cltz {
1069
sum,
1070
step,
1071
rs,
1072
tmp,
1073
ty,
1074
leading,
1075
} => {
1076
let rs = format_reg(rs);
1077
let tmp = format_reg(tmp.to_reg());
1078
let step = format_reg(step.to_reg());
1079
let sum = format_reg(sum.to_reg());
1080
format!(
1081
"{} {},{}##ty={} tmp={} step={}",
1082
if leading { "clz" } else { "ctz" },
1083
sum,
1084
rs,
1085
ty,
1086
tmp,
1087
step
1088
)
1089
}
1090
&Inst::AtomicCas {
1091
offset,
1092
t0,
1093
dst,
1094
e,
1095
addr,
1096
v,
1097
ty,
1098
} => {
1099
let offset = format_reg(offset);
1100
let e = format_reg(e);
1101
let addr = format_reg(addr);
1102
let v = format_reg(v);
1103
let t0 = format_reg(t0.to_reg());
1104
let dst = format_reg(dst.to_reg());
1105
format!("atomic_cas.{ty} {dst},{e},{v},({addr})##t0={t0} offset={offset}",)
1106
}
1107
&Inst::BrTable {
1108
index,
1109
tmp1,
1110
tmp2,
1111
ref targets,
1112
} => {
1113
format!(
1114
"{} {},{}##tmp1={},tmp2={}",
1115
"br_table",
1116
format_reg(index),
1117
format_labels(&targets[..]),
1118
format_reg(tmp1.to_reg()),
1119
format_reg(tmp2.to_reg()),
1120
)
1121
}
1122
&Inst::Auipc { rd, imm } => {
1123
format!("{} {},{}", "auipc", format_reg(rd.to_reg()), imm.as_i32(),)
1124
}
1125
&Inst::Jalr { rd, base, offset } => {
1126
let base = format_reg(base);
1127
let rd = format_reg(rd.to_reg());
1128
format!("{} {},{}({})", "jalr", rd, offset.as_i16(), base)
1129
}
1130
&Inst::Lui { rd, ref imm } => {
1131
format!("{} {},{}", "lui", format_reg(rd.to_reg()), imm.as_i32())
1132
}
1133
&Inst::Fli { rd, width, imm } => {
1134
let rd_s = format_reg(rd.to_reg());
1135
let imm_s = imm.format();
1136
format!("fli.{width} {rd_s},{imm_s}")
1137
}
1138
&Inst::LoadInlineConst { rd, imm, .. } => {
1139
let rd = format_reg(rd.to_reg());
1140
let mut buf = String::new();
1141
write!(&mut buf, "auipc {rd},0; ").unwrap();
1142
write!(&mut buf, "ld {rd},12({rd}); ").unwrap();
1143
write!(&mut buf, "j {}; ", Inst::UNCOMPRESSED_INSTRUCTION_SIZE + 8).unwrap();
1144
write!(&mut buf, ".8byte 0x{imm:x}").unwrap();
1145
buf
1146
}
1147
&Inst::AluRRR {
1148
alu_op,
1149
rd,
1150
rs1,
1151
rs2,
1152
} => {
1153
let rs1_s = format_reg(rs1);
1154
let rs2_s = format_reg(rs2);
1155
let rd_s = format_reg(rd.to_reg());
1156
match alu_op {
1157
AluOPRRR::Adduw if rs2 == zero_reg() => {
1158
format!("zext.w {rd_s},{rs1_s}")
1159
}
1160
_ => {
1161
format!("{} {},{},{}", alu_op.op_name(), rd_s, rs1_s, rs2_s)
1162
}
1163
}
1164
}
1165
&Inst::FpuRR {
1166
alu_op,
1167
width,
1168
frm,
1169
rd,
1170
rs,
1171
} => {
1172
let rs = format_reg(rs);
1173
let rd = format_reg(rd.to_reg());
1174
let frm = if alu_op.has_frm() {
1175
format_frm(frm)
1176
} else {
1177
String::new()
1178
};
1179
format!("{} {rd},{rs}{frm}", alu_op.op_name(width))
1180
}
1181
&Inst::FpuRRR {
1182
alu_op,
1183
width,
1184
rd,
1185
rs1,
1186
rs2,
1187
frm,
1188
} => {
1189
let rs1 = format_reg(rs1);
1190
let rs2 = format_reg(rs2);
1191
let rd = format_reg(rd.to_reg());
1192
let frm = if alu_op.has_frm() {
1193
format_frm(frm)
1194
} else {
1195
String::new()
1196
};
1197
1198
let rs1_is_rs2 = rs1 == rs2;
1199
match alu_op {
1200
FpuOPRRR::Fsgnj if rs1_is_rs2 => format!("fmv.{width} {rd},{rs1}"),
1201
FpuOPRRR::Fsgnjn if rs1_is_rs2 => format!("fneg.{width} {rd},{rs1}"),
1202
FpuOPRRR::Fsgnjx if rs1_is_rs2 => format!("fabs.{width} {rd},{rs1}"),
1203
_ => format!("{} {rd},{rs1},{rs2}{frm}", alu_op.op_name(width)),
1204
}
1205
}
1206
&Inst::FpuRRRR {
1207
alu_op,
1208
rd,
1209
rs1,
1210
rs2,
1211
rs3,
1212
frm,
1213
width,
1214
} => {
1215
let rs1 = format_reg(rs1);
1216
let rs2 = format_reg(rs2);
1217
let rs3 = format_reg(rs3);
1218
let rd = format_reg(rd.to_reg());
1219
let frm = format_frm(frm);
1220
let op_name = alu_op.op_name(width);
1221
format!("{op_name} {rd},{rs1},{rs2},{rs3}{frm}")
1222
}
1223
&Inst::AluRRImm12 {
1224
alu_op,
1225
rd,
1226
rs,
1227
ref imm12,
1228
} => {
1229
let rs_s = format_reg(rs);
1230
let rd = format_reg(rd.to_reg());
1231
1232
// Some of these special cases are better known as
1233
// their pseudo-instruction version, so prefer printing those.
1234
match (alu_op, rs, imm12) {
1235
(AluOPRRI::Addi, rs, _) if rs == zero_reg() => {
1236
return format!("li {},{}", rd, imm12.as_i16());
1237
}
1238
(AluOPRRI::Addiw, _, imm12) if imm12.as_i16() == 0 => {
1239
return format!("sext.w {rd},{rs_s}");
1240
}
1241
(AluOPRRI::Xori, _, imm12) if imm12.as_i16() == -1 => {
1242
return format!("not {rd},{rs_s}");
1243
}
1244
(AluOPRRI::SltiU, _, imm12) if imm12.as_i16() == 1 => {
1245
return format!("seqz {rd},{rs_s}");
1246
}
1247
(alu_op, _, _) if alu_op.option_funct12().is_some() => {
1248
format!("{} {},{}", alu_op.op_name(), rd, rs_s)
1249
}
1250
(alu_op, _, imm12) => {
1251
format!("{} {},{},{}", alu_op.op_name(), rd, rs_s, imm12.as_i16())
1252
}
1253
}
1254
}
1255
&Inst::CsrReg { op, rd, rs, csr } => {
1256
let rs_s = format_reg(rs);
1257
let rd_s = format_reg(rd.to_reg());
1258
1259
match (op, csr, rd) {
1260
(CsrRegOP::CsrRW, CSR::Frm, rd) if rd.to_reg() == zero_reg() => {
1261
format!("fsrm {rs_s}")
1262
}
1263
_ => {
1264
format!("{op} {rd_s},{csr},{rs_s}")
1265
}
1266
}
1267
}
1268
&Inst::CsrImm { op, rd, csr, imm } => {
1269
let rd_s = format_reg(rd.to_reg());
1270
1271
match (op, csr, rd) {
1272
(CsrImmOP::CsrRWI, CSR::Frm, rd) if rd.to_reg() != zero_reg() => {
1273
format!("fsrmi {rd_s},{imm}")
1274
}
1275
_ => {
1276
format!("{op} {rd_s},{csr},{imm}")
1277
}
1278
}
1279
}
1280
&Inst::Load {
1281
rd,
1282
op,
1283
from,
1284
flags: _flags,
1285
} => {
1286
let base = from.to_string();
1287
let rd = format_reg(rd.to_reg());
1288
format!("{} {},{}", op.op_name(), rd, base,)
1289
}
1290
&Inst::Store {
1291
to,
1292
src,
1293
op,
1294
flags: _flags,
1295
} => {
1296
let base = to.to_string();
1297
let src = format_reg(src);
1298
format!("{} {},{}", op.op_name(), src, base,)
1299
}
1300
&Inst::Args { ref args } => {
1301
let mut s = "args".to_string();
1302
for arg in args {
1303
let preg = format_reg(arg.preg);
1304
let def = format_reg(arg.vreg.to_reg());
1305
write!(&mut s, " {def}={preg}").unwrap();
1306
}
1307
s
1308
}
1309
&Inst::Rets { ref rets } => {
1310
let mut s = "rets".to_string();
1311
for ret in rets {
1312
let preg = format_reg(ret.preg);
1313
let vreg = format_reg(ret.vreg);
1314
write!(&mut s, " {vreg}={preg}").unwrap();
1315
}
1316
s
1317
}
1318
&Inst::Ret {} => "ret".to_string(),
1319
1320
&MInst::Extend {
1321
rd,
1322
rn,
1323
signed,
1324
from_bits,
1325
..
1326
} => {
1327
let rn = format_reg(rn);
1328
let rd = format_reg(rd.to_reg());
1329
return if signed == false && from_bits == 8 {
1330
format!("andi {rd},{rn}")
1331
} else {
1332
let op = if signed { "srai" } else { "srli" };
1333
let shift_bits = (64 - from_bits) as i16;
1334
format!("slli {rd},{rn},{shift_bits}; {op} {rd},{rd},{shift_bits}")
1335
};
1336
}
1337
&MInst::Call { ref info } => {
1338
let try_call = info
1339
.try_call_info
1340
.as_ref()
1341
.map(|tci| pretty_print_try_call(tci))
1342
.unwrap_or_default();
1343
format!("call {}{try_call}", info.dest.display(None))
1344
}
1345
&MInst::CallInd { ref info } => {
1346
let rd = format_reg(info.dest);
1347
let try_call = info
1348
.try_call_info
1349
.as_ref()
1350
.map(|tci| pretty_print_try_call(tci))
1351
.unwrap_or_default();
1352
format!("callind {rd}{try_call}")
1353
}
1354
&MInst::ReturnCall { ref info } => {
1355
let mut s = format!(
1356
"return_call {:?} new_stack_arg_size:{}",
1357
info.dest, info.new_stack_arg_size
1358
);
1359
for ret in &info.uses {
1360
let preg = format_reg(ret.preg);
1361
let vreg = format_reg(ret.vreg);
1362
write!(&mut s, " {vreg}={preg}").unwrap();
1363
}
1364
s
1365
}
1366
&MInst::ReturnCallInd { ref info } => {
1367
let callee = format_reg(info.dest);
1368
let mut s = format!(
1369
"return_call_ind {callee} new_stack_arg_size:{}",
1370
info.new_stack_arg_size
1371
);
1372
for ret in &info.uses {
1373
let preg = format_reg(ret.preg);
1374
let vreg = format_reg(ret.vreg);
1375
write!(&mut s, " {vreg}={preg}").unwrap();
1376
}
1377
s
1378
}
1379
&MInst::TrapIf {
1380
rs1,
1381
rs2,
1382
cc,
1383
trap_code,
1384
} => {
1385
let rs1 = format_reg(rs1);
1386
let rs2 = format_reg(rs2);
1387
format!("trap_if {trap_code}##({rs1} {cc} {rs2})")
1388
}
1389
&MInst::Jal { label } => {
1390
format!("j {}", label.to_string())
1391
}
1392
&MInst::CondBr {
1393
taken,
1394
not_taken,
1395
kind,
1396
..
1397
} => {
1398
let rs1 = format_reg(kind.rs1);
1399
let rs2 = format_reg(kind.rs2);
1400
if not_taken.is_fallthrouh() && taken.as_label().is_none() {
1401
format!("{} {},{},0", kind.op_name(), rs1, rs2)
1402
} else {
1403
let x = format!(
1404
"{} {},{},taken({}),not_taken({})",
1405
kind.op_name(),
1406
rs1,
1407
rs2,
1408
taken,
1409
not_taken
1410
);
1411
x
1412
}
1413
}
1414
&MInst::Atomic {
1415
op,
1416
rd,
1417
addr,
1418
src,
1419
amo,
1420
} => {
1421
let op_name = op.op_name(amo);
1422
let addr = format_reg(addr);
1423
let src = format_reg(src);
1424
let rd = format_reg(rd.to_reg());
1425
if op.is_load() {
1426
format!("{op_name} {rd},({addr})")
1427
} else {
1428
format!("{op_name} {rd},{src},({addr})")
1429
}
1430
}
1431
&MInst::LoadExtNameGot { rd, ref name } => {
1432
let rd = format_reg(rd.to_reg());
1433
format!("load_ext_name_got {rd},{}", name.display(None))
1434
}
1435
&MInst::LoadExtNameNear {
1436
rd,
1437
ref name,
1438
offset,
1439
} => {
1440
let rd = format_reg(rd.to_reg());
1441
format!("load_ext_name_near {rd},{}{offset:+}", name.display(None))
1442
}
1443
&MInst::LoadExtNameFar {
1444
rd,
1445
ref name,
1446
offset,
1447
} => {
1448
let rd = format_reg(rd.to_reg());
1449
format!("load_ext_name_far {rd},{}{offset:+}", name.display(None))
1450
}
1451
&Inst::ElfTlsGetAddr { rd, ref name } => {
1452
let rd = format_reg(rd.to_reg());
1453
format!("elf_tls_get_addr {rd},{}", name.display(None))
1454
}
1455
&MInst::LoadAddr { ref rd, ref mem } => {
1456
let rs = mem.to_string();
1457
let rd = format_reg(rd.to_reg());
1458
format!("load_addr {rd},{rs}")
1459
}
1460
&MInst::Mov { rd, rm, ty } => {
1461
let rm = format_reg(rm);
1462
let rd = format_reg(rd.to_reg());
1463
1464
let op = match ty {
1465
F16 => "fmv.h",
1466
F32 => "fmv.s",
1467
F64 => "fmv.d",
1468
ty if ty.is_vector() => "vmv1r.v",
1469
_ => "mv",
1470
};
1471
1472
format!("{op} {rd},{rm}")
1473
}
1474
&MInst::MovFromPReg { rd, rm } => {
1475
let rd = format_reg(rd.to_reg());
1476
debug_assert!([px_reg(2), px_reg(8)].contains(&rm));
1477
let rm = reg_name(Reg::from(rm));
1478
format!("mv {rd},{rm}")
1479
}
1480
&MInst::Fence { pred, succ } => {
1481
format!(
1482
"fence {},{}",
1483
Inst::fence_req_to_string(pred),
1484
Inst::fence_req_to_string(succ),
1485
)
1486
}
1487
&MInst::Select {
1488
ref dst,
1489
condition,
1490
ref x,
1491
ref y,
1492
} => {
1493
let c_rs1 = format_reg(condition.rs1);
1494
let c_rs2 = format_reg(condition.rs2);
1495
let x = format_regs(x.regs());
1496
let y = format_regs(y.regs());
1497
let dst = dst.map(|r| r.to_reg());
1498
let dst = format_regs(dst.regs());
1499
format!(
1500
"select {},{},{}##condition=({} {} {})",
1501
dst,
1502
x,
1503
y,
1504
c_rs1,
1505
condition.kind.to_static_str(),
1506
c_rs2
1507
)
1508
}
1509
&MInst::Udf { trap_code } => format!("udf##trap_code={trap_code}"),
1510
&MInst::EBreak {} => String::from("ebreak"),
1511
&Inst::VecAluRRRR {
1512
op,
1513
vd,
1514
vd_src,
1515
vs1,
1516
vs2,
1517
ref mask,
1518
ref vstate,
1519
} => {
1520
let vs1_s = format_reg(vs1);
1521
let vs2_s = format_reg(vs2);
1522
let vd_src_s = format_reg(vd_src);
1523
let vd_s = format_reg(vd.to_reg());
1524
let mask = format_mask(mask);
1525
1526
let vd_fmt = if vd_s != vd_src_s {
1527
format!("{vd_s},{vd_src_s}")
1528
} else {
1529
vd_s
1530
};
1531
1532
// Note: vs2 and vs1 here are opposite to the standard scalar ordering.
1533
// This is noted in Section 10.1 of the RISC-V Vector spec.
1534
format!("{op} {vd_fmt},{vs2_s},{vs1_s}{mask} {vstate}")
1535
}
1536
&Inst::VecAluRRRImm5 {
1537
op,
1538
vd,
1539
imm,
1540
vs2,
1541
ref mask,
1542
ref vstate,
1543
..
1544
} => {
1545
let vs2_s = format_reg(vs2);
1546
let vd_s = format_reg(vd.to_reg());
1547
let mask = format_mask(mask);
1548
1549
// Some opcodes interpret the immediate as unsigned, lets show the
1550
// correct number here.
1551
let imm_s = if op.imm_is_unsigned() {
1552
format!("{}", imm.bits())
1553
} else {
1554
format!("{imm}")
1555
};
1556
1557
format!("{op} {vd_s},{vs2_s},{imm_s}{mask} {vstate}")
1558
}
1559
&Inst::VecAluRRR {
1560
op,
1561
vd,
1562
vs1,
1563
vs2,
1564
ref mask,
1565
ref vstate,
1566
} => {
1567
let vs1_s = format_reg(vs1);
1568
let vs2_s = format_reg(vs2);
1569
let vd_s = format_reg(vd.to_reg());
1570
let mask = format_mask(mask);
1571
1572
// Note: vs2 and vs1 here are opposite to the standard scalar ordering.
1573
// This is noted in Section 10.1 of the RISC-V Vector spec.
1574
match (op, vs2, vs1) {
1575
(VecAluOpRRR::VrsubVX, _, vs1) if vs1 == zero_reg() => {
1576
format!("vneg.v {vd_s},{vs2_s}{mask} {vstate}")
1577
}
1578
(VecAluOpRRR::VfsgnjnVV, vs2, vs1) if vs2 == vs1 => {
1579
format!("vfneg.v {vd_s},{vs2_s}{mask} {vstate}")
1580
}
1581
(VecAluOpRRR::VfsgnjxVV, vs2, vs1) if vs2 == vs1 => {
1582
format!("vfabs.v {vd_s},{vs2_s}{mask} {vstate}")
1583
}
1584
(VecAluOpRRR::VmnandMM, vs2, vs1) if vs2 == vs1 => {
1585
format!("vmnot.m {vd_s},{vs2_s}{mask} {vstate}")
1586
}
1587
_ => format!("{op} {vd_s},{vs2_s},{vs1_s}{mask} {vstate}"),
1588
}
1589
}
1590
&Inst::VecAluRRImm5 {
1591
op,
1592
vd,
1593
imm,
1594
vs2,
1595
ref mask,
1596
ref vstate,
1597
} => {
1598
let vs2_s = format_reg(vs2);
1599
let vd_s = format_reg(vd.to_reg());
1600
let mask = format_mask(mask);
1601
1602
// Some opcodes interpret the immediate as unsigned, lets show the
1603
// correct number here.
1604
let imm_s = if op.imm_is_unsigned() {
1605
format!("{}", imm.bits())
1606
} else {
1607
format!("{imm}")
1608
};
1609
1610
match (op, imm) {
1611
(VecAluOpRRImm5::VxorVI, imm) if imm == Imm5::maybe_from_i8(-1).unwrap() => {
1612
format!("vnot.v {vd_s},{vs2_s}{mask} {vstate}")
1613
}
1614
_ => format!("{op} {vd_s},{vs2_s},{imm_s}{mask} {vstate}"),
1615
}
1616
}
1617
&Inst::VecAluRR {
1618
op,
1619
vd,
1620
vs,
1621
ref mask,
1622
ref vstate,
1623
} => {
1624
let vs_s = format_reg(vs);
1625
let vd_s = format_reg(vd.to_reg());
1626
let mask = format_mask(mask);
1627
1628
format!("{op} {vd_s},{vs_s}{mask} {vstate}")
1629
}
1630
&Inst::VecAluRImm5 {
1631
op,
1632
vd,
1633
imm,
1634
ref mask,
1635
ref vstate,
1636
} => {
1637
let vd_s = format_reg(vd.to_reg());
1638
let mask = format_mask(mask);
1639
1640
format!("{op} {vd_s},{imm}{mask} {vstate}")
1641
}
1642
&Inst::VecSetState { rd, ref vstate } => {
1643
let rd_s = format_reg(rd.to_reg());
1644
assert!(vstate.avl.is_static());
1645
format!("vsetivli {}, {}, {}", rd_s, vstate.avl, vstate.vtype)
1646
}
1647
Inst::VecLoad {
1648
eew,
1649
to,
1650
from,
1651
mask,
1652
vstate,
1653
..
1654
} => {
1655
let base = format_vec_amode(from);
1656
let vd = format_reg(to.to_reg());
1657
let mask = format_mask(mask);
1658
1659
format!("vl{eew}.v {vd},{base}{mask} {vstate}")
1660
}
1661
Inst::VecStore {
1662
eew,
1663
to,
1664
from,
1665
mask,
1666
vstate,
1667
..
1668
} => {
1669
let dst = format_vec_amode(to);
1670
let vs3 = format_reg(*from);
1671
let mask = format_mask(mask);
1672
1673
format!("vs{eew}.v {vs3},{dst}{mask} {vstate}")
1674
}
1675
Inst::EmitIsland { needed_space } => {
1676
format!("emit_island {needed_space}")
1677
}
1678
1679
Inst::LabelAddress { dst, label } => {
1680
let dst = format_reg(dst.to_reg());
1681
format!("label_address {dst}, {label:?}")
1682
}
1683
}
1684
}
1685
}
1686
1687
/// Different forms of label references for different instruction formats.
1688
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1689
pub enum LabelUse {
1690
/// 20-bit branch offset (unconditional branches). PC-rel, offset is
1691
/// imm << 1. Immediate is 20 signed bits. Use in Jal instructions.
1692
Jal20,
1693
1694
/// The unconditional jump instructions all use PC-relative
1695
/// addressing to help support position independent code. The JALR
1696
/// instruction was defined to enable a two-instruction sequence to
1697
/// jump anywhere in a 32-bit absolute address range. A LUI
1698
/// instruction can first load rs1 with the upper 20 bits of a
1699
/// target address, then JALR can add in the lower bits. Similarly,
1700
/// AUIPC then JALR can jump anywhere in a 32-bit pc-relative
1701
/// address range.
1702
PCRel32,
1703
1704
/// All branch instructions use the B-type instruction format. The
1705
/// 12-bit B-immediate encodes signed offsets in multiples of 2, and
1706
/// is added to the current pc to give the target address. The
1707
/// conditional branch range is ±4 KiB.
1708
B12,
1709
1710
/// Equivalent to the `R_RISCV_PCREL_HI20` relocation, Allows setting
1711
/// the immediate field of an `auipc` instruction.
1712
PCRelHi20,
1713
1714
/// Similar to the `R_RISCV_PCREL_LO12_I` relocation but pointing to
1715
/// the final address, instead of the `PCREL_HI20` label. Allows setting
1716
/// the immediate field of I Type instructions such as `addi` or `lw`.
1717
///
1718
/// Since we currently don't support offsets in labels, this relocation has
1719
/// an implicit offset of 4.
1720
PCRelLo12I,
1721
1722
/// 11-bit PC-relative jump offset. Equivalent to the `RVC_JUMP` relocation
1723
RVCJump,
1724
}
1725
1726
impl MachInstLabelUse for LabelUse {
1727
/// Alignment for veneer code. Every Riscv64 instruction must be
1728
/// 4-byte-aligned.
1729
const ALIGN: CodeOffset = 4;
1730
1731
/// Maximum PC-relative range (positive), inclusive.
1732
fn max_pos_range(self) -> CodeOffset {
1733
match self {
1734
LabelUse::Jal20 => ((1 << 19) - 1) * 2,
1735
LabelUse::PCRelLo12I | LabelUse::PCRelHi20 | LabelUse::PCRel32 => {
1736
Inst::imm_max() as CodeOffset
1737
}
1738
LabelUse::B12 => ((1 << 11) - 1) * 2,
1739
LabelUse::RVCJump => ((1 << 10) - 1) * 2,
1740
}
1741
}
1742
1743
/// Maximum PC-relative range (negative).
1744
fn max_neg_range(self) -> CodeOffset {
1745
match self {
1746
LabelUse::PCRel32 => Inst::imm_min().abs() as CodeOffset,
1747
_ => self.max_pos_range() + 2,
1748
}
1749
}
1750
1751
/// Size of window into code needed to do the patch.
1752
fn patch_size(self) -> CodeOffset {
1753
match self {
1754
LabelUse::RVCJump => 2,
1755
LabelUse::Jal20 | LabelUse::B12 | LabelUse::PCRelHi20 | LabelUse::PCRelLo12I => 4,
1756
LabelUse::PCRel32 => 8,
1757
}
1758
}
1759
1760
/// Perform the patch.
1761
fn patch(self, buffer: &mut [u8], use_offset: CodeOffset, label_offset: CodeOffset) {
1762
assert!(use_offset % 2 == 0);
1763
assert!(label_offset % 2 == 0);
1764
let offset = (label_offset as i64) - (use_offset as i64);
1765
1766
// re-check range
1767
assert!(
1768
offset >= -(self.max_neg_range() as i64) && offset <= (self.max_pos_range() as i64),
1769
"{self:?} offset '{offset}' use_offset:'{use_offset}' label_offset:'{label_offset}' must not exceed max range.",
1770
);
1771
self.patch_raw_offset(buffer, offset);
1772
}
1773
1774
/// Is a veneer supported for this label reference type?
1775
fn supports_veneer(self) -> bool {
1776
match self {
1777
Self::Jal20 | Self::B12 | Self::RVCJump => true,
1778
_ => false,
1779
}
1780
}
1781
1782
/// How large is the veneer, if supported?
1783
fn veneer_size(self) -> CodeOffset {
1784
match self {
1785
Self::B12 | Self::Jal20 | Self::RVCJump => 8,
1786
_ => unreachable!(),
1787
}
1788
}
1789
1790
fn worst_case_veneer_size() -> CodeOffset {
1791
8
1792
}
1793
1794
/// Generate a veneer into the buffer, given that this veneer is at `veneer_offset`, and return
1795
/// an offset and label-use for the veneer's use of the original label.
1796
fn generate_veneer(
1797
self,
1798
buffer: &mut [u8],
1799
veneer_offset: CodeOffset,
1800
) -> (CodeOffset, LabelUse) {
1801
let base = writable_spilltmp_reg();
1802
{
1803
let x = enc_auipc(base, Imm20::ZERO).to_le_bytes();
1804
buffer[0] = x[0];
1805
buffer[1] = x[1];
1806
buffer[2] = x[2];
1807
buffer[3] = x[3];
1808
}
1809
{
1810
let x = enc_jalr(writable_zero_reg(), base.to_reg(), Imm12::ZERO).to_le_bytes();
1811
buffer[4] = x[0];
1812
buffer[5] = x[1];
1813
buffer[6] = x[2];
1814
buffer[7] = x[3];
1815
}
1816
(veneer_offset, Self::PCRel32)
1817
}
1818
1819
fn from_reloc(reloc: Reloc, addend: Addend) -> Option<LabelUse> {
1820
match (reloc, addend) {
1821
(Reloc::RiscvCallPlt, _) => Some(Self::PCRel32),
1822
_ => None,
1823
}
1824
}
1825
}
1826
1827
impl LabelUse {
1828
#[expect(dead_code, reason = "in case it's needed in the future")]
1829
fn offset_in_range(self, offset: i64) -> bool {
1830
let min = -(self.max_neg_range() as i64);
1831
let max = self.max_pos_range() as i64;
1832
offset >= min && offset <= max
1833
}
1834
1835
fn patch_raw_offset(self, buffer: &mut [u8], offset: i64) {
1836
let insn = match self {
1837
LabelUse::RVCJump => u16::from_le_bytes(buffer[..2].try_into().unwrap()) as u32,
1838
_ => u32::from_le_bytes(buffer[..4].try_into().unwrap()),
1839
};
1840
1841
match self {
1842
LabelUse::Jal20 => {
1843
let offset = offset as u32;
1844
let v = ((offset >> 12 & 0b1111_1111) << 12)
1845
| ((offset >> 11 & 0b1) << 20)
1846
| ((offset >> 1 & 0b11_1111_1111) << 21)
1847
| ((offset >> 20 & 0b1) << 31);
1848
buffer[0..4].clone_from_slice(&u32::to_le_bytes(insn | v));
1849
}
1850
LabelUse::PCRel32 => {
1851
let insn2 = u32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]);
1852
Inst::generate_imm(offset as u64)
1853
.map(|(imm20, imm12)| {
1854
// Encode the OR-ed-in value with zero_reg(). The
1855
// register parameter must be in the original
1856
// encoded instruction and or'ing in zeroes does not
1857
// change it.
1858
buffer[0..4].clone_from_slice(&u32::to_le_bytes(
1859
insn | enc_auipc(writable_zero_reg(), imm20),
1860
));
1861
buffer[4..8].clone_from_slice(&u32::to_le_bytes(
1862
insn2 | enc_jalr(writable_zero_reg(), zero_reg(), imm12),
1863
));
1864
})
1865
// expect make sure we handled.
1866
.expect("we have check the range before,this is a compiler error.");
1867
}
1868
1869
LabelUse::B12 => {
1870
let offset = offset as u32;
1871
let v = ((offset >> 11 & 0b1) << 7)
1872
| ((offset >> 1 & 0b1111) << 8)
1873
| ((offset >> 5 & 0b11_1111) << 25)
1874
| ((offset >> 12 & 0b1) << 31);
1875
buffer[0..4].clone_from_slice(&u32::to_le_bytes(insn | v));
1876
}
1877
1878
LabelUse::PCRelHi20 => {
1879
// See https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses
1880
//
1881
// We need to add 0x800 to ensure that we land at the next page as soon as it goes out of range for the
1882
// Lo12 relocation. That relocation is signed and has a maximum range of -2048..2047. So when we get an
1883
// offset of 2048, we need to land at the next page and subtract instead.
1884
let offset = offset as u32;
1885
let hi20 = offset.wrapping_add(0x800) >> 12;
1886
let insn = (insn & 0xFFF) | (hi20 << 12);
1887
buffer[0..4].clone_from_slice(&u32::to_le_bytes(insn));
1888
}
1889
1890
LabelUse::PCRelLo12I => {
1891
// `offset` is the offset from the current instruction to the target address.
1892
//
1893
// However we are trying to compute the offset to the target address from the previous instruction.
1894
// The previous instruction should be the one that contains the PCRelHi20 relocation and
1895
// stores/references the program counter (`auipc` usually).
1896
//
1897
// Since we are trying to compute the offset from the previous instruction, we can
1898
// represent it as offset = target_address - (current_instruction_address - 4)
1899
// which is equivalent to offset = target_address - current_instruction_address + 4.
1900
//
1901
// Thus we need to add 4 to the offset here.
1902
let lo12 = (offset + 4) as u32 & 0xFFF;
1903
let insn = (insn & 0xFFFFF) | (lo12 << 20);
1904
buffer[0..4].clone_from_slice(&u32::to_le_bytes(insn));
1905
}
1906
LabelUse::RVCJump => {
1907
debug_assert!(offset & 1 == 0);
1908
1909
// We currently only support this for the C.J operation, so assert that is the opcode in
1910
// the buffer.
1911
debug_assert_eq!(insn & 0xFFFF, 0xA001);
1912
1913
buffer[0..2].clone_from_slice(&u16::to_le_bytes(encode_cj_type(
1914
CjOp::CJ,
1915
Imm12::from_i16(i16::try_from(offset).unwrap()),
1916
)));
1917
}
1918
}
1919
}
1920
}
1921
1922
#[cfg(test)]
1923
mod test {
1924
use super::*;
1925
#[test]
1926
fn label_use_max_range() {
1927
assert!(LabelUse::B12.max_neg_range() == LabelUse::B12.max_pos_range() + 2);
1928
assert!(LabelUse::Jal20.max_neg_range() == LabelUse::Jal20.max_pos_range() + 2);
1929
assert!(LabelUse::PCRel32.max_pos_range() == (Inst::imm_max() as CodeOffset));
1930
assert!(LabelUse::PCRel32.max_neg_range() == (Inst::imm_min().abs() as CodeOffset));
1931
assert!(LabelUse::B12.max_pos_range() == ((1 << 11) - 1) * 2);
1932
}
1933
}
1934
1935