use std::fmt;
use std::sync::Arc;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Copy)]
pub enum Cmp {
Slt,
Sle,
Sgt,
Sge,
Eq,
Ne,
O,
Uo,
Ult,
Ule,
Ugt,
Uge,
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Instr {
Add(Value, Value),
Sub(Value, Value),
Mul(Value, Value),
Div(Value, Value),
Rem(Value, Value),
Cmp(Type, Cmp, Value, Value),
And(Value, Value),
Or(Value, Value),
Xor(Value, Value),
Neg(Value),
Copy(Value),
Ret(Option<Value>),
Jnz(Value, String, String),
Jmp(String),
Call(String, Vec<(Type, Value)>, Option<u64>),
Alloc4(u32),
Alloc8(u64),
Alloc16(u128),
Store(Type, Value, Value),
Load(Type, Value),
Blit(Value, Value, u64),
DbgFile(String),
DbgLoc(u64, Option<u64>),
Udiv(Value, Value),
Urem(Value, Value),
Sar(Value, Value),
Shr(Value, Value),
Shl(Value, Value),
Cast(Value),
Extsw(Value),
Extuw(Value),
Extsh(Value),
Extuh(Value),
Extsb(Value),
Extub(Value),
Exts(Value),
Truncd(Value),
Stosi(Value),
Stoui(Value),
Dtosi(Value),
Dtoui(Value),
Swtof(Value),
Uwtof(Value),
Sltof(Value),
Ultof(Value),
Vastart(Value),
Vaarg(Type, Value),
Phi(Vec<(String, Value)>),
Hlt,
}
impl fmt::Display for Instr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Add(lhs, rhs) => write!(f, "add {lhs}, {rhs}"),
Self::Sub(lhs, rhs) => write!(f, "sub {lhs}, {rhs}"),
Self::Mul(lhs, rhs) => write!(f, "mul {lhs}, {rhs}"),
Self::Div(lhs, rhs) => write!(f, "div {lhs}, {rhs}"),
Self::Rem(lhs, rhs) => write!(f, "rem {lhs}, {rhs}"),
Self::Cmp(ty, cmp, lhs, rhs) => {
assert!(
!matches!(ty, Type::Aggregate(_)),
"cannot compare aggregate types"
);
write!(
f,
"c{}{} {}, {}",
match cmp {
Cmp::Slt => "slt",
Cmp::Sle => "sle",
Cmp::Sgt => "sgt",
Cmp::Sge => "sge",
Cmp::Eq => "eq",
Cmp::Ne => "ne",
Cmp::O => "o",
Cmp::Uo => "uo",
Cmp::Ult => "ult",
Cmp::Ule => "ule",
Cmp::Ugt => "ugt",
Cmp::Uge => "uge",
},
ty,
lhs,
rhs,
)
}
Self::And(lhs, rhs) => write!(f, "and {lhs}, {rhs}"),
Self::Or(lhs, rhs) => write!(f, "or {lhs}, {rhs}"),
Self::Xor(lhs, rhs) => write!(f, "xor {lhs}, {rhs}"),
Self::Neg(val) => write!(f, "neg {val}"),
Self::Copy(val) => write!(f, "copy {val}"),
Self::Ret(val) => match val {
Some(val) => write!(f, "ret {val}"),
None => write!(f, "ret"),
},
Self::DbgFile(val) => write!(f, r#"dbgfile "{val}""#),
Self::DbgLoc(lineno, column) => match column {
Some(val) => write!(f, "dbgloc {lineno}, {val}"),
None => write!(f, "dbgloc {lineno}"),
},
Self::Jnz(val, if_nonzero, if_zero) => {
write!(f, "jnz {val}, @{if_nonzero}, @{if_zero}")
}
Self::Jmp(label) => write!(f, "jmp @{label}"),
Self::Call(name, args, opt_variadic_i) => {
let mut args_fmt = args
.iter()
.map(|(ty, temp)| format!("{ty} {temp}"))
.collect::<Vec<String>>();
if let Some(i) = *opt_variadic_i {
args_fmt.insert(i as usize, "...".to_string());
}
write!(f, "call ${}({})", name, args_fmt.join(", "),)
}
Self::Alloc4(size) => write!(f, "alloc4 {size}"),
Self::Alloc8(size) => write!(f, "alloc8 {size}"),
Self::Alloc16(size) => write!(f, "alloc16 {size}"),
Self::Store(ty, dest, value) => {
let suffix = match ty {
Type::SignedByte | Type::UnsignedByte => "b".to_string(),
Type::SignedHalfword | Type::UnsignedHalfword => "h".to_string(),
Type::Aggregate(_) => panic!("cannot store to an aggregate type"),
_ => ty.to_string(),
};
write!(f, "store{suffix} {value}, {dest}")
}
Self::Load(ty, src) => match ty {
Type::Byte | Type::Halfword => panic!(
"ambiguous sub-word load: use SignedByte/UnsignedByte or SignedHalfword/UnsignedHalfword"
),
Type::Aggregate(_) => panic!("cannot load aggregate type"),
_ => write!(f, "load{ty} {src}"),
}
Self::Blit(src, dst, n) => write!(f, "blit {src}, {dst}, {n}"),
Self::Udiv(lhs, rhs) => write!(f, "udiv {lhs}, {rhs}"),
Self::Urem(lhs, rhs) => write!(f, "urem {lhs}, {rhs}"),
Self::Sar(lhs, rhs) => write!(f, "sar {lhs}, {rhs}"),
Self::Shr(lhs, rhs) => write!(f, "shr {lhs}, {rhs}"),
Self::Shl(lhs, rhs) => write!(f, "shl {lhs}, {rhs}"),
Self::Cast(val) => write!(f, "cast {val}"),
Self::Extsw(val) => write!(f, "extsw {val}"),
Self::Extuw(val) => write!(f, "extuw {val}"),
Self::Extsh(val) => write!(f, "extsh {val}"),
Self::Extuh(val) => write!(f, "extuh {val}"),
Self::Extsb(val) => write!(f, "extsb {val}"),
Self::Extub(val) => write!(f, "extub {val}"),
Self::Exts(val) => write!(f, "exts {val}"),
Self::Truncd(val) => write!(f, "truncd {val}"),
Self::Stosi(val) => write!(f, "stosi {val}"),
Self::Stoui(val) => write!(f, "stoui {val}"),
Self::Dtosi(val) => write!(f, "dtosi {val}"),
Self::Dtoui(val) => write!(f, "dtoui {val}"),
Self::Swtof(val) => write!(f, "swtof {val}"),
Self::Uwtof(val) => write!(f, "uwtof {val}"),
Self::Sltof(val) => write!(f, "sltof {val}"),
Self::Ultof(val) => write!(f, "ultof {val}"),
Self::Vastart(val) => write!(f, "vastart {val}"),
Self::Vaarg(ty, val) => write!(f, "vaarg{ty} {val}"),
Self::Phi(args) => {
let formatted_args = args
.iter()
.map(|(label, value)| format!("@{label} {value}"))
.collect::<Vec<String>>()
.join(", ");
write!(f, "phi {formatted_args}")
}
Self::Hlt => write!(f, "hlt"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Type {
Word,
Long,
Single,
Double,
Zero,
Byte,
SignedByte,
UnsignedByte,
Halfword,
SignedHalfword,
UnsignedHalfword,
Aggregate(Arc<TypeDef>),
}
impl From<Arc<TypeDef>> for Type {
fn from(td: Arc<TypeDef>) -> Self {
Type::Aggregate(td)
}
}
impl From<TypeDef> for Type {
fn from(td: TypeDef) -> Self {
Type::Aggregate(Arc::new(td))
}
}
impl Type {
pub fn aggregate(td: &Arc<TypeDef>) -> Self {
Type::Aggregate(Arc::clone(td))
}
pub fn into_abi(self) -> Self {
match self {
Self::Byte
| Self::SignedByte
| Self::UnsignedByte
| Self::Halfword
| Self::SignedHalfword
| Self::UnsignedHalfword => Self::Word,
other => other,
}
}
pub fn into_base(self) -> Self {
match self {
Self::Byte
| Self::SignedByte
| Self::UnsignedByte
| Self::Halfword
| Self::SignedHalfword
| Self::UnsignedHalfword => Self::Word,
Self::Aggregate(_) => Self::Long,
other => other,
}
}
pub fn size(&self) -> u64 {
match self {
Self::Byte | Self::SignedByte | Self::UnsignedByte | Self::Zero => 1,
Self::Halfword | Self::SignedHalfword | Self::UnsignedHalfword => 2,
Self::Word | Self::Single => 4,
Self::Long | Self::Double => 8,
Self::Aggregate(td) => {
fn size_of_items(s: &Type, items: &[(Type, usize)]) -> u64 {
let mut offset = 0;
for (item, repeat) in items.iter() {
let align = item.align();
let size = *repeat as u64 * item.size();
let padding = (align - (offset % align)) % align;
offset += padding + size;
}
let align = s.align();
let padding = (align - (offset % align)) % align;
offset + padding
}
match td.as_ref() {
TypeDef::Regular { items, .. } => size_of_items(self, items),
TypeDef::Union { variations, .. } => variations
.iter()
.map(|items| size_of_items(self, items))
.max()
.unwrap_or(0),
TypeDef::Opaque { size, .. } => *size,
}
}
}
}
pub fn align(&self) -> u64 {
match self {
Self::Aggregate(td) => {
fn align_of_items(items: &[(Type, usize)]) -> u64 {
items.iter().map(|item| item.0.align()).max().unwrap_or(1)
}
match td.as_ref() {
TypeDef::Regular { align, items, .. } => {
if let Some(align) = align {
return *align;
}
align_of_items(items)
}
TypeDef::Union {
align,
variations: items,
..
} => {
if let Some(align) = align {
return *align;
}
items.iter().map(|v| align_of_items(v)).max().unwrap_or(1)
}
TypeDef::Opaque { align, .. } => *align,
}
}
_ => self.size(),
}
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Byte => write!(f, "b"),
Self::SignedByte => write!(f, "sb"),
Self::UnsignedByte => write!(f, "ub"),
Self::Halfword => write!(f, "h"),
Self::SignedHalfword => write!(f, "sh"),
Self::UnsignedHalfword => write!(f, "uh"),
Self::Word => write!(f, "w"),
Self::Long => write!(f, "l"),
Self::Single => write!(f, "s"),
Self::Double => write!(f, "d"),
Self::Zero => write!(f, "z"),
Self::Aggregate(td) => write!(f, ":{}", td.ident()),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Value {
Temporary(String),
Global(String),
Const(u64),
}
impl From<u64> for Value {
fn from(val: u64) -> Self {
Value::Const(val)
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Temporary(name) => write!(f, "%{name}"),
Self::Global(name) => write!(f, "${name}"),
Self::Const(value) => write!(f, "{value}"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct DataDef {
pub linkage: Linkage,
pub name: String,
pub align: Option<u64>,
pub items: Vec<(Type, DataItem)>,
}
impl DataDef {
pub fn new(
linkage: Linkage,
name: impl Into<String>,
align: Option<u64>,
items: Vec<(Type, DataItem)>,
) -> Self {
Self {
linkage,
name: name.into(),
align,
items,
}
}
}
impl fmt::Display for DataDef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}data ${} = ", self.linkage, self.name)?;
if let Some(align) = self.align {
write!(f, "align {align} ")?;
}
write!(
f,
"{{ {} }}",
self.items
.iter()
.map(|(ty, item)| format!("{ty} {item}"))
.collect::<Vec<String>>()
.join(", ")
)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum DataItem {
Symbol(String, Option<u64>),
Str(String),
Const(u64),
Zero(u64),
}
impl fmt::Display for DataItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Symbol(name, offset) => match offset {
Some(off) => write!(f, "${name} +{off}"),
None => write!(f, "${name}"),
},
Self::Str(string) => write!(f, "\"{string}\""),
Self::Const(val) => write!(f, "{val}"),
Self::Zero(size) => write!(f, "z {size}"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum TypeDef {
Regular {
ident: String,
align: Option<u64>,
items: Vec<(Type, usize)>,
},
Union {
ident: String,
align: Option<u64>,
variations: Vec<Vec<(Type, usize)>>,
},
Opaque {
ident: String,
align: u64,
size: u64,
},
}
impl TypeDef {
pub fn ident(&self) -> &str {
match self {
TypeDef::Regular { ident, .. } => ident,
TypeDef::Union { ident, .. } => ident,
TypeDef::Opaque { ident, .. } => ident,
}
}
}
impl fmt::Display for TypeDef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "type :{} = ", self.ident())?;
let align = match self {
TypeDef::Regular { align, .. } => *align,
TypeDef::Union { align, .. } => *align,
TypeDef::Opaque { align, .. } => Some(*align),
};
if let Some(align) = align {
write!(f, "align {align} ")?;
}
fn format(items: &[(Type, usize)]) -> String {
items
.iter()
.map(|(ty, count)| {
if *count > 1 {
format!("{ty} {count}")
} else {
format!("{ty}")
}
})
.collect::<Vec<String>>()
.join(", ")
}
match self {
TypeDef::Regular { items, .. } => {
write!(f, "{{ {} }}", format(items))
}
TypeDef::Union { variations, .. } => write!(
f,
"{{ {} }}",
variations
.iter()
.map(|items| format!("{{ {} }}", format(items)))
.collect::<Vec<_>>()
.join(" ")
),
TypeDef::Opaque { size, .. } => write!(f, "{{ {size} }}"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Statement {
Assign(Value, Type, Instr),
Volatile(Instr),
}
impl fmt::Display for Statement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Assign(temp, ty, instr) => {
assert!(
matches!(temp, Value::Temporary(_)),
"assignment target must be a temporary, got {temp:?}"
);
write!(f, "{temp} ={ty} {instr}")
}
Self::Volatile(instr) => write!(f, "{instr}"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct Block {
pub label: String,
pub items: Vec<BlockItem>,
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum BlockItem {
Statement(Statement),
Comment(String),
}
impl fmt::Display for BlockItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Statement(stmt) => write!(f, "{stmt}"),
Self::Comment(comment) => write!(f, "# {comment}"),
}
}
}
impl Block {
pub fn add_comment(&mut self, contents: impl Into<String>) {
self.items.push(BlockItem::Comment(contents.into()));
}
pub fn add_instr(&mut self, instr: Instr) {
self.items
.push(BlockItem::Statement(Statement::Volatile(instr)));
}
pub fn assign_instr(&mut self, temp: Value, ty: Type, instr: Instr) {
let final_type = match instr {
Instr::Call(_, _, _) => ty,
_ => ty.into_base(),
};
self.items.push(BlockItem::Statement(Statement::Assign(
temp, final_type, instr,
)));
}
pub fn jumps(&self) -> bool {
let last = self.items.last();
if let Some(BlockItem::Statement(Statement::Volatile(instr))) = last {
matches!(instr, Instr::Ret(_) | Instr::Jmp(_) | Instr::Jnz(..))
} else {
false
}
}
}
impl fmt::Display for Block {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "@{}", self.label)?;
write!(
f,
"{}",
self.items
.iter()
.map(|instr| format!("\t{instr}"))
.collect::<Vec<String>>()
.join("\n")
)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct Function {
pub linkage: Linkage,
pub name: String,
pub arguments: Vec<(Type, Value)>,
pub return_ty: Option<Type>,
pub blocks: Vec<Block>,
}
impl Function {
pub fn new(
linkage: Linkage,
name: impl Into<String>,
arguments: Vec<(Type, Value)>,
return_ty: Option<Type>,
) -> Self {
Function {
linkage,
name: name.into(),
arguments,
return_ty,
blocks: Vec::new(),
}
}
pub fn add_block(&mut self, label: impl Into<String>) -> &mut Block {
self.blocks.push(Block {
label: label.into(),
items: Vec::new(),
});
self.blocks.last_mut().unwrap()
}
#[deprecated(
since = "3.0.0",
note = "Use `self.blocks.last()` or `self.blocks.last_mut()` instead."
)]
pub fn last_block(&mut self) -> &Block {
self.blocks
.last()
.expect("Function must have at least one block")
}
pub fn add_instr(&mut self, instr: Instr) {
self.blocks
.last_mut()
.expect("Last block must be present")
.add_instr(instr);
}
pub fn assign_instr(&mut self, temp: Value, ty: Type, instr: Instr) {
self.blocks
.last_mut()
.expect("Last block must be present")
.assign_instr(temp, ty, instr);
}
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}function", self.linkage)?;
if let Some(ty) = &self.return_ty {
write!(f, " {ty}")?;
}
writeln!(
f,
" ${name}({args}) {{",
name = self.name,
args = self
.arguments
.iter()
.map(|(ty, temp)| format!("{ty} {temp}"))
.collect::<Vec<String>>()
.join(", "),
)?;
for blk in self.blocks.iter() {
writeln!(f, "{blk}")?;
}
write!(f, "}}")
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct Linkage {
pub exported: bool,
pub section: Option<String>,
pub secflags: Option<String>,
pub thread_local: bool,
}
impl Linkage {
pub fn private() -> Linkage {
Linkage {
exported: false,
section: None,
secflags: None,
thread_local: false,
}
}
pub fn private_with_section(section: impl Into<String>) -> Linkage {
Linkage {
exported: false,
section: Some(section.into()),
secflags: None,
thread_local: false,
}
}
pub fn public() -> Linkage {
Linkage {
exported: true,
section: None,
secflags: None,
thread_local: false,
}
}
pub fn public_with_section(section: impl Into<String>) -> Linkage {
Linkage {
exported: true,
section: Some(section.into()),
secflags: None,
thread_local: false,
}
}
pub fn thread_local() -> Linkage {
Linkage {
exported: false,
thread_local: true,
section: None,
secflags: None,
}
}
pub fn exported_thread_local() -> Linkage {
Linkage {
exported: true,
thread_local: true,
section: None,
secflags: None,
}
}
pub fn thread_local_with_section(section: impl Into<String>) -> Linkage {
Linkage {
exported: false,
thread_local: true,
section: Some(section.into()),
secflags: None,
}
}
}
impl fmt::Display for Linkage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.exported {
write!(f, "export ")?;
}
if self.thread_local {
write!(f, "thread ")?;
}
if let Some(section) = &self.section {
write!(f, "section \"{section}\"")?;
if let Some(secflags) = &self.secflags {
write!(f, " \"{secflags}\"")?;
}
write!(f, " ")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct Module {
pub functions: Vec<Function>,
pub types: Vec<Arc<TypeDef>>,
pub data: Vec<DataDef>,
}
impl Module {
pub fn new() -> Module {
Module {
functions: Vec::new(),
types: Vec::new(),
data: Vec::new(),
}
}
pub fn add_function(&mut self, func: Function) -> &mut Function {
self.functions.push(func);
self.functions.last_mut().unwrap()
}
pub fn add_type(&mut self, def: Arc<TypeDef>) {
self.types.push(def);
}
pub fn add_data(&mut self, data: DataDef) -> &mut DataDef {
self.data.push(data);
self.data.last_mut().unwrap()
}
}
impl fmt::Display for Module {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for ty in self.types.iter() {
writeln!(f, "{ty}")?;
}
for func in self.functions.iter() {
writeln!(f, "{func}")?;
}
for data in self.data.iter() {
writeln!(f, "{data}")?;
}
Ok(())
}
}