Remove the old riscv backend
This commit is contained in:
@@ -63,7 +63,6 @@ unwind = ["gimli"]
|
|||||||
# If no ISA targets are explicitly enabled, the ISA target for the host machine is enabled.
|
# If no ISA targets are explicitly enabled, the ISA target for the host machine is enabled.
|
||||||
x86 = []
|
x86 = []
|
||||||
arm64 = []
|
arm64 = []
|
||||||
riscv = []
|
|
||||||
s390x = []
|
s390x = []
|
||||||
arm32 = [] # Work-in-progress codegen backend for ARM.
|
arm32 = [] # Work-in-progress codegen backend for ARM.
|
||||||
|
|
||||||
@@ -75,7 +74,6 @@ experimental_x64 = []
|
|||||||
all-arch = [
|
all-arch = [
|
||||||
"x86",
|
"x86",
|
||||||
"arm64",
|
"arm64",
|
||||||
"riscv",
|
|
||||||
"s390x"
|
"s390x"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,12 @@ use std::fmt;
|
|||||||
|
|
||||||
mod arm32;
|
mod arm32;
|
||||||
mod arm64;
|
mod arm64;
|
||||||
mod riscv;
|
|
||||||
mod s390x;
|
mod s390x;
|
||||||
pub(crate) mod x86;
|
pub(crate) mod x86;
|
||||||
|
|
||||||
/// Represents known ISA target.
|
/// Represents known ISA target.
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
pub enum Isa {
|
pub enum Isa {
|
||||||
Riscv,
|
|
||||||
X86,
|
X86,
|
||||||
Arm32,
|
Arm32,
|
||||||
Arm64,
|
Arm64,
|
||||||
@@ -31,7 +29,6 @@ impl Isa {
|
|||||||
/// Creates isa target from arch.
|
/// Creates isa target from arch.
|
||||||
pub fn from_arch(arch: &str) -> Option<Self> {
|
pub fn from_arch(arch: &str) -> Option<Self> {
|
||||||
match arch {
|
match arch {
|
||||||
"riscv" => Some(Isa::Riscv),
|
|
||||||
"aarch64" => Some(Isa::Arm64),
|
"aarch64" => Some(Isa::Arm64),
|
||||||
"s390x" => Some(Isa::S390x),
|
"s390x" => Some(Isa::S390x),
|
||||||
x if ["x86_64", "i386", "i586", "i686"].contains(&x) => Some(Isa::X86),
|
x if ["x86_64", "i386", "i586", "i686"].contains(&x) => Some(Isa::X86),
|
||||||
@@ -42,7 +39,7 @@ impl Isa {
|
|||||||
|
|
||||||
/// Returns all supported isa targets.
|
/// Returns all supported isa targets.
|
||||||
pub fn all() -> &'static [Isa] {
|
pub fn all() -> &'static [Isa] {
|
||||||
&[Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64, Isa::S390x]
|
&[Isa::X86, Isa::Arm32, Isa::Arm64, Isa::S390x]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +47,6 @@ impl fmt::Display for Isa {
|
|||||||
// These names should be kept in sync with the crate features.
|
// These names should be kept in sync with the crate features.
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Isa::Riscv => write!(f, "riscv"),
|
|
||||||
Isa::X86 => write!(f, "x86"),
|
Isa::X86 => write!(f, "x86"),
|
||||||
Isa::Arm32 => write!(f, "arm32"),
|
Isa::Arm32 => write!(f, "arm32"),
|
||||||
Isa::Arm64 => write!(f, "arm64"),
|
Isa::Arm64 => write!(f, "arm64"),
|
||||||
@@ -62,7 +58,6 @@ impl fmt::Display for Isa {
|
|||||||
pub(crate) fn define(isas: &[Isa], shared_defs: &mut SharedDefinitions) -> Vec<TargetIsa> {
|
pub(crate) fn define(isas: &[Isa], shared_defs: &mut SharedDefinitions) -> Vec<TargetIsa> {
|
||||||
isas.iter()
|
isas.iter()
|
||||||
.map(|isa| match isa {
|
.map(|isa| match isa {
|
||||||
Isa::Riscv => riscv::define(shared_defs),
|
|
||||||
Isa::X86 => x86::define(shared_defs),
|
Isa::X86 => x86::define(shared_defs),
|
||||||
Isa::Arm32 => arm32::define(shared_defs),
|
Isa::Arm32 => arm32::define(shared_defs),
|
||||||
Isa::Arm64 => arm64::define(shared_defs),
|
Isa::Arm64 => arm64::define(shared_defs),
|
||||||
|
|||||||
@@ -1,431 +0,0 @@
|
|||||||
use crate::cdsl::ast::{Apply, Expr, Literal, VarPool};
|
|
||||||
use crate::cdsl::encodings::{Encoding, EncodingBuilder};
|
|
||||||
use crate::cdsl::instructions::{
|
|
||||||
Bindable, BoundInstruction, InstSpec, InstructionPredicateNode, InstructionPredicateRegistry,
|
|
||||||
};
|
|
||||||
use crate::cdsl::recipes::{EncodingRecipeNumber, Recipes};
|
|
||||||
use crate::cdsl::settings::SettingGroup;
|
|
||||||
|
|
||||||
use crate::shared::types::Bool::B1;
|
|
||||||
use crate::shared::types::Float::{F32, F64};
|
|
||||||
use crate::shared::types::Int::{I16, I32, I64, I8};
|
|
||||||
use crate::shared::types::Reference::{R32, R64};
|
|
||||||
use crate::shared::Definitions as SharedDefinitions;
|
|
||||||
|
|
||||||
use super::recipes::RecipeGroup;
|
|
||||||
|
|
||||||
pub(crate) struct PerCpuModeEncodings<'defs> {
|
|
||||||
pub inst_pred_reg: InstructionPredicateRegistry,
|
|
||||||
pub enc32: Vec<Encoding>,
|
|
||||||
pub enc64: Vec<Encoding>,
|
|
||||||
recipes: &'defs Recipes,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'defs> PerCpuModeEncodings<'defs> {
|
|
||||||
fn new(recipes: &'defs Recipes) -> Self {
|
|
||||||
Self {
|
|
||||||
inst_pred_reg: InstructionPredicateRegistry::new(),
|
|
||||||
enc32: Vec::new(),
|
|
||||||
enc64: Vec::new(),
|
|
||||||
recipes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn enc(
|
|
||||||
&self,
|
|
||||||
inst: impl Into<InstSpec>,
|
|
||||||
recipe: EncodingRecipeNumber,
|
|
||||||
bits: u16,
|
|
||||||
) -> EncodingBuilder {
|
|
||||||
EncodingBuilder::new(inst.into(), recipe, bits)
|
|
||||||
}
|
|
||||||
fn add32(&mut self, encoding: EncodingBuilder) {
|
|
||||||
self.enc32
|
|
||||||
.push(encoding.build(self.recipes, &mut self.inst_pred_reg));
|
|
||||||
}
|
|
||||||
fn add64(&mut self, encoding: EncodingBuilder) {
|
|
||||||
self.enc64
|
|
||||||
.push(encoding.build(self.recipes, &mut self.inst_pred_reg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The low 7 bits of a RISC-V instruction is the base opcode. All 32-bit instructions have 11 as
|
|
||||||
// the two low bits, with bits 6:2 determining the base opcode.
|
|
||||||
//
|
|
||||||
// Encbits for the 32-bit recipes are opcode[6:2] | (funct3 << 5) | ...
|
|
||||||
// The functions below encode the encbits.
|
|
||||||
|
|
||||||
fn load_bits(funct3: u16) -> u16 {
|
|
||||||
assert!(funct3 <= 0b111);
|
|
||||||
funct3 << 5
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_bits(funct3: u16) -> u16 {
|
|
||||||
assert!(funct3 <= 0b111);
|
|
||||||
0b01000 | (funct3 << 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn branch_bits(funct3: u16) -> u16 {
|
|
||||||
assert!(funct3 <= 0b111);
|
|
||||||
0b11000 | (funct3 << 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jalr_bits() -> u16 {
|
|
||||||
// This was previously accepting an argument funct3 of 3 bits and used the following formula:
|
|
||||||
//0b11001 | (funct3 << 5)
|
|
||||||
0b11001
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jal_bits() -> u16 {
|
|
||||||
0b11011
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opimm_bits(funct3: u16, funct7: u16) -> u16 {
|
|
||||||
assert!(funct3 <= 0b111);
|
|
||||||
0b00100 | (funct3 << 5) | (funct7 << 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opimm32_bits(funct3: u16, funct7: u16) -> u16 {
|
|
||||||
assert!(funct3 <= 0b111);
|
|
||||||
0b00110 | (funct3 << 5) | (funct7 << 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn op_bits(funct3: u16, funct7: u16) -> u16 {
|
|
||||||
assert!(funct3 <= 0b111);
|
|
||||||
assert!(funct7 <= 0b111_1111);
|
|
||||||
0b01100 | (funct3 << 5) | (funct7 << 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn op32_bits(funct3: u16, funct7: u16) -> u16 {
|
|
||||||
assert!(funct3 <= 0b111);
|
|
||||||
assert!(funct7 <= 0b111_1111);
|
|
||||||
0b01110 | (funct3 << 5) | (funct7 << 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lui_bits() -> u16 {
|
|
||||||
0b01101
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn define<'defs>(
|
|
||||||
shared_defs: &'defs SharedDefinitions,
|
|
||||||
isa_settings: &SettingGroup,
|
|
||||||
recipes: &'defs RecipeGroup,
|
|
||||||
) -> PerCpuModeEncodings<'defs> {
|
|
||||||
// Instructions shorthands.
|
|
||||||
let shared = &shared_defs.instructions;
|
|
||||||
|
|
||||||
let band = shared.by_name("band");
|
|
||||||
let band_imm = shared.by_name("band_imm");
|
|
||||||
let bor = shared.by_name("bor");
|
|
||||||
let bor_imm = shared.by_name("bor_imm");
|
|
||||||
let br_icmp = shared.by_name("br_icmp");
|
|
||||||
let brz = shared.by_name("brz");
|
|
||||||
let brnz = shared.by_name("brnz");
|
|
||||||
let bxor = shared.by_name("bxor");
|
|
||||||
let bxor_imm = shared.by_name("bxor_imm");
|
|
||||||
let call = shared.by_name("call");
|
|
||||||
let call_indirect = shared.by_name("call_indirect");
|
|
||||||
let copy = shared.by_name("copy");
|
|
||||||
let copy_nop = shared.by_name("copy_nop");
|
|
||||||
let copy_to_ssa = shared.by_name("copy_to_ssa");
|
|
||||||
let fill = shared.by_name("fill");
|
|
||||||
let fill_nop = shared.by_name("fill_nop");
|
|
||||||
let iadd = shared.by_name("iadd");
|
|
||||||
let iadd_imm = shared.by_name("iadd_imm");
|
|
||||||
let iconst = shared.by_name("iconst");
|
|
||||||
let icmp = shared.by_name("icmp");
|
|
||||||
let icmp_imm = shared.by_name("icmp_imm");
|
|
||||||
let imul = shared.by_name("imul");
|
|
||||||
let ishl = shared.by_name("ishl");
|
|
||||||
let ishl_imm = shared.by_name("ishl_imm");
|
|
||||||
let isub = shared.by_name("isub");
|
|
||||||
let jump = shared.by_name("jump");
|
|
||||||
let regmove = shared.by_name("regmove");
|
|
||||||
let spill = shared.by_name("spill");
|
|
||||||
let sshr = shared.by_name("sshr");
|
|
||||||
let sshr_imm = shared.by_name("sshr_imm");
|
|
||||||
let ushr = shared.by_name("ushr");
|
|
||||||
let ushr_imm = shared.by_name("ushr_imm");
|
|
||||||
let return_ = shared.by_name("return");
|
|
||||||
|
|
||||||
// Recipes shorthands, prefixed with r_.
|
|
||||||
let r_copytossa = recipes.by_name("copytossa");
|
|
||||||
let r_fillnull = recipes.by_name("fillnull");
|
|
||||||
let r_icall = recipes.by_name("Icall");
|
|
||||||
let r_icopy = recipes.by_name("Icopy");
|
|
||||||
let r_ii = recipes.by_name("Ii");
|
|
||||||
let r_iicmp = recipes.by_name("Iicmp");
|
|
||||||
let r_iret = recipes.by_name("Iret");
|
|
||||||
let r_irmov = recipes.by_name("Irmov");
|
|
||||||
let r_iz = recipes.by_name("Iz");
|
|
||||||
let r_gp_sp = recipes.by_name("GPsp");
|
|
||||||
let r_gp_fi = recipes.by_name("GPfi");
|
|
||||||
let r_r = recipes.by_name("R");
|
|
||||||
let r_ricmp = recipes.by_name("Ricmp");
|
|
||||||
let r_rshamt = recipes.by_name("Rshamt");
|
|
||||||
let r_sb = recipes.by_name("SB");
|
|
||||||
let r_sb_zero = recipes.by_name("SBzero");
|
|
||||||
let r_stacknull = recipes.by_name("stacknull");
|
|
||||||
let r_u = recipes.by_name("U");
|
|
||||||
let r_uj = recipes.by_name("UJ");
|
|
||||||
let r_uj_call = recipes.by_name("UJcall");
|
|
||||||
|
|
||||||
// Predicates shorthands.
|
|
||||||
let use_m = isa_settings.predicate_by_name("use_m");
|
|
||||||
|
|
||||||
// Definitions.
|
|
||||||
let mut e = PerCpuModeEncodings::new(&recipes.recipes);
|
|
||||||
|
|
||||||
// Basic arithmetic binary instructions are encoded in an R-type instruction.
|
|
||||||
for &(inst, inst_imm, f3, f7) in &[
|
|
||||||
(iadd, Some(iadd_imm), 0b000, 0b000_0000),
|
|
||||||
(isub, None, 0b000, 0b010_0000),
|
|
||||||
(bxor, Some(bxor_imm), 0b100, 0b000_0000),
|
|
||||||
(bor, Some(bor_imm), 0b110, 0b000_0000),
|
|
||||||
(band, Some(band_imm), 0b111, 0b000_0000),
|
|
||||||
] {
|
|
||||||
e.add32(e.enc(inst.bind(I32), r_r, op_bits(f3, f7)));
|
|
||||||
e.add64(e.enc(inst.bind(I64), r_r, op_bits(f3, f7)));
|
|
||||||
|
|
||||||
// Immediate versions for add/xor/or/and.
|
|
||||||
if let Some(inst_imm) = inst_imm {
|
|
||||||
e.add32(e.enc(inst_imm.bind(I32), r_ii, opimm_bits(f3, 0)));
|
|
||||||
e.add64(e.enc(inst_imm.bind(I64), r_ii, opimm_bits(f3, 0)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 32-bit ops in RV64.
|
|
||||||
e.add64(e.enc(iadd.bind(I32), r_r, op32_bits(0b000, 0b000_0000)));
|
|
||||||
e.add64(e.enc(isub.bind(I32), r_r, op32_bits(0b000, 0b010_0000)));
|
|
||||||
// There are no andiw/oriw/xoriw variations.
|
|
||||||
e.add64(e.enc(iadd_imm.bind(I32), r_ii, opimm32_bits(0b000, 0)));
|
|
||||||
|
|
||||||
// Use iadd_imm with %x0 to materialize constants.
|
|
||||||
e.add32(e.enc(iconst.bind(I32), r_iz, opimm_bits(0b0, 0)));
|
|
||||||
e.add64(e.enc(iconst.bind(I32), r_iz, opimm_bits(0b0, 0)));
|
|
||||||
e.add64(e.enc(iconst.bind(I64), r_iz, opimm_bits(0b0, 0)));
|
|
||||||
|
|
||||||
// Dynamic shifts have the same masking semantics as the clif base instructions.
|
|
||||||
for &(inst, inst_imm, f3, f7) in &[
|
|
||||||
(ishl, ishl_imm, 0b1, 0b0),
|
|
||||||
(ushr, ushr_imm, 0b101, 0b0),
|
|
||||||
(sshr, sshr_imm, 0b101, 0b10_0000),
|
|
||||||
] {
|
|
||||||
e.add32(e.enc(inst.bind(I32).bind(I32), r_r, op_bits(f3, f7)));
|
|
||||||
e.add64(e.enc(inst.bind(I64).bind(I64), r_r, op_bits(f3, f7)));
|
|
||||||
e.add64(e.enc(inst.bind(I32).bind(I32), r_r, op32_bits(f3, f7)));
|
|
||||||
// Allow i32 shift amounts in 64-bit shifts.
|
|
||||||
e.add64(e.enc(inst.bind(I64).bind(I32), r_r, op_bits(f3, f7)));
|
|
||||||
e.add64(e.enc(inst.bind(I32).bind(I64), r_r, op32_bits(f3, f7)));
|
|
||||||
|
|
||||||
// Immediate shifts.
|
|
||||||
e.add32(e.enc(inst_imm.bind(I32), r_rshamt, opimm_bits(f3, f7)));
|
|
||||||
e.add64(e.enc(inst_imm.bind(I64), r_rshamt, opimm_bits(f3, f7)));
|
|
||||||
e.add64(e.enc(inst_imm.bind(I32), r_rshamt, opimm32_bits(f3, f7)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signed and unsigned integer 'less than'. There are no 'w' variants for comparing 32-bit
|
|
||||||
// numbers in RV64.
|
|
||||||
{
|
|
||||||
let mut var_pool = VarPool::new();
|
|
||||||
|
|
||||||
// Helper that creates an instruction predicate for an instruction in the icmp family.
|
|
||||||
let mut icmp_instp = |bound_inst: &BoundInstruction,
|
|
||||||
intcc_field: &'static str|
|
|
||||||
-> InstructionPredicateNode {
|
|
||||||
let x = var_pool.create("x");
|
|
||||||
let y = var_pool.create("y");
|
|
||||||
let cc = Literal::enumerator_for(&shared_defs.imm.intcc, intcc_field);
|
|
||||||
Apply::new(
|
|
||||||
bound_inst.clone().into(),
|
|
||||||
vec![Expr::Literal(cc), Expr::Var(x), Expr::Var(y)],
|
|
||||||
)
|
|
||||||
.inst_predicate(&var_pool)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let icmp_i32 = icmp.bind(I32);
|
|
||||||
let icmp_i64 = icmp.bind(I64);
|
|
||||||
e.add32(
|
|
||||||
e.enc(icmp_i32.clone(), r_ricmp, op_bits(0b010, 0b000_0000))
|
|
||||||
.inst_predicate(icmp_instp(&icmp_i32, "slt")),
|
|
||||||
);
|
|
||||||
e.add64(
|
|
||||||
e.enc(icmp_i64.clone(), r_ricmp, op_bits(0b010, 0b000_0000))
|
|
||||||
.inst_predicate(icmp_instp(&icmp_i64, "slt")),
|
|
||||||
);
|
|
||||||
|
|
||||||
e.add32(
|
|
||||||
e.enc(icmp_i32.clone(), r_ricmp, op_bits(0b011, 0b000_0000))
|
|
||||||
.inst_predicate(icmp_instp(&icmp_i32, "ult")),
|
|
||||||
);
|
|
||||||
e.add64(
|
|
||||||
e.enc(icmp_i64.clone(), r_ricmp, op_bits(0b011, 0b000_0000))
|
|
||||||
.inst_predicate(icmp_instp(&icmp_i64, "ult")),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Immediate variants.
|
|
||||||
let icmp_i32 = icmp_imm.bind(I32);
|
|
||||||
let icmp_i64 = icmp_imm.bind(I64);
|
|
||||||
e.add32(
|
|
||||||
e.enc(icmp_i32.clone(), r_iicmp, opimm_bits(0b010, 0))
|
|
||||||
.inst_predicate(icmp_instp(&icmp_i32, "slt")),
|
|
||||||
);
|
|
||||||
e.add64(
|
|
||||||
e.enc(icmp_i64.clone(), r_iicmp, opimm_bits(0b010, 0))
|
|
||||||
.inst_predicate(icmp_instp(&icmp_i64, "slt")),
|
|
||||||
);
|
|
||||||
|
|
||||||
e.add32(
|
|
||||||
e.enc(icmp_i32.clone(), r_iicmp, opimm_bits(0b011, 0))
|
|
||||||
.inst_predicate(icmp_instp(&icmp_i32, "ult")),
|
|
||||||
);
|
|
||||||
e.add64(
|
|
||||||
e.enc(icmp_i64.clone(), r_iicmp, opimm_bits(0b011, 0))
|
|
||||||
.inst_predicate(icmp_instp(&icmp_i64, "ult")),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integer constants with the low 12 bits clear are materialized by lui.
|
|
||||||
e.add32(e.enc(iconst.bind(I32), r_u, lui_bits()));
|
|
||||||
e.add64(e.enc(iconst.bind(I32), r_u, lui_bits()));
|
|
||||||
e.add64(e.enc(iconst.bind(I64), r_u, lui_bits()));
|
|
||||||
|
|
||||||
// "M" Standard Extension for Integer Multiplication and Division.
|
|
||||||
// Gated by the `use_m` flag.
|
|
||||||
e.add32(
|
|
||||||
e.enc(imul.bind(I32), r_r, op_bits(0b000, 0b0000_0001))
|
|
||||||
.isa_predicate(use_m),
|
|
||||||
);
|
|
||||||
e.add64(
|
|
||||||
e.enc(imul.bind(I64), r_r, op_bits(0b000, 0b0000_0001))
|
|
||||||
.isa_predicate(use_m),
|
|
||||||
);
|
|
||||||
e.add64(
|
|
||||||
e.enc(imul.bind(I32), r_r, op32_bits(0b000, 0b0000_0001))
|
|
||||||
.isa_predicate(use_m),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Control flow.
|
|
||||||
|
|
||||||
// Unconditional branches.
|
|
||||||
e.add32(e.enc(jump, r_uj, jal_bits()));
|
|
||||||
e.add64(e.enc(jump, r_uj, jal_bits()));
|
|
||||||
e.add32(e.enc(call, r_uj_call, jal_bits()));
|
|
||||||
e.add64(e.enc(call, r_uj_call, jal_bits()));
|
|
||||||
|
|
||||||
// Conditional branches.
|
|
||||||
{
|
|
||||||
let mut var_pool = VarPool::new();
|
|
||||||
|
|
||||||
// Helper that creates an instruction predicate for an instruction in the icmp family.
|
|
||||||
let mut br_icmp_instp = |bound_inst: &BoundInstruction,
|
|
||||||
intcc_field: &'static str|
|
|
||||||
-> InstructionPredicateNode {
|
|
||||||
let x = var_pool.create("x");
|
|
||||||
let y = var_pool.create("y");
|
|
||||||
let dest = var_pool.create("dest");
|
|
||||||
let args = var_pool.create("args");
|
|
||||||
let cc = Literal::enumerator_for(&shared_defs.imm.intcc, intcc_field);
|
|
||||||
Apply::new(
|
|
||||||
bound_inst.clone().into(),
|
|
||||||
vec![
|
|
||||||
Expr::Literal(cc),
|
|
||||||
Expr::Var(x),
|
|
||||||
Expr::Var(y),
|
|
||||||
Expr::Var(dest),
|
|
||||||
Expr::Var(args),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.inst_predicate(&var_pool)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let br_icmp_i32 = br_icmp.bind(I32);
|
|
||||||
let br_icmp_i64 = br_icmp.bind(I64);
|
|
||||||
for &(cond, f3) in &[
|
|
||||||
("eq", 0b000),
|
|
||||||
("ne", 0b001),
|
|
||||||
("slt", 0b100),
|
|
||||||
("sge", 0b101),
|
|
||||||
("ult", 0b110),
|
|
||||||
("uge", 0b111),
|
|
||||||
] {
|
|
||||||
e.add32(
|
|
||||||
e.enc(br_icmp_i32.clone(), r_sb, branch_bits(f3))
|
|
||||||
.inst_predicate(br_icmp_instp(&br_icmp_i32, cond)),
|
|
||||||
);
|
|
||||||
e.add64(
|
|
||||||
e.enc(br_icmp_i64.clone(), r_sb, branch_bits(f3))
|
|
||||||
.inst_predicate(br_icmp_instp(&br_icmp_i64, cond)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for &(inst, f3) in &[(brz, 0b000), (brnz, 0b001)] {
|
|
||||||
e.add32(e.enc(inst.bind(I32), r_sb_zero, branch_bits(f3)));
|
|
||||||
e.add64(e.enc(inst.bind(I64), r_sb_zero, branch_bits(f3)));
|
|
||||||
e.add32(e.enc(inst.bind(B1), r_sb_zero, branch_bits(f3)));
|
|
||||||
e.add64(e.enc(inst.bind(B1), r_sb_zero, branch_bits(f3)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns are a special case of jalr_bits using %x1 to hold the return address.
|
|
||||||
// The return address is provided by a special-purpose `link` return value that
|
|
||||||
// is added by legalize_signature().
|
|
||||||
e.add32(e.enc(return_, r_iret, jalr_bits()));
|
|
||||||
e.add64(e.enc(return_, r_iret, jalr_bits()));
|
|
||||||
e.add32(e.enc(call_indirect.bind(I32), r_icall, jalr_bits()));
|
|
||||||
e.add64(e.enc(call_indirect.bind(I64), r_icall, jalr_bits()));
|
|
||||||
|
|
||||||
// Spill and fill.
|
|
||||||
e.add32(e.enc(spill.bind(I32), r_gp_sp, store_bits(0b010)));
|
|
||||||
e.add64(e.enc(spill.bind(I32), r_gp_sp, store_bits(0b010)));
|
|
||||||
e.add64(e.enc(spill.bind(I64), r_gp_sp, store_bits(0b011)));
|
|
||||||
e.add32(e.enc(fill.bind(I32), r_gp_fi, load_bits(0b010)));
|
|
||||||
e.add64(e.enc(fill.bind(I32), r_gp_fi, load_bits(0b010)));
|
|
||||||
e.add64(e.enc(fill.bind(I64), r_gp_fi, load_bits(0b011)));
|
|
||||||
|
|
||||||
// No-op fills, created by late-stage redundant-fill removal.
|
|
||||||
for &ty in &[I64, I32] {
|
|
||||||
e.add64(e.enc(fill_nop.bind(ty), r_fillnull, 0));
|
|
||||||
e.add32(e.enc(fill_nop.bind(ty), r_fillnull, 0));
|
|
||||||
}
|
|
||||||
e.add64(e.enc(fill_nop.bind(B1), r_fillnull, 0));
|
|
||||||
e.add32(e.enc(fill_nop.bind(B1), r_fillnull, 0));
|
|
||||||
|
|
||||||
// Register copies.
|
|
||||||
e.add32(e.enc(copy.bind(I32), r_icopy, opimm_bits(0b000, 0)));
|
|
||||||
e.add64(e.enc(copy.bind(I64), r_icopy, opimm_bits(0b000, 0)));
|
|
||||||
e.add64(e.enc(copy.bind(I32), r_icopy, opimm32_bits(0b000, 0)));
|
|
||||||
|
|
||||||
e.add32(e.enc(regmove.bind(I32), r_irmov, opimm_bits(0b000, 0)));
|
|
||||||
e.add64(e.enc(regmove.bind(I64), r_irmov, opimm_bits(0b000, 0)));
|
|
||||||
e.add64(e.enc(regmove.bind(I32), r_irmov, opimm32_bits(0b000, 0)));
|
|
||||||
|
|
||||||
e.add32(e.enc(copy.bind(B1), r_icopy, opimm_bits(0b000, 0)));
|
|
||||||
e.add64(e.enc(copy.bind(B1), r_icopy, opimm_bits(0b000, 0)));
|
|
||||||
e.add32(e.enc(regmove.bind(B1), r_irmov, opimm_bits(0b000, 0)));
|
|
||||||
e.add64(e.enc(regmove.bind(B1), r_irmov, opimm_bits(0b000, 0)));
|
|
||||||
|
|
||||||
// Stack-slot-to-the-same-stack-slot copy, which is guaranteed to turn
|
|
||||||
// into a no-op.
|
|
||||||
// The same encoding is generated for both the 64- and 32-bit architectures.
|
|
||||||
for &ty in &[I64, I32, I16, I8] {
|
|
||||||
e.add32(e.enc(copy_nop.bind(ty), r_stacknull, 0));
|
|
||||||
e.add64(e.enc(copy_nop.bind(ty), r_stacknull, 0));
|
|
||||||
}
|
|
||||||
for &ty in &[F64, F32] {
|
|
||||||
e.add32(e.enc(copy_nop.bind(ty), r_stacknull, 0));
|
|
||||||
e.add64(e.enc(copy_nop.bind(ty), r_stacknull, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy-to-SSA
|
|
||||||
e.add32(e.enc(copy_to_ssa.bind(I32), r_copytossa, opimm_bits(0b000, 0)));
|
|
||||||
e.add64(e.enc(copy_to_ssa.bind(I64), r_copytossa, opimm_bits(0b000, 0)));
|
|
||||||
e.add64(e.enc(copy_to_ssa.bind(I32), r_copytossa, opimm32_bits(0b000, 0)));
|
|
||||||
e.add32(e.enc(copy_to_ssa.bind(B1), r_copytossa, opimm_bits(0b000, 0)));
|
|
||||||
e.add64(e.enc(copy_to_ssa.bind(B1), r_copytossa, opimm_bits(0b000, 0)));
|
|
||||||
e.add32(e.enc(copy_to_ssa.bind(R32), r_copytossa, opimm_bits(0b000, 0)));
|
|
||||||
e.add64(e.enc(copy_to_ssa.bind(R64), r_copytossa, opimm_bits(0b000, 0)));
|
|
||||||
|
|
||||||
e
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
use crate::cdsl::cpu_modes::CpuMode;
|
|
||||||
use crate::cdsl::isa::TargetIsa;
|
|
||||||
use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
|
|
||||||
use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
|
|
||||||
|
|
||||||
use crate::shared::types::Float::{F32, F64};
|
|
||||||
use crate::shared::types::Int::{I32, I64};
|
|
||||||
use crate::shared::Definitions as SharedDefinitions;
|
|
||||||
|
|
||||||
mod encodings;
|
|
||||||
mod recipes;
|
|
||||||
|
|
||||||
fn define_settings(shared: &SettingGroup) -> SettingGroup {
|
|
||||||
let mut setting = SettingGroupBuilder::new("riscv");
|
|
||||||
|
|
||||||
let supports_m = setting.add_bool(
|
|
||||||
"supports_m",
|
|
||||||
"CPU supports the 'M' extension (mul/div)",
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
let supports_a = setting.add_bool(
|
|
||||||
"supports_a",
|
|
||||||
"CPU supports the 'A' extension (atomics)",
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
let supports_f = setting.add_bool(
|
|
||||||
"supports_f",
|
|
||||||
"CPU supports the 'F' extension (float)",
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
let supports_d = setting.add_bool(
|
|
||||||
"supports_d",
|
|
||||||
"CPU supports the 'D' extension (double)",
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
let enable_m = setting.add_bool(
|
|
||||||
"enable_m",
|
|
||||||
"Enable the use of 'M' instructions if available",
|
|
||||||
"",
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
setting.add_bool(
|
|
||||||
"enable_e",
|
|
||||||
"Enable the 'RV32E' instruction set with only 16 registers",
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
let shared_enable_atomics = shared.get_bool("enable_atomics");
|
|
||||||
let shared_enable_float = shared.get_bool("enable_float");
|
|
||||||
let shared_enable_simd = shared.get_bool("enable_simd");
|
|
||||||
|
|
||||||
setting.add_predicate("use_m", predicate!(supports_m && enable_m));
|
|
||||||
setting.add_predicate("use_a", predicate!(supports_a && shared_enable_atomics));
|
|
||||||
setting.add_predicate("use_f", predicate!(supports_f && shared_enable_float));
|
|
||||||
setting.add_predicate("use_d", predicate!(supports_d && shared_enable_float));
|
|
||||||
setting.add_predicate(
|
|
||||||
"full_float",
|
|
||||||
predicate!(shared_enable_simd && supports_f && supports_d),
|
|
||||||
);
|
|
||||||
|
|
||||||
setting.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn define_registers() -> IsaRegs {
|
|
||||||
let mut regs = IsaRegsBuilder::new();
|
|
||||||
|
|
||||||
let builder = RegBankBuilder::new("IntRegs", "x")
|
|
||||||
.units(32)
|
|
||||||
.track_pressure(true);
|
|
||||||
let int_regs = regs.add_bank(builder);
|
|
||||||
|
|
||||||
let builder = RegBankBuilder::new("FloatRegs", "f")
|
|
||||||
.units(32)
|
|
||||||
.track_pressure(true);
|
|
||||||
let float_regs = regs.add_bank(builder);
|
|
||||||
|
|
||||||
let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
|
|
||||||
regs.add_class(builder);
|
|
||||||
|
|
||||||
let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
|
|
||||||
regs.add_class(builder);
|
|
||||||
|
|
||||||
regs.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
|
|
||||||
let settings = define_settings(&shared_defs.settings);
|
|
||||||
let regs = define_registers();
|
|
||||||
|
|
||||||
// CPU modes for 32-bit and 64-bit operation.
|
|
||||||
let mut rv_32 = CpuMode::new("RV32");
|
|
||||||
let mut rv_64 = CpuMode::new("RV64");
|
|
||||||
|
|
||||||
let expand = shared_defs.transform_groups.by_name("expand");
|
|
||||||
let narrow_no_flags = shared_defs.transform_groups.by_name("narrow_no_flags");
|
|
||||||
|
|
||||||
rv_32.legalize_monomorphic(expand);
|
|
||||||
rv_32.legalize_default(narrow_no_flags);
|
|
||||||
rv_32.legalize_type(I32, expand);
|
|
||||||
rv_32.legalize_type(F32, expand);
|
|
||||||
rv_32.legalize_type(F64, expand);
|
|
||||||
|
|
||||||
rv_64.legalize_monomorphic(expand);
|
|
||||||
rv_64.legalize_default(narrow_no_flags);
|
|
||||||
rv_64.legalize_type(I32, expand);
|
|
||||||
rv_64.legalize_type(I64, expand);
|
|
||||||
rv_64.legalize_type(F32, expand);
|
|
||||||
rv_64.legalize_type(F64, expand);
|
|
||||||
|
|
||||||
let recipes = recipes::define(shared_defs, ®s);
|
|
||||||
|
|
||||||
let encodings = encodings::define(shared_defs, &settings, &recipes);
|
|
||||||
rv_32.set_encodings(encodings.enc32);
|
|
||||||
rv_64.set_encodings(encodings.enc64);
|
|
||||||
let encodings_predicates = encodings.inst_pred_reg.extract();
|
|
||||||
|
|
||||||
let recipes = recipes.collect();
|
|
||||||
|
|
||||||
let cpu_modes = vec![rv_32, rv_64];
|
|
||||||
|
|
||||||
TargetIsa::new(
|
|
||||||
"riscv",
|
|
||||||
settings,
|
|
||||||
regs,
|
|
||||||
recipes,
|
|
||||||
cpu_modes,
|
|
||||||
encodings_predicates,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::cdsl::instructions::InstructionPredicate;
|
|
||||||
use crate::cdsl::recipes::{EncodingRecipeBuilder, EncodingRecipeNumber, Recipes, Stack};
|
|
||||||
use crate::cdsl::regs::IsaRegs;
|
|
||||||
use crate::shared::Definitions as SharedDefinitions;
|
|
||||||
|
|
||||||
/// An helper to create recipes and use them when defining the RISCV encodings.
|
|
||||||
pub(crate) struct RecipeGroup {
|
|
||||||
/// The actualy list of recipes explicitly created in this file.
|
|
||||||
pub recipes: Recipes,
|
|
||||||
|
|
||||||
/// Provides fast lookup from a name to an encoding recipe.
|
|
||||||
name_to_recipe: HashMap<String, EncodingRecipeNumber>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecipeGroup {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
recipes: Recipes::new(),
|
|
||||||
name_to_recipe: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push(&mut self, builder: EncodingRecipeBuilder) {
|
|
||||||
assert!(
|
|
||||||
self.name_to_recipe.get(&builder.name).is_none(),
|
|
||||||
"riscv recipe '{}' created twice",
|
|
||||||
builder.name
|
|
||||||
);
|
|
||||||
let name = builder.name.clone();
|
|
||||||
let number = self.recipes.push(builder.build());
|
|
||||||
self.name_to_recipe.insert(name, number);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn by_name(&self, name: &str) -> EncodingRecipeNumber {
|
|
||||||
*self
|
|
||||||
.name_to_recipe
|
|
||||||
.get(name)
|
|
||||||
.unwrap_or_else(|| panic!("unknown riscv recipe name {}", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn collect(self) -> Recipes {
|
|
||||||
self.recipes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeGroup {
|
|
||||||
let formats = &shared_defs.formats;
|
|
||||||
|
|
||||||
// Register classes shorthands.
|
|
||||||
let gpr = regs.class_by_name("GPR");
|
|
||||||
|
|
||||||
// Definitions.
|
|
||||||
let mut recipes = RecipeGroup::new();
|
|
||||||
|
|
||||||
// R-type 32-bit instructions: These are mostly binary arithmetic instructions.
|
|
||||||
// The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8)
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("R", &formats.binary, 4)
|
|
||||||
.operands_in(vec![gpr, gpr])
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.emit("put_r(bits, in_reg0, in_reg1, out_reg0, sink);"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// R-type with an immediate shift amount instead of rs2.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("Rshamt", &formats.binary_imm64, 4)
|
|
||||||
.operands_in(vec![gpr])
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.emit("put_rshamt(bits, in_reg0, imm.into(), out_reg0, sink);"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// R-type encoding of an integer comparison.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("Ricmp", &formats.int_compare, 4)
|
|
||||||
.operands_in(vec![gpr, gpr])
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.emit("put_r(bits, in_reg0, in_reg1, out_reg0, sink);"),
|
|
||||||
);
|
|
||||||
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("Ii", &formats.binary_imm64, 4)
|
|
||||||
.operands_in(vec![gpr])
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.inst_predicate(InstructionPredicate::new_is_signed_int(
|
|
||||||
&*formats.binary_imm64,
|
|
||||||
"imm",
|
|
||||||
12,
|
|
||||||
0,
|
|
||||||
))
|
|
||||||
.emit("put_i(bits, in_reg0, imm.into(), out_reg0, sink);"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// I-type instruction with a hardcoded %x0 rs1.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("Iz", &formats.unary_imm, 4)
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.inst_predicate(InstructionPredicate::new_is_signed_int(
|
|
||||||
&formats.unary_imm,
|
|
||||||
"imm",
|
|
||||||
12,
|
|
||||||
0,
|
|
||||||
))
|
|
||||||
.emit("put_i(bits, 0, imm.into(), out_reg0, sink);"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// I-type encoding of an integer comparison.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("Iicmp", &formats.int_compare_imm, 4)
|
|
||||||
.operands_in(vec![gpr])
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.inst_predicate(InstructionPredicate::new_is_signed_int(
|
|
||||||
&formats.int_compare_imm,
|
|
||||||
"imm",
|
|
||||||
12,
|
|
||||||
0,
|
|
||||||
))
|
|
||||||
.emit("put_i(bits, in_reg0, imm.into(), out_reg0, sink);"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// I-type encoding for `jalr` as a return instruction. We won't use the immediate offset. The
|
|
||||||
// variable return values are not encoded.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("Iret", &formats.multiary, 4).emit(
|
|
||||||
r#"
|
|
||||||
// Return instructions are always a jalr to %x1.
|
|
||||||
// The return address is provided as a special-purpose link argument.
|
|
||||||
put_i(
|
|
||||||
bits,
|
|
||||||
1, // rs1 = %x1
|
|
||||||
0, // no offset.
|
|
||||||
0, // rd = %x0: no address written.
|
|
||||||
sink,
|
|
||||||
);
|
|
||||||
"#,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// I-type encoding for `jalr` as a call_indirect.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("Icall", &formats.call_indirect, 4)
|
|
||||||
.operands_in(vec![gpr])
|
|
||||||
.emit(
|
|
||||||
r#"
|
|
||||||
// call_indirect instructions are jalr with rd=%x1.
|
|
||||||
put_i(
|
|
||||||
bits,
|
|
||||||
in_reg0,
|
|
||||||
0, // no offset.
|
|
||||||
1, // rd = %x1: link register.
|
|
||||||
sink,
|
|
||||||
);
|
|
||||||
"#,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Copy of a GPR is implemented as addi x, 0.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("Icopy", &formats.unary, 4)
|
|
||||||
.operands_in(vec![gpr])
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.emit("put_i(bits, in_reg0, 0, out_reg0, sink);"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Same for a GPR regmove.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("Irmov", &formats.reg_move, 4)
|
|
||||||
.operands_in(vec![gpr])
|
|
||||||
.emit("put_i(bits, src, 0, dst, sink);"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Same for copy-to-SSA -- GPR regmove.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("copytossa", &formats.copy_to_ssa, 4)
|
|
||||||
// No operands_in to mention, because a source register is specified directly.
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.emit("put_i(bits, src, 0, out_reg0, sink);"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// U-type instructions have a 20-bit immediate that targets bits 12-31.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("U", &formats.unary_imm, 4)
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.inst_predicate(InstructionPredicate::new_is_signed_int(
|
|
||||||
&formats.unary_imm,
|
|
||||||
"imm",
|
|
||||||
32,
|
|
||||||
12,
|
|
||||||
))
|
|
||||||
.emit("put_u(bits, imm.into(), out_reg0, sink);"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// UJ-type unconditional branch instructions.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("UJ", &formats.jump, 4)
|
|
||||||
.branch_range((0, 21))
|
|
||||||
.emit(
|
|
||||||
r#"
|
|
||||||
let dest = i64::from(func.offsets[destination]);
|
|
||||||
let disp = dest - i64::from(sink.offset());
|
|
||||||
put_uj(bits, disp, 0, sink);
|
|
||||||
"#,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
recipes.push(EncodingRecipeBuilder::new("UJcall", &formats.call, 4).emit(
|
|
||||||
r#"
|
|
||||||
sink.reloc_external(func.srclocs[inst],
|
|
||||||
Reloc::RiscvCall,
|
|
||||||
&func.dfg.ext_funcs[func_ref].name,
|
|
||||||
0);
|
|
||||||
// rd=%x1 is the standard link register.
|
|
||||||
put_uj(bits, 0, 1, sink);
|
|
||||||
"#,
|
|
||||||
));
|
|
||||||
|
|
||||||
// SB-type branch instructions.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("SB", &formats.branch_icmp, 4)
|
|
||||||
.operands_in(vec![gpr, gpr])
|
|
||||||
.branch_range((0, 13))
|
|
||||||
.emit(
|
|
||||||
r#"
|
|
||||||
let dest = i64::from(func.offsets[destination]);
|
|
||||||
let disp = dest - i64::from(sink.offset());
|
|
||||||
put_sb(bits, disp, in_reg0, in_reg1, sink);
|
|
||||||
"#,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// SB-type branch instruction with rs2 fixed to zero.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("SBzero", &formats.branch, 4)
|
|
||||||
.operands_in(vec![gpr])
|
|
||||||
.branch_range((0, 13))
|
|
||||||
.emit(
|
|
||||||
r#"
|
|
||||||
let dest = i64::from(func.offsets[destination]);
|
|
||||||
let disp = dest - i64::from(sink.offset());
|
|
||||||
put_sb(bits, disp, in_reg0, 0, sink);
|
|
||||||
"#,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Spill of a GPR.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("GPsp", &formats.unary, 4)
|
|
||||||
.operands_in(vec![gpr])
|
|
||||||
.operands_out(vec![Stack::new(gpr)])
|
|
||||||
.emit("unimplemented!();"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fill of a GPR.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("GPfi", &formats.unary, 4)
|
|
||||||
.operands_in(vec![Stack::new(gpr)])
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.emit("unimplemented!();"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Stack-slot to same stack-slot copy, which is guaranteed to turn into a no-op.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("stacknull", &formats.unary, 0)
|
|
||||||
.operands_in(vec![Stack::new(gpr)])
|
|
||||||
.operands_out(vec![Stack::new(gpr)])
|
|
||||||
.emit(""),
|
|
||||||
);
|
|
||||||
|
|
||||||
// No-op fills, created by late-stage redundant-fill removal.
|
|
||||||
recipes.push(
|
|
||||||
EncodingRecipeBuilder::new("fillnull", &formats.unary, 0)
|
|
||||||
.operands_in(vec![Stack::new(gpr)])
|
|
||||||
.operands_out(vec![gpr])
|
|
||||||
.clobbers_flags(false)
|
|
||||||
.emit(""),
|
|
||||||
);
|
|
||||||
|
|
||||||
recipes
|
|
||||||
}
|
|
||||||
@@ -119,7 +119,7 @@ pub fn generate(
|
|||||||
isa::Isa::S390x => {
|
isa::Isa::S390x => {
|
||||||
// s390x doesn't have platform-specific settings.
|
// s390x doesn't have platform-specific settings.
|
||||||
}
|
}
|
||||||
isa::Isa::Arm32 | isa::Isa::Riscv => todo!(),
|
isa::Isa::Arm32 => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,8 +58,6 @@ pub enum Reloc {
|
|||||||
/// value is sign-extended, multiplied by 4, and added to the PC of
|
/// value is sign-extended, multiplied by 4, and added to the PC of
|
||||||
/// the call instruction to form the destination address.
|
/// the call instruction to form the destination address.
|
||||||
Arm64Call,
|
Arm64Call,
|
||||||
/// RISC-V call target
|
|
||||||
RiscvCall,
|
|
||||||
/// s390x PC-relative 4-byte offset
|
/// s390x PC-relative 4-byte offset
|
||||||
S390xPCRel32Dbl,
|
S390xPCRel32Dbl,
|
||||||
|
|
||||||
@@ -93,7 +91,7 @@ impl fmt::Display for Reloc {
|
|||||||
Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
|
Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
|
||||||
Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
|
Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
|
||||||
Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
|
Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
|
||||||
Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"),
|
Self::Arm32Call | Self::Arm64Call => write!(f, "Call"),
|
||||||
|
|
||||||
Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
|
Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
|
||||||
Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
|
Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
//! Legacy ("old-style") backends that will be removed in the future.
|
|
||||||
|
|
||||||
#[cfg(feature = "riscv")]
|
|
||||||
pub(crate) mod riscv;
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
//! RISC-V ABI implementation.
|
|
||||||
//!
|
|
||||||
//! This module implements the RISC-V calling convention through the primary `legalize_signature()`
|
|
||||||
//! entry point.
|
|
||||||
//!
|
|
||||||
//! This doesn't support the soft-float ABI at the moment.
|
|
||||||
|
|
||||||
use super::registers::{FPR, GPR};
|
|
||||||
use super::settings;
|
|
||||||
use crate::abi::{legalize_args, ArgAction, ArgAssigner, ValueConversion};
|
|
||||||
use crate::ir::{self, AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose, Type};
|
|
||||||
use crate::isa::RegClass;
|
|
||||||
use crate::regalloc::RegisterSet;
|
|
||||||
use alloc::borrow::Cow;
|
|
||||||
use core::i32;
|
|
||||||
use target_lexicon::Triple;
|
|
||||||
|
|
||||||
struct Args {
|
|
||||||
pointer_bits: u8,
|
|
||||||
pointer_bytes: u8,
|
|
||||||
pointer_type: Type,
|
|
||||||
regs: u32,
|
|
||||||
reg_limit: u32,
|
|
||||||
offset: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Args {
|
|
||||||
fn new(bits: u8, enable_e: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
pointer_bits: bits,
|
|
||||||
pointer_bytes: bits / 8,
|
|
||||||
pointer_type: Type::int(u16::from(bits)).unwrap(),
|
|
||||||
regs: 0,
|
|
||||||
reg_limit: if enable_e { 6 } else { 8 },
|
|
||||||
offset: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArgAssigner for Args {
|
|
||||||
fn assign(&mut self, arg: &AbiParam) -> ArgAction {
|
|
||||||
fn align(value: u32, to: u32) -> u32 {
|
|
||||||
(value + to - 1) & !(to - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
let ty = arg.value_type;
|
|
||||||
|
|
||||||
// Check for a legal type.
|
|
||||||
// RISC-V doesn't have SIMD at all, so break all vectors down.
|
|
||||||
if ty.is_vector() {
|
|
||||||
return ValueConversion::VectorSplit.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Large integers and booleans are broken down to fit in a register.
|
|
||||||
if !ty.is_float() && ty.bits() > u16::from(self.pointer_bits) {
|
|
||||||
// Align registers and stack to a multiple of two pointers.
|
|
||||||
self.regs = align(self.regs, 2);
|
|
||||||
self.offset = align(self.offset, 2 * u32::from(self.pointer_bytes));
|
|
||||||
return ValueConversion::IntSplit.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Small integers are extended to the size of a pointer register.
|
|
||||||
if ty.is_int() && ty.bits() < u16::from(self.pointer_bits) {
|
|
||||||
match arg.extension {
|
|
||||||
ArgumentExtension::None => {}
|
|
||||||
ArgumentExtension::Uext => return ValueConversion::Uext(self.pointer_type).into(),
|
|
||||||
ArgumentExtension::Sext => return ValueConversion::Sext(self.pointer_type).into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.regs < self.reg_limit {
|
|
||||||
// Assign to a register.
|
|
||||||
let reg = if ty.is_float() {
|
|
||||||
FPR.unit(10 + self.regs as usize)
|
|
||||||
} else {
|
|
||||||
GPR.unit(10 + self.regs as usize)
|
|
||||||
};
|
|
||||||
self.regs += 1;
|
|
||||||
ArgumentLoc::Reg(reg).into()
|
|
||||||
} else {
|
|
||||||
// Assign a stack location.
|
|
||||||
let loc = ArgumentLoc::Stack(self.offset as i32);
|
|
||||||
self.offset += u32::from(self.pointer_bytes);
|
|
||||||
debug_assert!(self.offset <= i32::MAX as u32);
|
|
||||||
loc.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Legalize `sig` for RISC-V.
|
|
||||||
pub fn legalize_signature(
|
|
||||||
sig: &mut Cow<ir::Signature>,
|
|
||||||
triple: &Triple,
|
|
||||||
isa_flags: &settings::Flags,
|
|
||||||
current: bool,
|
|
||||||
) {
|
|
||||||
let bits = triple.pointer_width().unwrap().bits();
|
|
||||||
|
|
||||||
let mut args = Args::new(bits, isa_flags.enable_e());
|
|
||||||
if let Some(new_params) = legalize_args(&sig.params, &mut args) {
|
|
||||||
sig.to_mut().params = new_params;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rets = Args::new(bits, isa_flags.enable_e());
|
|
||||||
if let Some(new_returns) = legalize_args(&sig.returns, &mut rets) {
|
|
||||||
sig.to_mut().returns = new_returns;
|
|
||||||
}
|
|
||||||
|
|
||||||
if current {
|
|
||||||
let ptr = Type::int(u16::from(bits)).unwrap();
|
|
||||||
|
|
||||||
// Add the link register as an argument and return value.
|
|
||||||
//
|
|
||||||
// The `jalr` instruction implementing a return can technically accept the return address
|
|
||||||
// in any register, but a micro-architecture with a return address predictor will only
|
|
||||||
// recognize it as a return if the address is in `x1`.
|
|
||||||
let link = AbiParam::special_reg(ptr, ArgumentPurpose::Link, GPR.unit(1));
|
|
||||||
sig.to_mut().params.push(link);
|
|
||||||
sig.to_mut().returns.push(link);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get register class for a type appearing in a legalized signature.
|
|
||||||
pub fn regclass_for_abi_type(ty: Type) -> RegClass {
|
|
||||||
if ty.is_float() {
|
|
||||||
FPR
|
|
||||||
} else {
|
|
||||||
GPR
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn allocatable_registers(_func: &ir::Function, isa_flags: &settings::Flags) -> RegisterSet {
|
|
||||||
let mut regs = RegisterSet::new();
|
|
||||||
regs.take(GPR, GPR.unit(0)); // Hard-wired 0.
|
|
||||||
// %x1 is the link register which is available for allocation.
|
|
||||||
regs.take(GPR, GPR.unit(2)); // Stack pointer.
|
|
||||||
regs.take(GPR, GPR.unit(3)); // Global pointer.
|
|
||||||
regs.take(GPR, GPR.unit(4)); // Thread pointer.
|
|
||||||
// TODO: %x8 is the frame pointer. Reserve it?
|
|
||||||
|
|
||||||
// Remove %x16 and up for RV32E.
|
|
||||||
if isa_flags.enable_e() {
|
|
||||||
for u in 16..32 {
|
|
||||||
regs.take(GPR, GPR.unit(u));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
regs
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
//! Emitting binary RISC-V machine code.
|
|
||||||
|
|
||||||
use crate::binemit::{bad_encoding, CodeSink, Reloc};
|
|
||||||
use crate::ir::{Function, Inst, InstructionData};
|
|
||||||
use crate::isa::{RegUnit, StackBaseMask, StackRef, TargetIsa};
|
|
||||||
use crate::predicates::is_signed_int;
|
|
||||||
use crate::regalloc::RegDiversions;
|
|
||||||
use core::u32;
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/binemit-riscv.rs"));
|
|
||||||
|
|
||||||
/// R-type instructions.
|
|
||||||
///
|
|
||||||
/// 31 24 19 14 11 6
|
|
||||||
/// funct7 rs2 rs1 funct3 rd opcode
|
|
||||||
/// 25 20 15 12 7 0
|
|
||||||
///
|
|
||||||
/// Encoding bits: `opcode[6:2] | (funct3 << 5) | (funct7 << 8)`.
|
|
||||||
fn put_r<CS: CodeSink + ?Sized>(bits: u16, rs1: RegUnit, rs2: RegUnit, rd: RegUnit, sink: &mut CS) {
|
|
||||||
let bits = u32::from(bits);
|
|
||||||
let opcode5 = bits & 0x1f;
|
|
||||||
let funct3 = (bits >> 5) & 0x7;
|
|
||||||
let funct7 = (bits >> 8) & 0x7f;
|
|
||||||
let rs1 = u32::from(rs1) & 0x1f;
|
|
||||||
let rs2 = u32::from(rs2) & 0x1f;
|
|
||||||
let rd = u32::from(rd) & 0x1f;
|
|
||||||
|
|
||||||
// 0-6: opcode
|
|
||||||
let mut i = 0x3;
|
|
||||||
i |= opcode5 << 2;
|
|
||||||
i |= rd << 7;
|
|
||||||
i |= funct3 << 12;
|
|
||||||
i |= rs1 << 15;
|
|
||||||
i |= rs2 << 20;
|
|
||||||
i |= funct7 << 25;
|
|
||||||
|
|
||||||
sink.put4(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// R-type instructions with a shift amount instead of rs2.
|
|
||||||
///
|
|
||||||
/// 31 25 19 14 11 6
|
|
||||||
/// funct7 shamt rs1 funct3 rd opcode
|
|
||||||
/// 25 20 15 12 7 0
|
|
||||||
///
|
|
||||||
/// Both funct7 and shamt contribute to bit 25. In RV64, shamt uses it for shifts > 31.
|
|
||||||
///
|
|
||||||
/// Encoding bits: `opcode[6:2] | (funct3 << 5) | (funct7 << 8)`.
|
|
||||||
fn put_rshamt<CS: CodeSink + ?Sized>(
|
|
||||||
bits: u16,
|
|
||||||
rs1: RegUnit,
|
|
||||||
shamt: i64,
|
|
||||||
rd: RegUnit,
|
|
||||||
sink: &mut CS,
|
|
||||||
) {
|
|
||||||
let bits = u32::from(bits);
|
|
||||||
let opcode5 = bits & 0x1f;
|
|
||||||
let funct3 = (bits >> 5) & 0x7;
|
|
||||||
let funct7 = (bits >> 8) & 0x7f;
|
|
||||||
let rs1 = u32::from(rs1) & 0x1f;
|
|
||||||
let shamt = shamt as u32 & 0x3f;
|
|
||||||
let rd = u32::from(rd) & 0x1f;
|
|
||||||
|
|
||||||
// 0-6: opcode
|
|
||||||
let mut i = 0x3;
|
|
||||||
i |= opcode5 << 2;
|
|
||||||
i |= rd << 7;
|
|
||||||
i |= funct3 << 12;
|
|
||||||
i |= rs1 << 15;
|
|
||||||
i |= shamt << 20;
|
|
||||||
i |= funct7 << 25;
|
|
||||||
|
|
||||||
sink.put4(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// I-type instructions.
|
|
||||||
///
|
|
||||||
/// 31 19 14 11 6
|
|
||||||
/// imm rs1 funct3 rd opcode
|
|
||||||
/// 20 15 12 7 0
|
|
||||||
///
|
|
||||||
/// Encoding bits: `opcode[6:2] | (funct3 << 5)`
|
|
||||||
fn put_i<CS: CodeSink + ?Sized>(bits: u16, rs1: RegUnit, imm: i64, rd: RegUnit, sink: &mut CS) {
|
|
||||||
let bits = u32::from(bits);
|
|
||||||
let opcode5 = bits & 0x1f;
|
|
||||||
let funct3 = (bits >> 5) & 0x7;
|
|
||||||
let rs1 = u32::from(rs1) & 0x1f;
|
|
||||||
let rd = u32::from(rd) & 0x1f;
|
|
||||||
|
|
||||||
// 0-6: opcode
|
|
||||||
let mut i = 0x3;
|
|
||||||
i |= opcode5 << 2;
|
|
||||||
i |= rd << 7;
|
|
||||||
i |= funct3 << 12;
|
|
||||||
i |= rs1 << 15;
|
|
||||||
i |= (imm << 20) as u32;
|
|
||||||
|
|
||||||
sink.put4(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// U-type instructions.
|
|
||||||
///
|
|
||||||
/// 31 11 6
|
|
||||||
/// imm rd opcode
|
|
||||||
/// 12 7 0
|
|
||||||
///
|
|
||||||
/// Encoding bits: `opcode[6:2] | (funct3 << 5)`
|
|
||||||
fn put_u<CS: CodeSink + ?Sized>(bits: u16, imm: i64, rd: RegUnit, sink: &mut CS) {
|
|
||||||
let bits = u32::from(bits);
|
|
||||||
let opcode5 = bits & 0x1f;
|
|
||||||
let rd = u32::from(rd) & 0x1f;
|
|
||||||
|
|
||||||
// 0-6: opcode
|
|
||||||
let mut i = 0x3;
|
|
||||||
i |= opcode5 << 2;
|
|
||||||
i |= rd << 7;
|
|
||||||
i |= imm as u32 & 0xfffff000;
|
|
||||||
|
|
||||||
sink.put4(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SB-type branch instructions.
|
|
||||||
///
|
|
||||||
/// 31 24 19 14 11 6
|
|
||||||
/// imm rs2 rs1 funct3 imm opcode
|
|
||||||
/// 25 20 15 12 7 0
|
|
||||||
///
|
|
||||||
/// Encoding bits: `opcode[6:2] | (funct3 << 5)`
|
|
||||||
fn put_sb<CS: CodeSink + ?Sized>(bits: u16, imm: i64, rs1: RegUnit, rs2: RegUnit, sink: &mut CS) {
|
|
||||||
let bits = u32::from(bits);
|
|
||||||
let opcode5 = bits & 0x1f;
|
|
||||||
let funct3 = (bits >> 5) & 0x7;
|
|
||||||
let rs1 = u32::from(rs1) & 0x1f;
|
|
||||||
let rs2 = u32::from(rs2) & 0x1f;
|
|
||||||
|
|
||||||
debug_assert!(is_signed_int(imm, 13, 1), "SB out of range {:#x}", imm);
|
|
||||||
let imm = imm as u32;
|
|
||||||
|
|
||||||
// 0-6: opcode
|
|
||||||
let mut i = 0x3;
|
|
||||||
i |= opcode5 << 2;
|
|
||||||
i |= funct3 << 12;
|
|
||||||
i |= rs1 << 15;
|
|
||||||
i |= rs2 << 20;
|
|
||||||
|
|
||||||
// The displacement is completely hashed up.
|
|
||||||
i |= ((imm >> 11) & 0x1) << 7;
|
|
||||||
i |= ((imm >> 1) & 0xf) << 8;
|
|
||||||
i |= ((imm >> 5) & 0x3f) << 25;
|
|
||||||
i |= ((imm >> 12) & 0x1) << 31;
|
|
||||||
|
|
||||||
sink.put4(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// UJ-type jump instructions.
|
|
||||||
///
|
|
||||||
/// 31 11 6
|
|
||||||
/// imm rd opcode
|
|
||||||
/// 12 7 0
|
|
||||||
///
|
|
||||||
/// Encoding bits: `opcode[6:2]`
|
|
||||||
fn put_uj<CS: CodeSink + ?Sized>(bits: u16, imm: i64, rd: RegUnit, sink: &mut CS) {
|
|
||||||
let bits = u32::from(bits);
|
|
||||||
let opcode5 = bits & 0x1f;
|
|
||||||
let rd = u32::from(rd) & 0x1f;
|
|
||||||
|
|
||||||
debug_assert!(is_signed_int(imm, 21, 1), "UJ out of range {:#x}", imm);
|
|
||||||
let imm = imm as u32;
|
|
||||||
|
|
||||||
// 0-6: opcode
|
|
||||||
let mut i = 0x3;
|
|
||||||
i |= opcode5 << 2;
|
|
||||||
i |= rd << 7;
|
|
||||||
|
|
||||||
// The displacement is completely hashed up.
|
|
||||||
i |= imm & 0xff000;
|
|
||||||
i |= ((imm >> 11) & 0x1) << 20;
|
|
||||||
i |= ((imm >> 1) & 0x3ff) << 21;
|
|
||||||
i |= ((imm >> 20) & 0x1) << 31;
|
|
||||||
|
|
||||||
sink.put4(i);
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
//! Encoding tables for RISC-V.
|
|
||||||
|
|
||||||
use super::registers::*;
|
|
||||||
use crate::ir;
|
|
||||||
use crate::isa;
|
|
||||||
use crate::isa::constraints::*;
|
|
||||||
use crate::isa::enc_tables::*;
|
|
||||||
use crate::isa::encoding::{base_size, RecipeSizing};
|
|
||||||
use crate::predicates;
|
|
||||||
|
|
||||||
// Include the generated encoding tables:
|
|
||||||
// - `LEVEL1_RV32`
|
|
||||||
// - `LEVEL1_RV64`
|
|
||||||
// - `LEVEL2`
|
|
||||||
// - `ENCLIST`
|
|
||||||
// - `INFO`
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/encoding-riscv.rs"));
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/legalize-riscv.rs"));
|
|
||||||
@@ -1,304 +0,0 @@
|
|||||||
//! RISC-V Instruction Set Architecture.
|
|
||||||
|
|
||||||
mod abi;
|
|
||||||
mod binemit;
|
|
||||||
mod enc_tables;
|
|
||||||
mod registers;
|
|
||||||
pub mod settings;
|
|
||||||
|
|
||||||
use super::super::settings as shared_settings;
|
|
||||||
#[cfg(feature = "testing_hooks")]
|
|
||||||
use crate::binemit::CodeSink;
|
|
||||||
use crate::binemit::{emit_function, MemoryCodeSink};
|
|
||||||
use crate::ir;
|
|
||||||
use crate::isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings};
|
|
||||||
use crate::isa::Builder as IsaBuilder;
|
|
||||||
use crate::isa::{EncInfo, RegClass, RegInfo, TargetIsa};
|
|
||||||
use crate::regalloc;
|
|
||||||
use alloc::{borrow::Cow, boxed::Box, vec::Vec};
|
|
||||||
use core::any::Any;
|
|
||||||
use core::fmt;
|
|
||||||
use core::hash::{Hash, Hasher};
|
|
||||||
use target_lexicon::{PointerWidth, Triple};
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
struct Isa {
|
|
||||||
triple: Triple,
|
|
||||||
shared_flags: shared_settings::Flags,
|
|
||||||
isa_flags: settings::Flags,
|
|
||||||
cpumode: &'static [shared_enc_tables::Level1Entry<u16>],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an ISA builder for creating RISC-V targets.
|
|
||||||
pub fn isa_builder(triple: Triple) -> IsaBuilder {
|
|
||||||
IsaBuilder {
|
|
||||||
triple,
|
|
||||||
setup: settings::builder(),
|
|
||||||
constructor: isa_constructor,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isa_constructor(
|
|
||||||
triple: Triple,
|
|
||||||
shared_flags: shared_settings::Flags,
|
|
||||||
builder: shared_settings::Builder,
|
|
||||||
) -> Box<dyn TargetIsa> {
|
|
||||||
let level1 = match triple.pointer_width().unwrap() {
|
|
||||||
PointerWidth::U16 => panic!("16-bit RISC-V unrecognized"),
|
|
||||||
PointerWidth::U32 => &enc_tables::LEVEL1_RV32[..],
|
|
||||||
PointerWidth::U64 => &enc_tables::LEVEL1_RV64[..],
|
|
||||||
};
|
|
||||||
Box::new(Isa {
|
|
||||||
triple,
|
|
||||||
isa_flags: settings::Flags::new(&shared_flags, builder),
|
|
||||||
shared_flags,
|
|
||||||
cpumode: level1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TargetIsa for Isa {
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
"riscv"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn triple(&self) -> &Triple {
|
|
||||||
&self.triple
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flags(&self) -> &shared_settings::Flags {
|
|
||||||
&self.shared_flags
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isa_flags(&self) -> Vec<shared_settings::Value> {
|
|
||||||
self.isa_flags.iter().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
|
|
||||||
self.shared_flags.hash(&mut hasher);
|
|
||||||
self.isa_flags.hash(&mut hasher);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_info(&self) -> RegInfo {
|
|
||||||
registers::INFO.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encoding_info(&self) -> EncInfo {
|
|
||||||
enc_tables::INFO.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn legal_encodings<'a>(
|
|
||||||
&'a self,
|
|
||||||
func: &'a ir::Function,
|
|
||||||
inst: &'a ir::InstructionData,
|
|
||||||
ctrl_typevar: ir::Type,
|
|
||||||
) -> Encodings<'a> {
|
|
||||||
lookup_enclist(
|
|
||||||
ctrl_typevar,
|
|
||||||
inst,
|
|
||||||
func,
|
|
||||||
self.cpumode,
|
|
||||||
&enc_tables::LEVEL2[..],
|
|
||||||
&enc_tables::ENCLISTS[..],
|
|
||||||
&enc_tables::LEGALIZE_ACTIONS[..],
|
|
||||||
&enc_tables::RECIPE_PREDICATES[..],
|
|
||||||
&enc_tables::INST_PREDICATES[..],
|
|
||||||
self.isa_flags.predicate_view(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn legalize_signature(&self, sig: &mut Cow<ir::Signature>, current: bool) {
|
|
||||||
abi::legalize_signature(sig, &self.triple, &self.isa_flags, current)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn regclass_for_abi_type(&self, ty: ir::Type) -> RegClass {
|
|
||||||
abi::regclass_for_abi_type(ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocatable_registers(&self, func: &ir::Function) -> regalloc::RegisterSet {
|
|
||||||
abi::allocatable_registers(func, &self.isa_flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "testing_hooks")]
|
|
||||||
fn emit_inst(
|
|
||||||
&self,
|
|
||||||
func: &ir::Function,
|
|
||||||
inst: ir::Inst,
|
|
||||||
divert: &mut regalloc::RegDiversions,
|
|
||||||
sink: &mut dyn CodeSink,
|
|
||||||
) {
|
|
||||||
binemit::emit_inst(func, inst, divert, sink, self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_function_to_memory(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
|
|
||||||
emit_function(func, binemit::emit_inst, sink, self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unsigned_add_overflow_condition(&self) -> ir::condcodes::IntCC {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unsigned_sub_overflow_condition(&self) -> ir::condcodes::IntCC {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
self as &dyn Any
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::ir::{immediates, types};
|
|
||||||
use crate::ir::{Function, InstructionData, Opcode};
|
|
||||||
use crate::isa;
|
|
||||||
use crate::settings::{self, Configurable};
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
use core::str::FromStr;
|
|
||||||
use target_lexicon::triple;
|
|
||||||
|
|
||||||
fn encstr(isa: &dyn isa::TargetIsa, enc: Result<isa::Encoding, isa::Legalize>) -> String {
|
|
||||||
match enc {
|
|
||||||
Ok(e) => isa.encoding_info().display(e).to_string(),
|
|
||||||
Err(_) => "no encoding".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_64bitenc() {
|
|
||||||
let shared_builder = settings::builder();
|
|
||||||
let shared_flags = settings::Flags::new(shared_builder);
|
|
||||||
let isa = isa::lookup(triple!("riscv64"))
|
|
||||||
.unwrap()
|
|
||||||
.finish(shared_flags);
|
|
||||||
|
|
||||||
let mut func = Function::new();
|
|
||||||
let block = func.dfg.make_block();
|
|
||||||
let arg64 = func.dfg.append_block_param(block, types::I64);
|
|
||||||
let arg32 = func.dfg.append_block_param(block, types::I32);
|
|
||||||
|
|
||||||
// Try to encode iadd_imm.i64 v1, -10.
|
|
||||||
let inst64 = InstructionData::BinaryImm64 {
|
|
||||||
opcode: Opcode::IaddImm,
|
|
||||||
arg: arg64,
|
|
||||||
imm: immediates::Imm64::new(-10),
|
|
||||||
};
|
|
||||||
|
|
||||||
// ADDI is I/0b00100
|
|
||||||
assert_eq!(
|
|
||||||
encstr(&*isa, isa.encode(&func, &inst64, types::I64)),
|
|
||||||
"Ii#04"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Try to encode iadd_imm.i64 v1, -10000.
|
|
||||||
let inst64_large = InstructionData::BinaryImm64 {
|
|
||||||
opcode: Opcode::IaddImm,
|
|
||||||
arg: arg64,
|
|
||||||
imm: immediates::Imm64::new(-10000),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Immediate is out of range for ADDI.
|
|
||||||
assert!(isa.encode(&func, &inst64_large, types::I64).is_err());
|
|
||||||
|
|
||||||
// Create an iadd_imm.i32 which is encodable in RV64.
|
|
||||||
let inst32 = InstructionData::BinaryImm64 {
|
|
||||||
opcode: Opcode::IaddImm,
|
|
||||||
arg: arg32,
|
|
||||||
imm: immediates::Imm64::new(10),
|
|
||||||
};
|
|
||||||
|
|
||||||
// ADDIW is I/0b00110
|
|
||||||
assert_eq!(
|
|
||||||
encstr(&*isa, isa.encode(&func, &inst32, types::I32)),
|
|
||||||
"Ii#06"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same as above, but for RV32.
|
|
||||||
#[test]
|
|
||||||
fn test_32bitenc() {
|
|
||||||
let shared_builder = settings::builder();
|
|
||||||
let shared_flags = settings::Flags::new(shared_builder);
|
|
||||||
let isa = isa::lookup(triple!("riscv32"))
|
|
||||||
.unwrap()
|
|
||||||
.finish(shared_flags);
|
|
||||||
|
|
||||||
let mut func = Function::new();
|
|
||||||
let block = func.dfg.make_block();
|
|
||||||
let arg64 = func.dfg.append_block_param(block, types::I64);
|
|
||||||
let arg32 = func.dfg.append_block_param(block, types::I32);
|
|
||||||
|
|
||||||
// Try to encode iadd_imm.i64 v1, -10.
|
|
||||||
let inst64 = InstructionData::BinaryImm64 {
|
|
||||||
opcode: Opcode::IaddImm,
|
|
||||||
arg: arg64,
|
|
||||||
imm: immediates::Imm64::new(-10),
|
|
||||||
};
|
|
||||||
|
|
||||||
// In 32-bit mode, an i64 bit add should be narrowed.
|
|
||||||
assert!(isa.encode(&func, &inst64, types::I64).is_err());
|
|
||||||
|
|
||||||
// Try to encode iadd_imm.i64 v1, -10000.
|
|
||||||
let inst64_large = InstructionData::BinaryImm64 {
|
|
||||||
opcode: Opcode::IaddImm,
|
|
||||||
arg: arg64,
|
|
||||||
imm: immediates::Imm64::new(-10000),
|
|
||||||
};
|
|
||||||
|
|
||||||
// In 32-bit mode, an i64 bit add should be narrowed.
|
|
||||||
assert!(isa.encode(&func, &inst64_large, types::I64).is_err());
|
|
||||||
|
|
||||||
// Create an iadd_imm.i32 which is encodable in RV32.
|
|
||||||
let inst32 = InstructionData::BinaryImm64 {
|
|
||||||
opcode: Opcode::IaddImm,
|
|
||||||
arg: arg32,
|
|
||||||
imm: immediates::Imm64::new(10),
|
|
||||||
};
|
|
||||||
|
|
||||||
// ADDI is I/0b00100
|
|
||||||
assert_eq!(
|
|
||||||
encstr(&*isa, isa.encode(&func, &inst32, types::I32)),
|
|
||||||
"Ii#04"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create an imul.i32 which is encodable in RV32, but only when use_m is true.
|
|
||||||
let mul32 = InstructionData::Binary {
|
|
||||||
opcode: Opcode::Imul,
|
|
||||||
args: [arg32, arg32],
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(isa.encode(&func, &mul32, types::I32).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rv32m() {
|
|
||||||
let shared_builder = settings::builder();
|
|
||||||
let shared_flags = settings::Flags::new(shared_builder);
|
|
||||||
|
|
||||||
// Set the supports_m stting which in turn enables the use_m predicate that unlocks
|
|
||||||
// encodings for imul.
|
|
||||||
let mut isa_builder = isa::lookup(triple!("riscv32")).unwrap();
|
|
||||||
isa_builder.enable("supports_m").unwrap();
|
|
||||||
|
|
||||||
let isa = isa_builder.finish(shared_flags);
|
|
||||||
|
|
||||||
let mut func = Function::new();
|
|
||||||
let block = func.dfg.make_block();
|
|
||||||
let arg32 = func.dfg.append_block_param(block, types::I32);
|
|
||||||
|
|
||||||
// Create an imul.i32 which is encodable in RV32M.
|
|
||||||
let mul32 = InstructionData::Binary {
|
|
||||||
opcode: Opcode::Imul,
|
|
||||||
args: [arg32, arg32],
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
encstr(&*isa, isa.encode(&func, &mul32, types::I32)),
|
|
||||||
"R#10c"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Isa {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}\n{}", self.shared_flags, self.isa_flags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
//! RISC-V register descriptions.
|
|
||||||
|
|
||||||
use crate::isa::registers::{RegBank, RegClass, RegClassData, RegInfo, RegUnit};
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/registers-riscv.rs"));
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::{FPR, GPR, INFO};
|
|
||||||
use crate::isa::RegUnit;
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unit_encodings() {
|
|
||||||
assert_eq!(INFO.parse_regunit("x0"), Some(0));
|
|
||||||
assert_eq!(INFO.parse_regunit("x31"), Some(31));
|
|
||||||
assert_eq!(INFO.parse_regunit("f0"), Some(32));
|
|
||||||
assert_eq!(INFO.parse_regunit("f31"), Some(63));
|
|
||||||
|
|
||||||
assert_eq!(INFO.parse_regunit("x32"), None);
|
|
||||||
assert_eq!(INFO.parse_regunit("f32"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unit_names() {
|
|
||||||
fn uname(ru: RegUnit) -> String {
|
|
||||||
INFO.display_regunit(ru).to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(uname(0), "%x0");
|
|
||||||
assert_eq!(uname(1), "%x1");
|
|
||||||
assert_eq!(uname(31), "%x31");
|
|
||||||
assert_eq!(uname(32), "%f0");
|
|
||||||
assert_eq!(uname(33), "%f1");
|
|
||||||
assert_eq!(uname(63), "%f31");
|
|
||||||
assert_eq!(uname(64), "%INVALID64");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn classes() {
|
|
||||||
assert!(GPR.contains(GPR.unit(0)));
|
|
||||||
assert!(GPR.contains(GPR.unit(31)));
|
|
||||||
assert!(!FPR.contains(GPR.unit(0)));
|
|
||||||
assert!(!FPR.contains(GPR.unit(31)));
|
|
||||||
assert!(!GPR.contains(FPR.unit(0)));
|
|
||||||
assert!(!GPR.contains(FPR.unit(31)));
|
|
||||||
assert!(FPR.contains(FPR.unit(0)));
|
|
||||||
assert!(FPR.contains(FPR.unit(31)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
//! RISC-V Settings.
|
|
||||||
|
|
||||||
use crate::settings::{self, detail, Builder, Value};
|
|
||||||
use core::fmt;
|
|
||||||
|
|
||||||
// Include code generated by `cranelift-codegen/meta/src/gen_settings.rs`. This file contains a
|
|
||||||
// public `Flags` struct with an impl for all of the settings defined in
|
|
||||||
// `cranelift-codegen/meta/src/isa/riscv/mod.rs`.
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/settings-riscv.rs"));
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::{builder, Flags};
|
|
||||||
use crate::settings::{self, Configurable};
|
|
||||||
use alloc::string::ToString;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn display_default() {
|
|
||||||
let shared = settings::Flags::new(settings::builder());
|
|
||||||
let b = builder();
|
|
||||||
let f = Flags::new(&shared, b);
|
|
||||||
assert_eq!(
|
|
||||||
f.to_string(),
|
|
||||||
"[riscv]\n\
|
|
||||||
supports_m = false\n\
|
|
||||||
supports_a = false\n\
|
|
||||||
supports_f = false\n\
|
|
||||||
supports_d = false\n\
|
|
||||||
enable_m = true\n\
|
|
||||||
enable_e = false\n"
|
|
||||||
);
|
|
||||||
// Predicates are not part of the Display output.
|
|
||||||
assert_eq!(f.full_float(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn predicates() {
|
|
||||||
let mut sb = settings::builder();
|
|
||||||
sb.set("enable_simd", "true").unwrap();
|
|
||||||
let shared = settings::Flags::new(sb);
|
|
||||||
let mut b = builder();
|
|
||||||
b.enable("supports_f").unwrap();
|
|
||||||
b.enable("supports_d").unwrap();
|
|
||||||
let f = Flags::new(&shared, b);
|
|
||||||
assert_eq!(f.full_float(), true);
|
|
||||||
|
|
||||||
let mut sb = settings::builder();
|
|
||||||
sb.set("enable_simd", "false").unwrap();
|
|
||||||
let shared = settings::Flags::new(sb);
|
|
||||||
let mut b = builder();
|
|
||||||
b.enable("supports_f").unwrap();
|
|
||||||
b.enable("supports_d").unwrap();
|
|
||||||
let f = Flags::new(&shared, b);
|
|
||||||
assert_eq!(f.full_float(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -84,12 +84,6 @@ pub(crate) mod aarch64;
|
|||||||
#[cfg(feature = "s390x")]
|
#[cfg(feature = "s390x")]
|
||||||
mod s390x;
|
mod s390x;
|
||||||
|
|
||||||
#[cfg(feature = "riscv")]
|
|
||||||
mod legacy;
|
|
||||||
|
|
||||||
#[cfg(feature = "riscv")]
|
|
||||||
use legacy::riscv;
|
|
||||||
|
|
||||||
pub mod unwind;
|
pub mod unwind;
|
||||||
|
|
||||||
mod call_conv;
|
mod call_conv;
|
||||||
@@ -121,9 +115,6 @@ macro_rules! isa_builder {
|
|||||||
/// by `variant` if available.
|
/// by `variant` if available.
|
||||||
pub fn lookup_variant(triple: Triple) -> Result<Builder, LookupError> {
|
pub fn lookup_variant(triple: Triple) -> Result<Builder, LookupError> {
|
||||||
match triple.architecture {
|
match triple.architecture {
|
||||||
Architecture::Riscv32 { .. } | Architecture::Riscv64 { .. } => {
|
|
||||||
isa_builder!(riscv, (feature = "riscv"), triple)
|
|
||||||
}
|
|
||||||
Architecture::X86_64 => {
|
Architecture::X86_64 => {
|
||||||
isa_builder!(x64, (feature = "x86"), triple)
|
isa_builder!(x64, (feature = "x86"), triple)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,19 +13,12 @@
|
|||||||
//! The legalizer does not deal with register allocation constraints. These constraints are derived
|
//! The legalizer does not deal with register allocation constraints. These constraints are derived
|
||||||
//! from the encoding recipes, and solved later by the register allocator.
|
//! from the encoding recipes, and solved later by the register allocator.
|
||||||
|
|
||||||
#[cfg(any(feature = "x86", feature = "riscv"))]
|
|
||||||
use crate::bitset::BitSet;
|
|
||||||
use crate::cursor::{Cursor, FuncCursor};
|
use crate::cursor::{Cursor, FuncCursor};
|
||||||
use crate::flowgraph::ControlFlowGraph;
|
use crate::flowgraph::ControlFlowGraph;
|
||||||
use crate::ir::types::{I32, I64};
|
use crate::ir::types::{I32, I64};
|
||||||
use crate::ir::{self, InstBuilder, MemFlags};
|
use crate::ir::{self, InstBuilder, MemFlags};
|
||||||
use crate::isa::TargetIsa;
|
use crate::isa::TargetIsa;
|
||||||
|
|
||||||
#[cfg(feature = "riscv")]
|
|
||||||
use crate::predicates;
|
|
||||||
#[cfg(feature = "riscv")]
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
use crate::timing;
|
use crate::timing;
|
||||||
use alloc::collections::BTreeSet;
|
use alloc::collections::BTreeSet;
|
||||||
|
|
||||||
@@ -37,8 +30,6 @@ mod libcall;
|
|||||||
mod split;
|
mod split;
|
||||||
mod table;
|
mod table;
|
||||||
|
|
||||||
#[cfg(any(feature = "x86", feature = "riscv"))]
|
|
||||||
use self::call::expand_call;
|
|
||||||
use self::globalvalue::expand_global_value;
|
use self::globalvalue::expand_global_value;
|
||||||
use self::heap::expand_heap_addr;
|
use self::heap::expand_heap_addr;
|
||||||
pub(crate) use self::libcall::expand_as_libcall;
|
pub(crate) use self::libcall::expand_as_libcall;
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
; Test the legalization of function signatures for RV32E.
|
|
||||||
test legalizer
|
|
||||||
target riscv32 enable_e
|
|
||||||
|
|
||||||
; regex: V=v\d+
|
|
||||||
|
|
||||||
function %f() {
|
|
||||||
; Spilling into the stack args after %x15 since %16 and up are not
|
|
||||||
; available in RV32E.
|
|
||||||
sig0 = (i64, i64, i64, i64) -> i64 system_v
|
|
||||||
; check: sig0 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13], i32 [%x14], i32 [%x15], i32 [0], i32 [4]) -> i32 [%x10], i32 [%x11] system_v
|
|
||||||
block0:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
; Test the legalization of function signatures.
|
|
||||||
test legalizer
|
|
||||||
target riscv32
|
|
||||||
|
|
||||||
; regex: V=v\d+
|
|
||||||
|
|
||||||
function %f() {
|
|
||||||
sig0 = (i32) -> i32 system_v
|
|
||||||
; check: sig0 = (i32 [%x10]) -> i32 [%x10] system_v
|
|
||||||
|
|
||||||
sig1 = (i64) -> b1 system_v
|
|
||||||
; check: sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] system_v
|
|
||||||
|
|
||||||
; The i64 argument must go in an even-odd register pair.
|
|
||||||
sig2 = (f32, i64) -> f64 system_v
|
|
||||||
; check: sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] system_v
|
|
||||||
|
|
||||||
; Spilling into the stack args.
|
|
||||||
sig3 = (f64, f64, f64, f64, f64, f64, f64, i64) -> f64 system_v
|
|
||||||
; check: sig3 = (f64 [%f10], f64 [%f11], f64 [%f12], f64 [%f13], f64 [%f14], f64 [%f15], f64 [%f16], i32 [0], i32 [4]) -> f64 [%f10] system_v
|
|
||||||
|
|
||||||
; Splitting vectors.
|
|
||||||
sig4 = (i32x4) system_v
|
|
||||||
; check: sig4 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13]) system_v
|
|
||||||
|
|
||||||
; Splitting vectors, then splitting ints.
|
|
||||||
sig5 = (i64x4) system_v
|
|
||||||
; check: sig5 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13], i32 [%x14], i32 [%x15], i32 [%x16], i32 [%x17]) system_v
|
|
||||||
|
|
||||||
block0:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
; Binary emission of 32-bit code.
|
|
||||||
test binemit
|
|
||||||
target riscv32
|
|
||||||
|
|
||||||
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
|
||||||
sig0 = ()
|
|
||||||
fn0 = %foo()
|
|
||||||
|
|
||||||
block0(v9999: i32):
|
|
||||||
[-,%x10] v1 = iconst.i32 1
|
|
||||||
[-,%x21] v2 = iconst.i32 2
|
|
||||||
|
|
||||||
; Integer Register-Register Operations.
|
|
||||||
; add
|
|
||||||
[-,%x7] v10 = iadd v1, v2 ; bin: 015503b3
|
|
||||||
[-,%x16] v11 = iadd v2, v1 ; bin: 00aa8833
|
|
||||||
; sub
|
|
||||||
[-,%x7] v12 = isub v1, v2 ; bin: 415503b3
|
|
||||||
[-,%x16] v13 = isub v2, v1 ; bin: 40aa8833
|
|
||||||
; and
|
|
||||||
[-,%x7] v20 = band v1, v2 ; bin: 015573b3
|
|
||||||
[-,%x16] v21 = band v2, v1 ; bin: 00aaf833
|
|
||||||
; or
|
|
||||||
[-,%x7] v22 = bor v1, v2 ; bin: 015563b3
|
|
||||||
[-,%x16] v23 = bor v2, v1 ; bin: 00aae833
|
|
||||||
; xor
|
|
||||||
[-,%x7] v24 = bxor v1, v2 ; bin: 015543b3
|
|
||||||
[-,%x16] v25 = bxor v2, v1 ; bin: 00aac833
|
|
||||||
; sll
|
|
||||||
[-,%x7] v30 = ishl v1, v2 ; bin: 015513b3
|
|
||||||
[-,%x16] v31 = ishl v2, v1 ; bin: 00aa9833
|
|
||||||
; srl
|
|
||||||
[-,%x7] v32 = ushr v1, v2 ; bin: 015553b3
|
|
||||||
[-,%x16] v33 = ushr v2, v1 ; bin: 00aad833
|
|
||||||
; sra
|
|
||||||
[-,%x7] v34 = sshr v1, v2 ; bin: 415553b3
|
|
||||||
[-,%x16] v35 = sshr v2, v1 ; bin: 40aad833
|
|
||||||
; slt
|
|
||||||
[-,%x7] v42 = icmp slt v1, v2 ; bin: 015523b3
|
|
||||||
[-,%x16] v43 = icmp slt v2, v1 ; bin: 00aaa833
|
|
||||||
; sltu
|
|
||||||
[-,%x7] v44 = icmp ult v1, v2 ; bin: 015533b3
|
|
||||||
[-,%x16] v45 = icmp ult v2, v1 ; bin: 00aab833
|
|
||||||
|
|
||||||
; Integer Register-Immediate Instructions
|
|
||||||
|
|
||||||
; addi
|
|
||||||
[-,%x7] v100 = iadd_imm v1, 1000 ; bin: 3e850393
|
|
||||||
[-,%x16] v101 = iadd_imm v2, -905 ; bin: c77a8813
|
|
||||||
; andi
|
|
||||||
[-,%x7] v110 = band_imm v1, 1000 ; bin: 3e857393
|
|
||||||
[-,%x16] v111 = band_imm v2, -905 ; bin: c77af813
|
|
||||||
; ori
|
|
||||||
[-,%x7] v112 = bor_imm v1, 1000 ; bin: 3e856393
|
|
||||||
[-,%x16] v113 = bor_imm v2, -905 ; bin: c77ae813
|
|
||||||
; xori
|
|
||||||
[-,%x7] v114 = bxor_imm v1, 1000 ; bin: 3e854393
|
|
||||||
[-,%x16] v115 = bxor_imm v2, -905 ; bin: c77ac813
|
|
||||||
|
|
||||||
; slli
|
|
||||||
[-,%x7] v120 = ishl_imm v1, 31 ; bin: 01f51393
|
|
||||||
[-,%x16] v121 = ishl_imm v2, 8 ; bin: 008a9813
|
|
||||||
; srli
|
|
||||||
[-,%x7] v122 = ushr_imm v1, 31 ; bin: 01f55393
|
|
||||||
[-,%x16] v123 = ushr_imm v2, 8 ; bin: 008ad813
|
|
||||||
; srai
|
|
||||||
[-,%x7] v124 = sshr_imm v1, 31 ; bin: 41f55393
|
|
||||||
[-,%x16] v125 = sshr_imm v2, 8 ; bin: 408ad813
|
|
||||||
|
|
||||||
; slti
|
|
||||||
[-,%x7] v130 = icmp_imm slt v1, 1000 ; bin: 3e852393
|
|
||||||
[-,%x16] v131 = icmp_imm slt v2, -905 ; bin: c77aa813
|
|
||||||
; sltiu
|
|
||||||
[-,%x7] v132 = icmp_imm ult v1, 1000 ; bin: 3e853393
|
|
||||||
[-,%x16] v133 = icmp_imm ult v2, -905 ; bin: c77ab813
|
|
||||||
|
|
||||||
; lui
|
|
||||||
[-,%x7] v140 = iconst.i32 0x12345000 ; bin: 123453b7
|
|
||||||
[-,%x16] v141 = iconst.i32 0xffffffff_fedcb000 ; bin: fedcb837
|
|
||||||
; addi
|
|
||||||
[-,%x7] v142 = iconst.i32 1000 ; bin: 3e800393
|
|
||||||
[-,%x16] v143 = iconst.i32 -905 ; bin: c7700813
|
|
||||||
|
|
||||||
; Copies alias to iadd_imm.
|
|
||||||
[-,%x7] v150 = copy v1 ; bin: 00050393
|
|
||||||
[-,%x16] v151 = copy v2 ; bin: 000a8813
|
|
||||||
|
|
||||||
; Control Transfer Instructions
|
|
||||||
|
|
||||||
; jal %x1, fn0
|
|
||||||
call fn0() ; bin: Call(%foo) 000000ef
|
|
||||||
|
|
||||||
; jalr %x1, %x10
|
|
||||||
call_indirect sig0, v1() ; bin: 000500e7
|
|
||||||
call_indirect sig0, v2() ; bin: 000a80e7
|
|
||||||
|
|
||||||
brz v1, block3
|
|
||||||
fallthrough block4
|
|
||||||
|
|
||||||
block4:
|
|
||||||
brnz v1, block1
|
|
||||||
fallthrough block5
|
|
||||||
|
|
||||||
block5:
|
|
||||||
; jalr %x0, %x1, 0
|
|
||||||
return v9999 ; bin: 00008067
|
|
||||||
|
|
||||||
block1:
|
|
||||||
; beq 0x000
|
|
||||||
br_icmp eq v1, v2, block1 ; bin: 01550063
|
|
||||||
fallthrough block100
|
|
||||||
|
|
||||||
block100:
|
|
||||||
; bne 0xffc
|
|
||||||
br_icmp ne v1, v2, block1 ; bin: ff551ee3
|
|
||||||
fallthrough block101
|
|
||||||
|
|
||||||
block101:
|
|
||||||
; blt 0xff8
|
|
||||||
br_icmp slt v1, v2, block1 ; bin: ff554ce3
|
|
||||||
fallthrough block102
|
|
||||||
|
|
||||||
block102:
|
|
||||||
; bge 0xff4
|
|
||||||
br_icmp sge v1, v2, block1 ; bin: ff555ae3
|
|
||||||
fallthrough block103
|
|
||||||
|
|
||||||
block103:
|
|
||||||
; bltu 0xff0
|
|
||||||
br_icmp ult v1, v2, block1 ; bin: ff5568e3
|
|
||||||
fallthrough block104
|
|
||||||
|
|
||||||
block104:
|
|
||||||
; bgeu 0xfec
|
|
||||||
br_icmp uge v1, v2, block1 ; bin: ff5576e3
|
|
||||||
fallthrough block105
|
|
||||||
|
|
||||||
block105:
|
|
||||||
|
|
||||||
; Forward branches.
|
|
||||||
fallthrough block106
|
|
||||||
|
|
||||||
block106:
|
|
||||||
; beq 0x018
|
|
||||||
br_icmp eq v2, v1, block2 ; bin: 00aa8c63
|
|
||||||
fallthrough block107
|
|
||||||
|
|
||||||
block107:
|
|
||||||
; bne 0x014
|
|
||||||
br_icmp ne v2, v1, block2 ; bin: 00aa9a63
|
|
||||||
fallthrough block108
|
|
||||||
|
|
||||||
block108:
|
|
||||||
; blt 0x010
|
|
||||||
br_icmp slt v2, v1, block2 ; bin: 00aac863
|
|
||||||
fallthrough block109
|
|
||||||
|
|
||||||
block109:
|
|
||||||
; bge 0x00c
|
|
||||||
br_icmp sge v2, v1, block2 ; bin: 00aad663
|
|
||||||
fallthrough block110
|
|
||||||
|
|
||||||
block110:
|
|
||||||
; bltu 0x008
|
|
||||||
br_icmp ult v2, v1, block2 ; bin: 00aae463
|
|
||||||
fallthrough block111
|
|
||||||
|
|
||||||
block111:
|
|
||||||
; bgeu 0x004
|
|
||||||
br_icmp uge v2, v1, block2 ; bin: 00aaf263
|
|
||||||
|
|
||||||
fallthrough block2
|
|
||||||
|
|
||||||
block2:
|
|
||||||
; jal %x0, 0x00000
|
|
||||||
jump block2 ; bin: 0000006f
|
|
||||||
|
|
||||||
block3:
|
|
||||||
; beq x, %x0
|
|
||||||
brz v1, block3 ; bin: 00050063
|
|
||||||
fallthrough block6
|
|
||||||
|
|
||||||
block6:
|
|
||||||
; bne x, %x0
|
|
||||||
brnz v1, block3 ; bin: fe051ee3
|
|
||||||
|
|
||||||
; jal %x0, 0x1ffff4
|
|
||||||
jump block2 ; bin: ff5ff06f
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
test legalizer
|
|
||||||
target riscv32 supports_m=1
|
|
||||||
|
|
||||||
function %int32(i32, i32) {
|
|
||||||
block0(v1: i32, v2: i32):
|
|
||||||
v10 = iadd v1, v2
|
|
||||||
; check: [R#0c]
|
|
||||||
; sameln: v10 = iadd
|
|
||||||
|
|
||||||
v11 = isub v1, v2
|
|
||||||
; check: [R#200c]
|
|
||||||
; sameln: v11 = isub
|
|
||||||
|
|
||||||
v12 = imul v1, v2
|
|
||||||
; check: [R#10c]
|
|
||||||
; sameln: v12 = imul
|
|
||||||
|
|
||||||
return
|
|
||||||
; check: [Iret#19]
|
|
||||||
; sameln: return
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
; Test the legalization of i32 instructions that don't have RISC-V versions.
|
|
||||||
test legalizer
|
|
||||||
|
|
||||||
target riscv32 supports_m=1
|
|
||||||
|
|
||||||
target riscv64 supports_m=1
|
|
||||||
|
|
||||||
; regex: V=v\d+
|
|
||||||
|
|
||||||
function %carry_out(i32, i32) -> i32, b1 {
|
|
||||||
block0(v1: i32, v2: i32):
|
|
||||||
v3, v4 = iadd_cout v1, v2
|
|
||||||
return v3, v4
|
|
||||||
}
|
|
||||||
; check: v3 = iadd v1, v2
|
|
||||||
; check: v4 = icmp ult v3, v1
|
|
||||||
; check: return v3, v4
|
|
||||||
|
|
||||||
; Expanding illegal immediate constants.
|
|
||||||
; Note that at some point we'll probably expand the iconst as well.
|
|
||||||
function %large_imm(i32) -> i32 {
|
|
||||||
block0(v0: i32):
|
|
||||||
v1 = iadd_imm v0, 1000000000
|
|
||||||
return v1
|
|
||||||
}
|
|
||||||
; check: $(cst=$V) = iconst.i32 0x3b9a_ca00
|
|
||||||
; check: v1 = iadd v0, $cst
|
|
||||||
; check: return v1
|
|
||||||
|
|
||||||
function %bitclear(i32, i32) -> i32 {
|
|
||||||
block0(v0: i32, v1: i32):
|
|
||||||
v2 = band_not v0, v1
|
|
||||||
; check: iconst.i32 -1
|
|
||||||
; check: bxor
|
|
||||||
; check: band
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
; Test legalizer's handling of ABI boundaries.
|
|
||||||
test legalizer
|
|
||||||
target riscv32
|
|
||||||
|
|
||||||
; regex: V=v\d+
|
|
||||||
; regex: SS=ss\d+
|
|
||||||
; regex: WS=\s+
|
|
||||||
|
|
||||||
function %int_split_args(i64) -> i64 {
|
|
||||||
block0(v0: i64):
|
|
||||||
; check: block0($(v0l=$V): i32, $(v0h=$V): i32, $(link=$V): i32):
|
|
||||||
; check: v0 = iconcat $v0l, $v0h
|
|
||||||
v1 = iadd_imm v0, 1
|
|
||||||
; check: $(v1l=$V), $(v1h=$V) = isplit v1
|
|
||||||
; check: return $v1l, $v1h, $link
|
|
||||||
return v1
|
|
||||||
}
|
|
||||||
|
|
||||||
function %split_call_arg(i32) {
|
|
||||||
fn1 = %foo(i64)
|
|
||||||
fn2 = %foo(i32, i64)
|
|
||||||
block0(v0: i32):
|
|
||||||
v1 = uextend.i64 v0
|
|
||||||
call fn1(v1)
|
|
||||||
; check: $(v1h=$V) = iconst.i32 0
|
|
||||||
; check: call fn1(v0, $v1h)
|
|
||||||
call fn2(v0, v1)
|
|
||||||
; check: call fn2(v0, $V, $V)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function %split_ret_val() {
|
|
||||||
fn1 = %foo() -> i64
|
|
||||||
block0:
|
|
||||||
v1 = call fn1()
|
|
||||||
; check: block0($(link=$V): i32):
|
|
||||||
; nextln: $(v1l=$V), $(v1h=$V) = call fn1()
|
|
||||||
; check: v1 = iconcat $v1l, $v1h
|
|
||||||
jump block1(v1)
|
|
||||||
; check: jump block1(v1)
|
|
||||||
|
|
||||||
block1(v10: i64):
|
|
||||||
jump block1(v10)
|
|
||||||
}
|
|
||||||
|
|
||||||
; First return value is fine, second one is expanded.
|
|
||||||
function %split_ret_val2() {
|
|
||||||
fn1 = %foo() -> i32, i64
|
|
||||||
block0:
|
|
||||||
v1, v2 = call fn1()
|
|
||||||
; check: block0($(link=$V): i32):
|
|
||||||
; nextln: v1, $(v2l=$V), $(v2h=$V) = call fn1()
|
|
||||||
; check: v2 = iconcat $v2l, $v2h
|
|
||||||
jump block1(v1, v2)
|
|
||||||
; check: jump block1(v1, v2)
|
|
||||||
|
|
||||||
block1(v9: i32, v10: i64):
|
|
||||||
jump block1(v9, v10)
|
|
||||||
}
|
|
||||||
|
|
||||||
function %int_ext(i8, i8 sext, i8 uext) -> i8 uext {
|
|
||||||
block0(v1: i8, v2: i8, v3: i8):
|
|
||||||
; check: block0(v1: i8, $(v2x=$V): i32, $(v3x=$V): i32, $(link=$V): i32):
|
|
||||||
; check: v2 = ireduce.i8 $v2x
|
|
||||||
; check: v3 = ireduce.i8 $v3x
|
|
||||||
; check: $(v1x=$V) = uextend.i32 v1
|
|
||||||
; check: return $v1x, $link
|
|
||||||
return v1
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function produces single return value, still need to copy.
|
|
||||||
function %ext_ret_val() {
|
|
||||||
fn1 = %foo() -> i8 sext
|
|
||||||
block0:
|
|
||||||
v1 = call fn1()
|
|
||||||
; check: block0($V: i32):
|
|
||||||
; nextln: $(rv=$V) = call fn1()
|
|
||||||
; check: v1 = ireduce.i8 $rv
|
|
||||||
jump block1(v1)
|
|
||||||
; check: jump block1(v1)
|
|
||||||
|
|
||||||
block1(v10: i8):
|
|
||||||
jump block1(v10)
|
|
||||||
}
|
|
||||||
|
|
||||||
function %vector_split_args(i64x4) -> i64x4 {
|
|
||||||
block0(v0: i64x4):
|
|
||||||
; check: block0($(v0al=$V): i32, $(v0ah=$V): i32, $(v0bl=$V): i32, $(v0bh=$V): i32, $(v0cl=$V): i32, $(v0ch=$V): i32, $(v0dl=$V): i32, $(v0dh=$V): i32, $(link=$V): i32):
|
|
||||||
; check: $(v0a=$V) = iconcat $v0al, $v0ah
|
|
||||||
; check: $(v0b=$V) = iconcat $v0bl, $v0bh
|
|
||||||
; check: $(v0ab=$V) = vconcat $v0a, $v0b
|
|
||||||
; check: $(v0c=$V) = iconcat $v0cl, $v0ch
|
|
||||||
; check: $(v0d=$V) = iconcat $v0dl, $v0dh
|
|
||||||
; check: $(v0cd=$V) = vconcat $v0c, $v0d
|
|
||||||
; check: v0 = vconcat $v0ab, $v0cd
|
|
||||||
v1 = bxor v0, v0
|
|
||||||
; check: $(v1ab=$V), $(v1cd=$V) = vsplit v1
|
|
||||||
; check: $(v1a=$V), $(v1b=$V) = vsplit $v1ab
|
|
||||||
; check: $(v1al=$V), $(v1ah=$V) = isplit $v1a
|
|
||||||
; check: $(v1bl=$V), $(v1bh=$V) = isplit $v1b
|
|
||||||
; check: $(v1c=$V), $(v1d=$V) = vsplit $v1cd
|
|
||||||
; check: $(v1cl=$V), $(v1ch=$V) = isplit $v1c
|
|
||||||
; check: $(v1dl=$V), $(v1dh=$V) = isplit $v1d
|
|
||||||
; check: return $v1al, $v1ah, $v1bl, $v1bh, $v1cl, $v1ch, $v1dl, $v1dh, $link
|
|
||||||
return v1
|
|
||||||
}
|
|
||||||
|
|
||||||
function %indirect(i32) {
|
|
||||||
sig1 = () system_v
|
|
||||||
block0(v0: i32):
|
|
||||||
call_indirect sig1, v0()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
; The first argument to call_indirect doesn't get altered.
|
|
||||||
function %indirect_arg(i32, f32x2) {
|
|
||||||
sig1 = (f32x2) system_v
|
|
||||||
block0(v0: i32, v1: f32x2):
|
|
||||||
call_indirect sig1, v0(v1)
|
|
||||||
; check: call_indirect sig1, v0($V, $V)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
; Call a function that takes arguments on the stack.
|
|
||||||
function %stack_args(i32) {
|
|
||||||
; check: $(ss0=$SS) = outgoing_arg 4
|
|
||||||
fn1 = %foo(i64, i64, i64, i64, i32)
|
|
||||||
block0(v0: i32):
|
|
||||||
v1 = iconst.i64 1
|
|
||||||
call fn1(v1, v1, v1, v1, v0)
|
|
||||||
; check: [GPsp#48,$ss0]$WS $(v0s=$V) = spill v0
|
|
||||||
; check: call fn1($(=.*), $v0s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
; Test the legalization of i64 arithmetic instructions.
|
|
||||||
test legalizer
|
|
||||||
target riscv32 supports_m=1
|
|
||||||
|
|
||||||
; regex: V=v\d+
|
|
||||||
|
|
||||||
function %bitwise_and(i64, i64) -> i64 {
|
|
||||||
block0(v1: i64, v2: i64):
|
|
||||||
v3 = band v1, v2
|
|
||||||
return v3
|
|
||||||
}
|
|
||||||
; check: block0($(v1l=$V): i32, $(v1h=$V): i32, $(v2l=$V): i32, $(v2h=$V): i32, $(link=$V): i32):
|
|
||||||
; check: [R#ec
|
|
||||||
; sameln: $(v3l=$V) = band $v1l, $v2l
|
|
||||||
; check: [R#ec
|
|
||||||
; sameln: $(v3h=$V) = band $v1h, $v2h
|
|
||||||
; check: v3 = iconcat $v3l, $v3h
|
|
||||||
; check: return $v3l, $v3h, $link
|
|
||||||
|
|
||||||
function %bitwise_or(i64, i64) -> i64 {
|
|
||||||
block0(v1: i64, v2: i64):
|
|
||||||
v3 = bor v1, v2
|
|
||||||
return v3
|
|
||||||
}
|
|
||||||
; check: block0($(v1l=$V): i32, $(v1h=$V): i32, $(v2l=$V): i32, $(v2h=$V): i32, $(link=$V): i32):
|
|
||||||
; check: [R#cc
|
|
||||||
; sameln: $(v3l=$V) = bor $v1l, $v2l
|
|
||||||
; check: [R#cc
|
|
||||||
; sameln: $(v3h=$V) = bor $v1h, $v2h
|
|
||||||
; check: v3 = iconcat $v3l, $v3h
|
|
||||||
; check: return $v3l, $v3h, $link
|
|
||||||
|
|
||||||
function %bitwise_xor(i64, i64) -> i64 {
|
|
||||||
block0(v1: i64, v2: i64):
|
|
||||||
v3 = bxor v1, v2
|
|
||||||
return v3
|
|
||||||
}
|
|
||||||
; check: block0($(v1l=$V): i32, $(v1h=$V): i32, $(v2l=$V): i32, $(v2h=$V): i32, $(link=$V): i32):
|
|
||||||
; check: [R#8c
|
|
||||||
; sameln: $(v3l=$V) = bxor $v1l, $v2l
|
|
||||||
; check: [R#8c
|
|
||||||
; sameln: $(v3h=$V) = bxor $v1h, $v2h
|
|
||||||
; check: v3 = iconcat $v3l, $v3h
|
|
||||||
; check: return $v3l, $v3h, $link
|
|
||||||
|
|
||||||
function %arith_add(i64, i64) -> i64 {
|
|
||||||
; Legalizing iadd.i64 requires two steps:
|
|
||||||
; 1. Narrow to iadd_cout.i32, then
|
|
||||||
; 2. Expand iadd_cout.i32 since RISC-V has no carry flag.
|
|
||||||
block0(v1: i64, v2: i64):
|
|
||||||
v3 = iadd v1, v2
|
|
||||||
return v3
|
|
||||||
}
|
|
||||||
; check: block0($(v1l=$V): i32, $(v1h=$V): i32, $(v2l=$V): i32, $(v2h=$V): i32, $(link=$V): i32):
|
|
||||||
; check: [R#0c
|
|
||||||
; sameln: $(v3l=$V) = iadd $v1l, $v2l
|
|
||||||
; check: $(c=$V) = icmp ult $v3l, $v1l
|
|
||||||
; check: [R#0c
|
|
||||||
; sameln: $(v3h1=$V) = iadd $v1h, $v2h
|
|
||||||
; check: $(c_int=$V) = bint.i32 $c
|
|
||||||
; check: [R#0c
|
|
||||||
; sameln: $(v3h=$V) = iadd $v3h1, $c_int
|
|
||||||
; check: v3 = iconcat $v3l, $v3h
|
|
||||||
; check: return $v3l, $v3h, $link
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
test legalizer
|
|
||||||
target riscv32
|
|
||||||
|
|
||||||
; regex: V=v\d+
|
|
||||||
|
|
||||||
function %icmp_imm_eq(i64) -> b1 {
|
|
||||||
block0(v0: i64):
|
|
||||||
v1 = icmp_imm eq v0, 0x20202020_10101010
|
|
||||||
return v1
|
|
||||||
}
|
|
||||||
; check: block0($(v0l=$V): i32, $(v0h=$V): i32, $(link=$V): i32):
|
|
||||||
; nextln: $(v2l=$V) -> $(v0l)
|
|
||||||
; nextln: $(v2h=$V) -> $(v0h)
|
|
||||||
; nextln: v0 = iconcat $(v0l), $(v0h)
|
|
||||||
; nextln: $(imm_low=$V) = iconst.i32 0x1010_1010
|
|
||||||
; nextln: $(imm_high=$V) = iconst.i32 0x2020_2020
|
|
||||||
; nextln: $(v3=$V) = icmp eq $(v2l), $(imm_low)
|
|
||||||
; nextln: $(v4=$V) = icmp eq $(v2h), $(imm_high)
|
|
||||||
; nextln: v1 = band $(v3), $(v4)
|
|
||||||
; nextln: return v1, $(link)
|
|
||||||
|
|
||||||
function %icmp_imm_ne(i64) -> b1 {
|
|
||||||
block0(v0: i64):
|
|
||||||
v1 = icmp_imm ne v0, 0x33333333_44444444
|
|
||||||
return v1
|
|
||||||
}
|
|
||||||
; check: block0($(v0l=$V): i32, $(v0h=$V): i32, $(link=$V): i32):
|
|
||||||
; nextln: $(v2l=$V) -> $(v0l)
|
|
||||||
; nextln: $(v2h=$V) -> $(v0h)
|
|
||||||
; nextln: v0 = iconcat $(v0l), $(v0h)
|
|
||||||
; nextln: $(imm_low=$V) = iconst.i32 0x4444_4444
|
|
||||||
; nextln: $(imm_high=$V) = iconst.i32 0x3333_3333
|
|
||||||
; nextln: $(v3=$V) = icmp ne $(v2l), $(imm_low)
|
|
||||||
; nextln: $(v4=$V) = icmp ne $(v2h), $(imm_high)
|
|
||||||
; nextln: v1 = bor $(v3), $(v4)
|
|
||||||
; nextln: return v1, $(link)
|
|
||||||
|
|
||||||
function %icmp_imm_sge(i64) -> b1 {
|
|
||||||
block0(v0: i64):
|
|
||||||
v1 = icmp_imm sge v0, 0x01020304_05060708
|
|
||||||
return v1
|
|
||||||
}
|
|
||||||
; check: block0($(v0l=$V): i32, $(v0h=$V): i32, $(link=$V): i32):
|
|
||||||
; nextln: $(v2l=$V) -> $(v0l)
|
|
||||||
; nextln: $(v2h=$V) -> $(v0h)
|
|
||||||
; nextln: v0 = iconcat $(v0l), $(v0h)
|
|
||||||
; nextln: $(imm_low=$V) = iconst.i32 0x0506_0708
|
|
||||||
; nextln: $(imm_high=$V) = iconst.i32 0x0102_0304
|
|
||||||
; nextln: $(v3=$V) = icmp sgt $(v2h), $(imm_high)
|
|
||||||
; nextln: $(v4=$V) = icmp slt $(v2h), $(imm_high)
|
|
||||||
; nextln: $(v5=$V) = icmp uge $(v2l), $(imm_low)
|
|
||||||
; nextln: $(v6=$V) = bnot $v4
|
|
||||||
; nextln: $(v7=$V) = band $v6, $v5
|
|
||||||
; nextln: v1 = bor $(v3), $(v7)
|
|
||||||
; nextln: return v1, $(link)
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
; Test the parser's support for encoding annotations.
|
|
||||||
test legalizer
|
|
||||||
target riscv32
|
|
||||||
|
|
||||||
function %parse_encoding(i32 [%x5]) -> i32 [%x10] {
|
|
||||||
; check: function %parse_encoding(i32 [%x5], i32 link [%x1]) -> i32 [%x10], i32 link [%x1] fast {
|
|
||||||
|
|
||||||
sig0 = (i32 [%x10]) -> i32 [%x10] system_v
|
|
||||||
; check: sig0 = (i32 [%x10]) -> i32 [%x10] system_v
|
|
||||||
|
|
||||||
sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] system_v
|
|
||||||
; check: sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] system_v
|
|
||||||
|
|
||||||
sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] system_v
|
|
||||||
; check: sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] system_v
|
|
||||||
|
|
||||||
; Arguments on stack where not necessary
|
|
||||||
sig3 = (f64 [%f10], i32 [0], i32 [4]) -> f64 [%f10] system_v
|
|
||||||
; check: sig3 = (f64 [%f10], i32 [0], i32 [4]) -> f64 [%f10] system_v
|
|
||||||
|
|
||||||
; Stack argument before register argument
|
|
||||||
sig4 = (f32 [72], i32 [%x10]) system_v
|
|
||||||
; check: sig4 = (f32 [72], i32 [%x10]) system_v
|
|
||||||
|
|
||||||
; Return value on stack
|
|
||||||
sig5 = () -> f32 [0] system_v
|
|
||||||
; check: sig5 = () -> f32 [0] system_v
|
|
||||||
|
|
||||||
; function + signature
|
|
||||||
fn0 = %bar(i32 [%x10]) -> b1 [%x10] system_v
|
|
||||||
; check: sig6 = (i32 [%x10]) -> b1 [%x10] system_v
|
|
||||||
; nextln: fn0 = %bar sig6
|
|
||||||
|
|
||||||
block0(v0: i32):
|
|
||||||
return v0
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
; Test tracking of register moves.
|
|
||||||
test binemit
|
|
||||||
target riscv32
|
|
||||||
|
|
||||||
function %regmoves(i32 link [%x1]) -> i32 link [%x1] {
|
|
||||||
block0(v9999: i32):
|
|
||||||
[-,%x10] v1 = iconst.i32 1
|
|
||||||
[-,%x7] v2 = iadd_imm v1, 1000 ; bin: 3e850393
|
|
||||||
regmove v1, %x10 -> %x11 ; bin: 00050593
|
|
||||||
[-,%x7] v3 = iadd_imm v1, 1000 ; bin: 3e858393
|
|
||||||
regmove v1, %x11 -> %x10 ; bin: 00058513
|
|
||||||
[-,%x7] v4 = iadd_imm v1, 1000 ; bin: 3e850393
|
|
||||||
|
|
||||||
return v9999
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
; Test the legalization of block arguments that are split.
|
|
||||||
test legalizer
|
|
||||||
target riscv32
|
|
||||||
|
|
||||||
; regex: V=v\d+
|
|
||||||
|
|
||||||
function %simple(i64, i64) -> i64 {
|
|
||||||
block0(v1: i64, v2: i64):
|
|
||||||
; check: block0($(v1l=$V): i32, $(v1h=$V): i32, $(v2l=$V): i32, $(v2h=$V): i32, $(link=$V): i32):
|
|
||||||
jump block1(v1)
|
|
||||||
; check: jump block1($v1l, $v1h)
|
|
||||||
|
|
||||||
block1(v3: i64):
|
|
||||||
; check: block1($(v3l=$V): i32, $(v3h=$V): i32):
|
|
||||||
v4 = band v3, v2
|
|
||||||
; check: $(v4l=$V) = band $v3l, $v2l
|
|
||||||
; check: $(v4h=$V) = band $v3h, $v2h
|
|
||||||
return v4
|
|
||||||
; check: return $v4l, $v4h, $link
|
|
||||||
}
|
|
||||||
|
|
||||||
function %multi(i64) -> i64 {
|
|
||||||
block1(v1: i64):
|
|
||||||
; check: block1($(v1l=$V): i32, $(v1h=$V): i32, $(link=$V): i32):
|
|
||||||
jump block2(v1, v1)
|
|
||||||
; check: jump block2($v1l, $v1l, $v1h, $v1h)
|
|
||||||
|
|
||||||
block2(v2: i64, v3: i64):
|
|
||||||
; check: block2($(v2l=$V): i32, $(v3l=$V): i32, $(v2h=$V): i32, $(v3h=$V): i32):
|
|
||||||
jump block3(v2)
|
|
||||||
; check: jump block3($v2l, $v2h)
|
|
||||||
|
|
||||||
block3(v4: i64):
|
|
||||||
; check: block3($(v4l=$V): i32, $(v4h=$V): i32):
|
|
||||||
v5 = band v4, v3
|
|
||||||
; check: $(v5l=$V) = band $v4l, $v3l
|
|
||||||
; check: $(v5h=$V) = band $v4h, $v3h
|
|
||||||
return v5
|
|
||||||
; check: return $v5l, $v5h, $link
|
|
||||||
}
|
|
||||||
|
|
||||||
function %loop(i64, i64) -> i64 {
|
|
||||||
block0(v1: i64, v2: i64):
|
|
||||||
; check: block0($(v1l=$V): i32, $(v1h=$V): i32, $(v2l=$V): i32, $(v2h=$V): i32, $(link=$V): i32):
|
|
||||||
jump block1(v1)
|
|
||||||
; check: jump block1($v1l, $v1h)
|
|
||||||
|
|
||||||
block1(v3: i64):
|
|
||||||
; check: block1($(v3l=$V): i32, $(v3h=$V): i32):
|
|
||||||
v4 = band v3, v2
|
|
||||||
; check: $(v4l=$V) = band $v3l, $v2l
|
|
||||||
; check: $(v4h=$V) = band $v3h, $v2h
|
|
||||||
jump block1(v4)
|
|
||||||
; check: jump block1($v4l, $v4h)
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
test verifier
|
|
||||||
target riscv32
|
|
||||||
|
|
||||||
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
|
||||||
fn0 = %foo()
|
|
||||||
|
|
||||||
block0(v9999: i32):
|
|
||||||
; iconst.i32 needs legalizing, so it should throw a
|
|
||||||
[R#0,-] v1 = iconst.i32 0xf0f0f0f0f0 ; error: Instruction failed to re-encode
|
|
||||||
[Iret#19] return v9999
|
|
||||||
}
|
|
||||||
|
|
||||||
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
|
||||||
fn0 = %foo()
|
|
||||||
|
|
||||||
block0(v9999: i32):
|
|
||||||
v1 = iconst.i32 1
|
|
||||||
v2 = iconst.i32 2
|
|
||||||
[R#0,-] v3 = iadd v1, v2 ; error: encoding R#00 should be R#0c
|
|
||||||
[Iret#19] return v9999
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
test cat
|
|
||||||
|
|
||||||
target riscv32
|
|
||||||
|
|
||||||
; regex: WS=[ \t]*
|
|
||||||
|
|
||||||
function %foo(i32, i32) {
|
|
||||||
block1(v0: i32 [%x8], v1: i32):
|
|
||||||
[-,-] v2 = iadd v0, v1
|
|
||||||
[-] trap heap_oob
|
|
||||||
[R#1234, %x5, %x11] v6, v7 = iadd_ifcout v2, v0
|
|
||||||
[Rshamt#beef, %x25] v8 = ishl_imm v6, 2
|
|
||||||
@55 v9 = iadd v8, v7
|
|
||||||
@a5 [Iret#5] return v0, v8
|
|
||||||
}
|
|
||||||
; sameln: function %foo(i32, i32) fast {
|
|
||||||
; nextln: block1(v0: i32 [%x8], v1: i32):
|
|
||||||
; nextln: [-,-]$WS v2 = iadd v0, v1
|
|
||||||
; nextln: [-]$WS trap heap_oob
|
|
||||||
; nextln: [R#1234,%x5,%x11]$WS v6, v7 = iadd_ifcout v2, v0
|
|
||||||
; nextln: [Rshamt#beef,%x25]$WS v8 = ishl_imm v6, 2
|
|
||||||
; nextln: @0055 [-,-]$WS v9 = iadd v8, v7
|
|
||||||
; nextln: @00a5 [Iret#05]$WS return v0, v8
|
|
||||||
; nextln: }
|
|
||||||
@@ -3,7 +3,7 @@ use core::mem;
|
|||||||
use cranelift_codegen::binemit::{NullRelocSink, NullStackMapSink, NullTrapSink};
|
use cranelift_codegen::binemit::{NullRelocSink, NullStackMapSink, NullTrapSink};
|
||||||
use cranelift_codegen::data_value::DataValue;
|
use cranelift_codegen::data_value::DataValue;
|
||||||
use cranelift_codegen::ir::{condcodes::IntCC, Function, InstBuilder, Signature};
|
use cranelift_codegen::ir::{condcodes::IntCC, Function, InstBuilder, Signature};
|
||||||
use cranelift_codegen::isa::{BackendVariant, TargetIsa};
|
use cranelift_codegen::isa::TargetIsa;
|
||||||
use cranelift_codegen::{ir, settings, CodegenError, Context};
|
use cranelift_codegen::{ir, settings, CodegenError, Context};
|
||||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||||
use cranelift_native::builder_with_options;
|
use cranelift_native::builder_with_options;
|
||||||
|
|||||||
@@ -111,28 +111,6 @@ cfg_if! {
|
|||||||
|
|
||||||
fn get_disassembler(isa: &dyn TargetIsa) -> Result<Capstone> {
|
fn get_disassembler(isa: &dyn TargetIsa) -> Result<Capstone> {
|
||||||
let cs = match isa.triple().architecture {
|
let cs = match isa.triple().architecture {
|
||||||
Architecture::Riscv32(_) => {
|
|
||||||
let mut cs = Capstone::new()
|
|
||||||
.riscv()
|
|
||||||
.mode(arch::riscv::ArchMode::RiscV32)
|
|
||||||
.extra_mode(std::iter::once(arch::riscv::ArchExtraMode::RiscVC))
|
|
||||||
.build()
|
|
||||||
.map_err(map_caperr)?;
|
|
||||||
// See the comment of AArch64 below
|
|
||||||
cs.set_skipdata(true).map_err(map_caperr)?;
|
|
||||||
cs
|
|
||||||
}
|
|
||||||
Architecture::Riscv64(_) => {
|
|
||||||
let mut cs = Capstone::new()
|
|
||||||
.riscv()
|
|
||||||
.mode(arch::riscv::ArchMode::RiscV64)
|
|
||||||
.extra_mode(std::iter::once(arch::riscv::ArchExtraMode::RiscVC))
|
|
||||||
.build()
|
|
||||||
.map_err(map_caperr)?;
|
|
||||||
// See the comment of AArch64 below
|
|
||||||
cs.set_skipdata(true).map_err(map_caperr)?;
|
|
||||||
cs
|
|
||||||
}
|
|
||||||
Architecture::X86_32(_) => Capstone::new()
|
Architecture::X86_32(_) => Capstone::new()
|
||||||
.x86()
|
.x86()
|
||||||
.mode(arch::x86::ArchMode::Mode32)
|
.mode(arch::x86::ArchMode::Mode32)
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ smallvec = "1.6.1"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wat = "1.0.37"
|
wat = "1.0.37"
|
||||||
target-lexicon = "0.12"
|
target-lexicon = "0.12"
|
||||||
# Enable the riscv feature for cranelift-codegen, as some tests require it
|
cranelift-codegen = { path = "../codegen", version = "0.77.0", default-features = false }
|
||||||
cranelift-codegen = { path = "../codegen", version = "0.77.0", default-features = false, features = ["riscv"] }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
use cranelift_codegen::isa;
|
use cranelift_codegen::isa::{CallConv, TargetFrontendConfig};
|
||||||
use cranelift_codegen::print_errors::pretty_verifier_error;
|
use cranelift_codegen::print_errors::pretty_verifier_error;
|
||||||
use cranelift_codegen::settings::{self, Flags};
|
use cranelift_codegen::settings::{self, Flags};
|
||||||
use cranelift_codegen::verifier;
|
use cranelift_codegen::verifier;
|
||||||
use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode};
|
use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use target_lexicon::PointerWidth;
|
||||||
use target_lexicon::triple;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testsuite() {
|
fn testsuite() {
|
||||||
@@ -52,11 +51,15 @@ fn use_name_section() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let flags = Flags::new(settings::builder());
|
|
||||||
let triple = triple!("riscv64");
|
|
||||||
let isa = isa::lookup(triple).unwrap().finish(flags.clone());
|
|
||||||
let return_mode = ReturnMode::NormalReturns;
|
let return_mode = ReturnMode::NormalReturns;
|
||||||
let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), return_mode, false);
|
let mut dummy_environ = DummyEnvironment::new(
|
||||||
|
TargetFrontendConfig {
|
||||||
|
default_call_conv: CallConv::SystemV,
|
||||||
|
pointer_width: PointerWidth::U32,
|
||||||
|
},
|
||||||
|
return_mode,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
translate_module(data.as_ref(), &mut dummy_environ).unwrap();
|
translate_module(data.as_ref(), &mut dummy_environ).unwrap();
|
||||||
|
|
||||||
@@ -82,15 +85,20 @@ fn read_module(path: &Path) -> Vec<u8> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_module(data: Vec<u8>, flags: &Flags, return_mode: ReturnMode) {
|
fn handle_module(data: Vec<u8>, flags: &Flags, return_mode: ReturnMode) {
|
||||||
let triple = triple!("riscv64");
|
let mut dummy_environ = DummyEnvironment::new(
|
||||||
let isa = isa::lookup(triple).unwrap().finish(flags.clone());
|
TargetFrontendConfig {
|
||||||
let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), return_mode, false);
|
default_call_conv: CallConv::SystemV,
|
||||||
|
pointer_width: PointerWidth::U64,
|
||||||
|
},
|
||||||
|
return_mode,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
translate_module(&data, &mut dummy_environ).unwrap();
|
translate_module(&data, &mut dummy_environ).unwrap();
|
||||||
|
|
||||||
for func in dummy_environ.info.function_bodies.values() {
|
for func in dummy_environ.info.function_bodies.values() {
|
||||||
verifier::verify_function(func, &*isa)
|
verifier::verify_function(func, flags)
|
||||||
.map_err(|errors| panic!("{}", pretty_verifier_error(func, Some(&*isa), None, errors)))
|
.map_err(|errors| panic!("{}", pretty_verifier_error(func, None, None, errors)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,10 +176,14 @@ fn reachability_is_correct() {
|
|||||||
|
|
||||||
for (return_mode, wat, expected_reachability) in tests {
|
for (return_mode, wat, expected_reachability) in tests {
|
||||||
println!("testing wat:\n{}", wat);
|
println!("testing wat:\n{}", wat);
|
||||||
let flags = Flags::new(settings::builder());
|
let mut env = DummyEnvironment::new(
|
||||||
let triple = triple!("riscv64");
|
TargetFrontendConfig {
|
||||||
let isa = isa::lookup(triple).unwrap().finish(flags.clone());
|
default_call_conv: CallConv::SystemV,
|
||||||
let mut env = DummyEnvironment::new(isa.frontend_config(), return_mode, false);
|
pointer_width: PointerWidth::U64,
|
||||||
|
},
|
||||||
|
return_mode,
|
||||||
|
false,
|
||||||
|
);
|
||||||
env.test_expected_reachability(expected_reachability);
|
env.test_expected_reachability(expected_reachability);
|
||||||
let data = wat::parse_str(wat).unwrap();
|
let data = wat::parse_str(wat).unwrap();
|
||||||
translate_module(data.as_ref(), &mut env).unwrap();
|
translate_module(data.as_ref(), &mut env).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user