diff options
Diffstat (limited to 'third_party/rust/cranelift-codegen/src/ir/libcall.rs')
-rw-r--r-- | third_party/rust/cranelift-codegen/src/ir/libcall.rs | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-codegen/src/ir/libcall.rs b/third_party/rust/cranelift-codegen/src/ir/libcall.rs new file mode 100644 index 0000000000..9dc134e480 --- /dev/null +++ b/third_party/rust/cranelift-codegen/src/ir/libcall.rs @@ -0,0 +1,260 @@ +//! Naming well-known routines in the runtime library. + +use crate::ir::{ + types, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, Inst, Opcode, + Signature, Type, +}; +use crate::isa::{CallConv, RegUnit, TargetIsa}; +use core::fmt; +use core::str::FromStr; +#[cfg(feature = "enable-serde")] +use serde::{Deserialize, Serialize}; + +/// The name of a runtime library routine. +/// +/// Runtime library calls are generated for Cranelift IR instructions that don't have an equivalent +/// ISA instruction or an easy macro expansion. A `LibCall` is used as a well-known name to refer to +/// the runtime library routine. This way, Cranelift doesn't have to know about the naming +/// convention in the embedding VM's runtime library. +/// +/// This list is likely to grow over time. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub enum LibCall { + /// probe for stack overflow. These are emitted for functions which need + /// when the `enable_probestack` setting is true. + Probestack, + /// udiv.i64 + UdivI64, + /// sdiv.i64 + SdivI64, + /// urem.i64 + UremI64, + /// srem.i64 + SremI64, + /// ishl.i64 + IshlI64, + /// ushr.i64 + UshrI64, + /// sshr.i64 + SshrI64, + /// ceil.f32 + CeilF32, + /// ceil.f64 + CeilF64, + /// floor.f32 + FloorF32, + /// floor.f64 + FloorF64, + /// trunc.f32 + TruncF32, + /// frunc.f64 + TruncF64, + /// nearest.f32 + NearestF32, + /// nearest.f64 + NearestF64, + /// libc.memcpy + Memcpy, + /// libc.memset + Memset, + /// libc.memmove + Memmove, + + /// Elf __tls_get_addr + ElfTlsGetAddr, +} + +impl fmt::Display for LibCall { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl FromStr for LibCall { + type Err = (); + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "Probestack" => Ok(Self::Probestack), + "UdivI64" => Ok(Self::UdivI64), + "SdivI64" => Ok(Self::SdivI64), + "UremI64" => Ok(Self::UremI64), + "SremI64" => Ok(Self::SremI64), + "IshlI64" => Ok(Self::IshlI64), + "UshrI64" => Ok(Self::UshrI64), + "SshrI64" => Ok(Self::SshrI64), + "CeilF32" => Ok(Self::CeilF32), + "CeilF64" => Ok(Self::CeilF64), + "FloorF32" => Ok(Self::FloorF32), + "FloorF64" => Ok(Self::FloorF64), + "TruncF32" => Ok(Self::TruncF32), + "TruncF64" => Ok(Self::TruncF64), + "NearestF32" => Ok(Self::NearestF32), + "NearestF64" => Ok(Self::NearestF64), + "Memcpy" => Ok(Self::Memcpy), + "Memset" => Ok(Self::Memset), + "Memmove" => Ok(Self::Memmove), + + "ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr), + _ => Err(()), + } + } +} + +impl LibCall { + /// Get the well-known library call name to use as a replacement for an instruction with the + /// given opcode and controlling type variable. + /// + /// Returns `None` if no well-known library routine name exists for that instruction. + pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option<Self> { + Some(match ctrl_type { + types::I64 => match opcode { + Opcode::Udiv => Self::UdivI64, + Opcode::Sdiv => Self::SdivI64, + Opcode::Urem => Self::UremI64, + Opcode::Srem => Self::SremI64, + Opcode::Ishl => Self::IshlI64, + Opcode::Ushr => Self::UshrI64, + Opcode::Sshr => Self::SshrI64, + _ => return None, + }, + types::F32 => match opcode { + Opcode::Ceil => Self::CeilF32, + Opcode::Floor => Self::FloorF32, + Opcode::Trunc => Self::TruncF32, + Opcode::Nearest => Self::NearestF32, + _ => return None, + }, + types::F64 => match opcode { + Opcode::Ceil => Self::CeilF64, + Opcode::Floor => Self::FloorF64, + Opcode::Trunc => Self::TruncF64, + Opcode::Nearest => Self::NearestF64, + _ => return None, + }, + _ => return None, + }) + } +} + +/// Get a function reference for `libcall` in `func`, following the signature +/// for `inst`. +/// +/// If there is an existing reference, use it, otherwise make a new one. +pub(crate) fn get_libcall_funcref( + libcall: LibCall, + call_conv: CallConv, + func: &mut Function, + inst: Inst, + isa: &dyn TargetIsa, +) -> FuncRef { + find_funcref(libcall, func) + .unwrap_or_else(|| make_funcref_for_inst(libcall, call_conv, func, inst, isa)) +} + +/// Get a function reference for the probestack function in `func`. +/// +/// If there is an existing reference, use it, otherwise make a new one. +pub fn get_probestack_funcref( + func: &mut Function, + reg_type: Type, + arg_reg: RegUnit, + isa: &dyn TargetIsa, +) -> FuncRef { + find_funcref(LibCall::Probestack, func) + .unwrap_or_else(|| make_funcref_for_probestack(func, reg_type, arg_reg, isa)) +} + +/// Get the existing function reference for `libcall` in `func` if it exists. +fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> { + // We're assuming that all libcall function decls are at the end. + // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless. + for (fref, func_data) in func.dfg.ext_funcs.iter().rev() { + match func_data.name { + ExternalName::LibCall(lc) => { + if lc == libcall { + return Some(fref); + } + } + _ => break, + } + } + None +} + +/// Create a funcref for `LibCall::Probestack`. +fn make_funcref_for_probestack( + func: &mut Function, + reg_type: Type, + arg_reg: RegUnit, + isa: &dyn TargetIsa, +) -> FuncRef { + let mut sig = Signature::new(CallConv::Probestack); + let rax = AbiParam::special_reg(reg_type, ArgumentPurpose::Normal, arg_reg); + sig.params.push(rax); + if !isa.flags().probestack_func_adjusts_sp() { + sig.returns.push(rax); + } + make_funcref(LibCall::Probestack, func, sig, isa) +} + +/// Create a funcref for `libcall` with a signature matching `inst`. +fn make_funcref_for_inst( + libcall: LibCall, + call_conv: CallConv, + func: &mut Function, + inst: Inst, + isa: &dyn TargetIsa, +) -> FuncRef { + let mut sig = Signature::new(call_conv); + for &v in func.dfg.inst_args(inst) { + sig.params.push(AbiParam::new(func.dfg.value_type(v))); + } + for &v in func.dfg.inst_results(inst) { + sig.returns.push(AbiParam::new(func.dfg.value_type(v))); + } + + if call_conv.extends_baldrdash() { + // Adds the special VMContext parameter to the signature. + sig.params.push(AbiParam::special( + isa.pointer_type(), + ArgumentPurpose::VMContext, + )); + } + + make_funcref(libcall, func, sig, isa) +} + +/// Create a funcref for `libcall`. +fn make_funcref( + libcall: LibCall, + func: &mut Function, + sig: Signature, + isa: &dyn TargetIsa, +) -> FuncRef { + let sigref = func.import_signature(sig); + + func.import_function(ExtFuncData { + name: ExternalName::LibCall(libcall), + signature: sigref, + colocated: isa.flags().use_colocated_libcalls(), + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::string::ToString; + + #[test] + fn display() { + assert_eq!(LibCall::CeilF32.to_string(), "CeilF32"); + assert_eq!(LibCall::NearestF64.to_string(), "NearestF64"); + } + + #[test] + fn parsing() { + assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32)); + } +} |