1use alloc::vec;
25use alloc::vec::Vec;
26
27use super::asm::{Assembler, Cc, Label, Reg};
28use javm_exec::gas_sim::GasSimulator;
29
30pub(crate) const REG_MAP: [Reg; 13] = [
33 Reg::RBP, Reg::RBX, Reg::R12, Reg::R13, Reg::R14, Reg::RSI, Reg::RDI, Reg::R8, Reg::R9, Reg::R10, Reg::R11, Reg::RAX, Reg::RCX, ];
47
48pub(crate) const SCRATCH: Reg = Reg::RDX;
50
51pub(crate) use javm_exec::regs::REG_SLOT_LUT as RV_SLOT_LUT;
58
59#[inline(always)]
61pub(crate) fn rv_slot_or_ff(x: u8) -> u8 {
62 RV_SLOT_LUT[(x & 31) as usize]
63}
64pub(crate) const GAS: Reg = Reg::R15;
67
68pub const CTX_VA: u64 = 1u64 << 39;
80
81use super::JitContext;
82use memoffset::offset_of;
83
84pub const CTX_REGS: u64 = CTX_VA + offset_of!(JitContext, regs) as u64;
85pub const CTX_GAS: u64 = CTX_VA + offset_of!(JitContext, gas) as u64;
86pub const CTX_EXIT_REASON: u64 = CTX_VA + offset_of!(JitContext, exit_reason) as u64;
87pub const CTX_EXIT_ARG: u64 = CTX_VA + offset_of!(JitContext, exit_arg) as u64;
88pub const CTX_ENTRY_PC: u64 = CTX_VA + offset_of!(JitContext, entry_pc) as u64;
89pub const CTX_PC: u64 = CTX_VA + offset_of!(JitContext, pc) as u64;
90pub const CTX_DISPATCH_TABLE: u64 = CTX_VA + offset_of!(JitContext, dispatch_table) as u64;
91pub const CTX_CODE_BASE: u64 = CTX_VA + offset_of!(JitContext, code_base) as u64;
92pub const CTX_HOST_RSP_BASE: u64 = CTX_VA + offset_of!(JitContext, host_rsp_base) as u64;
93
94pub const EXIT_HALT: u32 = 0;
96pub const EXIT_PANIC: u32 = 1;
97pub const EXIT_OOG: u32 = 2;
98pub const EXIT_PAGE_FAULT: u32 = 3;
99pub const EXIT_HOST_CALL: u32 = 4;
100pub const EXIT_ECALL: u32 = 6;
101pub const EXIT_TRAP: u32 = 7;
102
103pub struct CompileResult {
105 pub native_code: Vec<u8>,
106 pub dispatch_entries: Vec<(u32, i32)>,
112 pub trap_table: Vec<(u32, u32, u32)>,
119 pub exit_label_offset: u32,
120 pub panic_offset: u32,
126}
127
128#[repr(C)]
130pub struct HelperFns {
131 pub mem_read_u8: u64,
132 pub mem_read_u16: u64,
133 pub mem_read_u32: u64,
134 pub mem_read_u64: u64,
135 pub mem_write_u8: u64,
136 pub mem_write_u16: u64,
137 pub mem_write_u32: u64,
138 pub mem_write_u64: u64,
139}
140
141#[derive(Clone, Copy, Debug)]
143pub(crate) enum RegDef {
144 Unknown,
146 Const(u32),
148 Shifted { src: usize, shift: u8 },
152 ScaledAdd { base: usize, idx: usize, shift: u8 },
155}
156
157struct PendingGas {
163 stub_label: Label,
164 block_pc: u32,
165 cmp_offset: usize,
166 sub_offset: usize,
167}
168
169pub struct Compiler {
171 pub asm: Assembler,
172 pub(crate) label_base: u32,
175 pub(crate) gas_block_pcs: Vec<u32>,
177 pub(crate) exit_label: Label,
179 oog_label: Label,
181 pub(crate) panic_label: Label,
183 oog_pc_label: Label,
185 pub(crate) oog_stubs: Vec<(Label, u32, u32)>, pub(crate) helpers: HelperFns,
189 pub(crate) bitmask_ptr: *const u8,
191 pub(crate) bitmask_len: usize,
192 pub(crate) reg_defs: [RegDef; 13],
194 pub(crate) reg_defs_active: u16,
196 pub(crate) last_add_cf: Option<(usize, usize, usize)>,
202 pub(crate) trap_entries: Vec<(u32, u32, u32)>,
207 pub(crate) mem_cycles: u8,
209 pub(crate) gas_sim: GasSimulator,
215 pub(crate) gas_reserve_accum: u32,
222 pub(crate) suppress_gas: bool,
229 pub(crate) code_base: u32,
233 pub(crate) code_len: u32,
236 pub(crate) rv_streaming: bool,
241 pub(crate) rv_pending_fwd_branches: Vec<(u32, u32, usize)>,
247 pub(crate) rv_valid_pc: Vec<bool>,
250}
251
252impl Compiler {
253 pub fn new(
254 helpers: HelperFns,
255 code_len: usize,
256 jit_va_base: u64,
257 mem_cycles: u8,
258 code_base: u32,
259 ) -> Self {
260 let estimated_native = code_len * 3 + 8192;
263 let estimated_labels = code_len + 1024;
265 let mut asm = Assembler::with_capacity(estimated_native, estimated_labels);
268 asm.set_jit_va_base(jit_va_base);
272 let _reserved = asm.new_label(); let exit_label = asm.new_label();
275 let oog_label = asm.new_label();
276 let panic_label = asm.new_label();
277 let oog_pc_label = asm.new_label();
278 let label_base = asm.labels_len() as u32;
283 asm.bulk_create_labels(code_len + 1);
284 Self {
285 label_base,
286 gas_block_pcs: Vec::with_capacity(1024),
287 asm,
288 exit_label,
289 oog_label,
290 panic_label,
291 oog_pc_label,
292 oog_stubs: Vec::with_capacity(1024),
293 reg_defs: [RegDef::Unknown; 13],
294 reg_defs_active: 0,
295 last_add_cf: None,
296 helpers,
297 bitmask_ptr: core::ptr::null(),
298 bitmask_len: 0,
299 trap_entries: Vec::with_capacity(2048),
300 mem_cycles,
301 gas_sim: GasSimulator::new(),
302 gas_reserve_accum: 0,
303 suppress_gas: false,
304 code_base,
305 code_len: code_len as u32,
306 rv_streaming: false,
307 rv_pending_fwd_branches: Vec::new(),
308 rv_valid_pc: Vec::new(),
309 }
310 }
311
312 #[inline(always)]
318 pub(crate) fn feed_gas_rv(&mut self, kind: u8, rs1: u8, rs2: u8, rd: u8) -> bool {
319 if self.suppress_gas {
323 let mut throwaway = GasSimulator::new();
324 return javm_exec::gas_cost::rv_feed_gas_kind(
325 kind,
326 rv_slot_or_ff(rs1),
327 rv_slot_or_ff(rs2),
328 rv_slot_or_ff(rd),
329 &mut throwaway,
330 self.mem_cycles,
331 );
332 }
333 self.gas_reserve_accum = self
337 .gas_reserve_accum
338 .saturating_add(javm_exec::gas_cost::rv_kind_reserve(kind));
339 javm_exec::gas_cost::rv_feed_gas_kind(
340 kind,
341 rv_slot_or_ff(rs1),
342 rv_slot_or_ff(rs2),
343 rv_slot_or_ff(rd),
344 &mut self.gas_sim,
345 self.mem_cycles,
346 )
347 }
348
349 #[inline]
351 pub(crate) fn label_for_pc(&self, pc: u32) -> Label {
352 Label(self.label_base + pc)
353 }
354
355 pub(crate) fn is_basic_block_start(&self, idx: u32) -> bool {
356 let i = idx as usize;
357 i < self.bitmask_len && unsafe { *self.bitmask_ptr.add(i) } == 1
360 }
361
362 pub(crate) fn emit_mem_read_sized(
366 &mut self,
367 dst: Reg,
368 fn_addr: u64,
369 width_bytes: u32,
370 pvm_pc: u32,
371 ) {
372 let w = if width_bytes > 0 {
373 width_bytes
374 } else if fn_addr == self.helpers.mem_read_u8 {
375 1
376 } else if fn_addr == self.helpers.mem_read_u16 {
377 2
378 } else if fn_addr == self.helpers.mem_read_u32 {
379 4
380 } else {
381 8
382 };
383
384 self.trap_entries
387 .push((self.asm.offset() as u32, pvm_pc, w));
388
389 match w {
390 1 => self.asm.movzx_load8_at_index(dst, SCRATCH),
391 2 => self.asm.movzx_load16_at_index(dst, SCRATCH),
392 4 => self.asm.mov_load32_at_index(dst, SCRATCH),
393 8 => self.asm.mov_load64_at_index(dst, SCRATCH),
394 _ => unreachable!(),
395 }
396 }
397
398 pub(crate) fn emit_mem_write(
401 &mut self,
402 _addr_in_scratch: bool,
403 val_reg: Reg,
404 fn_addr: u64,
405 pvm_pc: u32,
406 ) {
407 let w = if fn_addr == self.helpers.mem_write_u8 {
408 1u32
409 } else if fn_addr == self.helpers.mem_write_u16 {
410 2
411 } else if fn_addr == self.helpers.mem_write_u32 {
412 4
413 } else {
414 8
415 };
416
417 self.trap_entries
420 .push((self.asm.offset() as u32, pvm_pc, w));
421
422 match w {
423 1 => self.asm.mov_store8_at_index(SCRATCH, val_reg),
424 2 => self.asm.mov_store16_at_index(SCRATCH, val_reg),
425 4 => self.asm.mov_store32_at_index(SCRATCH, val_reg),
426 8 => self.asm.mov_store64_at_index(SCRATCH, val_reg),
427 _ => unreachable!(),
428 }
429 }
430
431 pub(crate) fn invalidate_dependents(&mut self, reg: usize) {
435 let mut active = self.reg_defs_active & !(1u16 << reg);
437 while active != 0 {
438 let i = active.trailing_zeros() as usize;
439 active &= active - 1;
440 let depends = match self.reg_defs[i] {
441 RegDef::Shifted { src, .. } => src == reg,
442 RegDef::ScaledAdd { base, idx, .. } => base == reg || idx == reg,
443 _ => false,
444 };
445 if depends {
446 self.reg_defs[i] = RegDef::Unknown;
447 self.reg_defs_active &= !(1u16 << i);
448 }
449 }
450 }
451
452 #[inline]
454 pub(crate) fn invalidate_reg(&mut self, reg: usize) {
455 self.reg_defs[reg] = RegDef::Unknown;
456 self.reg_defs_active &= !(1u16 << reg);
457 self.invalidate_dependents(reg);
458 }
459
460 #[inline]
462 pub(crate) fn invalidate_all_regs(&mut self) {
463 self.reg_defs = [RegDef::Unknown; 13];
464 self.reg_defs_active = 0;
465 }
466
467 pub(crate) fn emit_static_branch(
471 &mut self,
472 target: u32,
473 condition: bool,
474 _fallthrough: u32,
475 pc: u32,
476 ) {
477 if !condition {
478 return;
479 }
480 if self.rv_streaming && target > pc {
481 let label = self.label_for_pc(target);
482 let fixup_idx = self.asm.fixups_len();
483 self.asm.jmp_label(label);
484 self.rv_pending_fwd_branches.push((target, pc, fixup_idx));
485 return;
486 }
487 if !self.is_basic_block_start(target) {
488 self.asm.mov_store32_rip_rel_imm(CTX_PC, pc as i32);
489 self.emit_exit(EXIT_PANIC, 0);
490 return;
491 }
492 let label = self.label_for_pc(target);
493 self.asm.jmp_label(label);
494 }
495
496 pub(crate) fn emit_branch_reg(
498 &mut self,
499 a: Reg,
500 b: Reg,
501 cc: Cc,
502 target: u32,
503 _fallthrough: u32,
504 pc: u32,
505 ) {
506 if self.rv_streaming && target > pc {
507 self.asm.cmp_rr(a, b);
508 let label = self.label_for_pc(target);
509 let fixup_idx = self.asm.fixups_len();
510 self.asm.jcc_label(cc, label);
511 self.rv_pending_fwd_branches.push((target, pc, fixup_idx));
512 return;
513 }
514 if !self.is_basic_block_start(target) {
515 self.asm.mov_store32_rip_rel_imm(CTX_PC, pc as i32);
516 self.asm.cmp_rr(a, b);
517 self.asm.jcc_label(cc, self.panic_label);
518 return;
519 }
520 self.asm.cmp_rr(a, b);
521 let label = self.label_for_pc(target);
522 self.asm.jcc_label(cc, label);
523 }
524
525 pub(crate) fn emit_shift_by_reg32(&mut self, dst: Reg, shift_reg: Reg, shift_op: u8) {
528 if shift_reg == Reg::RCX {
531 self.asm.shift_cl32(shift_op, dst);
532 } else if dst == Reg::RCX {
533 self.emit_shift_dst_is_rcx(shift_reg, shift_op, false);
534 } else {
535 self.asm.push(Reg::RCX);
536 self.asm.mov_rr(Reg::RCX, shift_reg);
537 self.asm.shift_cl32(shift_op, dst);
538 self.asm.pop(Reg::RCX);
539 }
540 }
541
542 pub(crate) fn emit_shift_by_reg64(&mut self, dst: Reg, shift_reg: Reg, shift_op: u8) {
543 if shift_reg == Reg::RCX {
544 self.asm.shift_cl64(shift_op, dst);
545 } else if dst == Reg::RCX {
546 self.emit_shift_dst_is_rcx(shift_reg, shift_op, true);
547 } else {
548 self.asm.push(Reg::RCX);
549 self.asm.mov_rr(Reg::RCX, shift_reg);
550 self.asm.shift_cl64(shift_op, dst);
551 self.asm.pop(Reg::RCX);
552 }
553 }
554
555 fn emit_shift_dst_is_rcx(&mut self, shift_reg: Reg, shift_op: u8, is_64: bool) {
567 self.asm.push(Reg::RCX); self.asm.mov_rr(Reg::RCX, shift_reg); self.asm.pop(SCRATCH); if is_64 {
571 self.asm.shift_cl64(shift_op, SCRATCH);
572 } else {
573 self.asm.shift_cl32(shift_op, SCRATCH);
574 }
575 self.asm.mov_rr(Reg::RCX, SCRATCH); }
577
578 pub(crate) fn emit_exit(&mut self, reason: u32, arg: u32) {
580 self.asm
581 .mov_store32_rip_rel_imm(CTX_EXIT_REASON, reason as i32);
582 self.asm.mov_store32_rip_rel_imm(CTX_EXIT_ARG, arg as i32);
583 self.asm.jmp_label(self.exit_label);
584 }
585
586 pub(crate) fn emit_prologue(&mut self) {
589 self.asm.ensure_capacity(512); self.asm.push(Reg::RBX);
592 self.asm.push(Reg::RBP);
593 self.asm.push(Reg::R12);
594 self.asm.push(Reg::R13);
595 self.asm.push(Reg::R14);
596 self.asm.push(Reg::R15);
597
598 self.asm.push(SCRATCH); self.asm.mov_store64_rip_rel(CTX_HOST_RSP_BASE, Reg::RSP);
610
611 self.asm.mov_load64_rip_rel(GAS, CTX_GAS);
616
617 self.asm.mov_store32_rip_rel_imm(CTX_EXIT_REASON, 0);
619
620 self.asm.mov_load32_rip_rel(SCRATCH, CTX_ENTRY_PC);
622 self.asm.mov_load64_rip_rel(Reg::RAX, CTX_DISPATCH_TABLE);
623 self.asm.movsxd_load_sib4(Reg::RAX, Reg::RAX, SCRATCH);
624 self.asm.mov_load64_rip_rel(SCRATCH, CTX_CODE_BASE);
625 self.asm.add_rr(Reg::RAX, SCRATCH);
626 self.asm.push(Reg::RAX);
627
628 for (i, ®) in REG_MAP.iter().enumerate() {
630 self.asm.mov_load64_rip_rel(reg, CTX_REGS + (i as u64) * 8);
631 }
632
633 self.asm.pop(SCRATCH);
635 self.asm.jmp_reg(SCRATCH);
636 }
637
638 pub(crate) fn emit_exit_sequences(&mut self) {
640 let needed = 512 + self.oog_stubs.len() * 16;
643 self.asm.ensure_capacity(needed);
644 self.asm.bind_label(self.oog_pc_label);
647 self.asm.mov_store32_rip_rel(CTX_PC, SCRATCH);
648 self.asm.bind_label(self.oog_label);
650 self.asm
651 .mov_store32_rip_rel_imm(CTX_EXIT_REASON, EXIT_OOG as i32);
652 self.asm.jmp_label(self.exit_label);
653
654 let stubs = core::mem::take(&mut self.oog_stubs);
657 for (label, pvm_pc, _cost) in &stubs {
658 self.asm.bind_label(*label);
659 self.asm.mov_ri32(SCRATCH, *pvm_pc);
660 self.asm.jmp_label(self.oog_pc_label);
661 }
662
663 self.asm.bind_label(self.panic_label);
667 self.asm
668 .mov_store32_rip_rel_imm(CTX_EXIT_REASON, EXIT_PANIC as i32);
669 self.asm.bind_label(self.exit_label);
673 self.asm.mov_store64_rip_rel(CTX_GAS, GAS);
674 for (i, ®) in REG_MAP.iter().enumerate() {
675 self.asm.mov_store64_rip_rel(CTX_REGS + (i as u64) * 8, reg);
676 }
677
678 self.asm.mov_load64_rip_rel(Reg::RSP, CTX_HOST_RSP_BASE);
683
684 self.asm.pop(SCRATCH); self.asm.pop(Reg::R15);
687 self.asm.pop(Reg::R14);
688 self.asm.pop(Reg::R13);
689 self.asm.pop(Reg::R12);
690 self.asm.pop(Reg::RBP);
691 self.asm.pop(Reg::RBX);
692 self.asm.ret();
693 }
694}
695
696#[inline]
708fn peek_alu_rr_trailer(rest: &[u8]) -> Option<(AluOp, u8, u8, u8, usize)> {
709 if rest.len() < 2 {
710 return None;
711 }
712 if rest[0] & 0b11 == 0b11 {
714 if rest.len() < 4 {
715 return None;
716 }
717 let w = u32::from_le_bytes([rest[0], rest[1], rest[2], rest[3]]);
718 let op = match w & 0xFE00_707F {
719 0x0000_0033 => AluOp::Add,
720 0x0000_4033 => AluOp::Xor,
721 0x0000_6033 => AluOp::Or,
722 0x0000_7033 => AluOp::And,
723 _ => return None,
724 };
725 let rd = ((w >> 7) & 0x1F) as u8;
726 let rs1 = ((w >> 15) & 0x1F) as u8;
727 let rs2 = ((w >> 20) & 0x1F) as u8;
728 return Some((op, rd, rs1, rs2, 4));
729 }
730 let h = u16::from_le_bytes([rest[0], rest[1]]);
732 if h & 0xF003 == 0x9002 {
735 let rd = ((h >> 7) & 0x1F) as u8;
736 let rs2 = ((h >> 2) & 0x1F) as u8;
737 if rd != 0 && rs2 != 0 {
738 return Some((AluOp::Add, rd, rd, rs2, 2));
739 }
740 return None;
741 }
742 if h & 0xFC03 == 0x8C01 {
745 let op = match (h >> 5) & 0x3 {
746 0b01 => AluOp::Xor,
747 0b10 => AluOp::Or,
748 0b11 => AluOp::And,
749 _ => return None, };
751 let rd = ((h >> 7) & 0x7) as u8 + 8; let rs2 = ((h >> 2) & 0x7) as u8 + 8; return Some((op, rd, rd, rs2, 2));
754 }
755 None
756}
757
758const OP_LOAD: u32 = 0b00_000;
766const OP_MISC_MEM: u32 = 0b00_011;
767const OP_IMM: u32 = 0b00_100;
768const OP_OP_IMM_32: u32 = 0b00_110;
769const OP_STORE: u32 = 0b01_000;
770const OP_OP: u32 = 0b01_100;
771const OP_LUI: u32 = 0b01_101;
772const OP_AUIPC: u32 = 0b00_101;
773const OP_OP_32: u32 = 0b01_110;
774const OP_BRANCH: u32 = 0b11_000;
775const OP_JAL: u32 = 0b11_011;
776const OP_JALR: u32 = 0b11_001;
777const OP_CUSTOM_0: u32 = 0b00_010;
778
779#[inline]
785fn is_custom0_ecall(w: u32) -> bool {
786 let opcode5 = (w >> 2) & 0x1F;
787 let f3 = (w >> 12) & 0x7;
788 opcode5 == OP_CUSTOM_0 && (f3 == 0b001 || f3 == 0b010)
789}
790
791#[inline]
794fn imm_i(w: u32) -> i32 {
795 (w as i32) >> 20
796}
797#[inline]
798fn imm_s(w: u32) -> i32 {
799 let hi = (w >> 25) & 0x7F;
800 let lo = (w >> 7) & 0x1F;
801 let raw = ((hi << 5) | lo) as i32;
802 (raw << 20) >> 20
803}
804#[inline]
805fn imm_b(w: u32) -> i32 {
806 let b12 = (w >> 31) & 1;
807 let b11 = (w >> 7) & 1;
808 let b10_5 = (w >> 25) & 0x3F;
809 let b4_1 = (w >> 8) & 0xF;
810 let raw = (b12 << 12) | (b11 << 11) | (b10_5 << 5) | (b4_1 << 1);
811 ((raw as i32) << 19) >> 19
812}
813#[inline]
814fn imm_j(w: u32) -> i32 {
815 let b20 = (w >> 31) & 1;
816 let b10_1 = (w >> 21) & 0x3FF;
817 let b11 = (w >> 20) & 1;
818 let b19_12 = (w >> 12) & 0xFF;
819 let raw = (b20 << 20) | (b19_12 << 12) | (b11 << 11) | (b10_1 << 1);
820 ((raw as i32) << 11) >> 11
821}
822#[inline]
823fn imm_u(w: u32) -> i32 {
824 (w & 0xFFFFF000) as i32
825}
826
827#[inline]
838fn enc_i(opcode5: u32, f3: u32, rd: u8, rs1: u8, imm: i32) -> u32 {
839 let imm12 = (imm as u32) & 0xFFF;
840 (imm12 << 20) | ((rs1 as u32) << 15) | (f3 << 12) | ((rd as u32) << 7) | (opcode5 << 2) | 0b11
841}
842#[inline]
843fn enc_s(opcode5: u32, f3: u32, rs1: u8, rs2: u8, imm: i32) -> u32 {
844 let imm12 = (imm as u32) & 0xFFF;
845 ((imm12 >> 5) << 25)
846 | ((rs2 as u32) << 20)
847 | ((rs1 as u32) << 15)
848 | (f3 << 12)
849 | ((imm12 & 0x1F) << 7)
850 | (opcode5 << 2)
851 | 0b11
852}
853#[inline]
854fn enc_b(opcode5: u32, f3: u32, rs1: u8, rs2: u8, imm: i32) -> u32 {
855 let imm13 = (imm as u32) & 0x1FFF;
856 let b12 = (imm13 >> 12) & 1;
857 let b11 = (imm13 >> 11) & 1;
858 let b10_5 = (imm13 >> 5) & 0x3F;
859 let b4_1 = (imm13 >> 1) & 0xF;
860 (b12 << 31)
861 | (b10_5 << 25)
862 | ((rs2 as u32) << 20)
863 | ((rs1 as u32) << 15)
864 | (f3 << 12)
865 | (b4_1 << 8)
866 | (b11 << 7)
867 | (opcode5 << 2)
868 | 0b11
869}
870#[inline]
871fn enc_j(opcode5: u32, rd: u8, imm: i32) -> u32 {
872 let imm21 = (imm as u32) & 0x1FFFFF;
873 let b20 = (imm21 >> 20) & 1;
874 let b10_1 = (imm21 >> 1) & 0x3FF;
875 let b11 = (imm21 >> 11) & 1;
876 let b19_12 = (imm21 >> 12) & 0xFF;
877 (b20 << 31)
878 | (b10_1 << 21)
879 | (b11 << 20)
880 | (b19_12 << 12)
881 | ((rd as u32) << 7)
882 | (opcode5 << 2)
883 | 0b11
884}
885#[inline]
886fn enc_u(opcode5: u32, rd: u8, imm: i32) -> u32 {
887 let imm_u = (imm as u32) & 0xFFFFF000;
888 imm_u | ((rd as u32) << 7) | (opcode5 << 2) | 0b11
889}
890#[inline]
891fn enc_r(opcode5: u32, f3: u32, f7: u32, rd: u8, rs1: u8, rs2: u8) -> u32 {
892 (f7 << 25)
893 | ((rs2 as u32) << 20)
894 | ((rs1 as u32) << 15)
895 | (f3 << 12)
896 | ((rd as u32) << 7)
897 | (opcode5 << 2)
898 | 0b11
899}
900#[inline]
901fn enc_shimm6(opcode5: u32, f3: u32, shtype6: u32, rd: u8, rs1: u8, shamt6: u8) -> u32 {
902 (shtype6 << 26)
903 | ((shamt6 as u32) << 20)
904 | ((rs1 as u32) << 15)
905 | (f3 << 12)
906 | ((rd as u32) << 7)
907 | (opcode5 << 2)
908 | 0b11
909}
910
911#[inline]
913fn creg(r: u16) -> u8 {
914 (r + 8) as u8
915}
916
917#[inline]
919fn decode_ci_imm6(h: u16) -> i32 {
920 let imm = (((h >> 12) & 1) << 5) | ((h >> 2) & 0x1F);
921 ((imm as i32) << 26) >> 26
922}
923
924#[inline]
926fn decode_cj_imm(h: u16) -> i32 {
927 let b11 = (h >> 12) & 1;
928 let b4 = (h >> 11) & 1;
929 let b9_8 = (h >> 9) & 0x3;
930 let b10 = (h >> 8) & 1;
931 let b6 = (h >> 7) & 1;
932 let b7 = (h >> 6) & 1;
933 let b3_1 = (h >> 3) & 0x7;
934 let b5 = (h >> 2) & 1;
935 let imm = (b11 << 11)
936 | (b10 << 10)
937 | (b9_8 << 8)
938 | (b7 << 7)
939 | (b6 << 6)
940 | (b5 << 5)
941 | (b4 << 4)
942 | (b3_1 << 1);
943 ((imm as i32) << 20) >> 20
944}
945
946#[inline]
948fn decode_cb_imm(h: u16) -> i32 {
949 let b8 = (h >> 12) & 1;
950 let b4_3 = (h >> 10) & 0x3;
951 let b7_6 = (h >> 5) & 0x3;
952 let b2_1 = (h >> 3) & 0x3;
953 let b5 = (h >> 2) & 1;
954 let imm = (b8 << 8) | (b7_6 << 6) | (b5 << 5) | (b4_3 << 3) | (b2_1 << 1);
955 ((imm as i32) << 23) >> 23
956}
957
958fn expand_rvc(h: u16) -> Option<u32> {
970 if h == 0 {
972 return None;
973 }
974 let op = h & 0b11;
975 let f3 = (h >> 13) & 0b111;
976 match op {
977 0b00 => expand_rvc_q0(h, f3),
978 0b01 => expand_rvc_q1(h, f3),
979 0b10 => expand_rvc_q2(h, f3),
980 _ => None,
981 }
982}
983
984fn expand_rvc_q0(h: u16, f3: u16) -> Option<u32> {
985 let rs1c = creg((h >> 7) & 0b111);
986 let rdrs2c = creg((h >> 2) & 0b111);
987 match f3 {
988 0b000 => {
989 let n = (((h >> 11) & 0x3) << 4)
992 | (((h >> 7) & 0xF) << 6)
993 | (((h >> 6) & 0x1) << 2)
994 | (((h >> 5) & 0x1) << 3);
995 if n == 0 {
996 return None;
997 }
998 Some(enc_i(OP_IMM, 0b000, rdrs2c, 2, n as i32))
999 }
1000 0b010 => {
1001 let imm = (((h >> 10) & 0x7) << 3) | (((h >> 6) & 0x1) << 2) | (((h >> 5) & 0x1) << 6);
1003 Some(enc_i(OP_LOAD, 0b010, rdrs2c, rs1c, imm as i32))
1004 }
1005 0b011 => {
1006 let imm = (((h >> 10) & 0x7) << 3) | (((h >> 5) & 0x3) << 6);
1008 Some(enc_i(OP_LOAD, 0b011, rdrs2c, rs1c, imm as i32))
1009 }
1010 0b110 => {
1011 let imm = (((h >> 10) & 0x7) << 3) | (((h >> 6) & 0x1) << 2) | (((h >> 5) & 0x1) << 6);
1013 Some(enc_s(OP_STORE, 0b010, rs1c, rdrs2c, imm as i32))
1014 }
1015 0b111 => {
1016 let imm = (((h >> 10) & 0x7) << 3) | (((h >> 5) & 0x3) << 6);
1018 Some(enc_s(OP_STORE, 0b011, rs1c, rdrs2c, imm as i32))
1019 }
1020 _ => None,
1021 }
1022}
1023
1024fn expand_rvc_q1(h: u16, f3: u16) -> Option<u32> {
1025 match f3 {
1026 0b000 => {
1027 let rd = ((h >> 7) & 0x1F) as u8;
1029 let imm = decode_ci_imm6(h);
1030 if rd == 0 {
1031 Some(enc_i(OP_IMM, 0b000, 0, 0, 0)) } else {
1033 Some(enc_i(OP_IMM, 0b000, rd, rd, imm))
1034 }
1035 }
1036 0b001 => {
1037 let rd = ((h >> 7) & 0x1F) as u8;
1039 if rd == 0 {
1040 return None;
1041 }
1042 Some(enc_i(OP_OP_IMM_32, 0b000, rd, rd, decode_ci_imm6(h)))
1043 }
1044 0b010 => {
1045 let rd = ((h >> 7) & 0x1F) as u8;
1047 if rd == 0 {
1048 return None;
1049 }
1050 Some(enc_i(OP_IMM, 0b000, rd, 0, decode_ci_imm6(h)))
1051 }
1052 0b011 => {
1053 let rd = ((h >> 7) & 0x1F) as u8;
1055 if rd == 2 {
1056 let imm = (((h >> 12) & 1) << 9)
1057 | (((h >> 6) & 1) << 4)
1058 | (((h >> 5) & 1) << 6)
1059 | (((h >> 3) & 0x3) << 7)
1060 | (((h >> 2) & 1) << 5);
1061 let sx = ((imm as i32) << 22) >> 22;
1062 if sx == 0 {
1063 return None;
1064 }
1065 Some(enc_i(OP_IMM, 0b000, 2, 2, sx))
1066 } else if rd == 0 {
1067 None
1068 } else {
1069 let h_u = h as u32;
1070 let imm = (((h_u >> 12) & 1) << 17) | (((h_u >> 2) & 0x1F) << 12);
1071 let sx = ((imm as i32) << 14) >> 14;
1072 if sx == 0 {
1073 return None;
1074 }
1075 Some(enc_u(OP_LUI, rd, sx))
1076 }
1077 }
1078 0b100 => expand_rvc_q1_misc_alu(h),
1079 0b101 => {
1080 Some(enc_j(OP_JAL, 0, decode_cj_imm(h)))
1082 }
1083 0b110 | 0b111 => {
1084 let rs1 = creg((h >> 7) & 0b111);
1086 let imm = decode_cb_imm(h);
1087 let f3b = if f3 == 0b110 { 0b000 } else { 0b001 };
1088 Some(enc_b(OP_BRANCH, f3b, rs1, 0, imm))
1089 }
1090 _ => None,
1091 }
1092}
1093
1094fn expand_rvc_q1_misc_alu(h: u16) -> Option<u32> {
1095 let f6_10 = (h >> 10) & 0b11;
1096 let rdrs1c = creg((h >> 7) & 0b111);
1097 match f6_10 {
1098 0b00 | 0b01 => {
1099 let shamt = ((((h >> 12) & 1) << 5) | ((h >> 2) & 0x1F)) as u8;
1101 let shtype = if f6_10 == 0b00 { 0b000000 } else { 0b010000 };
1102 Some(enc_shimm6(OP_IMM, 0b101, shtype, rdrs1c, rdrs1c, shamt))
1103 }
1104 0b10 => {
1105 Some(enc_i(OP_IMM, 0b111, rdrs1c, rdrs1c, decode_ci_imm6(h)))
1107 }
1108 0b11 => {
1109 let rs2c = creg((h >> 2) & 0b111);
1111 let bit12 = (h >> 12) & 1;
1112 let f2 = (h >> 5) & 0b11;
1113 match (bit12, f2) {
1114 (0, 0b00) => Some(enc_r(OP_OP, 0b000, 0b0100000, rdrs1c, rdrs1c, rs2c)), (0, 0b01) => Some(enc_r(OP_OP, 0b100, 0b0000000, rdrs1c, rdrs1c, rs2c)), (0, 0b10) => Some(enc_r(OP_OP, 0b110, 0b0000000, rdrs1c, rdrs1c, rs2c)), (0, 0b11) => Some(enc_r(OP_OP, 0b111, 0b0000000, rdrs1c, rdrs1c, rs2c)), (1, 0b00) => Some(enc_r(OP_OP_32, 0b000, 0b0100000, rdrs1c, rdrs1c, rs2c)), (1, 0b01) => Some(enc_r(OP_OP_32, 0b000, 0b0000000, rdrs1c, rdrs1c, rs2c)), _ => None,
1123 }
1124 }
1125 _ => None,
1126 }
1127}
1128
1129fn expand_rvc_q2(h: u16, f3: u16) -> Option<u32> {
1130 let rdrs1 = ((h >> 7) & 0x1F) as u8;
1131 let rs2 = ((h >> 2) & 0x1F) as u8;
1132 match f3 {
1133 0b000 => {
1134 if rdrs1 == 0 {
1136 return None;
1137 }
1138 let shamt = ((((h >> 12) & 1) << 5) | ((h >> 2) & 0x1F)) as u8;
1139 Some(enc_shimm6(OP_IMM, 0b001, 0b000000, rdrs1, rdrs1, shamt))
1140 }
1141 0b010 => {
1142 if rdrs1 == 0 {
1144 return None;
1145 }
1146 let imm = (((h >> 12) & 1) << 5) | (((h >> 4) & 0x7) << 2) | (((h >> 2) & 0x3) << 6);
1147 Some(enc_i(OP_LOAD, 0b010, rdrs1, 2, imm as i32))
1148 }
1149 0b011 => {
1150 if rdrs1 == 0 {
1152 return None;
1153 }
1154 let imm = (((h >> 12) & 1) << 5) | (((h >> 5) & 0x3) << 3) | (((h >> 2) & 0x7) << 6);
1155 Some(enc_i(OP_LOAD, 0b011, rdrs1, 2, imm as i32))
1156 }
1157 0b100 => {
1158 let bit12 = (h >> 12) & 1;
1165 if rs2 == 0 {
1166 if rdrs1 == 0 {
1169 return None;
1170 }
1171 let rd = if bit12 == 0 { 0 } else { 1 };
1172 Some(enc_i(OP_JALR, 0b000, rd, rdrs1, 0))
1173 } else {
1174 if rdrs1 == 0 {
1176 return None;
1177 }
1178 let rs1_enc = if bit12 == 0 { 0 } else { rdrs1 };
1179 Some(enc_r(OP_OP, 0b000, 0b0000000, rdrs1, rs1_enc, rs2))
1180 }
1181 }
1182 0b110 => {
1183 let imm = (((h >> 9) & 0xF) << 2) | (((h >> 7) & 0x3) << 6);
1185 Some(enc_s(OP_STORE, 0b010, 2, rs2, imm as i32))
1186 }
1187 0b111 => {
1188 let imm = (((h >> 10) & 0x7) << 3) | (((h >> 7) & 0x7) << 6);
1190 Some(enc_s(OP_STORE, 0b011, 2, rs2, imm as i32))
1191 }
1192 _ => None,
1193 }
1194}
1195
1196#[inline]
1205fn rv_slot(x: u8) -> Option<usize> {
1206 match RV_SLOT_LUT[(x & 31) as usize] {
1207 s if s <= 12 => Some(s as usize),
1208 _ => None, }
1210}
1211
1212#[inline]
1217fn spill_va(x: u8) -> u64 {
1218 CTX_REGS + (RV_SLOT_LUT[(x & 31) as usize] as u64) * 8
1219}
1220
1221use javm_exec::regs::reg_is_reserved as rv_is_reserved;
1224use javm_exec::regs::reg_is_spilled;
1227
1228impl Compiler {
1229 pub fn compile(mut self, code: &[u8]) -> CompileResult {
1244 self.rv_valid_pc = vec![false; code.len()];
1250 self.bitmask_ptr = self.rv_valid_pc.as_ptr() as *const u8;
1251 self.bitmask_len = self.rv_valid_pc.len();
1252 self.rv_streaming = true;
1253
1254 self.emit_prologue();
1255
1256 let mut pending_gas: Option<PendingGas> = None;
1257 let mut next_is_gas_start = true;
1258 let mut pc: usize = 0;
1259
1260 while pc < code.len() {
1261 self.asm.ensure_capacity(512);
1262
1263 if pc + 2 > code.len() {
1267 self.rv_emit_panic_at(pc as u32);
1268 break;
1269 }
1270 let is_4byte = code[pc] & 0b11 == 0b11;
1271 let base_len = if is_4byte { 4 } else { 2 };
1272 if pc + base_len > code.len() {
1273 self.rv_emit_panic_at(pc as u32);
1274 break;
1275 }
1276
1277 let inst_pc = pc as u32;
1278
1279 let ecall_block = is_4byte
1286 && is_custom0_ecall(u32::from_le_bytes([
1287 code[pc],
1288 code[pc + 1],
1289 code[pc + 2],
1290 code[pc + 3],
1291 ]));
1292 if ecall_block {
1293 next_is_gas_start = true;
1294 }
1295
1296 if next_is_gas_start {
1297 self.bind_rv_gas_block_start_streaming(inst_pc, ecall_block, &mut pending_gas);
1298 next_is_gas_start = false;
1299 }
1300
1301 let rest = &code[pc + base_len..];
1308 let (term, preserve_cf, extra) = if is_4byte {
1309 let w = u32::from_le_bytes([code[pc], code[pc + 1], code[pc + 2], code[pc + 3]]);
1310 self.compile_rv4(w, inst_pc, 4, rest)
1311 } else {
1312 let h = u16::from_le_bytes([code[pc], code[pc + 1]]);
1313 self.compile_rvc(h, inst_pc, rest)
1314 };
1315
1316 if !preserve_cf {
1317 self.last_add_cf = None;
1318 }
1319
1320 if term {
1321 next_is_gas_start = true;
1322 }
1323
1324 pc += base_len + extra;
1325 }
1326
1327 self.flush_pending_gas(&mut pending_gas);
1329
1330 self.rv_streaming = false;
1341 let pending = core::mem::take(&mut self.rv_pending_fwd_branches);
1342 for (target, branch_pc, fixup_idx) in pending {
1343 if !self.is_basic_block_start(target) {
1344 let stub = self.asm.new_label();
1345 self.asm.bind_label(stub);
1346 self.asm.mov_store32_rip_rel_imm(CTX_PC, branch_pc as i32);
1347 self.asm.jmp_label(self.panic_label);
1348 self.asm.redirect_fixup(fixup_idx, stub);
1349 }
1350 }
1351
1352 self.emit_exit_sequences();
1353
1354 let mut dispatch_entries: Vec<(u32, i32)> = Vec::with_capacity(self.gas_block_pcs.len());
1358 for &pc in self.gas_block_pcs.iter() {
1359 let label = Label(self.label_base + pc);
1360 if let Some(off) = self.asm.label_offset(label) {
1361 dispatch_entries.push((pc, off as i32));
1362 }
1363 }
1364
1365 let exit_label_offset = self.asm.label_offset(self.exit_label).unwrap_or(0) as u32;
1366 let panic_offset = self
1372 .asm
1373 .label_offset(self.panic_label)
1374 .expect("panic stub label must resolve") as u32;
1375 let trap_table = core::mem::take(&mut self.trap_entries);
1376
1377 CompileResult {
1378 native_code: self.asm.finalize(),
1379 dispatch_entries,
1380 trap_table,
1381 exit_label_offset,
1382 panic_offset,
1383 }
1384 }
1385
1386 fn bind_rv_gas_block_start_streaming(
1392 &mut self,
1393 pc: u32,
1394 is_ecall: bool,
1395 pending: &mut Option<PendingGas>,
1396 ) {
1397 let label = Label(self.label_base + pc);
1398 self.asm.bind_label(label);
1399 self.gas_block_pcs.push(pc);
1400 if (pc as usize) < self.rv_valid_pc.len() {
1408 self.rv_valid_pc[pc as usize] = true;
1409 }
1410
1411 self.invalidate_all_regs();
1414 self.last_add_cf = None;
1415
1416 self.flush_pending_gas(pending);
1417 self.gas_sim.reset();
1418 self.gas_reserve_accum = 0;
1419
1420 if is_ecall {
1426 return;
1427 }
1428
1429 let stub_label = self.asm.new_label();
1437 self.asm.cmp_r64_imm32_patchable(GAS, 0);
1438 let cmp_offset = self.asm.offset() - 4;
1439 self.asm.jcc_label(Cc::L, stub_label);
1440 self.asm.sub_r64_imm32_patchable(GAS, 0);
1441 let sub_offset = self.asm.offset() - 4;
1442 *pending = Some(PendingGas {
1443 stub_label,
1444 block_pc: pc,
1445 cmp_offset,
1446 sub_offset,
1447 });
1448 }
1449
1450 fn flush_pending_gas(&mut self, pending: &mut Option<PendingGas>) {
1455 if let Some(p) = pending.take() {
1456 let cost = self.gas_sim.flush_and_get_cost();
1457 let gate = cost.saturating_add(self.gas_reserve_accum);
1458 self.asm.patch_i32(p.cmp_offset, gate as i32);
1459 self.asm.patch_i32(p.sub_offset, cost as i32);
1460 self.oog_stubs.push((p.stub_label, p.block_pc, cost));
1461 }
1462 }
1463
1464 fn compile_rv4(&mut self, w: u32, pc: u32, inst_len: u32, rest: &[u8]) -> (bool, bool, usize) {
1480 use javm_exec::gas_cost::*;
1481 let opcode = (w >> 2) & 0x1F;
1482 let rd = ((w >> 7) & 0x1F) as u8;
1483 let rs1 = ((w >> 15) & 0x1F) as u8;
1484 let rs2 = ((w >> 20) & 0x1F) as u8;
1485 let f3 = ((w >> 12) & 0x07) as u8;
1486 let f7 = ((w >> 25) & 0x7F) as u8;
1487
1488 if javm_exec::instruction::word_uses_reserved_reg(w) {
1493 self.rv_emit_panic_at(pc);
1494 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
1495 return (true, false, 0);
1496 }
1497
1498 if javm_exec::instruction::word_uses_spilled_reg(w) {
1505 return self.compile_rv_spilled(w, pc, inst_len);
1506 }
1507
1508 match opcode {
1509 OP_LOAD => self.compile_load(rd, rs1, f3, w, pc, rest),
1510 OP_STORE => self.compile_store(rs1, rs2, f3, w, pc),
1511 OP_IMM => self.compile_op_imm(rd, rs1, f3, w, pc),
1512 OP_OP_IMM_32 => self.compile_op_imm_32(rd, rs1, f3, w, pc),
1513 OP_OP => self.compile_op(rd, rs1, rs2, f3, f7, w, pc, rest),
1514 OP_OP_32 => self.compile_op_32(rd, rs1, rs2, f3, f7, w, pc),
1515 OP_LUI => self.compile_lui(rd, w, pc, rest),
1516 OP_AUIPC => self.compile_auipc(rd, w, pc),
1517 OP_JAL => self.compile_jal(rd, w, pc, inst_len),
1518 OP_JALR if f3 == 0 => self.compile_jalr(rd, rs1, w, pc, inst_len),
1519 OP_BRANCH => self.compile_branch(rs1, rs2, f3, w, pc),
1520 OP_CUSTOM_0 => self.compile_custom_0(rd, rs1, f3, w, pc),
1521 OP_MISC_MEM => {
1522 self.feed_gas_rv(RV_KIND_FENCE, 0, 0, 0);
1524 (false, false, 0)
1525 }
1526 _ => {
1530 self.rv_emit_panic_at(pc);
1531 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
1532 (true, false, 0)
1533 }
1534 }
1535 }
1536
1537 fn compile_rvc(&mut self, h: u16, pc: u32, rest: &[u8]) -> (bool, bool, usize) {
1553 use javm_exec::gas_cost::*;
1554 match expand_rvc(h) {
1555 Some(w) => self.compile_rv4(w, pc, 2, rest),
1558 None => {
1559 self.rv_emit_panic_at(pc);
1560 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
1561 (true, false, 0)
1562 }
1563 }
1564 }
1565
1566 fn compile_rv_spilled(&mut self, w: u32, pc: u32, inst_len: u32) -> (bool, bool, usize) {
1581 use javm_exec::gas_cost::{rv_feed_gas_direct, rv_gas_meta};
1582 let inst = javm_exec::instruction::decode(&w.to_le_bytes())
1585 .expect("4-byte spilled word decodes")
1586 .0;
1587 let meta = rv_gas_meta(&inst);
1588 self.gas_reserve_accum = self
1591 .gas_reserve_accum
1592 .saturating_add(javm_exec::gas_cost::rv_kind_reserve(meta.kind));
1593 let term = rv_feed_gas_direct(&meta, &mut self.gas_sim, self.mem_cycles);
1594
1595 let opcode = (w >> 2) & 0x1F;
1596 let f3 = (w >> 12) & 0x07;
1597 let rd = ((w >> 7) & 0x1F) as u8;
1598 let rs1 = ((w >> 15) & 0x1F) as u8;
1599 let rs2 = ((w >> 20) & 0x1F) as u8;
1600
1601 self.suppress_gas = true;
1602 match opcode {
1603 OP_JAL => self.rv_jal_spilled(rd, imm_j(w), pc, pc + inst_len),
1607 OP_JALR if f3 == 0 => self.rv_jalr_spilled(rd, rs1, imm_i(w), pc, pc + inst_len),
1608 OP_BRANCH => self.rv_branch_spilled(rs1, rs2, f3 as u8, imm_b(w), pc),
1609 OP_LOAD | OP_STORE | OP_IMM | OP_OP_IMM_32 | OP_OP | OP_OP_32 | OP_LUI | OP_AUIPC => {
1613 self.emit_spilled_via_donors(w, pc, inst_len)
1614 }
1615 _ => self.rv_emit_panic_at(pc),
1618 }
1619 self.suppress_gas = false;
1620 (term, false, 0)
1621 }
1622
1623 fn emit_spilled_via_donors(&mut self, w: u32, pc: u32, inst_len: u32) {
1628 let opcode = (w >> 2) & 0x1F;
1629 let rd = ((w >> 7) & 0x1F) as u8;
1630 let rs1 = ((w >> 15) & 0x1F) as u8;
1631 let rs2 = ((w >> 20) & 0x1F) as u8;
1632
1633 let (has_rd, has_rs1, has_rs2) = match opcode {
1635 OP_LOAD | OP_IMM | OP_OP_IMM_32 => (true, true, false),
1636 OP_OP | OP_OP_32 => (true, true, true),
1637 OP_STORE => (false, true, true),
1638 OP_LUI | OP_AUIPC => (true, false, false),
1639 _ => {
1640 self.rv_emit_panic_at(pc);
1641 return;
1642 }
1643 };
1644
1645 let mut blocked = [false; 16];
1648 let mut block = |x: u8| {
1649 if x != 0 && !reg_is_spilled(x) && !rv_is_reserved(x) {
1650 blocked[x as usize] = true;
1651 }
1652 };
1653 if has_rd {
1654 block(rd);
1655 }
1656 if has_rs1 {
1657 block(rs1);
1658 }
1659 if has_rs2 {
1660 block(rs2);
1661 }
1662
1663 let appears = |x: u8| (has_rd && rd == x) || (has_rs1 && rs1 == x) || (has_rs2 && rs2 == x);
1665 let need3 = appears(3);
1666 let need4 = appears(4);
1667
1668 const CANDIDATES: [u8; 13] = [1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1670 let pick = |blocked: &mut [bool; 16]| -> u8 {
1671 for &c in &CANDIDATES {
1672 if !blocked[c as usize] {
1673 blocked[c as usize] = true;
1674 return c;
1675 }
1676 }
1677 unreachable!("≤2 operands block ≤2 of 13 candidates")
1678 };
1679 let donor3 = if need3 { pick(&mut blocked) } else { 0 };
1680 let donor4 = if need4 { pick(&mut blocked) } else { 0 };
1681
1682 let mut donors = [0u8; 2];
1684 let mut n = 0;
1685 if need3 {
1686 donors[n] = donor3;
1687 n += 1;
1688 }
1689 if need4 {
1690 donors[n] = donor4;
1691 n += 1;
1692 }
1693 for d in &donors[..n] {
1694 self.asm.push(REG_MAP[rv_slot(*d).unwrap()]);
1695 }
1696
1697 let src3 = (has_rs1 && rs1 == 3) || (has_rs2 && rs2 == 3);
1699 let src4 = (has_rs1 && rs1 == 4) || (has_rs2 && rs2 == 4);
1700 if need3 && src3 {
1701 self.asm
1702 .mov_load64_rip_rel(REG_MAP[rv_slot(donor3).unwrap()], spill_va(3));
1703 }
1704 if need4 && src4 {
1705 self.asm
1706 .mov_load64_rip_rel(REG_MAP[rv_slot(donor4).unwrap()], spill_va(4));
1707 }
1708
1709 for d in &donors[..n] {
1712 self.invalidate_reg(rv_slot(*d).unwrap());
1713 }
1714
1715 let repl = |r: u8| -> u8 {
1717 if r == 3 {
1718 donor3
1719 } else if r == 4 {
1720 donor4
1721 } else {
1722 r
1723 }
1724 };
1725 let mut wr = w;
1726 if has_rd {
1727 wr = (wr & !(0x1F << 7)) | ((repl(rd) as u32) << 7);
1728 }
1729 if has_rs1 {
1730 wr = (wr & !(0x1F << 15)) | ((repl(rs1) as u32) << 15);
1731 }
1732 if has_rs2 {
1733 wr = (wr & !(0x1F << 20)) | ((repl(rs2) as u32) << 20);
1734 }
1735
1736 let _ = self.compile_rv4(wr, pc, inst_len, &[]);
1739
1740 if has_rd && reg_is_spilled(rd) {
1742 let donor = if rd == 3 { donor3 } else { donor4 };
1743 self.asm
1744 .mov_store64_rip_rel(spill_va(rd), REG_MAP[rv_slot(donor).unwrap()]);
1745 }
1746
1747 for d in &donors[..n] {
1749 self.invalidate_reg(rv_slot(*d).unwrap());
1750 }
1751 for d in donors[..n].iter().rev() {
1752 self.asm.pop(REG_MAP[rv_slot(*d).unwrap()]);
1753 }
1754 }
1755
1756 fn rv_jal_spilled(&mut self, rd: u8, imm: i32, pc: u32, next_pc: u32) {
1760 let ret = self.code_base.wrapping_add(next_pc);
1761 self.asm.mov_store32_rip_rel_imm(spill_va(rd), ret as i32);
1762 self.asm.mov_store32_rip_rel_imm(spill_va(rd) + 4, 0);
1763 let target = (pc as i64).wrapping_add(imm as i64) as u32;
1764 self.emit_static_branch(target, true, next_pc, pc);
1765 }
1766
1767 fn rv_jalr_spilled(&mut self, rd: u8, rs1: u8, imm: i32, pc: u32, next_pc: u32) {
1772 use super::asm::Cc;
1773 self.rv_read(rs1, SCRATCH, pc);
1775 if imm != 0 {
1776 self.asm.add_ri(SCRATCH, imm);
1777 }
1778 self.asm.shl_ri64(SCRATCH, 32);
1779 self.asm.shr_ri64(SCRATCH, 32);
1780 if rd != 0 {
1783 let ret = self.code_base.wrapping_add(next_pc);
1784 if reg_is_spilled(rd) {
1785 self.asm.mov_store32_rip_rel_imm(spill_va(rd), ret as i32);
1786 self.asm.mov_store32_rip_rel_imm(spill_va(rd) + 4, 0);
1787 } else {
1788 let slot = rv_slot(rd).unwrap();
1789 self.asm.mov_ri64(REG_MAP[slot], ret as u64);
1790 self.invalidate_reg(slot);
1791 }
1792 }
1793 if self.code_base != 0 {
1794 self.asm.sub_ri(SCRATCH, self.code_base as i32);
1795 }
1796 self.asm.mov_store32_rip_rel(CTX_PC, SCRATCH);
1797 self.asm.cmp_ri32(SCRATCH, self.code_len as i32);
1798 self.asm.jcc_label(Cc::AE, self.panic_label);
1799 self.asm.push(Reg::RAX);
1800 self.asm.mov_load64_rip_rel(Reg::RAX, CTX_DISPATCH_TABLE);
1801 self.asm.movsxd_load_sib4(Reg::RAX, Reg::RAX, SCRATCH);
1802 self.asm.add_r64_mem_rip_rel(Reg::RAX, CTX_CODE_BASE);
1803 self.asm.mov_rr(SCRATCH, Reg::RAX);
1804 self.asm.pop(Reg::RAX);
1805 self.asm.jmp_reg(SCRATCH);
1806 }
1807
1808 fn rv_branch_spilled(&mut self, rs1: u8, rs2: u8, f3: u8, imm: i32, pc: u32) {
1813 let cc = match f3 {
1814 0b000 => Cc::E,
1815 0b001 => Cc::NE,
1816 0b100 => Cc::L,
1817 0b101 => Cc::GE,
1818 0b110 => Cc::B,
1819 0b111 => Cc::AE,
1820 _ => {
1821 self.rv_emit_panic_at(pc);
1822 return;
1823 }
1824 };
1825 let target = (pc as i64).wrapping_add(imm as i64) as u32;
1826 let a = self.rv_read_into(rs1, SCRATCH, pc);
1827 if reg_is_spilled(rs2) {
1828 self.asm.cmp_r64_mem_rip_rel(a, spill_va(rs2));
1829 } else if rs2 == 0 {
1830 self.asm.cmp_ri32(a, 0);
1831 } else {
1832 self.asm.cmp_rr(a, REG_MAP[rv_slot(rs2).unwrap()]);
1833 }
1834 self.emit_cond_branch_to(cc, target, pc);
1835 }
1836
1837 fn emit_cond_branch_to(&mut self, cc: Cc, target: u32, pc: u32) {
1842 if self.rv_streaming && target > pc {
1843 let label = self.label_for_pc(target);
1844 let fixup_idx = self.asm.fixups_len();
1845 self.asm.jcc_label(cc, label);
1846 self.rv_pending_fwd_branches.push((target, pc, fixup_idx));
1847 return;
1848 }
1849 if !self.is_basic_block_start(target) {
1850 self.asm.mov_store32_rip_rel_imm(CTX_PC, pc as i32);
1851 self.asm.jcc_label(cc, self.panic_label);
1852 return;
1853 }
1854 let label = self.label_for_pc(target);
1855 self.asm.jcc_label(cc, label);
1856 }
1857
1858 fn compile_load(
1861 &mut self,
1862 rd: u8,
1863 rs1: u8,
1864 f3: u8,
1865 w: u32,
1866 pc: u32,
1867 rest: &[u8],
1868 ) -> (bool, bool, usize) {
1869 use javm_exec::gas_cost::*;
1870 let imm = imm_i(w);
1871 let (width, signed) = match f3 {
1872 0b000 => (1u32, true),
1873 0b001 => (2, true),
1874 0b010 => (4, true),
1875 0b011 => (8, false),
1876 0b100 => (1, false),
1877 0b101 => (2, false),
1878 0b110 => (4, false),
1879 _ => {
1880 self.rv_emit_panic_at(pc);
1881 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
1882 return (true, false, 0);
1883 }
1884 };
1885 if width == 8
1890 && rd != 0
1891 && !rv_is_reserved(rd)
1892 && !rv_is_reserved(rs1)
1893 && let Some((op, a_rd, a_rs1, a_rs2, consumed)) = peek_alu_rr_trailer(rest)
1894 && a_rd != 0
1895 && !rv_is_reserved(a_rd)
1896 && (a_rs1 == rd || a_rs2 == rd)
1897 && (a_rs1 == 0 || !rv_is_reserved(a_rs1))
1898 && (a_rs2 == 0 || !rv_is_reserved(a_rs2))
1899 {
1900 self.rv_load(rd, rs1, imm, 8, false, pc);
1901 self.feed_gas_rv(RV_KIND_LOAD, rs1, 0, rd);
1902 let next_pc = pc + 4;
1903 self.rv_alu_rr(a_rd, a_rs1, a_rs2, op, next_pc);
1904 if matches!(op, AluOp::Add) && a_rd != a_rs1 && a_rd != a_rs2 {
1906 self.track_add_scaledadd(a_rd, a_rs1, a_rs2);
1907 }
1908 self.feed_gas_rv(RV_KIND_ADD, a_rs1, a_rs2, a_rd);
1909 let preserve_cf = matches!(op, AluOp::Add);
1911 return (false, preserve_cf, consumed);
1912 }
1913 self.rv_load(rd, rs1, imm, width, signed, pc);
1914 let term = self.feed_gas_rv(RV_KIND_LOAD, rs1, 0, rd);
1915 (term, false, 0)
1916 }
1917
1918 fn compile_store(&mut self, rs1: u8, rs2: u8, f3: u8, w: u32, pc: u32) -> (bool, bool, usize) {
1919 use javm_exec::gas_cost::*;
1920 let imm = imm_s(w);
1921 let width = match f3 {
1922 0b000 => 1u32,
1923 0b001 => 2,
1924 0b010 => 4,
1925 0b011 => 8,
1926 _ => {
1927 self.rv_emit_panic_at(pc);
1928 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
1929 return (true, false, 0);
1930 }
1931 };
1932 self.rv_store(rs1, rs2, imm, width, pc);
1933 let term = self.feed_gas_rv(RV_KIND_STORE, rs1, rs2, 0);
1934 (term, false, 0)
1935 }
1936
1937 fn compile_op_imm(&mut self, rd: u8, rs1: u8, f3: u8, w: u32, pc: u32) -> (bool, bool, usize) {
1938 use javm_exec::gas_cost::*;
1939 match f3 {
1940 0b000 => {
1941 let imm = imm_i(w);
1943 self.rv_alu_imm(rd, rs1, imm, AluImmOp::Add, pc);
1944 if rs1 == 0 {
1945 self.track_const(rd, imm);
1946 }
1947 let term = self.feed_gas_rv(RV_KIND_ADDI, rs1, 0, rd);
1948 (term, false, 0)
1949 }
1950 0b010 => {
1951 let imm = imm_i(w);
1952 self.rv_slt_imm(rd, rs1, imm, true, pc);
1953 let term = self.feed_gas_rv(RV_KIND_ADDI, rs1, 0, rd);
1954 (term, false, 0)
1955 }
1956 0b011 => {
1957 let imm = imm_i(w);
1958 self.rv_slt_imm(rd, rs1, imm, false, pc);
1959 let term = self.feed_gas_rv(RV_KIND_ADDI, rs1, 0, rd);
1960 (term, false, 0)
1961 }
1962 0b100 => {
1963 let imm = imm_i(w);
1964 self.rv_alu_imm(rd, rs1, imm, AluImmOp::Xor, pc);
1965 let term = self.feed_gas_rv(RV_KIND_ADDI, rs1, 0, rd);
1966 (term, false, 0)
1967 }
1968 0b110 => {
1969 let imm = imm_i(w);
1970 self.rv_alu_imm(rd, rs1, imm, AluImmOp::Or, pc);
1971 let term = self.feed_gas_rv(RV_KIND_ADDI, rs1, 0, rd);
1972 (term, false, 0)
1973 }
1974 0b111 => {
1975 let imm = imm_i(w);
1976 self.rv_alu_imm(rd, rs1, imm, AluImmOp::And, pc);
1977 let term = self.feed_gas_rv(RV_KIND_ADDI, rs1, 0, rd);
1978 (term, false, 0)
1979 }
1980 0b001 => {
1981 let shtype = (w >> 26) & 0x3F;
1985 let shamt = ((w >> 20) & 0x3F) as u8;
1986 let rs2_field = (w >> 20) & 0x1F;
1987 match shtype {
1988 0b000000 => {
1989 self.rv_shift_imm(rd, rs1, shamt, ShiftOp::Shl64, pc);
1990 if (1..=3).contains(&shamt) && rs1 != rd {
1991 self.track_shifted(rd, rs1, shamt);
1992 }
1993 let term = self.feed_gas_rv(RV_KIND_ADDI, rs1, 0, rd);
1994 (term, false, 0)
1995 }
1996 0b010010 => {
1997 self.rv_bit_imm(rd, rs1, shamt, BitOp::Clear, pc);
1998 let term = self.feed_gas_rv(RV_KIND_ZBS_IMM, rs1, 0, rd);
1999 (term, false, 0)
2000 }
2001 0b001010 => {
2002 self.rv_bit_imm(rd, rs1, shamt, BitOp::Set, pc);
2003 let term = self.feed_gas_rv(RV_KIND_ZBS_IMM, rs1, 0, rd);
2004 (term, false, 0)
2005 }
2006 0b011010 => {
2007 self.rv_bit_imm(rd, rs1, shamt, BitOp::Invert, pc);
2008 let term = self.feed_gas_rv(RV_KIND_ZBS_IMM, rs1, 0, rd);
2009 (term, false, 0)
2010 }
2011 0b011000 => {
2012 let (op, kind) = match rs2_field {
2013 0b00000 => (UnaryOp::Clz64, RV_KIND_ZBB_U1),
2014 0b00001 => (UnaryOp::Ctz64, RV_KIND_ZBB_CTZ),
2015 0b00010 => (UnaryOp::Popcnt64, RV_KIND_ZBB_U1),
2016 0b00100 => (UnaryOp::SextB, RV_KIND_ZBB_U1),
2017 0b00101 => (UnaryOp::SextH, RV_KIND_ZBB_U1),
2018 _ => {
2019 self.rv_emit_panic_at(pc);
2020 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2021 return (true, false, 0);
2022 }
2023 };
2024 self.rv_unary(rd, rs1, op, pc);
2025 let term = self.feed_gas_rv(kind, rs1, 0, rd);
2026 (term, false, 0)
2027 }
2028 _ => {
2029 self.rv_emit_panic_at(pc);
2030 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2031 (true, false, 0)
2032 }
2033 }
2034 }
2035 0b101 => {
2036 let shtype = (w >> 26) & 0x3F;
2038 let shamt = ((w >> 20) & 0x3F) as u8;
2039 let rs2_field = (w >> 20) & 0x1F;
2040 match shtype {
2041 0b000000 => {
2042 self.rv_shift_imm(rd, rs1, shamt, ShiftOp::Shr64, pc);
2043 let term = self.feed_gas_rv(RV_KIND_ADDI, rs1, 0, rd);
2044 (term, false, 0)
2045 }
2046 0b010000 => {
2047 self.rv_shift_imm(rd, rs1, shamt, ShiftOp::Sar64, pc);
2048 let term = self.feed_gas_rv(RV_KIND_ADDI, rs1, 0, rd);
2049 (term, false, 0)
2050 }
2051 0b010010 => {
2052 self.rv_bit_imm(rd, rs1, shamt, BitOp::Extract, pc);
2053 let term = self.feed_gas_rv(RV_KIND_ZBS_IMM, rs1, 0, rd);
2054 (term, false, 0)
2055 }
2056 0b011000 => {
2057 self.rv_shift_imm(rd, rs1, shamt, ShiftOp::Ror64, pc);
2058 let term = self.feed_gas_rv(RV_KIND_ZBB_RORI, rs1, 0, rd);
2059 (term, false, 0)
2060 }
2061 0b001010 if rs2_field == 0b00111 => {
2062 self.rv_unary(rd, rs1, UnaryOp::OrcB, pc);
2063 let term = self.feed_gas_rv(RV_KIND_ZBB_U1, rs1, 0, rd);
2064 (term, false, 0)
2065 }
2066 0b011010 if rs2_field == 0b11000 => {
2067 self.rv_unary(rd, rs1, UnaryOp::Rev8, pc);
2068 let term = self.feed_gas_rv(RV_KIND_ZBB_U1, rs1, 0, rd);
2069 (term, false, 0)
2070 }
2071 _ => {
2072 self.rv_emit_panic_at(pc);
2073 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2074 (true, false, 0)
2075 }
2076 }
2077 }
2078 _ => {
2079 self.rv_emit_panic_at(pc);
2080 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2081 (true, false, 0)
2082 }
2083 }
2084 }
2085
2086 fn compile_op_imm_32(
2087 &mut self,
2088 rd: u8,
2089 rs1: u8,
2090 f3: u8,
2091 w: u32,
2092 pc: u32,
2093 ) -> (bool, bool, usize) {
2094 use javm_exec::gas_cost::*;
2095 match f3 {
2096 0b000 => {
2097 let imm = imm_i(w);
2098 self.rv_alu_imm(rd, rs1, imm, AluImmOp::Addw, pc);
2099 let term = self.feed_gas_rv(RV_KIND_ADDIW, rs1, 0, rd);
2100 (term, false, 0)
2101 }
2102 0b001 => {
2103 let f7 = (w >> 25) & 0x7F;
2104 let shamt5 = ((w >> 20) & 0x1F) as u8;
2105 match f7 {
2106 0b0000000 => {
2107 self.rv_shift_imm(rd, rs1, shamt5, ShiftOp::Shl32, pc);
2108 let term = self.feed_gas_rv(RV_KIND_ADDIW, rs1, 0, rd);
2109 (term, false, 0)
2110 }
2111 0b0000100 => {
2112 let shamt6 = ((w >> 20) & 0x3F) as u8;
2114 self.rv_slliuw(rd, rs1, shamt6, pc);
2115 let term = self.feed_gas_rv(RV_KIND_ZBA_IMM, rs1, 0, rd);
2116 (term, false, 0)
2117 }
2118 0b0110000 => {
2119 let rs2_field = (w >> 20) & 0x1F;
2120 let op = match rs2_field {
2121 0b00000 => UnaryOp::Clz32,
2122 0b00001 => UnaryOp::Ctz32,
2123 0b00010 => UnaryOp::Popcnt32,
2124 _ => {
2125 self.rv_emit_panic_at(pc);
2126 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2127 return (true, false, 0);
2128 }
2129 };
2130 let kind = if matches!(op, UnaryOp::Ctz32) {
2131 RV_KIND_ZBB_CTZ
2132 } else {
2133 RV_KIND_ZBB_U1
2134 };
2135 self.rv_unary(rd, rs1, op, pc);
2136 let term = self.feed_gas_rv(kind, rs1, 0, rd);
2137 (term, false, 0)
2138 }
2139 _ => {
2140 self.rv_emit_panic_at(pc);
2141 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2142 (true, false, 0)
2143 }
2144 }
2145 }
2146 0b101 => {
2147 let f7 = (w >> 25) & 0x7F;
2148 let shamt5 = ((w >> 20) & 0x1F) as u8;
2149 match f7 {
2150 0b0000000 => {
2151 self.rv_shift_imm(rd, rs1, shamt5, ShiftOp::Shr32, pc);
2152 let term = self.feed_gas_rv(RV_KIND_ADDIW, rs1, 0, rd);
2153 (term, false, 0)
2154 }
2155 0b0100000 => {
2156 self.rv_shift_imm(rd, rs1, shamt5, ShiftOp::Sar32, pc);
2157 let term = self.feed_gas_rv(RV_KIND_ADDIW, rs1, 0, rd);
2158 (term, false, 0)
2159 }
2160 0b0110000 => {
2161 self.rv_shift_imm(rd, rs1, shamt5, ShiftOp::Ror32, pc);
2162 let term = self.feed_gas_rv(RV_KIND_ZBB_RORIW, rs1, 0, rd);
2163 (term, false, 0)
2164 }
2165 _ => {
2166 self.rv_emit_panic_at(pc);
2167 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2168 (true, false, 0)
2169 }
2170 }
2171 }
2172 _ => {
2173 self.rv_emit_panic_at(pc);
2174 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2175 (true, false, 0)
2176 }
2177 }
2178 }
2179
2180 #[allow(clippy::too_many_arguments)]
2181 fn compile_op(
2182 &mut self,
2183 rd: u8,
2184 rs1: u8,
2185 rs2: u8,
2186 f3: u8,
2187 f7: u8,
2188 w: u32,
2189 pc: u32,
2190 rest: &[u8],
2191 ) -> (bool, bool, usize) {
2192 use javm_exec::gas_cost::*;
2193 if f7 == 0b0000001
2198 && f3 == 0b000
2199 && let Some(extra) = self.try_fuse_mul_pair_bytes(rd, rs1, rs2, rest, pc)
2200 {
2201 return (false, false, extra);
2202 }
2203 match (f7, f3) {
2204 (0b0000000, 0b000) => {
2205 self.rv_alu_rr(rd, rs1, rs2, AluOp::Add, pc);
2206 if rd != rs1 && rd != rs2 {
2207 self.track_add_scaledadd(rd, rs1, rs2);
2208 }
2209 let term = self.feed_gas_rv(RV_KIND_ADD, rs1, rs2, rd);
2210 (term, true, 0)
2211 }
2212 (0b0100000, 0b000) => {
2213 self.rv_alu_rr(rd, rs1, rs2, AluOp::Sub, pc);
2214 let term = self.feed_gas_rv(RV_KIND_ADD, rs1, rs2, rd);
2215 (term, false, 0)
2216 }
2217 (0b0000000, 0b001) => {
2218 self.rv_shift_rr(rd, rs1, rs2, ShiftOp::Shl64, pc);
2219 let term = self.feed_gas_rv(RV_KIND_SLL, rs1, rs2, rd);
2220 (term, false, 0)
2221 }
2222 (0b0000000, 0b010) => {
2223 self.rv_slt_rr(rd, rs1, rs2, true, pc);
2224 let term = self.feed_gas_rv(RV_KIND_SLT, rs1, rs2, rd);
2225 (term, false, 0)
2226 }
2227 (0b0000000, 0b011) => {
2228 self.rv_slt_rr(rd, rs1, rs2, false, pc);
2235 let term = self.feed_gas_rv(RV_KIND_SLT, rs1, rs2, rd);
2236 (term, true, 0)
2237 }
2238 (0b0000000, 0b100) => {
2239 self.rv_alu_rr(rd, rs1, rs2, AluOp::Xor, pc);
2240 let term = self.feed_gas_rv(RV_KIND_ADD, rs1, rs2, rd);
2241 (term, false, 0)
2242 }
2243 (0b0000000, 0b101) => {
2244 self.rv_shift_rr(rd, rs1, rs2, ShiftOp::Shr64, pc);
2245 let term = self.feed_gas_rv(RV_KIND_SLL, rs1, rs2, rd);
2246 (term, false, 0)
2247 }
2248 (0b0100000, 0b101) => {
2249 self.rv_shift_rr(rd, rs1, rs2, ShiftOp::Sar64, pc);
2250 let term = self.feed_gas_rv(RV_KIND_SLL, rs1, rs2, rd);
2251 (term, false, 0)
2252 }
2253 (0b0000000, 0b110) => {
2254 self.rv_alu_rr(rd, rs1, rs2, AluOp::Or, pc);
2255 let term = self.feed_gas_rv(RV_KIND_ADD, rs1, rs2, rd);
2256 (term, false, 0)
2257 }
2258 (0b0000000, 0b111) => {
2259 self.rv_alu_rr(rd, rs1, rs2, AluOp::And, pc);
2260 let term = self.feed_gas_rv(RV_KIND_ADD, rs1, rs2, rd);
2261 (term, false, 0)
2262 }
2263 (0b0000001, 0b000) => {
2265 self.rv_alu_rr(rd, rs1, rs2, AluOp::Mul, pc);
2266 let term = self.feed_gas_rv(RV_KIND_MUL, rs1, rs2, rd);
2267 (term, false, 0)
2268 }
2269 (0b0000001, 0b001) => {
2270 self.rv_mulh(rd, rs1, rs2, true, true, pc);
2271 let term = self.feed_gas_rv(RV_KIND_MULH, rs1, rs2, rd);
2272 (term, false, 0)
2273 }
2274 (0b0000001, 0b010) => {
2275 self.rv_mulh(rd, rs1, rs2, true, false, pc);
2276 let term = self.feed_gas_rv(RV_KIND_MULHSU, rs1, rs2, rd);
2277 (term, false, 0)
2278 }
2279 (0b0000001, 0b011) => {
2280 self.rv_mulh(rd, rs1, rs2, false, false, pc);
2281 let term = self.feed_gas_rv(RV_KIND_MULH, rs1, rs2, rd);
2282 (term, false, 0)
2283 }
2284 (0b0000001, 0b100) => {
2285 self.rv_div_rem(rd, rs1, rs2, true, false, false, pc);
2286 let term = self.feed_gas_rv(RV_KIND_DIV, rs1, rs2, rd);
2287 (term, false, 0)
2288 }
2289 (0b0000001, 0b101) => {
2290 self.rv_div_rem(rd, rs1, rs2, false, false, false, pc);
2291 let term = self.feed_gas_rv(RV_KIND_DIV, rs1, rs2, rd);
2292 (term, false, 0)
2293 }
2294 (0b0000001, 0b110) => {
2295 self.rv_div_rem(rd, rs1, rs2, true, true, false, pc);
2296 let term = self.feed_gas_rv(RV_KIND_DIV, rs1, rs2, rd);
2297 (term, false, 0)
2298 }
2299 (0b0000001, 0b111) => {
2300 self.rv_div_rem(rd, rs1, rs2, false, true, false, pc);
2301 let term = self.feed_gas_rv(RV_KIND_DIV, rs1, rs2, rd);
2302 (term, false, 0)
2303 }
2304 (0b0100000, 0b111) => {
2306 self.rv_alu_rr(rd, rs1, rs2, AluOp::Andn, pc);
2307 let term = self.feed_gas_rv(RV_KIND_ZBB_INV, rs1, rs2, rd);
2308 (term, false, 0)
2309 }
2310 (0b0100000, 0b110) => {
2311 self.rv_alu_rr(rd, rs1, rs2, AluOp::Orn, pc);
2312 let term = self.feed_gas_rv(RV_KIND_ZBB_INV, rs1, rs2, rd);
2313 (term, false, 0)
2314 }
2315 (0b0100000, 0b100) => {
2316 self.rv_alu_rr(rd, rs1, rs2, AluOp::Xnor, pc);
2317 let term = self.feed_gas_rv(RV_KIND_ZBB_XNOR, rs1, rs2, rd);
2318 (term, false, 0)
2319 }
2320 (0b0000101, 0b100) => {
2321 self.rv_alu_rr(rd, rs1, rs2, AluOp::Min, pc);
2322 let term = self.feed_gas_rv(RV_KIND_ZBB_MINMAX, rs1, rs2, rd);
2323 (term, false, 0)
2324 }
2325 (0b0000101, 0b101) => {
2326 self.rv_alu_rr(rd, rs1, rs2, AluOp::Minu, pc);
2327 let term = self.feed_gas_rv(RV_KIND_ZBB_MINMAX, rs1, rs2, rd);
2328 (term, false, 0)
2329 }
2330 (0b0000101, 0b110) => {
2331 self.rv_alu_rr(rd, rs1, rs2, AluOp::Max, pc);
2332 let term = self.feed_gas_rv(RV_KIND_ZBB_MINMAX, rs1, rs2, rd);
2333 (term, false, 0)
2334 }
2335 (0b0000101, 0b111) => {
2336 self.rv_alu_rr(rd, rs1, rs2, AluOp::Maxu, pc);
2337 let term = self.feed_gas_rv(RV_KIND_ZBB_MINMAX, rs1, rs2, rd);
2338 (term, false, 0)
2339 }
2340 (0b0110000, 0b001) => {
2341 self.rv_shift_rr(rd, rs1, rs2, ShiftOp::Rol64, pc);
2342 let term = self.feed_gas_rv(RV_KIND_ZBB_ROT, rs1, rs2, rd);
2343 (term, false, 0)
2344 }
2345 (0b0110000, 0b101) => {
2346 self.rv_shift_rr(rd, rs1, rs2, ShiftOp::Ror64, pc);
2347 let term = self.feed_gas_rv(RV_KIND_ZBB_ROT, rs1, rs2, rd);
2348 (term, false, 0)
2349 }
2350 (0b0010000, 0b010) => {
2352 self.rv_shadd(rd, rs1, rs2, 1, false, pc);
2353 self.record_scaledadd(rd, rs1, rs2, 1);
2354 let term = self.feed_gas_rv(RV_KIND_ZBA, rs1, rs2, rd);
2355 (term, false, 0)
2356 }
2357 (0b0010000, 0b100) => {
2358 self.rv_shadd(rd, rs1, rs2, 2, false, pc);
2359 self.record_scaledadd(rd, rs1, rs2, 2);
2360 let term = self.feed_gas_rv(RV_KIND_ZBA, rs1, rs2, rd);
2361 (term, false, 0)
2362 }
2363 (0b0010000, 0b110) => {
2364 self.rv_shadd(rd, rs1, rs2, 3, false, pc);
2365 self.record_scaledadd(rd, rs1, rs2, 3);
2366 let term = self.feed_gas_rv(RV_KIND_ZBA, rs1, rs2, rd);
2367 (term, false, 0)
2368 }
2369 (0b0100100, 0b001) => {
2371 self.rv_bit_rr(rd, rs1, rs2, BitOp::Clear, pc);
2372 let term = self.feed_gas_rv(RV_KIND_ZBS, rs1, rs2, rd);
2373 (term, false, 0)
2374 }
2375 (0b0010100, 0b001) => {
2376 self.rv_bit_rr(rd, rs1, rs2, BitOp::Set, pc);
2377 let term = self.feed_gas_rv(RV_KIND_ZBS, rs1, rs2, rd);
2378 (term, false, 0)
2379 }
2380 (0b0110100, 0b001) => {
2381 self.rv_bit_rr(rd, rs1, rs2, BitOp::Invert, pc);
2382 let term = self.feed_gas_rv(RV_KIND_ZBS, rs1, rs2, rd);
2383 (term, false, 0)
2384 }
2385 (0b0100100, 0b101) => {
2386 self.rv_bit_rr(rd, rs1, rs2, BitOp::Extract, pc);
2387 let term = self.feed_gas_rv(RV_KIND_ZBS, rs1, rs2, rd);
2388 (term, false, 0)
2389 }
2390 (0b0000111, 0b101) => {
2392 self.rv_czero(rd, rs1, rs2, Cc::E, pc);
2393 let term = self.feed_gas_rv(RV_KIND_ZICOND, rs1, rs2, rd);
2394 (term, false, 0)
2395 }
2396 (0b0000111, 0b111) => {
2397 self.rv_czero(rd, rs1, rs2, Cc::NE, pc);
2398 let term = self.feed_gas_rv(RV_KIND_ZICOND, rs1, rs2, rd);
2399 (term, false, 0)
2400 }
2401 (0b0000100, 0b100) if rs2 == 0 => {
2403 self.rv_unary(rd, rs1, UnaryOp::ZextH, pc);
2404 let term = self.feed_gas_rv(RV_KIND_ZBB_U1, rs1, 0, rd);
2405 (term, false, 0)
2406 }
2407 _ => {
2408 let _ = w;
2409 self.rv_emit_panic_at(pc);
2410 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2411 (true, false, 0)
2412 }
2413 }
2414 }
2415
2416 #[allow(clippy::too_many_arguments)]
2417 fn compile_op_32(
2418 &mut self,
2419 rd: u8,
2420 rs1: u8,
2421 rs2: u8,
2422 f3: u8,
2423 f7: u8,
2424 w: u32,
2425 pc: u32,
2426 ) -> (bool, bool, usize) {
2427 use javm_exec::gas_cost::*;
2428 match (f7, f3) {
2429 (0b0000000, 0b000) => {
2430 self.rv_alu_rr(rd, rs1, rs2, AluOp::Addw, pc);
2431 let term = self.feed_gas_rv(RV_KIND_ADDW, rs1, rs2, rd);
2432 (term, false, 0)
2433 }
2434 (0b0100000, 0b000) => {
2435 self.rv_alu_rr(rd, rs1, rs2, AluOp::Subw, pc);
2436 let term = self.feed_gas_rv(RV_KIND_ADDW, rs1, rs2, rd);
2437 (term, false, 0)
2438 }
2439 (0b0000000, 0b001) => {
2440 self.rv_shift_rr(rd, rs1, rs2, ShiftOp::Shl32, pc);
2441 let term = self.feed_gas_rv(RV_KIND_SLLW, rs1, rs2, rd);
2442 (term, false, 0)
2443 }
2444 (0b0000000, 0b101) => {
2445 self.rv_shift_rr(rd, rs1, rs2, ShiftOp::Shr32, pc);
2446 let term = self.feed_gas_rv(RV_KIND_SLLW, rs1, rs2, rd);
2447 (term, false, 0)
2448 }
2449 (0b0100000, 0b101) => {
2450 self.rv_shift_rr(rd, rs1, rs2, ShiftOp::Sar32, pc);
2451 let term = self.feed_gas_rv(RV_KIND_SLLW, rs1, rs2, rd);
2452 (term, false, 0)
2453 }
2454 (0b0000001, 0b000) => {
2455 self.rv_alu_rr(rd, rs1, rs2, AluOp::Mulw, pc);
2456 let term = self.feed_gas_rv(RV_KIND_MULW, rs1, rs2, rd);
2457 (term, false, 0)
2458 }
2459 (0b0000001, 0b100) => {
2460 self.rv_div_rem(rd, rs1, rs2, true, false, true, pc);
2461 let term = self.feed_gas_rv(RV_KIND_DIV, rs1, rs2, rd);
2462 (term, false, 0)
2463 }
2464 (0b0000001, 0b101) => {
2465 self.rv_div_rem(rd, rs1, rs2, false, false, true, pc);
2466 let term = self.feed_gas_rv(RV_KIND_DIV, rs1, rs2, rd);
2467 (term, false, 0)
2468 }
2469 (0b0000001, 0b110) => {
2470 self.rv_div_rem(rd, rs1, rs2, true, true, true, pc);
2471 let term = self.feed_gas_rv(RV_KIND_DIV, rs1, rs2, rd);
2472 (term, false, 0)
2473 }
2474 (0b0000001, 0b111) => {
2475 self.rv_div_rem(rd, rs1, rs2, false, true, true, pc);
2476 let term = self.feed_gas_rv(RV_KIND_DIV, rs1, rs2, rd);
2477 (term, false, 0)
2478 }
2479 (0b0110000, 0b001) => {
2480 self.rv_shift_rr(rd, rs1, rs2, ShiftOp::Rol32, pc);
2481 let term = self.feed_gas_rv(RV_KIND_ZBB_ROTW, rs1, rs2, rd);
2482 (term, false, 0)
2483 }
2484 (0b0110000, 0b101) => {
2485 self.rv_shift_rr(rd, rs1, rs2, ShiftOp::Ror32, pc);
2486 let term = self.feed_gas_rv(RV_KIND_ZBB_ROTW, rs1, rs2, rd);
2487 (term, false, 0)
2488 }
2489 (0b0000100, 0b000) => {
2490 self.rv_adduw(rd, rs1, rs2, pc);
2491 let term = self.feed_gas_rv(RV_KIND_ZBA, rs1, rs2, rd);
2492 (term, false, 0)
2493 }
2494 (0b0010000, 0b010) => {
2495 self.rv_shadd(rd, rs1, rs2, 1, true, pc);
2496 let term = self.feed_gas_rv(RV_KIND_ZBA, rs1, rs2, rd);
2497 (term, false, 0)
2498 }
2499 (0b0010000, 0b100) => {
2500 self.rv_shadd(rd, rs1, rs2, 2, true, pc);
2501 let term = self.feed_gas_rv(RV_KIND_ZBA, rs1, rs2, rd);
2502 (term, false, 0)
2503 }
2504 (0b0010000, 0b110) => {
2505 self.rv_shadd(rd, rs1, rs2, 3, true, pc);
2506 let term = self.feed_gas_rv(RV_KIND_ZBA, rs1, rs2, rd);
2507 (term, false, 0)
2508 }
2509 _ => {
2510 let _ = w;
2511 self.rv_emit_panic_at(pc);
2512 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2513 (true, false, 0)
2514 }
2515 }
2516 }
2517
2518 fn compile_lui(&mut self, rd: u8, w: u32, pc: u32, rest: &[u8]) -> (bool, bool, usize) {
2519 use javm_exec::gas_cost::*;
2520 let imm = imm_u(w);
2521
2522 if rd != 0
2528 && !rv_is_reserved(rd)
2529 && let Some((op, a_rd, a_rs1, a_rs2, consumed)) = peek_alu_rr_trailer(rest)
2530 && matches!(op, AluOp::Add)
2531 && a_rd == rd
2532 {
2533 let other = if a_rs1 == rd {
2534 Some(a_rs2)
2535 } else if a_rs2 == rd {
2536 Some(a_rs1)
2537 } else {
2538 None
2539 };
2540 if let Some(other) = other
2541 && (other == 0 || !rv_is_reserved(other))
2542 {
2543 if let Some(d) = self.rv_dst(a_rd, pc) {
2544 if other == 0 {
2545 self.asm.mov_ri64(d, imm as i64 as u64);
2549 self.track_const(a_rd, imm);
2550 } else {
2551 let base = REG_MAP[rv_slot(other).unwrap()];
2552 self.asm.lea(d, base, imm);
2553 self.invalidate_reg(rv_slot(a_rd).unwrap());
2554 }
2555 }
2556 self.feed_gas_rv(RV_KIND_LUI, 0, 0, rd);
2557 self.feed_gas_rv(RV_KIND_ADD, a_rs1, a_rs2, a_rd);
2558 return (false, false, consumed);
2561 }
2562 }
2563
2564 self.rv_lui(rd, imm, pc);
2565 self.track_const(rd, imm);
2566 let term = self.feed_gas_rv(RV_KIND_LUI, 0, 0, rd);
2567 (term, false, 0)
2568 }
2569
2570 fn compile_jal(&mut self, rd: u8, w: u32, pc: u32, inst_len: u32) -> (bool, bool, usize) {
2571 use javm_exec::gas_cost::*;
2572 let imm = imm_j(w);
2573 let next_pc = pc + inst_len;
2574 self.rv_jal(rd, imm, pc, next_pc);
2575 let term = self.feed_gas_rv(RV_KIND_JAL, 0, 0, rd);
2576 (term, false, 0)
2577 }
2578
2579 fn compile_auipc(&mut self, rd: u8, w: u32, pc: u32) -> (bool, bool, usize) {
2580 use javm_exec::gas_cost::*;
2581 let imm = imm_u(w);
2583 self.rv_auipc(rd, imm, pc);
2584 let term = self.feed_gas_rv(RV_KIND_LUI, 0, 0, rd);
2586 (term, false, 0)
2587 }
2588
2589 fn compile_jalr(
2590 &mut self,
2591 rd: u8,
2592 rs1: u8,
2593 w: u32,
2594 pc: u32,
2595 inst_len: u32,
2596 ) -> (bool, bool, usize) {
2597 use javm_exec::gas_cost::*;
2598 let imm = imm_i(w);
2599 let next_pc = pc + inst_len;
2600 self.rv_jalr(rd, rs1, imm, pc, next_pc);
2601 let term = self.feed_gas_rv(RV_KIND_JALR, rs1, 0, 0);
2603 (term, false, 0)
2604 }
2605
2606 fn compile_branch(&mut self, rs1: u8, rs2: u8, f3: u8, w: u32, pc: u32) -> (bool, bool, usize) {
2607 use javm_exec::gas_cost::*;
2608 let imm = imm_b(w);
2609 let next_pc = pc + 4;
2610 let cc = match f3 {
2611 0b000 => Cc::E,
2612 0b001 => Cc::NE,
2613 0b100 => Cc::L,
2614 0b101 => Cc::GE,
2615 0b110 => Cc::B,
2616 0b111 => Cc::AE,
2617 _ => {
2618 self.rv_emit_panic_at(pc);
2619 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2620 return (true, false, 0);
2621 }
2622 };
2623 self.rv_branch(rs1, rs2, imm, cc, pc, next_pc);
2624 let term = self.feed_gas_rv(RV_KIND_BRANCH, rs1, rs2, 0);
2625 (term, false, 0)
2626 }
2627
2628 fn compile_custom_0(
2629 &mut self,
2630 _rd: u8,
2631 _rs1: u8,
2632 f3: u8,
2633 w: u32,
2634 pc: u32,
2635 ) -> (bool, bool, usize) {
2636 use javm_exec::gas_cost::*;
2637 let next_pc = pc + 4;
2644 match f3 {
2645 0b000 => {
2646 self.rv_trap(pc);
2647 let term = self.feed_gas_rv(RV_KIND_TRAP, 0, 0, 0);
2648 (term, false, 0)
2649 }
2650 0b001 => {
2651 self.rv_ecall_jar(next_pc);
2652 let term = self.feed_gas_rv(RV_KIND_ECALL_JAR, 0, 0, 0);
2653 (term, false, 0)
2654 }
2655 0b010 => {
2656 let imm = imm_i(w);
2657 self.rv_ecalli(imm, next_pc);
2658 let term = self.feed_gas_rv(RV_KIND_ECALLI, 0, 0, 0);
2659 (term, false, 0)
2660 }
2661 0b100 => {
2662 let term = self.feed_gas_rv(RV_KIND_FALLTHROUGH, 0, 0, 0);
2663 (term, false, 0)
2664 }
2665 _ => {
2666 self.rv_emit_panic_at(pc);
2667 self.feed_gas_rv(RV_KIND_RESERVED, 0, 0, 0);
2668 (true, false, 0)
2669 }
2670 }
2671 }
2672
2673 fn try_fuse_mul_pair_bytes(
2678 &mut self,
2679 m_rd: u8,
2680 m_rs1: u8,
2681 m_rs2: u8,
2682 rest: &[u8],
2683 _pc: u32,
2684 ) -> Option<usize> {
2685 use javm_exec::gas_cost::*;
2686 if rest.len() < 4 {
2687 return None;
2688 }
2689 let w2 = u32::from_le_bytes([rest[0], rest[1], rest[2], rest[3]]);
2690 let signed = match w2 & 0xFE00_707F {
2693 0x0200_1033 => true, 0x0200_3033 => false, _ => return None,
2696 };
2697 let u_rd = ((w2 >> 7) & 0x1F) as u8;
2698 let u_rs1 = ((w2 >> 15) & 0x1F) as u8;
2699 let u_rs2 = ((w2 >> 20) & 0x1F) as u8;
2700 if u_rs1 != m_rs1 || u_rs2 != m_rs2 || u_rd == m_rd {
2701 return None;
2702 }
2703 if rv_is_reserved(m_rd) || rv_is_reserved(u_rd) {
2704 return None;
2705 }
2706 if rv_is_reserved(m_rs1) || rv_is_reserved(m_rs2) {
2707 return None;
2708 }
2709 let (rs1_slot, rs2_slot) = (rv_slot(m_rs1)?, rv_slot(m_rs2)?);
2710 let (lo_slot, hi_slot) = (rv_slot(m_rd)?, rv_slot(u_rd)?);
2711
2712 let a = REG_MAP[rs1_slot];
2713 let b = REG_MAP[rs2_slot];
2714 let rd_lo = REG_MAP[lo_slot];
2715 let rd_hi = REG_MAP[hi_slot];
2716 let phi11 = REG_MAP[11];
2717
2718 let need_save_phi11 = rd_lo != phi11 && rd_hi != phi11;
2719 if need_save_phi11 {
2720 self.asm.push(phi11);
2721 }
2722 let mul_src = if b == phi11 {
2723 if need_save_phi11 {
2724 self.asm.mov_load64(SCRATCH, Reg::RSP, 0);
2725 } else {
2726 self.asm.mov_rr(SCRATCH, b);
2727 }
2728 SCRATCH
2729 } else {
2730 b
2731 };
2732 if a != phi11 {
2733 self.asm.mov_rr(phi11, a);
2734 }
2735 if signed {
2736 self.asm.imul_rdx_rax(mul_src);
2737 } else {
2738 self.asm.mul_rdx_rax(mul_src);
2739 }
2740 if rd_lo != phi11 {
2741 self.asm.mov_rr(rd_lo, phi11);
2742 }
2743 if rd_hi != Reg::RDX {
2744 self.asm.mov_rr(rd_hi, Reg::RDX);
2745 }
2746 if need_save_phi11 {
2747 self.asm.pop(phi11);
2748 }
2749
2750 self.invalidate_reg(lo_slot);
2751 self.invalidate_reg(hi_slot);
2752 self.last_add_cf = None;
2753
2754 let _ = signed;
2757 self.feed_gas_rv(RV_KIND_MUL, m_rs1, m_rs2, m_rd);
2758 self.feed_gas_rv(RV_KIND_MULH, u_rs1, u_rs2, u_rd);
2759
2760 Some(4)
2761 }
2762
2763 fn rv_read(&mut self, rs: u8, dst_reg: Reg, pc: u32) {
2770 if rs == 0 {
2771 self.asm.mov_ri64(dst_reg, 0);
2772 } else if reg_is_spilled(rs) {
2773 self.asm.mov_load64_rip_rel(dst_reg, spill_va(rs));
2774 } else if rv_is_reserved(rs) {
2775 self.rv_emit_panic_at(pc);
2776 } else {
2777 self.asm.mov_rr(dst_reg, REG_MAP[rv_slot(rs).unwrap()]);
2778 }
2779 }
2780
2781 fn rv_read_into(&mut self, rs: u8, scratch: Reg, pc: u32) -> Reg {
2785 if rs == 0 {
2786 self.asm.mov_ri64(scratch, 0);
2787 scratch
2788 } else if reg_is_spilled(rs) {
2789 self.asm.mov_load64_rip_rel(scratch, spill_va(rs));
2790 scratch
2791 } else if rv_is_reserved(rs) {
2792 self.rv_emit_panic_at(pc);
2793 scratch
2794 } else {
2795 REG_MAP[rv_slot(rs).unwrap()]
2796 }
2797 }
2798
2799 fn rv_dst(&mut self, rd: u8, pc: u32) -> Option<Reg> {
2802 if rd == 0 {
2803 None
2804 } else if rv_is_reserved(rd) {
2805 self.rv_emit_panic_at(pc);
2806 None
2807 } else {
2808 Some(REG_MAP[rv_slot(rd).unwrap()])
2809 }
2810 }
2811
2812 fn rv_lui(&mut self, rd: u8, imm: i32, pc: u32) {
2815 if let Some(d) = self.rv_dst(rd, pc) {
2816 self.asm.mov_ri64(d, imm as i64 as u64);
2818 self.invalidate_reg(rv_slot(rd).unwrap());
2819 }
2820 }
2821
2822 fn rv_auipc(&mut self, rd: u8, imm: i32, pc: u32) {
2827 if let Some(d) = self.rv_dst(rd, pc) {
2828 let va = self.code_base.wrapping_add(pc).wrapping_add(imm as u32);
2829 self.asm.mov_ri64(d, va as i32 as i64 as u64);
2830 self.invalidate_reg(rv_slot(rd).unwrap());
2831 }
2832 }
2833
2834 fn rv_load(&mut self, rd: u8, rs1: u8, imm: i32, width: u32, signed: bool, pc: u32) {
2837 if rv_is_reserved(rd) || rv_is_reserved(rs1) {
2838 self.rv_emit_panic_at(pc);
2839 return;
2840 }
2841 self.rv_addr_to_scratch(rs1, imm, pc);
2842 let fn_addr = match width {
2843 1 => self.helpers.mem_read_u8,
2844 2 => self.helpers.mem_read_u16,
2845 4 => self.helpers.mem_read_u32,
2846 _ => self.helpers.mem_read_u64,
2847 };
2848 let dst = match self.rv_dst(rd, pc) {
2849 Some(r) => r,
2850 None => SCRATCH, };
2852 self.emit_mem_read_sized(dst, fn_addr, width, pc);
2853 if signed && width < 8 && rd != 0 {
2854 match width {
2855 1 => self.asm.movsx_8_64(dst, dst),
2856 2 => self.asm.movsx_16_64(dst, dst),
2857 4 => self.asm.movsxd(dst, dst),
2858 _ => {}
2859 }
2860 }
2861 if rd != 0 {
2862 self.invalidate_reg(rv_slot(rd).unwrap());
2863 }
2864 }
2865
2866 fn rv_store(&mut self, rs1: u8, rs2: u8, imm: i32, width: u32, pc: u32) {
2867 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
2868 self.rv_emit_panic_at(pc);
2869 return;
2870 }
2871 let fn_addr = match width {
2872 1 => self.helpers.mem_write_u8,
2873 2 => self.helpers.mem_write_u16,
2874 4 => self.helpers.mem_write_u32,
2875 _ => self.helpers.mem_write_u64,
2876 };
2877 if rs2 == 0 {
2878 self.rv_addr_to_scratch(rs1, imm, pc);
2883 self.asm.push(Reg::RAX);
2884 self.asm.mov_ri64(Reg::RAX, 0);
2885 self.emit_mem_write(true, Reg::RAX, fn_addr, pc);
2886 self.asm.pop(Reg::RAX);
2887 } else {
2888 let val = REG_MAP[rv_slot(rs2).unwrap()];
2889 self.rv_addr_to_scratch(rs1, imm, pc);
2890 self.emit_mem_write(true, val, fn_addr, pc);
2891 }
2892 }
2893
2894 fn rv_addr_to_scratch(&mut self, rs1: u8, imm: i32, pc: u32) {
2896 use super::codegen::RegDef;
2897 if rs1 == 0 {
2898 self.asm.mov_ri32(SCRATCH, imm as u32);
2899 return;
2900 }
2901 if rv_is_reserved(rs1) {
2902 self.rv_emit_panic_at(pc);
2903 return;
2904 }
2905 let slot = rv_slot(rs1).unwrap();
2909 if let RegDef::Const(addr) = self.reg_defs[slot] {
2910 let effective = addr.wrapping_add(imm as u32);
2911 self.asm.mov_ri32(SCRATCH, effective);
2912 return;
2913 }
2914 if imm == 0
2920 && let RegDef::ScaledAdd { base, idx, shift } = self.reg_defs[slot]
2921 {
2922 self.asm
2923 .lea_sib_scaled_32(SCRATCH, REG_MAP[base], REG_MAP[idx], shift);
2924 return;
2925 }
2926 let base = REG_MAP[slot];
2927 if imm != 0 {
2928 self.asm.lea_32(SCRATCH, base, imm);
2929 } else {
2930 self.asm.movzx_32_64(SCRATCH, base);
2931 }
2932 }
2933
2934 fn rv_alu_imm(&mut self, rd: u8, rs1: u8, imm: i32, op: AluImmOp, pc: u32) {
2937 let Some(d) = self.rv_dst(rd, pc) else { return };
2938 if rs1 == 0 && matches!(op, AluImmOp::Add) {
2942 self.asm.mov_ri64(d, imm as i64 as u64);
2943 self.invalidate_reg(rv_slot(rd).unwrap());
2944 return;
2945 }
2946 self.rv_read(rs1, d, pc);
2947 match op {
2948 AluImmOp::Add => self.asm.add_ri(d, imm),
2949 AluImmOp::And => self.asm.and_ri(d, imm),
2950 AluImmOp::Or => self.asm.or_ri(d, imm),
2951 AluImmOp::Xor => self.asm.xor_ri(d, imm),
2952 AluImmOp::Addw => {
2953 self.asm.add_ri32(d, imm);
2954 self.asm.movsxd(d, d);
2955 }
2956 }
2957 if rd != 0 {
2958 self.invalidate_reg(rv_slot(rd).unwrap());
2959 }
2960 }
2961
2962 fn rv_alu_rr(&mut self, rd: u8, rs1: u8, rs2: u8, op: AluOp, pc: u32) {
2963 let Some(d) = self.rv_dst(rd, pc) else { return };
2964 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
2965 self.rv_emit_panic_at(pc);
2966 return;
2967 }
2968 if matches!(op, AluOp::Add) && (rs1 == 0 || rs2 == 0) {
2978 let src = if rs1 == 0 { rs2 } else { rs1 };
2979 self.rv_read(src, d, pc);
2980 self.invalidate_reg(rv_slot(rd).unwrap());
2981 self.last_add_cf = None;
2982 return;
2983 }
2984 if matches!(op, AluOp::Sub) && rs1 != 0 && rs2 != 0 && rs1 != rs2 {
2990 let r1_x86 = REG_MAP[rv_slot(rs1).unwrap()];
2991 let r2_x86 = REG_MAP[rv_slot(rs2).unwrap()];
2992 if d == r2_x86 {
2993 self.asm.neg64(d);
2994 self.asm.add_rr(d, r1_x86);
2995 self.invalidate_reg(rv_slot(rd).unwrap());
2996 self.last_add_cf = None;
2997 return;
2998 }
2999 }
3000 let r1_is_x0 = rs1 == 0;
3005 let r2_is_x0 = rs2 == 0;
3006 let r1 = if r1_is_x0 {
3007 None
3008 } else {
3009 Some(REG_MAP[rv_slot(rs1).unwrap()])
3010 };
3011 let r2 = if r2_is_x0 {
3012 None
3013 } else {
3014 Some(REG_MAP[rv_slot(rs2).unwrap()])
3015 };
3016
3017 let b_reg = if r2_is_x0 {
3018 self.asm.mov_ri64(SCRATCH, 0);
3021 SCRATCH
3022 } else if Some(d) == r2 && r1 != r2 {
3023 self.asm.mov_rr(SCRATCH, r2.unwrap());
3026 SCRATCH
3027 } else {
3028 r2.unwrap()
3029 };
3030 self.rv_read(rs1, d, pc);
3032 self.apply_alu_op(op, d, b_reg);
3033 if rd != 0 {
3034 self.invalidate_reg(rv_slot(rd).unwrap());
3035 }
3036 if matches!(op, AluOp::Add)
3043 && rd != 0
3044 && rs1 != 0
3045 && rs2 != 0
3046 && let (Some(d_s), Some(a_s), Some(b_s)) = (rv_slot(rd), rv_slot(rs1), rv_slot(rs2))
3047 {
3048 self.last_add_cf = Some((d_s, a_s, b_s));
3049 }
3050 }
3051
3052 fn apply_alu_op(&mut self, op: AluOp, d: Reg, s: Reg) {
3053 match op {
3054 AluOp::Add => self.asm.add_rr(d, s),
3055 AluOp::Sub => self.asm.sub_rr(d, s),
3056 AluOp::And => self.asm.and_rr(d, s),
3057 AluOp::Or => self.asm.or_rr(d, s),
3058 AluOp::Xor => self.asm.xor_rr(d, s),
3059 AluOp::Mul => self.asm.imul_rr(d, s),
3060 AluOp::Addw => {
3061 self.asm.add_rr32(d, s);
3062 self.asm.movsxd(d, d);
3063 }
3064 AluOp::Subw => {
3065 self.asm.sub_rr32(d, s);
3066 self.asm.movsxd(d, d);
3067 }
3068 AluOp::Mulw => {
3069 self.asm.imul_rr32(d, s);
3070 self.asm.movsxd(d, d);
3071 }
3072 AluOp::Min => {
3073 self.asm.cmp_rr(d, s);
3074 self.asm.cmovcc(Cc::G, d, s);
3075 }
3076 AluOp::Max => {
3077 self.asm.cmp_rr(d, s);
3078 self.asm.cmovcc(Cc::L, d, s);
3079 }
3080 AluOp::Minu => {
3081 self.asm.cmp_rr(d, s);
3082 self.asm.cmovcc(Cc::A, d, s);
3083 }
3084 AluOp::Maxu => {
3085 self.asm.cmp_rr(d, s);
3086 self.asm.cmovcc(Cc::B, d, s);
3087 }
3088 AluOp::Andn => {
3089 self.asm.mov_rr(SCRATCH, s);
3090 self.asm.not64(SCRATCH);
3091 self.asm.and_rr(d, SCRATCH);
3092 }
3093 AluOp::Orn => {
3094 self.asm.mov_rr(SCRATCH, s);
3095 self.asm.not64(SCRATCH);
3096 self.asm.or_rr(d, SCRATCH);
3097 }
3098 AluOp::Xnor => {
3099 self.asm.xor_rr(d, s);
3100 self.asm.not64(d);
3101 }
3102 }
3103 }
3104
3105 fn rv_slt_imm(&mut self, rd: u8, rs1: u8, imm: i32, signed: bool, pc: u32) {
3106 let Some(d) = self.rv_dst(rd, pc) else { return };
3107 if rv_is_reserved(rs1) {
3108 self.rv_emit_panic_at(pc);
3109 return;
3110 }
3111 let src = if rs1 == 0 {
3114 self.asm.mov_ri64(SCRATCH, 0);
3115 SCRATCH
3116 } else {
3117 let r1 = REG_MAP[rv_slot(rs1).unwrap()];
3118 if d == r1 {
3119 self.asm.mov_rr(SCRATCH, r1);
3120 SCRATCH
3121 } else {
3122 r1
3123 }
3124 };
3125 self.asm.mov_ri64(d, 0);
3128 self.asm.cmp_ri(src, imm);
3129 self.asm.setcc(if signed { Cc::L } else { Cc::B }, d);
3130 if rd != 0 {
3131 self.invalidate_reg(rv_slot(rd).unwrap());
3132 }
3133 }
3134
3135 fn rv_slt_rr(&mut self, rd: u8, rs1: u8, rs2: u8, signed: bool, pc: u32) {
3136 let Some(d) = self.rv_dst(rd, pc) else { return };
3137 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
3138 self.rv_emit_panic_at(pc);
3139 return;
3140 }
3141 if !signed && let Some((add_d, add_a, add_b)) = self.last_add_cf {
3151 let rs1_s = rv_slot(rs1);
3152 let rs2_s = rv_slot(rs2);
3153 let rd_s = rv_slot(rd);
3154 if let (Some(rs1_s), Some(rs2_s), Some(rd_s)) = (rs1_s, rs2_s, rd_s)
3155 && rs1_s == add_d
3156 && rs2_s != add_d
3157 && (rs2_s == add_a || rs2_s == add_b)
3158 && rd_s != rs2_s
3159 {
3160 self.asm.mov_ri32(d, 0);
3165 self.asm.setcc(Cc::B, d);
3166 self.invalidate_reg(rd_s);
3167 return;
3171 }
3172 }
3173 self.last_add_cf = None;
3176 let r1 = if rs1 == 0 {
3180 None
3181 } else {
3182 Some(REG_MAP[rv_slot(rs1).unwrap()])
3183 };
3184 let r2 = if rs2 == 0 {
3185 None
3186 } else {
3187 Some(REG_MAP[rv_slot(rs2).unwrap()])
3188 };
3189 let (a_reg, b_reg) = match (r1, r2) {
3193 (Some(ra), Some(rb)) => {
3194 if d == ra && d == rb {
3195 (ra, rb)
3197 } else if d == ra {
3198 self.asm.mov_rr(SCRATCH, ra);
3202 (SCRATCH, rb)
3203 } else if d == rb {
3204 self.asm.mov_rr(SCRATCH, rb);
3205 (ra, SCRATCH)
3206 } else {
3207 (ra, rb)
3208 }
3209 }
3210 (None, Some(rb)) => {
3211 if d == rb {
3217 self.asm.mov_rr(SCRATCH, rb);
3223 self.asm.mov_ri64(d, 0);
3224 self.asm.test_rr(SCRATCH, SCRATCH);
3225 self.asm.setcc(if signed { Cc::G } else { Cc::A }, d);
3226 if rd != 0 {
3227 self.invalidate_reg(rv_slot(rd).unwrap());
3228 }
3229 return;
3230 } else {
3231 self.asm.mov_ri64(d, 0);
3232 self.asm.test_rr(rb, rb);
3233 self.asm.setcc(if signed { Cc::G } else { Cc::A }, d);
3234 if rd != 0 {
3235 self.invalidate_reg(rv_slot(rd).unwrap());
3236 }
3237 return;
3238 }
3239 }
3240 (Some(ra), None) => {
3241 if d == ra {
3243 self.asm.mov_rr(SCRATCH, ra);
3244 self.asm.mov_ri64(d, 0);
3245 self.asm.cmp_ri(SCRATCH, 0);
3246 self.asm.setcc(if signed { Cc::L } else { Cc::B }, d);
3247 if rd != 0 {
3248 self.invalidate_reg(rv_slot(rd).unwrap());
3249 }
3250 return;
3251 } else {
3252 self.asm.mov_ri64(d, 0);
3254 self.asm.cmp_ri(ra, 0);
3255 self.asm.setcc(if signed { Cc::L } else { Cc::B }, d);
3256 if rd != 0 {
3257 self.invalidate_reg(rv_slot(rd).unwrap());
3258 }
3259 return;
3260 }
3261 }
3262 (None, None) => {
3263 self.asm.mov_ri64(d, 0);
3265 if rd != 0 {
3266 self.invalidate_reg(rv_slot(rd).unwrap());
3267 }
3268 return;
3269 }
3270 };
3271 self.asm.mov_ri64(d, 0);
3273 self.asm.cmp_rr(a_reg, b_reg);
3274 self.asm.setcc(if signed { Cc::L } else { Cc::B }, d);
3275 if rd != 0 {
3276 self.invalidate_reg(rv_slot(rd).unwrap());
3277 }
3278 }
3279
3280 fn rv_shift_imm(&mut self, rd: u8, rs1: u8, shamt: u8, op: ShiftOp, pc: u32) {
3283 let Some(d) = self.rv_dst(rd, pc) else { return };
3284 self.rv_read(rs1, d, pc);
3285 match op {
3286 ShiftOp::Shl64 => self.asm.shl_ri64(d, shamt & 63),
3287 ShiftOp::Shr64 => self.asm.shr_ri64(d, shamt & 63),
3288 ShiftOp::Sar64 => self.asm.sar_ri64(d, shamt & 63),
3289 ShiftOp::Shl32 => {
3290 self.asm.shl_ri32(d, shamt & 31);
3291 self.asm.movsxd(d, d);
3292 }
3293 ShiftOp::Shr32 => {
3294 self.asm.movzx_32_64(d, d);
3295 self.asm.shr_ri32(d, shamt & 31);
3296 self.asm.movsxd(d, d);
3297 }
3298 ShiftOp::Sar32 => {
3299 self.asm.sar_ri32(d, shamt & 31);
3300 self.asm.movsxd(d, d);
3301 }
3302 ShiftOp::Ror64 => self.asm.ror_ri64(d, shamt & 63),
3303 ShiftOp::Ror32 => {
3304 self.asm.movzx_32_64(d, d);
3305 self.asm.ror_ri32(d, shamt & 31);
3306 self.asm.movsxd(d, d);
3307 }
3308 ShiftOp::Rol64 | ShiftOp::Rol32 => {
3309 self.rv_emit_panic_at(pc);
3311 }
3312 }
3313 if rd != 0 {
3314 self.invalidate_reg(rv_slot(rd).unwrap());
3315 }
3316 }
3317
3318 fn rv_shift_rr(&mut self, rd: u8, rs1: u8, rs2: u8, op: ShiftOp, pc: u32) {
3319 let Some(d) = self.rv_dst(rd, pc) else { return };
3320 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
3321 self.rv_emit_panic_at(pc);
3322 return;
3323 }
3324 let r2 = if rs2 == 0 {
3326 None
3327 } else {
3328 Some(REG_MAP[rv_slot(rs2).unwrap()])
3329 };
3330 let r1 = if rs1 == 0 {
3331 None
3332 } else {
3333 Some(REG_MAP[rv_slot(rs1).unwrap()])
3334 };
3335 let shift_src = if rs2 == 0 {
3336 self.asm.mov_ri64(SCRATCH, 0);
3337 SCRATCH
3338 } else if Some(d) == r2 && r1 != r2 {
3339 self.asm.mov_rr(SCRATCH, r2.unwrap());
3340 SCRATCH
3341 } else {
3342 r2.unwrap()
3343 };
3344 self.rv_read(rs1, d, pc);
3345 let sub_op: u8 = match op {
3346 ShiftOp::Shl64 | ShiftOp::Shl32 => 4,
3347 ShiftOp::Shr64 | ShiftOp::Shr32 => 5,
3348 ShiftOp::Sar64 | ShiftOp::Sar32 => 7,
3349 ShiftOp::Rol64 | ShiftOp::Rol32 => 0,
3350 ShiftOp::Ror64 | ShiftOp::Ror32 => 1,
3351 };
3352 let is_32 = matches!(
3353 op,
3354 ShiftOp::Shl32 | ShiftOp::Shr32 | ShiftOp::Sar32 | ShiftOp::Rol32 | ShiftOp::Ror32
3355 );
3356 if is_32 {
3357 if matches!(op, ShiftOp::Shr32 | ShiftOp::Ror32) {
3358 self.asm.movzx_32_64(d, d);
3359 }
3360 self.emit_shift_by_reg32(d, shift_src, sub_op);
3361 self.asm.movsxd(d, d);
3362 } else {
3363 self.emit_shift_by_reg64(d, shift_src, sub_op);
3364 }
3365 if rd != 0 {
3366 self.invalidate_reg(rv_slot(rd).unwrap());
3367 }
3368 }
3369
3370 fn rv_mulh(&mut self, rd: u8, rs1: u8, rs2: u8, a_signed: bool, b_signed: bool, pc: u32) {
3373 let Some(d) = self.rv_dst(rd, pc) else { return };
3374 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
3375 self.rv_emit_panic_at(pc);
3376 return;
3377 }
3378 let save_rax = d != Reg::RAX;
3380 let r2_mapped = if rs2 == 0 {
3381 None
3382 } else {
3383 Some(REG_MAP[rv_slot(rs2).unwrap()])
3384 };
3385 let snapshot_rs2 = r2_mapped == Some(Reg::RAX);
3391 if snapshot_rs2 {
3392 self.asm.mov_rr(SCRATCH, Reg::RAX);
3393 }
3394 if save_rax {
3395 self.asm.push(Reg::RAX);
3396 }
3397 if rs1 == 0 {
3399 self.asm.mov_ri64(Reg::RAX, 0);
3400 } else {
3401 let r1 = REG_MAP[rv_slot(rs1).unwrap()];
3402 if r1 != Reg::RAX {
3403 self.asm.mov_rr(Reg::RAX, r1);
3404 }
3405 if r1 == Reg::RAX && save_rax {
3407 self.asm.mov_load64(Reg::RAX, Reg::RSP, 0);
3408 }
3409 }
3410 let b_reg = if rs2 == 0 {
3412 self.asm.mov_ri64(SCRATCH, 0);
3413 SCRATCH
3414 } else if snapshot_rs2 {
3415 SCRATCH
3417 } else {
3418 r2_mapped.unwrap()
3419 };
3420 if a_signed && b_signed {
3421 self.asm.imul_rdx_rax(b_reg);
3422 } else if !a_signed && !b_signed {
3423 self.asm.mul_rdx_rax(b_reg);
3424 } else {
3425 self.asm.push(b_reg);
3427 self.asm.push(Reg::RAX); self.asm.mul_rdx_rax(b_reg);
3429 self.asm.pop(Reg::RAX); let skip = self.asm.new_label();
3431 self.asm.test_rr(Reg::RAX, Reg::RAX);
3432 self.asm.jcc_label(Cc::NS, skip);
3433 self.asm.pop(Reg::RAX); self.asm.sub_rr(SCRATCH, Reg::RAX);
3435 let done = self.asm.new_label();
3436 self.asm.jmp_label(done);
3437 self.asm.bind_label(skip);
3438 self.asm.add_ri(Reg::RSP, 8); self.asm.bind_label(done);
3440 }
3441 if save_rax {
3443 self.asm.mov_rr(d, SCRATCH);
3444 self.asm.pop(Reg::RAX);
3445 } else {
3446 self.asm.mov_rr(Reg::RAX, SCRATCH);
3447 }
3448 if rd != 0 {
3449 self.invalidate_reg(rv_slot(rd).unwrap());
3450 }
3451 }
3452
3453 #[allow(clippy::too_many_arguments)]
3456 fn rv_div_rem(
3457 &mut self,
3458 rd: u8,
3459 rs1: u8,
3460 rs2: u8,
3461 signed: bool,
3462 remainder: bool,
3463 is_32bit: bool,
3464 pc: u32,
3465 ) {
3466 let Some(d) = self.rv_dst(rd, pc) else { return };
3467 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
3468 self.rv_emit_panic_at(pc);
3469 return;
3470 }
3471 let save_rax = d != Reg::RAX;
3474 if save_rax {
3475 self.asm.push(Reg::RAX);
3476 }
3477 let r2 = if rs2 == 0 {
3481 None
3482 } else {
3483 Some(REG_MAP[rv_slot(rs2).unwrap()])
3484 };
3485 let spilled_rcx = rs2 == 0 || r2 == Some(Reg::RAX);
3486 if spilled_rcx {
3487 self.asm.push(Reg::RCX);
3488 }
3489 let b_reg = if rs2 == 0 {
3491 self.asm.mov_ri64(Reg::RCX, 0);
3492 Reg::RCX
3493 } else if r2 == Some(Reg::RAX) {
3494 if save_rax {
3496 self.asm.mov_load64(Reg::RCX, Reg::RSP, 8);
3498 } else {
3499 self.asm.mov_rr(Reg::RCX, Reg::RAX);
3502 }
3503 Reg::RCX
3504 } else {
3505 r2.unwrap()
3506 };
3507 if rs1 == 0 {
3509 self.asm.mov_ri64(Reg::RAX, 0);
3510 } else {
3511 let r1 = REG_MAP[rv_slot(rs1).unwrap()];
3512 if r1 == Reg::RAX {
3513 if save_rax {
3514 let off = if spilled_rcx { 8 } else { 0 };
3515 self.asm.mov_load64(Reg::RAX, Reg::RSP, off);
3516 }
3517 } else {
3519 self.asm.mov_rr(Reg::RAX, r1);
3520 }
3521 }
3522 if is_32bit {
3528 self.asm.test_rr32(b_reg, b_reg);
3529 } else {
3530 self.asm.test_rr(b_reg, b_reg);
3531 }
3532 let nonzero = self.asm.new_label();
3533 let join = self.asm.new_label();
3534 self.asm.jcc_label(Cc::NE, nonzero);
3535 if remainder {
3537 if d != Reg::RAX {
3538 self.asm.mov_rr(d, Reg::RAX);
3539 }
3540 if is_32bit {
3541 self.asm.movsxd(d, d);
3542 }
3543 } else {
3544 self.asm.mov_ri64(d, u64::MAX);
3545 }
3547 self.asm.jmp_label(join);
3548
3549 self.asm.bind_label(nonzero);
3551 if signed {
3559 let not_neg_one = self.asm.new_label();
3560 if is_32bit {
3561 self.asm.cmp_ri32(b_reg, -1);
3562 } else {
3563 self.asm.cmp_ri(b_reg, -1);
3564 }
3565 self.asm.jcc_label(Cc::NE, not_neg_one);
3566 if remainder {
3567 self.asm.mov_ri64(d, 0); } else {
3569 if d != Reg::RAX {
3570 self.asm.mov_rr(d, Reg::RAX);
3571 }
3572 self.asm.neg64(d); if is_32bit {
3574 self.asm.movsxd(d, d);
3577 }
3578 }
3579 self.asm.jmp_label(join);
3580 self.asm.bind_label(not_neg_one);
3581 }
3582 if is_32bit {
3583 if signed {
3584 self.asm.movsxd(Reg::RAX, Reg::RAX);
3585 self.asm.cdq();
3586 self.asm.idiv32(b_reg);
3587 } else {
3588 self.asm.movzx_32_64(Reg::RAX, Reg::RAX);
3589 self.asm.mov_ri64(SCRATCH, 0);
3590 self.asm.div32(b_reg);
3591 }
3592 } else if signed {
3593 self.asm.cqo();
3594 self.asm.idiv64(b_reg);
3595 } else {
3596 self.asm.mov_ri64(SCRATCH, 0);
3597 self.asm.div64(b_reg);
3598 }
3599 let result_reg = if remainder { SCRATCH } else { Reg::RAX };
3600 if d != result_reg {
3601 self.asm.mov_rr(d, result_reg);
3602 }
3603 if is_32bit {
3604 self.asm.movsxd(d, d);
3605 }
3606
3607 self.asm.bind_label(join);
3609 if spilled_rcx {
3610 self.asm.pop(Reg::RCX);
3611 }
3612 if save_rax {
3613 self.asm.pop(Reg::RAX);
3614 }
3615 if rd != 0 {
3616 self.invalidate_reg(rv_slot(rd).unwrap());
3617 }
3618 }
3619
3620 fn rv_unary(&mut self, rd: u8, rs1: u8, op: UnaryOp, pc: u32) {
3623 let Some(d) = self.rv_dst(rd, pc) else { return };
3624 let src = if rs1 == 0 {
3625 self.asm.mov_ri64(SCRATCH, 0);
3626 SCRATCH
3627 } else if rv_is_reserved(rs1) {
3628 self.rv_emit_panic_at(pc);
3629 return;
3630 } else {
3631 REG_MAP[rv_slot(rs1).unwrap()]
3632 };
3633 match op {
3634 UnaryOp::Clz64 => self.asm.lzcnt64(d, src),
3635 UnaryOp::Clz32 => self.asm.lzcnt32(d, src),
3636 UnaryOp::Ctz64 => self.asm.tzcnt64(d, src),
3637 UnaryOp::Ctz32 => self.asm.tzcnt32(d, src),
3638 UnaryOp::Popcnt64 => self.asm.popcnt64(d, src),
3639 UnaryOp::Popcnt32 => self.asm.popcnt32(d, src),
3640 UnaryOp::SextB => self.asm.movsx_8_64(d, src),
3641 UnaryOp::SextH => self.asm.movsx_16_64(d, src),
3642 UnaryOp::ZextH => self.asm.movzx_16_64(d, src),
3643 UnaryOp::Rev8 => {
3644 if d != src {
3645 self.asm.mov_rr(d, src);
3646 }
3647 self.asm.bswap64(d);
3648 }
3649 UnaryOp::OrcB => {
3650 const LO7: u64 = 0x7F7F_7F7F_7F7F_7F7F;
3661 const HI: u64 = 0x8080_8080_8080_8080;
3662 self.asm.mov_rr(SCRATCH, src); self.asm.push(SCRATCH); self.asm.mov_ri64(d, LO7);
3665 self.asm.and_rr(d, SCRATCH); self.asm.mov_ri64(SCRATCH, LO7);
3667 self.asm.add_rr(d, SCRATCH); self.asm.pop(SCRATCH); self.asm.or_rr(d, SCRATCH); self.asm.mov_ri64(SCRATCH, HI);
3671 self.asm.and_rr(d, SCRATCH); self.asm.push(d); self.asm.mov_rr(SCRATCH, d);
3674 self.asm.shr_ri64(SCRATCH, 7); self.asm.sub_rr(d, SCRATCH); self.asm.pop(SCRATCH); self.asm.or_rr(d, SCRATCH); }
3679 }
3680 if rd != 0 {
3681 self.invalidate_reg(rv_slot(rd).unwrap());
3682 }
3683 }
3684
3685 fn rv_shadd(&mut self, rd: u8, rs1: u8, rs2: u8, shift: u8, uw: bool, pc: u32) {
3688 let Some(d) = self.rv_dst(rd, pc) else { return };
3689 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
3690 self.rv_emit_panic_at(pc);
3691 return;
3692 }
3693 if rs1 == 0 {
3695 self.asm.mov_ri64(SCRATCH, 0);
3696 } else {
3697 let r1 = REG_MAP[rv_slot(rs1).unwrap()];
3698 if uw {
3699 self.asm.movzx_32_64(SCRATCH, r1);
3700 } else {
3701 self.asm.mov_rr(SCRATCH, r1);
3702 }
3703 }
3704 self.asm.shl_ri64(SCRATCH, shift);
3705 if rs2 == 0 {
3707 self.asm.mov_ri64(d, 0);
3708 } else {
3709 let r2 = REG_MAP[rv_slot(rs2).unwrap()];
3710 if d != r2 {
3711 self.asm.mov_rr(d, r2);
3712 }
3713 }
3714 self.asm.add_rr(d, SCRATCH);
3715 if rd != 0 {
3716 self.invalidate_reg(rv_slot(rd).unwrap());
3717 }
3718 }
3719
3720 fn rv_adduw(&mut self, rd: u8, rs1: u8, rs2: u8, pc: u32) {
3721 let Some(d) = self.rv_dst(rd, pc) else { return };
3722 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
3723 self.rv_emit_panic_at(pc);
3724 return;
3725 }
3726 if rs1 == 0 {
3727 self.asm.mov_ri64(SCRATCH, 0);
3728 } else {
3729 let r1 = REG_MAP[rv_slot(rs1).unwrap()];
3730 self.asm.movzx_32_64(SCRATCH, r1);
3731 }
3732 if rs2 == 0 {
3733 self.asm.mov_ri64(d, 0);
3734 } else {
3735 let r2 = REG_MAP[rv_slot(rs2).unwrap()];
3736 if d != r2 {
3737 self.asm.mov_rr(d, r2);
3738 }
3739 }
3740 self.asm.add_rr(d, SCRATCH);
3741 if rd != 0 {
3742 self.invalidate_reg(rv_slot(rd).unwrap());
3743 }
3744 }
3745
3746 fn rv_slliuw(&mut self, rd: u8, rs1: u8, shamt: u8, pc: u32) {
3747 let Some(d) = self.rv_dst(rd, pc) else { return };
3748 if rs1 == 0 {
3749 self.asm.mov_ri64(d, 0);
3750 } else if rv_is_reserved(rs1) {
3751 self.rv_emit_panic_at(pc);
3752 return;
3753 } else {
3754 let r1 = REG_MAP[rv_slot(rs1).unwrap()];
3755 self.asm.movzx_32_64(d, r1);
3756 self.asm.shl_ri64(d, shamt & 63);
3757 }
3758 if rd != 0 {
3759 self.invalidate_reg(rv_slot(rd).unwrap());
3760 }
3761 }
3762
3763 fn rv_bit_rr(&mut self, rd: u8, rs1: u8, rs2: u8, op: BitOp, pc: u32) {
3766 let Some(d) = self.rv_dst(rd, pc) else { return };
3767 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
3768 self.rv_emit_panic_at(pc);
3769 return;
3770 }
3771 self.asm.mov_ri64(SCRATCH, 1);
3773 if rs2 != 0 {
3774 let r2 = REG_MAP[rv_slot(rs2).unwrap()];
3775 if r2 == Reg::RCX {
3776 self.asm.shl_cl64(SCRATCH);
3777 } else {
3778 self.asm.push(Reg::RCX);
3779 self.asm.mov_rr(Reg::RCX, r2);
3780 self.asm.shl_cl64(SCRATCH);
3781 self.asm.pop(Reg::RCX);
3782 }
3783 }
3784 self.rv_read(rs1, d, pc);
3786 match op {
3787 BitOp::Clear => {
3788 self.asm.not64(SCRATCH);
3789 self.asm.and_rr(d, SCRATCH);
3790 }
3791 BitOp::Set => self.asm.or_rr(d, SCRATCH),
3792 BitOp::Invert => self.asm.xor_rr(d, SCRATCH),
3793 BitOp::Extract => {
3794 self.asm.test_rr(d, SCRATCH);
3797 self.asm.mov_ri32(d, 0);
3798 self.asm.setcc(Cc::NE, d);
3799 }
3800 }
3801 if rd != 0 {
3802 self.invalidate_reg(rv_slot(rd).unwrap());
3803 }
3804 }
3805
3806 fn rv_bit_imm(&mut self, rd: u8, rs1: u8, shamt: u8, op: BitOp, pc: u32) {
3807 let Some(d) = self.rv_dst(rd, pc) else { return };
3808 if rv_is_reserved(rs1) {
3809 self.rv_emit_panic_at(pc);
3810 return;
3811 }
3812 let s = shamt & 0x3F;
3813 if s < 31 {
3814 let mask_lo: i32 = 1i32 << s;
3815 self.rv_read(rs1, d, pc);
3816 match op {
3817 BitOp::Clear => self.asm.and_ri(d, !mask_lo),
3818 BitOp::Set => self.asm.or_ri(d, mask_lo),
3819 BitOp::Invert => self.asm.xor_ri(d, mask_lo),
3820 BitOp::Extract => {
3821 self.asm.shr_ri64(d, s);
3822 self.asm.and_ri(d, 1);
3823 }
3824 }
3825 } else {
3826 let mask: u64 = 1u64 << s;
3827 self.asm.mov_ri64(SCRATCH, mask);
3828 self.rv_read(rs1, d, pc);
3829 match op {
3830 BitOp::Clear => {
3831 self.asm.not64(SCRATCH);
3832 self.asm.and_rr(d, SCRATCH);
3833 }
3834 BitOp::Set => self.asm.or_rr(d, SCRATCH),
3835 BitOp::Invert => self.asm.xor_rr(d, SCRATCH),
3836 BitOp::Extract => {
3837 self.asm.shr_ri64(d, s);
3838 self.asm.and_ri(d, 1);
3839 }
3840 }
3841 }
3842 if rd != 0 {
3843 self.invalidate_reg(rv_slot(rd).unwrap());
3844 }
3845 }
3846
3847 fn rv_czero(&mut self, rd: u8, rs1: u8, rs2: u8, cond: Cc, pc: u32) {
3864 let Some(d) = self.rv_dst(rd, pc) else { return };
3865 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
3866 self.rv_emit_panic_at(pc);
3867 return;
3868 }
3869 let slot = rv_slot(rd).unwrap();
3870
3871 if rs2 == 0 {
3873 if matches!(cond, Cc::E) {
3877 self.asm.mov_ri64(d, 0);
3878 } else {
3879 self.rv_read(rs1, d, pc);
3880 }
3881 self.invalidate_reg(slot);
3882 return;
3883 }
3884 if rs1 == 0 {
3885 self.asm.mov_ri64(d, 0);
3887 self.invalidate_reg(slot);
3888 return;
3889 }
3890 if rs1 == rs2 {
3891 if matches!(cond, Cc::E) {
3894 self.rv_read(rs1, d, pc);
3895 } else {
3896 self.asm.mov_ri64(d, 0);
3897 }
3898 self.invalidate_reg(slot);
3899 return;
3900 }
3901
3902 let r1 = REG_MAP[rv_slot(rs1).unwrap()];
3903 let r2 = REG_MAP[rv_slot(rs2).unwrap()];
3904 let opposite = match cond {
3905 Cc::E => Cc::NE,
3906 Cc::NE => Cc::E,
3907 _ => unreachable!("rv_czero only accepts E/NE"),
3908 };
3909
3910 if d == r1 {
3911 self.asm.test_rr(r2, r2);
3916 self.asm.mov_ri32(SCRATCH, 0);
3917 self.asm.cmovcc(cond, d, SCRATCH);
3918 } else {
3919 self.asm.test_rr(r2, r2);
3922 self.asm.mov_ri32(d, 0);
3923 self.asm.cmovcc(opposite, d, r1);
3924 }
3925 self.invalidate_reg(slot);
3926 }
3927
3928 fn rv_jal(&mut self, rd: u8, imm: i32, pc: u32, next_pc: u32) {
3931 if rv_is_reserved(rd) {
3932 self.rv_emit_panic_at(pc);
3933 return;
3934 }
3935 if rd != 0 {
3936 let slot = rv_slot(rd).unwrap();
3939 self.asm
3940 .mov_ri64(REG_MAP[slot], self.code_base.wrapping_add(next_pc) as u64);
3941 self.invalidate_reg(slot);
3942 }
3943 let target = (pc as i64).wrapping_add(imm as i64) as u32;
3944 self.emit_static_branch(target, true, next_pc, pc);
3945 }
3946
3947 fn rv_jalr(&mut self, rd: u8, rs1: u8, imm: i32, pc: u32, next_pc: u32) {
3961 use super::asm::Cc;
3962
3963 if rv_is_reserved(rs1) {
3964 self.rv_emit_panic_at(pc);
3965 return;
3966 }
3967
3968 self.rv_read(rs1, SCRATCH, pc);
3970 if imm != 0 {
3971 self.asm.add_ri(SCRATCH, imm);
3972 }
3973 self.asm.shl_ri64(SCRATCH, 32);
3975 self.asm.shr_ri64(SCRATCH, 32);
3976
3977 if rd != 0 {
3980 let slot = rv_slot(rd).unwrap();
3981 self.asm
3982 .mov_ri64(REG_MAP[slot], self.code_base.wrapping_add(next_pc) as u64);
3983 self.invalidate_reg(slot);
3984 }
3985
3986 if self.code_base != 0 {
3988 self.asm.sub_ri(SCRATCH, self.code_base as i32);
3989 }
3990
3991 self.asm.mov_store32_rip_rel(CTX_PC, SCRATCH);
3993
3994 self.asm.cmp_ri32(SCRATCH, self.code_len as i32);
3997 self.asm.jcc_label(Cc::AE, self.panic_label);
3998
3999 self.asm.push(Reg::RAX);
4006 self.asm.mov_load64_rip_rel(Reg::RAX, CTX_DISPATCH_TABLE);
4007 self.asm.movsxd_load_sib4(Reg::RAX, Reg::RAX, SCRATCH);
4008 self.asm.add_r64_mem_rip_rel(Reg::RAX, CTX_CODE_BASE);
4009 self.asm.mov_rr(SCRATCH, Reg::RAX);
4010 self.asm.pop(Reg::RAX);
4011 self.asm.jmp_reg(SCRATCH);
4012 }
4013
4014 fn rv_branch(&mut self, rs1: u8, rs2: u8, imm: i32, cc: Cc, pc: u32, next_pc: u32) {
4015 if rv_is_reserved(rs1) || rv_is_reserved(rs2) {
4016 self.rv_emit_panic_at(pc);
4017 return;
4018 }
4019 let target = (pc as i64).wrapping_add(imm as i64) as u32;
4020 let a = self.rv_read_into(rs1, SCRATCH, pc);
4021 let b = if a == SCRATCH {
4022 if rs2 == 0 {
4023 SCRATCH
4025 } else {
4026 REG_MAP[rv_slot(rs2).unwrap()]
4027 }
4028 } else if rs2 == 0 {
4029 self.asm.mov_ri64(SCRATCH, 0);
4030 SCRATCH
4031 } else {
4032 REG_MAP[rv_slot(rs2).unwrap()]
4033 };
4034 self.emit_branch_reg(a, b, cc, target, next_pc, pc);
4035 }
4036
4037 fn rv_trap(&mut self, pc: u32) {
4040 self.asm.mov_store32_rip_rel_imm(CTX_PC, pc as i32);
4041 self.asm
4042 .mov_store32_rip_rel_imm(CTX_EXIT_REASON, EXIT_TRAP as i32);
4043 self.asm.mov_store32_rip_rel_imm(CTX_EXIT_ARG, 0);
4044 self.asm.jmp_label(self.exit_label);
4045 }
4046
4047 fn rv_ecall_jar(&mut self, next_pc: u32) {
4048 self.asm.mov_store32_rip_rel_imm(CTX_PC, next_pc as i32);
4049 self.asm
4050 .mov_store32_rip_rel_imm(CTX_EXIT_REASON, EXIT_ECALL as i32);
4051 self.asm.mov_store32_rip_rel_imm(CTX_EXIT_ARG, 0);
4052 self.asm.jmp_label(self.exit_label);
4053 }
4054
4055 fn rv_ecalli(&mut self, imm: i32, next_pc: u32) {
4056 self.asm.mov_store32_rip_rel_imm(CTX_PC, next_pc as i32);
4057 self.asm
4058 .mov_store32_rip_rel_imm(CTX_EXIT_REASON, EXIT_HOST_CALL as i32);
4059 self.asm.mov_store32_rip_rel_imm(CTX_EXIT_ARG, imm);
4060 self.asm.jmp_label(self.exit_label);
4061 }
4062
4063 fn rv_emit_panic_at(&mut self, pc: u32) {
4065 self.asm.mov_store32_rip_rel_imm(CTX_PC, pc as i32);
4066 self.asm
4067 .mov_store32_rip_rel_imm(CTX_EXIT_REASON, EXIT_PANIC as i32);
4068 self.asm.jmp_label(self.exit_label);
4069 }
4070
4071 #[inline]
4087 fn track_const(&mut self, rd: u8, imm: i32) {
4088 use super::codegen::RegDef;
4089 if let Some(slot) = rv_slot(rd) {
4090 self.reg_defs[slot] = RegDef::Const(imm as u32);
4091 self.reg_defs_active |= 1u16 << slot;
4092 self.invalidate_dependents(slot);
4093 }
4094 }
4095
4096 #[inline]
4101 fn track_shifted(&mut self, rd: u8, rs1: u8, shamt: u8) {
4102 use super::codegen::RegDef;
4103 if let (Some(d), Some(s)) = (rv_slot(rd), rv_slot(rs1)) {
4104 self.reg_defs[d] = RegDef::Shifted {
4105 src: s,
4106 shift: shamt,
4107 };
4108 self.reg_defs_active |= 1u16 << d;
4109 self.invalidate_dependents(d);
4110 }
4111 }
4112
4113 #[inline]
4117 fn track_add_scaledadd(&mut self, rd: u8, rs1: u8, rs2: u8) {
4118 use super::codegen::RegDef;
4119 let (Some(d), Some(a), Some(b)) = (rv_slot(rd), rv_slot(rs1), rv_slot(rs2)) else {
4120 return;
4121 };
4122 let def = if let RegDef::Shifted { src, shift } = self.reg_defs[b] {
4123 Some(RegDef::ScaledAdd {
4124 base: a,
4125 idx: src,
4126 shift,
4127 })
4128 } else if let RegDef::Shifted { src, shift } = self.reg_defs[a] {
4129 Some(RegDef::ScaledAdd {
4130 base: b,
4131 idx: src,
4132 shift,
4133 })
4134 } else {
4135 None
4136 };
4137 if let Some(def) = def {
4138 self.reg_defs[d] = def;
4139 self.reg_defs_active |= 1u16 << d;
4140 self.invalidate_dependents(d);
4141 }
4142 }
4144
4145 #[inline]
4154 fn record_scaledadd(&mut self, rd: u8, rs1: u8, rs2: u8, shift: u8) {
4155 use super::codegen::RegDef;
4156 if rd == rs1 || rd == rs2 {
4157 return;
4158 }
4159 let (Some(d), Some(idx), Some(base)) = (rv_slot(rd), rv_slot(rs1), rv_slot(rs2)) else {
4160 return;
4161 };
4162 self.reg_defs[d] = RegDef::ScaledAdd { base, idx, shift };
4163 self.reg_defs_active |= 1u16 << d;
4164 self.invalidate_dependents(d);
4165 }
4166}
4167
4168#[derive(Clone, Copy)]
4169enum AluImmOp {
4170 Add,
4171 And,
4172 Or,
4173 Xor,
4174 Addw,
4175}
4176
4177#[derive(Clone, Copy)]
4178enum AluOp {
4179 Add,
4180 Sub,
4181 And,
4182 Or,
4183 Xor,
4184 Mul,
4185 Addw,
4186 Subw,
4187 Mulw,
4188 Min,
4189 Max,
4190 Minu,
4191 Maxu,
4192 Andn,
4193 Orn,
4194 Xnor,
4195}
4196
4197#[derive(Clone, Copy)]
4198enum ShiftOp {
4199 Shl64,
4200 Shr64,
4201 Sar64,
4202 Shl32,
4203 Shr32,
4204 Sar32,
4205 Rol64,
4206 Ror64,
4207 Rol32,
4208 Ror32,
4209}
4210
4211#[derive(Clone, Copy)]
4212enum BitOp {
4213 Clear,
4214 Set,
4215 Invert,
4216 Extract,
4217}
4218
4219#[derive(Clone, Copy)]
4220enum UnaryOp {
4221 Clz64,
4222 Clz32,
4223 Ctz64,
4224 Ctz32,
4225 Popcnt64,
4226 Popcnt32,
4227 SextB,
4228 SextH,
4229 ZextH,
4230 Rev8,
4231 OrcB,
4232}