From 50856f96e11928c7dca101060cbaccbfc36e9f02 Mon Sep 17 00:00:00 2001 From: KonPet Date: Tue, 15 Sep 2026 15:23:59 +0200 Subject: [PATCH 1/7] Add Risc-V XAndesPerf BBC, BBS, BEQC and BNEC --- arch/riscv/disasm/src/lib.rs | 140 +++++++++++++++++++++++++++++++++++ arch/riscv/src/lib.rs | 98 ++++++++++++++++++++++++ 2 files changed, 238 insertions(+) diff --git a/arch/riscv/disasm/src/lib.rs b/arch/riscv/disasm/src/lib.rs index d19ebf220c..d530566f7d 100644 --- a/arch/riscv/disasm/src/lib.rs +++ b/arch/riscv/disasm/src/lib.rs @@ -236,6 +236,16 @@ pub enum Op { // WCH WchMcpy(RTypeIntInst), // does not use R-format, but uses three registers + + // + // XAndesPerf + // + + // BRANCH + Bbc(NdsBranchBit), + Bbs(NdsBranchBit), + Beqc(NdsBranchConst), + Bnec(NdsBranchConst), } pub trait Register { @@ -1752,6 +1762,99 @@ impl FpClassInst { } } +#[derive(Copy, Clone, Debug)] +pub struct NdsBranchBit { + rs1: IntReg, + cimm: u8, + imm: i16, +} + +impl NdsBranchBit { + #[inline(always)] + fn from_instr32(instr: Instr32) -> DisResult { + let rs1 = IntReg::new(instr.rs1()); + + // Propagate sign, clear out bits 0 through 9, then insert the missing bits + let imm = (instr.0 as i32 >> 31) & !((1 << 10) - 1) + | (instr.extract_bits(25, 5) << 5) as i32 + | (instr.extract_bits(8, 4) << 1) as i32; + + // cimm has an additional bit in RV64 + let cimm = instr.extract_bits(20, 5) + | if ::Int::width() == 8 { + instr.extract_bits(7, 1) << 5 + } else { + 0 + }; + + Ok(Self { + rs1: rs1, + cimm: cimm as u8, + imm: imm as i16, + }) + } + + #[inline(always)] + pub fn rs1(&self) -> IntReg { + self.rs1 + } + + #[inline(always)] + pub fn cimm(&self) -> u8 { + self.cimm + } + + #[inline(always)] + pub fn imm(&self) -> i16 { + self.imm + } +} + +#[derive(Copy, Clone, Debug)] +pub struct NdsBranchConst { + rs1: IntReg, + cimm: u8, + imm: i16, +} + +impl NdsBranchConst { + #[inline(always)] + fn from_instr32(instr: Instr32) -> DisResult { + let rs1 = IntReg::new(instr.rs1()); + + // Propagate sign, clear out bits 0 through 9, then insert the missing bits + let imm = (instr.0 as i32 >> 31) & !((1 << 10) - 1) + | (instr.extract_bits(25, 5) << 5) as i32 + | (instr.extract_bits(8, 4) << 1) as i32; + + // cimm has an additional bit in RV64 + let cimm = instr.extract_bits(20, 5) + | (instr.extract_bits(7, 1) << 5) + | (instr.extract_bits(30, 1) << 6); + + Ok(Self { + rs1: rs1, + cimm: cimm as u8, + imm: imm as i16, + }) + } + + #[inline(always)] + pub fn rs1(&self) -> IntReg { + self.rs1 + } + + #[inline(always)] + pub fn cimm(&self) -> u8 { + self.cimm + } + + #[inline(always)] + pub fn imm(&self) -> i16 { + self.imm + } +} + #[derive(Copy, Clone, Debug)] pub struct Instr16(u16); impl Instr16 { @@ -2107,6 +2210,26 @@ impl Instr { ops.push(Operand::R(f.rd())); ops.push(Operand::F(f.rs1())); } + Op::Bbc(ref a) => { + ops.push(Operand::R(a.rs1())); + ops.push(Operand::I(a.cimm() as i32)); + ops.push(Operand::I(a.imm() as i32)); + } + Op::Bbs(ref a) => { + ops.push(Operand::R(a.rs1())); + ops.push(Operand::I(a.cimm() as i32)); + ops.push(Operand::I(a.imm() as i32)); + } + Op::Beqc(ref a) => { + ops.push(Operand::R(a.rs1())); + ops.push(Operand::I(a.cimm() as i32)); + ops.push(Operand::I(a.imm() as i32)); + } + Op::Bnec(ref a) => { + ops.push(Operand::R(a.rs1())); + ops.push(Operand::I(a.cimm() as i32)); + ops.push(Operand::I(a.imm() as i32)); + } }, } @@ -2286,6 +2409,11 @@ impl<'a, D: RiscVDisassembler + 'a> Mnem<'a, D> { Op::Rev8(..) => "rev8", Op::WchMcpy(..) => "qk.mcpy", + + Op::Bbc(..) => "nds.bbc", + Op::Bbs(..) => "nds.bbs", + Op::Beqc(..) => "nds.beqc", + Op::Bnec(..) => "nds.bnec", }, } } @@ -3557,6 +3685,18 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn _ => return Err(InvalidSubop), } } + 0b10110 => match inst.funct3() { + 0b111 => { + if inst.extract_bits(30, 1) == 0 { + Op::Bbc(NdsBranchBit::from_instr32(inst)?) + } else { + Op::Bbs(NdsBranchBit::from_instr32(inst)?) + } + } + 0b101 => Op::Beqc(NdsBranchConst::from_instr32(inst)?), + 0b110 => Op::Bnec(NdsBranchConst::from_instr32(inst)?), + _ => return Err(InvalidSubop), + }, // TODO CUSTOM_2 0b11000 => { // BRANCH diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index d796bef1b4..19d18a0c15 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -834,6 +834,14 @@ impl Architecture for RiscVArch { Op::Uret | Op::Sret | Op::Mret => { res.add_branch(BranchKind::FunctionReturn); } + Op::Bbc(ref a) | Op::Bbs(ref a) => { + res.add_branch(BranchKind::False(addr.wrapping_add(inst_len as u64))); + res.add_branch(BranchKind::True(addr.wrapping_add(a.imm() as u64))); + } + Op::Beqc(ref a) | Op::Bnec(ref a) => { + res.add_branch(BranchKind::False(addr.wrapping_add(inst_len as u64))); + res.add_branch(BranchKind::True(addr.wrapping_add(a.imm() as u64))); + } _ => {} } @@ -1097,6 +1105,8 @@ impl Architecture for RiscVArch { res.push(InstructionTextToken::new(" ", Text)); } + let idx = i; + match *oper { Operand::R(r) => { let reg = self::Register::from(r); @@ -1129,6 +1139,19 @@ impl Architecture for RiscVArch { }, )); } + Op::Bbc(..) | Op::Bbs(..) | Op::Beqc(..) | Op::Bnec(..) if idx == 2 => { + // BRANCH or JAL + let target = addr.wrapping_add(i as i64 as u64); + + res.push(InstructionTextToken::new( + format!("0x{:x}", target), + CodeRelativeAddress { + value: target, + size: Some(self.address_size()), + operand: None, + }, + )); + } _ => { res.push(InstructionTextToken::new( &match i { @@ -2227,6 +2250,81 @@ impl Architecture for RiscVArch { ) .append(); } + Op::Bbc(a) | Op::Bbs(a) => { + let rs1 = Register::from(a.rs1()); + let bit_idx = a.cimm(); + let bit = il.test_bit(rs1.size(), rs1, bit_idx); + + let cond_expr = match op { + Op::Bbc(..) => il.cmp_e(max_width, bit, 0), + Op::Bbs(..) => il.cmp_ne(max_width, bit, 0), + _ => unreachable!(), + }; + + let mut new_false = false; + let mut new_true = false; + + let ft = addr.wrapping_add(inst_len); + let tt = addr.wrapping_add(a.imm() as i64 as u64); + + let mut f = il.label_for_address(ft).unwrap_or_else(|| { + new_false = true; + LowLevelILLabel::new() + }); + + let mut t = il.label_for_address(tt).unwrap_or_else(|| { + new_true = true; + LowLevelILLabel::new() + }); + + il.if_expr(cond_expr, &mut t, &mut f).append(); + + if new_true { + il.mark_label(&mut t); + il.jump(il.const_ptr(tt)).append(); + } + + if new_false { + il.mark_label(&mut f); + } + } + Op::Beqc(a) | Op::Bnec(a) => { + let rs1 = Register::from(a.rs1()); + let cimm = a.cimm(); + + let cond_expr = match op { + Op::Beqc(..) => il.cmp_e(max_width, rs1, cimm), + Op::Bnec(..) => il.cmp_ne(max_width, rs1, cimm), + _ => unreachable!(), + }; + + let mut new_false = false; + let mut new_true = false; + + let ft = addr.wrapping_add(inst_len); + let tt = addr.wrapping_add(a.imm() as i64 as u64); + + let mut f = il.label_for_address(ft).unwrap_or_else(|| { + new_false = true; + LowLevelILLabel::new() + }); + + let mut t = il.label_for_address(tt).unwrap_or_else(|| { + new_true = true; + LowLevelILLabel::new() + }); + + il.if_expr(cond_expr, &mut t, &mut f).append(); + + if new_true { + il.mark_label(&mut t); + il.jump(il.const_ptr(tt)).append(); + } + + if new_false { + il.mark_label(&mut f); + } + } _ => il.unimplemented().append(), }; From 1f3698db7acea816db87d8bfb4629e0cb9ec15bd Mon Sep 17 00:00:00 2001 From: KonPet Date: Wed, 16 Sep 2026 03:57:19 +0200 Subject: [PATCH 2/7] Add Risc-V XAndersPerf decoding for BFOS, BFOZ and the LEA variants --- arch/riscv/disasm/src/lib.rs | 213 +++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/arch/riscv/disasm/src/lib.rs b/arch/riscv/disasm/src/lib.rs index d530566f7d..e8b29939d1 100644 --- a/arch/riscv/disasm/src/lib.rs +++ b/arch/riscv/disasm/src/lib.rs @@ -246,6 +246,13 @@ pub enum Op { Bbs(NdsBranchBit), Beqc(NdsBranchConst), Bnec(NdsBranchConst), + + // BIT FIELD OPERATIONS + Bfos(NdsBitfieldInst), + Bfoz(NdsBitfieldInst), + + // LOAD EFFECTIVE ADDRESS + Lea(NdsLeaInst), } pub trait Register { @@ -1855,6 +1862,127 @@ impl NdsBranchConst { } } +#[derive(Copy, Clone, Debug)] +pub struct NdsBitfieldInst { + rs1: IntReg, + rd: IntReg, + msb: u8, + lsb: u8, +} + +impl NdsBitfieldInst { + #[inline(always)] + fn from_instr32(instr: Instr32) -> DisResult { + let rs1 = IntReg::new(instr.rs1()); + let rd = IntReg::new(instr.rd()); + + let is_rv64 = ::Int::width() == 8; + + let msb_5 = instr.extract_bits(31, 1); + if !is_rv64 && msb_5 != 0 { + return Err(Error::InvalidSubop); + } + + let msb = instr.extract_bits(26, 5) | (msb_5 << 5); + + let lsb_5 = instr.extract_bits(25, 1); + if !is_rv64 && lsb_5 != 0 { + return Err(Error::InvalidSubop); + } + + let lsb = instr.extract_bits(20, 5) | (lsb_5 << 5); + + Ok(Self { + rs1: rs1, + rd: rd, + msb: msb as u8, + lsb: lsb as u8, + }) + } + + #[inline(always)] + pub fn rs1(&self) -> IntReg { + self.rs1 + } + + #[inline(always)] + pub fn rd(&self) -> IntReg { + self.rd + } + + #[inline(always)] + pub fn msb(&self) -> u8 { + self.msb + } + + #[inline(always)] + pub fn lsb(&self) -> u8 { + self.lsb + } +} + +#[derive(Copy, Clone, Debug)] +pub struct NdsLeaInst { + rs1: IntReg, + rs2: IntReg, + rd: IntReg, + width: u8, + zero_extend: bool, +} + +impl NdsLeaInst { + #[inline(always)] + fn from_ops( + rs1: IntReg, + rs2: IntReg, + rd: IntReg, + width: u8, + zero_extend: bool, + ) -> DisResult { + if width == 1 && !zero_extend { + return Err(Error::InvalidSubop); + } + + let is_rv64 = ::Int::width() == 8; + if !is_rv64 && (zero_extend || width == 1) { + return Err(Error::InvalidSubop); + } + + Ok(Self { + rs1: rs1, + rs2: rs2, + rd: rd, + width: width, + zero_extend: zero_extend, + }) + } + + #[inline(always)] + pub fn rs1(&self) -> IntReg { + self.rs1 + } + + #[inline(always)] + pub fn rs2(&self) -> IntReg { + self.rs2 + } + + #[inline(always)] + pub fn rd(&self) -> IntReg { + self.rd + } + + #[inline(always)] + pub fn width(&self) -> u8 { + self.width + } + + #[inline(always)] + pub fn zero_extend(&self) -> bool { + self.zero_extend + } +} + #[derive(Copy, Clone, Debug)] pub struct Instr16(u16); impl Instr16 { @@ -2230,6 +2358,17 @@ impl Instr { ops.push(Operand::I(a.cimm() as i32)); ops.push(Operand::I(a.imm() as i32)); } + Op::Bfos(ref a) | Op::Bfoz(ref a) => { + ops.push(Operand::R(a.rd())); + ops.push(Operand::R(a.rs1())); + ops.push(Operand::I(a.msb() as i32)); + ops.push(Operand::I(a.lsb() as i32)); + } + Op::Lea(ref a) => { + ops.push(Operand::R(a.rs1())); + ops.push(Operand::R(a.rs2())); + ops.push(Operand::R(a.rd())); + } }, } @@ -2414,6 +2553,11 @@ impl<'a, D: RiscVDisassembler + 'a> Mnem<'a, D> { Op::Bbs(..) => "nds.bbs", Op::Beqc(..) => "nds.beqc", Op::Bnec(..) => "nds.bnec", + + Op::Bfos(..) => "nds.bfos", + Op::Bfoz(..) => "nds.bfoz", + + Op::Lea(..) => "nds.lea", }, } } @@ -2595,6 +2739,20 @@ impl<'a, D: RiscVDisassembler + 'a> Mnem<'a, D> { Some(suf.into()) } + Op::Lea(ref a) => { + let width_suf = match a.width() { + 1 => ".b", + 2 => ".h", + 4 => ".w", + 8 => ".d", + _ => unreachable!(), + }; + + let full_suf = + String::from(width_suf) + if a.zero_extend() { ".ze" } else { "" }; + + Some(full_suf.into()) + } _ => None, }, } @@ -3695,6 +3853,61 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn } 0b101 => Op::Beqc(NdsBranchConst::from_instr32(inst)?), 0b110 => Op::Bnec(NdsBranchConst::from_instr32(inst)?), + 0b011 => Op::Bfos(NdsBitfieldInst::from_instr32(inst)?), + 0b010 => Op::Bfoz(NdsBitfieldInst::from_instr32(inst)?), + + 0b000 => match inst.funct7() { + 0b0000101 => Op::Lea(NdsLeaInst::from_ops( + IntReg::new(inst.rs1()), + IntReg::new(inst.rs2()), + IntReg::new(inst.rd()), + 2, + false, + )?), + 0b0000110 => Op::Lea(NdsLeaInst::from_ops( + IntReg::new(inst.rs1()), + IntReg::new(inst.rs2()), + IntReg::new(inst.rd()), + 4, + false, + )?), + 0b0000111 => Op::Lea(NdsLeaInst::from_ops( + IntReg::new(inst.rs1()), + IntReg::new(inst.rs2()), + IntReg::new(inst.rd()), + 8, + false, + )?), + 0b0001000 => Op::Lea(NdsLeaInst::from_ops( + IntReg::new(inst.rs1()), + IntReg::new(inst.rs2()), + IntReg::new(inst.rd()), + 1, + true, + )?), + 0b0001001 => Op::Lea(NdsLeaInst::from_ops( + IntReg::new(inst.rs1()), + IntReg::new(inst.rs2()), + IntReg::new(inst.rd()), + 2, + true, + )?), + 0b0001010 => Op::Lea(NdsLeaInst::from_ops( + IntReg::new(inst.rs1()), + IntReg::new(inst.rs2()), + IntReg::new(inst.rd()), + 4, + true, + )?), + 0b0001011 => Op::Lea(NdsLeaInst::from_ops( + IntReg::new(inst.rs1()), + IntReg::new(inst.rs2()), + IntReg::new(inst.rd()), + 8, + true, + )?), + _ => return Err(InvalidSubop), + }, _ => return Err(InvalidSubop), }, // TODO CUSTOM_2 From 179e546a4f9a50f6e92ef8275ec508ce6c272285 Mon Sep 17 00:00:00 2001 From: KonPet Date: Wed, 16 Sep 2026 03:59:53 +0200 Subject: [PATCH 3/7] Clean up Risc-V's BEQC and BNEC --- arch/riscv/disasm/src/lib.rs | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/arch/riscv/disasm/src/lib.rs b/arch/riscv/disasm/src/lib.rs index e8b29939d1..af412b50de 100644 --- a/arch/riscv/disasm/src/lib.rs +++ b/arch/riscv/disasm/src/lib.rs @@ -1779,6 +1779,8 @@ pub struct NdsBranchBit { impl NdsBranchBit { #[inline(always)] fn from_instr32(instr: Instr32) -> DisResult { + let is_rv64 = ::Int::width() == 8; + let rs1 = IntReg::new(instr.rs1()); // Propagate sign, clear out bits 0 through 9, then insert the missing bits @@ -1786,13 +1788,12 @@ impl NdsBranchBit { | (instr.extract_bits(25, 5) << 5) as i32 | (instr.extract_bits(8, 4) << 1) as i32; - // cimm has an additional bit in RV64 - let cimm = instr.extract_bits(20, 5) - | if ::Int::width() == 8 { - instr.extract_bits(7, 1) << 5 - } else { - 0 - }; + let cimm_5 = instr.extract_bits(7, 1); + if !is_rv64 && cimm_5 != 0 { + return Err(Error::InvalidSubop); + } + + let cimm = instr.extract_bits(20, 5) | cimm_5 << 5; Ok(Self { rs1: rs1, @@ -2338,22 +2339,12 @@ impl Instr { ops.push(Operand::R(f.rd())); ops.push(Operand::F(f.rs1())); } - Op::Bbc(ref a) => { - ops.push(Operand::R(a.rs1())); - ops.push(Operand::I(a.cimm() as i32)); - ops.push(Operand::I(a.imm() as i32)); - } - Op::Bbs(ref a) => { - ops.push(Operand::R(a.rs1())); - ops.push(Operand::I(a.cimm() as i32)); - ops.push(Operand::I(a.imm() as i32)); - } - Op::Beqc(ref a) => { + Op::Bbc(ref a) | Op::Bbs(ref a) => { ops.push(Operand::R(a.rs1())); ops.push(Operand::I(a.cimm() as i32)); ops.push(Operand::I(a.imm() as i32)); } - Op::Bnec(ref a) => { + Op::Beqc(ref a) | Op::Bnec(ref a) => { ops.push(Operand::R(a.rs1())); ops.push(Operand::I(a.cimm() as i32)); ops.push(Operand::I(a.imm() as i32)); From 8b9ee6ff2c70708de46a82212b7d8977c2937888 Mon Sep 17 00:00:00 2001 From: KonPet Date: Wed, 16 Sep 2026 16:54:06 +0200 Subject: [PATCH 4/7] Lift Risc-V XandesPerf BFOS, BFOZ and the LEA variants --- arch/riscv/src/lib.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index 19d18a0c15..02745ce263 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -2325,6 +2325,45 @@ impl Architecture for RiscVArch { il.mark_label(&mut f); } } + Op::Bfos(a) | Op::Bfoz(a) => { + let bytes = ::Int::width(); + let bits = 8 * bytes as u8; + + let rs1 = Register::from(a.rs1()); + let rd = Register::from(a.rd()); + + let (left_shift, right_shift) = if a.msb() == 0 { + (bits - 1, bits - 1 - a.lsb()) + } else if a.msb() < a.lsb() { + (bits - 1 - (a.lsb() - a.msb()), bits - 1 - a.lsb()) + } else { + (bits - 1 - a.msb(), bits - 1 - (a.msb() - a.lsb())) + }; + + let shifted = il.lsl(bytes, rs1, left_shift); + let res = match op { + Op::Bfos(..) => il.asr(bytes, shifted, right_shift), + Op::Bfoz(..) => il.lsr(bytes, shifted, right_shift), + _ => unreachable!(), + }; + il.set_reg(bytes, rd, res).append(); + } + Op::Lea(a) => { + let bytes = ::Int::width(); + + let rs1 = Register::from(a.rs1()); + let rs2 = Register::from(a.rs2()); + let rd = Register::from(a.rd()); + + let offset = if a.zero_extend() { + il.mul(bytes, il.zx(8, il.low_part(4, rs2)), a.width()) + } else { + il.mul(bytes, rs2, a.width()) + }; + + il.set_reg(bytes, rd, il.add_overflow(bytes, rs1, offset)) + .append(); + } _ => il.unimplemented().append(), }; From b6bdd1f5d1912d55577f751105b8cb4bd78d5572 Mon Sep 17 00:00:00 2001 From: KonPet Date: Fri, 18 Sep 2026 00:57:36 +0200 Subject: [PATCH 5/7] Add Risc-V XAndersPerf decoding for GP relative instructions --- arch/riscv/disasm/src/lib.rs | 233 ++++++++++++++++++++++++++++++++++- 1 file changed, 231 insertions(+), 2 deletions(-) diff --git a/arch/riscv/disasm/src/lib.rs b/arch/riscv/disasm/src/lib.rs index af412b50de..9fd842220b 100644 --- a/arch/riscv/disasm/src/lib.rs +++ b/arch/riscv/disasm/src/lib.rs @@ -253,6 +253,20 @@ pub enum Op { // LOAD EFFECTIVE ADDRESS Lea(NdsLeaInst), + + // GP-RELATIVE INSTRUCTIONS + Addigp(NdsGPRelativeAddInst), + Lbgp(NdsGPRelativeLoadInst), + Lbugp(NdsGPRelativeLoadInst), + Lhgp(NdsGPRelativeLoadInst), + Lhugp(NdsGPRelativeLoadInst), + Lwgp(NdsGPRelativeLoadInst), + Lwugp(NdsGPRelativeLoadInst), + Ldgp(NdsGPRelativeLoadInst), + Sbgp(NdsGPRelativeStoreInst), + Shgp(NdsGPRelativeStoreInst), + Swgp(NdsGPRelativeStoreInst), + Sdgp(NdsGPRelativeStoreInst), } pub trait Register { @@ -1984,6 +1998,75 @@ impl NdsLeaInst { } } +#[derive(Copy, Clone, Debug)] +pub struct NdsGPRelativeAddInst { + rd: IntReg, + imm: i32, +} + +impl NdsGPRelativeAddInst { + #[inline(always)] + fn from_ops(rd: IntReg, imm: i32) -> DisResult { + Ok(Self { rd: rd, imm: imm }) + } + + #[inline(always)] + pub fn rd(&self) -> IntReg { + self.rd + } + + #[inline(always)] + pub fn imm(&self) -> i32 { + self.imm + } +} + +#[derive(Copy, Clone, Debug)] +pub struct NdsGPRelativeLoadInst { + rd: IntReg, + imm: i32, +} + +impl NdsGPRelativeLoadInst { + #[inline(always)] + fn from_ops(rd: IntReg, imm: i32) -> DisResult { + Ok(Self { rd: rd, imm: imm }) + } + + #[inline(always)] + pub fn rd(&self) -> IntReg { + self.rd + } + + #[inline(always)] + pub fn imm(&self) -> i32 { + self.imm + } +} + +#[derive(Copy, Clone, Debug)] +pub struct NdsGPRelativeStoreInst { + rs2: IntReg, + imm: i32, +} + +impl NdsGPRelativeStoreInst { + #[inline(always)] + fn from_ops(rs2: IntReg, imm: i32) -> DisResult { + Ok(Self { rs2: rs2, imm: imm }) + } + + #[inline(always)] + pub fn rs2(&self) -> IntReg { + self.rs2 + } + + #[inline(always)] + pub fn imm(&self) -> i32 { + self.imm + } +} + #[derive(Copy, Clone, Debug)] pub struct Instr16(u16); impl Instr16 { @@ -2360,6 +2443,24 @@ impl Instr { ops.push(Operand::R(a.rs2())); ops.push(Operand::R(a.rd())); } + Op::Addigp(ref a) => { + ops.push(Operand::R(a.rd())); + ops.push(Operand::M(a.imm() as i32, IntReg::new(3))); + } + Op::Lbgp(ref a) + | Op::Lbugp(ref a) + | Op::Lhgp(ref a) + | Op::Lhugp(ref a) + | Op::Lwgp(ref a) + | Op::Lwugp(ref a) + | Op::Ldgp(ref a) => { + ops.push(Operand::R(a.rd())); + ops.push(Operand::M(a.imm() as i32, IntReg::new(3))); + } + Op::Sbgp(ref a) | Op::Shgp(ref a) | Op::Swgp(ref a) | Op::Sdgp(ref a) => { + ops.push(Operand::R(a.rs2())); + ops.push(Operand::M(a.imm() as i32, IntReg::new(3))); + } }, } @@ -2549,6 +2650,19 @@ impl<'a, D: RiscVDisassembler + 'a> Mnem<'a, D> { Op::Bfoz(..) => "nds.bfoz", Op::Lea(..) => "nds.lea", + + Op::Addigp(..) => "nds.addigp", + Op::Lbgp(..) => "nds.lbgp", + Op::Lbugp(..) => "nds.lbugp", + Op::Lhgp(..) => "nds.lhgp", + Op::Lhugp(..) => "nds.lhugp", + Op::Lwgp(..) => "nds.lwgp", + Op::Lwugp(..) => "nds.lwugp", + Op::Ldgp(..) => "nds.ldgp", + Op::Sbgp(..) => "nds.sbgp", + Op::Shgp(..) => "nds.shgp", + Op::Swgp(..) => "nds.swgp", + Op::Sdgp(..) => "nds.sdgp", }, } } @@ -3313,7 +3427,37 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn Op::LoadFp(FpMemInst::new(width, fr, rs1, imm)?) } - // TODO CUSTOM_0 + 0b00010 => match inst.funct3() & 0b11 { + 0b00..0b11 => { + let rd = IntReg::new(inst.rd()); + let imm = ((inst.0 & (1 << 31)) as i32 >> (31 - 17)) + | (inst.extract_bits(15, 2) << 15) as i32 + | (inst.extract_bits(17, 3) << 12) as i32 + | (inst.extract_bits(20, 1) << 11) as i32 + | (inst.extract_bits(21, 10) << 1) as i32 + | inst.extract_bits(14, 1) as i32; + match inst.funct3() & 0b11 { + 0b01 => Op::Addigp(NdsGPRelativeAddInst::from_ops(rd, imm)?), + 0b00 => Op::Lbgp(NdsGPRelativeLoadInst::from_ops(rd, imm)?), + 0b10 => Op::Lbugp(NdsGPRelativeLoadInst::from_ops(rd, imm)?), + + _ => unreachable!(), + } + } + 0b11 => { + let rs2 = IntReg::new(inst.rs2()); + let imm = ((inst.0 & (1 << 31)) as i32 >> (31 - 17)) + | (inst.extract_bits(15, 2) << 15) as i32 + | (inst.extract_bits(17, 3) << 12) as i32 + | (inst.extract_bits(7, 1) << 11) as i32 + | (inst.extract_bits(25, 6) << 5) as i32 + | (inst.extract_bits(8, 4) << 1) as i32 + | inst.extract_bits(14, 1) as i32; + + Op::Sbgp(NdsGPRelativeStoreInst::from_ops(rs2, imm)?) + } + _ => unreachable!(), + }, 0b00011 => { // MISC-MEM if Self::WCHExtension::supported() @@ -3483,7 +3627,92 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn Op::StoreFp(FpMemInst::new(width, fr, rs1, imm)?) } - // TODO CUSTOM_1 + 0b01010 => match inst.funct3() { + 0b001 | 0b101 => { + let rd = IntReg::new(inst.rd()); + let imm = ((inst.0 & (1 << 31)) as i32 >> (31 - 17)) + | (inst.extract_bits(15, 2) << 15) as i32 + | (inst.extract_bits(17, 3) << 12) as i32 + | (inst.extract_bits(20, 1) << 11) as i32 + | (inst.extract_bits(21, 10) << 1) as i32; + + match inst.funct3() { + 0b001 => Op::Lhgp(NdsGPRelativeLoadInst::from_ops(rd, imm)?), + 0b101 => Op::Lhugp(NdsGPRelativeLoadInst::from_ops(rd, imm)?), + _ => unreachable!(), + } + } + 0b010 | 0b110 => { + let rd = IntReg::new(inst.rd()); + let imm = ((inst.0 & (1 << 31)) as i32 >> (31 - 18)) + | (inst.extract_bits(21, 1) << 17) as i32 + | (inst.extract_bits(15, 2) << 15) as i32 + | (inst.extract_bits(17, 3) << 12) as i32 + | (inst.extract_bits(20, 1) << 11) as i32 + | (inst.extract_bits(22, 9) << 2) as i32; + + match inst.funct3() { + 0b010 => Op::Lwgp(NdsGPRelativeLoadInst::from_ops(rd, imm)?), + 0b110 if int_width == 8 => { + Op::Lwugp(NdsGPRelativeLoadInst::from_ops(rd, imm)?) + } + _ => return Err(InvalidSubop), + } + } + 0b011 if int_width == 8 => { + let rd = IntReg::new(inst.rd()); + let imm = ((inst.0 & (1 << 31)) as i32 >> (31 - 19)) + | (inst.extract_bits(21, 2) << 17) as i32 + | (inst.extract_bits(15, 2) << 15) as i32 + | (inst.extract_bits(17, 3) << 12) as i32 + | (inst.extract_bits(20, 1) << 11) as i32 + | (inst.extract_bits(23, 8) << 3) as i32; + + Op::Ldgp(NdsGPRelativeLoadInst::from_ops(rd, imm)?) + } + 0b000 => { + let imm = ((inst.0 & (1 << 31)) as i32 >> (31 - 17)) + | (inst.extract_bits(15, 2) << 15) as i32 + | (inst.extract_bits(17, 3) << 12) as i32 + | (inst.extract_bits(7, 1) << 11) as i32 + | (inst.extract_bits(25, 6) << 5) as i32 + | (inst.extract_bits(8, 4) << 1) as i32; + + Op::Shgp(NdsGPRelativeStoreInst::from_ops( + IntReg::new(inst.rs2()), + imm, + )?) + } + 0b100 => { + let imm = ((inst.0 & (1 << 31)) as i32 >> (31 - 18)) + | (inst.extract_bits(8, 1) << 17) as i32 + | (inst.extract_bits(15, 2) << 15) as i32 + | (inst.extract_bits(17, 3) << 12) as i32 + | (inst.extract_bits(7, 1) << 11) as i32 + | (inst.extract_bits(25, 6) << 5) as i32 + | (inst.extract_bits(9, 3) << 2) as i32; + + Op::Swgp(NdsGPRelativeStoreInst::from_ops( + IntReg::new(inst.rs2()), + imm, + )?) + } + 0b111 if int_width == 8 => { + let imm = ((inst.0 & (1 << 31)) as i32 >> (31 - 19)) + | (inst.extract_bits(8, 2) << 17) as i32 + | (inst.extract_bits(15, 2) << 15) as i32 + | (inst.extract_bits(17, 3) << 12) as i32 + | (inst.extract_bits(7, 1) << 11) as i32 + | (inst.extract_bits(25, 6) << 5) as i32 + | (inst.extract_bits(10, 2) << 3) as i32; + + Op::Sdgp(NdsGPRelativeStoreInst::from_ops( + IntReg::new(inst.rs2()), + imm, + )?) + } + _ => return Err(InvalidSubop), + }, 0b01011 if Self::AtomicExtension::supported() => { // AMO let atomic = AtomicInst::new(inst)?; From 8445dc20db6cc3450f1fad6fef0df6e7b68f5922 Mon Sep 17 00:00:00 2001 From: KonPet Date: Fri, 18 Sep 2026 01:47:42 +0200 Subject: [PATCH 6/7] Lift Risc-V XandersPerf GP relative instructions --- arch/riscv/src/lib.rs | 88 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index 02745ce263..da6f02a247 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -2364,6 +2364,94 @@ impl Architecture for RiscVArch { il.set_reg(bytes, rd, il.add_overflow(bytes, rs1, offset)) .append(); } + Op::Addigp(a) => { + let rd = Register::from(a.rd()); + let imm = a.imm(); + + let bytes = ::Int::width(); + let address = il.add(max_width, Register::::new(3.into()), imm); + + il.set_reg( + bytes, + rd, + if max_width > bytes { + il.low_part(bytes, address) + } else { + address + } + .with_source_operand(1), + ) + .append(); + } + Op::Lbgp(a) + | Op::Lbugp(a) + | Op::Lhgp(a) + | Op::Lhugp(a) + | Op::Lwgp(a) + | Op::Lwugp(a) + | Op::Ldgp(a) => { + let rd = Register::from(a.rd()); + let imm = a.imm(); + + let bytes = ::Int::width(); + let load_bytes = match op { + Op::Lbgp(..) | Op::Lbugp(..) => 1, + Op::Lhgp(..) | Op::Lhugp(..) => 2, + Op::Lwgp(..) | Op::Lwugp(..) => 4, + Op::Ldgp(..) => 8, + _ => unreachable!(), + }; + + let val = il.load( + load_bytes, + il.add(max_width, Register::::new(3.into()), imm) + .with_source_operand(1), + ); + + il.set_reg( + bytes, + rd, + if bytes > load_bytes { + match op { + Op::Lbgp(..) | Op::Lhgp(..) | Op::Lwgp(..) | Op::Ldgp(..) => { + il.sx(bytes, val) + } + Op::Lbugp(..) | Op::Lhugp(..) | Op::Lwugp(..) => il.zx(bytes, val), + _ => unreachable!(), + } + } else { + val + }, + ) + .append(); + } + Op::Sbgp(a) | Op::Shgp(a) | Op::Swgp(a) | Op::Sdgp(a) => { + let rs2 = Register::from(a.rs2()); + let imm = a.imm(); + + let bytes = ::Int::width(); + + let dest_bytes = match op { + Op::Sbgp(..) => 1, + Op::Shgp(..) => 2, + Op::Swgp(..) => 4, + Op::Sdgp(..) => 8, + _ => unreachable!(), + }; + + let val = if dest_bytes < bytes { + il.low_part(dest_bytes, rs2).build() + } else { + il.expression(rs2) + } + .with_source_operand(0); + + let dest_addr = il + .add(max_width, Register::::new(3.into()), imm) + .with_source_operand(1); + + il.store(dest_bytes, dest_addr, val).append(); + } _ => il.unimplemented().append(), }; From 6fb9e554995868ab60f815247e058129add3023e Mon Sep 17 00:00:00 2001 From: KonPet Date: Fri, 18 Sep 2026 17:34:36 +0200 Subject: [PATCH 7/7] Add intrinsics for Risc-V XAndersPerf String Processing instructions --- arch/riscv/disasm/src/lib.rs | 20 +++++++++++ arch/riscv/src/lib.rs | 68 ++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/arch/riscv/disasm/src/lib.rs b/arch/riscv/disasm/src/lib.rs index 9fd842220b..1fc7dce2cd 100644 --- a/arch/riscv/disasm/src/lib.rs +++ b/arch/riscv/disasm/src/lib.rs @@ -267,6 +267,12 @@ pub enum Op { Shgp(NdsGPRelativeStoreInst), Swgp(NdsGPRelativeStoreInst), Sdgp(NdsGPRelativeStoreInst), + + // STRING PROCESSING + Ffb(RTypeIntInst), + Ffzmism(RTypeIntInst), + Ffmism(RTypeIntInst), + Flmism(RTypeIntInst), } pub trait Register { @@ -2461,6 +2467,11 @@ impl Instr { ops.push(Operand::R(a.rs2())); ops.push(Operand::M(a.imm() as i32, IntReg::new(3))); } + Op::Ffb(ref a) | Op::Ffzmism(ref a) | Op::Ffmism(ref a) | Op::Flmism(ref a) => { + ops.push(Operand::R(a.rd())); + ops.push(Operand::R(a.rs1())); + ops.push(Operand::R(a.rs2())); + } }, } @@ -2663,6 +2674,11 @@ impl<'a, D: RiscVDisassembler + 'a> Mnem<'a, D> { Op::Shgp(..) => "nds.shgp", Op::Swgp(..) => "nds.swgp", Op::Sdgp(..) => "nds.sdgp", + + Op::Ffb(..) => "nds.ffb", + Op::Ffzmism(..) => "nds.ffzmism", + Op::Ffmism(..) => "nds.ffmism", + Op::Flmism(..) => "nds.flmism", }, } } @@ -4126,6 +4142,10 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn 8, true, )?), + 0b0010000 => Op::Ffb(RTypeIntInst::new(inst)?), + 0b0010001 => Op::Ffzmism(RTypeIntInst::new(inst)?), + 0b0010010 => Op::Ffmism(RTypeIntInst::new(inst)?), + 0b0010011 => Op::Flmism(RTypeIntInst::new(inst)?), _ => return Err(InvalidSubop), }, _ => return Err(InvalidSubop), diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index da6f02a247..62681802df 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -90,6 +90,11 @@ enum Intrinsic { OrCombine, Rev8, WchMcpy, + + Ffb, + Ffzmism, + Ffmism, + Flmism, } #[derive(Copy, Clone)] @@ -353,6 +358,10 @@ impl RiscVIntrinsic { Some((29, _, _, _)) => Some(Intrinsic::OrCombine.into()), Some((30, _, _, _)) => Some(Intrinsic::Rev8.into()), Some((31, _, _, _)) => Some(Intrinsic::WchMcpy.into()), + Some((32, _, _, _)) => Some(Intrinsic::Ffb.into()), + Some((33, _, _, _)) => Some(Intrinsic::Ffzmism.into()), + Some((34, _, _, _)) => Some(Intrinsic::Ffmism.into()), + Some((35, _, _, _)) => Some(Intrinsic::Flmism.into()), _ => None, } } @@ -493,6 +502,10 @@ impl architecture::Intrinsic for RiscVIntrinsic { Intrinsic::OrCombine => "_orc_b".into(), Intrinsic::Rev8 => "_rev8".into(), Intrinsic::WchMcpy => "_wch_mcpy".into(), + Intrinsic::Ffb => "_nds_ffb".into(), + Intrinsic::Ffzmism => "_nds_ffzmism".into(), + Intrinsic::Ffmism => "_nds_ffmism".into(), + Intrinsic::Flmism => "_nds_flmism".into(), } } @@ -540,6 +553,10 @@ impl architecture::Intrinsic for RiscVIntrinsic { Intrinsic::OrCombine => Self::id_from_parts(29, None, None, None), Intrinsic::Rev8 => Self::id_from_parts(30, None, None, None), Intrinsic::WchMcpy => Self::id_from_parts(31, None, None, None), + Intrinsic::Ffb => Self::id_from_parts(32, None, None, None), + Intrinsic::Ffzmism => Self::id_from_parts(33, None, None, None), + Intrinsic::Ffmism => Self::id_from_parts(34, None, None, None), + Intrinsic::Flmism => Self::id_from_parts(35, None, None, None), } } @@ -646,6 +663,36 @@ impl architecture::Intrinsic for RiscVIntrinsic { ), ] } + Intrinsic::Ffb => { + vec![ + NameAndType::new( + "", + Conf::new( + Type::int(::Int::width(), false), + MIN_CONFIDENCE, + ), + ), + NameAndType::new("", Conf::new(Type::int(1, false), MAX_CONFIDENCE)), + ] + } + Intrinsic::Ffzmism | Intrinsic::Ffmism | Intrinsic::Flmism => { + vec![ + NameAndType::new( + "", + Conf::new( + Type::int(::Int::width(), false), + MIN_CONFIDENCE, + ), + ), + NameAndType::new( + "", + Conf::new( + Type::int(::Int::width(), false), + MIN_CONFIDENCE, + ), + ), + ] + } } } @@ -700,6 +747,12 @@ impl architecture::Intrinsic for RiscVIntrinsic { MIN_CONFIDENCE, )] } + Intrinsic::Ffb | Intrinsic::Ffzmism | Intrinsic::Ffmism | Intrinsic::Flmism => { + vec![Conf::new( + Type::int(::Int::width(), true), + MIN_CONFIDENCE, + )] + } } } } @@ -2452,6 +2505,21 @@ impl Architecture for RiscVArch { il.store(dest_bytes, dest_addr, val).append(); } + Op::Ffb(a) | Op::Ffzmism(a) | Op::Ffmism(a) | Op::Flmism(a) => { + let rs1 = Register::from(a.rs1()); + let rs2 = Register::from(a.rs2()); + let rd = Register::from(a.rd()); + + let intrinsic = match op { + Op::Ffb(..) => RiscVIntrinsic::::from(Intrinsic::Ffb), + Op::Ffzmism(..) => RiscVIntrinsic::::from(Intrinsic::Ffzmism), + Op::Ffmism(..) => RiscVIntrinsic::::from(Intrinsic::Ffmism), + Op::Flmism(..) => RiscVIntrinsic::::from(Intrinsic::Flmism), + _ => unreachable!(), + }; + + il.intrinsic([rd], intrinsic, [rs1, rs2]).append(); + } _ => il.unimplemented().append(), };