Source code for zisk/state-machines/frequent-ops/src/frequent_ops_table.rs

  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()
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}