mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
TBZ and TBNZ for AArch64 (https://github.com/Shopify/ruby/pull/434)
This commit is contained in:
parent
c2e9253893
commit
29e0713a12
Notes:
git
2022-08-30 01:10:08 +09:00
3 changed files with 171 additions and 0 deletions
|
@ -22,6 +22,7 @@ mod reg_pair;
|
|||
mod sbfm;
|
||||
mod shift_imm;
|
||||
mod sys_reg;
|
||||
mod test_bit;
|
||||
|
||||
pub use atomic::Atomic;
|
||||
pub use branch::Branch;
|
||||
|
@ -44,3 +45,4 @@ pub use reg_pair::RegisterPair;
|
|||
pub use sbfm::SBFM;
|
||||
pub use shift_imm::ShiftImm;
|
||||
pub use sys_reg::SysReg;
|
||||
pub use test_bit::TestBit;
|
||||
|
|
135
yjit/src/asm/arm64/inst/test_bit.rs
Normal file
135
yjit/src/asm/arm64/inst/test_bit.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
/// The upper bit of the bit number to test.
|
||||
#[derive(Debug)]
|
||||
enum B5 {
|
||||
/// When the bit number is below 32.
|
||||
B532 = 0,
|
||||
|
||||
/// When the bit number is equal to or above 32.
|
||||
B564 = 1
|
||||
}
|
||||
|
||||
/// A convenience function so that we can convert the bit number directly into a
|
||||
/// B5 variant.
|
||||
impl From<u8> for B5 {
|
||||
fn from(bit_num: u8) -> Self {
|
||||
match bit_num {
|
||||
0..=31 => B5::B532,
|
||||
32..=63 => B5::B564,
|
||||
_ => panic!("Invalid bit number: {}", bit_num)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The operation to perform for this instruction.
|
||||
enum Op {
|
||||
/// The test bit zero operation.
|
||||
TBZ = 0,
|
||||
|
||||
/// The test bit not zero operation.
|
||||
TBNZ = 1
|
||||
}
|
||||
|
||||
/// The struct that represents an A64 test bit instruction that can be encoded.
|
||||
///
|
||||
/// TBNZ/TBZ
|
||||
/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
|
||||
/// | 31 30 29 28 | 27 26 25 24 | 23 22 21 20 | 19 18 17 16 | 15 14 13 12 | 11 10 09 08 | 07 06 05 04 | 03 02 01 00 |
|
||||
/// | 0 1 1 0 1 1 |
|
||||
/// | b5 op b40............. imm14.......................................... rt.............. |
|
||||
/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
|
||||
///
|
||||
pub struct TestBit {
|
||||
/// The number of the register to test.
|
||||
rt: u8,
|
||||
|
||||
/// The PC-relative offset to the target instruction in term of number of
|
||||
/// instructions.
|
||||
imm14: i16,
|
||||
|
||||
/// The lower 5 bits of the bit number to be tested.
|
||||
b40: u8,
|
||||
|
||||
/// The operation to perform for this instruction.
|
||||
op: Op,
|
||||
|
||||
/// The upper bit of the bit number to test.
|
||||
b5: B5
|
||||
}
|
||||
|
||||
impl TestBit {
|
||||
/// TBNZ
|
||||
/// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/TBNZ--Test-bit-and-Branch-if-Nonzero-?lang=en
|
||||
pub fn tbnz(rt: u8, bit_num: u8, offset: i16) -> Self {
|
||||
Self { rt, imm14: offset, b40: bit_num & 0b11111, op: Op::TBNZ, b5: bit_num.into() }
|
||||
}
|
||||
|
||||
/// TBZ
|
||||
/// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/TBZ--Test-bit-and-Branch-if-Zero-?lang=en
|
||||
pub fn tbz(rt: u8, bit_num: u8, offset: i16) -> Self {
|
||||
Self { rt, imm14: offset, b40: bit_num & 0b11111, op: Op::TBZ, b5: bit_num.into() }
|
||||
}
|
||||
}
|
||||
|
||||
/// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Branches--Exception-Generating-and-System-instructions?lang=en
|
||||
const FAMILY: u32 = 0b11011;
|
||||
|
||||
impl From<TestBit> for u32 {
|
||||
/// Convert an instruction into a 32-bit value.
|
||||
fn from(inst: TestBit) -> Self {
|
||||
let b40 = (inst.b40 & 0b11111) as u32;
|
||||
let mut imm14 = (inst.imm14 & ((1 << 13) - 1)) as u32;
|
||||
|
||||
if inst.imm14 < 0 {
|
||||
imm14 |= (1 << 13);
|
||||
}
|
||||
|
||||
0
|
||||
| ((inst.b5 as u32) << 31)
|
||||
| (FAMILY << 25)
|
||||
| ((inst.op as u32) << 24)
|
||||
| (b40 << 19)
|
||||
| (imm14 << 5)
|
||||
| inst.rt as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestBit> for [u8; 4] {
|
||||
/// Convert an instruction into a 4 byte array.
|
||||
fn from(inst: TestBit) -> [u8; 4] {
|
||||
let result: u32 = inst.into();
|
||||
result.to_le_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_tbnz() {
|
||||
let inst = TestBit::tbnz(0, 0, 0);
|
||||
let result: u32 = inst.into();
|
||||
assert_eq!(0x37000000, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tbnz_negative() {
|
||||
let inst = TestBit::tbnz(0, 0, -1);
|
||||
let result: u32 = inst.into();
|
||||
assert_eq!(0x3707ffe0, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tbz() {
|
||||
let inst = TestBit::tbz(0, 0, 0);
|
||||
let result: u32 = inst.into();
|
||||
assert_eq!(0x36000000, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tbz_negative() {
|
||||
let inst = TestBit::tbz(0, 0, -1);
|
||||
let result: u32 = inst.into();
|
||||
assert_eq!(0x3607ffe0, result);
|
||||
}
|
||||
}
|
|
@ -934,6 +934,30 @@ pub fn ret(cb: &mut CodeBlock, rn: A64Opnd) {
|
|||
cb.write_bytes(&bytes);
|
||||
}
|
||||
|
||||
/// TBNZ - test bit and branch if not zero
|
||||
pub fn tbnz(cb: &mut CodeBlock, rt: A64Opnd, bit_num: A64Opnd, offset: A64Opnd) {
|
||||
let bytes: [u8; 4] = match (rt, bit_num, offset) {
|
||||
(A64Opnd::Reg(rt), A64Opnd::UImm(bit_num), A64Opnd::Imm(offset)) => {
|
||||
TestBit::tbnz(rt.reg_no, bit_num.try_into().unwrap(), offset.try_into().unwrap()).into()
|
||||
},
|
||||
_ => panic!("Invalid operand combination to tbnz instruction.")
|
||||
};
|
||||
|
||||
cb.write_bytes(&bytes);
|
||||
}
|
||||
|
||||
/// TBZ - test bit and branch if zero
|
||||
pub fn tbz(cb: &mut CodeBlock, rt: A64Opnd, bit_num: A64Opnd, offset: A64Opnd) {
|
||||
let bytes: [u8; 4] = match (rt, bit_num, offset) {
|
||||
(A64Opnd::Reg(rt), A64Opnd::UImm(bit_num), A64Opnd::Imm(offset)) => {
|
||||
TestBit::tbz(rt.reg_no, bit_num.try_into().unwrap(), offset.try_into().unwrap()).into()
|
||||
},
|
||||
_ => panic!("Invalid operand combination to tbz instruction.")
|
||||
};
|
||||
|
||||
cb.write_bytes(&bytes);
|
||||
}
|
||||
|
||||
/// TST - test the bits of a register against a mask, then update flags
|
||||
pub fn tst(cb: &mut CodeBlock, rn: A64Opnd, rm: A64Opnd) {
|
||||
let bytes: [u8; 4] = match (rn, rm) {
|
||||
|
@ -1393,6 +1417,16 @@ mod tests {
|
|||
check_bytes("6a7d4093", |cb| sxtw(cb, X10, W11));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tbnz() {
|
||||
check_bytes("4a005037", |cb| tbnz(cb, X10, A64Opnd::UImm(10), A64Opnd::Imm(2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tbz() {
|
||||
check_bytes("4a005036", |cb| tbz(cb, X10, A64Opnd::UImm(10), A64Opnd::Imm(2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tst_register() {
|
||||
check_bytes("1f0001ea", |cb| tst(cb, X0, X1));
|
||||
|
|
Loading…
Reference in a new issue