improved implementation of checked arithmetic

This commit is contained in:
Folkert 2023-09-17 15:43:11 +02:00
parent 54732b5750
commit c6593725df
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
3 changed files with 272 additions and 16 deletions

View file

@ -1676,6 +1676,47 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
cset_reg64_cond(buf, dst, ConditionCode::VS)
}
#[inline(always)]
fn add_with_overflow(
buf: &mut Vec<'_, u8>,
_register_width: RegisterWidth,
dst: AArch64GeneralReg,
src1: AArch64GeneralReg,
src2: AArch64GeneralReg,
overflow: AArch64GeneralReg,
) {
adds_reg64_reg64_reg64(buf, dst, src1, src2);
Self::set_if_overflow(buf, overflow)
}
#[inline(always)]
fn sub_with_overflow(
buf: &mut Vec<'_, u8>,
_register_width: RegisterWidth,
dst: AArch64GeneralReg,
src1: AArch64GeneralReg,
src2: AArch64GeneralReg,
overflow: AArch64GeneralReg,
) {
subs_reg64_reg64_reg64(buf, dst, src1, src2);
Self::set_if_overflow(buf, overflow)
}
#[inline(always)]
fn imul_with_overflow(
buf: &mut Vec<'_, u8>,
_register_width: RegisterWidth,
dst: AArch64GeneralReg,
src1: AArch64GeneralReg,
src2: AArch64GeneralReg,
overflow: AArch64GeneralReg,
) {
smulh_reg64_reg64_reg64(buf, overflow, src1, src2);
mul_reg64_reg64_reg64(buf, dst, src1, src2);
subs_reg64_reg64_reg64(buf, overflow, overflow, dst); // arithmetic shift right 63
cset_reg64_cond(buf, overflow, ConditionCode::NE);
}
#[inline(always)]
fn ret(buf: &mut Vec<'_, u8>) {
ret_reg64(buf, AArch64GeneralReg::LR)
@ -2005,18 +2046,18 @@ impl ArithmeticShifted {
debug_assert!(imm6 <= 0b111111);
Self {
reg_d: rd.id().into(),
reg_n: rn.id().into(),
imm6: imm6.into(),
reg_m: rm.id().into(),
fixed2: false,
shift: shift.id().into(),
fixed: 0b01011.into(),
s,
op,
// true for 64 bit addition
// false for 32 bit addition
sf: true,
fixed: 0b01011.into(),
op,
s,
fixed2: false,
reg_m: rm.id().into(),
shift: shift.id().into(),
imm6: imm6.into(),
reg_d: rd.id().into(),
reg_n: rn.id().into(),
}
}
}
@ -2937,6 +2978,32 @@ fn add_reg64_reg64_reg64(
buf.extend(inst.bytes());
}
/// add and set flags
#[inline(always)]
fn adds_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>,
dst: AArch64GeneralReg,
src1: AArch64GeneralReg,
src2: AArch64GeneralReg,
) {
let inst = ArithmeticShifted {
// true for 64 bit addition
// false for 32 bit addition
sf: true,
op: false,
s: true,
fixed: 0b01011.into(),
shift: 0b00.into(),
fixed2: false,
reg_m: src2.id().into(),
imm6: 0b00_0000.into(),
reg_d: dst.id().into(),
reg_n: src1.id().into(),
};
buf.extend(inst.bytes());
}
/// `AND Xd, Xn, Xm` -> Bitwise AND Xn and Xm and place the result into Xd.
#[inline(always)]
fn and_reg64_reg64_reg64(
@ -3327,6 +3394,46 @@ fn mul_reg64_reg64_reg64(
madd_reg64_reg64_reg64_reg64(buf, dst, src1, src2, AArch64GeneralReg::ZRSP);
}
#[inline(always)]
fn smulh_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>,
dst: AArch64GeneralReg,
src1: AArch64GeneralReg,
src2: AArch64GeneralReg,
) {
#[derive(PackedStruct)]
#[packed_struct(endian = "msb")]
pub struct Inst {
fixed: Integer<u8, packed_bits::Bits<1>>,
fixed1: Integer<u8, packed_bits::Bits<2>>,
fixed2: Integer<u8, packed_bits::Bits<5>>,
u: Integer<u8, packed_bits::Bits<1>>,
fixed3: Integer<u8, packed_bits::Bits<2>>,
rm: Integer<u8, packed_bits::Bits<5>>,
o0: bool,
ra: Integer<u8, packed_bits::Bits<5>>,
rn: Integer<u8, packed_bits::Bits<5>>,
rd: Integer<u8, packed_bits::Bits<5>>,
}
impl Aarch64Bytes for Inst {}
let inst = Inst {
fixed: 0b1.into(),
fixed1: 0b00.into(),
fixed2: 0b11011.into(),
u: 0b0.into(),
fixed3: 0b10.into(),
rm: src2.id().into(),
o0: false,
ra: 0b11111.into(),
rn: src1.id().into(),
rd: dst.id().into(),
};
buf.extend(inst.bytes());
}
/// `NEG Xd, Xm` -> Negate Xm and place the result into Xd.
#[inline(always)]
fn neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, src: AArch64GeneralReg) {
@ -3535,12 +3642,24 @@ fn subs_reg64_reg64_reg64(
dst: AArch64GeneralReg,
src1: AArch64GeneralReg,
src2: AArch64GeneralReg,
) {
subs_reg64_reg64_reg64_with_shift(buf, dst, src1, src2, (ShiftType::LSL, 0))
}
/// `SUBS Xd, Xn, Xm` -> Subtract Xn and Xm and place the result into Xd. Set condition flags.
#[inline(always)]
fn subs_reg64_reg64_reg64_with_shift(
buf: &mut Vec<'_, u8>,
dst: AArch64GeneralReg,
src1: AArch64GeneralReg,
src2: AArch64GeneralReg,
(shift, amount): (ShiftType, u8),
) {
let inst = ArithmeticShifted::new(ArithmeticShiftedParams {
op: true,
s: true,
shift: ShiftType::LSL,
imm6: 0,
shift,
imm6: amount,
rm: src2,
rn: src1,
rd: dst,
@ -3956,7 +4075,7 @@ fn scvtf_freg_reg64(
#[cfg(test)]
mod tests {
use super::*;
use crate::disassembler_test;
use crate::{disassembler_test, generic64::aarch64::subs_reg64_reg64_reg64_with_shift};
use capstone::prelude::*;
enum ZRSPKind {
@ -4581,6 +4700,22 @@ mod tests {
);
}
#[test]
fn test_smulh_reg64_reg64_reg64() {
disassembler_test!(
smulh_reg64_reg64_reg64,
|reg1: AArch64GeneralReg, reg2: AArch64GeneralReg, reg3: AArch64GeneralReg| format!(
"smulh {}, {}, {}",
reg1.capstone_string(UsesZR),
reg2.capstone_string(UsesZR),
reg3.capstone_string(UsesZR)
),
ALL_GENERAL_REGS,
ALL_GENERAL_REGS,
ALL_GENERAL_REGS
);
}
#[test]
fn test_neg_reg64_reg64() {
disassembler_test!(
@ -4771,6 +4906,24 @@ mod tests {
[0x123]
);
}
#[test]
fn test_subs_reg64_reg64_reg64_with_shift() {
disassembler_test!(
subs_reg64_reg64_reg64_with_shift,
|reg1: AArch64GeneralReg, reg2: AArch64GeneralReg, reg3: AArch64GeneralReg, _| {
format!(
"subs {}, {}, {}, asr #63",
reg1.capstone_string(UsesZR),
reg2.capstone_string(UsesZR),
reg3.capstone_string(UsesZR)
)
},
[AArch64GeneralReg::X0, AArch64GeneralReg::X1],
[AArch64GeneralReg::X0, AArch64GeneralReg::X1],
[AArch64GeneralReg::X0, AArch64GeneralReg::X1],
[(ShiftType::ASR, 63)]
);
}
#[test]
fn test_subs_reg64_reg64_reg64() {
@ -4808,6 +4961,34 @@ mod tests {
);
}
#[test]
fn test_adds_reg64_reg64_reg64() {
disassembler_test!(
adds_reg64_reg64_reg64,
|reg1: AArch64GeneralReg, reg2: AArch64GeneralReg, reg3: AArch64GeneralReg| {
if reg1 == AArch64GeneralReg::ZRSP {
// When the first register is SP, it gets disassembled as cmp,
// which is an alias for subs.
format!(
"cmn {}, {}",
reg2.capstone_string(UsesZR),
reg3.capstone_string(UsesZR)
)
} else {
format!(
"adds {}, {}, {}",
reg1.capstone_string(UsesZR),
reg2.capstone_string(UsesZR),
reg3.capstone_string(UsesZR)
)
}
},
ALL_GENERAL_REGS,
ALL_GENERAL_REGS,
ALL_GENERAL_REGS
);
}
#[test]
fn test_ret_reg64() {
disassembler_test!(

View file

@ -717,6 +717,33 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
fn set_if_overflow(buf: &mut Vec<'_, u8>, dst: GeneralReg);
fn add_with_overflow(
buf: &mut Vec<'_, u8>,
register_width: RegisterWidth,
dst: GeneralReg,
src1: GeneralReg,
src2: GeneralReg,
overflow: GeneralReg,
);
fn sub_with_overflow(
buf: &mut Vec<'_, u8>,
register_width: RegisterWidth,
dst: GeneralReg,
src1: GeneralReg,
src2: GeneralReg,
overflow: GeneralReg,
);
fn imul_with_overflow(
buf: &mut Vec<'_, u8>,
register_width: RegisterWidth,
dst: GeneralReg,
src1: GeneralReg,
src2: GeneralReg,
overflow: GeneralReg,
);
fn ret(buf: &mut Vec<'_, u8>);
}
@ -1402,8 +1429,14 @@ impl<
let src1_reg = self.storage_manager.load_to_general_reg(buf, src1);
let src2_reg = self.storage_manager.load_to_general_reg(buf, src2);
ASM::add_reg64_reg64_reg64(buf, dst_reg, src1_reg, src2_reg);
ASM::set_if_overflow(buf, overflow_reg);
ASM::add_with_overflow(
buf,
RegisterWidth::W64, // TODO
dst_reg,
src1_reg,
src2_reg,
overflow_reg,
);
ASM::mov_base32_reg64(buf, base_offset, dst_reg);
ASM::mov_base32_reg64(buf, base_offset + 8, overflow_reg);
@ -1614,8 +1647,14 @@ impl<
let src1_reg = self.storage_manager.load_to_general_reg(buf, src1);
let src2_reg = self.storage_manager.load_to_general_reg(buf, src2);
ASM::imul_reg64_reg64_reg64(buf, dst_reg, src1_reg, src2_reg);
ASM::set_if_overflow(buf, overflow_reg);
ASM::imul_with_overflow(
buf,
RegisterWidth::W64, // TODO
dst_reg,
src1_reg,
src2_reg,
overflow_reg,
);
ASM::mov_base32_reg64(buf, base_offset, dst_reg);
ASM::mov_base32_reg64(buf, base_offset + 8, overflow_reg);

View file

@ -2616,6 +2616,42 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
seto_reg64(buf, dst);
}
fn add_with_overflow(
buf: &mut Vec<'_, u8>,
_register_width: RegisterWidth,
dst: X86_64GeneralReg,
src1: X86_64GeneralReg,
src2: X86_64GeneralReg,
overflow: X86_64GeneralReg,
) {
Self::add_reg64_reg64_reg64(buf, dst, src1, src2);
Self::set_if_overflow(buf, overflow);
}
fn sub_with_overflow(
buf: &mut Vec<'_, u8>,
_register_width: RegisterWidth,
dst: X86_64GeneralReg,
src1: X86_64GeneralReg,
src2: X86_64GeneralReg,
overflow: X86_64GeneralReg,
) {
Self::sub_reg64_reg64_reg64(buf, dst, src1, src2);
Self::set_if_overflow(buf, overflow);
}
fn imul_with_overflow(
buf: &mut Vec<'_, u8>,
_register_width: RegisterWidth,
dst: X86_64GeneralReg,
src1: X86_64GeneralReg,
src2: X86_64GeneralReg,
overflow: X86_64GeneralReg,
) {
Self::imul_reg64_reg64_reg64(buf, dst, src1, src2);
Self::set_if_overflow(buf, overflow);
}
fn and_reg64_reg64_reg64(buf: &mut Vec<'_, u8>, dst: Reg64, src1: Reg64, src2: Reg64) {
binop_move_src_to_dst_reg64(buf, and_reg64_reg64, dst, src1, src2)
}