Skip to main content

logicaffeine_compile/codegen_sva/hart/
rvc.rs

1//! The RV64C decompressor: every 16-bit instruction is a shorthand for one 32-bit instruction, and the hart executes
2//! the expansion. [`expand`] returns that 32-bit instruction, or `None` for an encoding the C extension reserves
3//! (an illegal instruction). HINT encodings expand to the base instruction whose destination is `x0` or whose effect
4//! is nil, as the specification defines them. The hart's RTL expander is held to this function over all 65,536
5//! encodings, and this function is held to the GNU disassembler.
6
7fn bit(c: u16, i: u32) -> u32 {
8    ((c >> i) & 1) as u32
9}
10
11fn bits(c: u16, hi: u32, lo: u32) -> u32 {
12    ((c as u32) >> lo) & ((1 << (hi - lo + 1)) - 1)
13}
14
15/// The register `x8 + n` a 3-bit compressed register field `n` names.
16fn creg(n: u32) -> u32 {
17    8 + n
18}
19
20fn sext(v: u32, width: u32) -> i32 {
21    ((v << (32 - width)) as i32) >> (32 - width)
22}
23
24fn r_type(funct7: u32, rs2: u32, rs1: u32, funct3: u32, rd: u32, opcode: u32) -> u32 {
25    (funct7 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | opcode
26}
27
28fn i_type(imm: i32, rs1: u32, funct3: u32, rd: u32, opcode: u32) -> u32 {
29    (((imm as u32) & 0xfff) << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | opcode
30}
31
32fn s_type(imm: i32, rs2: u32, rs1: u32, funct3: u32, opcode: u32) -> u32 {
33    let imm = imm as u32;
34    (((imm >> 5) & 0x7f) << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) | ((imm & 0x1f) << 7) | opcode
35}
36
37fn b_type(imm: i32, rs2: u32, rs1: u32, funct3: u32, opcode: u32) -> u32 {
38    let imm = imm as u32;
39    (((imm >> 12) & 1) << 31)
40        | (((imm >> 5) & 0x3f) << 25)
41        | (rs2 << 20)
42        | (rs1 << 15)
43        | (funct3 << 12)
44        | (((imm >> 1) & 0xf) << 8)
45        | (((imm >> 11) & 1) << 7)
46        | opcode
47}
48
49fn u_type(imm20: u32, rd: u32, opcode: u32) -> u32 {
50    ((imm20 & 0xfffff) << 12) | (rd << 7) | opcode
51}
52
53fn j_type(imm: i32, rd: u32, opcode: u32) -> u32 {
54    let imm = imm as u32;
55    (((imm >> 20) & 1) << 31)
56        | (((imm >> 1) & 0x3ff) << 21)
57        | (((imm >> 11) & 1) << 20)
58        | (((imm >> 12) & 0xff) << 12)
59        | (rd << 7)
60        | opcode
61}
62
63const OP_LOAD: u32 = 0x03;
64const OP_LOAD_FP: u32 = 0x07;
65const OP_IMM: u32 = 0x13;
66const OP_IMM_32: u32 = 0x1b;
67const OP_STORE: u32 = 0x23;
68const OP_STORE_FP: u32 = 0x27;
69const OP: u32 = 0x33;
70const OP_LUI: u32 = 0x37;
71const OP_32: u32 = 0x3b;
72const OP_BRANCH: u32 = 0x63;
73const OP_JALR: u32 = 0x67;
74const OP_JAL: u32 = 0x6f;
75const OP_SYSTEM: u32 = 0x73;
76
77/// Whether the low two bits mark a 16-bit (compressed) instruction.
78pub fn is_compressed(low_half: u16) -> bool {
79    low_half & 3 != 3
80}
81
82/// The 32-bit instruction the compressed instruction `c` stands for, or `None` for a reserved encoding.
83pub fn expand(c: u16) -> Option<u32> {
84    let funct3 = bits(c, 15, 13);
85    let rd = bits(c, 11, 7);
86    let rs2 = bits(c, 6, 2);
87    let rdp = creg(bits(c, 4, 2));
88    let rs1p = creg(bits(c, 9, 7));
89    let imm6 = sext((bit(c, 12) << 5) | bits(c, 6, 2), 6);
90    match c & 3 {
91        0 => match funct3 {
92            0 => {
93                // C.ADDI4SPN: addi rd', x2, nzuimm
94                let nzuimm =
95                    (bits(c, 12, 11) << 4) | (bits(c, 10, 7) << 6) | (bit(c, 6) << 2) | (bit(c, 5) << 3);
96                (nzuimm != 0).then(|| i_type(nzuimm as i32, 2, 0, rdp, OP_IMM))
97            }
98            1 => {
99                // C.FLD
100                let uimm = (bits(c, 12, 10) << 3) | (bits(c, 6, 5) << 6);
101                Some(i_type(uimm as i32, rs1p, 3, rdp, OP_LOAD_FP))
102            }
103            2 => {
104                // C.LW
105                let uimm = (bits(c, 12, 10) << 3) | (bit(c, 6) << 2) | (bit(c, 5) << 6);
106                Some(i_type(uimm as i32, rs1p, 2, rdp, OP_LOAD))
107            }
108            3 => {
109                // C.LD
110                let uimm = (bits(c, 12, 10) << 3) | (bits(c, 6, 5) << 6);
111                Some(i_type(uimm as i32, rs1p, 3, rdp, OP_LOAD))
112            }
113            4 => None,
114            5 => {
115                // C.FSD
116                let uimm = (bits(c, 12, 10) << 3) | (bits(c, 6, 5) << 6);
117                Some(s_type(uimm as i32, rdp, rs1p, 3, OP_STORE_FP))
118            }
119            6 => {
120                // C.SW
121                let uimm = (bits(c, 12, 10) << 3) | (bit(c, 6) << 2) | (bit(c, 5) << 6);
122                Some(s_type(uimm as i32, rdp, rs1p, 2, OP_STORE))
123            }
124            _ => {
125                // C.SD
126                let uimm = (bits(c, 12, 10) << 3) | (bits(c, 6, 5) << 6);
127                Some(s_type(uimm as i32, rdp, rs1p, 3, OP_STORE))
128            }
129        },
130        1 => match funct3 {
131            // C.ADDI (rd = 0: C.NOP and its HINTs)
132            0 => Some(i_type(imm6, rd, 0, rd, OP_IMM)),
133            // C.ADDIW (rd = 0 reserved)
134            1 => (rd != 0).then(|| i_type(imm6, rd, 0, rd, OP_IMM_32)),
135            // C.LI
136            2 => Some(i_type(imm6, 0, 0, rd, OP_IMM)),
137            3 if rd == 2 => {
138                // C.ADDI16SP
139                let nzimm = (bit(c, 12) << 9)
140                    | (bit(c, 6) << 4)
141                    | (bit(c, 5) << 6)
142                    | (bits(c, 4, 3) << 7)
143                    | (bit(c, 2) << 5);
144                (nzimm != 0).then(|| i_type(sext(nzimm, 10), 2, 0, 2, OP_IMM))
145            }
146            3 => {
147                // C.LUI
148                let nzimm = (bit(c, 12) << 5) | bits(c, 6, 2);
149                (nzimm != 0).then(|| u_type(sext(nzimm, 6) as u32, rd, OP_LUI))
150            }
151            4 => {
152                let shamt = (bit(c, 12) << 5) | bits(c, 6, 2);
153                match bits(c, 11, 10) {
154                    // C.SRLI
155                    0 => Some(i_type(shamt as i32, rs1p, 5, rs1p, OP_IMM)),
156                    // C.SRAI
157                    1 => Some(i_type((0x400 | shamt) as i32, rs1p, 5, rs1p, OP_IMM)),
158                    // C.ANDI
159                    2 => Some(i_type(imm6, rs1p, 7, rs1p, OP_IMM)),
160                    _ => {
161                        let rs2p = creg(bits(c, 4, 2));
162                        match (bit(c, 12), bits(c, 6, 5)) {
163                            (0, 0) => Some(r_type(0x20, rs2p, rs1p, 0, rs1p, OP)), // C.SUB
164                            (0, 1) => Some(r_type(0, rs2p, rs1p, 4, rs1p, OP)),    // C.XOR
165                            (0, 2) => Some(r_type(0, rs2p, rs1p, 6, rs1p, OP)),    // C.OR
166                            (0, _) => Some(r_type(0, rs2p, rs1p, 7, rs1p, OP)),    // C.AND
167                            (_, 0) => Some(r_type(0x20, rs2p, rs1p, 0, rs1p, OP_32)), // C.SUBW
168                            (_, 1) => Some(r_type(0, rs2p, rs1p, 0, rs1p, OP_32)), // C.ADDW
169                            _ => None,
170                        }
171                    }
172                }
173            }
174            5 => {
175                // C.J
176                let imm = (bit(c, 12) << 11)
177                    | (bit(c, 11) << 4)
178                    | (bits(c, 10, 9) << 8)
179                    | (bit(c, 8) << 10)
180                    | (bit(c, 7) << 6)
181                    | (bit(c, 6) << 7)
182                    | (bits(c, 5, 3) << 1)
183                    | (bit(c, 2) << 5);
184                Some(j_type(sext(imm, 12), 0, OP_JAL))
185            }
186            _ => {
187                // C.BEQZ / C.BNEZ
188                let imm = (bit(c, 12) << 8)
189                    | (bits(c, 11, 10) << 3)
190                    | (bits(c, 6, 5) << 6)
191                    | (bits(c, 4, 3) << 1)
192                    | (bit(c, 2) << 5);
193                Some(b_type(sext(imm, 9), 0, rs1p, if funct3 == 6 { 0 } else { 1 }, OP_BRANCH))
194            }
195        },
196        2 => match funct3 {
197            // C.SLLI
198            0 => {
199                let shamt = (bit(c, 12) << 5) | bits(c, 6, 2);
200                Some(i_type(shamt as i32, rd, 1, rd, OP_IMM))
201            }
202            1 => {
203                // C.FLDSP
204                let uimm = (bit(c, 12) << 5) | (bits(c, 6, 5) << 3) | (bits(c, 4, 2) << 6);
205                Some(i_type(uimm as i32, 2, 3, rd, OP_LOAD_FP))
206            }
207            2 => {
208                // C.LWSP (rd = 0 reserved)
209                let uimm = (bit(c, 12) << 5) | (bits(c, 6, 4) << 2) | (bits(c, 3, 2) << 6);
210                (rd != 0).then(|| i_type(uimm as i32, 2, 2, rd, OP_LOAD))
211            }
212            3 => {
213                // C.LDSP (rd = 0 reserved)
214                let uimm = (bit(c, 12) << 5) | (bits(c, 6, 5) << 3) | (bits(c, 4, 2) << 6);
215                (rd != 0).then(|| i_type(uimm as i32, 2, 3, rd, OP_LOAD))
216            }
217            4 => match (bit(c, 12), rd, rs2) {
218                (0, 0, 0) => None,                                   // C.JR with rs1 = 0 is reserved
219                (0, _, 0) => Some(i_type(0, rd, 0, 0, OP_JALR)),     // C.JR
220                (0, _, _) => Some(r_type(0, rs2, 0, 0, rd, OP)),     // C.MV
221                (_, 0, 0) => Some(i_type(1, 0, 0, 0, OP_SYSTEM)),    // C.EBREAK
222                (_, _, 0) => Some(i_type(0, rd, 0, 1, OP_JALR)),     // C.JALR
223                _ => Some(r_type(0, rs2, rd, 0, rd, OP)),            // C.ADD
224            },
225            5 => {
226                // C.FSDSP
227                let uimm = (bits(c, 12, 10) << 3) | (bits(c, 9, 7) << 6);
228                Some(s_type(uimm as i32, rs2, 2, 3, OP_STORE_FP))
229            }
230            6 => {
231                // C.SWSP
232                let uimm = (bits(c, 12, 9) << 2) | (bits(c, 8, 7) << 6);
233                Some(s_type(uimm as i32, rs2, 2, 2, OP_STORE))
234            }
235            _ => {
236                // C.SDSP
237                let uimm = (bits(c, 12, 10) << 3) | (bits(c, 9, 7) << 6);
238                Some(s_type(uimm as i32, rs2, 2, 3, OP_STORE))
239            }
240        },
241        _ => None,
242    }
243}