1pub fn enc(words: &[u32]) -> Vec<u8> {
12 let mut v = Vec::with_capacity(words.len() * 4);
13 for w in words {
14 v.extend_from_slice(&w.to_le_bytes());
15 }
16 v
17}
18
19#[inline]
22fn r(opcode: u32, funct7: u32, funct3: u32, rd: u8, rs1: u8, rs2: u8) -> u32 {
23 (funct7 << 25)
24 | ((rs2 as u32) << 20)
25 | ((rs1 as u32) << 15)
26 | (funct3 << 12)
27 | ((rd as u32) << 7)
28 | opcode
29}
30
31#[inline]
32fn i(opcode: u32, funct3: u32, rd: u8, rs1: u8, imm: i32) -> u32 {
33 ((imm as u32 & 0xFFF) << 20)
34 | ((rs1 as u32) << 15)
35 | (funct3 << 12)
36 | ((rd as u32) << 7)
37 | opcode
38}
39
40#[inline]
41fn s(opcode: u32, funct3: u32, rs1: u8, rs2: u8, imm: i32) -> u32 {
42 let u = imm as u32;
43 (((u >> 5) & 0x7F) << 25)
44 | ((rs2 as u32) << 20)
45 | ((rs1 as u32) << 15)
46 | (funct3 << 12)
47 | ((u & 0x1F) << 7)
48 | opcode
49}
50
51#[inline]
52fn b_(opcode: u32, funct3: u32, rs1: u8, rs2: u8, imm: i32) -> u32 {
53 let u = imm as u32;
54 (((u >> 12) & 1) << 31)
55 | (((u >> 5) & 0x3F) << 25)
56 | ((rs2 as u32) << 20)
57 | ((rs1 as u32) << 15)
58 | (funct3 << 12)
59 | (((u >> 1) & 0xF) << 8)
60 | (((u >> 11) & 1) << 7)
61 | opcode
62}
63
64#[inline]
65fn u_(opcode: u32, rd: u8, imm20: u32) -> u32 {
66 ((imm20 & 0xFFFFF) << 12) | ((rd as u32) << 7) | opcode
67}
68
69#[inline]
71fn i_shift64(opcode: u32, funct3: u32, funct6: u32, rd: u8, rs1: u8, shamt: u8) -> u32 {
72 let imm = ((funct6 & 0x3F) << 6) | (shamt as u32 & 0x3F);
73 i(opcode, funct3, rd, rs1, imm as i32)
74}
75
76#[inline]
78fn i_shift32(opcode: u32, funct3: u32, funct7: u32, rd: u8, rs1: u8, shamt: u8) -> u32 {
79 let imm = ((funct7 & 0x7F) << 5) | (shamt as u32 & 0x1F);
80 i(opcode, funct3, rd, rs1, imm as i32)
81}
82
83const OP: u32 = 0x33; const OP_IMM: u32 = 0x13; const OP_IMM_32: u32 = 0x1B; const OP_32: u32 = 0x3B; const LOAD: u32 = 0x03;
89const STORE: u32 = 0x23;
90const BRANCH: u32 = 0x63;
91const LUI: u32 = 0x37;
92const AUIPC: u32 = 0x17;
93
94#[derive(Clone, Copy, Debug, PartialEq, Eq)]
96pub enum Fmt {
97 R,
99 I,
101 IShift,
103 IShift32,
105 Store,
107 Branch,
109 U,
111 Unary,
113}
114
115#[derive(Clone, Copy, Debug)]
117pub struct OpSpec {
118 pub name: &'static str,
119 pub fmt: Fmt,
120 pub opcode: u32,
121 pub funct3: u32,
122 pub aux: u32,
124}
125
126const fn op(name: &'static str, fmt: Fmt, opcode: u32, funct3: u32, aux: u32) -> OpSpec {
127 OpSpec {
128 name,
129 fmt,
130 opcode,
131 funct3,
132 aux,
133 }
134}
135
136impl OpSpec {
137 pub fn touches_memory_or_control(&self) -> bool {
142 self.opcode == LOAD || matches!(self.fmt, Fmt::Store | Fmt::Branch)
143 }
144}
145
146#[rustfmt::skip]
151pub const OPS: &[OpSpec] = &[
152 op("add", Fmt::R, OP, 0, 0x00), op("sub", Fmt::R, OP, 0, 0x20),
154 op("sll", Fmt::R, OP, 1, 0x00), op("slt", Fmt::R, OP, 2, 0x00),
155 op("sltu", Fmt::R, OP, 3, 0x00), op("xor", Fmt::R, OP, 4, 0x00),
156 op("srl", Fmt::R, OP, 5, 0x00), op("sra", Fmt::R, OP, 5, 0x20),
157 op("or", Fmt::R, OP, 6, 0x00), op("and", Fmt::R, OP, 7, 0x00),
158 op("mul", Fmt::R, OP, 0, 0x01), op("mulh", Fmt::R, OP, 1, 0x01),
160 op("mulhsu", Fmt::R, OP, 2, 0x01), op("mulhu", Fmt::R, OP, 3, 0x01),
161 op("div", Fmt::R, OP, 4, 0x01), op("divu", Fmt::R, OP, 5, 0x01),
162 op("rem", Fmt::R, OP, 6, 0x01), op("remu", Fmt::R, OP, 7, 0x01),
163 op("min", Fmt::R, OP, 4, 0x05), op("minu", Fmt::R, OP, 5, 0x05),
165 op("max", Fmt::R, OP, 6, 0x05), op("maxu", Fmt::R, OP, 7, 0x05),
166 op("andn", Fmt::R, OP, 7, 0x20), op("orn", Fmt::R, OP, 6, 0x20),
167 op("xnor", Fmt::R, OP, 4, 0x20), op("rol", Fmt::R, OP, 1, 0x30),
168 op("ror", Fmt::R, OP, 5, 0x30),
169 op("sh1add", Fmt::R, OP, 2, 0x10), op("sh2add", Fmt::R, OP, 4, 0x10),
171 op("sh3add", Fmt::R, OP, 6, 0x10),
172 op("bclr", Fmt::R, OP, 1, 0x24), op("bext", Fmt::R, OP, 5, 0x24),
174 op("binv", Fmt::R, OP, 1, 0x34), op("bset", Fmt::R, OP, 1, 0x14),
175 op("czero.eqz", Fmt::R, OP, 5, 0x07), op("czero.nez", Fmt::R, OP, 7, 0x07),
177
178 op("addw", Fmt::R, OP_32, 0, 0x00), op("subw", Fmt::R, OP_32, 0, 0x20),
180 op("sllw", Fmt::R, OP_32, 1, 0x00), op("srlw", Fmt::R, OP_32, 5, 0x00),
181 op("sraw", Fmt::R, OP_32, 5, 0x20),
182 op("mulw", Fmt::R, OP_32, 0, 0x01), op("divw", Fmt::R, OP_32, 4, 0x01),
183 op("divuw", Fmt::R, OP_32, 5, 0x01), op("remw", Fmt::R, OP_32, 6, 0x01),
184 op("remuw", Fmt::R, OP_32, 7, 0x01),
185 op("adduw", Fmt::R, OP_32, 0, 0x04),
186 op("sh1add.uw", Fmt::R, OP_32, 2, 0x10), op("sh2add.uw", Fmt::R, OP_32, 4, 0x10),
187 op("sh3add.uw", Fmt::R, OP_32, 6, 0x10),
188 op("rolw", Fmt::R, OP_32, 1, 0x30), op("rorw", Fmt::R, OP_32, 5, 0x30),
189
190 op("addi", Fmt::I, OP_IMM, 0, 0), op("slti", Fmt::I, OP_IMM, 2, 0),
192 op("sltiu", Fmt::I, OP_IMM, 3, 0), op("xori", Fmt::I, OP_IMM, 4, 0),
193 op("ori", Fmt::I, OP_IMM, 6, 0), op("andi", Fmt::I, OP_IMM, 7, 0),
194 op("addiw", Fmt::I, OP_IMM_32, 0, 0),
195
196 op("slli", Fmt::IShift, OP_IMM, 1, 0x00), op("srli", Fmt::IShift, OP_IMM, 5, 0x00),
198 op("srai", Fmt::IShift, OP_IMM, 5, 0x10), op("rori", Fmt::IShift, OP_IMM, 5, 0x18),
199 op("bclri", Fmt::IShift, OP_IMM, 1, 0x12), op("bexti", Fmt::IShift, OP_IMM, 5, 0x12),
200 op("bseti", Fmt::IShift, OP_IMM, 1, 0x0A), op("binvi", Fmt::IShift, OP_IMM, 1, 0x1A),
201 op("slliw", Fmt::IShift32, OP_IMM_32, 1, 0x00), op("srliw", Fmt::IShift32, OP_IMM_32, 5, 0x00),
207 op("sraiw", Fmt::IShift32, OP_IMM_32, 5, 0x20), op("roriw", Fmt::IShift32, OP_IMM_32, 5, 0x30),
208
209 op("clz", Fmt::Unary, OP_IMM, 1, 0x600), op("ctz", Fmt::Unary, OP_IMM, 1, 0x601),
211 op("cpop", Fmt::Unary, OP_IMM, 1, 0x602), op("sext.b", Fmt::Unary, OP_IMM, 1, 0x604),
212 op("sext.h", Fmt::Unary, OP_IMM, 1, 0x605), op("orc.b", Fmt::Unary, OP_IMM, 5, 0x287),
213 op("rev8", Fmt::Unary, OP_IMM, 5, 0x6B8),
214 op("clzw", Fmt::Unary, OP_IMM_32, 1, 0x600), op("ctzw", Fmt::Unary, OP_IMM_32, 1, 0x601),
215 op("cpopw", Fmt::Unary, OP_IMM_32, 1, 0x602),
216 op("lb", Fmt::I, LOAD, 0, 0), op("lh", Fmt::I, LOAD, 1, 0),
222 op("lw", Fmt::I, LOAD, 2, 0), op("ld", Fmt::I, LOAD, 3, 0),
223 op("lbu", Fmt::I, LOAD, 4, 0), op("lhu", Fmt::I, LOAD, 5, 0),
224 op("lwu", Fmt::I, LOAD, 6, 0),
225 op("sb", Fmt::Store, STORE, 0, 0), op("sh", Fmt::Store, STORE, 1, 0),
226 op("sw", Fmt::Store, STORE, 2, 0), op("sd", Fmt::Store, STORE, 3, 0),
227
228 op("lui", Fmt::U, LUI, 0, 0), op("auipc", Fmt::U, AUIPC, 0, 0),
230
231 op("beq", Fmt::Branch, BRANCH, 0, 0), op("bne", Fmt::Branch, BRANCH, 1, 0),
233 op("blt", Fmt::Branch, BRANCH, 4, 0), op("bge", Fmt::Branch, BRANCH, 5, 0),
234 op("bltu", Fmt::Branch, BRANCH, 6, 0), op("bgeu", Fmt::Branch, BRANCH, 7, 0),
235];
236
237pub fn encode_op(spec: &OpSpec, rd: u8, rs1: u8, rs2: u8, imm: i32) -> u32 {
242 match spec.fmt {
243 Fmt::R => r(spec.opcode, spec.aux, spec.funct3, rd, rs1, rs2),
244 Fmt::I => i(spec.opcode, spec.funct3, rd, rs1, imm),
245 Fmt::IShift => i_shift64(
246 spec.opcode,
247 spec.funct3,
248 spec.aux,
249 rd,
250 rs1,
251 (imm as u32 & 0x3F) as u8,
252 ),
253 Fmt::IShift32 => i_shift32(
254 spec.opcode,
255 spec.funct3,
256 spec.aux,
257 rd,
258 rs1,
259 (imm as u32 & 0x1F) as u8,
260 ),
261 Fmt::Store => s(spec.opcode, spec.funct3, rs1, rs2, imm),
262 Fmt::Branch => b_(spec.opcode, spec.funct3, rs1, rs2, imm),
263 Fmt::U => u_(spec.opcode, rd, imm as u32),
264 Fmt::Unary => i(spec.opcode, spec.funct3, rd, rs1, spec.aux as i32),
265 }
266}
267
268pub const HALT: u32 = 0x0000_200B;
273
274pub fn addi(rd: u8, rs1: u8, imm: i32) -> u32 {
275 i(OP_IMM, 0, rd, rs1, imm)
276}
277pub fn add(rd: u8, rs1: u8, rs2: u8) -> u32 {
278 r(OP, 0x00, 0, rd, rs1, rs2)
279}
280pub fn sub(rd: u8, rs1: u8, rs2: u8) -> u32 {
281 r(OP, 0x20, 0, rd, rs1, rs2)
282}
283pub fn xor(rd: u8, rs1: u8, rs2: u8) -> u32 {
284 r(OP, 0x00, 4, rd, rs1, rs2)
285}
286pub fn div(rd: u8, rs1: u8, rs2: u8) -> u32 {
287 r(OP, 0x01, 4, rd, rs1, rs2)
288}
289pub fn rem(rd: u8, rs1: u8, rs2: u8) -> u32 {
290 r(OP, 0x01, 6, rd, rs1, rs2)
291}
292pub fn mulhsu(rd: u8, rs1: u8, rs2: u8) -> u32 {
293 r(OP, 0x01, 2, rd, rs1, rs2)
294}
295pub fn slli(rd: u8, rs1: u8, shamt: u8) -> u32 {
296 i_shift64(OP_IMM, 1, 0x00, rd, rs1, shamt)
297}
298pub fn rori(rd: u8, rs1: u8, shamt: u8) -> u32 {
299 i_shift64(OP_IMM, 5, 0x18, rd, rs1, shamt)
300}
301pub fn ld(rd: u8, rs1: u8, imm: i32) -> u32 {
302 i(LOAD, 3, rd, rs1, imm)
303}
304pub fn sd(rs1: u8, rs2: u8, imm: i32) -> u32 {
305 s(STORE, 3, rs1, rs2, imm)
306}
307pub fn lui(rd: u8, imm20: u32) -> u32 {
308 u_(LUI, rd, imm20)
309}
310pub fn beq(rs1: u8, rs2: u8, imm: i32) -> u32 {
311 b_(BRANCH, 0, rs1, rs2, imm)
312}
313
314pub fn li64(rd: u8, value: u64) -> Vec<u32> {
318 let chunk = |sh: u32| ((value >> sh) & 0x7FF) as i32;
319 let mut out = vec![addi(rd, 0, ((value >> 55) & 0x1FF) as i32)];
320 for sh in [44u32, 33, 22, 11, 0] {
321 out.push(slli(rd, rd, 11));
322 out.push(addi(rd, rd, chunk(sh)));
323 }
324 out
325}
326
327pub const SIG_REGS: usize = 13;
332
333pub const SIG_BYTES: usize = SIG_REGS * 8;
336
337pub const SIG_XREGS: [u8; SIG_REGS] = [1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
342
343pub const SIG_BASE_REG: u8 = 3;
350
351pub fn signature_epilogue(sig_base: u32) -> Vec<u32> {
358 let mut out = li64(SIG_BASE_REG, sig_base as u64);
359 for (i, &xr) in SIG_XREGS.iter().enumerate() {
360 out.push(sd(SIG_BASE_REG, xr, (i * 8) as i32));
361 }
362 out
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368 use javm_exec::instruction::{Inst, decode};
369
370 fn decode1(w: u32) -> (Inst, u8) {
371 decode(&w.to_le_bytes()).unwrap_or_else(|| panic!("decode failed for {w:#010x}"))
372 }
373
374 #[test]
375 fn round_trip_all_ops() {
376 let mut bad = Vec::new();
380 for spec in OPS {
381 let w = encode_op(spec, 10, 11, 12, 4);
383 match decode(&w.to_le_bytes()) {
384 Some((Inst::Reserved { .. }, _)) | None => {
385 bad.push(format!("{} -> Reserved/None ({w:#010x})", spec.name))
386 }
387 Some((_, 4)) => {}
388 Some((_, len)) => bad.push(format!("{} -> len {len} ({w:#010x})", spec.name)),
389 }
390 }
391 assert!(
392 bad.is_empty(),
393 "encoder/decoder mismatch:\n {}",
394 bad.join("\n ")
395 );
396 }
397
398 #[test]
399 fn halt_decodes_as_ecalli_zero() {
400 assert!(matches!(decode1(HALT), (Inst::Ecalli { imm: 0 }, 4)));
401 }
402
403 #[test]
404 fn curated_exact_fields() {
405 assert!(matches!(
406 decode1(add(10, 11, 12)),
407 (
408 Inst::Add {
409 rd: 10,
410 rs1: 11,
411 rs2: 12
412 },
413 4
414 )
415 ));
416 assert!(matches!(
417 decode1(sub(5, 6, 7)),
418 (
419 Inst::Sub {
420 rd: 5,
421 rs1: 6,
422 rs2: 7
423 },
424 4
425 )
426 ));
427 assert!(matches!(
428 decode1(div(10, 8, 9)),
429 (
430 Inst::Div {
431 rd: 10,
432 rs1: 8,
433 rs2: 9
434 },
435 4
436 )
437 ));
438 assert!(matches!(
439 decode1(rem(10, 8, 9)),
440 (
441 Inst::Rem {
442 rd: 10,
443 rs1: 8,
444 rs2: 9
445 },
446 4
447 )
448 ));
449 assert!(matches!(
450 decode1(mulhsu(10, 8, 9)),
451 (
452 Inst::Mulhsu {
453 rd: 10,
454 rs1: 8,
455 rs2: 9
456 },
457 4
458 )
459 ));
460 assert!(matches!(
461 decode1(rori(5, 5, 5)),
462 (
463 Inst::Rori {
464 rd: 5,
465 rs1: 5,
466 shamt: 5
467 },
468 4
469 )
470 ));
471 assert!(matches!(
472 decode1(slli(5, 5, 11)),
473 (
474 Inst::Slli {
475 rd: 5,
476 rs1: 5,
477 shamt: 11
478 },
479 4
480 )
481 ));
482 assert!(matches!(
483 decode1(ld(7, 6, 8)),
484 (
485 Inst::Ld {
486 rd: 7,
487 rs1: 6,
488 imm: 8
489 },
490 4
491 )
492 ));
493 assert!(matches!(
494 decode1(sd(6, 7, 0)),
495 (
496 Inst::Sd {
497 rs1: 6,
498 rs2: 7,
499 imm: 0
500 },
501 4
502 )
503 ));
504 assert!(matches!(
505 decode1(addi(10, 0, -4)),
506 (
507 Inst::Addi {
508 rd: 10,
509 rs1: 0,
510 imm: -4
511 },
512 4
513 )
514 ));
515 assert!(matches!(
516 decode1(lui(10, 0x12345)),
517 (
518 Inst::Lui {
519 rd: 10,
520 imm: 0x1234_5000
521 },
522 4
523 )
524 ));
525 assert!(matches!(
526 decode1(beq(8, 9, 12)),
527 (
528 Inst::Beq {
529 rs1: 8,
530 rs2: 9,
531 imm: 12
532 },
533 4
534 )
535 ));
536 }
537
538 #[test]
539 fn li64_materializes_boundary_values() {
540 for v in [
543 0u64,
544 1,
545 u64::MAX,
546 0x8000_0000_0000_0000,
547 0x7FFF_FFFF,
548 0xDEAD_BEEF_CAFE_F00D,
549 ] {
550 for w in li64(7, v) {
551 assert!(
552 !matches!(decode1(w), (Inst::Reserved { .. }, _)),
553 "li64({v:#018x}) produced Reserved word {w:#010x}",
554 );
555 }
556 }
557 }
558
559 #[test]
560 fn signature_epilogue_is_all_valid() {
561 let ep = signature_epilogue(0x1000_0000);
562 for w in &ep {
563 assert!(
564 !matches!(decode1(*w), (Inst::Reserved { .. }, _)),
565 "signature epilogue produced Reserved word {w:#010x}",
566 );
567 }
568 assert_eq!(
570 ep.len(),
571 li64(SIG_BASE_REG, 0).len() + SIG_REGS,
572 "epilogue length"
573 );
574 }
575}