ruby-changes:73187
From: Kevin <ko1@a...>
Date: Tue, 30 Aug 2022 00:59:55 +0900 (JST)
Subject: [ruby-changes:73187] 160e29b9e5 (master): Port print_str to new backend (https://github.com/Shopify/ruby/pull/318)
https://git.ruby-lang.org/ruby.git/commit/?id=160e29b9e5 From 160e29b9e5c9419e3275d4bd6de09c9c4f242602 Mon Sep 17 00:00:00 2001 From: Kevin Newton <kddnewton@g...> Date: Fri, 15 Jul 2022 13:25:26 -0400 Subject: Port print_str to new backend (https://github.com/Shopify/ruby/pull/318) * ADR and ADRP for AArch64 * Implement Op::Jbe on X86 * Lera instruction * Op::BakeString * LeaPC -> LeaLabel * Port print_str to the new backend * Port print_value to the new backend * Port print_ptr to the new backend * Write null-terminators in Op::BakeString * Fix up rebase issues on print-str port * Add back in panic for X86 backend for unsupported instructions being lowered * Fix target architecture --- yjit/src/asm/arm64/inst/mod.rs | 2 + yjit/src/asm/arm64/inst/pc_rel.rs | 107 ++++++++++++++++++++++++++++++++++++++ yjit/src/asm/arm64/mod.rs | 42 +++++++++++++++ yjit/src/backend/arm64/mod.rs | 75 +++++++++++++++++++++----- yjit/src/backend/ir.rs | 62 ++++++++++++++++------ yjit/src/backend/tests.rs | 8 +++ yjit/src/backend/x86_64/mod.rs | 53 ++++++++++++++++--- yjit/src/codegen.rs | 4 +- yjit/src/utils.rs | 89 +++++++++---------------------- 9 files changed, 339 insertions(+), 103 deletions(-) create mode 100644 yjit/src/asm/arm64/inst/pc_rel.rs diff --git a/yjit/src/asm/arm64/inst/mod.rs b/yjit/src/asm/arm64/inst/mod.rs index 9dfc923f53..752ee64aa3 100644 --- a/yjit/src/asm/arm64/inst/mod.rs +++ b/yjit/src/asm/arm64/inst/mod.rs @@ -14,6 +14,7 @@ mod logical_imm; https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/mod.rs#L14 mod logical_reg; mod mov; mod nop; +mod pc_rel; mod shift_imm; mod store; mod sys_reg; @@ -31,6 +32,7 @@ pub use logical_imm::LogicalImm; https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/mod.rs#L32 pub use logical_reg::LogicalReg; pub use mov::Mov; pub use nop::Nop; +pub use pc_rel::PCRelative; pub use shift_imm::ShiftImm; pub use store::Store; pub use sys_reg::SysReg; diff --git a/yjit/src/asm/arm64/inst/pc_rel.rs b/yjit/src/asm/arm64/inst/pc_rel.rs new file mode 100644 index 0000000000..fa330cb9d6 --- /dev/null +++ b/yjit/src/asm/arm64/inst/pc_rel.rs @@ -0,0 +1,107 @@ https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/pc_rel.rs#L1 +/// Which operation to perform for the PC-relative instruction. +enum Op { + /// Form a PC-relative address. + ADR = 0, + + /// Form a PC-relative address to a 4KB page. + ADRP = 1 +} + +/// The struct that represents an A64 PC-relative address instruction that can +/// be encoded. +/// +/// ADR +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// | 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 0 0 0 0 | +/// | op immlo immhi........................................................... rd.............. | +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// +pub struct PCRelative { + /// The number for the general-purpose register to load the address into. + rd: u8, + + /// The number of bytes to add to the PC to form the address. + imm: i32, + + /// Which operation to perform for this instruction. + op: Op +} + +impl PCRelative { + /// ADR + /// https://developer.arm.com/documentation/ddi0602/2022-03/Base-Instructions/ADR--Form-PC-relative-address- + pub fn adr(rd: u8, imm: i32) -> Self { + Self { rd, imm, op: Op::ADR } + } + + /// ADRP + /// https://developer.arm.com/documentation/ddi0602/2022-03/Base-Instructions/ADRP--Form-PC-relative-address-to-4KB-page- + pub fn adrp(rd: u8, imm: i32) -> Self { + Self { rd, imm: imm >> 12, op: Op::ADRP } + } +} + +/// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Data-Processing----Immediate?lang=en +const FAMILY: u32 = 0b1000; + +impl From<PCRelative> for u32 { + /// Convert an instruction into a 32-bit value. + fn from(inst: PCRelative) -> Self { + let immlo = (inst.imm & 0b11) as u32; + let mut immhi = ((inst.imm >> 2) & ((1 << 18) - 1)) as u32; + + // Toggle the sign bit if necessary. + if inst.imm < 0 { + immhi |= (1 << 18); + } + + 0 + | ((inst.op as u32) << 31) + | (immlo << 29) + | (FAMILY << 25) + | (immhi << 5) + | inst.rd as u32 + } +} + +impl From<PCRelative> for [u8; 4] { + /// Convert an instruction into a 4 byte array. + fn from(inst: PCRelative) -> [u8; 4] { + let result: u32 = inst.into(); + result.to_le_bytes() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_adr_positive() { + let inst = PCRelative::adr(0, 5); + let result: u32 = inst.into(); + assert_eq!(0x30000020, result); + } + + #[test] + fn test_adr_negative() { + let inst = PCRelative::adr(0, -5); + let result: u32 = inst.into(); + assert_eq!(0x70ffffc0, result); + } + + #[test] + fn test_adrp_positive() { + let inst = PCRelative::adrp(0, 0x4000); + let result: u32 = inst.into(); + assert_eq!(0x90000020, result); + } + + #[test] + fn test_adrp_negative() { + let inst = PCRelative::adrp(0, -0x4000); + let result: u32 = inst.into(); + assert_eq!(0x90ffffe0, result); + } +} diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs index 7adc1a2745..ca69b33d9e 100644 --- a/yjit/src/asm/arm64/mod.rs +++ b/yjit/src/asm/arm64/mod.rs @@ -94,6 +94,38 @@ pub fn adds(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L94 cb.write_bytes(&bytes); } +/// ADR - form a PC-relative address and load it into a register +pub fn adr(cb: &mut CodeBlock, rd: A64Opnd, imm: A64Opnd) { + let bytes: [u8; 4] = match (rd, imm) { + (A64Opnd::Reg(rd), A64Opnd::Imm(imm)) => { + assert!(rd.num_bits == 64, "The destination register must be 64 bits."); + assert!(imm_fits_bits(imm, 21), "The immediate operand must be 21 bits or less."); + + PCRelative::adr(rd.reg_no, imm as i32).into() + }, + _ => panic!("Invalid operand combination to adr instruction."), + }; + + cb.write_bytes(&bytes); +} + +/// ADRP - form a PC-relative address to a 4KB page and load it into a register. +/// This is effectively the same as ADR except that the immediate must be a +/// multiple of 4KB. +pub fn adrp(cb: &mut CodeBlock, rd: A64Opnd, imm: A64Opnd) { + let bytes: [u8; 4] = match (rd, imm) { + (A64Opnd::Reg(rd), A64Opnd::Imm(imm)) => { + assert!(rd.num_bits == 64, "The destination register must be 64 bits."); + assert!(imm_fits_bits(imm, 32), "The immediate operand must be 32 bits or less."); + + PCRelative::adrp(rd.reg_no, imm as i32).into() + }, + _ => panic!("Invalid operand combination to adr instruction."), + }; + + cb.write_bytes(&bytes); +} + /// AND - and rn and rm, put the result in rd, don't update flags pub fn and(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { let bytes: [u8; 4] = match (rd, rn, rm) { @@ -628,6 +660,16 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L660 check_bytes("201c00f1", |cb| adds(cb, X0, X1, A64Opnd::new_imm(-7))); } + #[test] + fn test_adr() { + check_bytes("aa000010", |cb| adr(cb, X10, A64Opnd::new_imm(20))); + } + + #[test] + fn test_adrp() { + check_bytes("4a000090", |cb| adrp(cb, X10, A64Opnd::new_imm(0x8000))); + } + #[test] fn test_and_register() { check_bytes("2000028a", |cb| and(cb, X0, X1, X2)); diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 22998b1ab5..153237a9b1 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -58,6 +58,9 @@ impl From<Opnd> for A64Opnd { https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/arm64/mod.rs#L58 impl Assembler { + // A special scratch register for intermediate processing. + const SCRATCH0: A64Opnd = A64Opnd::Reg(X22_REG); + /// Get the list of registers from which we will allocate on this platform /// These are caller-saved registers /// Note: we intentionally exclude C_RET_REG (X0) from this list @@ -78,7 +81,7 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/arm64/mod.rs#L81 /// have no memory operands. fn arm64_split(mut self) -> Assembler { - self.forward_pass(|asm, index, op, opnds, target| { + self.forward_pass(|asm, index, op, opnds, target, text| { // Load all Value operands into registers that aren't already a part // of Load instructions. let opnds = match op { @@ -100,15 +103,15 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/arm64/mod.rs#L103 (Opnd::Mem(_), Opnd::Mem(_)) => { let opnd0 = asm.load(opnds[0]); let opnd1 = asm.load(opnds[1]); - asm.push_insn(op, vec![opnd0, opnd1], target); + asm.push_insn(op, vec![opnd0, opnd1], target, text); }, (mem_opnd @ Opnd::Mem(_), other_opnd) | (other_opnd, mem_opnd @ Opnd::Mem(_)) => { let opnd0 = asm.load(mem_opnd); - asm.push_insn(op, vec![opnd0, other_opnd], target); + asm.push_insn(op, vec![opnd0, other_opnd], target, text); }, (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/