summaryrefslogtreecommitdiff
path: root/yjit
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2023-04-06 08:34:58 -0700
committerGitHub <noreply@github.com>2023-04-06 11:34:58 -0400
commit89bdf6e94cb36567478c509722fe98069402fb8e (patch)
tree633c8fd7d2ebe53e2ce5a9105e346f04068bc843 /yjit
parent2a34bcaa1023c5f0b41774d8153b9625a4233c04 (diff)
downloadruby-89bdf6e94cb36567478c509722fe98069402fb8e.tar.gz
YJIT: Stack temp register allocation for arm64 (#7659)
* YJIT: Stack temp register allocation for arm64 * Update a comment Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com> * Update comments about assertion * Update a comment Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com> --------- Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Diffstat (limited to 'yjit')
-rw-r--r--yjit/src/backend/arm64/mod.rs40
-rw-r--r--yjit/src/backend/ir.rs19
-rw-r--r--yjit/src/backend/x86_64/mod.rs10
-rw-r--r--yjit/src/codegen.rs6
-rw-r--r--yjit/src/options.rs8
-rw-r--r--yjit/src/utils.rs21
6 files changed, 66 insertions, 38 deletions
diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs
index b87068df11..b85c032076 100644
--- a/yjit/src/backend/arm64/mod.rs
+++ b/yjit/src/backend/arm64/mod.rs
@@ -5,9 +5,11 @@
use crate::asm::{CodeBlock};
use crate::asm::arm64::*;
use crate::codegen::{JITState, CodegenGlobals};
+use crate::core::Context;
use crate::cruby::*;
use crate::backend::ir::*;
use crate::virtualmem::CodePtr;
+use crate::options::*;
// Use the arm64 register type for this platform
pub type Reg = A64Reg;
@@ -167,6 +169,10 @@ impl Assembler
const SCRATCH0: A64Opnd = A64Opnd::Reg(X16_REG);
const SCRATCH1: A64Opnd = A64Opnd::Reg(X17_REG);
+ /// List of registers that can be used for stack temps.
+ /// These are caller-saved registers.
+ pub const TEMP_REGS: [Reg; 5] = [X1_REG, X9_REG, X10_REG, X14_REG, X15_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
@@ -175,16 +181,9 @@ impl Assembler
vec![X11_REG, X12_REG, X13_REG]
}
- /// Get the list of registers that can be used for stack temps.
- pub fn get_temp_regs() -> Vec<Reg> {
- // FIXME: arm64 is not supported yet. Insn::Store doesn't support registers
- // in its dest operand. Currently crashing at split_memory_address.
- vec![]
- }
-
/// Get a list of all of the caller-saved registers
pub fn get_caller_save_regs() -> Vec<Reg> {
- vec![X9_REG, X10_REG, X11_REG, X12_REG, X13_REG, X14_REG, X15_REG]
+ vec![X1_REG, X9_REG, X10_REG, X11_REG, X12_REG, X13_REG, X14_REG, X15_REG]
}
/// Split platform-specific instructions
@@ -595,11 +594,6 @@ impl Assembler
asm.not(opnd0);
},
Insn::Store { dest, src } => {
- // The displacement for the STUR instruction can't be more
- // than 9 bits long. If it's longer, we need to load the
- // memory address into a register first.
- let opnd0 = split_memory_address(asm, dest);
-
// The value being stored must be in a register, so if it's
// not already one we'll load it first.
let opnd1 = match src {
@@ -610,7 +604,19 @@ impl Assembler
_ => split_load_operand(asm, src)
};
- asm.store(opnd0, opnd1);
+ match dest {
+ Opnd::Reg(_) => {
+ // Store does not support a register as a dest operand.
+ asm.mov(dest, opnd1);
+ }
+ _ => {
+ // The displacement for the STUR instruction can't be more
+ // than 9 bits long. If it's longer, we need to load the
+ // memory address into a register first.
+ let opnd0 = split_memory_address(asm, dest);
+ asm.store(opnd0, opnd1);
+ }
+ }
},
Insn::Sub { left, right, .. } => {
let opnd0 = split_load_operand(asm, left);
@@ -1085,7 +1091,9 @@ impl Assembler
/// Optimize and compile the stored instructions
pub fn compile_with_regs(self, cb: &mut CodeBlock, regs: Vec<Reg>) -> Vec<u32>
{
- let mut asm = self.lower_stack().arm64_split().alloc_regs(regs);
+ let asm = self.lower_stack();
+ let asm = asm.arm64_split();
+ let mut asm = asm.alloc_regs(regs);
// Create label instances in the code block
for (idx, name) in asm.label_names.iter().enumerate() {
@@ -1170,7 +1178,7 @@ mod tests {
fn test_emit_cpop_all() {
let (mut asm, mut cb) = setup_asm();
- asm.cpop_all();
+ asm.cpop_all(&Context::default());
asm.compile_with_num_regs(&mut cb, 0);
}
diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs
index 37fb378905..a32750eae2 100644
--- a/yjit/src/backend/ir.rs
+++ b/yjit/src/backend/ir.rs
@@ -913,6 +913,13 @@ impl Assembler
}
}
+ /// Get the list of registers that can be used for stack temps.
+ pub fn get_temp_regs() -> Vec<Reg> {
+ let num_regs = get_option!(num_temp_regs);
+ let mut regs = Self::TEMP_REGS.to_vec();
+ regs.drain(0..num_regs).collect()
+ }
+
/// Build an Opnd::InsnOut from the current index of the assembler and the
/// given number of bits.
pub(super) fn next_opnd_out(&self, num_bits: u8) -> Opnd {
@@ -1493,8 +1500,12 @@ impl Assembler {
out
}
- pub fn cpop_all(&mut self) {
+ pub fn cpop_all(&mut self, ctx: &Context) {
self.push_insn(Insn::CPopAll);
+
+ // Re-enable ccall's RegTemps assertion disabled by cpush_all.
+ // cpush_all + cpop_all preserve all stack temp registers, so it's safe.
+ self.set_reg_temps(ctx.get_reg_temps());
}
pub fn cpop_into(&mut self, opnd: Opnd) {
@@ -1507,6 +1518,12 @@ impl Assembler {
pub fn cpush_all(&mut self) {
self.push_insn(Insn::CPushAll);
+
+ // Mark all temps as not being in registers.
+ // Temps will be marked back as being in registers by cpop_all.
+ // We assume that cpush_all + cpop_all are used for C functions in utils.rs
+ // that don't require spill_temps for GC.
+ self.set_reg_temps(RegTemps::default());
}
pub fn cret(&mut self, opnd: Opnd) {
diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs
index 0121e19142..dffac1758d 100644
--- a/yjit/src/backend/x86_64/mod.rs
+++ b/yjit/src/backend/x86_64/mod.rs
@@ -88,6 +88,9 @@ impl Assembler
// a closure and we don't want it to have to capture anything.
const SCRATCH0: X86Opnd = X86Opnd::Reg(R11_REG);
+ /// List of registers that can be used for stack temps.
+ pub const TEMP_REGS: [Reg; 5] = [RSI_REG, RDI_REG, R8_REG, R9_REG, R10_REG];
+
/// Get the list of registers from which we can allocate on this platform
pub fn get_alloc_regs() -> Vec<Reg>
{
@@ -98,13 +101,6 @@ impl Assembler
]
}
- /// Get the list of registers that can be used for stack temps.
- pub fn get_temp_regs() -> Vec<Reg> {
- let num_regs = get_option!(num_temp_regs);
- let mut regs = vec![RSI_REG, RDI_REG, R8_REG, R9_REG, R10_REG];
- regs.drain(0..num_regs).collect()
- }
-
/// Get a list of all of the caller-save registers
pub fn get_caller_save_regs() -> Vec<Reg> {
vec![RAX_REG, RCX_REG, RDX_REG, RSI_REG, RDI_REG, R8_REG, R9_REG, R10_REG, R11_REG]
diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs
index fdee17c166..e54b2e2752 100644
--- a/yjit/src/codegen.rs
+++ b/yjit/src/codegen.rs
@@ -933,7 +933,7 @@ pub fn gen_single_block(
// If requested, dump instructions for debugging
if get_option!(dump_insns) {
println!("compiling {}", insn_name(opcode));
- print_str(&mut asm, &format!("executing {}", insn_name(opcode)));
+ print_str(&mut asm, &ctx, &format!("executing {}", insn_name(opcode)));
}
// Call the code generation function
@@ -8402,7 +8402,9 @@ mod tests {
let status = gen_pop(&mut jit, &mut context, &mut asm, &mut ocb);
assert_eq!(status, KeepCompiling);
- assert_eq!(context.diff(&Context::default()), TypeDiff::Compatible(0));
+ let mut default = Context::default();
+ default.set_reg_temps(context.get_reg_temps());
+ assert_eq!(context.diff(&default), TypeDiff::Compatible(0));
}
#[test]
diff --git a/yjit/src/options.rs b/yjit/src/options.rs
index 759ed16205..03acc7bbe0 100644
--- a/yjit/src/options.rs
+++ b/yjit/src/options.rs
@@ -1,4 +1,5 @@
use std::ffi::CStr;
+use crate::backend::ir::Assembler;
// Command-line options
#[derive(Clone, PartialEq, Eq, Debug)]
@@ -55,7 +56,7 @@ pub static mut OPTIONS: Options = Options {
greedy_versioning: false,
no_type_prop: false,
max_versions: 4,
- num_temp_regs: 0,
+ num_temp_regs: 5,
gen_stats: false,
gen_trace_exits: false,
pause: false,
@@ -146,7 +147,10 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> {
},
("temp-regs", _) => match opt_val.parse() {
- Ok(n) => unsafe { OPTIONS.num_temp_regs = n },
+ Ok(n) => {
+ assert!(n <= Assembler::TEMP_REGS.len(), "--yjit-temp-regs must be <= {}", Assembler::TEMP_REGS.len());
+ unsafe { OPTIONS.num_temp_regs = n }
+ }
Err(_) => {
return None;
}
diff --git a/yjit/src/utils.rs b/yjit/src/utils.rs
index d9a75b5302..5291e05c96 100644
--- a/yjit/src/utils.rs
+++ b/yjit/src/utils.rs
@@ -1,6 +1,7 @@
#![allow(dead_code)] // Some functions for print debugging in here
use crate::backend::ir::*;
+use crate::core::Context;
use crate::cruby::*;
use std::slice;
@@ -141,7 +142,7 @@ macro_rules! c_callable {
}
pub(crate) use c_callable;
-pub fn print_int(asm: &mut Assembler, opnd: Opnd) {
+pub fn print_int(asm: &mut Assembler, ctx: &Context, opnd: Opnd) {
c_callable!{
fn print_int_fn(val: i64) {
println!("{}", val);
@@ -164,11 +165,11 @@ pub fn print_int(asm: &mut Assembler, opnd: Opnd) {
};
asm.ccall(print_int_fn as *const u8, vec![argument]);
- asm.cpop_all();
+ asm.cpop_all(ctx);
}
/// Generate code to print a pointer
-pub fn print_ptr(asm: &mut Assembler, opnd: Opnd) {
+pub fn print_ptr(asm: &mut Assembler, ctx: &Context, opnd: Opnd) {
c_callable!{
fn print_ptr_fn(ptr: *const u8) {
println!("{:p}", ptr);
@@ -179,11 +180,11 @@ pub fn print_ptr(asm: &mut Assembler, opnd: Opnd) {
asm.cpush_all();
asm.ccall(print_ptr_fn as *const u8, vec![opnd]);
- asm.cpop_all();
+ asm.cpop_all(ctx);
}
/// Generate code to print a value
-pub fn print_value(asm: &mut Assembler, opnd: Opnd) {
+pub fn print_value(asm: &mut Assembler, ctx: &Context, opnd: Opnd) {
c_callable!{
fn print_value_fn(val: VALUE) {
unsafe { rb_obj_info_dump(val) }
@@ -194,11 +195,11 @@ pub fn print_value(asm: &mut Assembler, opnd: Opnd) {
asm.cpush_all();
asm.ccall(print_value_fn as *const u8, vec![opnd]);
- asm.cpop_all();
+ asm.cpop_all(ctx);
}
/// Generate code to print constant string to stdout
-pub fn print_str(asm: &mut Assembler, str: &str) {
+pub fn print_str(asm: &mut Assembler, ctx: &Context, str: &str) {
c_callable!{
fn print_str_cfun(ptr: *const u8, num_bytes: usize) {
unsafe {
@@ -222,7 +223,7 @@ pub fn print_str(asm: &mut Assembler, str: &str) {
let opnd = asm.lea_label(string_data);
asm.ccall(print_str_cfun as *const u8, vec![opnd, Opnd::UImm(str.len() as u64)]);
- asm.cpop_all();
+ asm.cpop_all(ctx);
}
#[cfg(test)]
@@ -262,7 +263,7 @@ mod tests {
let mut asm = Assembler::new();
let mut cb = CodeBlock::new_dummy(1024);
- print_int(&mut asm, Opnd::Imm(42));
+ print_int(&mut asm, &Context::default(), Opnd::Imm(42));
asm.compile(&mut cb);
}
@@ -271,7 +272,7 @@ mod tests {
let mut asm = Assembler::new();
let mut cb = CodeBlock::new_dummy(1024);
- print_str(&mut asm, "Hello, world!");
+ print_str(&mut asm, &Context::default(), "Hello, world!");
asm.compile(&mut cb);
}
}