Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions yjit/src/asm/arm64/inst/load_literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub struct LoadLiteral {
impl LoadLiteral {
/// LDR (load literal)
/// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDR--literal---Load-Register--literal--?lang=en
pub fn ldr(rt: u8, imm19: i32, num_bits: u8) -> Self {
pub fn ldr_literal(rt: u8, imm19: i32, num_bits: u8) -> Self {
Self { rt, imm19, opc: num_bits.into() }
}
}
Expand Down Expand Up @@ -75,14 +75,14 @@ mod tests {

#[test]
fn test_ldr_positive() {
let inst = LoadLiteral::ldr(0, 5, 64);
let inst = LoadLiteral::ldr_literal(0, 5, 64);
let result: u32 = inst.into();
assert_eq!(0x580000a0, result);
}

#[test]
fn test_ldr_negative() {
let inst = LoadLiteral::ldr(0, -5, 64);
let inst = LoadLiteral::ldr_literal(0, -5, 64);
let result: u32 = inst.into();
assert_eq!(0x58ffff60, result);
}
Expand Down
108 changes: 108 additions & 0 deletions yjit/src/asm/arm64/inst/load_register.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/// Whether or not to shift the register.
enum S {
Shift = 1,
NoShift = 0
}

/// The option for this instruction.
enum Option {
UXTW = 0b010,
LSL = 0b011,
SXTW = 0b110,
SXTX = 0b111
}

/// The size of the operands of this instruction.
enum Size {
Size32 = 0b10,
Size64 = 0b11
}

/// A convenience function so that we can convert the number of bits of an
/// register operand directly into a Size 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 load instruction that can be encoded.
///
/// LDR
/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
/// | 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 1 1 1 0 |
/// | size. rm.............. option.. S rn.............. rt.............. |
/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
///
pub struct LoadRegister {
/// The number of the register to load the value into.
rt: u8,

/// The base register with which to form the address.
rn: u8,

/// Whether or not to shift the value of the register.
s: S,

/// The option associated with this instruction that controls the shift.
option: Option,

/// The number of the offset register.
rm: u8,

/// The size of the operands.
size: Size
}

impl LoadRegister {
/// LDR
/// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDR--register---Load-Register--register--?lang=en
pub fn ldr(rt: u8, rn: u8, rm: u8, num_bits: u8) -> Self {
Self { rt, rn, s: S::NoShift, option: Option::LSL, rm, 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<LoadRegister> for u32 {
/// Convert an instruction into a 32-bit value.
fn from(inst: LoadRegister) -> Self {
0
| ((inst.size as u32) << 30)
| (0b11 << 28)
| (FAMILY << 25)
| (0b11 << 21)
| ((inst.rm as u32) << 16)
| ((inst.option as u32) << 13)
| ((inst.s as u32) << 12)
| (0b10 << 10)
| ((inst.rn as u32) << 5)
| (inst.rt as u32)
}
}

impl From<LoadRegister> for [u8; 4] {
/// Convert an instruction into a 4 byte array.
fn from(inst: LoadRegister) -> [u8; 4] {
let result: u32 = inst.into();
result.to_le_bytes()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_ldr() {
let inst = LoadRegister::ldr(0, 1, 2, 64);
let result: u32 = inst.into();
assert_eq!(0xf8626820, result);
}
}
2 changes: 2 additions & 0 deletions yjit/src/asm/arm64/inst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod conditional;
mod data_imm;
mod data_reg;
mod load_literal;
mod load_register;
mod load_store;
mod logical_imm;
mod logical_reg;
Expand All @@ -30,6 +31,7 @@ pub use conditional::Conditional;
pub use data_imm::DataImm;
pub use data_reg::DataReg;
pub use load_literal::LoadLiteral;
pub use load_register::LoadRegister;
pub use load_store::LoadStore;
pub use logical_imm::LogicalImm;
pub use logical_reg::LogicalReg;
Expand Down
46 changes: 36 additions & 10 deletions yjit/src/asm/arm64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,24 +374,45 @@ pub fn ldp_post(cb: &mut CodeBlock, rt1: A64Opnd, rt2: A64Opnd, rn: A64Opnd) {
cb.write_bytes(&bytes);
}

/// LDR - load a memory address into a register with a register offset
pub fn ldr(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd, rm: A64Opnd) {
let bytes: [u8; 4] = match (rt, rn, rm) {
(A64Opnd::Reg(rt), A64Opnd::Reg(rn), A64Opnd::Reg(rm)) => {
assert!(rt.num_bits == rn.num_bits, "Expected registers to be the same size");
assert!(rn.num_bits == rm.num_bits, "Expected registers to be the same size");

LoadRegister::ldr(rt.reg_no, rn.reg_no, rm.reg_no, rt.num_bits).into()
},
_ => panic!("Invalid operand combination to ldr instruction.")
};

cb.write_bytes(&bytes);
}

/// LDR - load a PC-relative memory address into a register
pub fn ldr(cb: &mut CodeBlock, rt: A64Opnd, rn: i32) {
pub fn ldr_literal(cb: &mut CodeBlock, rt: A64Opnd, rn: i32) {
let bytes: [u8; 4] = match rt {
A64Opnd::Reg(rt) => {
LoadLiteral::ldr(rt.reg_no, rn, rt.num_bits).into()
LoadLiteral::ldr_literal(rt.reg_no, rn, rt.num_bits).into()
},
_ => panic!("Invalid operand combination to ldr instruction."),
};

cb.write_bytes(&bytes);
}

/// Whether or not a memory address displacement fits into the maximum number of
/// bits such that it can be used without loading it into a register first.
pub fn mem_disp_fits_bits(disp: i32) -> bool {
imm_fits_bits(disp.into(), 9)
}

/// 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.");
assert!(mem_disp_fits_bits(rn.disp), "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()
},
Expand All @@ -406,7 +427,7 @@ 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.");
assert!(mem_disp_fits_bits(rn.disp), "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()
},
Expand All @@ -426,7 +447,7 @@ pub fn ldur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) {
},
(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");
assert!(mem_disp_fits_bits(rn.disp), "Expected displacement to be 9 bits or less");

LoadStore::ldur(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into()
},
Expand All @@ -441,7 +462,7 @@ pub fn ldursw(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, "Expected registers to be the same size");
assert!(imm_fits_bits(rn.disp.into(), 9), "Expected displacement to be 9 bits or less");
assert!(mem_disp_fits_bits(rn.disp), "Expected displacement to be 9 bits or less");

LoadStore::ldursw(rt.reg_no, rn.base_reg_no, rn.disp as i16).into()
},
Expand Down Expand Up @@ -670,7 +691,7 @@ 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.");
assert!(mem_disp_fits_bits(rn.disp), "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()
},
Expand All @@ -685,7 +706,7 @@ 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.");
assert!(mem_disp_fits_bits(rn.disp), "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()
},
Expand All @@ -700,7 +721,7 @@ pub fn stur(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, "Expected registers to be the same size");
assert!(imm_fits_bits(rn.disp.into(), 9), "Expected displacement to be 9 bits or less");
assert!(mem_disp_fits_bits(rn.disp), "Expected displacement to be 9 bits or less");

LoadStore::stur(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into()
},
Expand Down Expand Up @@ -1024,7 +1045,12 @@ mod tests {

#[test]
fn test_ldr() {
check_bytes("40010058", |cb| ldr(cb, X0, 10));
check_bytes("6a696cf8", |cb| ldr(cb, X10, X11, X12));
}

#[test]
fn test_ldr_literal() {
check_bytes("40010058", |cb| ldr_literal(cb, X0, 10));
}

#[test]
Expand Down
Loading