use std::{
cell::RefCell,
collections::HashSet,
io,
fmt,
};
use typed_arena::Arena;
#[derive(Default)]
pub struct ModuleContext<'m> {
types: Arena<Typedef<'m>>,
data: Arena<Datadef<'m>>,
strings: Arena<u8>,
}
impl<'m> ModuleContext<'m> {
pub fn new_module(&'m self, name: &str) -> Module<'m> {
let name = self.strings.alloc_str(name);
let mut string_table: HashSet<&'m str> = HashSet::new();
string_table.insert(name);
Module {
name,
ctx: self,
string_table: RefCell::new(string_table),
types: RefCell::new(Vec::new()),
data: RefCell::new(Vec::new()),
}
}
pub fn clear(&mut self) {
self.types = Default::default();
self.strings = Default::default();
}
}
pub struct Module<'m> {
name: &'m str,
ctx: &'m ModuleContext<'m>,
string_table: RefCell<HashSet<&'m str>>,
types: RefCell<Vec<&'m Typedef<'m>>>,
data: RefCell<Vec<&'m Datadef<'m>>>,
}
#[derive(Debug, Clone, Copy)]
pub struct Symbol<'m>(&'m str);
#[derive(Debug)]
pub struct Typedef<'m> {
name: &'m str,
repr: TypeRepr<'m>,
}
impl fmt::Display for Typedef<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "type :{} = ", self.name)?;
match self.repr {
TypeRepr::Opaque { align, size } => write!(f, "align {} {{ {} }}", align, size),
TypeRepr::Regular { align, ref subtys } => {
if let Some(align) = align {
write!(f, "align {} ", align)?;
}
f.write_str("{ ")?;
for &(ty, n) in subtys {
write!(f, "{}", ty)?;
if n > 1 {
write!(f, " {}, ", n)?;
} else {
f.write_str(", ")?;
}
}
f.write_str("}")
}
}
}
}
#[derive(Debug)]
pub struct TypeBuilder<'m> {
name: &'m str,
pub align: Option<usize>,
subtys: Vec<(Type<'m>, usize)>,
}
impl<'m> TypeBuilder<'m> {
pub fn add_subtype<T: Into<Type<'m>>>(&mut self, ty: T) {
let ty = ty.into();
match self.subtys.last_mut() {
Some((last, ref mut n)) if ty == *last => {
*n += 1;
}
_ => self.subtys.push((ty, 1)),
}
}
pub fn add_subtype_array<T: Into<Type<'m>>>(&mut self, ty: T, count: usize) {
let ty = ty.into();
match self.subtys.last_mut() {
Some((last, ref mut n)) if ty == *last => {
*n += count;
}
_ => self.subtys.push((ty, count)),
}
}
}
#[derive(Debug)]
enum TypeRepr<'m> {
Opaque {
align: usize,
size: usize,
},
Regular {
align: Option<usize>,
subtys: Vec<(Type<'m>, usize)>,
},
}
#[derive(Debug, Clone, Copy)]
pub enum Type<'m> {
Word,
Long,
Float,
Double,
Byte,
Short,
Typedef(&'m Typedef<'m>),
}
impl Type<'_> {
#[inline]
pub fn is_base(&self) -> bool {
matches!(self, Self::Word | Self::Long | Self::Float | Self::Double)
}
#[inline]
pub fn is_ext(&self) -> bool {
self.is_base() || matches!(self, Self::Byte | Self::Short)
}
}
impl fmt::Display for Type<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Type::*;
match self {
Word => f.pad("w"),
Long => f.pad("l"),
Float => f.pad("s"),
Double => f.pad("d"),
Byte => f.pad("b"),
Short => f.pad("h"),
Typedef(ty) => write!(f, ":{}", ty.name),
}
}
}
impl<'m> From<&'m Typedef<'m>> for Type<'m> {
fn from(ty: &'m Typedef<'m>) -> Self {
Self::Typedef(ty)
}
}
impl PartialEq for Type<'_> {
fn eq(&self, rhs: &Type<'_>) -> bool {
use Type::*;
match (*self, *rhs) {
(Word, Word) => true,
(Long, Long) => true,
(Double, Double) => true,
(Byte, Byte) => true,
(Short, Short) => true,
(Typedef(tyl), Typedef(tyr)) => std::ptr::eq(tyl, tyr),
_ => false,
}
}
}
pub struct Datadef<'m> {
name: Symbol<'m>,
export: bool,
m: &'m Module<'m>,
pub align: Option<usize>,
data: Vec<DataItem<'m>>,
}
impl<'m> Datadef<'m> {
pub fn zeros(&mut self, n: usize) {
self.data.push(DataItem::Z(n));
}
pub fn string(&mut self, s: &str) {
let s = self.m.intern(s);
self.data.push(DataItem::Str(s))
}
pub fn symbol(&mut self, ty: Type<'m>, sym: Symbol<'m>, offset: isize) {
assert!(ty.is_ext());
self.data.push(DataItem::Sym(ty, sym, offset));
}
pub fn byte(&mut self, b: u8) {
self.data.push(DataItem::Const(Type::Byte, Const::Int(b as i64)));
}
pub fn short(&mut self, n: i16) {
self.data.push(DataItem::Const(Type::Short, Const::Int(n as i64)));
}
pub fn long(&mut self, n: i64) {
self.data.push(DataItem::Const(Type::Long, Const::Int(n)));
}
pub fn word(&mut self, n: i32) {
self.data.push(DataItem::Const(Type::Word, Const::Int(n as i64)));
}
pub fn float(&mut self, f: f32) {
self.data.push(DataItem::Const(Type::Float, Const::Float(f)));
}
pub fn double(&mut self, d: f64) {
self.data.push(DataItem::Const(Type::Double, Const::Double(d)));
}
}
impl fmt::Display for Datadef<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let vis = if self.export { "export " } else { "" };
write!(f, "{}data ${} = ", vis, self.name.0)?;
if let Some(align) = self.align {
write!(f, "align {} ", align)?;
}
f.write_str("{")?;
let mut first = true;
let mut prev_ty = None;
for data in &self.data {
if data.ty() == prev_ty && data.ty().is_some() {
write!(f, " {:#}", data)?;
} else if first {
first = false;
write!(f, " {}", data)?;
} else {
write!(f, ", {}", data)?;
}
prev_ty = data.ty();
}
f.write_str(" }")
}
}
#[allow(unused)]
enum DataItem<'m> {
Z(usize),
Str(&'m str),
Sym(Type<'m>, Symbol<'m>, isize),
Const(Type<'m>, Const<'m>),
}
impl<'m> DataItem<'m> {
fn ty(&self) -> Option<Type<'m>> {
match self {
Self::Z(_) => None,
Self::Str(_) => Some(Type::Byte),
Self::Sym(ty, _, _) | Self::Const(ty, _) => Some(*ty),
}
}
fn ty_s(&self) -> &'static str {
use Type::*;
match self.ty() {
Some(Word) => "w ",
Some(Long) => "l ",
Some(Float) => "s ",
Some(Double) => "d ",
Some(Byte) => "b ",
Some(Short) => "h ",
None => "z ",
_ => unreachable!(),
}
}
}
impl fmt::Display for DataItem<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ty = if f.alternate() {
""
} else {
self.ty_s()
};
match self {
Self::Z(n) => write!(f, "{}{}", ty, n),
Self::Str(s) => write!(f, "{}{:?}", ty, s),
Self::Sym(_ty, sym, off) => {
if *off != 0 {
write!(f, "{}${}+{}", ty, sym.0, off)
} else {
write!(f, "{}${}", ty, sym.0)
}
}
Self::Const(_ty, constant) => {
write!(f, "{}{}", ty, constant)
}
}
}
}
#[allow(unused)]
enum Const<'m> {
Int(i64),
Float(f32),
Double(f64),
Id(Symbol<'m>),
}
impl fmt::Display for Const<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Int(i) => write!(f, "{}", i),
Self::Float(s) => write!(f, "s_{}", s),
Self::Double(d) => write!(f, "d_{}", d),
Self::Id(sym) => write!(f, "${}", sym.0),
}
}
}
impl<'m> Module<'m> {
pub fn render<W>(&self, mut w: W) -> io::Result<()>
where
W: io::Write,
{
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
write!(
&mut w,
"## QBE IL generated by cuebee version {}\n\
# name: `{}' \n\n",
VERSION, self.name
)?;
for ty in &*self.types.borrow() {
writeln!(&mut w, "{}", ty)?;
}
let data = self.data.borrow();
if !data.is_empty() {
writeln!(&mut w)?;
}
for data in &*data {
writeln!(&mut w, "{}", data)?;
}
Ok(())
}
pub fn render_string(&self) -> String {
let mut v = Vec::new();
self.render(&mut v).expect("writing to vec failed");
unsafe { String::from_utf8_unchecked(v) }
}
fn intern(&self, s: &str) -> &'m str {
let mut table = self.string_table.borrow_mut();
if let Some(s) = table.get(s) {
s
} else {
let s = self.ctx.strings.alloc_str(s);
table.insert(s);
s
}
}
fn gensym(&self, s: &str) -> Symbol<'m> {
Symbol(self.intern(s))
}
pub fn opaque_type(&self, name: &str, align: usize, size: usize) -> &Typedef {
let name = self.intern(name);
let ty = self.ctx.types.alloc(Typedef {
name,
repr: TypeRepr::Opaque { align, size },
});
self.types.borrow_mut().push(ty);
ty
}
pub fn new_type(&self, name: &str) -> TypeBuilder<'m> {
let name = self.intern(name);
TypeBuilder {
name,
align: None,
subtys: Vec::new(),
}
}
pub fn add_type(&self, tb: TypeBuilder<'m>) -> &Typedef {
let TypeBuilder { name, align, subtys } = tb;
let ty = self.ctx.types.alloc(Typedef {
name,
repr: TypeRepr::Regular {
align,
subtys,
}
});
self.types.borrow_mut().push(ty);
ty
}
pub fn new_data(&self, name: &str) -> (&'m mut Datadef, Symbol<'m>) {
let name = self.gensym(name);
let data = self.ctx.data.alloc(Datadef {
name,
m: &self,
align: None,
export: false,
data: Vec::new(),
});
(data, name)
}
pub fn add_data(&self, data: &'m Datadef<'m>) -> &'m Datadef {
self.data.borrow_mut().push(data);
data
}
}