ruby-changes:73098
From: Maxime <ko1@a...>
Date: Tue, 30 Aug 2022 00:45:19 +0900 (JST)
Subject: [ruby-changes:73098] 2ffaa377c2 (master): WIP backend IR sketch
https://git.ruby-lang.org/ruby.git/commit/?id=2ffaa377c2 From 2ffaa377c212279e4a8bf1da8ac65a00dcdadd53 Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@s...> Date: Wed, 11 May 2022 15:17:44 -0400 Subject: WIP backend IR sketch --- yjit/src/asm/x86_64/mod.rs | 24 +-- yjit/src/ir.rs | 479 +++++++++++++++++++++++++++++++++++++++++++++ yjit/src/lib.rs | 1 + 3 files changed, 492 insertions(+), 12 deletions(-) create mode 100644 yjit/src/ir.rs diff --git a/yjit/src/asm/x86_64/mod.rs b/yjit/src/asm/x86_64/mod.rs index 6eb7efaa0a..b4ef2e4bf9 100644 --- a/yjit/src/asm/x86_64/mod.rs +++ b/yjit/src/asm/x86_64/mod.rs @@ -9,20 +9,20 @@ mod tests; https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/x86_64/mod.rs#L9 pub struct X86Imm { // Size in bits - num_bits: u8, + pub num_bits: u8, // The value of the immediate - value: i64 + pub value: i64 } #[derive(Clone, Copy, Debug)] pub struct X86UImm { // Size in bits - num_bits: u8, + pub num_bits: u8, // The value of the immediate - value: u64 + pub value: u64 } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -38,32 +38,32 @@ pub enum RegType https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/x86_64/mod.rs#L38 pub struct X86Reg { // Size in bits - num_bits: u8, + pub num_bits: u8, // Register type - reg_type: RegType, + pub reg_type: RegType, // Register index number - reg_no: u8, + pub reg_no: u8, } #[derive(Clone, Copy, Debug)] pub struct X86Mem { // Size in bits - num_bits: u8, + pub num_bits: u8, /// Base register number - base_reg_no: u8, + pub base_reg_no: u8, /// Index register number - idx_reg_no: Option<u8>, + pub idx_reg_no: Option<u8>, /// SIB scale exponent value (power of two, two bits) - scale_exp: u8, + pub scale_exp: u8, /// Constant displacement from the base, not scaled - disp: i32, + pub disp: i32, } #[derive(Clone, Copy, Debug)] diff --git a/yjit/src/ir.rs b/yjit/src/ir.rs new file mode 100644 index 0000000000..a20a982493 --- /dev/null +++ b/yjit/src/ir.rs @@ -0,0 +1,479 @@ https://github.com/ruby/ruby/blob/trunk/yjit/src/ir.rs#L1 +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_imports)] + +use std::convert::From; +use crate::cruby::{VALUE}; +use crate::virtualmem::{CodePtr}; +use crate::asm::x86_64::{X86Opnd, X86Imm, X86UImm, X86Reg, X86Mem, RegType}; +use crate::core::{Context, Type, TempMapping}; + + + + +/* +// Minimally, we might want to specify how many operands and branch targets an insn has +// Branch targets are not interchangeable with other operand types. We distinguish +// between branch and regular instructions. +// +// TODO: should mark instructions that produce no output operand +// +make_ops! { + (Comment, 1, 0), + ... + + // Call is variadic, might need to be special-cased +} +*/ + + + + + + + + + +/// 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). + Comment, + + // Add a label into the IR at the point that this instruction is added. It + // will eventually be translated into an offset when generating code such + // that EIR_LABEL_IDX operands know where to jump to. Accepts as its only + // operand an EIR_LABEL_NAME operand (typically generated by ir_label_opnd). + Label, + + // Add two operands together, and return the result as a new operand. This + // operand can then be used as the operand on another instruction. It + // accepts two operands, which can be of any type + // + // Under the hood when allocating registers, the IR will determine the most + // efficient way to get these values into memory. For example, if both + // operands are immediates, then it will load the first one into a register + // first with a mov instruction and then add them together. If one of them + // is a register, however, it will just perform a single add instruction. + Add, + + // This is the same as the OP_ADD instruction, except for subtraction. + Sub, + + // This is the same as the OP_ADD instruction, except that it performs the + // binary AND operation. + And, + + // Perform the NOT operation on an individual operand, and return the result + // as a new operand. This operand can then be used as the operand on another + // instruction. + Not, + + // + // Low-level instructions + // + + // A low-level mov instruction. It accepts two operands. + Mov, + + // Bitwise AND test instruction + Test, + + // Jump if not zero + Jnz, + + /* + // The following are conditional jump instructions. They all accept as their + // first operand an EIR_LABEL_NAME, which is used as the target of the jump. + // + // The OP_JUMP_EQ instruction accepts two additional operands, to be + // compared for equality. If they're equal, then the generated code jumps to + // the target label. If they're not, then it continues on to the next + // instruction. + JumpEq, + + // The OP_JUMP_NE instruction is very similar to the OP_JUMP_EQ instruction, + // except it compares for inequality instead. + JumpNe, + + // Checks the overflow flag and conditionally jumps to the target if it is + // currently set. + JumpOvf, + + // A low-level call instruction for calling a function by a pointer. It + // accepts one operand of type EIR_IMM that should be a pointer to the + // function. Usually this is done by first casting the function to a void*, + // as in: ir_const_ptr((void *)&my_function)). + Call, + + // Calls a function by a pointer and returns an operand that contains the + // result of the function. Accepts as its operands a pointer to a function + // of type EIR_IMM (usually generated from ir_const_ptr) and a variable + // number of arguments to the function being called. + // + // This is the higher-level instruction that should be used when you want to + // call a function with arguments, as opposed to OP_CALL which is + // lower-level and just calls a function without moving arguments into + // registers for you. + CCall, + + // Returns from the function being generated immediately. This is different + // from OP_RETVAL in that it does nothing with the return value register + // (whatever is in there is what will get returned). Accepts no operands. + Ret, + + // First, moves a value into the return value register. Then, returns from + // the generated function. Accepts as its only operand the value that should + // be returned from the generated function. + RetVal, + + // A low-level cmp instruction. It accepts two operands. The first it + // expects to be a register. The second can be anything. Most of the time + // this instruction shouldn't be used by the developer since other + // instructions break down to this one. + Cmp, + + // A conditional move instruction that should be preceeded at some point by + // an OP_CMP instruction that would have set the requisite comparison flags. + // Accepts 2 operands, both of which are expected to be of the EIR_REG type. + // + // If the comparison indicates the left compared value is greater than or + // equal to the right compared value, then the conditional move is executed, + // otherwise we just continue on to the next instruction. + // + // This is considered a low-level instruction, and the OP_SELECT_* variants + // should be preferred if possible. + CMovGE, + + // The same as OP_CMOV_GE, except the comparison is greater than. + CMovGT, + + // The same as OP_CMOV_GE, except the comparison is less than or equal. + CMovLE, + + // The same as OP_CMOV_GE, except the comparison is less than. + CMovLT, + + // Selects between two different values based on a comparison of two other + // values. Accepts 4 operands. The first two are the basis of the + // comparison. The second two are the "then" case and the "else" case. You + // can effectively think of this instruction as a ternary operation, where + // the first two values are being compared. + // + // OP_SELECT_GE performs the described ternary using a greater than or equal + // comparison, that is if the first operand is greater than or equal to the + // second operand. + SelectGE, + + // The same as OP_SELECT_GE, except the comparison is greater than. + SelectGT, + + // The same as OP_SELECT_GE, except the comparison is less than or equal. + SelectLE, + + // The same as OP_SELECT_GE, except the comparison is less than. + SelectLT, + + // For later: + // These encode Ruby true/false semantics + // Can be used to enable op fusion of Ruby compare + branch. + // OP_JUMP_TRUE, // (opnd, target) + // OP_JUMP_FALSE, // (opnd, target) + + // For later: + // OP_GUARD_HEAP, // (opnd, target) + // OP_GUARD_IMM, // (opnd, target) + // OP_GUARD_FIXNUM, // (opnd, target) + + // For later: + // OP_COUNTER_INC, (counter_name) + + // For later: + // OP_LEA, + // OP_TEST, + */ +} + + + + + + + + + + +// 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, + + // Offset relative to the base pointer + disp: i32, + + // Size in bits + num_bits: u8, +} + +/// Operand to an I (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/