use crate::ir::{ExternalName, SigRef, Type};
use crate::isa::CallConv;
use crate::machinst::RelocDistance;
use alloc::vec::Vec;
use core::fmt;
use core::str::FromStr;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
use super::function::FunctionParameters;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Signature {
pub params: Vec<AbiParam>,
pub returns: Vec<AbiParam>,
pub call_conv: CallConv,
}
impl Signature {
pub fn new(call_conv: CallConv) -> Self {
Self {
params: Vec::new(),
returns: Vec::new(),
call_conv,
}
}
pub fn clear(&mut self, call_conv: CallConv) {
self.params.clear();
self.returns.clear();
self.call_conv = call_conv;
}
pub fn special_param_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
self.params.iter().rposition(|arg| arg.purpose == purpose)
}
pub fn special_return_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
self.returns.iter().rposition(|arg| arg.purpose == purpose)
}
pub fn uses_special_param(&self, purpose: ArgumentPurpose) -> bool {
self.special_param_index(purpose).is_some()
}
pub fn uses_special_return(&self, purpose: ArgumentPurpose) -> bool {
self.special_return_index(purpose).is_some()
}
pub fn num_special_params(&self) -> usize {
self.params
.iter()
.filter(|p| p.purpose != ArgumentPurpose::Normal)
.count()
}
pub fn num_special_returns(&self) -> usize {
self.returns
.iter()
.filter(|r| r.purpose != ArgumentPurpose::Normal)
.count()
}
pub fn uses_struct_return_param(&self) -> bool {
self.uses_special_param(ArgumentPurpose::StructReturn)
}
pub fn is_multi_return(&self) -> bool {
self.returns
.iter()
.filter(|r| r.purpose == ArgumentPurpose::Normal)
.count()
> 1
}
}
fn write_list(f: &mut fmt::Formatter, args: &[AbiParam]) -> fmt::Result {
match args.split_first() {
None => {}
Some((first, rest)) => {
write!(f, "{}", first)?;
for arg in rest {
write!(f, ", {}", arg)?;
}
}
}
Ok(())
}
impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(")?;
write_list(f, &self.params)?;
write!(f, ")")?;
if !self.returns.is_empty() {
write!(f, " -> ")?;
write_list(f, &self.returns)?;
}
write!(f, " {}", self.call_conv)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct AbiParam {
pub value_type: Type,
pub purpose: ArgumentPurpose,
pub extension: ArgumentExtension,
}
impl AbiParam {
pub fn new(vt: Type) -> Self {
Self {
value_type: vt,
extension: ArgumentExtension::None,
purpose: ArgumentPurpose::Normal,
}
}
pub fn special(vt: Type, purpose: ArgumentPurpose) -> Self {
Self {
value_type: vt,
extension: ArgumentExtension::None,
purpose,
}
}
pub fn uext(self) -> Self {
debug_assert!(self.value_type.is_int(), "uext on {} arg", self.value_type);
Self {
extension: ArgumentExtension::Uext,
..self
}
}
pub fn sext(self) -> Self {
debug_assert!(self.value_type.is_int(), "sext on {} arg", self.value_type);
Self {
extension: ArgumentExtension::Sext,
..self
}
}
}
impl fmt::Display for AbiParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value_type)?;
match self.extension {
ArgumentExtension::None => {}
ArgumentExtension::Uext => write!(f, " uext")?,
ArgumentExtension::Sext => write!(f, " sext")?,
}
if self.purpose != ArgumentPurpose::Normal {
write!(f, " {}", self.purpose)?;
}
Ok(())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum ArgumentExtension {
None,
Uext,
Sext,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum ArgumentPurpose {
Normal,
StructArgument(u32),
StructReturn,
VMContext,
SignatureId,
StackLimit,
}
impl fmt::Display for ArgumentPurpose {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
Self::Normal => "normal",
Self::StructArgument(size) => return write!(f, "sarg({})", size),
Self::StructReturn => "sret",
Self::VMContext => "vmctx",
Self::SignatureId => "sigid",
Self::StackLimit => "stack_limit",
})
}
}
impl FromStr for ArgumentPurpose {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
match s {
"normal" => Ok(Self::Normal),
"sret" => Ok(Self::StructReturn),
"vmctx" => Ok(Self::VMContext),
"sigid" => Ok(Self::SignatureId),
"stack_limit" => Ok(Self::StackLimit),
_ if s.starts_with("sarg(") => {
if !s.ends_with(")") {
return Err(());
}
let size: u32 = s["sarg(".len()..s.len() - 1].parse().map_err(|_| ())?;
Ok(Self::StructArgument(size))
}
_ => Err(()),
}
}
}
#[derive(Clone, Debug, PartialEq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ExtFuncData {
pub name: ExternalName,
pub signature: SigRef,
pub colocated: bool,
}
impl ExtFuncData {
pub fn reloc_distance(&self) -> RelocDistance {
if self.colocated {
RelocDistance::Near
} else {
RelocDistance::Far
}
}
pub fn display<'a>(
&'a self,
params: Option<&'a FunctionParameters>,
) -> DisplayableExtFuncData<'a> {
DisplayableExtFuncData {
ext_func: self,
params,
}
}
}
pub struct DisplayableExtFuncData<'a> {
ext_func: &'a ExtFuncData,
params: Option<&'a FunctionParameters>,
}
impl<'a> fmt::Display for DisplayableExtFuncData<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.ext_func.colocated {
write!(f, "colocated ")?;
}
write!(
f,
"{} {}",
self.ext_func.name.display(self.params),
self.ext_func.signature
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::types::{B8, F32, I32};
use alloc::string::ToString;
#[test]
fn argument_type() {
let t = AbiParam::new(I32);
assert_eq!(t.to_string(), "i32");
let mut t = t.uext();
assert_eq!(t.to_string(), "i32 uext");
assert_eq!(t.sext().to_string(), "i32 sext");
t.purpose = ArgumentPurpose::StructReturn;
assert_eq!(t.to_string(), "i32 uext sret");
}
#[test]
fn argument_purpose() {
let all_purpose = [
(ArgumentPurpose::Normal, "normal"),
(ArgumentPurpose::StructReturn, "sret"),
(ArgumentPurpose::VMContext, "vmctx"),
(ArgumentPurpose::SignatureId, "sigid"),
(ArgumentPurpose::StackLimit, "stack_limit"),
(ArgumentPurpose::StructArgument(42), "sarg(42)"),
];
for &(e, n) in &all_purpose {
assert_eq!(e.to_string(), n);
assert_eq!(Ok(e), n.parse());
}
}
#[test]
fn call_conv() {
for &cc in &[
CallConv::Fast,
CallConv::Cold,
CallConv::SystemV,
CallConv::WindowsFastcall,
] {
assert_eq!(Ok(cc), cc.to_string().parse())
}
}
#[test]
fn signatures() {
let mut sig = Signature::new(CallConv::WindowsFastcall);
assert_eq!(sig.to_string(), "() windows_fastcall");
sig.params.push(AbiParam::new(I32));
assert_eq!(sig.to_string(), "(i32) windows_fastcall");
sig.returns.push(AbiParam::new(F32));
assert_eq!(sig.to_string(), "(i32) -> f32 windows_fastcall");
sig.params.push(AbiParam::new(I32.by(4).unwrap()));
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 windows_fastcall");
sig.returns.push(AbiParam::new(B8));
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8 windows_fastcall");
}
}