summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>2022-05-17 17:31:36 -0400
committerTakashi Kokubun <takashikkbn@gmail.com>2022-08-29 08:46:53 -0700
commite9cc17dcc9a365d59330b8c37baeafed5d75a519 (patch)
tree91ebfe56849c527d98df38e7361e7e6d41ba9124
parent18dc379aca69fd9dc72debae3fd504399799e86f (diff)
Start work on platform-specific codegen
-rw-r--r--yjit/src/asm/x86_64/mod.rs51
-rw-r--r--yjit/src/backend/ir.rs (renamed from yjit/src/ir.rs)138
-rw-r--r--yjit/src/backend/mod.rs3
-rw-r--r--yjit/src/backend/x86_64/mod.rs55
-rw-r--r--yjit/src/codegen.rs1
-rw-r--r--yjit/src/lib.rs2
6 files changed, 161 insertions, 89 deletions
diff --git a/yjit/src/asm/x86_64/mod.rs b/yjit/src/asm/x86_64/mod.rs
index b4ef2e4bf9..0a930ecf60 100644
--- a/yjit/src/asm/x86_64/mod.rs
+++ b/yjit/src/asm/x86_64/mod.rs
@@ -34,7 +34,7 @@ pub enum RegType
IP,
}
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct X86Reg
{
// Size in bits
@@ -157,22 +157,39 @@ const RBP_REG_NO: u8 = 5;
const R12_REG_NO: u8 = 12;
const R13_REG_NO: u8 = 13;
-pub const RAX: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: RAX_REG_NO });
-pub const RCX: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 1 });
-pub const RDX: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 2 });
-pub const RBX: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 3 });
-pub const RSP: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: RSP_REG_NO });
-pub const RBP: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: RBP_REG_NO });
-pub const RSI: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 6 });
-pub const RDI: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 7 });
-pub const R8: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 8 });
-pub const R9: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 9 });
-pub const R10: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 10 });
-pub const R11: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 11 });
-pub const R12: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: R12_REG_NO });
-pub const R13: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: R13_REG_NO });
-pub const R14: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 14 });
-pub const R15: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 15 });
+pub const RAX_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: RAX_REG_NO };
+pub const RCX_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 1 };
+pub const RDX_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 2 };
+pub const RBX_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 3 };
+pub const RSP_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: RSP_REG_NO };
+pub const RBP_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: RBP_REG_NO };
+pub const RSI_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 6 };
+pub const RDI_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 7 };
+pub const R8_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 8 };
+pub const R9_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 9 };
+pub const R10_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 10 };
+pub const R11_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 11 };
+pub const R12_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: R12_REG_NO };
+pub const R13_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: R13_REG_NO };
+pub const R14_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 14 };
+pub const R15_REG: X86Reg = X86Reg { num_bits: 64, reg_type: RegType::GP, reg_no: 15 };
+
+pub const RAX: X86Opnd = X86Opnd::Reg(RAX_REG);
+pub const RCX: X86Opnd = X86Opnd::Reg(RCX_REG);
+pub const RDX: X86Opnd = X86Opnd::Reg(RDX_REG);
+pub const RBX: X86Opnd = X86Opnd::Reg(RBX_REG);
+pub const RSP: X86Opnd = X86Opnd::Reg(RSP_REG);
+pub const RBP: X86Opnd = X86Opnd::Reg(RBP_REG);
+pub const RSI: X86Opnd = X86Opnd::Reg(RSI_REG);
+pub const RDI: X86Opnd = X86Opnd::Reg(RDI_REG);
+pub const R8: X86Opnd = X86Opnd::Reg(R8_REG);
+pub const R9: X86Opnd = X86Opnd::Reg(R9_REG);
+pub const R10: X86Opnd = X86Opnd::Reg(R10_REG);
+pub const R11: X86Opnd = X86Opnd::Reg(R11_REG);
+pub const R12: X86Opnd = X86Opnd::Reg(R12_REG);
+pub const R13: X86Opnd = X86Opnd::Reg(R13_REG);
+pub const R14: X86Opnd = X86Opnd::Reg(R14_REG);
+pub const R15: X86Opnd = X86Opnd::Reg(R15_REG);
// 32-bit GP registers
pub const EAX: X86Opnd = X86Opnd::Reg(X86Reg { num_bits: 32, reg_type: RegType::GP, reg_no: 0 });
diff --git a/yjit/src/ir.rs b/yjit/src/backend/ir.rs
index 9a4fc559de..9cff4aeac9 100644
--- a/yjit/src/ir.rs
+++ b/yjit/src/backend/ir.rs
@@ -5,17 +5,22 @@
use std::convert::From;
use crate::cruby::{VALUE};
use crate::virtualmem::{CodePtr};
+use crate::asm::{CodeBlock};
use crate::asm::x86_64::{X86Opnd, X86Imm, X86UImm, X86Reg, X86Mem, RegType};
use crate::core::{Context, Type, TempMapping};
+#[cfg(target_arch = "x86_64")]
+use crate::backend::x86_64::*;
+
+//#[cfg(target_arch = "aarch64")]
+//use crate::backend:aarch64::*
+
/// Instruction opcodes
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Op
{
- // Add a comment into the IR at the point that this instruction is added. It
- // won't have any impact on that actual compiled code, but it will impact
- // the output of ir_print_insns. Accepts as its only operand an EIR_IMM
- // operand (typically generated by ir_str_ptr).
+ // Add a comment into the IR at the point that this instruction is added.
+ // It won't have any impact on that actual compiled code.
Comment,
// Add a label into the IR at the point that this instruction is added.
@@ -51,6 +56,9 @@ pub enum Op
// A low-level instruction that loads a value into a register.
Load,
+ // Low-level instruction to store a value to memory.
+ Store,
+
// A low-level mov instruction. It accepts two operands.
Mov,
@@ -170,32 +178,18 @@ pub enum Op
*/
}
-// Register value used by IR operands
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct Reg
-{
- // Register number/index
- reg_no: u8,
-
- // Size in bits
- num_bits: u8,
-
- // Special register flag EC/CFP/SP/SELF
- special: bool,
-}
-
// Memory location
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Mem
{
// Base register
- base: Reg,
+ pub(super) base: Reg,
// Offset relative to the base pointer
- disp: i32,
+ pub(super) disp: i32,
// Size in bits
- num_bits: u8,
+ pub(super) num_bits: u8,
}
/// Operand to an IR instruction
@@ -222,7 +216,7 @@ impl Opnd
pub fn mem(num_bits: u8, base: Opnd, disp: i32) -> Self {
match base {
Opnd::Reg(base_reg) => {
- assert!(base_reg.num_bits == 64 && !base_reg.special);
+ assert!(base_reg.num_bits == 64);
Opnd::Mem(Mem {
num_bits: num_bits,
base: base_reg,
@@ -234,12 +228,6 @@ impl Opnd
}
}
-// Special register constants
-pub const EC : Opnd = Opnd::Reg(Reg { reg_no: 0, num_bits: 64, special: true });
-pub const CFP : Opnd = Opnd::Reg(Reg { reg_no: 1, num_bits: 64, special: true });
-pub const SP : Opnd = Opnd::Reg(Reg { reg_no: 2, num_bits: 64, special: true });
-pub const SELF : Opnd = Opnd::Reg(Reg { reg_no: 3, num_bits: 64, special: true });
-
/// Method to convert from an X86Opnd to an IR Opnd
impl From<X86Opnd> for Opnd {
fn from(opnd: X86Opnd) -> Self {
@@ -249,17 +237,13 @@ impl From<X86Opnd> for Opnd {
X86Opnd::Imm(X86Imm{ value, .. }) => Opnd::Imm(value),
// General-purpose register
- X86Opnd::Reg(X86Reg{ num_bits, reg_no, reg_type: RegType::GP }) => {
- Opnd::Reg(Reg {
- reg_no,
- num_bits,
- special: false,
- })
+ X86Opnd::Reg(reg) => {
+ Opnd::Reg(reg)
}
// Memory operand with displacement
X86Opnd::Mem(X86Mem{ num_bits, base_reg_no, disp, idx_reg_no: None, scale_exp: 0 }) => {
- let base_reg = Reg { num_bits: 64, reg_no: base_reg_no, special: false };
+ let base_reg = Reg { num_bits: 64, reg_no: base_reg_no, reg_type: RegType::GP };
Opnd::Mem(Mem {
base: base_reg,
@@ -273,15 +257,10 @@ impl From<X86Opnd> for Opnd {
}
}
-
-
-
-
-
/// Branch target (something that we can jump to)
/// for branch instructions
#[derive(Clone, PartialEq, Eq, Debug)]
-enum Target
+pub enum Target
{
CodePtr(CodePtr), // Pointer to a piece of code (e.g. side-exit)
LabelName(String), // A label without an index in the output
@@ -293,30 +272,30 @@ enum Target
pub struct Insn
{
// Opcode for the instruction
- op: Op,
+ pub(super) op: Op,
// Optional string for comments and labels
- text: Option<String>,
+ pub(super) text: Option<String>,
// List of input operands/values
- opnds: Vec<Opnd>,
+ pub(super) opnds: Vec<Opnd>,
// Output operand for this instruction
- out: Opnd,
+ pub(super) out: Opnd,
// List of branch targets (branch instructions only)
- target: Option<Target>,
+ pub(super) target: Option<Target>,
// Position in the generated machine code
// Useful for comments and for patching jumps
- pos: Option<CodePtr>,
+ pub(super) pos: Option<CodePtr>,
}
/// Object into which we assemble instructions to be
/// optimized and lowered
-struct Assembler
+pub struct Assembler
{
- insns: Vec<Insn>,
+ pub(super) insns: Vec<Insn>,
/// Parallel vec with insns
/// Index of the last insn using the output of this insn
@@ -450,7 +429,7 @@ impl Assembler
Op::Add | Op::Sub | Op::Mov => {
match opnds.as_slice() {
[Opnd::Mem(_), Opnd::Mem(_)] => {
- let output = asm.push_insn(Op::Load, vec![opnds[0]], None);
+ let output = asm.load(opnds[0]);
asm.push_insn(op, vec![output, opnds[1]], None);
},
_ => {
@@ -534,19 +513,18 @@ impl Assembler
}
// Optimize and compile the stored instructions
- fn compile(self, regs: Vec<Reg>) -> Assembler
+ fn compile(self, cb: &mut CodeBlock)
{
- self.split_insns().alloc_regs(regs)
+ // NOTE: for arm we're going to want to split loads but also stores
+ // This can be done in a platform-agnostic way, but the set of passes
+ // we run will be slightly different.
- // TODO: splitting pass, split_insns()
+ let scratch_regs = Self::get_scrach_regs();
- // Peephole optimizations
- // Register allocation
- // Generic lowering pass
- // Platform-specific lowering
-
- // Question: should this method return machine code?
- // How do we go from lowered/optimized insn to an array of bytes?
+ self
+ .split_insns()
+ .alloc_regs(scratch_regs)
+ .target_emit(cb)
}
}
@@ -564,6 +542,18 @@ impl Assembler
}
}
+macro_rules! def_push_1_opnd {
+ ($op_name:ident, $opcode:expr) => {
+ impl Assembler
+ {
+ fn $op_name(&mut self, opnd0: Opnd) -> Opnd
+ {
+ self.push_insn($opcode, vec![opnd0], None)
+ }
+ }
+ };
+}
+
macro_rules! def_push_2_opnd {
($op_name:ident, $opcode:expr) => {
impl Assembler
@@ -591,6 +581,7 @@ macro_rules! def_push_2_opnd_no_out {
def_push_2_opnd!(add, Op::Add);
def_push_2_opnd!(sub, Op::Sub);
def_push_2_opnd!(and, Op::And);
+def_push_1_opnd!(load, Op::Load);
def_push_2_opnd_no_out!(mov, Op::Mov);
def_push_2_opnd_no_out!(cmp, Op::Cmp);
def_push_2_opnd_no_out!(test, Op::Test);
@@ -660,12 +651,11 @@ mod tests {
fn test_split_insns() {
let mut asm = Assembler::new();
- let reg1 = Reg { reg_no: 0, num_bits: 64, special: false };
- let reg2 = Reg { reg_no: 1, num_bits: 64, special: false };
+ let regs = Assembler::get_scrach_regs();
asm.add(
- Opnd::mem(64, Opnd::Reg(reg1), 0),
- Opnd::mem(64, Opnd::Reg(reg2), 0)
+ Opnd::mem(64, Opnd::Reg(regs[0]), 0),
+ Opnd::mem(64, Opnd::Reg(regs[1]), 0)
);
let result = asm.split_insns();
@@ -698,14 +688,22 @@ mod tests {
asm.add(out3, Opnd::UImm(6));
// Here we're going to allocate the registers.
- let reg1 = Reg { reg_no: 0, num_bits: 64, special: false };
- let reg2 = Reg { reg_no: 1, num_bits: 64, special: false };
- let result = asm.alloc_regs(vec![reg1, reg2]);
+ let result = asm.alloc_regs(Assembler::get_scrach_regs());
// Now we're going to verify that the out field has been appropriately
// updated for each of the instructions that needs it.
- assert_eq!(result.insns[0].out, Opnd::Reg(reg1));
- assert_eq!(result.insns[2].out, Opnd::Reg(reg2));
- assert_eq!(result.insns[5].out, Opnd::Reg(reg1));
+ let regs = Assembler::get_scrach_regs();
+ assert_eq!(result.insns[0].out, Opnd::Reg(regs[0]));
+ assert_eq!(result.insns[2].out, Opnd::Reg(regs[1]));
+ assert_eq!(result.insns[5].out, Opnd::Reg(regs[0]));
+ }
+
+ #[test]
+ fn test_compile()
+ {
+ // TODO: test full compile pipeline
+
+
+
}
}
diff --git a/yjit/src/backend/mod.rs b/yjit/src/backend/mod.rs
new file mode 100644
index 0000000000..a83fe4f69e
--- /dev/null
+++ b/yjit/src/backend/mod.rs
@@ -0,0 +1,3 @@
+pub mod x86_64;
+
+pub mod ir; \ No newline at end of file
diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs
new file mode 100644
index 0000000000..257373e86f
--- /dev/null
+++ b/yjit/src/backend/x86_64/mod.rs
@@ -0,0 +1,55 @@
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_imports)]
+
+use crate::asm::{CodeBlock};
+use crate::asm::x86_64::*;
+use crate::backend::ir::*;
+
+// Use the x86 register type for this platform
+pub type Reg = X86Reg;
+
+// Callee-saved registers
+pub const CFP: Opnd = Opnd::Reg(R13_REG);
+pub const EC: Opnd = Opnd::Reg(R12_REG);
+pub const SP: Opnd = Opnd::Reg(RBX_REG);
+
+impl Assembler
+{
+ // Get the list of registers from which we can allocate on this platform
+ pub fn get_scrach_regs() -> Vec<Reg>
+ {
+ vec![
+ RAX_REG,
+ RCX_REG,
+ ]
+ }
+
+ // Emit platform-specific machine code
+ pub fn target_emit(&self, cb: &mut CodeBlock)
+ {
+
+
+
+ for insn in &self.insns {
+
+
+ // For each instruction, either handle it here or allow the map_insn
+ // callback to handle it.
+ match insn.op {
+ Op::Comment => {
+ },
+ Op::Label => {
+ },
+ _ => {
+ }
+ };
+
+
+ }
+
+
+
+
+ }
+}
diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs
index 119477f505..67d3ecd573 100644
--- a/yjit/src/codegen.rs
+++ b/yjit/src/codegen.rs
@@ -33,7 +33,6 @@ pub const REG0: X86Opnd = RAX;
pub const REG0_32: X86Opnd = EAX;
pub const REG0_8: X86Opnd = AL;
pub const REG1: X86Opnd = RCX;
-// pub const REG1_32: X86Opnd = ECX;
// A block that can be invalidated needs space to write a jump.
// We'll reserve a minimum size for any block that could
diff --git a/yjit/src/lib.rs b/yjit/src/lib.rs
index 019189e8e8..752b7872c1 100644
--- a/yjit/src/lib.rs
+++ b/yjit/src/lib.rs
@@ -4,7 +4,7 @@
#![allow(clippy::identity_op)] // Sometimes we do it for style
mod asm;
-mod ir;
+mod backend;
mod codegen;
mod core;
mod cruby;