use crate::ir::{ArgumentLoc, ExternalName, SigRef, Type};
use crate::isa::{CallConv, RegInfo, RegUnit};
use alloc::vec::Vec;
use core::fmt;
use core::str::FromStr;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
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 display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplaySignature<'a> {
DisplaySignature(self, regs.into())
}
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
}
pub fn param_types(&self) -> Vec<Type> {
self.params
.iter()
.filter(|ap| ap.purpose == ArgumentPurpose::Normal)
.map(|ap| ap.value_type)
.collect()
}
pub fn return_types(&self) -> Vec<Type> {
self.returns
.iter()
.filter(|ap| ap.purpose == ArgumentPurpose::Normal)
.map(|ap| ap.value_type)
.collect()
}
}
pub struct DisplaySignature<'a>(&'a Signature, Option<&'a RegInfo>);
fn write_list(f: &mut fmt::Formatter, args: &[AbiParam], regs: Option<&RegInfo>) -> fmt::Result {
match args.split_first() {
None => {}
Some((first, rest)) => {
write!(f, "{}", first.display(regs))?;
for arg in rest {
write!(f, ", {}", arg.display(regs))?;
}
}
}
Ok(())
}
impl<'a> fmt::Display for DisplaySignature<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(")?;
write_list(f, &self.0.params, self.1)?;
write!(f, ")")?;
if !self.0.returns.is_empty() {
write!(f, " -> ")?;
write_list(f, &self.0.returns, self.1)?;
}
write!(f, " {}", self.0.call_conv)
}
}
impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.display(None).fmt(f)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct AbiParam {
pub value_type: Type,
pub purpose: ArgumentPurpose,
pub extension: ArgumentExtension,
pub location: ArgumentLoc,
}
impl AbiParam {
pub fn new(vt: Type) -> Self {
Self {
value_type: vt,
extension: ArgumentExtension::None,
purpose: ArgumentPurpose::Normal,
location: Default::default(),
}
}
pub fn special(vt: Type, purpose: ArgumentPurpose) -> Self {
Self {
value_type: vt,
extension: ArgumentExtension::None,
purpose,
location: Default::default(),
}
}
pub fn special_reg(vt: Type, purpose: ArgumentPurpose, regunit: RegUnit) -> Self {
Self {
value_type: vt,
extension: ArgumentExtension::None,
purpose,
location: ArgumentLoc::Reg(regunit),
}
}
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
}
}
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplayAbiParam<'a> {
DisplayAbiParam(self, regs.into())
}
}
pub struct DisplayAbiParam<'a>(&'a AbiParam, Option<&'a RegInfo>);
impl<'a> fmt::Display for DisplayAbiParam<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.value_type)?;
match self.0.extension {
ArgumentExtension::None => {}
ArgumentExtension::Uext => write!(f, " uext")?,
ArgumentExtension::Sext => write!(f, " sext")?,
}
if self.0.purpose != ArgumentPurpose::Normal {
write!(f, " {}", self.0.purpose)?;
}
if self.0.location.is_assigned() {
write!(f, " [{}]", self.0.location.display(self.1))?;
}
Ok(())
}
}
impl fmt::Display for AbiParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.display(None).fmt(f)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ArgumentExtension {
None,
Uext,
Sext,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ArgumentPurpose {
Normal,
StructReturn,
Link,
FramePointer,
CalleeSaved,
VMContext,
SignatureId,
StackLimit,
}
static PURPOSE_NAMES: [&str; 8] = [
"normal",
"sret",
"link",
"fp",
"csr",
"vmctx",
"sigid",
"stack_limit",
];
impl fmt::Display for ArgumentPurpose {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(PURPOSE_NAMES[*self as usize])
}
}
impl FromStr for ArgumentPurpose {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
match s {
"normal" => Ok(Self::Normal),
"sret" => Ok(Self::StructReturn),
"link" => Ok(Self::Link),
"fp" => Ok(Self::FramePointer),
"csr" => Ok(Self::CalleeSaved),
"vmctx" => Ok(Self::VMContext),
"sigid" => Ok(Self::SignatureId),
"stack_limit" => Ok(Self::StackLimit),
_ => Err(()),
}
}
}
#[derive(Clone, Debug)]
pub struct ExtFuncData {
pub name: ExternalName,
pub signature: SigRef,
pub colocated: bool,
}
impl fmt::Display for ExtFuncData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.colocated {
write!(f, "colocated ")?;
}
write!(f, "{} {}", self.name, self.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,
ArgumentPurpose::StructReturn,
ArgumentPurpose::Link,
ArgumentPurpose::FramePointer,
ArgumentPurpose::CalleeSaved,
ArgumentPurpose::VMContext,
ArgumentPurpose::SignatureId,
ArgumentPurpose::StackLimit,
];
for (&e, &n) in all_purpose.iter().zip(PURPOSE_NAMES.iter()) {
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,
CallConv::BaldrdashSystemV,
CallConv::BaldrdashWindows,
] {
assert_eq!(Ok(cc), cc.to_string().parse())
}
}
#[test]
fn signatures() {
let mut sig = Signature::new(CallConv::BaldrdashSystemV);
assert_eq!(sig.to_string(), "() baldrdash_system_v");
sig.params.push(AbiParam::new(I32));
assert_eq!(sig.to_string(), "(i32) baldrdash_system_v");
sig.returns.push(AbiParam::new(F32));
assert_eq!(sig.to_string(), "(i32) -> f32 baldrdash_system_v");
sig.params.push(AbiParam::new(I32.by(4).unwrap()));
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 baldrdash_system_v");
sig.returns.push(AbiParam::new(B8));
assert_eq!(
sig.to_string(),
"(i32, i32x4) -> f32, b8 baldrdash_system_v"
);
sig.params[0].location = ArgumentLoc::Stack(24);
sig.params[1].location = ArgumentLoc::Stack(8);
assert_eq!(
sig.to_string(),
"(i32 [24], i32x4 [8]) -> f32, b8 baldrdash_system_v"
);
}
}