diff options
author | Kevin Newton <kddnewton@gmail.com> | 2022-07-19 17:44:39 -0400 |
---|---|---|
committer | Takashi Kokubun <takashikkbn@gmail.com> | 2022-08-29 08:47:02 -0700 |
commit | 4ae2c744ac6b5b84f2bfebb9046c0c364863d7a4 (patch) | |
tree | d301009779db91e28a7189b441fbe02a05e6b9a8 | |
parent | c9484fe0c1f2897521d08780a66fab89e5e2f5b1 (diff) |
A lot of fixes coming from our pairing session (https://github.com/Shopify/ruby/pull/329)
* Move to/from SP on AArch64
* Consolidate loads and stores
* Implement LDR post-index and LDR pre-index for AArch64
* Implement STR post-index and STR pre-index for AArch64
* Module entrypoints for LDR pre/post -index and STR pre/post -index
* Use STR (pre-index) and LDR (post-index) to implement push/pop
* Go back to using MOV for to/from SP
-rw-r--r-- | yjit/src/asm/arm64/inst/load.rs | 124 | ||||
-rw-r--r-- | yjit/src/asm/arm64/inst/load_store.rs | 215 | ||||
-rw-r--r-- | yjit/src/asm/arm64/inst/mod.rs | 6 | ||||
-rw-r--r-- | yjit/src/asm/arm64/inst/store.rs | 105 | ||||
-rw-r--r-- | yjit/src/asm/arm64/mod.rs | 108 | ||||
-rw-r--r-- | yjit/src/backend/arm64/mod.rs | 12 |
6 files changed, 326 insertions, 244 deletions
diff --git a/yjit/src/asm/arm64/inst/load.rs b/yjit/src/asm/arm64/inst/load.rs deleted file mode 100644 index b64a6a96ac..0000000000 --- a/yjit/src/asm/arm64/inst/load.rs +++ /dev/null @@ -1,124 +0,0 @@ -/// The size of the operands being operated on. -enum Size { - Size32 = 0b10, - Size64 = 0b11, -} - -/// The operation to perform for this instruction. -enum Opc { - LDUR = 0b01, - LDURSW = 0b10 -} - -/// A convenience function so that we can convert the number of bits of an -/// register operand directly into an Sf enum variant. -impl From<u8> for Size { - fn from(num_bits: u8) -> Self { - match num_bits { - 64 => Size::Size64, - 32 => Size::Size32, - _ => panic!("Invalid number of bits: {}", num_bits) - } - } -} - -/// The struct that represents an A64 data processing -- immediate instruction -/// that can be encoded. -/// -/// LDUR -/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -/// | 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 | -/// | 1 1 1 0 0 0 0 0 0 | -/// | size. opc.. imm9.......................... rn.............. rt.............. | -/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -/// -pub struct Load { - /// The number of the register to load the value into. - rt: u8, - - /// The base register with which to form the address. - rn: u8, - - /// The optional signed immediate byte offset from the base register. - imm9: i16, - - /// The operation to perform for this instruction. - opc: Opc, - - /// The size of the operands being operated on. - size: Size -} - -impl Load { - /// LDUR (load register, unscaled) - /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDUR--Load-Register--unscaled--?lang=en - pub fn ldur(rt: u8, rn: u8, imm9: i16, num_bits: u8) -> Self { - Self { rt, rn, imm9, opc: Opc::LDUR, size: num_bits.into() } - } - - /// LDURSW (load register, unscaled, signed) - /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDURSW--Load-Register-Signed-Word--unscaled--?lang=en - pub fn ldursw(rt: u8, rn: u8, imm9: i16) -> Self { - Self { rt, rn, imm9, opc: Opc::LDURSW, size: Size::Size32 } - } -} - -/// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Loads-and-Stores?lang=en -const FAMILY: u32 = 0b0100; - -impl From<Load> for u32 { - /// Convert an instruction into a 32-bit value. - fn from(inst: Load) -> Self { - let imm9 = (inst.imm9 as u32) & ((1 << 9) - 1); - - 0 - | ((inst.size as u32) << 30) - | (0b11 << 28) - | (FAMILY << 25) - | ((inst.opc as u32) << 22) - | (imm9 << 12) - | ((inst.rn as u32) << 5) - | (inst.rt as u32) - } -} - -impl From<Load> for [u8; 4] { - /// Convert an instruction into a 4 byte array. - fn from(inst: Load) -> [u8; 4] { - let result: u32 = inst.into(); - result.to_le_bytes() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_ldur() { - let inst = Load::ldur(0, 1, 0, 64); - let result: u32 = inst.into(); - assert_eq!(0xf8400020, result); - } - - #[test] - fn test_ldur_with_imm() { - let inst = Load::ldur(0, 1, 123, 64); - let result: u32 = inst.into(); - assert_eq!(0xf847b020, result); - } - - #[test] - fn test_ldursw() { - let inst = Load::ldursw(0, 1, 0); - let result: u32 = inst.into(); - assert_eq!(0xb8800020, result); - } - - #[test] - fn test_ldursw_with_imm() { - let inst = Load::ldursw(0, 1, 123); - let result: u32 = inst.into(); - assert_eq!(0xb887b020, result); - } -} diff --git a/yjit/src/asm/arm64/inst/load_store.rs b/yjit/src/asm/arm64/inst/load_store.rs new file mode 100644 index 0000000000..80a67c837e --- /dev/null +++ b/yjit/src/asm/arm64/inst/load_store.rs @@ -0,0 +1,215 @@ +/// The size of the operands being operated on. +enum Size { + Size32 = 0b10, + Size64 = 0b11, +} + +/// A convenience function so that we can convert the number of bits of an +/// register operand directly into an Sf enum variant. +impl From<u8> for Size { + fn from(num_bits: u8) -> Self { + match num_bits { + 64 => Size::Size64, + 32 => Size::Size32, + _ => panic!("Invalid number of bits: {}", num_bits) + } + } +} + +/// The operation to perform for this instruction. +enum Opc { + STR = 0b00, + LDR = 0b01, + LDURSW = 0b10 +} + +/// What kind of indexing to perform for this instruction. +enum Index { + None = 0b00, + PostIndex = 0b01, + PreIndex = 0b11 +} + +/// The struct that represents an A64 load or store instruction that can be +/// encoded. +/// +/// LDR/LDUR/LDURSW/STR/STUR +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// | 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 | +/// | 1 1 1 0 0 0 0 | +/// | size. opc.. imm9.......................... idx.. rn.............. rt.............. | +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// +pub struct LoadStore { + /// The number of the register to load the value into. + rt: u8, + + /// The base register with which to form the address. + rn: u8, + + /// What kind of indexing to perform for this instruction. + idx: Index, + + /// The optional signed immediate byte offset from the base register. + imm9: i16, + + /// The operation to perform for this instruction. + opc: Opc, + + /// The size of the operands being operated on. + size: Size +} + +impl LoadStore { + /// LDR (immediate, post-index) + /// https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/LDR--immediate---Load-Register--immediate-- + pub fn ldr_post(rt: u8, rn: u8, imm9: i16, num_bits: u8) -> Self { + Self { rt, rn, idx: Index::PostIndex, imm9, opc: Opc::LDR, size: num_bits.into() } + } + + /// LDR (immediate, pre-index) + /// https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/LDR--immediate---Load-Register--immediate-- + pub fn ldr_pre(rt: u8, rn: u8, imm9: i16, num_bits: u8) -> Self { + Self { rt, rn, idx: Index::PreIndex, imm9, opc: Opc::LDR, size: num_bits.into() } + } + + /// LDUR (load register, unscaled) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDUR--Load-Register--unscaled--?lang=en + pub fn ldur(rt: u8, rn: u8, imm9: i16, num_bits: u8) -> Self { + Self { rt, rn, idx: Index::None, imm9, opc: Opc::LDR, size: num_bits.into() } + } + + /// LDURSW (load register, unscaled, signed) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDURSW--Load-Register-Signed-Word--unscaled--?lang=en + pub fn ldursw(rt: u8, rn: u8, imm9: i16) -> Self { + Self { rt, rn, idx: Index::None, imm9, opc: Opc::LDURSW, size: Size::Size32 } + } + + /// STR (immediate, post-index) + /// https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/STR--immediate---Store-Register--immediate-- + pub fn str_post(rt: u8, rn: u8, imm9: i16, num_bits: u8) -> Self { + Self { rt, rn, idx: Index::PostIndex, imm9, opc: Opc::STR, size: num_bits.into() } + } + + /// STR (immediate, pre-index) + /// https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/STR--immediate---Store-Register--immediate-- + pub fn str_pre(rt: u8, rn: u8, imm9: i16, num_bits: u8) -> Self { + Self { rt, rn, idx: Index::PreIndex, imm9, opc: Opc::STR, size: num_bits.into() } + } + + /// STUR (store register, unscaled) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STUR--Store-Register--unscaled--?lang=en + pub fn stur(rt: u8, rn: u8, imm9: i16, num_bits: u8) -> Self { + Self { rt, rn, idx: Index::None, imm9, opc: Opc::STR, size: num_bits.into() } + } +} + +/// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Loads-and-Stores?lang=en +const FAMILY: u32 = 0b0100; + +impl From<LoadStore> for u32 { + /// Convert an instruction into a 32-bit value. + fn from(inst: LoadStore) -> Self { + let imm9 = (inst.imm9 as u32) & ((1 << 9) - 1); + + 0 + | ((inst.size as u32) << 30) + | (0b11 << 28) + | (FAMILY << 25) + | ((inst.opc as u32) << 22) + | (imm9 << 12) + | ((inst.idx as u32) << 10) + | ((inst.rn as u32) << 5) + | (inst.rt as u32) + } +} + +impl From<LoadStore> for [u8; 4] { + /// Convert an instruction into a 4 byte array. + fn from(inst: LoadStore) -> [u8; 4] { + let result: u32 = inst.into(); + result.to_le_bytes() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ldr_post() { + let inst = LoadStore::ldr_post(0, 1, 16, 64); + let result: u32 = inst.into(); + assert_eq!(0xf8410420, result); + } + + #[test] + fn test_ldr_pre() { + let inst = LoadStore::ldr_pre(0, 1, 16, 64); + let result: u32 = inst.into(); + assert_eq!(0xf8410c20, result); + } + + #[test] + fn test_ldur() { + let inst = LoadStore::ldur(0, 1, 0, 64); + let result: u32 = inst.into(); + assert_eq!(0xf8400020, result); + } + + #[test] + fn test_ldur_with_imm() { + let inst = LoadStore::ldur(0, 1, 123, 64); + let result: u32 = inst.into(); + assert_eq!(0xf847b020, result); + } + + #[test] + fn test_ldursw() { + let inst = LoadStore::ldursw(0, 1, 0); + let result: u32 = inst.into(); + assert_eq!(0xb8800020, result); + } + + #[test] + fn test_ldursw_with_imm() { + let inst = LoadStore::ldursw(0, 1, 123); + let result: u32 = inst.into(); + assert_eq!(0xb887b020, result); + } + + #[test] + fn test_str_post() { + let inst = LoadStore::str_post(0, 1, -16, 64); + let result: u32 = inst.into(); + assert_eq!(0xf81f0420, result); + } + + #[test] + fn test_str_pre() { + let inst = LoadStore::str_pre(0, 1, -16, 64); + let result: u32 = inst.into(); + assert_eq!(0xf81f0c20, result); + } + + #[test] + fn test_stur() { + let inst = LoadStore::stur(0, 1, 0, 64); + let result: u32 = inst.into(); + assert_eq!(0xf8000020, result); + } + + #[test] + fn test_stur_negative_offset() { + let inst = LoadStore::stur(0, 1, -1, 64); + let result: u32 = inst.into(); + assert_eq!(0xf81ff020, result); + } + + #[test] + fn test_stur_positive_offset() { + let inst = LoadStore::stur(0, 1, 255, 64); + let result: u32 = inst.into(); + assert_eq!(0xf80ff020, result); + } +} diff --git a/yjit/src/asm/arm64/inst/mod.rs b/yjit/src/asm/arm64/inst/mod.rs index 8c82eba435..42df2d137a 100644 --- a/yjit/src/asm/arm64/inst/mod.rs +++ b/yjit/src/asm/arm64/inst/mod.rs @@ -9,8 +9,8 @@ mod call; mod conditional; mod data_imm; mod data_reg; -mod load; mod load_literal; +mod load_store; mod logical_imm; mod logical_reg; mod mov; @@ -19,7 +19,6 @@ mod pc_rel; mod reg_pair; mod sbfm; mod shift_imm; -mod store; mod sys_reg; pub use atomic::Atomic; @@ -30,8 +29,8 @@ pub use call::Call; pub use conditional::Conditional; pub use data_imm::DataImm; pub use data_reg::DataReg; -pub use load::Load; pub use load_literal::LoadLiteral; +pub use load_store::LoadStore; pub use logical_imm::LogicalImm; pub use logical_reg::LogicalReg; pub use mov::Mov; @@ -40,5 +39,4 @@ pub use pc_rel::PCRelative; pub use reg_pair::RegisterPair; pub use sbfm::SBFM; pub use shift_imm::ShiftImm; -pub use store::Store; pub use sys_reg::SysReg; diff --git a/yjit/src/asm/arm64/inst/store.rs b/yjit/src/asm/arm64/inst/store.rs deleted file mode 100644 index 42b9055ae8..0000000000 --- a/yjit/src/asm/arm64/inst/store.rs +++ /dev/null @@ -1,105 +0,0 @@ -/// The size of the operands being operated on. -enum Size { - Size32 = 0b10, - Size64 = 0b11, -} - -/// A convenience function so that we can convert the number of bits of an -/// register operand directly into an Sf enum variant. -impl From<u8> for Size { - fn from(num_bits: u8) -> Self { - match num_bits { - 64 => Size::Size64, - 32 => Size::Size32, - _ => panic!("Invalid number of bits: {}", num_bits) - } - } -} - -/// The struct that represents an A64 store instruction that can be encoded. -/// -/// STUR -/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -/// | 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 | -/// | 1 1 1 0 0 0 0 0 0 0 0 | -/// | size. imm9.......................... rn.............. rt.............. | -/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -/// -pub struct Store { - /// The number of the register to be transferred. - rt: u8, - - /// The register holding the memory location. - rn: u8, - - /// The optional signed immediate byte offset from the base register. - imm9: i16, - - /// The size of the operands being operated on. - size: Size -} - -impl Store { - /// STUR (store register, unscaled) - /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STUR--Store-Register--unscaled--?lang=en - pub fn stur(rt: u8, rn: u8, imm9: i16, num_bits: u8) -> Self { - Self { - rt, - rn, - imm9, - size: num_bits.into() - } - } -} - -/// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Loads-and-Stores?lang=en -const FAMILY: u32 = 0b0100; - -impl From<Store> for u32 { - /// Convert an instruction into a 32-bit value. - fn from(inst: Store) -> Self { - let imm9 = (inst.imm9 as u32) & ((1 << 9) - 1); - - 0 - | ((inst.size as u32) << 30) - | (0b11 << 28) - | (FAMILY << 25) - | (imm9 << 12) - | ((inst.rn as u32) << 5) - | (inst.rt as u32) - } -} - -impl From<Store> for [u8; 4] { - /// Convert an instruction into a 4 byte array. - fn from(inst: Store) -> [u8; 4] { - let result: u32 = inst.into(); - result.to_le_bytes() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_stur() { - let inst = Store::stur(0, 1, 0, 64); - let result: u32 = inst.into(); - assert_eq!(0xf8000020, result); - } - - #[test] - fn test_stur_negative_offset() { - let inst = Store::stur(0, 1, -1, 64); - let result: u32 = inst.into(); - assert_eq!(0xf81ff020, result); - } - - #[test] - fn test_stur_positive_offset() { - let inst = Store::stur(0, 1, 255, 64); - let result: u32 = inst.into(); - assert_eq!(0xf80ff020, result); - } -} diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs index 1f9efd1629..8b59d6c354 100644 --- a/yjit/src/asm/arm64/mod.rs +++ b/yjit/src/asm/arm64/mod.rs @@ -376,19 +376,49 @@ pub fn ldr(cb: &mut CodeBlock, rt: A64Opnd, rn: i32) { cb.write_bytes(&bytes); } +/// LDR (post-index) - load a register from memory, update the base pointer after loading it +pub fn ldr_post(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { + let bytes: [u8; 4] = match (rt, rn) { + (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => { + assert!(rt.num_bits == rn.num_bits, "All operands must be of the same size."); + assert!(imm_fits_bits(rn.disp.into(), 9), "The displacement must be 9 bits or less."); + + LoadStore::ldr_post(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into() + }, + _ => panic!("Invalid operand combination to ldr instruction."), + }; + + cb.write_bytes(&bytes); +} + +/// LDR (pre-index) - load a register from memory, update the base pointer before loading it +pub fn ldr_pre(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { + let bytes: [u8; 4] = match (rt, rn) { + (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => { + assert!(rt.num_bits == rn.num_bits, "All operands must be of the same size."); + assert!(imm_fits_bits(rn.disp.into(), 9), "The displacement must be 9 bits or less."); + + LoadStore::ldr_pre(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into() + }, + _ => panic!("Invalid operand combination to ldr instruction."), + }; + + cb.write_bytes(&bytes); +} + /// LDUR - load a memory address into a register pub fn ldur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { let bytes: [u8; 4] = match (rt, rn) { (A64Opnd::Reg(rt), A64Opnd::Reg(rn)) => { assert!(rt.num_bits == rn.num_bits, "All operands must be of the same size."); - Load::ldur(rt.reg_no, rn.reg_no, 0, rt.num_bits).into() + LoadStore::ldur(rt.reg_no, rn.reg_no, 0, rt.num_bits).into() }, (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => { assert!(rt.num_bits == rn.num_bits, "Expected registers to be the same size"); assert!(imm_fits_bits(rn.disp.into(), 9), "Expected displacement to be 9 bits or less"); - Load::ldur(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into() + LoadStore::ldur(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into() }, _ => panic!("Invalid operands for LDUR") }; @@ -403,7 +433,7 @@ pub fn ldursw(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { assert!(rt.num_bits == rn.num_bits, "Expected registers to be the same size"); assert!(imm_fits_bits(rn.disp.into(), 9), "Expected displacement to be 9 bits or less"); - Load::ldursw(rt.reg_no, rn.base_reg_no, rn.disp as i16).into() + LoadStore::ldursw(rt.reg_no, rn.base_reg_no, rn.disp as i16).into() }, _ => panic!("Invalid operand combination to ldursw instruction.") }; @@ -444,6 +474,16 @@ pub fn lsr(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, shift: A64Opnd) { /// MOV - move a value in a register to another register pub fn mov(cb: &mut CodeBlock, rd: A64Opnd, rm: A64Opnd) { let bytes: [u8; 4] = match (rd, rm) { + (A64Opnd::Reg(A64Reg { reg_no: 31, num_bits: 64 }), A64Opnd::Reg(rm)) => { + assert!(rm.num_bits == 64, "Expected rm to be 64 bits"); + + DataImm::add(31, rm.reg_no, 0, 64).into() + }, + (A64Opnd::Reg(rd), A64Opnd::Reg(A64Reg { reg_no: 31, num_bits: 64 })) => { + assert!(rd.num_bits == 64, "Expected rd to be 64 bits"); + + DataImm::add(rd.reg_no, 31, 0, 64).into() + }, (A64Opnd::Reg(rd), A64Opnd::Reg(rm)) => { assert!(rd.num_bits == rm.num_bits, "Expected registers to be the same size"); @@ -615,6 +655,36 @@ pub fn stp_post(cb: &mut CodeBlock, rt1: A64Opnd, rt2: A64Opnd, rn: A64Opnd) { cb.write_bytes(&bytes); } +/// STR (post-index) - store a register to memory, update the base pointer after loading it +pub fn str_post(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { + let bytes: [u8; 4] = match (rt, rn) { + (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => { + assert!(rt.num_bits == rn.num_bits, "All operands must be of the same size."); + assert!(imm_fits_bits(rn.disp.into(), 9), "The displacement must be 9 bits or less."); + + LoadStore::str_post(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into() + }, + _ => panic!("Invalid operand combination to str instruction."), + }; + + cb.write_bytes(&bytes); +} + +/// STR (pre-index) - store a register to memory, update the base pointer before loading it +pub fn str_pre(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { + let bytes: [u8; 4] = match (rt, rn) { + (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => { + assert!(rt.num_bits == rn.num_bits, "All operands must be of the same size."); + assert!(imm_fits_bits(rn.disp.into(), 9), "The displacement must be 9 bits or less."); + + LoadStore::str_pre(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into() + }, + _ => panic!("Invalid operand combination to str instruction."), + }; + + cb.write_bytes(&bytes); +} + /// STUR - store a value in a register at a memory address pub fn stur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { let bytes: [u8; 4] = match (rt, rn) { @@ -622,7 +692,7 @@ pub fn stur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { assert!(rt.num_bits == rn.num_bits, "Expected registers to be the same size"); assert!(imm_fits_bits(rn.disp.into(), 9), "Expected displacement to be 9 bits or less"); - Store::stur(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into() + LoadStore::stur(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into() }, _ => panic!("Invalid operand combination to stur instruction.") }; @@ -919,6 +989,16 @@ mod tests { } #[test] + fn test_ldr_post() { + check_bytes("6a0541f8", |cb| ldr_post(cb, X10, A64Opnd::new_mem(64, X11, 16))); + } + + #[test] + fn test_ldr_pre() { + check_bytes("6a0d41f8", |cb| ldr_pre(cb, X10, A64Opnd::new_mem(64, X11, 16))); + } + + #[test] fn test_ldur_memory() { check_bytes("20b047f8", |cb| ldur(cb, X0, A64Opnd::new_mem(64, X1, 123))); } @@ -954,6 +1034,16 @@ mod tests { } #[test] + fn test_mov_into_sp() { + check_bytes("1f000091", |cb| mov(cb, X31, X0)); + } + + #[test] + fn test_mov_from_sp() { + check_bytes("e0030091", |cb| mov(cb, X0, X31)); + } + + #[test] fn test_movk() { check_bytes("600fa0f2", |cb| movk(cb, X0, A64Opnd::new_uimm(123), 16)); } @@ -1024,6 +1114,16 @@ mod tests { } #[test] + fn test_str_post() { + check_bytes("6a051ff8", |cb| str_post(cb, X10, A64Opnd::new_mem(64, X11, -16))); + } + + #[test] + fn test_str_pre() { + check_bytes("6a0d1ff8", |cb| str_pre(cb, X10, A64Opnd::new_mem(64, X11, -16))); + } + + #[test] fn test_stur() { check_bytes("6a0108f8", |cb| stur(cb, X10, A64Opnd::new_mem(64, X11, 128))); } diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index c48c03fe04..df4fcceec6 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -34,7 +34,7 @@ pub const _C_RET_OPND: Opnd = Opnd::Reg(X0_REG); // These constants define the way we work with Arm64's stack pointer. The stack // pointer always needs to be aligned to a 16-byte boundary. pub const C_SP_REG: A64Opnd = X31; -pub const C_SP_STEP: A64Opnd = A64Opnd::UImm(16); +pub const C_SP_STEP: i32 = 16; /// Map Opnd to A64Opnd impl From<Opnd> for A64Opnd { @@ -380,15 +380,13 @@ impl Assembler /// Emit a push instruction for the given operand by adding to the stack /// pointer and then storing the given value. fn emit_push(cb: &mut CodeBlock, opnd: A64Opnd) { - sub(cb, C_SP_REG, C_SP_REG, C_SP_STEP); - stur(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, 0)); + str_pre(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, -C_SP_STEP)); } /// Emit a pop instruction into the given operand by loading the value /// and then subtracting from the stack pointer. fn emit_pop(cb: &mut CodeBlock, opnd: A64Opnd) { - ldur(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, 0)); - add(cb, C_SP_REG, C_SP_REG, C_SP_STEP); + ldr_post(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, C_SP_STEP)); } // dbg!(&self.insns); @@ -430,11 +428,11 @@ impl Assembler stp_pre(cb, X29, X30, A64Opnd::new_mem(128, C_SP_REG, -16)); // X29 (frame_pointer) = SP - add(cb, X29, C_SP_REG, A64Opnd::new_uimm(0)); + mov(cb, X29, C_SP_REG); }, Op::FrameTeardown => { // SP = X29 (frame pointer) - add(cb, C_SP_REG, X29, A64Opnd::new_uimm(0)); + mov(cb, C_SP_REG, X29); ldp_post(cb, X29, X30, A64Opnd::new_mem(128, C_SP_REG, 16)); }, |