add riscv64 backend for cranelift. (#4271)
Add a RISC-V 64 (`riscv64`, RV64GC) backend. Co-authored-by: yuyang <756445638@qq.com> Co-authored-by: Chris Fallin <chris@cfallin.org> Co-authored-by: Afonso Bordado <afonsobordado@az8.co>
This commit is contained in:
@@ -68,7 +68,7 @@ unwind = ["gimli"]
|
||||
x86 = []
|
||||
arm64 = []
|
||||
s390x = []
|
||||
|
||||
riscv64 = []
|
||||
# Stub feature that does nothing, for Cargo-features compatibility: the new
|
||||
# backend is the default now.
|
||||
experimental_x64 = []
|
||||
@@ -77,7 +77,8 @@ experimental_x64 = []
|
||||
all-arch = [
|
||||
"x86",
|
||||
"arm64",
|
||||
"s390x"
|
||||
"s390x",
|
||||
"riscv64"
|
||||
]
|
||||
|
||||
# For dependent crates that want to serialize some parts of cranelift
|
||||
|
||||
@@ -187,6 +187,8 @@ fn get_isle_compilations(
|
||||
let src_isa_s390x =
|
||||
make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("isa").join("s390x"));
|
||||
|
||||
let src_isa_risc_v =
|
||||
make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("isa").join("riscv64"));
|
||||
// This is a set of ISLE compilation units.
|
||||
//
|
||||
// The format of each entry is:
|
||||
@@ -234,6 +236,16 @@ fn get_isle_compilations(
|
||||
],
|
||||
untracked_inputs: vec![clif_isle.clone()],
|
||||
},
|
||||
// The risc-v instruction selector.
|
||||
IsleCompilation {
|
||||
output: out_dir.join("isle_riscv64.rs"),
|
||||
inputs: vec![
|
||||
prelude_isle.clone(),
|
||||
src_isa_risc_v.join("inst.isle"),
|
||||
src_isa_risc_v.join("lower.isle"),
|
||||
],
|
||||
untracked_inputs: vec![clif_isle.clone()],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::shared::Definitions as SharedDefinitions;
|
||||
use std::fmt;
|
||||
|
||||
mod arm64;
|
||||
mod riscv64;
|
||||
mod s390x;
|
||||
pub(crate) mod x86;
|
||||
|
||||
@@ -13,6 +14,7 @@ pub enum Isa {
|
||||
X86,
|
||||
Arm64,
|
||||
S390x,
|
||||
Riscv64,
|
||||
}
|
||||
|
||||
impl Isa {
|
||||
@@ -30,13 +32,14 @@ impl Isa {
|
||||
"aarch64" => Some(Isa::Arm64),
|
||||
"s390x" => Some(Isa::S390x),
|
||||
x if ["x86_64", "i386", "i586", "i686"].contains(&x) => Some(Isa::X86),
|
||||
"riscv64" | "riscv64gc" | "riscv64imac" => Some(Isa::Riscv64),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all supported isa targets.
|
||||
pub fn all() -> &'static [Isa] {
|
||||
&[Isa::X86, Isa::Arm64, Isa::S390x]
|
||||
&[Isa::X86, Isa::Arm64, Isa::S390x, Isa::Riscv64]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +50,7 @@ impl fmt::Display for Isa {
|
||||
Isa::X86 => write!(f, "x86"),
|
||||
Isa::Arm64 => write!(f, "arm64"),
|
||||
Isa::S390x => write!(f, "s390x"),
|
||||
Isa::Riscv64 => write!(f, "riscv64"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,6 +61,7 @@ pub(crate) fn define(isas: &[Isa], shared_defs: &mut SharedDefinitions) -> Vec<T
|
||||
Isa::X86 => x86::define(shared_defs),
|
||||
Isa::Arm64 => arm64::define(shared_defs),
|
||||
Isa::S390x => s390x::define(shared_defs),
|
||||
Isa::Riscv64 => riscv64::define(shared_defs),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
27
cranelift/codegen/meta/src/isa/riscv64.rs
Normal file
27
cranelift/codegen/meta/src/isa/riscv64.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use crate::cdsl::isa::TargetIsa;
|
||||
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
|
||||
|
||||
use crate::shared::Definitions as SharedDefinitions;
|
||||
|
||||
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
|
||||
let mut setting = SettingGroupBuilder::new("riscv64");
|
||||
|
||||
let _has_m = setting.add_bool("has_m", "has extension M?", "", false);
|
||||
let _has_a = setting.add_bool("has_a", "has extension A?", "", false);
|
||||
let _has_f = setting.add_bool("has_f", "has extension F?", "", false);
|
||||
let _has_d = setting.add_bool("has_d", "has extension D?", "", false);
|
||||
let _has_v = setting.add_bool("has_v", "has extension V?", "", false);
|
||||
let _has_b = setting.add_bool("has_b", "has extension B?", "", false);
|
||||
let _has_c = setting.add_bool("has_c", "has extension C?", "", false);
|
||||
let _has_zbkb = setting.add_bool("has_zbkb", "has extension zbkb?", "", false);
|
||||
|
||||
let _has_zicsr = setting.add_bool("has_zicsr", "has extension zicsr?", "", false);
|
||||
let _has_zifencei = setting.add_bool("has_zifencei", "has extension zifencei?", "", false);
|
||||
|
||||
setting.build()
|
||||
}
|
||||
|
||||
pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
|
||||
let settings = define_settings(&shared_defs.settings);
|
||||
TargetIsa::new("riscv64", settings)
|
||||
}
|
||||
@@ -66,6 +66,13 @@ pub enum Reloc {
|
||||
/// This is equivalent to `R_AARCH64_TLSGD_ADD_LO12_NC` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#relocations-for-thread-local-storage)
|
||||
Aarch64TlsGdAddLo12Nc,
|
||||
|
||||
/// procedure call.
|
||||
/// call symbol
|
||||
/// expands to the following assembly and relocation:
|
||||
/// auipc ra, 0
|
||||
/// jalr ra, ra, 0
|
||||
RiscvCall,
|
||||
|
||||
/// s390x TLS GD64 - 64-bit offset of tls_index for GD symbol in GOT
|
||||
S390xTlsGd64,
|
||||
/// s390x TLS GDCall - marker to enable optimization of TLS calls
|
||||
@@ -87,6 +94,7 @@ impl fmt::Display for Reloc {
|
||||
Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
|
||||
Self::X86SecRel => write!(f, "SecRel"),
|
||||
Self::Arm32Call | Self::Arm64Call => write!(f, "Call"),
|
||||
Self::RiscvCall => write!(f, "RiscvCall"),
|
||||
|
||||
Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
|
||||
Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
|
||||
|
||||
@@ -66,6 +66,9 @@ pub mod x64;
|
||||
#[cfg(feature = "arm64")]
|
||||
pub(crate) mod aarch64;
|
||||
|
||||
#[cfg(feature = "riscv64")]
|
||||
pub mod riscv64;
|
||||
|
||||
#[cfg(feature = "s390x")]
|
||||
mod s390x;
|
||||
|
||||
@@ -97,6 +100,7 @@ pub fn lookup(triple: Triple) -> Result<Builder, LookupError> {
|
||||
}
|
||||
Architecture::Aarch64 { .. } => isa_builder!(aarch64, (feature = "arm64"), triple),
|
||||
Architecture::S390x { .. } => isa_builder!(s390x, (feature = "s390x"), triple),
|
||||
Architecture::Riscv64 { .. } => isa_builder!(riscv64, (feature = "riscv64"), triple),
|
||||
_ => Err(LookupError::Unsupported),
|
||||
}
|
||||
}
|
||||
|
||||
716
cranelift/codegen/src/isa/riscv64/abi.rs
Normal file
716
cranelift/codegen/src/isa/riscv64/abi.rs
Normal file
@@ -0,0 +1,716 @@
|
||||
//! Implementation of a standard Riscv64 ABI.
|
||||
|
||||
use crate::ir;
|
||||
use crate::ir::types::*;
|
||||
|
||||
use crate::ir::ExternalName;
|
||||
use crate::ir::MemFlags;
|
||||
use crate::isa;
|
||||
|
||||
use crate::isa::riscv64::{inst::EmitState, inst::*};
|
||||
use crate::isa::CallConv;
|
||||
use crate::machinst::*;
|
||||
|
||||
use crate::ir::types::I8;
|
||||
use crate::ir::LibCall;
|
||||
use crate::ir::Signature;
|
||||
use crate::isa::riscv64::settings::Flags as RiscvFlags;
|
||||
use crate::isa::unwind::UnwindInst;
|
||||
use crate::settings;
|
||||
use crate::CodegenError;
|
||||
use crate::CodegenResult;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use regalloc2::PRegSet;
|
||||
use regs::x_reg;
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
/// Support for the Riscv64 ABI from the callee side (within a function body).
|
||||
pub(crate) type Riscv64Callee = Callee<Riscv64MachineDeps>;
|
||||
|
||||
/// Support for the Riscv64 ABI from the caller side (at a callsite).
|
||||
pub(crate) type Riscv64ABICaller = Caller<Riscv64MachineDeps>;
|
||||
|
||||
/// This is the limit for the size of argument and return-value areas on the
|
||||
/// stack. We place a reasonable limit here to avoid integer overflow issues
|
||||
/// with 32-bit arithmetic: for now, 128 MB.
|
||||
static STACK_ARG_RET_SIZE_LIMIT: u64 = 128 * 1024 * 1024;
|
||||
|
||||
/// Riscv64-specific ABI behavior. This struct just serves as an implementation
|
||||
/// point for the trait; it is never actually instantiated.
|
||||
pub struct Riscv64MachineDeps;
|
||||
|
||||
impl IsaFlags for RiscvFlags {}
|
||||
|
||||
impl ABIMachineSpec for Riscv64MachineDeps {
|
||||
type I = Inst;
|
||||
type F = RiscvFlags;
|
||||
|
||||
fn word_bits() -> u32 {
|
||||
64
|
||||
}
|
||||
|
||||
/// Return required stack alignment in bytes.
|
||||
fn stack_align(_call_conv: isa::CallConv) -> u32 {
|
||||
16
|
||||
}
|
||||
|
||||
fn compute_arg_locs(
|
||||
call_conv: isa::CallConv,
|
||||
_flags: &settings::Flags,
|
||||
params: &[ir::AbiParam],
|
||||
args_or_rets: ArgsOrRets,
|
||||
add_ret_area_ptr: bool,
|
||||
) -> CodegenResult<(ABIArgVec, i64, Option<usize>)> {
|
||||
// All registers that can be used as parameters or rets.
|
||||
// both start and end are included.
|
||||
let (x_start, x_end, f_start, f_end) = if args_or_rets == ArgsOrRets::Args {
|
||||
(10, 17, 10, 17)
|
||||
} else {
|
||||
let end = if call_conv.extends_wasmtime() { 10 } else { 11 };
|
||||
(10, end, 10, end)
|
||||
};
|
||||
let mut next_x_reg = x_start;
|
||||
let mut next_f_reg = f_start;
|
||||
// Stack space.
|
||||
let mut next_stack: u64 = 0;
|
||||
let mut ret = smallvec![];
|
||||
let mut return_one_register_used = false;
|
||||
|
||||
for param in params {
|
||||
if let ir::ArgumentPurpose::StructArgument(size) = param.purpose {
|
||||
let offset = next_stack;
|
||||
assert!(size % 8 == 0, "StructArgument size is not properly aligned");
|
||||
next_stack += size as u64;
|
||||
ret.push(ABIArg::StructArg {
|
||||
pointer: None,
|
||||
offset: offset as i64,
|
||||
size: size as u64,
|
||||
purpose: param.purpose,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find regclass(es) of the register(s) used to store a value of this type.
|
||||
let (rcs, reg_tys) = Inst::rc_for_type(param.value_type)?;
|
||||
let mut slots = ABIArgSlotVec::new();
|
||||
for (rc, reg_ty) in rcs.iter().zip(reg_tys.iter()) {
|
||||
let next_reg =
|
||||
if (next_x_reg <= x_end) && *rc == RegClass::Int && !return_one_register_used {
|
||||
let x = Some(x_reg(next_x_reg));
|
||||
if args_or_rets == ArgsOrRets::Rets && call_conv.extends_wasmtime() {
|
||||
return_one_register_used = true;
|
||||
}
|
||||
next_x_reg += 1;
|
||||
x
|
||||
} else if (next_f_reg <= f_end)
|
||||
&& *rc == RegClass::Float
|
||||
&& !return_one_register_used
|
||||
{
|
||||
let x = Some(f_reg(next_f_reg));
|
||||
if args_or_rets == ArgsOrRets::Rets && call_conv.extends_wasmtime() {
|
||||
return_one_register_used = true;
|
||||
}
|
||||
next_f_reg += 1;
|
||||
x
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(reg) = next_reg {
|
||||
slots.push(ABIArgSlot::Reg {
|
||||
reg: reg.to_real_reg().unwrap(),
|
||||
ty: *reg_ty,
|
||||
extension: param.extension,
|
||||
});
|
||||
} else {
|
||||
// Compute size. For the wasmtime ABI it differs from native
|
||||
// ABIs in how multiple values are returned, so we take a
|
||||
// leaf out of arm64's book by not rounding everything up to
|
||||
// 8 bytes. For all ABI arguments, and other ABI returns,
|
||||
// though, each slot takes a minimum of 8 bytes.
|
||||
//
|
||||
// Note that in all cases 16-byte stack alignment happens
|
||||
// separately after all args.
|
||||
let size = (reg_ty.bits() / 8) as u64;
|
||||
let size = if args_or_rets == ArgsOrRets::Rets && call_conv.extends_wasmtime() {
|
||||
size
|
||||
} else {
|
||||
std::cmp::max(size, 8)
|
||||
};
|
||||
// Align.
|
||||
debug_assert!(size.is_power_of_two());
|
||||
next_stack = align_to(next_stack, size);
|
||||
slots.push(ABIArgSlot::Stack {
|
||||
offset: next_stack as i64,
|
||||
ty: *reg_ty,
|
||||
extension: param.extension,
|
||||
});
|
||||
next_stack += size;
|
||||
}
|
||||
}
|
||||
ret.push(ABIArg::Slots {
|
||||
slots,
|
||||
purpose: param.purpose,
|
||||
});
|
||||
}
|
||||
let pos: Option<usize> = if add_ret_area_ptr {
|
||||
assert!(ArgsOrRets::Args == args_or_rets);
|
||||
if next_x_reg <= x_end {
|
||||
let arg = ABIArg::reg(
|
||||
x_reg(next_x_reg).to_real_reg().unwrap(),
|
||||
I64,
|
||||
ir::ArgumentExtension::None,
|
||||
ir::ArgumentPurpose::Normal,
|
||||
);
|
||||
ret.push(arg);
|
||||
} else {
|
||||
let arg = ABIArg::stack(
|
||||
next_stack as i64,
|
||||
I64,
|
||||
ir::ArgumentExtension::None,
|
||||
ir::ArgumentPurpose::Normal,
|
||||
);
|
||||
ret.push(arg);
|
||||
next_stack += 8;
|
||||
}
|
||||
Some(ret.len() - 1)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
next_stack = align_to(next_stack, Self::stack_align(call_conv) as u64);
|
||||
// To avoid overflow issues, limit the arg/return size to something
|
||||
// reasonable -- here, 128 MB.
|
||||
if next_stack > STACK_ARG_RET_SIZE_LIMIT {
|
||||
return Err(CodegenError::ImplLimitExceeded);
|
||||
}
|
||||
CodegenResult::Ok((ret, next_stack as i64, pos))
|
||||
}
|
||||
|
||||
fn fp_to_arg_offset(_call_conv: isa::CallConv, _flags: &settings::Flags) -> i64 {
|
||||
// lr fp.
|
||||
16
|
||||
}
|
||||
|
||||
fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Inst {
|
||||
Inst::gen_load(into_reg, mem.into(), ty, MemFlags::trusted())
|
||||
}
|
||||
|
||||
fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Inst {
|
||||
Inst::gen_store(mem.into(), from_reg, ty, MemFlags::trusted())
|
||||
}
|
||||
|
||||
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {
|
||||
Inst::gen_move(to_reg, from_reg, ty)
|
||||
}
|
||||
|
||||
fn gen_extend(
|
||||
to_reg: Writable<Reg>,
|
||||
from_reg: Reg,
|
||||
signed: bool,
|
||||
from_bits: u8,
|
||||
to_bits: u8,
|
||||
) -> Inst {
|
||||
assert!(from_bits < to_bits);
|
||||
Inst::Extend {
|
||||
rd: to_reg,
|
||||
rn: from_reg,
|
||||
signed,
|
||||
from_bits,
|
||||
to_bits,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ext_mode(
|
||||
_call_conv: isa::CallConv,
|
||||
specified: ir::ArgumentExtension,
|
||||
) -> ir::ArgumentExtension {
|
||||
specified
|
||||
}
|
||||
|
||||
fn gen_args(_isa_flags: &crate::isa::riscv64::settings::Flags, args: Vec<ArgPair>) -> Inst {
|
||||
Inst::Args { args }
|
||||
}
|
||||
|
||||
fn gen_ret(_setup_frame: bool, _isa_flags: &Self::F, rets: Vec<Reg>) -> Inst {
|
||||
Inst::Ret { rets }
|
||||
}
|
||||
|
||||
fn get_stacklimit_reg() -> Reg {
|
||||
spilltmp_reg()
|
||||
}
|
||||
|
||||
fn gen_add_imm(into_reg: Writable<Reg>, from_reg: Reg, imm: u32) -> SmallInstVec<Inst> {
|
||||
let mut insts = SmallInstVec::new();
|
||||
if let Some(imm12) = Imm12::maybe_from_u64(imm as u64) {
|
||||
insts.push(Inst::AluRRImm12 {
|
||||
alu_op: AluOPRRI::Andi,
|
||||
rd: into_reg,
|
||||
rs: from_reg,
|
||||
imm12,
|
||||
});
|
||||
} else {
|
||||
insts.extend(Inst::load_constant_u32(
|
||||
writable_spilltmp_reg2(),
|
||||
imm as u64,
|
||||
));
|
||||
insts.push(Inst::AluRRR {
|
||||
alu_op: AluOPRRR::Add,
|
||||
rd: into_reg,
|
||||
rs1: spilltmp_reg2(),
|
||||
rs2: from_reg,
|
||||
});
|
||||
}
|
||||
insts
|
||||
}
|
||||
|
||||
fn gen_stack_lower_bound_trap(limit_reg: Reg) -> SmallInstVec<Inst> {
|
||||
let mut insts = SmallVec::new();
|
||||
insts.push(Inst::TrapIfC {
|
||||
cc: IntCC::UnsignedLessThan,
|
||||
rs1: stack_reg(),
|
||||
rs2: limit_reg,
|
||||
trap_code: ir::TrapCode::StackOverflow,
|
||||
});
|
||||
insts
|
||||
}
|
||||
|
||||
fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>, _ty: Type) -> Inst {
|
||||
Inst::LoadAddr {
|
||||
rd: into_reg,
|
||||
mem: mem.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
|
||||
let mem = AMode::RegOffset(base, offset as i64, ty);
|
||||
Inst::gen_load(into_reg, mem, ty, MemFlags::trusted())
|
||||
}
|
||||
|
||||
fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
|
||||
let mem = AMode::RegOffset(base, offset as i64, ty);
|
||||
Inst::gen_store(mem, from_reg, ty, MemFlags::trusted())
|
||||
}
|
||||
|
||||
fn gen_sp_reg_adjust(amount: i32) -> SmallInstVec<Inst> {
|
||||
let mut insts = SmallVec::new();
|
||||
if amount == 0 {
|
||||
return insts;
|
||||
}
|
||||
insts.push(Inst::AjustSp {
|
||||
amount: amount as i64,
|
||||
});
|
||||
insts
|
||||
}
|
||||
|
||||
fn gen_nominal_sp_adj(offset: i32) -> Inst {
|
||||
Inst::VirtualSPOffsetAdj {
|
||||
amount: offset as i64,
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_prologue_frame_setup(flags: &settings::Flags) -> SmallInstVec<Inst> {
|
||||
// add sp,sp,-16 ;; alloc stack space for fp.
|
||||
// sd ra,8(sp) ;; save ra.
|
||||
// sd fp,0(sp) ;; store old fp.
|
||||
// mv fp,sp ;; set fp to sp.
|
||||
let mut insts = SmallVec::new();
|
||||
insts.push(Inst::AjustSp { amount: -16 });
|
||||
insts.push(Self::gen_store_stack(
|
||||
StackAMode::SPOffset(8, I64),
|
||||
link_reg(),
|
||||
I64,
|
||||
));
|
||||
insts.push(Self::gen_store_stack(
|
||||
StackAMode::SPOffset(0, I64),
|
||||
fp_reg(),
|
||||
I64,
|
||||
));
|
||||
if flags.unwind_info() {
|
||||
insts.push(Inst::Unwind {
|
||||
inst: UnwindInst::PushFrameRegs {
|
||||
offset_upward_to_caller_sp: 16, // FP, LR
|
||||
},
|
||||
});
|
||||
}
|
||||
insts.push(Inst::Mov {
|
||||
rd: writable_fp_reg(),
|
||||
rm: stack_reg(),
|
||||
ty: I64,
|
||||
});
|
||||
insts
|
||||
}
|
||||
/// reverse of gen_prologue_frame_setup.
|
||||
fn gen_epilogue_frame_restore(_: &settings::Flags) -> SmallInstVec<Inst> {
|
||||
let mut insts = SmallVec::new();
|
||||
insts.push(Self::gen_load_stack(
|
||||
StackAMode::SPOffset(8, I64),
|
||||
writable_link_reg(),
|
||||
I64,
|
||||
));
|
||||
insts.push(Self::gen_load_stack(
|
||||
StackAMode::SPOffset(0, I64),
|
||||
writable_fp_reg(),
|
||||
I64,
|
||||
));
|
||||
insts.push(Inst::AjustSp { amount: 16 });
|
||||
insts
|
||||
}
|
||||
|
||||
fn gen_probestack(frame_size: u32) -> SmallInstVec<Self::I> {
|
||||
let mut insts = SmallVec::new();
|
||||
insts.extend(Inst::load_constant_u32(writable_a0(), frame_size as u64));
|
||||
insts.push(Inst::Call {
|
||||
info: Box::new(CallInfo {
|
||||
dest: ExternalName::LibCall(LibCall::Probestack),
|
||||
uses: smallvec![CallArgPair {
|
||||
vreg: a0(),
|
||||
preg: a0(),
|
||||
}],
|
||||
defs: smallvec![],
|
||||
clobbers: PRegSet::empty(),
|
||||
opcode: Opcode::Call,
|
||||
callee_callconv: CallConv::SystemV,
|
||||
caller_callconv: CallConv::SystemV,
|
||||
}),
|
||||
});
|
||||
insts
|
||||
}
|
||||
// Returns stack bytes used as well as instructions. Does not adjust
|
||||
// nominal SP offset; abi_impl generic code will do that.
|
||||
fn gen_clobber_save(
|
||||
_call_conv: isa::CallConv,
|
||||
setup_frame: bool,
|
||||
flags: &settings::Flags,
|
||||
clobbered_callee_saves: &[Writable<RealReg>],
|
||||
fixed_frame_storage_size: u32,
|
||||
_outgoing_args_size: u32,
|
||||
) -> (u64, SmallVec<[Inst; 16]>) {
|
||||
let mut insts = SmallVec::new();
|
||||
let clobbered_size = compute_clobber_size(&clobbered_callee_saves);
|
||||
// Adjust the stack pointer downward for clobbers and the function fixed
|
||||
// frame (spillslots and storage slots).
|
||||
let stack_size = fixed_frame_storage_size + clobbered_size;
|
||||
if flags.unwind_info() && setup_frame {
|
||||
// The *unwind* frame (but not the actual frame) starts at the
|
||||
// clobbers, just below the saved FP/LR pair.
|
||||
insts.push(Inst::Unwind {
|
||||
inst: UnwindInst::DefineNewFrame {
|
||||
offset_downward_to_clobbers: clobbered_size,
|
||||
offset_upward_to_caller_sp: 16, // FP, LR
|
||||
},
|
||||
});
|
||||
}
|
||||
// Store each clobbered register in order at offsets from SP,
|
||||
// placing them above the fixed frame slots.
|
||||
if stack_size > 0 {
|
||||
// since we use fp, we didn't need use UnwindInst::StackAlloc.
|
||||
let mut cur_offset = 8;
|
||||
for reg in clobbered_callee_saves {
|
||||
let r_reg = reg.to_reg();
|
||||
let ty = match r_reg.class() {
|
||||
regalloc2::RegClass::Int => I64,
|
||||
regalloc2::RegClass::Float => F64,
|
||||
};
|
||||
if flags.unwind_info() {
|
||||
insts.push(Inst::Unwind {
|
||||
inst: UnwindInst::SaveReg {
|
||||
clobber_offset: clobbered_size - cur_offset,
|
||||
reg: r_reg,
|
||||
},
|
||||
});
|
||||
}
|
||||
insts.push(Self::gen_store_stack(
|
||||
StackAMode::SPOffset(-(cur_offset as i64), ty),
|
||||
real_reg_to_reg(reg.to_reg()),
|
||||
ty,
|
||||
));
|
||||
cur_offset += 8
|
||||
}
|
||||
insts.push(Inst::AjustSp {
|
||||
amount: -(stack_size as i64),
|
||||
});
|
||||
}
|
||||
(clobbered_size as u64, insts)
|
||||
}
|
||||
|
||||
fn gen_clobber_restore(
|
||||
call_conv: isa::CallConv,
|
||||
sig: &Signature,
|
||||
_flags: &settings::Flags,
|
||||
clobbers: &[Writable<RealReg>],
|
||||
fixed_frame_storage_size: u32,
|
||||
_outgoing_args_size: u32,
|
||||
) -> SmallVec<[Inst; 16]> {
|
||||
let mut insts = SmallVec::new();
|
||||
let clobbered_callee_saves =
|
||||
Self::get_clobbered_callee_saves(call_conv, _flags, sig, clobbers);
|
||||
let stack_size = fixed_frame_storage_size + compute_clobber_size(&clobbered_callee_saves);
|
||||
if stack_size > 0 {
|
||||
insts.push(Inst::AjustSp {
|
||||
amount: stack_size as i64,
|
||||
});
|
||||
}
|
||||
let mut cur_offset = 8;
|
||||
for reg in &clobbered_callee_saves {
|
||||
let rreg = reg.to_reg();
|
||||
let ty = match rreg.class() {
|
||||
regalloc2::RegClass::Int => I64,
|
||||
regalloc2::RegClass::Float => F64,
|
||||
};
|
||||
insts.push(Self::gen_load_stack(
|
||||
StackAMode::SPOffset(-cur_offset, ty),
|
||||
Writable::from_reg(real_reg_to_reg(reg.to_reg())),
|
||||
ty,
|
||||
));
|
||||
cur_offset += 8
|
||||
}
|
||||
insts
|
||||
}
|
||||
|
||||
fn gen_call(
|
||||
dest: &CallDest,
|
||||
uses: CallArgList,
|
||||
defs: CallRetList,
|
||||
clobbers: PRegSet,
|
||||
opcode: ir::Opcode,
|
||||
tmp: Writable<Reg>,
|
||||
callee_conv: isa::CallConv,
|
||||
caller_conv: isa::CallConv,
|
||||
) -> SmallVec<[Self::I; 2]> {
|
||||
let mut insts = SmallVec::new();
|
||||
match &dest {
|
||||
&CallDest::ExtName(ref name, RelocDistance::Near) => insts.push(Inst::Call {
|
||||
info: Box::new(CallInfo {
|
||||
dest: name.clone(),
|
||||
uses,
|
||||
defs,
|
||||
clobbers,
|
||||
opcode,
|
||||
caller_callconv: caller_conv,
|
||||
callee_callconv: callee_conv,
|
||||
}),
|
||||
}),
|
||||
&CallDest::ExtName(ref name, RelocDistance::Far) => {
|
||||
insts.push(Inst::LoadExtName {
|
||||
rd: tmp,
|
||||
name: Box::new(name.clone()),
|
||||
offset: 0,
|
||||
});
|
||||
insts.push(Inst::CallInd {
|
||||
info: Box::new(CallIndInfo {
|
||||
rn: tmp.to_reg(),
|
||||
uses,
|
||||
defs,
|
||||
clobbers,
|
||||
opcode,
|
||||
caller_callconv: caller_conv,
|
||||
callee_callconv: callee_conv,
|
||||
}),
|
||||
});
|
||||
}
|
||||
&CallDest::Reg(reg) => insts.push(Inst::CallInd {
|
||||
info: Box::new(CallIndInfo {
|
||||
rn: *reg,
|
||||
uses,
|
||||
defs,
|
||||
clobbers,
|
||||
opcode,
|
||||
caller_callconv: caller_conv,
|
||||
callee_callconv: callee_conv,
|
||||
}),
|
||||
}),
|
||||
}
|
||||
insts
|
||||
}
|
||||
|
||||
fn gen_memcpy(
|
||||
call_conv: isa::CallConv,
|
||||
dst: Reg,
|
||||
src: Reg,
|
||||
tmp: Writable<Reg>,
|
||||
_tmp2: Writable<Reg>,
|
||||
size: usize,
|
||||
) -> SmallVec<[Self::I; 8]> {
|
||||
let mut insts = SmallVec::new();
|
||||
let arg0 = Writable::from_reg(x_reg(10));
|
||||
let arg1 = Writable::from_reg(x_reg(11));
|
||||
let arg2 = Writable::from_reg(x_reg(12));
|
||||
insts.extend(Inst::load_constant_u64(tmp, size as u64).into_iter());
|
||||
insts.push(Inst::Call {
|
||||
info: Box::new(CallInfo {
|
||||
dest: ExternalName::LibCall(LibCall::Memcpy),
|
||||
uses: smallvec![
|
||||
CallArgPair {
|
||||
vreg: dst,
|
||||
preg: arg0.to_reg()
|
||||
},
|
||||
CallArgPair {
|
||||
vreg: src,
|
||||
preg: arg1.to_reg()
|
||||
},
|
||||
CallArgPair {
|
||||
vreg: tmp.to_reg(),
|
||||
preg: arg2.to_reg()
|
||||
}
|
||||
],
|
||||
defs: smallvec![],
|
||||
clobbers: Self::get_regs_clobbered_by_call(call_conv),
|
||||
opcode: Opcode::Call,
|
||||
caller_callconv: call_conv,
|
||||
callee_callconv: call_conv,
|
||||
}),
|
||||
});
|
||||
insts
|
||||
}
|
||||
|
||||
fn get_number_of_spillslots_for_value(rc: RegClass, _target_vector_bytes: u32) -> u32 {
|
||||
// We allocate in terms of 8-byte slots.
|
||||
match rc {
|
||||
RegClass::Int => 1,
|
||||
RegClass::Float => 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current virtual-SP offset from an instruction-emission state.
|
||||
fn get_virtual_sp_offset_from_state(s: &EmitState) -> i64 {
|
||||
s.virtual_sp_offset
|
||||
}
|
||||
|
||||
/// Get the nominal-SP-to-FP offset from an instruction-emission state.
|
||||
fn get_nominal_sp_to_fp(s: &EmitState) -> i64 {
|
||||
s.nominal_sp_to_fp
|
||||
}
|
||||
|
||||
fn get_regs_clobbered_by_call(_call_conv_of_callee: isa::CallConv) -> PRegSet {
|
||||
let mut v = PRegSet::empty();
|
||||
for (k, need_save) in CALLER_SAVE_X_REG.iter().enumerate() {
|
||||
if !*need_save {
|
||||
continue;
|
||||
}
|
||||
v.add(px_reg(k));
|
||||
}
|
||||
for (k, need_save) in CALLER_SAVE_F_REG.iter().enumerate() {
|
||||
if !*need_save {
|
||||
continue;
|
||||
}
|
||||
v.add(pf_reg(k));
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
fn get_clobbered_callee_saves(
|
||||
call_conv: isa::CallConv,
|
||||
_flags: &settings::Flags,
|
||||
_sig: &Signature,
|
||||
regs: &[Writable<RealReg>],
|
||||
) -> Vec<Writable<RealReg>> {
|
||||
let mut regs: Vec<Writable<RealReg>> = regs
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|r| is_reg_saved_in_prologue(call_conv, r.to_reg()))
|
||||
.collect();
|
||||
|
||||
regs.sort();
|
||||
regs
|
||||
}
|
||||
|
||||
fn is_frame_setup_needed(
|
||||
is_leaf: bool,
|
||||
stack_args_size: u32,
|
||||
num_clobbered_callee_saves: usize,
|
||||
fixed_frame_storage_size: u32,
|
||||
) -> bool {
|
||||
!is_leaf
|
||||
// The function arguments that are passed on the stack are addressed
|
||||
// relative to the Frame Pointer.
|
||||
|| stack_args_size > 0
|
||||
|| num_clobbered_callee_saves > 0
|
||||
|| fixed_frame_storage_size > 0
|
||||
}
|
||||
|
||||
fn gen_inline_probestack(frame_size: u32, guard_size: u32) -> SmallInstVec<Self::I> {
|
||||
// Unroll at most n consecutive probes, before falling back to using a loop
|
||||
const PROBE_MAX_UNROLL: u32 = 3;
|
||||
// Number of probes that we need to perform
|
||||
let probe_count = align_to(frame_size, guard_size) / guard_size;
|
||||
|
||||
if probe_count <= PROBE_MAX_UNROLL {
|
||||
Self::gen_probestack_unroll(guard_size, probe_count)
|
||||
} else {
|
||||
Self::gen_probestack_loop(guard_size, probe_count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CALLER_SAVE_X_REG: [bool; 32] = [
|
||||
false, true, false, false, false, true, true, true, // 0-7
|
||||
false, false, true, true, true, true, true, true, // 8-15
|
||||
true, true, false, false, false, false, false, false, // 16-23
|
||||
false, false, false, false, true, true, true, true, // 24-31
|
||||
];
|
||||
const CALLEE_SAVE_X_REG: [bool; 32] = [
|
||||
false, false, true, false, false, false, false, false, // 0-7
|
||||
true, true, false, false, false, false, false, false, // 8-15
|
||||
false, false, true, true, true, true, true, true, // 16-23
|
||||
true, true, true, true, false, false, false, false, // 24-31
|
||||
];
|
||||
const CALLER_SAVE_F_REG: [bool; 32] = [
|
||||
true, true, true, true, true, true, true, true, // 0-7
|
||||
false, true, true, true, true, true, true, true, // 8-15
|
||||
true, true, false, false, false, false, false, false, // 16-23
|
||||
false, false, false, false, true, true, true, true, // 24-31
|
||||
];
|
||||
const CALLEE_SAVE_F_REG: [bool; 32] = [
|
||||
false, false, false, false, false, false, false, false, // 0-7
|
||||
true, false, false, false, false, false, false, false, // 8-15
|
||||
false, false, true, true, true, true, true, true, // 16-23
|
||||
true, true, true, true, false, false, false, false, // 24-31
|
||||
];
|
||||
|
||||
/// This should be the registers that must be saved by callee.
|
||||
#[inline]
|
||||
fn is_reg_saved_in_prologue(_conv: CallConv, reg: RealReg) -> bool {
|
||||
if reg.class() == RegClass::Int {
|
||||
CALLEE_SAVE_X_REG[reg.hw_enc() as usize]
|
||||
} else {
|
||||
CALLEE_SAVE_F_REG[reg.hw_enc() as usize]
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
|
||||
let mut clobbered_size = 0;
|
||||
for reg in clobbers {
|
||||
match reg.to_reg().class() {
|
||||
RegClass::Int => {
|
||||
clobbered_size += 8;
|
||||
}
|
||||
RegClass::Float => {
|
||||
clobbered_size += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
align_to(clobbered_size, 16)
|
||||
}
|
||||
|
||||
impl Riscv64MachineDeps {
|
||||
fn gen_probestack_unroll(guard_size: u32, probe_count: u32) -> SmallInstVec<Inst> {
|
||||
let mut insts = SmallVec::with_capacity(probe_count as usize);
|
||||
for i in 0..probe_count {
|
||||
let offset = (guard_size * (i + 1)) as i64;
|
||||
insts.push(Self::gen_store_stack(
|
||||
StackAMode::SPOffset(-offset, I8),
|
||||
zero_reg(),
|
||||
I32,
|
||||
));
|
||||
}
|
||||
insts
|
||||
}
|
||||
fn gen_probestack_loop(guard_size: u32, probe_count: u32) -> SmallInstVec<Inst> {
|
||||
smallvec![Inst::StackProbeLoop {
|
||||
guard_size,
|
||||
probe_count,
|
||||
tmp: Writable::from_reg(x_reg(28)), // t3
|
||||
}]
|
||||
}
|
||||
}
|
||||
2084
cranelift/codegen/src/isa/riscv64/inst.isle
Normal file
2084
cranelift/codegen/src/isa/riscv64/inst.isle
Normal file
File diff suppressed because it is too large
Load Diff
1972
cranelift/codegen/src/isa/riscv64/inst/args.rs
Normal file
1972
cranelift/codegen/src/isa/riscv64/inst/args.rs
Normal file
File diff suppressed because it is too large
Load Diff
2920
cranelift/codegen/src/isa/riscv64/inst/emit.rs
Normal file
2920
cranelift/codegen/src/isa/riscv64/inst/emit.rs
Normal file
File diff suppressed because it is too large
Load Diff
2279
cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
Normal file
2279
cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
Normal file
File diff suppressed because it is too large
Load Diff
218
cranelift/codegen/src/isa/riscv64/inst/imms.rs
Normal file
218
cranelift/codegen/src/isa/riscv64/inst/imms.rs
Normal file
@@ -0,0 +1,218 @@
|
||||
//! Riscv64 ISA definitions: immediate constants.
|
||||
|
||||
// Some variants are never constructed, but we still want them as options in the future.
|
||||
use super::Inst;
|
||||
#[allow(dead_code)]
|
||||
use std::fmt::{Debug, Display, Formatter, Result};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Imm12 {
|
||||
pub bits: i16,
|
||||
}
|
||||
|
||||
impl Imm12 {
|
||||
pub(crate) const FALSE: Self = Self { bits: 0 };
|
||||
pub(crate) const TRUE: Self = Self { bits: -1 };
|
||||
pub fn maybe_from_u64(val: u64) -> Option<Imm12> {
|
||||
let sign_bit = 1 << 11;
|
||||
if val == 0 {
|
||||
Some(Imm12 { bits: 0 })
|
||||
} else if (val & sign_bit) != 0 && (val >> 12) == 0xffff_ffff_ffff_f {
|
||||
Some(Imm12 {
|
||||
bits: (val & 0xffff) as i16,
|
||||
})
|
||||
} else if (val & sign_bit) == 0 && (val >> 12) == 0 {
|
||||
Some(Imm12 {
|
||||
bits: (val & 0xffff) as i16,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn from_bits(bits: i16) -> Self {
|
||||
Self { bits: bits & 0xfff }
|
||||
}
|
||||
/// Create a zero immediate of this format.
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Imm12 { bits: 0 }
|
||||
}
|
||||
#[inline]
|
||||
pub fn as_i16(self) -> i16 {
|
||||
self.bits
|
||||
}
|
||||
#[inline]
|
||||
pub fn as_u32(&self) -> u32 {
|
||||
(self.bits as u32) & 0xfff
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<i64> for Imm12 {
|
||||
fn into(self) -> i64 {
|
||||
self.bits as i64
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Imm12 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{:+}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Neg for Imm12 {
|
||||
type Output = Self;
|
||||
fn neg(self) -> Self::Output {
|
||||
Self { bits: -self.bits }
|
||||
}
|
||||
}
|
||||
|
||||
// singed
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct Imm20 {
|
||||
/// The immediate bits.
|
||||
pub bits: i32,
|
||||
}
|
||||
|
||||
impl Imm20 {
|
||||
#[inline]
|
||||
pub fn from_bits(bits: i32) -> Self {
|
||||
Self {
|
||||
bits: bits & 0xf_ffff,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn as_u32(&self) -> u32 {
|
||||
(self.bits as u32) & 0xf_ffff
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Imm20 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Imm20 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Uimm5 {
|
||||
bits: u8,
|
||||
}
|
||||
|
||||
impl Uimm5 {
|
||||
pub fn from_bits(bits: u8) -> Self {
|
||||
Self { bits }
|
||||
}
|
||||
/// Create a zero immediate of this format.
|
||||
pub fn zero() -> Self {
|
||||
Self { bits: 0 }
|
||||
}
|
||||
pub fn as_u32(&self) -> u32 {
|
||||
(self.bits as u32) & 0b1_1111
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Uimm5 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Uimm5 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
pub(crate) fn imm_min() -> i64 {
|
||||
let imm20_max: i64 = (1 << 19) << 12;
|
||||
let imm12_max = 1 << 11;
|
||||
-imm20_max - imm12_max
|
||||
}
|
||||
pub(crate) fn imm_max() -> i64 {
|
||||
let imm20_max: i64 = ((1 << 19) - 1) << 12;
|
||||
let imm12_max = (1 << 11) - 1;
|
||||
imm20_max + imm12_max
|
||||
}
|
||||
|
||||
/// An imm20 immediate and an Imm12 immediate can generate a 32-bit immediate.
|
||||
/// This helper produces an imm12, imm20, or both to generate the value.
|
||||
///
|
||||
/// `value` must be between `imm_min()` and `imm_max()`, or else
|
||||
/// this helper returns `None`.
|
||||
pub(crate) fn generate_imm<R>(
|
||||
value: u64,
|
||||
mut handle_imm: impl FnMut(Option<Imm20>, Option<Imm12>) -> R,
|
||||
) -> Option<R> {
|
||||
if let Some(imm12) = Imm12::maybe_from_u64(value) {
|
||||
// can be load using single imm12.
|
||||
let r = handle_imm(None, Some(imm12));
|
||||
return Some(r);
|
||||
}
|
||||
let value = value as i64;
|
||||
if !(value >= Self::imm_min() && value <= Self::imm_max()) {
|
||||
// not in range, return None.
|
||||
return None;
|
||||
}
|
||||
const MOD_NUM: i64 = 4096;
|
||||
let (imm20, imm12) = if value > 0 {
|
||||
let mut imm20 = value / MOD_NUM;
|
||||
let mut imm12 = value % MOD_NUM;
|
||||
if imm12 >= 2048 {
|
||||
imm12 -= MOD_NUM;
|
||||
imm20 += 1;
|
||||
}
|
||||
assert!(imm12 >= -2048 && imm12 <= 2047);
|
||||
(imm20, imm12)
|
||||
} else {
|
||||
// this is the abs value.
|
||||
let value_abs = value.abs();
|
||||
let imm20 = value_abs / MOD_NUM;
|
||||
let imm12 = value_abs % MOD_NUM;
|
||||
let mut imm20 = -imm20;
|
||||
let mut imm12 = -imm12;
|
||||
if imm12 < -2048 {
|
||||
imm12 += MOD_NUM;
|
||||
imm20 -= 1;
|
||||
}
|
||||
(imm20, imm12)
|
||||
};
|
||||
assert!(imm20 >= -(0x7_ffff + 1) && imm20 <= 0x7_ffff);
|
||||
assert!(imm20 != 0 || imm12 != 0);
|
||||
Some(handle_imm(
|
||||
if imm20 != 0 {
|
||||
Some(Imm20::from_bits(imm20 as i32))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if imm12 != 0 {
|
||||
Some(Imm12::from_bits(imm12 as i16))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_imm12() {
|
||||
let x = Imm12::zero();
|
||||
assert_eq!(0, x.as_u32());
|
||||
Imm12::maybe_from_u64(0xffff_ffff_ffff_ffff).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imm20_and_imm12() {
|
||||
assert!(Inst::imm_max() == (i32::MAX - 2048) as i64);
|
||||
assert!(Inst::imm_min() == i32::MIN as i64 - 2048);
|
||||
}
|
||||
}
|
||||
1749
cranelift/codegen/src/isa/riscv64/inst/mod.rs
Normal file
1749
cranelift/codegen/src/isa/riscv64/inst/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
220
cranelift/codegen/src/isa/riscv64/inst/regs.rs
Normal file
220
cranelift/codegen/src/isa/riscv64/inst/regs.rs
Normal file
@@ -0,0 +1,220 @@
|
||||
//! Riscv64 ISA definitions: registers.
|
||||
//!
|
||||
|
||||
use crate::settings;
|
||||
|
||||
use crate::machinst::{Reg, Writable};
|
||||
|
||||
use crate::machinst::RealReg;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use regalloc2::VReg;
|
||||
use regalloc2::{MachineEnv, PReg, RegClass};
|
||||
|
||||
// first argument of function call
|
||||
#[inline]
|
||||
pub fn a0() -> Reg {
|
||||
x_reg(10)
|
||||
}
|
||||
|
||||
// second argument of function call
|
||||
#[inline]
|
||||
pub fn a1() -> Reg {
|
||||
x_reg(11)
|
||||
}
|
||||
|
||||
// third argument of function call
|
||||
#[inline]
|
||||
pub fn a2() -> Reg {
|
||||
x_reg(12)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn writable_a0() -> Writable<Reg> {
|
||||
Writable::from_reg(a0())
|
||||
}
|
||||
#[inline]
|
||||
pub fn writable_a1() -> Writable<Reg> {
|
||||
Writable::from_reg(a1())
|
||||
}
|
||||
#[inline]
|
||||
pub fn writable_a2() -> Writable<Reg> {
|
||||
Writable::from_reg(a2())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fa0() -> Reg {
|
||||
f_reg(10)
|
||||
}
|
||||
#[inline]
|
||||
pub fn writable_fa0() -> Writable<Reg> {
|
||||
Writable::from_reg(fa0())
|
||||
}
|
||||
#[inline]
|
||||
pub fn writable_fa1() -> Writable<Reg> {
|
||||
Writable::from_reg(fa1())
|
||||
}
|
||||
#[inline]
|
||||
pub fn fa1() -> Reg {
|
||||
f_reg(11)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fa7() -> Reg {
|
||||
f_reg(17)
|
||||
}
|
||||
|
||||
/// Get a reference to the zero-register.
|
||||
#[inline]
|
||||
pub fn zero_reg() -> Reg {
|
||||
x_reg(0)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the zero-register (this discards a result).
|
||||
#[inline]
|
||||
pub fn writable_zero_reg() -> Writable<Reg> {
|
||||
Writable::from_reg(zero_reg())
|
||||
}
|
||||
#[inline]
|
||||
pub fn stack_reg() -> Reg {
|
||||
x_reg(2)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the stack-pointer register.
|
||||
#[inline]
|
||||
pub fn writable_stack_reg() -> Writable<Reg> {
|
||||
Writable::from_reg(stack_reg())
|
||||
}
|
||||
|
||||
/// Get a reference to the link register (x1).
|
||||
pub fn link_reg() -> Reg {
|
||||
x_reg(1)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the link register.
|
||||
#[inline]
|
||||
pub fn writable_link_reg() -> Writable<Reg> {
|
||||
Writable::from_reg(link_reg())
|
||||
}
|
||||
|
||||
/// Get a reference to the frame pointer (x29).
|
||||
#[inline]
|
||||
pub fn fp_reg() -> Reg {
|
||||
x_reg(8)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the frame pointer.
|
||||
#[inline]
|
||||
pub fn writable_fp_reg() -> Writable<Reg> {
|
||||
Writable::from_reg(fp_reg())
|
||||
}
|
||||
|
||||
/// Get a reference to the first temporary, sometimes "spill temporary",
|
||||
/// register. This register is used in various ways as a temporary.
|
||||
#[inline]
|
||||
pub fn spilltmp_reg() -> Reg {
|
||||
x_reg(31)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the spilltmp reg.
|
||||
#[inline]
|
||||
pub fn writable_spilltmp_reg() -> Writable<Reg> {
|
||||
Writable::from_reg(spilltmp_reg())
|
||||
}
|
||||
|
||||
///spilltmp2
|
||||
#[inline]
|
||||
pub fn spilltmp_reg2() -> Reg {
|
||||
x_reg(30)
|
||||
}
|
||||
|
||||
/// Get a writable reference to the spilltmp2 reg.
|
||||
#[inline]
|
||||
pub fn writable_spilltmp_reg2() -> Writable<Reg> {
|
||||
Writable::from_reg(spilltmp_reg2())
|
||||
}
|
||||
|
||||
pub fn crate_reg_eviroment(_flags: &settings::Flags) -> MachineEnv {
|
||||
let preferred_regs_by_class: [Vec<PReg>; 2] = {
|
||||
let mut x_register: Vec<PReg> = vec![];
|
||||
x_register.push(PReg::new(5, RegClass::Int));
|
||||
for i in 6..=7 {
|
||||
x_register.push(PReg::new(i, RegClass::Int));
|
||||
}
|
||||
for i in 10..=17 {
|
||||
x_register.push(PReg::new(i, RegClass::Int));
|
||||
}
|
||||
for i in 28..=29 {
|
||||
x_register.push(PReg::new(i, RegClass::Int));
|
||||
}
|
||||
|
||||
let mut f_register: Vec<PReg> = vec![];
|
||||
for i in 0..=7 {
|
||||
f_register.push(PReg::new(i, RegClass::Float));
|
||||
}
|
||||
for i in 10..=17 {
|
||||
f_register.push(PReg::new(i, RegClass::Float));
|
||||
}
|
||||
for i in 28..=31 {
|
||||
f_register.push(PReg::new(i, RegClass::Float));
|
||||
}
|
||||
[x_register, f_register]
|
||||
};
|
||||
|
||||
let non_preferred_regs_by_class: [Vec<PReg>; 2] = {
|
||||
let mut x_register: Vec<PReg> = vec![];
|
||||
x_register.push(PReg::new(9, RegClass::Int));
|
||||
for i in 18..=27 {
|
||||
x_register.push(PReg::new(i, RegClass::Int));
|
||||
}
|
||||
let mut f_register: Vec<PReg> = vec![];
|
||||
for i in 8..=9 {
|
||||
f_register.push(PReg::new(i, RegClass::Float));
|
||||
}
|
||||
for i in 18..=27 {
|
||||
f_register.push(PReg::new(i, RegClass::Float));
|
||||
}
|
||||
[x_register, f_register]
|
||||
};
|
||||
|
||||
MachineEnv {
|
||||
preferred_regs_by_class,
|
||||
non_preferred_regs_by_class,
|
||||
fixed_stack_slots: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn x_reg(enc: usize) -> Reg {
|
||||
let p_reg = PReg::new(enc, RegClass::Int);
|
||||
let v_reg = VReg::new(p_reg.index(), p_reg.class());
|
||||
Reg::from(v_reg)
|
||||
}
|
||||
pub fn px_reg(enc: usize) -> PReg {
|
||||
PReg::new(enc, RegClass::Int)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn f_reg(enc: usize) -> Reg {
|
||||
let p_reg = PReg::new(enc, RegClass::Float);
|
||||
let v_reg = VReg::new(p_reg.index(), p_reg.class());
|
||||
Reg::from(v_reg)
|
||||
}
|
||||
pub const fn pf_reg(enc: usize) -> PReg {
|
||||
PReg::new(enc, RegClass::Float)
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn real_reg_to_reg(x: RealReg) -> Reg {
|
||||
let v_reg = VReg::new(x.hw_enc() as usize, x.class());
|
||||
Reg::from(v_reg)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn x_reg_range(start: usize, end: usize) -> Vec<Writable<Reg>> {
|
||||
let mut regs = vec![];
|
||||
for i in start..=end {
|
||||
regs.push(Writable::from_reg(x_reg(i)));
|
||||
}
|
||||
regs
|
||||
}
|
||||
2
cranelift/codegen/src/isa/riscv64/inst/unwind.rs
Normal file
2
cranelift/codegen/src/isa/riscv64/inst/unwind.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "unwind")]
|
||||
pub(crate) mod systemv;
|
||||
173
cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs
Normal file
173
cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
//! Unwind information for System V ABI (Riscv64).
|
||||
|
||||
use crate::isa::riscv64::inst::regs;
|
||||
use crate::isa::unwind::systemv::RegisterMappingError;
|
||||
use crate::machinst::Reg;
|
||||
use gimli::{write::CommonInformationEntry, Encoding, Format, Register};
|
||||
use regalloc2::RegClass;
|
||||
|
||||
/// Creates a new riscv64 common information entry (CIE).
|
||||
pub fn create_cie() -> CommonInformationEntry {
|
||||
use gimli::write::CallFrameInstruction;
|
||||
|
||||
let mut entry = CommonInformationEntry::new(
|
||||
Encoding {
|
||||
address_size: 8,
|
||||
format: Format::Dwarf32,
|
||||
version: 1,
|
||||
},
|
||||
4, // Code alignment factor
|
||||
-8, // Data alignment factor
|
||||
Register(regs::link_reg().to_real_reg().unwrap().hw_enc() as u16),
|
||||
);
|
||||
|
||||
// Every frame will start with the call frame address (CFA) at SP
|
||||
let sp = Register(regs::stack_reg().to_real_reg().unwrap().hw_enc().into());
|
||||
entry.add_instruction(CallFrameInstruction::Cfa(sp, 0));
|
||||
|
||||
entry
|
||||
}
|
||||
|
||||
/// Map Cranelift registers to their corresponding Gimli registers.
|
||||
pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
|
||||
match reg.class() {
|
||||
RegClass::Int => {
|
||||
let reg = reg.to_real_reg().unwrap().hw_enc() as u16;
|
||||
Ok(Register(reg))
|
||||
}
|
||||
RegClass::Float => {
|
||||
let reg = reg.to_real_reg().unwrap().hw_enc() as u16;
|
||||
Ok(Register(32 + reg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RegisterMapper;
|
||||
|
||||
impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
|
||||
fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
|
||||
Ok(map_reg(reg)?.0)
|
||||
}
|
||||
fn sp(&self) -> u16 {
|
||||
regs::stack_reg().to_real_reg().unwrap().hw_enc() as u16
|
||||
}
|
||||
fn fp(&self) -> Option<u16> {
|
||||
Some(regs::fp_reg().to_real_reg().unwrap().hw_enc() as u16)
|
||||
}
|
||||
fn lr(&self) -> Option<u16> {
|
||||
Some(regs::link_reg().to_real_reg().unwrap().hw_enc() as u16)
|
||||
}
|
||||
fn lr_offset(&self) -> Option<u32> {
|
||||
Some(8)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::cursor::{Cursor, FuncCursor};
|
||||
|
||||
use crate::ir::{
|
||||
types, AbiParam, Function, InstBuilder, Signature, StackSlotData, StackSlotKind,
|
||||
UserFuncName,
|
||||
};
|
||||
use crate::isa::{lookup, CallConv};
|
||||
use crate::settings::{builder, Flags};
|
||||
use crate::Context;
|
||||
use gimli::write::Address;
|
||||
use std::str::FromStr;
|
||||
use target_lexicon::triple;
|
||||
|
||||
#[test]
|
||||
fn test_simple_func() {
|
||||
let isa = lookup(triple!("riscv64"))
|
||||
.expect("expect riscv64 ISA")
|
||||
.finish(Flags::new(builder()))
|
||||
.expect("Creating compiler backend");
|
||||
|
||||
let mut context = Context::for_function(create_function(
|
||||
CallConv::SystemV,
|
||||
Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
|
||||
));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let fde = match context
|
||||
.create_unwind_info(isa.as_ref())
|
||||
.expect("can create unwind info")
|
||||
{
|
||||
Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
|
||||
info.to_fde(Address::Constant(1234))
|
||||
}
|
||||
_ => panic!("expected unwind information"),
|
||||
};
|
||||
|
||||
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 40, lsda: None, instructions: [(12, CfaOffset(16)), (12, Offset(Register(8), -16)), (12, Offset(Register(1), -8)), (16, CfaRegister(Register(8)))] }");
|
||||
}
|
||||
|
||||
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
|
||||
let mut func =
|
||||
Function::with_name_signature(UserFuncName::user(0, 0), Signature::new(call_conv));
|
||||
|
||||
let block0 = func.dfg.make_block();
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
if let Some(stack_slot) = stack_slot {
|
||||
func.sized_stack_slots.push(stack_slot);
|
||||
}
|
||||
|
||||
func
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_return_func() {
|
||||
let isa = lookup(triple!("riscv64"))
|
||||
.expect("expect riscv64 ISA")
|
||||
.finish(Flags::new(builder()))
|
||||
.expect("Creating compiler backend");
|
||||
|
||||
let mut context = Context::for_function(create_multi_return_function(CallConv::SystemV));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let fde = match context
|
||||
.create_unwind_info(isa.as_ref())
|
||||
.expect("can create unwind info")
|
||||
{
|
||||
Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
|
||||
info.to_fde(Address::Constant(4321))
|
||||
}
|
||||
_ => panic!("expected unwind information"),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{:?}", fde),
|
||||
"FrameDescriptionEntry { address: Constant(4321), length: 12, lsda: None, instructions: [] }"
|
||||
);
|
||||
}
|
||||
|
||||
fn create_multi_return_function(call_conv: CallConv) -> Function {
|
||||
let mut sig = Signature::new(call_conv);
|
||||
sig.params.push(AbiParam::new(types::I32));
|
||||
let mut func = Function::with_name_signature(UserFuncName::user(0, 0), sig);
|
||||
|
||||
let block0 = func.dfg.make_block();
|
||||
let v0 = func.dfg.append_block_param(block0, types::I32);
|
||||
let block1 = func.dfg.make_block();
|
||||
let block2 = func.dfg.make_block();
|
||||
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().brnz(v0, block2, &[]);
|
||||
pos.ins().jump(block1, &[]);
|
||||
|
||||
pos.insert_block(block1);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
pos.insert_block(block2);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
func
|
||||
}
|
||||
}
|
||||
983
cranelift/codegen/src/isa/riscv64/lower.isle
Normal file
983
cranelift/codegen/src/isa/riscv64/lower.isle
Normal file
@@ -0,0 +1,983 @@
|
||||
;; riscv64 instruction selection and CLIF-to-MachInst lowering.
|
||||
|
||||
;; The main lowering constructor term: takes a clif `Inst` and returns the
|
||||
;; register(s) within which the lowered instruction's result values live.
|
||||
(decl lower (Inst) InstOutput)
|
||||
|
||||
;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (has_type ty (iconst (u64_from_imm64 n))))
|
||||
(imm ty n))
|
||||
|
||||
;;;; Rules for `bconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (has_type ty (bconst $false)))
|
||||
(imm ty 0))
|
||||
|
||||
(rule (lower (has_type ty (bconst $true)))
|
||||
(imm ty 1))
|
||||
|
||||
|
||||
;;;; Rules for `null` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (has_type ty (null)))
|
||||
(imm ty 0))
|
||||
|
||||
|
||||
;;;; Rules for `iadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_32 ty) (iadd x y)))
|
||||
(alu_rrr (AluOPRRR.Addw) x y))
|
||||
|
||||
;; Base case, simply adding things in registers.
|
||||
(rule (lower (has_type (fits_in_64 ty) (iadd x y)))
|
||||
(alu_add x y))
|
||||
|
||||
;; Special cases for when one operand is an immediate that fits in 12 bits.
|
||||
(rule (lower (has_type (fits_in_64 ty) (iadd x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (select_addi ty) x y))
|
||||
|
||||
(rule (lower (has_type (fits_in_64 ty) (iadd (imm12_from_value x) y)))
|
||||
(alu_rr_imm12 (select_addi ty) y x))
|
||||
|
||||
(rule
|
||||
(lower (has_type $I128 (iadd x y)))
|
||||
(let
|
||||
( ;; low part.
|
||||
(low Reg (alu_add (value_regs_get x 0) (value_regs_get y 0)))
|
||||
;; compute carry.
|
||||
(carry Reg(alu_rrr (AluOPRRR.SltU) low (value_regs_get y 0)))
|
||||
;;
|
||||
(high_tmp Reg (alu_add (value_regs_get x 1) (value_regs_get y 1)))
|
||||
;; add carry.
|
||||
(high Reg (alu_add high_tmp carry)))
|
||||
(value_regs low high)))
|
||||
|
||||
;;; Rules for `iadd_ifcout` ;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type (fits_in_64 ty) (iadd_ifcout x y)))
|
||||
(output_ifcout (alu_add x y)))
|
||||
|
||||
|
||||
;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Base case, simply subtracting things in registers.
|
||||
|
||||
(rule (lower (has_type (fits_in_64 ty) (isub x y)))
|
||||
(alu_rrr (AluOPRRR.Sub) x y))
|
||||
|
||||
(rule (lower (has_type (fits_in_32 ty) (isub x y)))
|
||||
(alu_rrr (AluOPRRR.Subw) x y))
|
||||
|
||||
(rule (lower (has_type $I128 (isub x y)))
|
||||
(i128_sub x y))
|
||||
|
||||
;;;; Rules for `ineg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; `i64` and smaller.
|
||||
(rule (lower (has_type (fits_in_64 ty) (ineg x)))
|
||||
(alu_rrr (AluOPRRR.Sub) (zero_reg) x))
|
||||
|
||||
|
||||
;;;; Rules for `imul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (has_type (fits_in_64 ty) (imul x y)))
|
||||
(alu_rrr (AluOPRRR.Mul) x y))
|
||||
(rule (lower (has_type (fits_in_32 ty) (imul x y)))
|
||||
(alu_rrr (AluOPRRR.Mulw) x y))
|
||||
|
||||
;;;; Rules for `smulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty) (smulhi x y)))
|
||||
(lower_smlhi ty (ext_int_if_need $true x ty) (ext_int_if_need $true y ty)))
|
||||
|
||||
;;;; Rules for `umulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty) (umulhi x y)))
|
||||
(lower_umlhi ty (ext_int_if_need $false x ty) (ext_int_if_need $false y ty)))
|
||||
|
||||
;; for I128
|
||||
(rule (lower (has_type $I128 (imul x y)))
|
||||
(let
|
||||
((x_regs ValueRegs x)
|
||||
(x_lo Reg (value_regs_get x_regs 0))
|
||||
(x_hi Reg (value_regs_get x_regs 1))
|
||||
|
||||
;; Get the high/low registers for `y`.
|
||||
(y_regs ValueRegs y)
|
||||
(y_lo Reg (value_regs_get y_regs 0))
|
||||
(y_hi Reg (value_regs_get y_regs 1))
|
||||
|
||||
;; 128bit mul formula:
|
||||
;; dst_lo = x_lo * y_lo
|
||||
;; dst_hi = umulhi(x_lo, y_lo) + (x_lo * y_hi) + (x_hi * y_lo)
|
||||
;;
|
||||
;; We can convert the above formula into the following
|
||||
;; umulh dst_hi, x_lo, y_lo
|
||||
;; madd dst_hi, x_lo, y_hi, dst_hi
|
||||
;; madd dst_hi, x_hi, y_lo, dst_hi
|
||||
;; madd dst_lo, x_lo, y_lo, zero
|
||||
(dst_hi1 Reg (umulh x_lo y_lo))
|
||||
(dst_hi2 Reg (madd x_lo y_hi dst_hi1))
|
||||
(dst_hi Reg (madd x_hi y_lo dst_hi2))
|
||||
(dst_lo Reg (madd x_lo y_lo (zero_reg))))
|
||||
(value_regs dst_lo dst_hi)))
|
||||
|
||||
|
||||
;;;; Rules for `div` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (has_type (fits_in_32 ty) (udiv x y)))
|
||||
(let
|
||||
((y2 Reg (ext_int_if_need $false y ty))
|
||||
(_ InstOutput (gen_div_by_zero y2)))
|
||||
(alu_rrr (AluOPRRR.Divuw) (ext_int_if_need $false x ty) y2)))
|
||||
|
||||
(rule (lower (has_type (fits_in_32 ty) (sdiv x y)))
|
||||
(let
|
||||
((a Reg (ext_int_if_need $true x ty))
|
||||
(b Reg (ext_int_if_need $true y ty))
|
||||
(_ InstOutput (gen_div_overflow a b ty))
|
||||
(_ InstOutput (gen_div_by_zero b)))
|
||||
(alu_rrr (AluOPRRR.Divw) a b)))
|
||||
|
||||
(rule (lower (has_type $I64 (sdiv x y)))
|
||||
(let
|
||||
((_ InstOutput (gen_div_overflow x y $I64))
|
||||
(_ InstOutput (gen_div_by_zero y)) )
|
||||
(alu_rrr (AluOPRRR.Div) x y)))
|
||||
|
||||
(rule (lower (has_type $I64 (udiv x y)))
|
||||
(let
|
||||
((_ InstOutput (gen_div_by_zero y)))
|
||||
(alu_rrr (AluOPRRR.DivU) x y)))
|
||||
|
||||
;;;; Rules for `rem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (has_type (fits_in_16 ty) (urem x y)))
|
||||
(let
|
||||
((y2 Reg(ext_int_if_need $false y ty))
|
||||
(_ InstOutput (gen_div_by_zero y2)))
|
||||
(alu_rrr (AluOPRRR.Remuw) (ext_int_if_need $false x ty) y2)))
|
||||
|
||||
(rule (lower (has_type (fits_in_16 ty) (srem x y)))
|
||||
(let
|
||||
((y2 Reg (ext_int_if_need $true y ty))
|
||||
(_ InstOutput (gen_div_by_zero y2)))
|
||||
(alu_rrr (AluOPRRR.Remw) (ext_int_if_need $true x ty) y2)))
|
||||
|
||||
(rule (lower (has_type $I32 (srem x y)))
|
||||
(let
|
||||
((y2 Reg (ext_int_if_need $true y $I32))
|
||||
(_ InstOutput (gen_div_by_zero y2)))
|
||||
(alu_rrr (AluOPRRR.Remw) x y2)))
|
||||
|
||||
(rule (lower (has_type $I32 (urem x y)))
|
||||
(let
|
||||
((y2 Reg (ext_int_if_need $false y $I32))
|
||||
(_ InstOutput (gen_div_by_zero y2)))
|
||||
(alu_rrr (AluOPRRR.Remuw) x y2)))
|
||||
|
||||
(rule (lower (has_type $I64 (srem x y)))
|
||||
(let
|
||||
((_ InstOutput (gen_div_by_zero y)))
|
||||
(alu_rrr (AluOPRRR.Rem) x y)))
|
||||
|
||||
(rule (lower (has_type $I64 (urem x y)))
|
||||
(let
|
||||
((_ InstOutput (gen_div_by_zero y)))
|
||||
(alu_rrr (AluOPRRR.RemU) x y)))
|
||||
|
||||
;;;; Rules for `and` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty) (band x y)))
|
||||
(alu_rrr (AluOPRRR.And) x y))
|
||||
|
||||
;; Special cases for when one operand is an immediate that fits in 12 bits.
|
||||
(rule (lower (has_type (fits_in_64 ty) (band x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Andi) x y))
|
||||
|
||||
(rule (lower (has_type (fits_in_64 ty) (band (imm12_from_value x) y)))
|
||||
(alu_rr_imm12 (AluOPRRI.Andi) y x))
|
||||
|
||||
(rule (lower (has_type $B128 (band x y)))
|
||||
(lower_b128_binary (AluOPRRR.And) x y))
|
||||
(rule (lower (has_type $I128 (band x y)))
|
||||
(lower_b128_binary (AluOPRRR.And) x y))
|
||||
|
||||
(rule (lower (has_type $F32 (band x y)))
|
||||
(lower_float_binary (AluOPRRR.And) x y $F32))
|
||||
(rule (lower (has_type $F64 (band x y)))
|
||||
(lower_float_binary (AluOPRRR.And) x y $F64))
|
||||
|
||||
|
||||
;;;; Rules for `or` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty) (bor x y)))
|
||||
(alu_rrr (AluOPRRR.Or) x y))
|
||||
|
||||
;; Special cases for when one operand is an immediate that fits in 12 bits.
|
||||
(rule (lower (has_type (fits_in_64 ty) (bor x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Ori) x y))
|
||||
|
||||
(rule (lower (has_type (fits_in_64 ty) (bor (imm12_from_value x) y)))
|
||||
(alu_rr_imm12 (AluOPRRI.Ori) y x))
|
||||
(rule (lower (has_type $B128 (bor x y)))
|
||||
(lower_b128_binary (AluOPRRR.Or) x y))
|
||||
(rule (lower (has_type $I128 (bor x y)))
|
||||
(lower_b128_binary (AluOPRRR.Or) x y))
|
||||
(rule (lower (has_type $F32 (bor x y)))
|
||||
(lower_float_binary (AluOPRRR.Or) x y $F32))
|
||||
(rule (lower (has_type $F64 (bor x y)))
|
||||
(lower_float_binary (AluOPRRR.Or) x y $F64))
|
||||
|
||||
|
||||
;;;; Rules for `xor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty) (bxor x y)))
|
||||
(alu_rrr (AluOPRRR.Xor) x y))
|
||||
|
||||
;; Special cases for when one operand is an immediate that fits in 12 bits.
|
||||
(rule (lower (has_type (fits_in_64 ty) (bxor x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Xori) x y))
|
||||
|
||||
(rule (lower (has_type (fits_in_64 ty) (bxor (imm12_from_value x) y)))
|
||||
(alu_rr_imm12 (AluOPRRI.Xori) y x))
|
||||
(rule (lower (has_type $B128 (bxor x y)))
|
||||
(lower_b128_binary (AluOPRRR.Xor) x y))
|
||||
(rule (lower (has_type $I128 (bxor x y)))
|
||||
(lower_b128_binary (AluOPRRR.Xor) x y))
|
||||
(rule (lower (has_type $F32 (bxor x y)))
|
||||
(lower_float_binary (AluOPRRR.Xor) x y $F32))
|
||||
(rule (lower (has_type $F64 (bxor x y)))
|
||||
(lower_float_binary (AluOPRRR.Xor) x y $F64))
|
||||
|
||||
|
||||
;;;; Rules for `bnot` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type fits_in_64 (bnot x)))
|
||||
(alu_rr_imm12 (AluOPRRI.Xori) x (imm_from_neg_bits -1)))
|
||||
|
||||
(rule (lower (has_type $I128 (bnot x)))
|
||||
(bnot_128 x))
|
||||
(rule (lower (has_type $B128 (bnot x)))
|
||||
(bnot_128 x))
|
||||
(rule
|
||||
(lower (has_type $F32 (bnot x)))
|
||||
(lower_float_bnot x $F32)
|
||||
)
|
||||
(rule
|
||||
(lower (has_type $F64 (bnot x)))
|
||||
(lower_float_bnot x $F64)
|
||||
)
|
||||
|
||||
;;;; Rules for `bit_reverse` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type ty (bitrev x)))
|
||||
(lower_bit_reverse x ty))
|
||||
|
||||
(rule (lower (has_type $I128 (bitrev x)))
|
||||
(let ((val ValueRegs x)
|
||||
(lo_rev Reg (lower_bit_reverse (value_regs_get val 0) $I64))
|
||||
(hi_rev Reg (lower_bit_reverse (value_regs_get val 1) $I64)))
|
||||
(value_regs hi_rev lo_rev)))
|
||||
|
||||
|
||||
;;;; Rules for `ctz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type ty (ctz x)))
|
||||
(lower_ctz ty x))
|
||||
|
||||
(rule (lower (has_type $I128 (ctz x)))
|
||||
(lower_ctz_128 x))
|
||||
|
||||
;;;; Rules for `clz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type ty (clz x)))
|
||||
(lower_clz ty x))
|
||||
(rule (lower (has_type $I128 (clz x)))
|
||||
(lower_clz_i128 x))
|
||||
|
||||
;;;; Rules for `uextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type out (uextend x @ (value_type in))))
|
||||
(lower_extend x $false (ty_bits in) (ty_bits out)))
|
||||
|
||||
;;;; Rules for `sextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type out (sextend x @ (value_type in))))
|
||||
(lower_extend x $true (ty_bits in) (ty_bits out)))
|
||||
|
||||
|
||||
;;;; Rules for `band_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty) (band_not x y)))
|
||||
(gen_andn x y))
|
||||
(rule (lower (has_type $I128 (band_not x y)))
|
||||
(let
|
||||
((low Reg (gen_andn (value_regs_get x 0) (value_regs_get y 0)))
|
||||
(high Reg (gen_andn (value_regs_get x 1) (value_regs_get y 1))))
|
||||
(value_regs low high)))
|
||||
|
||||
;;;; Rules for `popcnt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty) (popcnt x)))
|
||||
(lower_popcnt x ty))
|
||||
(rule (lower (has_type $I128 (popcnt x)))
|
||||
(lower_popcnt_i128 x))
|
||||
|
||||
;;;; Rules for `ishl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type $I8 (ishl x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Sllw) x (alu_andi y 7))
|
||||
)
|
||||
(rule (lower (has_type $I8(ishl x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Slliw) x (imm12_and y 7)))
|
||||
|
||||
(rule (lower (has_type $I16 (ishl x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Sllw) x (alu_andi y 15))
|
||||
)
|
||||
(rule (lower (has_type $I16(ishl x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Slliw) x (imm12_and y 15)))
|
||||
|
||||
(rule (lower (has_type $I32(ishl x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Sllw) x y))
|
||||
(rule (lower (has_type $I32 (ishl x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Slliw) x y))
|
||||
|
||||
(rule (lower (has_type $I64 (ishl x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Slli) x y))
|
||||
(rule (lower (has_type $I64(ishl x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Sll) x y))
|
||||
|
||||
(rule (lower (has_type $I128 (ishl x y)))
|
||||
(lower_i128_ishl x y))
|
||||
|
||||
;;;; Rules for `ushr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type $I8 (ushr x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Srlw) (ext_int_if_need $false x $I8) (alu_andi y 7))
|
||||
)
|
||||
(rule (lower (has_type $I8(ushr x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.SrliW) (ext_int_if_need $false x $I8) (imm12_and y 7)))
|
||||
|
||||
(rule (lower (has_type $I16 (ushr x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Srlw) (ext_int_if_need $false x $I16) (alu_andi y 15))
|
||||
)
|
||||
(rule (lower (has_type $I16(ushr x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.SrliW) (ext_int_if_need $false x $I16) (imm12_and y 15)))
|
||||
|
||||
(rule (lower (has_type $I32(ushr x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Srlw) x y))
|
||||
(rule (lower (has_type $I32 (ushr x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.SrliW) x y))
|
||||
|
||||
(rule (lower (has_type $I64 (ushr x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Srli) x y))
|
||||
(rule (lower (has_type $I64(ushr x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Srl) x y))
|
||||
|
||||
(rule (lower (has_type $I128 (ushr x y)))
|
||||
(lower_i128_ushr x y))
|
||||
|
||||
|
||||
;;;; Rules for `sshr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type $I8 (sshr x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Sra) (ext_int_if_need $true x $I8) (alu_andi y 7))
|
||||
)
|
||||
(rule (lower (has_type $I8(sshr x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Srai) (ext_int_if_need $true x $I8) (imm12_and y 7)))
|
||||
|
||||
(rule (lower (has_type $I16 (sshr x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Sra) (ext_int_if_need $true x $I16) (alu_andi y 15))
|
||||
)
|
||||
(rule (lower (has_type $I16(sshr x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Srai) (ext_int_if_need $true x $I16) (imm12_and y 15)))
|
||||
|
||||
(rule (lower (has_type $I32 (sshr x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Sraw) x y))
|
||||
(rule (lower (has_type $I32 (sshr x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Sraiw) x y))
|
||||
(rule (lower (has_type $I64 (sshr x (valueregs_2_reg y))))
|
||||
(alu_rrr (AluOPRRR.Sra) x y))
|
||||
(rule (lower (has_type $I64(sshr x (imm12_from_value y))))
|
||||
(alu_rr_imm12 (AluOPRRI.Srai) x y))
|
||||
(rule (lower (has_type $I128 (sshr x y)))
|
||||
(lower_i128_sshr x y))
|
||||
|
||||
|
||||
;;;; Rules for `rotl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty)(rotl x (valueregs_2_reg y))))
|
||||
(lower_rotl ty (ext_int_if_need $false x ty) y))
|
||||
|
||||
(rule (lower (has_type $I128 (rotl x y)))
|
||||
(lower_i128_rotl x y))
|
||||
|
||||
;;;; Rules for `rotr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty)(rotr x (valueregs_2_reg y))))
|
||||
(lower_rotr ty (ext_int_if_need $false x ty) y))
|
||||
|
||||
(rule (lower (has_type $I128 (rotr x y)))
|
||||
(lower_i128_rotr x y))
|
||||
|
||||
|
||||
;;;; Rules for `bxor_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; notice x y order!!!
|
||||
(rule (lower (has_type (fits_in_64 ty)(bxor_not x y)))
|
||||
(gen_xor_not x y))
|
||||
(rule (lower (has_type $I128 (bxor_not x y)))
|
||||
(let
|
||||
((low Reg (gen_xor_not (value_regs_get x 0) (value_regs_get y 0)))
|
||||
(high Reg (gen_xor_not (value_regs_get x 1) (value_regs_get y 1))))
|
||||
(value_regs low high)
|
||||
)
|
||||
)
|
||||
|
||||
;;;; Rules for `bor_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty)(bor_not x y)))
|
||||
(gen_orn x y))
|
||||
|
||||
(rule (lower (has_type $I128 (bor_not x y)))
|
||||
(let
|
||||
((low Reg (gen_orn (value_regs_get x 0) (value_regs_get y 0)))
|
||||
(high Reg (gen_orn (value_regs_get x 1) (value_regs_get y 1))))
|
||||
(value_regs low high)))
|
||||
|
||||
|
||||
;;;; Rules for `cls` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type (fits_in_64 ty)(cls x)))
|
||||
(lower_cls x ty))
|
||||
(rule (lower (has_type $I128 (cls x)))
|
||||
(lower_cls_i128 x))
|
||||
|
||||
|
||||
;;;; Rules for `fabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (fabs x)))
|
||||
(gen_fabs x ty))
|
||||
|
||||
;;;; Rules for `fneg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (fneg x)))
|
||||
(fpu_rrr (f_copy_neg_sign_op ty) ty x x))
|
||||
|
||||
;;;; Rules for `fcopysign` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type ty (fcopysign x y)))
|
||||
(fpu_rrr (f_copysign_op ty) ty x y))
|
||||
|
||||
;;;; Rules for `fma` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type $F32 (fma x y z)))
|
||||
(fpu_rrrr (FpuOPRRRR.FmaddS) $F64 x y z))
|
||||
(rule (lower (has_type $F64 (fma x y z)))
|
||||
(fpu_rrrr (FpuOPRRRR.FmaddD) $F64 x y z))
|
||||
|
||||
|
||||
;;;; Rules for `sqrt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule (lower (has_type $F32 (sqrt x)))
|
||||
(fpu_rr (FpuOPRR.FsqrtS)$F64 x))
|
||||
|
||||
(rule (lower (has_type $F64 (sqrt x)))
|
||||
(fpu_rr (FpuOPRR.FsqrtD)$F64 x))
|
||||
|
||||
|
||||
;;;; Rules for `AtomicRMW` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(rule
|
||||
;;
|
||||
(lower
|
||||
(has_type (valid_atomic_transaction ty) (atomic_rmw flags op addr x)))
|
||||
(gen_atomic (get_atomic_rmw_op ty op) addr x (atomic_amo)))
|
||||
|
||||
;;; for I8 and I16
|
||||
(rule
|
||||
(lower
|
||||
(has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw flags op addr x)))
|
||||
(gen_atomic_rmw_loop op ty addr x))
|
||||
|
||||
;;;special for I8 and I16 max min etc.
|
||||
;;;because I need uextend or sextend the value.
|
||||
(rule
|
||||
(lower
|
||||
(has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw flags (is_atomic_rmw_max_etc op $true) addr x)))
|
||||
(gen_atomic_rmw_loop op ty addr (ext_int_if_need $true x ty)))
|
||||
|
||||
|
||||
(rule
|
||||
;;
|
||||
(lower
|
||||
(has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw flags (is_atomic_rmw_max_etc op $false) addr x)))
|
||||
;;
|
||||
(gen_atomic_rmw_loop op ty addr (ext_int_if_need $false x ty)))
|
||||
|
||||
;;;;; Rules for `AtomicRmwOp.Sub`
|
||||
(rule
|
||||
(lower
|
||||
(has_type (valid_atomic_transaction ty) (atomic_rmw flags (AtomicRmwOp.Sub) addr x)))
|
||||
(let
|
||||
((tmp WritableReg (temp_writable_reg ty))
|
||||
(x2 Reg (alu_rrr (AluOPRRR.Sub) (zero_reg) x)))
|
||||
(gen_atomic (get_atomic_rmw_op ty (AtomicRmwOp.Add)) addr x2 (atomic_amo))))
|
||||
|
||||
(decl gen_atomic_rmw_loop (AtomicRmwOp Type Reg Reg) Reg)
|
||||
(rule
|
||||
(gen_atomic_rmw_loop op ty addr x)
|
||||
(let
|
||||
((dst WritableReg (temp_writable_reg $I64))
|
||||
(t0 WritableReg (temp_writable_reg $I64))
|
||||
(_ Unit (emit (MInst.AtomicRmwLoop (gen_atomic_offset addr ty) op dst ty (gen_atomic_p addr ty) x t0))))
|
||||
(writable_reg_to_reg dst)))
|
||||
|
||||
;;;;; Rules for `AtomicRmwOp.Nand`
|
||||
(rule
|
||||
(lower
|
||||
(has_type (valid_atomic_transaction ty) (atomic_rmw flags (AtomicRmwOp.Nand) addr x)))
|
||||
(gen_atomic_rmw_loop (AtomicRmwOp.Nand) ty addr x))
|
||||
|
||||
(decl is_atomic_rmw_max_etc (AtomicRmwOp bool)AtomicRmwOp)
|
||||
(extern extractor is_atomic_rmw_max_etc is_atomic_rmw_max_etc)
|
||||
|
||||
;;;;; Rules for `atomic load`;;;;;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type (valid_atomic_transaction ty) (atomic_load flags p)))
|
||||
(gen_atomic_load p ty))
|
||||
|
||||
|
||||
;;;;; Rules for `atomic store`;;;;;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (atomic_store flags src @ (value_type (valid_atomic_transaction ty)) p))
|
||||
(gen_atomic_store p ty src))
|
||||
|
||||
(decl gen_atomic_offset (Reg Type) Reg)
|
||||
(rule (gen_atomic_offset p (fits_in_16 ty))
|
||||
(alu_slli (alu_andi p 3) 3))
|
||||
|
||||
(rule (gen_atomic_offset p _)
|
||||
(zero_reg))
|
||||
|
||||
(decl gen_atomic_p (Reg Type) Reg)
|
||||
(rule (gen_atomic_p p (fits_in_16 ty))
|
||||
(alu_andi p -4))
|
||||
|
||||
(rule (gen_atomic_p p _)
|
||||
p)
|
||||
|
||||
|
||||
;;;;; Rules for `atomic cas`;;;;;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type (valid_atomic_transaction ty) (atomic_cas flags p e x)))
|
||||
(let
|
||||
((t0 WritableReg (temp_writable_reg ty))
|
||||
(dst WritableReg (temp_writable_reg ty))
|
||||
(_ Unit(emit (MInst.AtomicCas (gen_atomic_offset p ty) t0 dst (ext_int_if_need $false e ty) (gen_atomic_p p ty) x ty))))
|
||||
(writable_reg_to_reg dst)))
|
||||
|
||||
;;;;; Rules for `copy`;;;;;;;;;;;;;;;;;
|
||||
(rule (lower(has_type ty (copy x)))
|
||||
(gen_move2 x ty ty))
|
||||
|
||||
;;;;; Rules for `breduce`;;;;;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (breduce x)))
|
||||
(gen_move2 (value_regs_get x 0) ty ty))
|
||||
|
||||
;;;;; Rules for `ireduce`;;;;;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (ireduce x)))
|
||||
(gen_move2 (value_regs_get x 0) ty ty))
|
||||
|
||||
;;;;; Rules for `fpromote`;;;;;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (fpromote x)))
|
||||
(fpu_rr (FpuOPRR.FcvtDS) ty x))
|
||||
|
||||
(rule
|
||||
(lower (has_type ty (fdemote x)))
|
||||
(fpu_rr (FpuOPRR.FcvtSD) ty x))
|
||||
|
||||
|
||||
;;;;; Rules for `for float arithmatic`
|
||||
(rule
|
||||
(lower (has_type ty (fadd x y)))
|
||||
(fpu_rrr (f_arithmatic_op ty (Opcode.Fadd)) ty x y))
|
||||
(rule
|
||||
(lower (has_type ty (fsub x y)))
|
||||
(fpu_rrr (f_arithmatic_op ty (Opcode.Fsub)) ty x y))
|
||||
(rule
|
||||
(lower (has_type ty (fmul x y)))
|
||||
(fpu_rrr (f_arithmatic_op ty (Opcode.Fmul)) ty x y))
|
||||
(rule
|
||||
(lower (has_type ty (fdiv x y)))
|
||||
(fpu_rrr (f_arithmatic_op ty (Opcode.Fdiv)) ty x y))
|
||||
|
||||
(rule
|
||||
(lower (has_type ty (fmin x y)))
|
||||
(gen_float_select (FloatSelectOP.Min) x y ty))
|
||||
|
||||
(rule
|
||||
(lower (has_type ty (fmin_pseudo x y)))
|
||||
(gen_float_select_pseudo (FloatSelectOP.Min) x y ty))
|
||||
|
||||
(rule
|
||||
(lower (has_type ty (fmax x y)))
|
||||
(gen_float_select (FloatSelectOP.Max) x y ty))
|
||||
|
||||
(rule
|
||||
(lower (has_type ty (fmax_pseudo x y)))
|
||||
(gen_float_select_pseudo (FloatSelectOP.Max) x y ty))
|
||||
|
||||
;;;;; Rules for `stack_addr`;;;;;;;;;
|
||||
(rule
|
||||
(lower (stack_addr ss offset))
|
||||
(gen_stack_addr ss offset))
|
||||
|
||||
;;;;; Rules for `is_null`;;;;;;;;;
|
||||
(rule
|
||||
(lower (is_null v))
|
||||
(gen_reference_check (ReferenceCheckOP.IsNull) v))
|
||||
|
||||
;;;;; Rules for `is_invalid`;;;;;;;;;
|
||||
(rule
|
||||
(lower (is_invalid v))
|
||||
(gen_reference_check (ReferenceCheckOP.IsInvalid) v))
|
||||
|
||||
;;;;; Rules for `select`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (select c x y)))
|
||||
(gen_select ty c x y)
|
||||
)
|
||||
|
||||
;;;;; Rules for `bitselect`;;;;;;;;;
|
||||
|
||||
(rule
|
||||
(lower (has_type ty (bitselect c x y)))
|
||||
(gen_bitselect ty c x y))
|
||||
|
||||
;;;;; Rules for `bint`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type (fits_in_64 ty) (bint (valueregs_2_reg x))))
|
||||
(gen_bint x))
|
||||
(rule
|
||||
(lower (has_type $I128 (bint (valueregs_2_reg x))))
|
||||
(let ((tmp Reg (gen_bint x)))
|
||||
(value_regs tmp (zero_reg)))
|
||||
)
|
||||
|
||||
;;;;; Rules for `isplit`;;;;;;;;;
|
||||
(rule
|
||||
(lower (isplit x))
|
||||
(let
|
||||
((t1 Reg (gen_move2 (value_regs_get x 0) $I64 $I64))
|
||||
(t2 Reg (gen_move2 (value_regs_get x 1) $I64 $I64)))
|
||||
(output_pair t1 t2)))
|
||||
|
||||
;;;;; Rules for `iconcat`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type $I128 (iconcat x y)))
|
||||
(let
|
||||
((t1 Reg (gen_move2 x $I64 $I64))
|
||||
(t2 Reg (gen_move2 y $I64 $I64)))
|
||||
(value_regs t1 t2)))
|
||||
|
||||
;;;;; Rules for `imax`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (imax x y)))
|
||||
(gen_int_select ty (IntSelectOP.Imax) (ext_int_if_need $true x ty) (ext_int_if_need $true y ty)))
|
||||
|
||||
;;;;; Rules for `imin`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (imin x y)))
|
||||
(gen_int_select ty(IntSelectOP.Imin) (ext_int_if_need $true x ty) (ext_int_if_need $true y ty)))
|
||||
;;;;; Rules for `umax`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (umax x y)))
|
||||
(gen_int_select ty(IntSelectOP.Umax) (ext_int_if_need $false x ty) (ext_int_if_need $false y ty)))
|
||||
|
||||
;;;;; Rules for `umin`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (umin x y)))
|
||||
(gen_int_select ty(IntSelectOP.Umin) (ext_int_if_need $false x ty) (ext_int_if_need $false y ty)))
|
||||
|
||||
;;;;; Rules for `debugtrap`;;;;;;;;;
|
||||
(rule
|
||||
(lower (debugtrap))
|
||||
(side_effect (SideEffectNoResult.Inst(MInst.EBreak))))
|
||||
|
||||
;;;;; Rules for `fence`;;;;;;;;;
|
||||
(rule
|
||||
(lower (fence))
|
||||
(side_effect (SideEffectNoResult.Inst(MInst.Fence 15 15))))
|
||||
|
||||
;;;;; Rules for `trap`;;;;;;;;;
|
||||
(rule
|
||||
(lower (trap code))
|
||||
(udf code))
|
||||
|
||||
;;;;; Rules for `resumable_trap`;;;;;;;;;
|
||||
(rule
|
||||
(lower (resumable_trap code))
|
||||
(udf code))
|
||||
|
||||
;;;;; Rules for `uload8`;;;;;;;;;
|
||||
(rule
|
||||
(lower (uload8 flags p offset))
|
||||
(gen_load p offset (int_load_op $false 8) flags $I64))
|
||||
;;;;; Rules for `sload8`;;;;;;;;;
|
||||
(rule
|
||||
(lower (sload8 flags p offset))
|
||||
(gen_load p offset (int_load_op $true 8) flags $I64))
|
||||
;;;;; Rules for `uload16`;;;;;;;;;
|
||||
(rule
|
||||
(lower (uload16 flags p offset))
|
||||
(gen_load p offset (int_load_op $false 16) flags $I64))
|
||||
|
||||
;;;;; Rules for `iload16`;;;;;;;;;
|
||||
(rule
|
||||
(lower (sload16 flags p offset))
|
||||
(gen_load p offset (int_load_op $true 16) flags $I64))
|
||||
|
||||
;;;;; Rules for `uload32`;;;;;;;;;
|
||||
(rule
|
||||
(lower (uload32 flags p offset))
|
||||
(gen_load p offset (int_load_op $false 32) flags $I64))
|
||||
|
||||
;;;;; Rules for `iload16`;;;;;;;;;
|
||||
(rule
|
||||
(lower (sload32 flags p offset))
|
||||
(gen_load p offset (int_load_op $true 32) flags $I64))
|
||||
|
||||
(rule
|
||||
(lower (has_type ty (load flags p offset)))
|
||||
(gen_load p offset (load_op ty) flags ty)
|
||||
)
|
||||
;;;; for I128
|
||||
(rule
|
||||
(lower (has_type $I128 (load flags p offset)))
|
||||
(gen_load_128 p offset flags))
|
||||
;;;; for B128
|
||||
(rule
|
||||
(lower (has_type $B128 (load flags p offset)))
|
||||
(gen_load_128 p offset flags))
|
||||
|
||||
;;;;; Rules for `istore8`;;;;;;;;;
|
||||
(rule
|
||||
(lower (istore8 flags x p offset))
|
||||
(gen_store p offset (StoreOP.Sb) flags x))
|
||||
;;;;; Rules for `istore16`;;;;;;;;;
|
||||
(rule
|
||||
(lower (istore16 flags x p offset))
|
||||
(gen_store p offset (StoreOP.Sh) flags x))
|
||||
|
||||
;;;;; Rules for `istore32`;;;;;;;;;
|
||||
(rule
|
||||
(lower (istore32 flags x p offset))
|
||||
(gen_store p offset (StoreOP.Sw) flags x))
|
||||
|
||||
;;;;; Rules for `store`;;;;;;;;;
|
||||
(rule
|
||||
(lower (store flags x @(value_type ty) p offset))
|
||||
(gen_store p offset (store_op ty) flags x))
|
||||
|
||||
;;; special for I128
|
||||
(rule
|
||||
(lower (store flags x @ (value_type $I128 ) p offset))
|
||||
(gen_store_128 p offset flags x))
|
||||
|
||||
;;; special for B128
|
||||
(rule
|
||||
(lower (store flags x @ (value_type $B128 ) p offset))
|
||||
(gen_store_128 p offset flags x))
|
||||
|
||||
(decl gen_icmp(IntCC ValueRegs ValueRegs Type)Reg)
|
||||
(rule
|
||||
(gen_icmp cc x y ty)
|
||||
(let
|
||||
((result WritableReg (temp_writable_reg $I64))
|
||||
(_ Unit (emit (MInst.Icmp cc result x y ty))))
|
||||
result))
|
||||
|
||||
;;;;; Rules for `icmp`;;;;;;;;;
|
||||
(rule
|
||||
(lower (icmp cc x @ (value_type ty) y))
|
||||
(lower_icmp cc x y ty))
|
||||
;; special for `iadd_ifcout` first out.
|
||||
(rule
|
||||
(lower (icmp cc (iadd_ifcout a @ (value_type ty) b) y))
|
||||
(lower_icmp cc (alu_add a b) y ty))
|
||||
|
||||
(rule
|
||||
(lower (icmp cc x (iadd_ifcout a @ (value_type ty) b)))
|
||||
(lower_icmp cc x (alu_add a b) ty))
|
||||
|
||||
(decl gen_fcmp(FloatCC Value Value Type)Reg)
|
||||
(rule
|
||||
(gen_fcmp cc x y ty)
|
||||
(let
|
||||
((result WritableReg (temp_writable_reg $I64))
|
||||
(_ Unit (emit (MInst.Fcmp cc result x y ty))))
|
||||
(writable_reg_to_reg result)))
|
||||
|
||||
;;;;; Rules for `fcmp`;;;;;;;;;
|
||||
(rule
|
||||
(lower (fcmp cc x @ (value_type ty) y))
|
||||
(gen_fcmp cc x y ty))
|
||||
|
||||
;;;;; Rules for `func_addr`;;;;;;;;;
|
||||
(rule
|
||||
(lower (func_addr (func_ref_data _ name _)))
|
||||
(load_ext_name name 0))
|
||||
|
||||
;;;;; Rules for `fcvt_to_uint`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type to (fcvt_to_uint v @(value_type from))))
|
||||
(gen_fcvt_int $false v $false from to))
|
||||
|
||||
;;;;; Rules for `fcvt_to_sint`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type to (fcvt_to_sint v @ (value_type from))))
|
||||
(gen_fcvt_int $false v $true from to))
|
||||
|
||||
;;;;; Rules for `fcvt_to_sint_sat`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type to (fcvt_to_sint_sat v @ (value_type from))))
|
||||
(gen_fcvt_int $true v $true from to))
|
||||
|
||||
;;;;; Rules for `fcvt_to_uint_sat`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type to (fcvt_to_uint_sat v @ (value_type from))))
|
||||
(gen_fcvt_int $true v $false from to))
|
||||
|
||||
;;;;; Rules for `fcvt_from_sint`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type to (fcvt_from_sint v @ (value_type from))))
|
||||
(fpu_rr (int_convert_2_float_op from $true to) to v))
|
||||
|
||||
;;;;; Rules for `fcvt_from_uint`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type to (fcvt_from_uint v @ (value_type from))))
|
||||
(fpu_rr (int_convert_2_float_op from $false to) to v))
|
||||
|
||||
;;;;; Rules for `symbol_value`;;;;;;;;;
|
||||
(rule
|
||||
(lower (symbol_value (symbol_value_data name _ offset)))
|
||||
(load_ext_name name offset)
|
||||
)
|
||||
;;;;; Rules for `bitcast`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type out (bitcast v @ (value_type in_ty))))
|
||||
(gen_moves v in_ty out))
|
||||
|
||||
;;;;; Rules for `raw_bitcast`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type out (raw_bitcast v @ (value_type in_ty))))
|
||||
(gen_moves v in_ty out))
|
||||
|
||||
;;;;; Rules for `ceil`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (ceil x)))
|
||||
(gen_float_round (FloatRoundOP.Ceil) x ty)
|
||||
)
|
||||
|
||||
;;;;; Rules for `floor`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (floor x)))
|
||||
(gen_float_round (FloatRoundOP.Floor) x ty))
|
||||
;;;;; Rules for `trunc`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (trunc x)))
|
||||
(gen_float_round (FloatRoundOP.Trunc) x ty))
|
||||
|
||||
;;;;; Rules for `nearest`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (nearest x)))
|
||||
(gen_float_round (FloatRoundOP.Nearest) x ty))
|
||||
|
||||
|
||||
;;;;; Rules for `selectif`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type r_ty (selectif cc (ifcmp ca @ (value_type cty) cb) a b)))
|
||||
(let
|
||||
((dst VecWritableReg (alloc_vec_writable r_ty))
|
||||
(r Reg (lower_icmp cc ca cb cty))
|
||||
(_ Unit (emit (MInst.SelectIf $false (vec_writable_clone dst) r a b))))
|
||||
(vec_writable_to_regs dst)))
|
||||
|
||||
;;;;; Rules for `selectif_spectre_guard`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type r_ty (selectif_spectre_guard cc (ifcmp ca @ (value_type cty) cb) a b)))
|
||||
(let
|
||||
((dst VecWritableReg (alloc_vec_writable r_ty))
|
||||
(r Reg (lower_icmp cc ca cb cty))
|
||||
(_ Unit (emit (MInst.SelectIf $true (vec_writable_clone dst) r a b))))
|
||||
(vec_writable_to_regs dst)))
|
||||
|
||||
;;;;; Rules for `trueif`;;;;;;;;;
|
||||
|
||||
(rule
|
||||
(lower (has_type ty (trueif cc (ifcmp ca @ (value_type cty) cb))))
|
||||
(lower_icmp cc ca cb cty))
|
||||
|
||||
;;;;; Rules for `trueff`;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type ty (trueff cc (ffcmp ca @ (value_type cty) cb))))
|
||||
(gen_fcmp cc ca cb cty))
|
||||
|
||||
|
||||
;;;;; Rules for `trapif`;;;;;;;;;
|
||||
(rule
|
||||
(lower (trapif cc (ifcmp a @ (value_type ty) b) trap_code))
|
||||
(let
|
||||
((test Reg (lower_icmp cc a b ty)))
|
||||
(gen_trapif test trap_code)))
|
||||
|
||||
(rule
|
||||
(lower (trapif _ (iadd_ifcout a @ (value_type ty) b) trap_code))
|
||||
(let
|
||||
((test Reg (lower_uadd_overflow a b ty)))
|
||||
(gen_trapif test trap_code)))
|
||||
|
||||
|
||||
;;;;; Rules for `trapff`;;;;;;;;;
|
||||
(rule
|
||||
(lower (trapff cc (ffcmp a @(value_type ty) b) trap_code))
|
||||
(gen_trapff cc a b ty trap_code))
|
||||
|
||||
;;;;; Rules for `bmask`;;;;;;;;;
|
||||
(rule
|
||||
;; because we encode bool all 1s.
|
||||
;; move is just ok.
|
||||
(lower (has_type (fits_in_64 ty) (bmask x @ (value_type ity))))
|
||||
(gen_move2 (value_regs_get x 0) ity ty))
|
||||
;;; for i128
|
||||
(rule
|
||||
;; because we encode bool all 1s.
|
||||
;; move is just ok.
|
||||
(lower (has_type $I128 (bmask x @ (value_type ity))))
|
||||
(value_regs (gen_move2 (value_regs_get x 0) $I64 $I64) (gen_move2 (value_regs_get x 0) $I64 $I64)))
|
||||
|
||||
;;;;; Rules for `bextend`;;;;;;;;;
|
||||
(rule
|
||||
;; because we encode bool all 1s.
|
||||
;; move is just ok.
|
||||
(lower (has_type ty (bextend x @ (value_type ity))))
|
||||
;;extra checks.
|
||||
(if-let _ (valid_bextend_ty ity ty))
|
||||
(gen_moves x ity ty))
|
||||
|
||||
;;; for B128
|
||||
(rule
|
||||
;; because we encode bool all 1s.
|
||||
;; move is just ok.
|
||||
(lower (has_type ty (bextend x @ (value_type ity))))
|
||||
;;extra checks.
|
||||
(if-let $B128 (valid_bextend_ty ity ty))
|
||||
(value_regs (gen_moves x $I64 $I64) (gen_moves x $I64 $I64)))
|
||||
|
||||
;; N.B.: the Ret itself is generated by the ABI.
|
||||
(rule (lower (return args))
|
||||
(lower_return (range 0 (value_slice_len args)) args))
|
||||
|
||||
|
||||
;;; Rules for `get_{frame,stack}_pointer` and `get_return_address` ;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (get_frame_pointer))
|
||||
(gen_move2 (x_reg 8) $I64 $I64))
|
||||
|
||||
(rule (lower (get_stack_pointer))
|
||||
(gen_move2 (x_reg 2) $I64 $I64))
|
||||
|
||||
(rule (lower (get_return_address))
|
||||
(load_ra))
|
||||
|
||||
;;; Rules for `iabs` ;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type (fits_in_64 ty) (iabs x)))
|
||||
(lower_iabs x ty))
|
||||
|
||||
;;;; Rules for calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (call (func_ref_data sig_ref extname dist) inputs))
|
||||
(gen_call sig_ref extname dist inputs))
|
||||
|
||||
(rule (lower (call_indirect sig_ref val inputs))
|
||||
(gen_call_indirect sig_ref val inputs))
|
||||
62
cranelift/codegen/src/isa/riscv64/lower.rs
Normal file
62
cranelift/codegen/src/isa/riscv64/lower.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
//! Lowering rules for Riscv64.
|
||||
use super::lower_inst;
|
||||
use crate::ir::Inst as IRInst;
|
||||
use crate::isa::riscv64::inst::*;
|
||||
use crate::isa::riscv64::Riscv64Backend;
|
||||
use crate::machinst::lower::*;
|
||||
use crate::machinst::*;
|
||||
use crate::CodegenResult;
|
||||
pub mod isle;
|
||||
|
||||
//=============================================================================
|
||||
// Lowering-backend trait implementation.
|
||||
|
||||
impl LowerBackend for Riscv64Backend {
|
||||
type MInst = Inst;
|
||||
|
||||
fn lower(&self, ctx: &mut Lower<Inst>, ir_inst: IRInst) -> CodegenResult<()> {
|
||||
lower_inst::lower_insn_to_regs(ctx, ir_inst, &self.triple, &self.flags, &self.isa_flags)
|
||||
}
|
||||
|
||||
fn lower_branch_group(
|
||||
&self,
|
||||
ctx: &mut Lower<Inst>,
|
||||
branches: &[IRInst],
|
||||
targets: &[MachLabel],
|
||||
) -> CodegenResult<()> {
|
||||
// A block should end with at most two branches. The first may be a
|
||||
// conditional branch; a conditional branch can be followed only by an
|
||||
// unconditional branch or fallthrough. Otherwise, if only one branch,
|
||||
// it may be an unconditional branch, a fallthrough, a return, or a
|
||||
// trap. These conditions are verified by `is_ebb_basic()` during the
|
||||
// verifier pass.
|
||||
assert!(branches.len() <= 2);
|
||||
if branches.len() == 2 {
|
||||
let op1 = ctx.data(branches[1]).opcode();
|
||||
assert!(op1 == Opcode::Jump);
|
||||
}
|
||||
|
||||
// Lower the first branch in ISLE. This will automatically handle
|
||||
// the second branch (if any) by emitting a two-way conditional branch.
|
||||
if let Ok(()) = super::lower::isle::lower_branch(
|
||||
ctx,
|
||||
&self.triple,
|
||||
&self.flags,
|
||||
&self.isa_flags,
|
||||
branches[0],
|
||||
targets,
|
||||
) {
|
||||
return Ok(());
|
||||
}
|
||||
unreachable!(
|
||||
"implemented in ISLE: branch = `{}`",
|
||||
ctx.dfg().display_inst(branches[0]),
|
||||
);
|
||||
}
|
||||
|
||||
fn maybe_pinned_reg(&self) -> Option<Reg> {
|
||||
// pinned register is a register that you want put anything in it.
|
||||
// right now riscv64 not support this feature.
|
||||
None
|
||||
}
|
||||
}
|
||||
544
cranelift/codegen/src/isa/riscv64/lower/isle.rs
Normal file
544
cranelift/codegen/src/isa/riscv64/lower/isle.rs
Normal file
@@ -0,0 +1,544 @@
|
||||
//! ISLE integration glue code for riscv64 lowering.
|
||||
|
||||
// Pull in the ISLE generated code.
|
||||
#[allow(unused)]
|
||||
pub mod generated_code;
|
||||
use generated_code::{Context, MInst};
|
||||
|
||||
use target_lexicon::Triple;
|
||||
|
||||
// Types that the generated ISLE code uses via `use super::*`.
|
||||
use super::{writable_zero_reg, zero_reg};
|
||||
use std::vec::Vec;
|
||||
|
||||
use crate::isa::riscv64::settings::Flags as IsaFlags;
|
||||
use crate::machinst::{isle::*, MachInst, SmallInstVec};
|
||||
use crate::settings::Flags;
|
||||
|
||||
use crate::machinst::{VCodeConstant, VCodeConstantData};
|
||||
use crate::{
|
||||
ir::{
|
||||
immediates::*, types::*, AtomicRmwOp, ExternalName, Inst, InstructionData, MemFlags,
|
||||
StackSlot, TrapCode, Value, ValueList,
|
||||
},
|
||||
isa::riscv64::inst::*,
|
||||
machinst::{ArgPair, InsnOutput, Lower},
|
||||
};
|
||||
use regalloc2::PReg;
|
||||
|
||||
use crate::isa::riscv64::abi::Riscv64ABICaller;
|
||||
use std::boxed::Box;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::machinst::Reg;
|
||||
|
||||
type BoxCallInfo = Box<CallInfo>;
|
||||
type BoxCallIndInfo = Box<CallIndInfo>;
|
||||
type BoxExternalName = Box<ExternalName>;
|
||||
type VecMachLabel = Vec<MachLabel>;
|
||||
type VecArgPair = Vec<ArgPair>;
|
||||
use crate::machinst::valueregs;
|
||||
|
||||
/// The main entry point for lowering with ISLE.
|
||||
pub(crate) fn lower(
|
||||
lower_ctx: &mut Lower<MInst>,
|
||||
flags: &Flags,
|
||||
triple: &Triple,
|
||||
isa_flags: &IsaFlags,
|
||||
outputs: &[InsnOutput],
|
||||
inst: Inst,
|
||||
) -> Result<(), ()> {
|
||||
lower_common(
|
||||
lower_ctx,
|
||||
triple,
|
||||
flags,
|
||||
isa_flags,
|
||||
outputs,
|
||||
inst,
|
||||
|cx, insn| generated_code::constructor_lower(cx, insn),
|
||||
)
|
||||
}
|
||||
|
||||
impl IsleContext<'_, '_, MInst, Flags, IsaFlags, 6> {
|
||||
isle_prelude_method_helpers!(Riscv64ABICaller);
|
||||
}
|
||||
|
||||
impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6> {
|
||||
isle_prelude_methods!();
|
||||
isle_prelude_caller_methods!(Riscv64MachineDeps, Riscv64ABICaller);
|
||||
|
||||
fn vec_writable_to_regs(&mut self, val: &VecWritableReg) -> ValueRegs {
|
||||
match val.len() {
|
||||
1 => ValueRegs::one(val[0].to_reg()),
|
||||
2 => ValueRegs::two(val[0].to_reg(), val[1].to_reg()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn valid_bextend_ty(&mut self, from: Type, to: Type) -> Option<Type> {
|
||||
if from.is_bool() && to.is_bool() && from.bits() < to.bits() {
|
||||
Some(to)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn lower_br_fcmp(
|
||||
&mut self,
|
||||
cc: &FloatCC,
|
||||
a: Reg,
|
||||
b: Reg,
|
||||
targets: &VecMachLabel,
|
||||
ty: Type,
|
||||
) -> InstOutput {
|
||||
let tmp = self.temp_writable_reg(I64);
|
||||
MInst::lower_br_fcmp(
|
||||
*cc,
|
||||
a,
|
||||
b,
|
||||
BranchTarget::Label(targets[0]),
|
||||
BranchTarget::Label(targets[1]),
|
||||
ty,
|
||||
tmp,
|
||||
)
|
||||
.iter()
|
||||
.for_each(|i| self.emit(i));
|
||||
InstOutput::default()
|
||||
}
|
||||
|
||||
fn lower_brz_or_nz(
|
||||
&mut self,
|
||||
cc: &IntCC,
|
||||
a: ValueRegs,
|
||||
targets: &VecMachLabel,
|
||||
ty: Type,
|
||||
) -> InstOutput {
|
||||
MInst::lower_br_icmp(
|
||||
*cc,
|
||||
a,
|
||||
self.int_zero_reg(ty),
|
||||
BranchTarget::Label(targets[0]),
|
||||
BranchTarget::Label(targets[1]),
|
||||
ty,
|
||||
)
|
||||
.iter()
|
||||
.for_each(|i| self.emit(i));
|
||||
InstOutput::default()
|
||||
}
|
||||
fn lower_br_icmp(
|
||||
&mut self,
|
||||
cc: &IntCC,
|
||||
a: ValueRegs,
|
||||
b: ValueRegs,
|
||||
targets: &VecMachLabel,
|
||||
ty: Type,
|
||||
) -> InstOutput {
|
||||
let test = generated_code::constructor_lower_icmp(self, cc, a, b, ty).unwrap();
|
||||
self.emit(&MInst::CondBr {
|
||||
taken: BranchTarget::Label(targets[0]),
|
||||
not_taken: BranchTarget::Label(targets[1]),
|
||||
kind: IntegerCompare {
|
||||
kind: IntCC::NotEqual,
|
||||
rs1: test,
|
||||
rs2: zero_reg(),
|
||||
},
|
||||
});
|
||||
InstOutput::default()
|
||||
}
|
||||
fn load_ra(&mut self) -> Reg {
|
||||
if self.flags.preserve_frame_pointers() {
|
||||
let tmp = self.temp_writable_reg(I64);
|
||||
self.emit(&MInst::Load {
|
||||
rd: tmp,
|
||||
op: LoadOP::Ld,
|
||||
flags: MemFlags::trusted(),
|
||||
from: AMode::FPOffset(8, I64),
|
||||
});
|
||||
tmp.to_reg()
|
||||
} else {
|
||||
self.gen_move2(link_reg(), I64, I64)
|
||||
}
|
||||
}
|
||||
fn int_zero_reg(&mut self, ty: Type) -> ValueRegs {
|
||||
assert!(ty.is_int() || ty.is_bool(), "{:?}", ty);
|
||||
if ty.bits() == 128 {
|
||||
ValueRegs::two(self.zero_reg(), self.zero_reg())
|
||||
} else {
|
||||
ValueRegs::one(self.zero_reg())
|
||||
}
|
||||
}
|
||||
|
||||
fn vec_label_get(&mut self, val: &VecMachLabel, x: u8) -> MachLabel {
|
||||
val[x as usize]
|
||||
}
|
||||
|
||||
fn label_to_br_target(&mut self, label: MachLabel) -> BranchTarget {
|
||||
BranchTarget::Label(label)
|
||||
}
|
||||
|
||||
fn vec_writable_clone(&mut self, v: &VecWritableReg) -> VecWritableReg {
|
||||
v.clone()
|
||||
}
|
||||
|
||||
fn gen_moves(&mut self, rs: ValueRegs, in_ty: Type, out_ty: Type) -> ValueRegs {
|
||||
let tmp = construct_dest(|ty| self.temp_writable_reg(ty), out_ty);
|
||||
if in_ty.bits() < 64 {
|
||||
self.emit(&gen_move(tmp.regs()[0], out_ty, rs.regs()[0], in_ty));
|
||||
} else {
|
||||
gen_moves(tmp.regs(), rs.regs())
|
||||
.iter()
|
||||
.for_each(|i| self.emit(i));
|
||||
}
|
||||
tmp.map(|r| r.to_reg())
|
||||
}
|
||||
fn imm12_and(&mut self, imm: Imm12, x: i32) -> Imm12 {
|
||||
Imm12::from_bits(imm.as_i16() & (x as i16))
|
||||
}
|
||||
fn alloc_vec_writable(&mut self, ty: Type) -> VecWritableReg {
|
||||
if ty.is_int() || ty.is_bool() || ty == R32 || ty == R64 {
|
||||
if ty.bits() <= 64 {
|
||||
vec![self.temp_writable_reg(I64)]
|
||||
} else {
|
||||
vec![self.temp_writable_reg(I64), self.temp_writable_reg(I64)]
|
||||
}
|
||||
} else if ty.is_float() {
|
||||
vec![self.temp_writable_reg(ty)]
|
||||
} else {
|
||||
unimplemented!("ty:{:?}", ty)
|
||||
}
|
||||
}
|
||||
|
||||
fn imm(&mut self, ty: Type, mut val: u64) -> Reg {
|
||||
// Boolean types
|
||||
// Boolean values are either true or false.
|
||||
|
||||
// The b1 type represents an abstract boolean value. It can only exist as an SSA value, and can't be directly stored in memory. It can, however, be converted into an integer with value 0 or 1 by the bint instruction (and converted back with icmp_imm with 0).
|
||||
|
||||
// Several larger boolean types are also defined, primarily to be used as SIMD element types. They can be stored in memory, and are represented as either all zero bits or all one bits.
|
||||
|
||||
// b1
|
||||
// b8
|
||||
// b16
|
||||
// b32
|
||||
// b64
|
||||
// ///////////////////////////////////////////////////////////
|
||||
// "represented as either all zero bits or all one bits."
|
||||
// \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||
if ty.is_bool() && val != 0 {
|
||||
// need all be one
|
||||
val = !0;
|
||||
}
|
||||
let tmp = self.temp_writable_reg(ty);
|
||||
self.emit_list(&MInst::load_constant_u64(tmp, val));
|
||||
tmp.to_reg()
|
||||
}
|
||||
#[inline]
|
||||
fn emit(&mut self, arg0: &MInst) -> Unit {
|
||||
self.lower_ctx.emit(arg0.clone());
|
||||
}
|
||||
#[inline]
|
||||
fn imm12_from_u64(&mut self, arg0: u64) -> Option<Imm12> {
|
||||
Imm12::maybe_from_u64(arg0)
|
||||
}
|
||||
#[inline]
|
||||
fn writable_zero_reg(&mut self) -> WritableReg {
|
||||
writable_zero_reg()
|
||||
}
|
||||
#[inline]
|
||||
fn neg_imm12(&mut self, arg0: Imm12) -> Imm12 {
|
||||
-arg0
|
||||
}
|
||||
#[inline]
|
||||
fn zero_reg(&mut self) -> Reg {
|
||||
zero_reg()
|
||||
}
|
||||
#[inline]
|
||||
fn imm_from_bits(&mut self, val: u64) -> Imm12 {
|
||||
Imm12::maybe_from_u64(val).unwrap()
|
||||
}
|
||||
#[inline]
|
||||
fn imm_from_neg_bits(&mut self, val: i64) -> Imm12 {
|
||||
Imm12::maybe_from_u64(val as u64).unwrap()
|
||||
}
|
||||
|
||||
fn gen_default_frm(&mut self) -> OptionFloatRoundingMode {
|
||||
None
|
||||
}
|
||||
fn gen_select_reg(&mut self, cc: &IntCC, a: Reg, b: Reg, rs1: Reg, rs2: Reg) -> Reg {
|
||||
let rd = self.temp_writable_reg(MInst::canonical_type_for_rc(rs1.class()));
|
||||
self.emit(&MInst::SelectReg {
|
||||
rd,
|
||||
rs1,
|
||||
rs2,
|
||||
condition: IntegerCompare {
|
||||
kind: *cc,
|
||||
rs1: a,
|
||||
rs2: b,
|
||||
},
|
||||
});
|
||||
rd.to_reg()
|
||||
}
|
||||
fn load_u64_constant(&mut self, val: u64) -> Reg {
|
||||
let rd = self.temp_writable_reg(I64);
|
||||
MInst::load_constant_u64(rd, val)
|
||||
.iter()
|
||||
.for_each(|i| self.emit(i));
|
||||
rd.to_reg()
|
||||
}
|
||||
fn u8_as_i32(&mut self, x: u8) -> i32 {
|
||||
x as i32
|
||||
}
|
||||
|
||||
fn ext_sign_bit(&mut self, ty: Type, r: Reg) -> Reg {
|
||||
assert!(ty.is_int());
|
||||
let rd = self.temp_writable_reg(I64);
|
||||
self.emit(&MInst::AluRRImm12 {
|
||||
alu_op: AluOPRRI::Bexti,
|
||||
rd,
|
||||
rs: r,
|
||||
imm12: Imm12::from_bits((ty.bits() - 1) as i16),
|
||||
});
|
||||
rd.to_reg()
|
||||
}
|
||||
fn imm12_const(&mut self, val: i32) -> Imm12 {
|
||||
Imm12::maybe_from_u64(val as u64).unwrap()
|
||||
}
|
||||
fn imm12_const_add(&mut self, val: i32, add: i32) -> Imm12 {
|
||||
Imm12::maybe_from_u64((val + add) as u64).unwrap()
|
||||
}
|
||||
|
||||
//
|
||||
fn gen_shamt(&mut self, ty: Type, shamt: Reg) -> ValueRegs {
|
||||
let shamt = {
|
||||
let tmp = self.temp_writable_reg(I64);
|
||||
self.emit(&MInst::AluRRImm12 {
|
||||
alu_op: AluOPRRI::Andi,
|
||||
rd: tmp,
|
||||
rs: shamt,
|
||||
imm12: Imm12::from_bits((ty.bits() - 1) as i16),
|
||||
});
|
||||
tmp.to_reg()
|
||||
};
|
||||
let len_sub_shamt = {
|
||||
let len_sub_shamt = self.temp_writable_reg(I64);
|
||||
self.emit(&MInst::load_imm12(
|
||||
len_sub_shamt,
|
||||
Imm12::from_bits(ty.bits() as i16),
|
||||
));
|
||||
self.emit(&MInst::AluRRR {
|
||||
alu_op: AluOPRRR::Sub,
|
||||
rd: len_sub_shamt,
|
||||
rs1: len_sub_shamt.to_reg(),
|
||||
rs2: shamt,
|
||||
});
|
||||
len_sub_shamt.to_reg()
|
||||
};
|
||||
ValueRegs::two(shamt, len_sub_shamt)
|
||||
}
|
||||
|
||||
fn has_b(&mut self) -> Option<bool> {
|
||||
Some(self.isa_flags.has_b())
|
||||
}
|
||||
fn has_zbkb(&mut self) -> Option<bool> {
|
||||
Some(self.isa_flags.has_zbkb())
|
||||
}
|
||||
|
||||
fn valueregs_2_reg(&mut self, val: Value) -> Reg {
|
||||
self.put_in_regs(val).regs()[0]
|
||||
}
|
||||
|
||||
fn inst_output_get(&mut self, x: InstOutput, index: u8) -> ValueRegs {
|
||||
x[index as usize]
|
||||
}
|
||||
|
||||
fn move_f_to_x(&mut self, r: Reg, ty: Type) -> Reg {
|
||||
let result = self.temp_writable_reg(I64);
|
||||
self.emit(&gen_move(result, I64, r, ty));
|
||||
result.to_reg()
|
||||
}
|
||||
fn offset32_imm(&mut self, offset: i32) -> Offset32 {
|
||||
Offset32::new(offset)
|
||||
}
|
||||
fn default_memflags(&mut self) -> MemFlags {
|
||||
MemFlags::new()
|
||||
}
|
||||
fn move_x_to_f(&mut self, r: Reg, ty: Type) -> Reg {
|
||||
let result = self.temp_writable_reg(ty);
|
||||
self.emit(&gen_move(result, ty, r, I64));
|
||||
result.to_reg()
|
||||
}
|
||||
|
||||
fn pack_float_rounding_mode(&mut self, f: &FRM) -> OptionFloatRoundingMode {
|
||||
Some(*f)
|
||||
}
|
||||
|
||||
fn int_convert_2_float_op(&mut self, from: Type, is_signed: bool, to: Type) -> FpuOPRR {
|
||||
FpuOPRR::int_convert_2_float_op(from, is_signed, to)
|
||||
}
|
||||
fn gen_amode(&mut self, base: Reg, offset: Offset32, ty: Type) -> AMode {
|
||||
AMode::RegOffset(base, i64::from(offset), ty)
|
||||
}
|
||||
fn valid_atomic_transaction(&mut self, ty: Type) -> Option<Type> {
|
||||
if ty.is_int() && ty.bits() <= 64 {
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn is_atomic_rmw_max_etc(&mut self, op: &AtomicRmwOp) -> Option<(AtomicRmwOp, bool)> {
|
||||
let op = *op;
|
||||
match op {
|
||||
crate::ir::AtomicRmwOp::Umin => Some((op, false)),
|
||||
crate::ir::AtomicRmwOp::Umax => Some((op, false)),
|
||||
crate::ir::AtomicRmwOp::Smin => Some((op, true)),
|
||||
crate::ir::AtomicRmwOp::Smax => Some((op, true)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn load_op(&mut self, ty: Type) -> LoadOP {
|
||||
LoadOP::from_type(ty)
|
||||
}
|
||||
fn store_op(&mut self, ty: Type) -> StoreOP {
|
||||
StoreOP::from_type(ty)
|
||||
}
|
||||
fn load_ext_name(&mut self, name: ExternalName, offset: i64) -> Reg {
|
||||
let tmp = self.temp_writable_reg(I64);
|
||||
self.emit(&MInst::LoadExtName {
|
||||
rd: tmp,
|
||||
name: Box::new(name),
|
||||
offset,
|
||||
});
|
||||
tmp.to_reg()
|
||||
}
|
||||
|
||||
fn offset32_add(&mut self, a: Offset32, adden: i64) -> Offset32 {
|
||||
a.try_add_i64(adden).expect("offset exceed range.")
|
||||
}
|
||||
|
||||
fn gen_stack_addr(&mut self, slot: StackSlot, offset: Offset32) -> Reg {
|
||||
let result = self.temp_writable_reg(I64);
|
||||
let i = self
|
||||
.lower_ctx
|
||||
.abi()
|
||||
.sized_stackslot_addr(slot, i64::from(offset) as u32, result);
|
||||
self.emit(&i);
|
||||
result.to_reg()
|
||||
}
|
||||
fn atomic_amo(&mut self) -> AMO {
|
||||
AMO::SeqCst
|
||||
}
|
||||
|
||||
fn gen_move2(&mut self, r: Reg, ity: Type, oty: Type) -> Reg {
|
||||
let tmp = self.temp_writable_reg(oty);
|
||||
self.emit(&gen_move(tmp, oty, r, ity));
|
||||
tmp.to_reg()
|
||||
}
|
||||
|
||||
fn intcc_is_gt_etc(&mut self, cc: &IntCC) -> Option<(IntCC, bool)> {
|
||||
let cc = *cc;
|
||||
match cc {
|
||||
IntCC::SignedLessThan => Some((cc, true)),
|
||||
IntCC::SignedGreaterThanOrEqual => Some((cc, true)),
|
||||
IntCC::SignedGreaterThan => Some((cc, true)),
|
||||
IntCC::SignedLessThanOrEqual => Some((cc, true)),
|
||||
//
|
||||
IntCC::UnsignedLessThan => Some((cc, false)),
|
||||
IntCC::UnsignedGreaterThanOrEqual => Some((cc, false)),
|
||||
IntCC::UnsignedGreaterThan => Some((cc, false)),
|
||||
IntCC::UnsignedLessThanOrEqual => Some((cc, false)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn intcc_is_eq_or_ne(&mut self, cc: &IntCC) -> Option<IntCC> {
|
||||
let cc = *cc;
|
||||
if cc == IntCC::Equal || cc == IntCC::NotEqual {
|
||||
Some(cc)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn lower_br_table(&mut self, index: Reg, targets: &VecMachLabel) -> InstOutput {
|
||||
let tmp = self.temp_writable_reg(I64);
|
||||
let default_ = BranchTarget::Label(targets[0]);
|
||||
let targets: Vec<BranchTarget> = targets
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(|bix| BranchTarget::Label(*bix))
|
||||
.collect();
|
||||
self.emit(&MInst::BrTableCheck {
|
||||
index,
|
||||
targets_len: targets.len() as i32,
|
||||
default_: default_,
|
||||
});
|
||||
self.emit(&MInst::BrTable {
|
||||
index,
|
||||
tmp1: tmp,
|
||||
targets,
|
||||
});
|
||||
InstOutput::default()
|
||||
}
|
||||
fn x_reg(&mut self, x: u8) -> Reg {
|
||||
x_reg(x as usize)
|
||||
}
|
||||
fn shift_int_to_most_significant(&mut self, v: Reg, ty: Type) -> Reg {
|
||||
assert!(ty.is_int() && ty.bits() <= 64);
|
||||
if ty == I64 {
|
||||
return v;
|
||||
}
|
||||
let tmp = self.temp_writable_reg(I64);
|
||||
self.emit(&MInst::AluRRImm12 {
|
||||
alu_op: AluOPRRI::Slli,
|
||||
rd: tmp,
|
||||
rs: v,
|
||||
imm12: Imm12::from_bits((64 - ty.bits()) as i16),
|
||||
});
|
||||
|
||||
tmp.to_reg()
|
||||
}
|
||||
}
|
||||
|
||||
impl IsleContext<'_, '_, MInst, Flags, IsaFlags, 6> {
|
||||
#[inline]
|
||||
fn emit_list(&mut self, list: &SmallInstVec<MInst>) {
|
||||
for i in list {
|
||||
self.lower_ctx.emit(i.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The main entry point for branch lowering with ISLE.
|
||||
pub(crate) fn lower_branch(
|
||||
lower_ctx: &mut Lower<MInst>,
|
||||
triple: &Triple,
|
||||
flags: &Flags,
|
||||
isa_flags: &IsaFlags,
|
||||
branch: Inst,
|
||||
targets: &[MachLabel],
|
||||
) -> Result<(), ()> {
|
||||
lower_common(
|
||||
lower_ctx,
|
||||
triple,
|
||||
flags,
|
||||
isa_flags,
|
||||
&[],
|
||||
branch,
|
||||
|cx, insn| generated_code::constructor_lower_branch(cx, insn, &targets.to_vec()),
|
||||
)
|
||||
}
|
||||
|
||||
/// construct destination according to ty.
|
||||
fn construct_dest<F: std::ops::FnMut(Type) -> WritableReg>(
|
||||
mut alloc: F,
|
||||
ty: Type,
|
||||
) -> WritableValueRegs {
|
||||
if ty.is_bool() || ty.is_int() {
|
||||
if ty.bits() == 128 {
|
||||
WritableValueRegs::two(alloc(I64), alloc(I64))
|
||||
} else {
|
||||
WritableValueRegs::one(alloc(I64))
|
||||
}
|
||||
} else if ty.is_float() {
|
||||
WritableValueRegs::one(alloc(F64))
|
||||
} else {
|
||||
unimplemented!("vector type not implemented.");
|
||||
}
|
||||
}
|
||||
9
cranelift/codegen/src/isa/riscv64/lower/isle/generated_code.rs
generated
Normal file
9
cranelift/codegen/src/isa/riscv64/lower/isle/generated_code.rs
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
// See https://github.com/rust-lang/rust/issues/47995: we cannot use `#![...]` attributes inside of
|
||||
// the generated ISLE source below because we include!() it. We must include!() it because its path
|
||||
// depends on an environment variable; and also because of this, we can't do the `#[path = "..."]
|
||||
// mod generated_code;` trick either.
|
||||
#![allow(dead_code, unreachable_code, unreachable_patterns)]
|
||||
#![allow(unused_imports, unused_variables, non_snake_case, unused_mut)]
|
||||
#![allow(irrefutable_let_patterns)]
|
||||
|
||||
include!(concat!(env!("ISLE_DIR"), "/isle_riscv64.rs"));
|
||||
36
cranelift/codegen/src/isa/riscv64/lower_inst.rs
Normal file
36
cranelift/codegen/src/isa/riscv64/lower_inst.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
//! Lower a single Cranelift instruction into vcode.
|
||||
|
||||
use crate::ir::Inst as IRInst;
|
||||
|
||||
use crate::isa::riscv64::settings as riscv64_settings;
|
||||
use crate::machinst::lower::*;
|
||||
use crate::machinst::*;
|
||||
use crate::settings::Flags;
|
||||
use crate::CodegenResult;
|
||||
|
||||
use crate::isa::riscv64::inst::*;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
/// Actually codegen an instruction's results into registers.
|
||||
pub(crate) fn lower_insn_to_regs(
|
||||
ctx: &mut Lower<Inst>,
|
||||
insn: IRInst,
|
||||
triple: &Triple,
|
||||
flags: &Flags,
|
||||
isa_flags: &riscv64_settings::Flags,
|
||||
) -> CodegenResult<()> {
|
||||
let outputs = insn_outputs(ctx, insn);
|
||||
let ty = if outputs.len() > 0 {
|
||||
Some(ctx.output_ty(insn, 0))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Ok(()) = super::lower::isle::lower(ctx, flags, triple, isa_flags, &outputs, insn) {
|
||||
return Ok(());
|
||||
}
|
||||
unreachable!(
|
||||
"not implemented in ISLE: inst = `{}`, type = `{:?}`",
|
||||
ctx.dfg().display_inst(insn),
|
||||
ty
|
||||
);
|
||||
}
|
||||
246
cranelift/codegen/src/isa/riscv64/mod.rs
Normal file
246
cranelift/codegen/src/isa/riscv64/mod.rs
Normal file
@@ -0,0 +1,246 @@
|
||||
//! risc-v 64-bit Instruction Set Architecture.
|
||||
|
||||
use crate::ir;
|
||||
use crate::ir::condcodes::IntCC;
|
||||
use crate::ir::Function;
|
||||
|
||||
use crate::isa::riscv64::settings as riscv_settings;
|
||||
use crate::isa::{Builder as IsaBuilder, TargetIsa};
|
||||
use crate::machinst::{
|
||||
compile, CompiledCode, CompiledCodeStencil, MachTextSectionBuilder, Reg, SigSet,
|
||||
TextSectionBuilder, VCode,
|
||||
};
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings as shared_settings;
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::fmt;
|
||||
use regalloc2::MachineEnv;
|
||||
use target_lexicon::{Architecture, Triple};
|
||||
mod abi;
|
||||
pub(crate) mod inst;
|
||||
mod lower;
|
||||
mod lower_inst;
|
||||
mod settings;
|
||||
#[cfg(feature = "unwind")]
|
||||
use crate::isa::unwind::systemv;
|
||||
|
||||
use inst::crate_reg_eviroment;
|
||||
|
||||
use self::inst::EmitInfo;
|
||||
|
||||
/// An riscv64 backend.
|
||||
pub struct Riscv64Backend {
|
||||
triple: Triple,
|
||||
flags: shared_settings::Flags,
|
||||
isa_flags: riscv_settings::Flags,
|
||||
mach_env: MachineEnv,
|
||||
}
|
||||
|
||||
impl Riscv64Backend {
|
||||
/// Create a new riscv64 backend with the given (shared) flags.
|
||||
pub fn new_with_flags(
|
||||
triple: Triple,
|
||||
flags: shared_settings::Flags,
|
||||
isa_flags: riscv_settings::Flags,
|
||||
) -> Riscv64Backend {
|
||||
let mach_env = crate_reg_eviroment(&flags);
|
||||
Riscv64Backend {
|
||||
triple,
|
||||
flags,
|
||||
isa_flags,
|
||||
mach_env,
|
||||
}
|
||||
}
|
||||
|
||||
/// This performs lowering to VCode, register-allocates the code, computes block layout and
|
||||
/// finalizes branches. The result is ready for binary emission.
|
||||
fn compile_vcode(
|
||||
&self,
|
||||
func: &Function,
|
||||
flags: shared_settings::Flags,
|
||||
) -> CodegenResult<(VCode<inst::Inst>, regalloc2::Output)> {
|
||||
let emit_info = EmitInfo::new(flags.clone(), self.isa_flags.clone());
|
||||
let sigs = SigSet::new::<abi::Riscv64MachineDeps>(func, &self.flags)?;
|
||||
let abi = abi::Riscv64Callee::new(func, self, &self.isa_flags, &sigs)?;
|
||||
compile::compile::<Riscv64Backend>(func, self, abi, &self.mach_env, emit_info, sigs)
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetIsa for Riscv64Backend {
|
||||
fn compile_function(
|
||||
&self,
|
||||
func: &Function,
|
||||
want_disasm: bool,
|
||||
) -> CodegenResult<CompiledCodeStencil> {
|
||||
let flags = self.flags();
|
||||
let (vcode, regalloc_result) = self.compile_vcode(func, flags.clone())?;
|
||||
|
||||
let want_disasm = want_disasm || log::log_enabled!(log::Level::Debug);
|
||||
let emit_result = vcode.emit(®alloc_result, want_disasm, flags.machine_code_cfg_info());
|
||||
let frame_size = emit_result.frame_size;
|
||||
let value_labels_ranges = emit_result.value_labels_ranges;
|
||||
let buffer = emit_result.buffer.finish();
|
||||
let sized_stackslot_offsets = emit_result.sized_stackslot_offsets;
|
||||
let dynamic_stackslot_offsets = emit_result.dynamic_stackslot_offsets;
|
||||
|
||||
if let Some(disasm) = emit_result.disasm.as_ref() {
|
||||
log::debug!("disassembly:\n{}", disasm);
|
||||
}
|
||||
|
||||
Ok(CompiledCodeStencil {
|
||||
buffer,
|
||||
frame_size,
|
||||
disasm: emit_result.disasm,
|
||||
value_labels_ranges,
|
||||
sized_stackslot_offsets,
|
||||
dynamic_stackslot_offsets,
|
||||
bb_starts: emit_result.bb_offsets,
|
||||
bb_edges: emit_result.bb_edges,
|
||||
alignment: emit_result.alignment,
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"riscv64"
|
||||
}
|
||||
fn dynamic_vector_bytes(&self, _dynamic_ty: ir::Type) -> u32 {
|
||||
16
|
||||
}
|
||||
|
||||
fn triple(&self) -> &Triple {
|
||||
&self.triple
|
||||
}
|
||||
|
||||
fn flags(&self) -> &shared_settings::Flags {
|
||||
&self.flags
|
||||
}
|
||||
|
||||
fn isa_flags(&self) -> Vec<shared_settings::Value> {
|
||||
self.isa_flags.iter().collect()
|
||||
}
|
||||
|
||||
fn unsigned_add_overflow_condition(&self) -> IntCC {
|
||||
IntCC::UnsignedGreaterThanOrEqual
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn emit_unwind_info(
|
||||
&self,
|
||||
result: &CompiledCode,
|
||||
kind: crate::machinst::UnwindInfoKind,
|
||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||
use crate::isa::unwind::UnwindInfo;
|
||||
use crate::machinst::UnwindInfoKind;
|
||||
Ok(match kind {
|
||||
UnwindInfoKind::SystemV => {
|
||||
let mapper = self::inst::unwind::systemv::RegisterMapper;
|
||||
Some(UnwindInfo::SystemV(
|
||||
crate::isa::unwind::systemv::create_unwind_info_from_insts(
|
||||
&result.buffer.unwind_info[..],
|
||||
result.buffer.data().len(),
|
||||
&mapper,
|
||||
)?,
|
||||
))
|
||||
}
|
||||
UnwindInfoKind::Windows => None,
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
Some(inst::unwind::systemv::create_cie())
|
||||
}
|
||||
|
||||
fn text_section_builder(&self, num_funcs: u32) -> Box<dyn TextSectionBuilder> {
|
||||
Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs))
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn map_regalloc_reg_to_dwarf(&self, reg: Reg) -> Result<u16, systemv::RegisterMappingError> {
|
||||
inst::unwind::systemv::map_reg(reg).map(|reg| reg.0)
|
||||
}
|
||||
|
||||
fn function_alignment(&self) -> u32 {
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Riscv64Backend {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("MachBackend")
|
||||
.field("name", &self.name())
|
||||
.field("triple", &self.triple())
|
||||
.field("flags", &format!("{}", self.flags()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `isa::Builder`.
|
||||
pub fn isa_builder(triple: Triple) -> IsaBuilder {
|
||||
match triple.architecture {
|
||||
Architecture::Riscv64(..) => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
IsaBuilder {
|
||||
triple,
|
||||
setup: riscv_settings::builder(),
|
||||
constructor: |triple, shared_flags, builder| {
|
||||
let isa_flags = riscv_settings::Flags::new(&shared_flags, builder);
|
||||
let backend = Riscv64Backend::new_with_flags(triple, shared_flags, isa_flags);
|
||||
Ok(Box::new(backend))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::cursor::{Cursor, FuncCursor};
|
||||
use crate::ir::types::*;
|
||||
use crate::ir::{AbiParam, Function, InstBuilder, Signature, UserFuncName};
|
||||
use crate::isa::CallConv;
|
||||
use crate::settings;
|
||||
use crate::settings::Configurable;
|
||||
use core::str::FromStr;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
#[test]
|
||||
fn test_compile_function() {
|
||||
let name = UserFuncName::testcase("test0");
|
||||
let mut sig = Signature::new(CallConv::SystemV);
|
||||
sig.params.push(AbiParam::new(I32));
|
||||
sig.returns.push(AbiParam::new(I32));
|
||||
let mut func = Function::with_name_signature(name, sig);
|
||||
|
||||
let bb0 = func.dfg.make_block();
|
||||
let arg0 = func.dfg.append_block_param(bb0, I32);
|
||||
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(bb0);
|
||||
let v0 = pos.ins().iconst(I32, 0x1234);
|
||||
let v1 = pos.ins().iadd(arg0, v0);
|
||||
pos.ins().return_(&[v1]);
|
||||
|
||||
let mut shared_flags_builder = settings::builder();
|
||||
shared_flags_builder.set("opt_level", "none").unwrap();
|
||||
let shared_flags = settings::Flags::new(shared_flags_builder);
|
||||
let isa_flags = riscv_settings::Flags::new(&shared_flags, riscv_settings::builder());
|
||||
let backend = Riscv64Backend::new_with_flags(
|
||||
Triple::from_str("riscv64").unwrap(),
|
||||
shared_flags,
|
||||
isa_flags,
|
||||
);
|
||||
let buffer = backend.compile_function(&mut func, true).unwrap();
|
||||
let code = buffer.buffer.data();
|
||||
// 0: 000015b7 lui a1,0x1
|
||||
// 4: 23458593 addi a1,a1,564 # 0x1234
|
||||
// 8: 00b5053b addw a0,a0,a1
|
||||
// c: 00008067 ret
|
||||
let golden = vec![
|
||||
0xb7, 0x15, 0x0, 0x0, 0x93, 0x85, 0x45, 0x23, 0x3b, 0x5, 0xb5, 0x0, 0x67, 0x80, 0x0,
|
||||
0x0,
|
||||
];
|
||||
assert_eq!(code, &golden[..]);
|
||||
}
|
||||
}
|
||||
8
cranelift/codegen/src/isa/riscv64/settings.rs
Normal file
8
cranelift/codegen/src/isa/riscv64/settings.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
//! riscv64 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
|
||||
include!(concat!(env!("OUT_DIR"), "/settings-riscv64.rs"));
|
||||
Reference in New Issue
Block a user