From 5e82e4e23ad7fd1f6c8d6b50000faaadd8051a21 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 26 Dec 2025 22:15:56 +0100 Subject: [PATCH 1/3] harm: refactor `tbz`/`tbnz` + Use type aliases. + Generalize on offsets. + Make tests more readable. --- harm/src/instructions/control/testbranch.rs | 62 ++++++++++----------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/harm/src/instructions/control/testbranch.rs b/harm/src/instructions/control/testbranch.rs index e79b539b..7bc8e3c1 100644 --- a/harm/src/instructions/control/testbranch.rs +++ b/harm/src/instructions/control/testbranch.rs @@ -15,23 +15,28 @@ use crate::{ sealed::Sealed, }; -pub struct TestBranch { +pub type TestBit64 = UBitValue<6>; +pub type TestBit32 = UBitValue<5>; +pub type TestOffset = SBitValue<14, 2>; + +pub struct TestBranch { op: bool, reg: Reg, bit: Bit, - offset: SBitValue<14, 2>, + offset: Offset, } -impl Sealed for TestBranch {} +impl Sealed for TestBranch {} +impl Sealed for TestBranch {} -pub trait MakeTestBranch: Sealed { - fn new(op: bool, reg: Reg, bit: Bit, offset: SBitValue<14, 2>) -> Self; +pub trait MakeTestBranch: Sealed { + fn new(op: bool, reg: Reg, bit: Bit, offset: Offset) -> Self; } -impl> MakeTestBranch> - for TestBranch> +impl> MakeTestBranch + for TestBranch, TestOffset> { - fn new(op: bool, reg: R, bit: UBitValue<6>, offset: SBitValue<14, 2>) -> Self { + fn new(op: bool, reg: R, bit: UBitValue<6>, offset: TestOffset) -> Self { Self { op, reg: reg.into_reg(), @@ -41,10 +46,10 @@ impl> MakeTestBranch> } } -impl> MakeTestBranch> - for TestBranch> +impl> MakeTestBranch + for TestBranch { - fn new(op: bool, reg: R, bit: UBitValue<5>, offset: SBitValue<14, 2>) -> Self { + fn new(op: bool, reg: R, bit: TestBit32, offset: TestOffset) -> Self { Self { op, reg: reg.into(), @@ -54,7 +59,7 @@ impl> MakeTestBranch> } } -impl RawInstruction for TestBranch> { +impl RawInstruction for TestBranch { #[inline] fn to_code(&self) -> InstructionCode { let bit = self.bit.bits(); @@ -64,17 +69,12 @@ impl RawInstruction for TestBranch> { if self.op { TBNZ_only_testbranch(b5.into(), b40.into(), self.offset.into(), self.reg.index()) } else { - TBZ_only_testbranch( - (bit >> 5).into(), - (bit & 0b11111).into(), - self.offset.into(), - self.reg.index(), - ) + TBZ_only_testbranch(b5.into(), b40.into(), self.offset.into(), self.reg.index()) } } } -impl RawInstruction for TestBranch> { +impl RawInstruction for TestBranch { #[inline] fn to_code(&self) -> InstructionCode { let bit = self.bit.bits(); @@ -89,20 +89,20 @@ impl RawInstruction for TestBranch> { } } -pub fn tbnz( +pub fn tbnz( reg: InpReg, bit: Bit, - offset: SBitValue<14, 2>, -) -> TestBranch + offset: Offset, +) -> TestBranch where - TestBranch: MakeTestBranch, + TestBranch: MakeTestBranch, { TestBranch::new(true, reg, bit, offset) } -pub fn tbz(reg: Reg, bit: Bit, offset: SBitValue<14, 2>) -> TestBranch +pub fn tbz(reg: Reg, bit: Bit, offset: Offset) -> TestBranch where - TestBranch: MakeTestBranch, + TestBranch: MakeTestBranch, { TestBranch::new(false, reg, bit, offset) } @@ -127,7 +127,7 @@ mod tests { let it = tbz(X2, bit, offset); let words: Vec<_> = it.encode().collect(); // tbz x2, 42, 76 - assert_eq!(words, inst!([0x62, 0x02, 0x50, 0xb6])); // 0xb6500262 + assert_eq!(words, inst!(0xb6500262)); } #[test] @@ -137,7 +137,7 @@ mod tests { let it = tbz(X2, bit, offset); let words: Vec<_> = it.encode().collect(); - assert_eq!(words, inst!([0x62, 0x02, 0xe8, 0x36])); // 0x36e80262 + assert_eq!(words, inst!(0x36e80262)); } #[test] @@ -146,7 +146,7 @@ mod tests { let bit = UBitValue::new(42).unwrap(); let it = tbz(XZR, bit, offset); let words: Vec<_> = it.encode().collect(); - assert_eq!(words, inst!([0x7f, 0x02, 0x50, 0xb6])); // 0xb650027f + assert_eq!(words, inst!(0xb650027f)); } #[test] @@ -155,7 +155,7 @@ mod tests { let bit = UBitValue::new(29).unwrap(); let it = tbz(XZR, bit, offset); let words: Vec<_> = it.encode().collect(); - assert_eq!(words, inst!([0x7f, 0x02, 0xe8, 0x36])); // 0x36e8027f + assert_eq!(words, inst!(0x36e8027f)); } #[test] @@ -164,7 +164,7 @@ mod tests { let bit = UBitValue::new(29).unwrap(); let it = tbz(W2, bit, offset); let words: Vec<_> = it.encode().collect(); - assert_eq!(words, inst!([0x62, 0x02, 0xe8, 0x36])); // 0x36e80262 + assert_eq!(words, inst!(0x36e80262)); } #[test] @@ -173,6 +173,6 @@ mod tests { let bit = UBitValue::new(29).unwrap(); let it = tbz(WZR, bit, offset); let words: Vec<_> = it.encode().collect(); - assert_eq!(words, inst!([0x7f, 0x02, 0xe8, 0x36])); // 0x36e8027f + assert_eq!(words, inst!(0x36e8027f)); } } From 58de0d30b015d65263af74ae2fd8273363d8e5de Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sat, 27 Dec 2025 15:42:03 +0100 Subject: [PATCH 2/3] Fixes and improvements --- harm/src/instructions/control/testbranch.rs | 50 ++++++++++----------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/harm/src/instructions/control/testbranch.rs b/harm/src/instructions/control/testbranch.rs index 7bc8e3c1..356ffd8d 100644 --- a/harm/src/instructions/control/testbranch.rs +++ b/harm/src/instructions/control/testbranch.rs @@ -15,9 +15,9 @@ use crate::{ sealed::Sealed, }; -pub type TestBit64 = UBitValue<6>; -pub type TestBit32 = UBitValue<5>; -pub type TestOffset = SBitValue<14, 2>; +pub type TestBranchBit64 = UBitValue<6>; +pub type TestBranchBit32 = UBitValue<5>; +pub type TestBranchOffset = SBitValue<14, 2>; pub struct TestBranch { op: bool, @@ -26,17 +26,17 @@ pub struct TestBranch { offset: Offset, } -impl Sealed for TestBranch {} -impl Sealed for TestBranch {} +impl Sealed for TestBranch {} +impl Sealed for TestBranch {} pub trait MakeTestBranch: Sealed { fn new(op: bool, reg: Reg, bit: Bit, offset: Offset) -> Self; } -impl> MakeTestBranch - for TestBranch, TestOffset> +impl> MakeTestBranch + for TestBranch { - fn new(op: bool, reg: R, bit: UBitValue<6>, offset: TestOffset) -> Self { + fn new(op: bool, reg: R, bit: TestBranchBit64, offset: TestBranchOffset) -> Self { Self { op, reg: reg.into_reg(), @@ -46,10 +46,10 @@ impl> MakeTestBranch } } -impl> MakeTestBranch - for TestBranch +impl> MakeTestBranch + for TestBranch { - fn new(op: bool, reg: R, bit: TestBit32, offset: TestOffset) -> Self { + fn new(op: bool, reg: R, bit: TestBranchBit32, offset: TestBranchOffset) -> Self { Self { op, reg: reg.into(), @@ -59,7 +59,7 @@ impl> MakeTestBranch } } -impl RawInstruction for TestBranch { +impl RawInstruction for TestBranch { #[inline] fn to_code(&self) -> InstructionCode { let bit = self.bit.bits(); @@ -74,7 +74,7 @@ impl RawInstruction for TestBranch { } } -impl RawInstruction for TestBranch { +impl RawInstruction for TestBranch { #[inline] fn to_code(&self) -> InstructionCode { let bit = self.bit.bits(); @@ -122,8 +122,8 @@ mod tests { #[test] fn test_tbz_64_big_pos() { - let offset = SBitValue::new(76).unwrap(); - let bit = UBitValue::new(42).unwrap(); + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit64::new(42).unwrap(); let it = tbz(X2, bit, offset); let words: Vec<_> = it.encode().collect(); // tbz x2, 42, 76 @@ -132,8 +132,8 @@ mod tests { #[test] fn test_tbz_64_small_pos() { - let offset = SBitValue::new(76).unwrap(); - let bit = UBitValue::new(29).unwrap(); + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit64::new(29).unwrap(); let it = tbz(X2, bit, offset); let words: Vec<_> = it.encode().collect(); @@ -142,8 +142,8 @@ mod tests { #[test] fn test_tbz_xzr_big_pos() { - let offset = SBitValue::new(76).unwrap(); - let bit = UBitValue::new(42).unwrap(); + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit64::new(42).unwrap(); let it = tbz(XZR, bit, offset); let words: Vec<_> = it.encode().collect(); assert_eq!(words, inst!(0xb650027f)); @@ -151,8 +151,8 @@ mod tests { #[test] fn test_tbz_xzr_small_pos() { - let offset = SBitValue::new(76).unwrap(); - let bit = UBitValue::new(29).unwrap(); + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit64::new(29).unwrap(); let it = tbz(XZR, bit, offset); let words: Vec<_> = it.encode().collect(); assert_eq!(words, inst!(0x36e8027f)); @@ -160,8 +160,8 @@ mod tests { #[test] fn test_tbz_32_pos() { - let offset = SBitValue::new(76).unwrap(); - let bit = UBitValue::new(29).unwrap(); + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit32::new(29).unwrap(); let it = tbz(W2, bit, offset); let words: Vec<_> = it.encode().collect(); assert_eq!(words, inst!(0x36e80262)); @@ -169,8 +169,8 @@ mod tests { #[test] fn test_tbz_wzr_pos() { - let offset = SBitValue::new(76).unwrap(); - let bit = UBitValue::new(29).unwrap(); + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit32::new(29).unwrap(); let it = tbz(WZR, bit, offset); let words: Vec<_> = it.encode().collect(); assert_eq!(words, inst!(0x36e8027f)); From a82ac801db5ea5c2d2ca2b234bad9178bbc5cbf4 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sat, 27 Dec 2025 15:52:45 +0100 Subject: [PATCH 3/3] Extend tests --- harm/src/instructions/control/testbranch.rs | 80 ++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/harm/src/instructions/control/testbranch.rs b/harm/src/instructions/control/testbranch.rs index 356ffd8d..4d51c41b 100644 --- a/harm/src/instructions/control/testbranch.rs +++ b/harm/src/instructions/control/testbranch.rs @@ -46,13 +46,13 @@ impl> MakeTestBranch> MakeTestBranch +impl> MakeTestBranch for TestBranch { fn new(op: bool, reg: R, bit: TestBranchBit32, offset: TestBranchOffset) -> Self { Self { op, - reg: reg.into(), + reg: reg.into_reg(), bit, offset, } @@ -149,6 +149,16 @@ mod tests { assert_eq!(words, inst!(0xb650027f)); } + #[test] + fn test_tbz_64_neg_pos() { + let offset = TestBranchOffset::new(-76).unwrap(); + let bit = TestBranchBit64::new(29).unwrap(); + let it = tbz(X2, bit, offset); + let words: Vec<_> = it.encode().collect(); + + assert_eq!(words, inst!(0x36effda2)); + } + #[test] fn test_tbz_xzr_small_pos() { let offset = TestBranchOffset::new(76).unwrap(); @@ -175,4 +185,70 @@ mod tests { let words: Vec<_> = it.encode().collect(); assert_eq!(words, inst!(0x36e8027f)); } + + #[test] + fn test_tbnz_64_big_pos() { + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit64::new(42).unwrap(); + let it = tbnz(X2, bit, offset); + let words: Vec<_> = it.encode().collect(); + // tbnz x2, 42, 76 + assert_eq!(words, inst!(0xb7500262)); + } + + #[test] + fn test_tbnz_64_small_pos() { + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit64::new(29).unwrap(); + let it = tbnz(X2, bit, offset); + let words: Vec<_> = it.encode().collect(); + + assert_eq!(words, inst!(0x37e80262)); + } + + #[test] + fn test_tbnz_xzr_big_pos() { + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit64::new(42).unwrap(); + let it = tbnz(XZR, bit, offset); + let words: Vec<_> = it.encode().collect(); + assert_eq!(words, inst!(0xb750027f)); + } + + #[test] + fn test_tbnz_64_neg_pos() { + let offset = TestBranchOffset::new(-76).unwrap(); + let bit = TestBranchBit64::new(29).unwrap(); + let it = tbnz(X2, bit, offset); + let words: Vec<_> = it.encode().collect(); + + assert_eq!(words, inst!(0x37effda2)); + } + + #[test] + fn test_tbnz_xzr_small_pos() { + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit64::new(29).unwrap(); + let it = tbnz(XZR, bit, offset); + let words: Vec<_> = it.encode().collect(); + assert_eq!(words, inst!(0x37e8027f)); + } + + #[test] + fn test_tbnz_32_pos() { + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit32::new(29).unwrap(); + let it = tbnz(W2, bit, offset); + let words: Vec<_> = it.encode().collect(); + assert_eq!(words, inst!(0x37e80262)); + } + + #[test] + fn test_tbnz_wzr_pos() { + let offset = TestBranchOffset::new(76).unwrap(); + let bit = TestBranchBit32::new(29).unwrap(); + let it = tbnz(WZR, bit, offset); + let words: Vec<_> = it.encode().collect(); + assert_eq!(words, inst!(0x37e8027f)); + } }