1#![allow(dead_code)] #[derive(Clone, Copy, Debug)]
21pub struct FastCost {
22 pub cycles: u8,
23 pub decode_slots: u8,
24 pub exec_unit: u8,
26 pub src_mask: u16,
27 pub dst_mask: u16,
28 pub is_terminator: bool,
29 pub is_move_reg: bool,
30}
31
32const EU_NONE: u8 = 0;
33const EU_ALU: u8 = 1;
34const EU_LOAD: u8 = 2;
35const EU_STORE: u8 = 3;
36const EU_MUL: u8 = 4;
37const EU_DIV: u8 = 5;
38
39pub const DEFAULT_MEM_CYCLES: u8 = 25;
41
42const RVF_TERM: u8 = 1;
62const RVF_OVERLAP_DST_SRC: u8 = 2;
66const RVF_OVERLAP_RS1_RD: u8 = 4;
69
70#[derive(Clone, Copy)]
71struct RvGasCostEntry {
72 cycles: u8,
75 decode_slots: u8,
77 exec_unit: u8,
79 src_pat: u8,
81 dst_pat: u8,
85 flags: u8,
87 overlap_slots: u8,
90}
91
92#[allow(clippy::too_many_arguments)]
93const fn rgc(
94 cycles: u8,
95 decode_slots: u8,
96 exec_unit: u8,
97 src_pat: u8,
98 dst_pat: u8,
99 flags: u8,
100) -> RvGasCostEntry {
101 RvGasCostEntry {
102 cycles,
103 decode_slots,
104 exec_unit,
105 src_pat,
106 dst_pat,
107 flags,
108 overlap_slots: 0,
109 }
110}
111
112#[allow(clippy::too_many_arguments)]
113const fn rgc_ov(
114 cycles: u8,
115 overlap_if: u8,
116 overlap_no: u8,
117 exec_unit: u8,
118 src_pat: u8,
119 dst_pat: u8,
120 flags: u8,
121) -> RvGasCostEntry {
122 RvGasCostEntry {
123 cycles,
124 decode_slots: 0,
125 exec_unit,
126 src_pat,
127 dst_pat,
128 flags,
129 overlap_slots: overlap_if | (overlap_no << 4),
130 }
131}
132
133pub const RV_KIND_RESERVED: u8 = 0;
141pub const RV_KIND_TRAP: u8 = 1;
142pub const RV_KIND_FALLTHROUGH: u8 = 2;
143pub const RV_KIND_ECALL_JAR: u8 = 3;
144pub const RV_KIND_ECALLI: u8 = 4;
145pub const RV_KIND_JALR: u8 = 5; pub const RV_KIND_FENCE: u8 = 6;
147pub const RV_KIND_JAL: u8 = 7;
148pub const RV_KIND_BRANCH: u8 = 8;
149pub const RV_KIND_LOAD: u8 = 9;
150pub const RV_KIND_STORE: u8 = 10;
151pub const RV_KIND_LUI: u8 = 11;
152pub const RV_KIND_ADDI: u8 = 12; pub const RV_KIND_ADDIW: u8 = 13; pub const RV_KIND_ADD: u8 = 14; pub const RV_KIND_SLL: u8 = 15; pub const RV_KIND_SLT: u8 = 16; pub const RV_KIND_ADDW: u8 = 17; pub const RV_KIND_SLLW: u8 = 18; pub const RV_KIND_MUL: u8 = 19;
160pub const RV_KIND_MULW: u8 = 20;
161pub const RV_KIND_MULH: u8 = 21; pub const RV_KIND_MULHSU: u8 = 22;
163pub const RV_KIND_DIV: u8 = 23; pub const RV_KIND_ZBB_U1: u8 = 24; pub const RV_KIND_ZBB_CTZ: u8 = 25; pub const RV_KIND_ZBB_MINMAX: u8 = 26; pub const RV_KIND_ZBB_INV: u8 = 27; pub const RV_KIND_ZBB_XNOR: u8 = 28; pub const RV_KIND_ZBB_ROT: u8 = 29; pub const RV_KIND_ZBB_RORI: u8 = 30; pub const RV_KIND_ZBB_ROTW: u8 = 31; pub const RV_KIND_ZBB_RORIW: u8 = 32; pub const RV_KIND_ZBA: u8 = 33; pub const RV_KIND_ZBA_IMM: u8 = 34; pub const RV_KIND_ZBS: u8 = 35; pub const RV_KIND_ZBS_IMM: u8 = 36; pub const RV_KIND_ZICOND: u8 = 37; const RV_LUT_LEN: usize = 64;
180
181static RV_GAS_COST_LUT: [RvGasCostEntry; RV_LUT_LEN] = {
182 let d = rgc(2, 1, EU_NONE, 0, 0, RVF_TERM); let mut t = [d; RV_LUT_LEN];
184 t[RV_KIND_RESERVED as usize] = rgc(2, 1, EU_NONE, 0, 0, RVF_TERM);
186 t[RV_KIND_TRAP as usize] = rgc(2, 1, EU_NONE, 0, 0, RVF_TERM);
187 t[RV_KIND_FALLTHROUGH as usize] = rgc(2, 1, EU_NONE, 0, 0, RVF_TERM);
188 t[RV_KIND_ECALL_JAR as usize] = rgc(100, 4, EU_ALU, 0, 0, RVF_TERM);
189 t[RV_KIND_ECALLI as usize] = rgc(100, 4, EU_ALU, 0, 0, RVF_TERM);
190 t[RV_KIND_JALR as usize] = rgc(22, 1, EU_ALU, 1, 0, RVF_TERM);
194 t[RV_KIND_FENCE as usize] = rgc(1, 1, EU_NONE, 0, 0, 0);
196 t[RV_KIND_JAL as usize] = rgc(15, 1, EU_ALU, 0, 1, RVF_TERM);
198 t[RV_KIND_BRANCH as usize] = rgc(20, 1, EU_ALU, 2, 0, RVF_TERM);
202 t[RV_KIND_LOAD as usize] = rgc(25, 1, EU_LOAD, 1, 1, 0);
204 t[RV_KIND_STORE as usize] = rgc(25, 1, EU_STORE, 2, 0, 0);
205 t[RV_KIND_LUI as usize] = rgc(1, 2, EU_NONE, 0, 1, 0);
207 t[RV_KIND_ADDI as usize] = rgc_ov(1, 1, 2, EU_ALU, 1, 1, RVF_OVERLAP_DST_SRC);
209 t[RV_KIND_ADDIW as usize] = rgc_ov(2, 2, 3, EU_ALU, 1, 1, RVF_OVERLAP_DST_SRC);
211 t[RV_KIND_ADD as usize] = rgc_ov(1, 1, 2, EU_ALU, 2, 1, RVF_OVERLAP_DST_SRC);
213 t[RV_KIND_SLL as usize] = rgc_ov(1, 2, 3, EU_ALU, 2, 1, RVF_OVERLAP_RS1_RD);
215 t[RV_KIND_SLT as usize] = rgc(3, 3, EU_ALU, 2, 1, 0);
217 t[RV_KIND_ADDW as usize] = rgc_ov(2, 2, 3, EU_ALU, 2, 1, RVF_OVERLAP_DST_SRC);
219 t[RV_KIND_SLLW as usize] = rgc_ov(2, 3, 4, EU_ALU, 2, 1, RVF_OVERLAP_RS1_RD);
221 t[RV_KIND_MUL as usize] = rgc_ov(3, 1, 2, EU_MUL, 2, 1, RVF_OVERLAP_DST_SRC);
223 t[RV_KIND_MULW as usize] = rgc_ov(4, 2, 3, EU_MUL, 2, 1, RVF_OVERLAP_DST_SRC);
224 t[RV_KIND_MULH as usize] = rgc(4, 4, EU_MUL, 2, 1, 0);
225 t[RV_KIND_MULHSU as usize] = rgc(6, 4, EU_MUL, 2, 1, 0);
226 t[RV_KIND_DIV as usize] = rgc(60, 4, EU_DIV, 2, 1, 0);
228 t[RV_KIND_ZBB_U1 as usize] = rgc(1, 1, EU_ALU, 1, 1, 0);
230 t[RV_KIND_ZBB_CTZ as usize] = rgc(2, 1, EU_ALU, 1, 1, 0);
232 t[RV_KIND_ZBB_MINMAX as usize] = rgc_ov(3, 2, 3, EU_ALU, 2, 1, RVF_OVERLAP_DST_SRC);
234 t[RV_KIND_ZBB_INV as usize] = rgc(2, 3, EU_ALU, 2, 1, 0);
236 t[RV_KIND_ZBB_XNOR as usize] = rgc_ov(2, 2, 3, EU_ALU, 2, 1, RVF_OVERLAP_DST_SRC);
238 t[RV_KIND_ZBB_ROT as usize] = rgc_ov(1, 2, 3, EU_ALU, 2, 1, RVF_OVERLAP_RS1_RD);
240 t[RV_KIND_ZBB_RORI as usize] = rgc_ov(1, 1, 2, EU_ALU, 1, 1, RVF_OVERLAP_DST_SRC);
241 t[RV_KIND_ZBB_ROTW as usize] = rgc_ov(2, 3, 4, EU_ALU, 2, 1, RVF_OVERLAP_RS1_RD);
242 t[RV_KIND_ZBB_RORIW as usize] = rgc_ov(2, 2, 3, EU_ALU, 1, 1, RVF_OVERLAP_DST_SRC);
243 t[RV_KIND_ZBA as usize] = rgc_ov(1, 1, 2, EU_ALU, 2, 1, RVF_OVERLAP_DST_SRC);
245 t[RV_KIND_ZBA_IMM as usize] = rgc_ov(1, 1, 2, EU_ALU, 1, 1, RVF_OVERLAP_DST_SRC);
246 t[RV_KIND_ZBS as usize] = rgc_ov(1, 1, 2, EU_ALU, 2, 1, RVF_OVERLAP_DST_SRC);
248 t[RV_KIND_ZBS_IMM as usize] = rgc_ov(1, 1, 2, EU_ALU, 1, 1, RVF_OVERLAP_DST_SRC);
249 t[RV_KIND_ZICOND as usize] = rgc(2, 2, EU_ALU, 2, 1, 0);
251 t
252};
253
254#[inline(always)]
259pub fn rv_slot_u8(r: u8) -> u8 {
260 crate::regs::reg_slot_or_ff(r)
261}
262
263#[inline(always)]
267fn is_spilled_slot(slot: u8) -> bool {
268 slot == 13 || slot == 14
269}
270
271#[inline]
276pub fn rv_gas_meta(inst: &crate::instruction::Inst) -> crate::predecode::RvGasMeta {
277 let (kind, rs1, rs2, rd) = rv_op_metadata(inst);
278 let entry = &RV_GAS_COST_LUT[kind as usize];
279 let src1_slot = if entry.src_pat >= 1 {
282 rv_slot_u8(rs1)
283 } else {
284 0xFF
285 };
286 let src2_slot = if entry.src_pat == 2 {
287 rv_slot_u8(rs2)
288 } else {
289 0xFF
290 };
291 let dst_slot = if entry.dst_pat == 1 {
292 rv_slot_u8(rd)
293 } else {
294 0xFF
295 };
296 crate::predecode::RvGasMeta {
297 kind,
298 src1_slot,
299 src2_slot,
300 dst_slot,
301 }
302}
303
304#[inline(always)]
308fn rv_op_metadata(inst: &crate::instruction::Inst) -> (u8, u8, u8, u8) {
309 use crate::instruction::Inst::*;
310 match *inst {
311 Trap => (RV_KIND_TRAP, 0, 0, 0),
313 Fallthrough => (RV_KIND_FALLTHROUGH, 0, 0, 0),
314 EcallJar => (RV_KIND_ECALL_JAR, 0, 0, 0),
315 Ecalli { .. } => (RV_KIND_ECALLI, 0, 0, 0),
316 Fence | FenceI => (RV_KIND_FENCE, 0, 0, 0),
317 Reserved { .. } => (RV_KIND_RESERVED, 0, 0, 0),
318
319 Jal { rd, .. } => (RV_KIND_JAL, 0, 0, rd),
321
322 Jalr { rs1, .. } => (RV_KIND_JALR, rs1, 0, 0),
325
326 Beq { rs1, rs2, .. }
328 | Bne { rs1, rs2, .. }
329 | Blt { rs1, rs2, .. }
330 | Bge { rs1, rs2, .. }
331 | Bltu { rs1, rs2, .. }
332 | Bgeu { rs1, rs2, .. } => (RV_KIND_BRANCH, rs1, rs2, 0),
333
334 Lb { rd, rs1, .. }
336 | Lh { rd, rs1, .. }
337 | Lw { rd, rs1, .. }
338 | Ld { rd, rs1, .. }
339 | Lbu { rd, rs1, .. }
340 | Lhu { rd, rs1, .. }
341 | Lwu { rd, rs1, .. } => (RV_KIND_LOAD, rs1, 0, rd),
342
343 Sb { rs1, rs2, .. } | Sh { rs1, rs2, .. } | Sw { rs1, rs2, .. } | Sd { rs1, rs2, .. } => {
345 (RV_KIND_STORE, rs1, rs2, 0)
346 }
347
348 Lui { rd, .. } => (RV_KIND_LUI, 0, 0, rd),
350 Auipc { rd, .. } => (RV_KIND_LUI, 0, 0, rd),
351
352 Addi { rd, rs1, .. }
354 | Andi { rd, rs1, .. }
355 | Ori { rd, rs1, .. }
356 | Xori { rd, rs1, .. }
357 | Sltiu { rd, rs1, .. }
358 | Slti { rd, rs1, .. }
359 | Slli { rd, rs1, .. }
360 | Srli { rd, rs1, .. }
361 | Srai { rd, rs1, .. } => (RV_KIND_ADDI, rs1, 0, rd),
362
363 Addiw { rd, rs1, .. }
365 | Slliw { rd, rs1, .. }
366 | Srliw { rd, rs1, .. }
367 | Sraiw { rd, rs1, .. } => (RV_KIND_ADDIW, rs1, 0, rd),
368
369 Add { rd, rs1, rs2 }
371 | Sub { rd, rs1, rs2 }
372 | And { rd, rs1, rs2 }
373 | Or { rd, rs1, rs2 }
374 | Xor { rd, rs1, rs2 } => (RV_KIND_ADD, rs1, rs2, rd),
375 Sll { rd, rs1, rs2 } | Srl { rd, rs1, rs2 } | Sra { rd, rs1, rs2 } => {
377 (RV_KIND_SLL, rs1, rs2, rd)
378 }
379 Slt { rd, rs1, rs2 } | Sltu { rd, rs1, rs2 } => (RV_KIND_SLT, rs1, rs2, rd),
381
382 Addw { rd, rs1, rs2 } | Subw { rd, rs1, rs2 } => (RV_KIND_ADDW, rs1, rs2, rd),
384 Sllw { rd, rs1, rs2 } | Srlw { rd, rs1, rs2 } | Sraw { rd, rs1, rs2 } => {
386 (RV_KIND_SLLW, rs1, rs2, rd)
387 }
388
389 Mul { rd, rs1, rs2 } => (RV_KIND_MUL, rs1, rs2, rd),
391 Mulw { rd, rs1, rs2 } => (RV_KIND_MULW, rs1, rs2, rd),
392 Mulh { rd, rs1, rs2 } | Mulhu { rd, rs1, rs2 } => (RV_KIND_MULH, rs1, rs2, rd),
393 Mulhsu { rd, rs1, rs2 } => (RV_KIND_MULHSU, rs1, rs2, rd),
394
395 Div { rd, rs1, rs2 }
397 | Divu { rd, rs1, rs2 }
398 | Rem { rd, rs1, rs2 }
399 | Remu { rd, rs1, rs2 }
400 | Divw { rd, rs1, rs2 }
401 | Divuw { rd, rs1, rs2 }
402 | Remw { rd, rs1, rs2 }
403 | Remuw { rd, rs1, rs2 } => (RV_KIND_DIV, rs1, rs2, rd),
404
405 Clz { rd, rs1 }
407 | Clzw { rd, rs1 }
408 | Cpop { rd, rs1 }
409 | Cpopw { rd, rs1 }
410 | SextB { rd, rs1 }
411 | SextH { rd, rs1 }
412 | ZextH { rd, rs1 }
413 | Rev8 { rd, rs1 }
414 | OrcB { rd, rs1 } => (RV_KIND_ZBB_U1, rs1, 0, rd),
415 Ctz { rd, rs1 } | Ctzw { rd, rs1 } => (RV_KIND_ZBB_CTZ, rs1, 0, rd),
417 Min { rd, rs1, rs2 }
419 | Minu { rd, rs1, rs2 }
420 | Max { rd, rs1, rs2 }
421 | Maxu { rd, rs1, rs2 } => (RV_KIND_ZBB_MINMAX, rs1, rs2, rd),
422 Andn { rd, rs1, rs2 } | Orn { rd, rs1, rs2 } => (RV_KIND_ZBB_INV, rs1, rs2, rd),
424 Xnor { rd, rs1, rs2 } => (RV_KIND_ZBB_XNOR, rs1, rs2, rd),
425 Rol { rd, rs1, rs2 } | Ror { rd, rs1, rs2 } => (RV_KIND_ZBB_ROT, rs1, rs2, rd),
427 Rori { rd, rs1, .. } => (RV_KIND_ZBB_RORI, rs1, 0, rd),
428 Rolw { rd, rs1, rs2 } | Rorw { rd, rs1, rs2 } => (RV_KIND_ZBB_ROTW, rs1, rs2, rd),
429 Roriw { rd, rs1, .. } => (RV_KIND_ZBB_RORIW, rs1, 0, rd),
430
431 Sh1add { rd, rs1, rs2 }
433 | Sh2add { rd, rs1, rs2 }
434 | Sh3add { rd, rs1, rs2 }
435 | Sh1adduw { rd, rs1, rs2 }
436 | Sh2adduw { rd, rs1, rs2 }
437 | Sh3adduw { rd, rs1, rs2 }
438 | Adduw { rd, rs1, rs2 } => (RV_KIND_ZBA, rs1, rs2, rd),
439 Slliuw { rd, rs1, .. } => (RV_KIND_ZBA_IMM, rs1, 0, rd),
440
441 Bclr { rd, rs1, rs2 }
443 | Bset { rd, rs1, rs2 }
444 | Binv { rd, rs1, rs2 }
445 | Bext { rd, rs1, rs2 } => (RV_KIND_ZBS, rs1, rs2, rd),
446 Bclri { rd, rs1, .. }
447 | Bseti { rd, rs1, .. }
448 | Binvi { rd, rs1, .. }
449 | Bexti { rd, rs1, .. } => (RV_KIND_ZBS_IMM, rs1, 0, rd),
450
451 CzeroEqz { rd, rs1, rs2 } | CzeroNez { rd, rs1, rs2 } => (RV_KIND_ZICOND, rs1, rs2, rd),
453 }
454}
455
456#[inline(always)]
469pub fn rv_feed_gas_kind(
470 kind: u8,
471 src1: u8,
472 src2: u8,
473 dst: u8,
474 gas_sim: &mut crate::gas_sim::GasSimulator,
475 mem_cycles: u8,
476) -> bool {
477 let entry = &RV_GAS_COST_LUT[kind as usize];
478
479 let cycles = if entry.exec_unit == EU_LOAD || entry.exec_unit == EU_STORE {
481 mem_cycles
482 } else {
483 entry.cycles
484 };
485
486 let spill_ops =
493 is_spilled_slot(src1) as u8 + is_spilled_slot(src2) as u8 + is_spilled_slot(dst) as u8;
494 let cycles = cycles.saturating_add(mem_cycles.saturating_mul(spill_ops));
495
496 let decode_slots = if entry.flags & RVF_OVERLAP_DST_SRC != 0 {
498 let overlap = dst != 0xFF && (dst == src1 || dst == src2);
500 if overlap {
501 entry.overlap_slots & 0x0F
502 } else {
503 entry.overlap_slots >> 4
504 }
505 } else if entry.flags & RVF_OVERLAP_RS1_RD != 0 {
506 let overlap = dst != 0xFF && src1 != 0xFF && dst == src1;
508 if overlap {
509 entry.overlap_slots & 0x0F
510 } else {
511 entry.overlap_slots >> 4
512 }
513 } else {
514 entry.decode_slots
515 };
516
517 gas_sim.feed_direct(cycles, decode_slots, src1, src2, dst);
518 entry.flags & RVF_TERM != 0
519}
520
521#[inline(always)]
528pub fn rv_feed_gas_direct(
529 meta: &crate::predecode::RvGasMeta,
530 gas_sim: &mut crate::gas_sim::GasSimulator,
531 mem_cycles: u8,
532) -> bool {
533 rv_feed_gas_kind(
534 meta.kind,
535 meta.src1_slot,
536 meta.src2_slot,
537 meta.dst_slot,
538 gas_sim,
539 mem_cycles,
540 )
541}
542
543pub fn rv_gas_cost_for_block(
551 insts: &[crate::predecode::RvPreDecodedInst],
552 block_start: usize,
553 mem_cycles: u8,
554) -> u32 {
555 let mut end = block_start + 1;
556 while end < insts.len() && !insts[end].is_gas_block_start {
557 end += 1;
558 }
559 let mut sim = crate::gas_sim::GasSimulator::new();
560 for i in &insts[block_start..end] {
561 rv_feed_gas_direct(&i.gas_meta, &mut sim, mem_cycles);
562 }
563 sim.flush_and_get_cost()
564}
565
566#[derive(Clone, Copy, Debug, PartialEq, Eq)]
568pub enum MemClass {
569 None,
570 Load,
571 Store,
572}
573
574#[inline]
579pub fn rv_kind_mem_class(kind: u8) -> MemClass {
580 match RV_GAS_COST_LUT[kind as usize].exec_unit {
581 EU_LOAD => MemClass::Load,
582 EU_STORE => MemClass::Store,
583 _ => MemClass::None,
584 }
585}
586
587#[inline]
595pub fn rv_kind_reserve(kind: u8) -> u32 {
596 use crate::gas_const::{COW_COST, MAX_PAGES_PER_ACCESS};
597 let r = match rv_kind_mem_class(kind) {
598 MemClass::Load => 0,
599 MemClass::Store => COW_COST.saturating_mul(MAX_PAGES_PER_ACCESS),
600 MemClass::None => 0,
601 };
602 r.min(u32::MAX as u64) as u32
603}
604
605pub fn rv_block_reserve_for_block(
616 insts: &[crate::predecode::RvPreDecodedInst],
617 block_start: usize,
618) -> u32 {
619 let mut end = block_start + 1;
620 while end < insts.len() && !insts[end].is_gas_block_start {
621 end += 1;
622 }
623 let mut reserve: u64 = 0;
624 for i in &insts[block_start..end] {
625 reserve = reserve.saturating_add(rv_kind_reserve(i.gas_meta.kind) as u64);
626 }
627 reserve.min(u32::MAX as u64) as u32
628}
629
630#[cfg(test)]
631mod tests {
632 use super::*;
633 use crate::instruction::decode;
634 use crate::predecode::is_terminator;
635
636 fn lut_term(inst: &crate::instruction::Inst) -> bool {
640 RV_GAS_COST_LUT[rv_gas_meta(inst).kind as usize].flags & RVF_TERM != 0
641 }
642
643 #[test]
653 fn predecode_and_gas_lut_terminator_agree() {
654 for h in 0u16..=0xFFFF {
657 if h & 0b11 == 0b11 {
658 continue;
659 }
660 if let Some((inst, _len)) = decode(&h.to_le_bytes()) {
661 assert_eq!(
662 is_terminator(&inst),
663 lut_term(&inst),
664 "RVC halfword {h:#06x} -> {inst:?}"
665 );
666 }
667 }
668 for major in 0u32..0x20 {
674 let opcode7 = (major << 2) | 0b11;
675 for funct3 in 0u32..8 {
676 for &funct7 in &[0u32, 0b000_0001, 0b010_0000, 0b000_0111, 0b011_0000] {
677 let w = (funct7 << 25)
678 | (11 << 20)
679 | (10 << 15)
680 | (funct3 << 12)
681 | (10 << 7)
682 | opcode7;
683 if let Some((inst, _len)) = decode(&w.to_le_bytes()) {
684 assert_eq!(
685 is_terminator(&inst),
686 lut_term(&inst),
687 "word {w:#010x} -> {inst:?}"
688 );
689 }
690 }
691 }
692 }
693 }
694}