1use static_assertions::const_assert;
2use zisk_core::zisk_ops::ZiskOp;
3
4const OP_SIGNEXTENDB: u8 = ZiskOp::SignExtendB.code();
5const OP_SIGNEXTENDH: u8 = ZiskOp::SignExtendH.code();
6const OP_SIGNEXTENDW: u8 = ZiskOp::SignExtendW.code();
7const OP_ADD: u8 = ZiskOp::Add.code();
8const OP_ADDW: u8 = ZiskOp::AddW.code();
9const OP_SUB: u8 = ZiskOp::Sub.code();
10const OP_SUBW: u8 = ZiskOp::SubW.code();
11const OP_SLL: u8 = ZiskOp::Sll.code();
12const OP_SLLW: u8 = ZiskOp::SllW.code();
13const OP_SRA: u8 = ZiskOp::Sra.code();
14const OP_SRL: u8 = ZiskOp::Srl.code();
15const OP_SRAW: u8 = ZiskOp::SraW.code();
16const OP_SRLW: u8 = ZiskOp::SrlW.code();
17const OP_EQ: u8 = ZiskOp::Eq.code();
18const OP_EQW: u8 = ZiskOp::EqW.code();
19const OP_LTU: u8 = ZiskOp::Ltu.code();
20const OP_LT: u8 = ZiskOp::Lt.code();
21const OP_LTUW: u8 = ZiskOp::LtuW.code();
22const OP_LTW: u8 = ZiskOp::LtW.code();
23const OP_LEU: u8 = ZiskOp::Leu.code();
24const OP_LE: u8 = ZiskOp::Le.code();
25const OP_LEUW: u8 = ZiskOp::LeuW.code();
26const OP_LEW: u8 = ZiskOp::LeW.code();
27const OP_AND: u8 = ZiskOp::And.code();
28const OP_OR: u8 = ZiskOp::Or.code();
29const OP_XOR: u8 = ZiskOp::Xor.code();
30const OP_MULU: u8 = ZiskOp::Mulu.code();
31const OP_MULUH: u8 = ZiskOp::Muluh.code();
32const OP_MULSUH: u8 = ZiskOp::Mulsuh.code();
33const OP_MUL: u8 = ZiskOp::Mul.code();
34const OP_MULH: u8 = ZiskOp::Mulh.code();
35const OP_MULW: u8 = ZiskOp::MulW.code();
36const OP_DIVU: u8 = ZiskOp::Divu.code();
37const OP_REMU: u8 = ZiskOp::Remu.code();
38const OP_DIV: u8 = ZiskOp::Div.code();
39const OP_REM: u8 = ZiskOp::Rem.code();
40const OP_DIVUW: u8 = ZiskOp::DivuW.code();
41const OP_REMUW: u8 = ZiskOp::RemuW.code();
42const OP_DIVW: u8 = ZiskOp::DivW.code();
43const OP_REMW: u8 = ZiskOp::RemW.code();
44
45const LOW_VALUES_OPCODES: [u8; 40] = [
46 OP_SIGNEXTENDB,
47 OP_SIGNEXTENDH,
48 OP_SIGNEXTENDW,
49 OP_ADD,
50 OP_ADDW,
51 OP_SUB,
52 OP_SUBW,
53 OP_SLL,
54 OP_SLLW,
55 OP_SRA,
56 OP_SRL,
57 OP_SRAW,
58 OP_SRLW,
59 OP_EQ,
60 OP_EQW,
61 OP_LTU,
62 OP_LT,
63 OP_LTUW,
64 OP_LTW,
65 OP_LEU,
66 OP_LE,
67 OP_LEUW,
68 OP_LEW,
69 OP_AND,
70 OP_OR,
71 OP_XOR,
72 OP_MULU,
73 OP_MULUH,
74 OP_MULSUH,
75 OP_MUL,
76 OP_MULH,
77 OP_MULW,
78 OP_DIVU,
79 OP_REMU,
80 OP_DIV,
81 OP_REM,
82 OP_DIVUW,
83 OP_REMUW,
84 OP_DIVW,
85 OP_REMW,
86];
87
88const MAX_A_LOW_VALUE: u64 = 386;
89const MAX_B_LOW_VALUE: u64 = 386;
90const LOW_VALUE_SIZE: usize = (MAX_A_LOW_VALUE * MAX_B_LOW_VALUE) as usize;
91const MINUS_ONE: u64 = -1i64 as u64;
92const MAX_U64: u64 = 0xFFFF_FFFF_FFFF_FFFF;
93const EQ_OP_B_ZERO_A_LIMIT: u64 = 0xFFFFF;
94const LTU_OP_B_LT_ONE_FROM: u64 = -128i64 as u64;
95
96// LT
97
98const LT_FROM_ADDR: u64 = 0xA010_0000;
99const LT_TO_ADDR: u64 = 0xA012_0000;
100const LT_DELTA: u64 = 8;
101const LT_LOW_DISTANCE_1: u64 = 16; // 0 - 15
102const LT_HIGH_DISTANCE_8: u64 = 240; // 16,24,32,40,.....
103const LT_LOW_HIGH_DISTANCES: u64 = LT_LOW_DISTANCE_1 + LT_HIGH_DISTANCE_8;
104const LT_MAX_DISTANCE: u64 = LT_LOW_DISTANCE_1 + (LT_HIGH_DISTANCE_8 - 1) * 8;
105const LT_FROM_TO_SIZE: usize = ((LT_TO_ADDR - LT_FROM_ADDR) / LT_DELTA) as usize;
106const LT_ALL_FROM_TO_SIZE: usize = LT_FROM_TO_SIZE * LT_LOW_HIGH_DISTANCES as usize;
107
108const LT_ZERO_TO_B: u64 = 0x10000;
109
110// ADD
111const MAX_ADD_MINUS_ONE: u64 = 24628;
112const MAX_ADD_MINUS_A: u64 = 1024;
113const MAX_ADD_MINUS_B: u64 = 8;
114const ADD_ONE_FROM_ADDR: u64 = 0xA010_0000; // address
115const ADD_ONE_TO_ADDR: u64 = 0xA020_0000;
116const ADD_EIGHT_FROM_ADDR: u64 = 0xA010_0000; // address
117const ADD_EIGHT_TO_ADDR: u64 = 0xA020_0000;
118const ADD_EIGHT_FROM_CODE: u64 = 0x8000_0000; // address
119const ADD_EIGHT_TO_CODE: u64 = 0x8080_0000;
120const ADD_EIGHT_STEP: u64 = 8;
121
122const ADD_ZERO_FROM_ADDR: u64 = 0xA010_0000; // address
123const ADD_ZERO_TO_ADDR: u64 = 0xA020_0000;
124const ADD_ZERO_STEP: u64 = 8;
125
126const ADD_MINUS_ONE_SIZE: usize = MAX_ADD_MINUS_ONE as usize;
127const ADD_MINUS_A_B_SIZE: usize = (MAX_ADD_MINUS_A * MAX_ADD_MINUS_B) as usize;
128const ADD_MINUS_A_B_FROM_B: u64 = MINUS_ONE - MAX_ADD_MINUS_B;
129
130const ADD_ONE_ADDR_SIZE: usize = (ADD_ONE_TO_ADDR - ADD_ONE_FROM_ADDR) as usize;
131const ADD_EIGHT_ADDR_SIZE: usize =
132 ((ADD_EIGHT_TO_ADDR - ADD_EIGHT_FROM_ADDR) / ADD_EIGHT_STEP) as usize;
133const ADD_EIGHT_CODE_SIZE: usize =
134 ((ADD_EIGHT_TO_CODE - ADD_EIGHT_FROM_CODE) / ADD_EIGHT_STEP) as usize;
135const ADD_ZERO_ADDR_SIZE: usize =
136 ((ADD_ZERO_TO_ADDR - ADD_ZERO_FROM_ADDR) / ADD_ZERO_STEP) as usize;
137
138const ADD_MINUS_ONE_OFFSET: usize = LOW_VALUE_SIZE;
139const ADD_MINUS_A_B_OFFSET: usize = ADD_MINUS_ONE_OFFSET + ADD_MINUS_ONE_SIZE;
140const ADD_ONE_ADDR_OFFSET: usize = ADD_MINUS_A_B_OFFSET + ADD_MINUS_A_B_SIZE;
141const ADD_EIGHT_ADDR_OFFSET: usize = ADD_ONE_ADDR_OFFSET + ADD_ONE_ADDR_SIZE;
142const ADD_EIGHT_CODE_OFFSET: usize = ADD_EIGHT_ADDR_OFFSET + ADD_EIGHT_ADDR_SIZE;
143const ADD_ZERO_ADDR_OFFSET: usize = ADD_EIGHT_CODE_OFFSET + ADD_EIGHT_CODE_SIZE;
144
145// AND
146const AND_CODE_ADDR_FROM: u64 = 0x8000_0000;
147const AND_CODE_ADDR_TO: u64 = 0x8090_0000; // address
148const AND_CODE_ADDR_STEP: u64 = 4;
149const AND_CODE_ADDR_MASK: u64 = 0xFFFF_FFFF_FFFF_FFFC;
150
151const AND_RESET_LAST_THREE_BITS_B: u64 = 0xFFFF_FFFF_FFFF_FFF8;
152const AND_RESET_LAST_THREE_BITS_A_TO: u64 = 1024;
153const AND_GET_LAST_THREE_BITS_B: u64 = 0x7;
154const AND_GET_LAST_THREE_BITS_FROM: u64 = 0xA010_0000;
155const AND_GET_LAST_THREE_BITS_TO: u64 = 0xA020_0000;
156const AND_GET_LAST_THREE_BITS_STEP: u64 = 8;
157
158const AND_CODE_ADDR_OFFSET: usize = LOW_VALUE_SIZE;
159const AND_CODE_ADDR_SIZE: usize =
160 ((AND_CODE_ADDR_TO - AND_CODE_ADDR_FROM) / AND_CODE_ADDR_STEP) as usize;
161
162const AND_RESET_LAST_THREE_BITS_OFFSET: usize = AND_CODE_ADDR_OFFSET + AND_CODE_ADDR_SIZE;
163const AND_RESET_LAST_THREE_BITS_SIZE: usize = AND_RESET_LAST_THREE_BITS_A_TO as usize;
164
165const AND_GET_LAST_THREE_BITS_OFFSET: usize =
166 AND_RESET_LAST_THREE_BITS_OFFSET + AND_RESET_LAST_THREE_BITS_SIZE;
167const AND_GET_LAST_THREE_BITS_SIZE: usize = ((AND_GET_LAST_THREE_BITS_TO
168 - AND_GET_LAST_THREE_BITS_FROM)
169 / AND_GET_LAST_THREE_BITS_STEP) as usize;
170
171const OR_TO_A: u64 = 0x1000;
172const OR_TO_B: u64 = 16;
173
174const SLR_MASK_FROM: u64 = 0xFFFF_FFFF_FFFF_F000;
175const SLR_TO_B: u64 = 64;
176
177const SUB_W_ADDR_FROM: u64 = 0xA010_0000;
178const SUB_W_ADDR_TO: u64 = 0xA020_0000;
179const SUB_W_ADDR_STEP: u64 = 4;
180
181const SUB_TO_A: u64 = 4192;
182const SUB_TO_B: u64 = 8;
183
184// table autogenerated with FrequentOpsTable::print_table_offsets()
185// this table is used to calculate the offset (row) of each operation
186const OP_TABLE_OFFSETS: [usize; 192] = [
187 0, 0, 0, 0, 0, 0, 0, 149124, 0, 4557574, 5755146, 8296258, 8479508, 8628504, 8777500, 11417888,
188 11629954, 0, 0, 0, 0, 0, 11778952, 11927948, 0, 12076944, 12225940, 12374936, 12786076,
189 12935072, 0, 0, 0, 13084068, 13233064, 13648300, 13797296, 13946292, 14095288, 14244284,
190 14393280, 14542276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
191 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
192 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
193 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
194 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14691272, 14840268, 0, 14989264, 15138260, 15287256,
195 15436252, 0, 15585248, 15734244, 15883240, 16032236, 16181232, 16330228, 16479224, 16628220,
196];
197
198#[derive(Debug, Clone)]
199pub struct FrequentOpsTable {
200 table_by_op: [usize; 256],
201 table_ops: Vec<Vec<[u64; 2]>>,
202}
203
204const FREQUENT_OP_EMPTY: usize = 256;
205
206impl Default for FrequentOpsTable {
207 fn default() -> Self {
208 Self::new()
209 }
210}
211impl FrequentOpsTable {
212 pub fn new() -> Self {
213 Self { table_by_op: [FREQUENT_OP_EMPTY; 256], table_ops: Vec::new() }
214 }
215 fn add_ops(&mut self, op: u8, ops: &mut Vec<[u64; 2]>, move_contents: bool) {
216 let mut index = self.table_by_op[op as usize];
217 if index == FREQUENT_OP_EMPTY {
218 index = self.table_ops.len();
219 self.table_ops.push(Vec::new());
220 self.table_by_op[op as usize] = index;
221 }
222 if move_contents {
223 self.table_ops[index].append(ops);
224 } else {
225 self.table_ops[index].extend(ops.iter().cloned());
226 }
227 }
228
229 fn build_low_values_operations(&mut self) {
230 let mut ops: Vec<[u64; 2]> = Vec::new();
231 for i in 0..MAX_A_LOW_VALUE {
232 for j in 0..MAX_B_LOW_VALUE {
233 ops.push([i, j]);
234 }
235 }
236
237 for op in LOW_VALUES_OPCODES.iter() {
238 self.add_ops(*op, &mut ops, false);
239 }
240 }
241
242 fn build_eq_zero(&mut self) {
243 let mut ops: Vec<[u64; 2]> = Vec::new();
244 for i in 0..=EQ_OP_B_ZERO_A_LIMIT {
245 ops.push([i, 0]);
246 }
247 self.add_ops(OP_EQ, &mut ops, true);
248 }
249 #[inline(always)]
250 fn get_eq_offset(a: u64, b: u64) -> Option<usize> {
251 if b == 0 && a <= EQ_OP_B_ZERO_A_LIMIT {
252 Some(LOW_VALUE_SIZE + a as usize)
253 } else if b < MAX_B_LOW_VALUE && a < MAX_A_LOW_VALUE {
254 Some(Self::get_low_values_offset(a, b))
255 } else {
256 None
257 }
258 }
259
260 fn build_ltu_one(&mut self) {
261 let mut ops: Vec<[u64; 2]> = Vec::new();
262 for i in LTU_OP_B_LT_ONE_FROM..=MAX_U64 {
263 ops.push([i, 1]);
264 }
265 self.add_ops(OP_LTU, &mut ops, true);
266 }
267
268 #[inline(always)]
269 fn get_ltu_offset(a: u64, b: u64) -> Option<usize> {
270 if b == 1 {
271 if a >= LTU_OP_B_LT_ONE_FROM {
272 Some(LOW_VALUE_SIZE + (a - LTU_OP_B_LT_ONE_FROM) as usize)
273 } else if a < MAX_A_LOW_VALUE {
274 Some(Self::get_low_values_offset(a, 1))
275 } else {
276 None
277 }
278 } else if b < MAX_B_LOW_VALUE && a < MAX_A_LOW_VALUE {
279 Some(Self::get_low_values_offset(a, b))
280 } else {
281 None
282 }
283 }
284
285 fn build_lt(&mut self) {
286 let mut ops: Vec<[u64; 2]> = Vec::new();
287 let mut i = LT_FROM_ADDR;
288 while i < LT_TO_ADDR {
289 for j in 0..LT_LOW_DISTANCE_1 {
290 ops.push([i - j, i]);
291 }
292 for j in 0..LT_HIGH_DISTANCE_8 {
293 ops.push([i - j * 8 - 16, i]);
294 }
295 i += LT_DELTA;
296 }
297 for i in MAX_B_LOW_VALUE..LT_ZERO_TO_B {
298 ops.push([0, i]);
299 }
300 self.add_ops(OP_LT, &mut ops, true);
301 }
302 #[inline(always)]
303 fn is_frequent_lt(a: u64, b: u64) -> bool {
304 if a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE {
305 true
306 } else if a <= b && b & 0xFFFF_FFFF_FFFE_0007 == 0xA010_0000 {
307 // 256 / 8 = 32 (5 bits)
308 let dist = b - a;
309 if dist < LT_LOW_DISTANCE_1 {
310 true
311 } else if dist <= LT_MAX_DISTANCE && dist & 0x7 == 0 {
312 // 16 - dist >> 3 - 2 = 14 - dist >> 3
313 true
314 } else {
315 false
316 }
317 } else {
318 a == 0 && b < LT_ZERO_TO_B
319 }
320 }
321
322 #[inline(always)]
323 fn get_lt_offset(a: u64, b: u64) -> Option<usize> {
324 const_assert!(LT_DELTA == 8);
325 const_assert!(LT_FROM_ADDR == 0xA010_0000);
326 const_assert!(LT_TO_ADDR == 0xA012_0000);
327
328 if a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE {
329 Some(Self::get_low_values_offset(a, b))
330 // TODO_ASSERT
331 } else if a <= b && (b & 0xFFFF_FFFF_FFFE_0007) == 0xA010_0000 {
332 // 256 / 8 = 32 (5 bits)
333 let addr_offset = ((b - LT_FROM_ADDR) >> 3) * LT_LOW_HIGH_DISTANCES;
334 let dist = b - a;
335 if dist < LT_LOW_DISTANCE_1 {
336 Some(LOW_VALUE_SIZE + (addr_offset + dist) as usize)
337 } else if dist <= LT_MAX_DISTANCE && dist & 0x7 == 0 {
338 // 16 - dist >> 3 - 2 = 14 - dist >> 3
339 Some(
340 LOW_VALUE_SIZE
341 + (addr_offset + LT_LOW_DISTANCE_1 + ((dist - 16) >> 3)) as usize,
342 )
343 } else {
344 None
345 }
346 } else if a == 0 && b < LT_ZERO_TO_B {
347 // in this point B >= MAX_B_LOW_VALUE
348 Some(LOW_VALUE_SIZE + LT_ALL_FROM_TO_SIZE + (b - MAX_B_LOW_VALUE) as usize)
349 } else {
350 None
351 }
352 }
353
354 fn build_add(&mut self) {
355 let mut ops: Vec<[u64; 2]> = Vec::new();
356 for i in 0..MAX_ADD_MINUS_ONE {
357 ops.push([i, MINUS_ONE]);
358 }
359 assert_eq!(ADD_MINUS_ONE_SIZE, ops.len());
360
361 assert_eq!(ADD_MINUS_A_B_OFFSET, LOW_VALUE_SIZE + ops.len());
362 for i in 0..MAX_ADD_MINUS_A {
363 for j in 1..=MAX_ADD_MINUS_B {
364 ops.push([i, MAX_U64 - j]);
365 }
366 }
367 assert_eq!((ADD_MINUS_A_B_OFFSET + ADD_MINUS_A_B_SIZE), LOW_VALUE_SIZE + ops.len());
368 // address + 1
369
370 assert_eq!(ADD_ONE_ADDR_OFFSET, LOW_VALUE_SIZE + ops.len());
371 for i in ADD_ONE_FROM_ADDR..ADD_ONE_TO_ADDR {
372 ops.push([i, 1]);
373 }
374 assert_eq!((ADD_ONE_ADDR_OFFSET + ADD_ONE_ADDR_SIZE), LOW_VALUE_SIZE + ops.len());
375
376 assert_eq!(ADD_EIGHT_ADDR_OFFSET, LOW_VALUE_SIZE + ops.len());
377 for i in (ADD_EIGHT_FROM_ADDR..ADD_EIGHT_TO_ADDR).step_by(ADD_EIGHT_STEP as usize) {
378 ops.push([i, 8]);
379 }
380 assert_eq!((ADD_EIGHT_ADDR_OFFSET + ADD_EIGHT_ADDR_SIZE), LOW_VALUE_SIZE + ops.len());
381
382 assert_eq!(ADD_EIGHT_CODE_OFFSET, LOW_VALUE_SIZE + ops.len());
383 for i in (ADD_EIGHT_FROM_CODE..ADD_EIGHT_TO_CODE).step_by(ADD_EIGHT_STEP as usize) {
384 ops.push([i, 8]);
385 }
386 assert_eq!((ADD_EIGHT_CODE_OFFSET + ADD_EIGHT_CODE_SIZE), LOW_VALUE_SIZE + ops.len());
387
388 assert_eq!(ADD_ZERO_ADDR_OFFSET, LOW_VALUE_SIZE + ops.len());
389 for i in (ADD_ZERO_FROM_ADDR..ADD_ZERO_TO_ADDR).step_by(ADD_ZERO_STEP as usize) {
390 ops.push([i, 0]);
391 }
392 assert_eq!((ADD_ZERO_ADDR_OFFSET + ADD_ZERO_ADDR_SIZE), LOW_VALUE_SIZE + ops.len());
393
394 self.add_ops(OP_ADD, &mut ops, true);
395 }
396
397 #[inline(always)]
398 fn get_add_offset(a: u64, b: u64) -> Option<usize> {
399 const_assert!(ADD_ZERO_STEP == 8);
400 if b < MAX_B_LOW_VALUE {
401 if a < MAX_A_LOW_VALUE {
402 Some(Self::get_low_values_offset(a, b))
403 } else {
404 match b {
405 0 => {
406 if (ADD_ZERO_FROM_ADDR..ADD_ZERO_TO_ADDR).contains(&a) && a & 0x7 == 0 {
407 Some(ADD_ZERO_ADDR_OFFSET + ((a - ADD_ZERO_FROM_ADDR) >> 3) as usize)
408 } else {
409 None
410 }
411 }
412 1 => {
413 if (ADD_ONE_FROM_ADDR..ADD_ONE_TO_ADDR).contains(&a) {
414 Some(ADD_ONE_ADDR_OFFSET + (a - ADD_ONE_FROM_ADDR) as usize)
415 } else {
416 None
417 }
418 }
419 8 => {
420 if a & 0x7 == 0 {
421 if (ADD_EIGHT_FROM_ADDR..ADD_EIGHT_TO_ADDR).contains(&a) {
422 Some(
423 ADD_EIGHT_ADDR_OFFSET
424 + ((a - ADD_EIGHT_FROM_ADDR) >> 3) as usize,
425 )
426 } else if (ADD_EIGHT_FROM_CODE..ADD_EIGHT_TO_CODE).contains(&a) {
427 Some(
428 ADD_EIGHT_CODE_OFFSET
429 + ((a - ADD_EIGHT_FROM_CODE) >> 3) as usize,
430 )
431 } else {
432 None
433 }
434 } else {
435 None
436 }
437 }
438 _ => None,
439 }
440 }
441 } else if b == MINUS_ONE {
442 if a < MAX_ADD_MINUS_ONE {
443 Some(ADD_MINUS_ONE_OFFSET + a as usize)
444 } else {
445 None
446 }
447 } else if b >= ADD_MINUS_A_B_FROM_B && a < MAX_ADD_MINUS_A {
448 Some(ADD_MINUS_A_B_OFFSET + (a * MAX_ADD_MINUS_B + (MAX_U64 - 1 - b)) as usize)
449 } else {
450 None
451 }
452 }
453
454 fn build_and(&mut self) {
455 let mut ops: Vec<[u64; 2]> = Vec::new();
456 assert_eq!(AND_CODE_ADDR_OFFSET, LOW_VALUE_SIZE + ops.len());
457 for i in (AND_CODE_ADDR_FROM..AND_CODE_ADDR_TO).step_by(AND_CODE_ADDR_STEP as usize) {
458 ops.push([AND_CODE_ADDR_MASK, i]);
459 }
460 assert_eq!(AND_CODE_ADDR_OFFSET + AND_CODE_ADDR_SIZE, LOW_VALUE_SIZE + ops.len());
461
462 assert_eq!(AND_RESET_LAST_THREE_BITS_OFFSET, LOW_VALUE_SIZE + ops.len());
463 for i in 0..AND_RESET_LAST_THREE_BITS_A_TO {
464 ops.push([i, AND_RESET_LAST_THREE_BITS_B]);
465 }
466 assert_eq!(
467 (AND_RESET_LAST_THREE_BITS_OFFSET + AND_RESET_LAST_THREE_BITS_SIZE),
468 LOW_VALUE_SIZE + ops.len()
469 );
470
471 assert_eq!(AND_GET_LAST_THREE_BITS_OFFSET, LOW_VALUE_SIZE + ops.len());
472 for i in (AND_GET_LAST_THREE_BITS_FROM..AND_GET_LAST_THREE_BITS_TO)
473 .step_by(AND_GET_LAST_THREE_BITS_STEP as usize)
474 {
475 ops.push([i, AND_GET_LAST_THREE_BITS_B]);
476 }
477 assert_eq!(
478 (AND_GET_LAST_THREE_BITS_OFFSET + AND_GET_LAST_THREE_BITS_SIZE),
479 LOW_VALUE_SIZE + ops.len()
480 );
481 self.add_ops(OP_AND, &mut ops, true);
482 }
483
484 #[inline(always)]
485 fn get_and_offset(a: u64, b: u64) -> Option<usize> {
486 if a == AND_CODE_ADDR_MASK
487 && (b & 0x03) == 0
488 && (AND_CODE_ADDR_FROM..AND_CODE_ADDR_TO).contains(&b)
489 {
490 Some(AND_CODE_ADDR_OFFSET + ((b - AND_CODE_ADDR_FROM) >> 2) as usize)
491 } else if b == AND_RESET_LAST_THREE_BITS_B && a < AND_RESET_LAST_THREE_BITS_A_TO {
492 Some(AND_RESET_LAST_THREE_BITS_OFFSET + a as usize)
493 } else if b == AND_GET_LAST_THREE_BITS_B
494 && (AND_GET_LAST_THREE_BITS_FROM..AND_GET_LAST_THREE_BITS_TO).contains(&a)
495 && a & 0x7 == 0
496 {
497 Some(
498 AND_GET_LAST_THREE_BITS_OFFSET + ((a - AND_GET_LAST_THREE_BITS_FROM) >> 3) as usize,
499 )
500 } else if a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE {
501 Some(Self::get_low_values_offset(a, b))
502 } else {
503 None
504 }
505 }
506
507 fn build_or(&mut self) {
508 let mut ops: Vec<[u64; 2]> = Vec::new();
509 for i in MAX_A_LOW_VALUE..OR_TO_A {
510 for j in 0..=OR_TO_B {
511 ops.push([i, j]);
512 }
513 }
514 self.add_ops(OP_OR, &mut ops, true);
515 }
516 #[inline(always)]
517 fn is_frequent_or(a: u64, b: u64) -> bool {
518 (a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE) || (a < OR_TO_A && b <= OR_TO_B)
519 }
520 #[inline(always)]
521 fn get_or_offset(a: u64, b: u64) -> Option<usize> {
522 if a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE {
523 Some(Self::get_low_values_offset(a, b))
524 } else if a < OR_TO_A && b <= OR_TO_B {
525 Some(LOW_VALUE_SIZE + ((a - MAX_A_LOW_VALUE) * (OR_TO_B + 1) + b) as usize)
526 } else {
527 None
528 }
529 }
530
531 fn build_srl(&mut self) {
532 let mut ops: Vec<[u64; 2]> = Vec::new();
533 for i in SLR_MASK_FROM..=MAX_U64 {
534 for j in 0..=SLR_TO_B {
535 ops.push([i, j]);
536 }
537 }
538 self.add_ops(OP_SRL, &mut ops, true);
539 }
540 #[inline(always)]
541 fn is_frequent_srl(a: u64, b: u64) -> bool {
542 (a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE) || (a >= SLR_MASK_FROM && b <= SLR_TO_B)
543 }
544 #[inline(always)]
545 fn get_srl_offset(a: u64, b: u64) -> Option<usize> {
546 if a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE {
547 Some(Self::get_low_values_offset(a, b))
548 } else if a >= SLR_MASK_FROM && b <= SLR_TO_B {
549 Some(LOW_VALUE_SIZE + ((a - SLR_MASK_FROM) * (SLR_TO_B + 1) + b) as usize)
550 } else {
551 None
552 }
553 }
554 fn build_sub_w(&mut self) {
555 let mut ops: Vec<[u64; 2]> = Vec::new();
556 for i in (SUB_W_ADDR_FROM..SUB_W_ADDR_TO).step_by(SUB_W_ADDR_STEP as usize) {
557 ops.push([0, i]);
558 }
559 self.add_ops(OP_SUBW, &mut ops, true);
560 }
561 #[inline(always)]
562 fn is_frequent_sub_w(a: u64, b: u64) -> bool {
563 (a == 0 && ((b & 0xFFFF_FFFF_FFF0_0003 == 0xA010_0000) || b < MAX_B_LOW_VALUE))
564 || (a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE)
565 }
566 #[inline(always)]
567 fn get_sub_w_offset(a: u64, b: u64) -> Option<usize> {
568 if a == 0 {
569 if b & 0xFFFF_FFFF_FFF0_0003 == 0xA010_0000 {
570 Some(LOW_VALUE_SIZE + ((b - SUB_W_ADDR_FROM) >> 2) as usize)
571 } else if b < MAX_B_LOW_VALUE {
572 Some(Self::get_low_values_offset(0, b))
573 } else {
574 None
575 }
576 } else if a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE {
577 Some(Self::get_low_values_offset(a, b))
578 } else {
579 None
580 }
581 }
582 fn build_xor(&mut self) {
583 let mut ops: Vec<[u64; 2]> = Vec::new();
584 ops.push([0, MAX_U64]);
585 ops.push([1, MAX_U64]);
586 self.add_ops(OP_XOR, &mut ops, true);
587 }
588 #[inline(always)]
589 fn is_frequent_xor(a: u64, b: u64) -> bool {
590 (b == MAX_U64 && a < 2) || (a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE)
591 }
592 #[inline(always)]
593 fn get_xor_offset(a: u64, b: u64) -> Option<usize> {
594 if b == MAX_U64 {
595 if a < 2 {
596 Some(LOW_VALUE_SIZE + a as usize)
597 } else {
598 None
599 }
600 } else if a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE {
601 Some(Self::get_low_values_offset(a, b))
602 } else {
603 None
604 }
605 }
606 fn build_sub(&mut self) {
607 let mut ops: Vec<[u64; 2]> = Vec::new();
608 for i in MAX_A_LOW_VALUE..SUB_TO_A {
609 for j in 0..=SUB_TO_B {
610 ops.push([i, j]);
611 }
612 }
613 self.add_ops(OP_SUB, &mut ops, true);
614 }
615 #[inline(always)]
616 fn is_frequent_sub(a: u64, b: u64) -> bool {
617 (a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE) || (a < SUB_TO_A && b <= SUB_TO_B)
618 }
619 #[inline(always)]
620 fn get_sub_offset(a: u64, b: u64) -> Option<usize> {
621 if a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE {
622 Some(Self::get_low_values_offset(a, b))
623 } else if a < SUB_TO_A && b <= SUB_TO_B {
624 Some(LOW_VALUE_SIZE + ((a - MAX_A_LOW_VALUE) * (SUB_TO_B + 1) + b) as usize)
625 } else {
626 None
627 }
628 }
629 pub fn build_table(&mut self) {
630 self.build_low_values_operations();
631 self.build_eq_zero();
632 self.build_ltu_one();
633 self.build_lt();
634 self.build_add();
635 self.build_and();
636 self.build_or();
637 self.build_srl();
638 self.build_sub_w();
639 self.build_xor();
640 self.build_sub();
641 }
642 #[inline(always)]
643 fn get_low_values_offset(a: u64, b: u64) -> usize {
644 (a * MAX_B_LOW_VALUE + b) as usize
645 }
646
647 #[inline(always)]
648 pub fn is_frequent_op(op: u8, a: u64, b: u64) -> bool {
649 // Use lookup table for faster branching instead of match on enum
650 match op {
651 // Low value operations - check bounds first (most common case)
652 OP_SIGNEXTENDB | OP_SIGNEXTENDH | OP_SIGNEXTENDW | OP_ADDW | OP_SLL | OP_SLLW
653 | OP_SRA | OP_SRAW | OP_SRLW | OP_EQW | OP_LTUW | OP_LTW | OP_LEU | OP_LE | OP_LEUW
654 | OP_LEW | OP_MULU | OP_MULUH | OP_MULSUH | OP_MUL | OP_MULH | OP_MULW | OP_DIVU
655 | OP_REMU | OP_DIV | OP_REM | OP_DIVUW | OP_REMUW | OP_DIVW | OP_REMW => {
656 a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE
657 }
658 // Special cases - inline the logic to avoid function calls
659 OP_EQ => {
660 (b == 0 && a <= EQ_OP_B_ZERO_A_LIMIT)
661 || (b < MAX_B_LOW_VALUE && a < MAX_A_LOW_VALUE)
662 }
663 OP_LTU => {
664 (b == 1 && !(MAX_A_LOW_VALUE..LTU_OP_B_LT_ONE_FROM).contains(&a))
665 || (b < MAX_B_LOW_VALUE && a < MAX_A_LOW_VALUE)
666 }
667 OP_ADD => {
668 // Inline is_frequent_add logic
669 if b < MAX_B_LOW_VALUE {
670 if a < MAX_A_LOW_VALUE {
671 true
672 } else {
673 match b {
674 0 => {
675 (ADD_ZERO_FROM_ADDR..ADD_ZERO_TO_ADDR).contains(&a) && a & 0x7 == 0
676 }
677 1 => (ADD_ONE_FROM_ADDR..ADD_ONE_TO_ADDR).contains(&a),
678 8 => {
679 a & 0x7 == 0
680 && ((ADD_EIGHT_FROM_ADDR..ADD_EIGHT_TO_ADDR).contains(&a)
681 || (ADD_EIGHT_FROM_CODE..ADD_EIGHT_TO_CODE).contains(&a))
682 }
683 _ => false,
684 }
685 }
686 } else if b == MINUS_ONE {
687 a < MAX_ADD_MINUS_ONE
688 } else {
689 b >= ADD_MINUS_A_B_FROM_B && a < MAX_ADD_MINUS_A
690 }
691 }
692 OP_AND => {
693 // Inline is_frequent_and logic
694 (a == AND_CODE_ADDR_MASK
695 && (b & 0x03) == 0
696 && (AND_CODE_ADDR_FROM..AND_CODE_ADDR_TO).contains(&b))
697 || (b == AND_RESET_LAST_THREE_BITS_B && a < AND_RESET_LAST_THREE_BITS_A_TO)
698 || (b == AND_GET_LAST_THREE_BITS_B
699 && (AND_GET_LAST_THREE_BITS_FROM..AND_GET_LAST_THREE_BITS_TO).contains(&a)
700 && a & 0x7 == 0)
701 || (a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE)
702 }
703 // Other special cases - call functions for less common operations
704 OP_LT => Self::is_frequent_lt(a, b),
705 OP_SUBW => Self::is_frequent_sub_w(a, b),
706 OP_SUB => Self::is_frequent_sub(a, b),
707 OP_OR => Self::is_frequent_or(a, b),
708 OP_SRL => Self::is_frequent_srl(a, b),
709 OP_XOR => Self::is_frequent_xor(a, b),
710 _ => false,
711 }
712 }
713
714 #[inline(always)]
715 pub fn get_row(op: u8, a: u64, b: u64) -> Option<usize> {
716 // ecall/system call functions are not candidates to be usual
717 let relative_offset = match op {
718 OP_SIGNEXTENDB | OP_SIGNEXTENDH | OP_SIGNEXTENDW | OP_ADDW | OP_SLL | OP_SLLW
719 | OP_SRA | OP_SRAW | OP_SRLW | OP_EQW | OP_LTUW | OP_LTW | OP_LEU | OP_LE | OP_LEUW
720 | OP_LEW | OP_MULU | OP_MULUH | OP_MULSUH | OP_MUL | OP_MULH | OP_MULW | OP_DIVU
721 | OP_REMU | OP_DIV | OP_REM | OP_DIVUW | OP_REMUW | OP_DIVW | OP_REMW => {
722 if a < MAX_A_LOW_VALUE && b < MAX_B_LOW_VALUE {
723 Some(Self::get_low_values_offset(a, b))
724 } else {
725 None
726 }
727 }
728 OP_EQ => Self::get_eq_offset(a, b),
729 OP_LTU => Self::get_ltu_offset(a, b),
730 OP_LT => Self::get_lt_offset(a, b),
731 OP_SUBW => Self::get_sub_w_offset(a, b),
732 OP_SUB => Self::get_sub_offset(a, b),
733 OP_OR => Self::get_or_offset(a, b),
734 OP_SRL => Self::get_srl_offset(a, b),
735 OP_XOR => Self::get_xor_offset(a, b),
736 OP_AND => Self::get_and_offset(a, b),
737 OP_ADD => Self::get_add_offset(a, b),
738 _ => None,
739 };
740 relative_offset.map(|offset| OP_TABLE_OFFSETS[op as usize] + offset)
741 }
742 pub fn count(&self) -> usize {
743 self.table_ops.iter().map(|ops| ops.len()).sum()
744 }
745 pub fn print_table_offsets(&self) {
746 let offsets = self.generate_table_offsets();
747 println!("const OP_TABLE_OFFSETS: [usize; {}] = {:?};", offsets.len(), &offsets);
748 }
749
750 pub fn generate_table_offsets(&self) -> Vec<usize> {
751 let op_indexes = self.get_op_indexes();
752 let mut offsets: [usize; 256] = [0; 256];
753 let mut size: usize = 0;
754 for (op, index) in op_indexes.iter() {
755 offsets[*op as usize] = size;
756 size += self.table_ops[*index].len();
757 }
758 let mut last_non_zero: usize = 255;
759 while (offsets[last_non_zero] == 0) && (last_non_zero > 0) {
760 last_non_zero -= 1;
761 }
762
763 assert_eq!(size, 1 << 24);
764 offsets[..last_non_zero + 1].to_vec()
765 }
766 pub fn test_table_offsets(&self) {
767 let offsets = self.generate_table_offsets();
768 assert_eq!(offsets, OP_TABLE_OFFSETS);
769 }
770 pub fn generate_full_table(&self) -> Vec<(u8, u64, u64, u64, bool)> {
771 let op_indexes = self.get_op_indexes();
772 let mut table: Vec<(u8, u64, u64, u64, bool)> = Vec::new();
773 for (op, index) in op_indexes.iter() {
774 table.extend(
775 self.table_ops[*index]
776 .iter()
777 .map(|ab| {
778 let (c, flag) = ZiskOp::try_from_code(*op).unwrap().call_ab(ab[0], ab[1]);
779 (*op, ab[0], ab[1], c, flag)
780 })
781 .clone(),
782 );
783 }
784 table
785 }
786 pub fn generate_table(&self) -> Vec<(u8, u64, u64)> {
787 let op_indexes = self.get_op_indexes();
788 let mut table: Vec<(u8, u64, u64)> = Vec::new();
789 for (op, index) in op_indexes.iter() {
790 table.extend(self.table_ops[*index].iter().map(|ab| (*op, ab[0], ab[1])).clone());
791 }
792 table
793 }
794 pub fn get_op_indexes(&self) -> Vec<(u8, usize)> {
795 self.table_by_op
796 .iter()
797 .enumerate()
798 .filter(|(_, index)| *index != &FREQUENT_OP_EMPTY)
799 .map(|(op, index)| (op as u8, *index))
800 .collect()
801 }
802 pub fn get_list(&self) -> Vec<(u8, usize)> {
803 self.table_by_op
804 .iter()
805 .enumerate()
806 .filter(|(_, index)| *index != &FREQUENT_OP_EMPTY)
807 .map(|(op, index)| (op as u8, self.table_ops[*index].len()))
808 .collect()
809 }
810 pub fn get_top(&self, num: usize) -> Vec<(u8, usize)> {
811 let mut list = self.get_list();
812 list.sort_by(|a, b| b.1.cmp(&a.1));
813 list.truncate(num);
814 list
815 }
816 pub fn get_top10(&self) -> Vec<(u8, usize)> {
817 self.get_top(10)
818 }
819}
820
821#[test]
822fn test_frequent_ops() {
823 let mut fops = FrequentOpsTable::new();
824 fops.build_table();
825 let table = fops.generate_full_table();
826
827 let tests = [
828 (OP_ADD, 100, 100, true),
829 (OP_ADD, 100, -1i64 as u64, true),
830 (OP_ADD, 100000, 100000, false),
831 (OP_ADD, 100, -200000i64 as u64, false),
832 (OP_ADD, 100, -2i64 as u64, true),
833 (OP_ADD, 0xFFFF_FFFF_FFFF_FFFC, 0x8000_1000, false),
834 (OP_ADD, 0xFFFF_FFFF_FFFF_FFFC, 0xA010_1000, false),
835 ];
836 check_tests(&table, &tests);
837}
838
839#[test]
840fn test_all_accesible_values() {
841 let mut fops = FrequentOpsTable::new();
842 fops.build_table();
843 let table = fops.generate_full_table();
844 let tests = table.iter().map(|(op, a, b, _c, _f)| (*op, *a, *b, true)).collect::<Vec<_>>();
845 check_tests(&table, &tests);
846}
847
848#[test]
849fn test_low_values() {
850 let mut fops = FrequentOpsTable::new();
851 fops.build_table();
852 let table = fops.generate_full_table();
853 let mut tests: Vec<(u8, u64, u64, bool)> = Vec::new();
854
855 for op in 0..255 {
856 let found = LOW_VALUES_OPCODES.contains(&op);
857 tests.push((op, 0, MAX_B_LOW_VALUE - 1, found));
858 tests.push((op, 0, MAX_B_LOW_VALUE - 2, found));
859 tests.push((op, 1, MAX_B_LOW_VALUE - 1, found));
860 tests.push((op, 1, MAX_B_LOW_VALUE - 2, found));
861 tests.push((op, MAX_A_LOW_VALUE - 1, 0, found));
862 tests.push((op, MAX_A_LOW_VALUE - 2, 0, found));
863 tests.push((op, MAX_A_LOW_VALUE - 1, 1, found));
864 tests.push((op, MAX_A_LOW_VALUE - 2, 1, found));
865 tests.push((op, MAX_A_LOW_VALUE - 1, MAX_B_LOW_VALUE - 1, found));
866 tests.push((op, MAX_A_LOW_VALUE - 2, MAX_B_LOW_VALUE - 2, found));
867 tests.push((op, MAX_A_LOW_VALUE - 1, MAX_B_LOW_VALUE - 2, found));
868 tests.push((op, MAX_A_LOW_VALUE - 2, MAX_B_LOW_VALUE - 1, found));
869 }
870 check_tests(&table, &tests);
871}
872
873#[cfg(test)]
874fn check_tests(table: &[(u8, u64, u64, u64, bool)], tests: &[(u8, u64, u64, bool)]) {
875 for (itest, test) in tests.iter().enumerate() {
876 let op_name = if let Ok(_op) = ZiskOp::try_from_code(test.0) { _op.name() } else { "?" };
877 if let Some(index) = FrequentOpsTable::get_row(test.0, test.1, test.2) {
878 if !test.3
879 || table[index].0 != test.0
880 || table[index].1 != test.1
881 || table[index].2 != test.2
882 {
883 panic!(
884 "> #{} {1} 0x{2:X}({2}) 0x{3:X}({3}) {4} = {5} 0x{6:X}({6}) 0x{7:X}({7}) = 0x{8:X}({8}) F:{9} [\x1B[31mFAIL\x1B[0m]",
885 itest, op_name, test.1, test.2, test.3, ZiskOp::try_from_code(table[index].0).unwrap().name(), table[index].1, table[index].2, table[index].3, table[index].4 as u8
886 );
887 }
888 } else if test.3 {
889 panic!(
890 "> #{} {1} 0x{2:X}({2}) 0x{3:X}({3}) {4} = NOT FOUND [\x1B[31mFAIL\x1B[0m]",
891 itest, op_name, test.1, test.2, test.3
892 );
893 }
894 }
895 println!("Table Size: {}", table.len());
896}