ab_riscv_primitives/instructions/rv32/c/zca.rs
1//! RV32 Zca extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::instructions::Instruction;
7use crate::instructions::utils::I24;
8use crate::registers::general_purpose::Register;
9use ab_riscv_macros::instruction;
10use core::fmt;
11
12/// RISC-V RV32 Zca compressed instruction set
13#[instruction]
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[rustfmt::skip]
16pub enum Rv32ZcaInstruction<Reg> {
17 // Quadrant 00
18 /// C.ADDI4SPN rd' = sp + nzuimm (nzuimm != 0)
19 CAddi4spn { rd: Reg, nzuimm: u16 },
20 /// C.LW rd' = sext(mem32\[rs1' + uimm])
21 CLw { rd: Reg, rs1: Reg, uimm: u8 },
22 /// C.SW mem32\[rs1' + uimm] = rs2'
23 CSw { rs1: Reg, rs2: Reg, uimm: u8 },
24
25 // Quadrant 01
26 /// C.NOP (ADDI x0, x0, 0 with rd==x0 and nzimm==0)
27 CNop,
28 /// C.ADDI rd += nzimm (rd != x0)
29 CAddi { rd: Reg, nzimm: i8 },
30 /// C.JAL ra = pc+2; pc += imm
31 CJal { imm: i16 },
32 /// C.LI rd = sext(imm) (rd=x0 is a HINT)
33 CLi { rd: Reg, imm: i8 },
34 /// C.ADDI16SP sp += nzimm (nzimm != 0)
35 CAddi16sp { nzimm: i16 },
36 /// C.LUI rd = sext(nzimm << 12) (rd != x0, rd != x2, nzimm != 0)
37 CLui { rd: Reg, nzimm: I24 },
38 /// C.SRLI rd' >>= shamt (logical, 5-bit shamt; shamt=0 is a HINT)
39 CSrli { rd: Reg, shamt: u8 },
40 /// C.SRAI rd' >>= shamt (arithmetic, 5-bit shamt; shamt=0 is a HINT)
41 CSrai { rd: Reg, shamt: u8 },
42 /// C.ANDI rd' &= sext(imm)
43 CAndi { rd: Reg, imm: i8 },
44 /// C.SUB rd' -= rs2'
45 CSub { rd: Reg, rs2: Reg },
46 /// C.XOR rd' ^= rs2'
47 CXor { rd: Reg, rs2: Reg },
48 /// C.OR rd' |= rs2'
49 COr { rd: Reg, rs2: Reg },
50 /// C.AND rd' &= rs2'
51 CAnd { rd: Reg, rs2: Reg },
52 /// C.J pc += sext(imm)
53 CJ { imm: i16 },
54 /// C.BEQZ if rs1' == 0: pc += sext(imm)
55 CBeqz { rs1: Reg, imm: i16 },
56 /// C.BNEZ if rs1' != 0: pc += sext(imm)
57 CBnez { rs1: Reg, imm: i16 },
58
59 // Quadrant 10
60 /// C.SLLI rd <<= shamt (5-bit shamt; rd=x0 or shamt=0 is a HINT)
61 CSlli { rd: Reg, shamt: u8 },
62 /// C.LWSP rd = sext(mem32\[sp + uimm]) (rd != x0)
63 CLwsp { rd: Reg, uimm: u8 },
64 /// C.JR pc = rs1 (rs1 != x0)
65 CJr { rs1: Reg },
66 /// C.MV rd = rs2 (rs2 != x0; rd=x0 is a HINT)
67 CMv { rd: Reg, rs2: Reg },
68 /// C.EBREAK
69 CEbreak,
70 /// C.JALR ra = pc+2; pc = rs1 (rs1 != x0)
71 CJalr { rs1: Reg },
72 /// C.ADD rd += rs2 (rs2 != x0; rd=x0 is a HINT)
73 CAdd { rd: Reg, rs2: Reg },
74 /// C.SWSP mem32\[sp + uimm] = rs2
75 CSwsp { rs2: Reg, uimm: u8 },
76
77 // Unimplemented/illegal
78 CUnimp,
79}
80
81#[instruction]
82impl<Reg> const Instruction for Rv32ZcaInstruction<Reg>
83where
84 Reg: [const] Register<Type = u32>,
85{
86 type Reg = Reg;
87
88 #[inline(always)]
89 fn try_decode(instruction: u32) -> Option<Self> {
90 /// Map a 3-bit "prime" register field to an absolute register number
91 #[inline(always)]
92 const fn prime_reg_bits(bits: u8) -> u8 {
93 bits + 8
94 }
95
96 /// Reconstruct the CB-type branch offset used by C.BEQZ / C.BNEZ.
97 ///
98 /// Bit layout in the 16-bit instruction word:
99 /// ```text
100 /// imm[8] = inst[12]
101 /// imm[4:3] = inst[11:10]
102 /// imm[7:6] = inst[6:5]
103 /// imm[2:1] = inst[4:3]
104 /// imm[5] = inst[2]
105 /// imm[0] is always 0 (2-byte aligned).
106 /// ```
107 #[inline(always)]
108 const fn decode_cb_branch_imm(inst: u16) -> i16 {
109 let imm8 = ((inst >> 12) & 1).cast_signed();
110 let imm4_3 = ((inst >> 10) & 0b11).cast_signed();
111 let imm7_6 = ((inst >> 5) & 0b11).cast_signed();
112 let imm2_1 = ((inst >> 3) & 0b11).cast_signed();
113 let imm5 = ((inst >> 2) & 1).cast_signed();
114 let raw = (imm8 << 8) | (imm7_6 << 6) | (imm5 << 5) | (imm4_3 << 3) | (imm2_1 << 1);
115 // Sign-extend from bit 8 (9-bit immediate -> i16)
116 (raw << 7) >> 7
117 }
118
119 /// Decode CJ-type jump offset (C.J / C.JAL).
120 ///
121 /// Bit layout:
122 /// ```text
123 /// imm[11] = inst[12]
124 /// imm[4] = inst[11]
125 /// imm[9:8] = inst[10:9]
126 /// imm[10] = inst[8]
127 /// imm[6] = inst[7]
128 /// imm[7] = inst[6]
129 /// imm[3:1] = inst[5:3]
130 /// imm[5] = inst[2]
131 /// imm[0] is always 0 (2-byte aligned).
132 /// ```
133 #[inline(always)]
134 const fn decode_cj_imm(inst: u16) -> i16 {
135 let imm11 = ((inst >> 12) & 1).cast_signed();
136 let imm4 = ((inst >> 11) & 1).cast_signed();
137 let imm9_8 = ((inst >> 9) & 0b11).cast_signed();
138 let imm10 = ((inst >> 8) & 1).cast_signed();
139 let imm6 = ((inst >> 7) & 1).cast_signed();
140 let imm7 = ((inst >> 6) & 1).cast_signed();
141 let imm3_1 = ((inst >> 3) & 0b111).cast_signed();
142 let imm5 = ((inst >> 2) & 1).cast_signed();
143 let raw = (imm11 << 11)
144 | (imm10 << 10)
145 | (imm9_8 << 8)
146 | (imm7 << 7)
147 | (imm6 << 6)
148 | (imm5 << 5)
149 | (imm4 << 4)
150 | (imm3_1 << 1);
151 // Sign-extend from bit 11 (12-bit immediate -> i16)
152 (raw << 4) >> 4
153 }
154
155 let inst = instruction as u16;
156 let quadrant = inst & 0b11;
157 let funct3 = ((inst >> 13) & 0b111) as u8;
158
159 match quadrant {
160 // Quadrant 00
161 0b00 => match funct3 {
162 // C.ADDI4SPN
163 // nzuimm[5:4] = inst[12:11]
164 // nzuimm[9:6] = inst[10:7]
165 // nzuimm[2] = inst[6]
166 // nzuimm[3] = inst[5]
167 0b000 => {
168 let imm5_4 = (inst >> 11) & 0b11;
169 let imm9_6 = (inst >> 7) & 0xf;
170 let imm2 = (inst >> 6) & 1;
171 let imm3 = (inst >> 5) & 1;
172 let nzuimm = (imm9_6 << 6) | (imm5_4 << 4) | (imm3 << 3) | (imm2 << 2);
173 if nzuimm == 0 {
174 if inst == 0 {
175 Some(Self::CUnimp)
176 } else {
177 // Reserved encoding
178 None
179 }
180 } else {
181 let rd_bits = prime_reg_bits(((inst >> 2) & 0b111) as u8);
182 let rd = Reg::from_bits(rd_bits)?;
183 Some(Self::CAddi4spn { rd, nzuimm })
184 }
185 }
186 // C.LW
187 // uimm[5:3] = inst[12:10], uimm[2] = inst[6], uimm[6] = inst[5]
188 0b010 => {
189 let uimm5_3 = ((inst >> 10) & 0b111) as u8;
190 let uimm2 = ((inst >> 6) & 1) as u8;
191 let uimm6 = ((inst >> 5) & 1) as u8;
192 let uimm = (uimm6 << 6) | (uimm5_3 << 3) | (uimm2 << 2);
193 let rs1_bits = prime_reg_bits(((inst >> 7) & 0b111) as u8);
194 let rd_bits = prime_reg_bits(((inst >> 2) & 0b111) as u8);
195 let rs1 = Reg::from_bits(rs1_bits)?;
196 let rd = Reg::from_bits(rd_bits)?;
197 Some(Self::CLw { rd, rs1, uimm })
198 }
199 // C.SW (same uimm layout as C.LW)
200 0b110 => {
201 let uimm5_3 = ((inst >> 10) & 0b111) as u8;
202 let uimm2 = ((inst >> 6) & 1) as u8;
203 let uimm6 = ((inst >> 5) & 1) as u8;
204 let uimm = (uimm6 << 6) | (uimm5_3 << 3) | (uimm2 << 2);
205 let rs1_bits = prime_reg_bits(((inst >> 7) & 0b111) as u8);
206 let rs2_bits = prime_reg_bits(((inst >> 2) & 0b111) as u8);
207 let rs1 = Reg::from_bits(rs1_bits)?;
208 let rs2 = Reg::from_bits(rs2_bits)?;
209 Some(Self::CSw { rs1, rs2, uimm })
210 }
211 // funct3=001: C.FLD (Zcd) - not in Zca, reserved
212 // funct3=011: C.FLD (Zcd) - not in Zca, reserved
213 // funct3=100: used by Zcb
214 // funct3=101: C.FSD (Zcd) - not in Zca, reserved
215 // funct3=111: C.FSD (Zcd) - not in Zca, reserved
216 _ => None,
217 },
218
219 // Quadrant 01
220 0b01 => match funct3 {
221 // C.NOP (rd=x0) / C.ADDI (rd!=x0)
222 // nzimm[5] = inst[12], nzimm[4:0] = inst[6:2]
223 0b000 => {
224 let rd_bits = ((inst >> 7) & 0x1f) as u8;
225 let imm5 = ((inst >> 12) & 1) as u8;
226 let imm4_0 = ((inst >> 2) & 0x1f) as u8;
227 let imm_raw = (imm5 << 5) | imm4_0;
228 // Sign-extend 6-bit immediate to i8
229 let nzimm = ((imm_raw.cast_signed()) << 2) >> 2;
230 if rd_bits == 0 && nzimm == 0 {
231 Some(Self::CNop)
232 } else {
233 let rd = Reg::from_bits(rd_bits)?;
234 Some(Self::CAddi { rd, nzimm })
235 }
236 }
237 // C.JAL (same CJ immediate encoding as C.J)
238 0b001 => Some(Self::CJal {
239 imm: decode_cj_imm(inst),
240 }),
241 // C.LI rd = sext(imm) (rd=x0 is a HINT, still decoded)
242 // imm[5] = inst[12], imm[4:0] = inst[6:2]
243 0b010 => {
244 let rd_bits = ((inst >> 7) & 0x1f) as u8;
245 let rd = Reg::from_bits(rd_bits)?;
246 let imm5 = ((inst >> 12) & 1) as u8;
247 let imm4_0 = ((inst >> 2) & 0x1f) as u8;
248 let imm_raw = (imm5 << 5) | imm4_0;
249 let imm = ((imm_raw.cast_signed()) << 2) >> 2;
250 Some(Self::CLi { rd, imm })
251 }
252 // C.ADDI16SP (rd=x2) / C.LUI (rd!=x0, rd!=x2)
253 0b011 => {
254 let rd_bits = ((inst >> 7) & 0x1f) as u8;
255 if rd_bits == 2 {
256 // C.ADDI16SP
257 // nzimm[9] = inst[12]
258 // nzimm[4] = inst[6]
259 // nzimm[6] = inst[5]
260 // nzimm[8:7] = inst[4:3]
261 // nzimm[5] = inst[2]
262 let imm9 = ((inst >> 12) & 1).cast_signed();
263 let imm4 = ((inst >> 6) & 1).cast_signed();
264 let imm6 = ((inst >> 5) & 1).cast_signed();
265 let imm8_7 = ((inst >> 3) & 0b11).cast_signed();
266 let imm5 = ((inst >> 2) & 1).cast_signed();
267 let raw =
268 (imm9 << 9) | (imm8_7 << 7) | (imm6 << 6) | (imm5 << 5) | (imm4 << 4);
269 if raw == 0 {
270 None?;
271 }
272 // Sign-extend from bit 9 (10-bit nzimm -> i16)
273 let nzimm = (raw << 6) >> 6;
274 Some(Self::CAddi16sp { nzimm })
275 } else {
276 // C.LUI (rd=x0 is a hint, still decoded)
277 let rd = Reg::from_bits(rd_bits)?;
278 // nzimm[17] = inst[12]
279 // nzimm[16:12] = inst[6:2]
280 let imm17 = ((inst >> 12) & 1) as i32;
281 let imm16_12 = ((inst >> 2) & 0x1f) as i32;
282 let raw = (imm17 << 17) | (imm16_12 << 12);
283 if raw == 0 {
284 None?;
285 }
286 // Sign-extend from bit 17 (18-bit nzimm -> i32)
287 let nzimm = I24::from_i32((raw << 14) >> 14);
288 Some(Self::CLui { rd, nzimm })
289 }
290 }
291 // C.SRLI / C.SRAI / C.ANDI / arithmetic
292 // RV32: shamt is 5-bit only (inst[12] must be 0 for shifts, else reserved)
293 0b100 => {
294 let funct2 = ((inst >> 10) & 0b11) as u8;
295 let rd_bits = prime_reg_bits(((inst >> 7) & 0b111) as u8);
296 match funct2 {
297 // C.SRLI shamt[4:0]=inst[6:2]
298 // RV32: shamt[5]=inst[12] must be 0, else reserved (NSE)
299 // shamt=0 is a HINT, still decoded
300 0b00 => {
301 let rd = Reg::from_bits(rd_bits)?;
302 let shamt5 = ((inst >> 12) & 1) as u8;
303 let shamt40 = ((inst >> 2) & 0x1f) as u8;
304 if shamt5 != 0 {
305 None?;
306 }
307 Some(Self::CSrli { rd, shamt: shamt40 })
308 }
309 // C.SRAI (same shamt layout as C.SRLI)
310 // RV32: shamt[5]=inst[12] must be 0, else reserved (NSE)
311 // shamt=0 is a HINT, still decoded
312 0b01 => {
313 let rd = Reg::from_bits(rd_bits)?;
314 let shamt5 = ((inst >> 12) & 1) as u8;
315 let shamt40 = ((inst >> 2) & 0x1f) as u8;
316 if shamt5 != 0 {
317 None?;
318 }
319 Some(Self::CSrai { rd, shamt: shamt40 })
320 }
321 // C.ANDI imm[5]=inst[12], imm[4:0]=inst[6:2]
322 0b10 => {
323 let rd = Reg::from_bits(rd_bits)?;
324 let imm5 = ((inst >> 12) & 1) as u8;
325 let imm4_0 = ((inst >> 2) & 0x1f) as u8;
326 let imm_raw = (imm5 << 5) | imm4_0;
327 let imm = ((imm_raw.cast_signed()) << 2) >> 2;
328 Some(Self::CAndi { rd, imm })
329 }
330 // Arithmetic: only bit12=0 variants valid in RV32
331 // bit12=1 (C.SUBW/C.ADDW) does not exist in RV32, reserved
332 0b11 => {
333 let bit12 = (inst >> 12) & 1;
334 if bit12 != 0 {
335 None?;
336 }
337 let funct2b = ((inst >> 5) & 0b11) as u8;
338 let rs2_bits = prime_reg_bits(((inst >> 2) & 0b111) as u8);
339 let rd = Reg::from_bits(rd_bits)?;
340 let rs2 = Reg::from_bits(rs2_bits)?;
341 match funct2b {
342 0b00 => Some(Self::CSub { rd, rs2 }),
343 0b01 => Some(Self::CXor { rd, rs2 }),
344 0b10 => Some(Self::COr { rd, rs2 }),
345 0b11 => Some(Self::CAnd { rd, rs2 }),
346 _ => None,
347 }
348 }
349 _ => None,
350 }
351 }
352 // C.J
353 0b101 => Some(Self::CJ {
354 imm: decode_cj_imm(inst),
355 }),
356 // C.BEQZ
357 0b110 => {
358 let rs1_bits = prime_reg_bits(((inst >> 7) & 0b111) as u8);
359 let rs1 = Reg::from_bits(rs1_bits)?;
360 Some(Self::CBeqz {
361 rs1,
362 imm: decode_cb_branch_imm(inst),
363 })
364 }
365 // C.BNEZ
366 0b111 => {
367 let rs1_bits = prime_reg_bits(((inst >> 7) & 0b111) as u8);
368 let rs1 = Reg::from_bits(rs1_bits)?;
369 Some(Self::CBnez {
370 rs1,
371 imm: decode_cb_branch_imm(inst),
372 })
373 }
374 _ => None,
375 },
376
377 // Quadrant 10
378 0b10 => match funct3 {
379 // C.SLLI shamt[4:0]=inst[6:2]
380 // RV32: shamt[5]=inst[12] must be 0, else reserved (NSE)
381 // rd=x0 or shamt=0 is a HINT, still decoded
382 0b000 => {
383 let rd_bits = ((inst >> 7) & 0x1f) as u8;
384 let rd = Reg::from_bits(rd_bits)?;
385 let shamt5 = ((inst >> 12) & 1) as u8;
386 let shamt40 = ((inst >> 2) & 0x1f) as u8;
387 if shamt5 != 0 {
388 None?;
389 }
390 Some(Self::CSlli { rd, shamt: shamt40 })
391 }
392 // C.LWSP uimm[5]=inst[12], uimm[4:2]=inst[6:4], uimm[7:6]=inst[3:2]
393 // rd=x0 is reserved
394 0b010 => {
395 let rd_bits = ((inst >> 7) & 0x1f) as u8;
396 if rd_bits == 0 {
397 None?;
398 }
399 let rd = Reg::from_bits(rd_bits)?;
400 let uimm5 = ((inst >> 12) & 1) as u8;
401 let uimm42 = ((inst >> 4) & 0b111) as u8;
402 let uimm76 = ((inst >> 2) & 0b11) as u8;
403 let uimm = (uimm76 << 6) | (uimm5 << 5) | (uimm42 << 2);
404 Some(Self::CLwsp { rd, uimm })
405 }
406 // funct3=001: C.FLWSP (Zcf, not Zca) - reserved
407 // funct3=011: C.FLDSP (Zcd, not Zca) - reserved
408 // C.JR / C.MV / C.EBREAK / C.JALR / C.ADD
409 0b100 => {
410 let rs1_bits = ((inst >> 7) & 0x1f) as u8;
411 let rs2_bits = ((inst >> 2) & 0x1f) as u8;
412 let bit12 = (inst >> 12) & 1;
413 if bit12 == 0 {
414 if rs2_bits == 0 {
415 // C.JR (rs1=x0 is reserved)
416 if rs1_bits == 0 {
417 None?;
418 }
419 let rs1 = Reg::from_bits(rs1_bits)?;
420 Some(Self::CJr { rs1 })
421 } else {
422 // C.MV (rs2!=x0; rd=x0 is a HINT, still decoded)
423 let rd = Reg::from_bits(rs1_bits)?;
424 let rs2 = Reg::from_bits(rs2_bits)?;
425 Some(Self::CMv { rd, rs2 })
426 }
427 } else if rs2_bits == 0 {
428 if rs1_bits == 0 {
429 // C.EBREAK
430 Some(Self::CEbreak)
431 } else {
432 // C.JALR (rs1!=x0)
433 let rs1 = Reg::from_bits(rs1_bits)?;
434 Some(Self::CJalr { rs1 })
435 }
436 } else {
437 // C.ADD (rs2!=x0; rd=x0 is a HINT, still decoded)
438 let rd = Reg::from_bits(rs1_bits)?;
439 let rs2 = Reg::from_bits(rs2_bits)?;
440 Some(Self::CAdd { rd, rs2 })
441 }
442 }
443 // C.SWSP uimm[5:2]=inst[12:9], uimm[7:6]=inst[8:7]
444 0b110 => {
445 let rs2_bits = ((inst >> 2) & 0x1f) as u8;
446 let rs2 = Reg::from_bits(rs2_bits)?;
447 let uimm52 = ((inst >> 9) & 0xf) as u8;
448 let uimm76 = ((inst >> 7) & 0b11) as u8;
449 let uimm = (uimm76 << 6) | (uimm52 << 2);
450 Some(Self::CSwsp { rs2, uimm })
451 }
452 // funct3=111: C.FSWSP (Zcf, not Zca) - reserved
453 _ => None,
454 },
455
456 // Quadrant 11 = 32-bit instructions
457 _ => None,
458 }
459 }
460
461 #[inline(always)]
462 fn alignment() -> u8 {
463 align_of::<u16>() as u8
464 }
465
466 #[inline(always)]
467 fn size(&self) -> u8 {
468 size_of::<u16>() as u8
469 }
470}
471
472#[instruction]
473impl<Reg> fmt::Display for Rv32ZcaInstruction<Reg>
474where
475 Reg: fmt::Display,
476{
477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 match self {
479 Self::CAddi4spn { rd, nzuimm } => write!(f, "c.addi4spn {rd}, sp, {nzuimm}"),
480 Self::CLw { rd, rs1, uimm } => write!(f, "c.lw {rd}, {uimm}({rs1})"),
481 Self::CSw { rs1, rs2, uimm } => write!(f, "c.sw {rs2}, {uimm}({rs1})"),
482 Self::CNop => write!(f, "c.nop"),
483 Self::CAddi { rd, nzimm } => write!(f, "c.addi {rd}, {nzimm}"),
484 Self::CJal { imm } => write!(f, "c.jal {imm}"),
485 Self::CLi { rd, imm } => write!(f, "c.li {rd}, {imm}"),
486 Self::CAddi16sp { nzimm } => write!(f, "c.addi16sp sp, {nzimm}"),
487 Self::CLui { rd, nzimm } => write!(f, "c.lui {rd}, 0x{:x}", nzimm >> 12),
488 Self::CSrli { rd, shamt } => write!(f, "c.srli {rd}, {shamt}"),
489 Self::CSrai { rd, shamt } => write!(f, "c.srai {rd}, {shamt}"),
490 Self::CAndi { rd, imm } => write!(f, "c.andi {rd}, {imm}"),
491 Self::CSub { rd, rs2 } => write!(f, "c.sub {rd}, {rs2}"),
492 Self::CXor { rd, rs2 } => write!(f, "c.xor {rd}, {rs2}"),
493 Self::COr { rd, rs2 } => write!(f, "c.or {rd}, {rs2}"),
494 Self::CAnd { rd, rs2 } => write!(f, "c.and {rd}, {rs2}"),
495 Self::CJ { imm } => write!(f, "c.j {imm}"),
496 Self::CBeqz { rs1, imm } => write!(f, "c.beqz {rs1}, {imm}"),
497 Self::CBnez { rs1, imm } => write!(f, "c.bnez {rs1}, {imm}"),
498 Self::CSlli { rd, shamt } => write!(f, "c.slli {rd}, {shamt}"),
499 Self::CLwsp { rd, uimm } => write!(f, "c.lwsp {rd}, {uimm}(sp)"),
500 Self::CJr { rs1 } => write!(f, "c.jr {rs1}"),
501 Self::CMv { rd, rs2 } => write!(f, "c.mv {rd}, {rs2}"),
502 Self::CEbreak => write!(f, "c.ebreak"),
503 Self::CJalr { rs1 } => write!(f, "c.jalr {rs1}"),
504 Self::CAdd { rd, rs2 } => write!(f, "c.add {rd}, {rs2}"),
505 Self::CSwsp { rs2, uimm } => write!(f, "c.swsp {rs2}, {uimm}(sp)"),
506 Self::CUnimp => write!(f, "c.unimp"),
507 }
508 }
509}