summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Newton <kddnewton@gmail.com>2022-07-19 17:44:39 -0400
committerTakashi Kokubun <takashikkbn@gmail.com>2022-08-29 08:47:02 -0700
commit4ae2c744ac6b5b84f2bfebb9046c0c364863d7a4 (patch)
treed301009779db91e28a7189b441fbe02a05e6b9a8
parentc9484fe0c1f2897521d08780a66fab89e5e2f5b1 (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.rs124
-rw-r--r--yjit/src/asm/arm64/inst/load_store.rs215
-rw-r--r--yjit/src/asm/arm64/inst/mod.rs6
-rw-r--r--yjit/src/asm/arm64/inst/store.rs105
-rw-r--r--yjit/src/asm/arm64/mod.rs108
-rw-r--r--yjit/src/backend/arm64/mod.rs12
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));
},