1use crate::Program;
24use crate::encode::{self, Fmt, OpSpec};
25use javm_exec::regs::reg_slot_or_ff;
26use std::collections::BTreeMap;
27
28pub const OPERANDS: &[u64] = &[
30 0x0000_0000_0000_0000, 0x0000_0000_0000_0001, 0x0000_0000_0000_0002, 0xFFFF_FFFF_FFFF_FFFF, 0x8000_0000_0000_0000, 0x7FFF_FFFF_FFFF_FFFF, 0x0000_0000_7FFF_FFFF, 0x0000_0000_8000_0000, 0xFFFF_FFFF_8000_0000, 0x0000_0000_FFFF_FFFF, 0x0000_0000_0000_0010, 0x0000_0000_0000_0040, 0xDEAD_BEEF_CAFE_BABE, ];
44
45pub const IMMS: &[i32] = &[0, 1, -1, 2, -2, 2047, -2048, 0x555, -0x555];
47
48pub const SHAMTS: &[i32] = &[0, 1, 7, 8, 31, 32, 63, 64, 65];
51
52pub const IMM20S: &[i32] = &[0, 1, 0xF_FFFF, 0x8_0000, 0x7_FFFF, 0xA_5A5A];
54
55const DEST: &[u8] = &[1, 2, 5, 8, 9, 10, 11, 12, 13, 14, 15];
59const SRC: &[u8] = &[0, 1, 2, 5, 8, 9, 10, 11, 12, 13, 14, 15];
61
62fn seed(init: &mut BTreeMap<u8, u64>, xreg: u8, val: u64) {
65 let slot = reg_slot_or_ff(xreg);
66 if slot != 0xFF {
67 init.insert(slot, val);
68 }
69}
70
71fn finish(body: Vec<u32>, init_regs: BTreeMap<u8, u64>) -> Program {
73 let mut code = body;
74 code.extend(encode::signature_epilogue(crate::SIG_BASE));
75 Program {
76 code,
77 init_regs,
78 init_mem: None,
79 }
80}
81
82fn reg_only_ops() -> impl Iterator<Item = &'static OpSpec> {
84 encode::OPS
85 .iter()
86 .filter(|s| !s.touches_memory_or_control())
87}
88
89pub fn enumerate_boundary() -> Vec<Program> {
93 const RA: u8 = 8;
94 const RB: u8 = 9;
95 const RD: u8 = 10;
96 let mut progs = Vec::new();
97 for spec in reg_only_ops() {
98 match spec.fmt {
99 Fmt::R => {
101 for &a in OPERANDS {
102 for &b in OPERANDS {
103 let mut init = BTreeMap::new();
104 seed(&mut init, RA, a);
105 seed(&mut init, RB, b);
106 let body = vec![encode::encode_op(spec, RD, RA, RB, 0)];
107 progs.push(finish(body, init));
108 }
109 }
110 }
111 Fmt::I => {
113 for &a in OPERANDS {
114 for &imm in IMMS {
115 let mut init = BTreeMap::new();
116 seed(&mut init, RA, a);
117 let body = vec![encode::encode_op(spec, RD, RA, 0, imm)];
118 progs.push(finish(body, init));
119 }
120 }
121 }
122 Fmt::IShift | Fmt::IShift32 => {
124 for &a in OPERANDS {
125 for &sh in SHAMTS {
126 let mut init = BTreeMap::new();
127 seed(&mut init, RA, a);
128 let body = vec![encode::encode_op(spec, RD, RA, 0, sh)];
129 progs.push(finish(body, init));
130 }
131 }
132 }
133 Fmt::Unary => {
135 for &a in OPERANDS {
136 let mut init = BTreeMap::new();
137 seed(&mut init, RA, a);
138 let body = vec![encode::encode_op(spec, RD, RA, 0, 0)];
139 progs.push(finish(body, init));
140 }
141 }
142 Fmt::U => {
144 for &imm in IMM20S {
145 let body = vec![encode::encode_op(spec, RD, 0, 0, imm)];
146 progs.push(finish(body, BTreeMap::new()));
147 }
148 }
149 Fmt::Store | Fmt::Branch => {} }
151 }
152 progs
153}
154
155pub struct XorShift64(u64);
157
158impl XorShift64 {
159 pub fn new(seed: u64) -> Self {
160 XorShift64(seed ^ 0x9E37_79B9_7F4A_7C15 | 1)
161 }
162 pub fn next_u64(&mut self) -> u64 {
163 let mut x = self.0;
164 x ^= x << 13;
165 x ^= x >> 7;
166 x ^= x << 17;
167 self.0 = x;
168 x.wrapping_mul(0x2545_F491_4F6C_DD1D)
169 }
170 fn pick<T: Copy>(&mut self, xs: &[T]) -> T {
171 xs[(self.next_u64() % xs.len() as u64) as usize]
172 }
173}
174
175pub struct Gen {
177 rng: XorShift64,
178}
179
180impl Gen {
181 pub fn new(seed: u64) -> Self {
182 Gen {
183 rng: XorShift64::new(seed),
184 }
185 }
186
187 pub fn random_program(&mut self, body_len: usize) -> Program {
190 let mut init = BTreeMap::new();
191 for &xr in DEST {
192 if self.rng.next_u64() & 3 != 0 {
194 seed(&mut init, xr, self.rng.pick(OPERANDS));
195 }
196 }
197 let ops: Vec<&OpSpec> = reg_only_ops().collect();
198 let mut body = Vec::with_capacity(body_len);
199 for _ in 0..body_len {
200 let spec = self.rng.pick(&ops);
201 let rd = self.rng.pick(DEST);
202 let rs1 = self.rng.pick(SRC);
203 let rs2 = self.rng.pick(SRC);
204 let imm = match spec.fmt {
205 Fmt::IShift | Fmt::IShift32 => self.rng.pick(SHAMTS),
206 Fmt::U => self.rng.pick(IMM20S),
207 _ => self.rng.pick(IMMS),
208 };
209 body.push(encode::encode_op(spec, rd, rs1, rs2, imm));
210 }
211 finish(body, init)
212 }
213
214 pub fn random_batch(&mut self, count: usize, body_len: usize) -> Vec<Program> {
216 (0..count).map(|_| self.random_program(body_len)).collect()
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223 use javm_exec::instruction::{Inst, decode};
224
225 fn assert_all_valid(prog: &Program) {
230 let bytes = prog.code_bytes();
231 let mut off = 0;
232 while off < bytes.len() {
233 let (inst, len) = decode(&bytes[off..]).expect("decodes");
234 let w =
235 u32::from_le_bytes([bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3]]);
236 assert!(
237 !matches!(inst, Inst::Reserved { .. }),
238 "generator emitted a reserved encoding {w:#010x} at byte {off}",
239 );
240 off += len as usize;
241 }
242 }
243
244 #[test]
245 fn boundary_enumeration_is_all_valid_and_hits_div() {
246 let progs = enumerate_boundary();
247 assert!(progs.len() > 100, "expected a broad enumeration");
248 for p in &progs {
249 assert_all_valid(p);
250 }
251 }
252
253 #[test]
254 fn random_programs_are_valid_and_deterministic() {
255 let a = Gen::new(42).random_batch(50, 8);
256 let b = Gen::new(42).random_batch(50, 8);
257 assert_eq!(a, b, "same seed must reproduce the same programs");
258 for p in &a {
259 assert_all_valid(p);
260 }
261 }
262}