diff options
Diffstat (limited to 'yjit/src/asm/arm64/inst/mod.rs')
-rw-r--r-- | yjit/src/asm/arm64/inst/mod.rs | 175 |
1 files changed, 148 insertions, 27 deletions
diff --git a/yjit/src/asm/arm64/inst/mod.rs b/yjit/src/asm/arm64/inst/mod.rs index 9c5b53b0ac..eec9d116b2 100644 --- a/yjit/src/asm/arm64/inst/mod.rs +++ b/yjit/src/asm/arm64/inst/mod.rs @@ -1,19 +1,20 @@ -mod branches_and_system; -mod data_processing_immediate; -mod data_processing_register; -mod family; -mod loads_and_stores; +mod branch; +mod data_imm; +mod data_reg; +mod load; +mod mov; mod sf; -use branches_and_system::BranchesAndSystem; -use data_processing_immediate::DataProcessingImmediate; -use data_processing_register::DataProcessingRegister; -use loads_and_stores::LoadsAndStores; +use branch::Branch; +use data_imm::DataImm; +use data_reg::DataReg; +use load::Load; +use mov::Mov; use crate::asm::{CodeBlock, imm_num_bits}; use super::opnd::*; -/// ADD +/// ADD - add rn and rm, put the result in rd, don't update flags pub fn add(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { let bytes: [u8; 4] = match (rd, rn, rm) { (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Reg(rm)) => { @@ -22,13 +23,13 @@ pub fn add(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { "All operands must be of the same size." ); - DataProcessingRegister::add(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into() + DataReg::add(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into() }, (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::UImm(imm12)) => { assert!(rd.num_bits == rn.num_bits, "rd and rn must be of the same size."); assert!(imm12.num_bits <= 12, "The immediate operand must be 12 bits or less."); - DataProcessingImmediate::add(rd.reg_no, rn.reg_no, imm12.value as u16, rd.num_bits).into() + DataImm::add(rd.reg_no, rn.reg_no, imm12.value as u16, rd.num_bits).into() }, _ => panic!("Invalid operand combination to add instruction."), }; @@ -36,7 +37,7 @@ pub fn add(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { cb.write_bytes(&bytes); } -/// ADDS +/// ADDS - add rn and rm, put the result in rd, update flags pub fn adds(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { let bytes: [u8; 4] = match (rd, rn, rm) { (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Reg(rm)) => { @@ -45,13 +46,13 @@ pub fn adds(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { "All operands must be of the same size." ); - DataProcessingRegister::adds(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into() + DataReg::adds(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into() }, (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::UImm(imm12)) => { assert!(rd.num_bits == rn.num_bits, "rd and rn must be of the same size."); assert!(imm12.num_bits <= 12, "The immediate operand must be 12 bits or less."); - DataProcessingImmediate::adds(rd.reg_no, rn.reg_no, imm12.value as u16, rd.num_bits).into() + DataImm::adds(rd.reg_no, rn.reg_no, imm12.value as u16, rd.num_bits).into() }, _ => panic!("Invalid operand combination to adds instruction."), }; @@ -59,14 +60,24 @@ pub fn adds(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { cb.write_bytes(&bytes); } -/// LDUR +/// BR - branch to a register +pub fn br(cb: &mut CodeBlock, rn: A64Opnd) { + let bytes: [u8; 4] = match rn { + A64Opnd::Reg(rn) => Branch::br(rn.reg_no).into(), + _ => panic!("Invalid operand to br 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::Mem(rn)) => { assert!(rt.num_bits == rn.num_bits, "Expected registers to be the same size"); assert!(imm_num_bits(rn.disp.into()) <= 9, "Expected displacement to be 9 bits or less"); - LoadsAndStores::ldur(rt.reg_no, rn.base_reg_no, rn.disp.try_into().unwrap(), rt.num_bits).into() + Load::ldur(rt.reg_no, rn.base_reg_no, rn.disp.try_into().unwrap(), rt.num_bits).into() }, _ => panic!("Invalid operands for LDUR") }; @@ -74,7 +85,35 @@ pub fn ldur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { cb.write_bytes(&bytes); } -/// SUB +/// MOVK - move a 16 bit immediate into a register, keep the other bits in place +pub fn movk(cb: &mut CodeBlock, rd: A64Opnd, imm16: A64Opnd, shift: u8) { + let bytes: [u8; 4] = match (rd, imm16) { + (A64Opnd::Reg(rd), A64Opnd::UImm(imm16)) => { + assert!(imm16.num_bits <= 16, "The immediate operand must be 16 bits or less."); + + Mov::movk(rd.reg_no, imm16.value as u16, shift, rd.num_bits).into() + }, + _ => panic!("Invalid operand combination to movk instruction.") + }; + + cb.write_bytes(&bytes); +} + +/// MOVZ - move a 16 bit immediate into a register, zero the other bits +pub fn movz(cb: &mut CodeBlock, rd: A64Opnd, imm16: A64Opnd, shift: u8) { + let bytes: [u8; 4] = match (rd, imm16) { + (A64Opnd::Reg(rd), A64Opnd::UImm(imm16)) => { + assert!(imm16.num_bits <= 16, "The immediate operand must be 16 bits or less."); + + Mov::movz(rd.reg_no, imm16.value as u16, shift, rd.num_bits).into() + }, + _ => panic!("Invalid operand combination to movz instruction.") + }; + + cb.write_bytes(&bytes); +} + +/// SUB - subtract rm from rn, put the result in rd, don't update flags pub fn sub(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { let bytes: [u8; 4] = match (rd, rn, rm) { (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Reg(rm)) => { @@ -83,13 +122,13 @@ pub fn sub(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { "All operands must be of the same size." ); - DataProcessingRegister::sub(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into() + DataReg::sub(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into() }, (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::UImm(imm12)) => { assert!(rd.num_bits == rn.num_bits, "rd and rn must be of the same size."); assert!(imm12.num_bits <= 12, "The immediate operand must be 12 bits or less."); - DataProcessingImmediate::sub(rd.reg_no, rn.reg_no, imm12.value as u16, rd.num_bits).into() + DataImm::sub(rd.reg_no, rn.reg_no, imm12.value as u16, rd.num_bits).into() }, _ => panic!("Invalid operand combination to sub instruction."), }; @@ -97,7 +136,7 @@ pub fn sub(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { cb.write_bytes(&bytes); } -/// SUBS +/// SUBS - subtract rm from rn, put the result in rd, update flags pub fn subs(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { let bytes: [u8; 4] = match (rd, rn, rm) { (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Reg(rm)) => { @@ -106,13 +145,13 @@ pub fn subs(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { "All operands must be of the same size." ); - DataProcessingRegister::subs(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into() + DataReg::subs(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into() }, (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::UImm(imm12)) => { assert!(rd.num_bits == rn.num_bits, "rd and rn must be of the same size."); assert!(imm12.num_bits <= 12, "The immediate operand must be 12 bits or less."); - DataProcessingImmediate::subs(rd.reg_no, rn.reg_no, imm12.value as u16, rd.num_bits).into() + DataImm::subs(rd.reg_no, rn.reg_no, imm12.value as u16, rd.num_bits).into() }, _ => panic!("Invalid operand combination to subs instruction."), }; @@ -120,13 +159,95 @@ pub fn subs(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { cb.write_bytes(&bytes); } -/// RET +/// RET - unconditionally return to a location in a register, defaults to X30 pub fn ret(cb: &mut CodeBlock, rn: A64Opnd) { let bytes: [u8; 4] = match rn { - A64Opnd::None => BranchesAndSystem::ret(30).into(), - A64Opnd::Reg(reg) => BranchesAndSystem::ret(reg.reg_no).into(), - _ => panic!("Invalid operand for RET") + A64Opnd::None => Branch::ret(30).into(), + A64Opnd::Reg(reg) => Branch::ret(reg.reg_no).into(), + _ => panic!("Invalid operand to ret instruction.") }; cb.write_bytes(&bytes); } + +#[cfg(test)] +mod tests { + use super::*; + + /// Check that the bytes for an instruction sequence match a hex string + fn check_bytes<R>(bytes: &str, run: R) where R: FnOnce(&mut super::CodeBlock) { + let mut cb = super::CodeBlock::new_dummy(128); + run(&mut cb); + assert_eq!(format!("{:x}", cb), bytes); + } + + #[test] + fn test_add_register() { + check_bytes("2000028b", |cb| add(cb, X0, X1, X2)); + } + + #[test] + fn test_add_immediate() { + check_bytes("201c0091", |cb| add(cb, X0, X1, A64Opnd::new_uimm(7))); + } + + #[test] + fn test_adds_register() { + check_bytes("200002ab", |cb| adds(cb, X0, X1, X2)); + } + + #[test] + fn test_adds_immediate() { + check_bytes("201c00b1", |cb| adds(cb, X0, X1, A64Opnd::new_uimm(7))); + } + + #[test] + fn test_br() { + check_bytes("80021fd6", |cb| br(cb, X20)); + } + + #[test] + fn test_ldur() { + check_bytes("20b047f8", |cb| ldur(cb, X0, A64Opnd::new_mem(X1, 123))); + } + + #[test] + fn test_movk() { + check_bytes("600fa0f2", |cb| movk(cb, X0, A64Opnd::new_uimm(123), 16)); + } + + #[test] + fn test_movz() { + check_bytes("600fa0d2", |cb| movz(cb, X0, A64Opnd::new_uimm(123), 16)); + } + + #[test] + fn test_ret_none() { + check_bytes("c0035fd6", |cb| ret(cb, A64Opnd::None)); + } + + #[test] + fn test_ret_register() { + check_bytes("80025fd6", |cb| ret(cb, X20)); + } + + #[test] + fn test_sub_register() { + check_bytes("200002cb", |cb| sub(cb, X0, X1, X2)); + } + + #[test] + fn test_sub_immediate() { + check_bytes("201c00d1", |cb| sub(cb, X0, X1, A64Opnd::new_uimm(7))); + } + + #[test] + fn test_subs_register() { + check_bytes("200002eb", |cb| subs(cb, X0, X1, X2)); + } + + #[test] + fn test_subs_immediate() { + check_bytes("201c00f1", |cb| subs(cb, X0, X1, A64Opnd::new_uimm(7))); + } +} |