diff --git a/Cargo.lock b/Cargo.lock index 282c713..fd946f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -297,6 +297,7 @@ dependencies = [ name = "orco" version = "0.0.1-prealpha" dependencies = [ + "papaya", "sinter", ] @@ -305,7 +306,6 @@ name = "orco-cgen" version = "0.1.0" dependencies = [ "orco", - "papaya", ] [[package]] @@ -318,21 +318,12 @@ dependencies = [ "orco", ] -[[package]] -name = "orco-ir" -version = "0.1.0" -dependencies = [ - "orco", - "papaya", -] - [[package]] name = "orco-rustc" version = "0.1.0" dependencies = [ "orco", "orco-cgen", - "orco-ir", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ea3c22c..21db1ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "1" members = [ "orco", "frontends/orco-rustc", - "backends/orco-ir", "backends/orco-cgen", "backends/orco-cranelift", + "backends/orco-cgen", "backends/orco-cranelift", ] [workspace.package] diff --git a/backends/orco-cgen/Cargo.toml b/backends/orco-cgen/Cargo.toml index b4033de..e26b2d6 100644 --- a/backends/orco-cgen/Cargo.toml +++ b/backends/orco-cgen/Cargo.toml @@ -4,5 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] -papaya.workspace = true orco.workspace = true diff --git a/backends/orco-cgen/src/lib.rs b/backends/orco-cgen/src/lib.rs index c86532f..5d04574 100644 --- a/backends/orco-cgen/src/lib.rs +++ b/backends/orco-cgen/src/lib.rs @@ -1,239 +1,109 @@ //! C transpilation backend for orco. //! Also used to generate C headers and -//! is generally the reference for other backends -//! See [Backend] +//! is generally the reference for other backends. +//! See [FmtModule]. // TODO: ABI #![warn(missing_docs)] -/// Type formatting & other things +/// Type formatting & other things. pub mod types; use types::FmtType; -/// Type interning and name conversion -mod type_names; - -/// Symbol container types +/// Symbol formatting stuff. pub mod symbols; // /// Code generation, used to generate function bodies. // pub mod codegen; // pub use codegen::Codegen; -use papaya::HashMap; - -/// Root backend struct -#[derive(Debug, Default)] -pub struct Backend { - /// Type aliases - pub types: HashMap, - /// Interned types - interned: HashMap, - /// Function declarations - pub functions: HashMap, - /// Definitions - definitions: std::sync::Mutex>, -} - -impl Backend { - #[allow(missing_docs)] - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a definition - pub fn define(&self, code: String) { - self.definitions.lock().unwrap().push(code); - } - - /// Get the name of the symbol used in generated C code ("mangling") - pub fn cname(&self, name: orco::Symbol) -> String { - // Take only the method name, not the path - // FIXME: conflicts... - let mut new_name = String::new(); - for split in name.split([',', '<', '>', '{', '}']) { - let split = &split[split.rfind([':', '.']).map_or(0, |i| i + 1)..]; - if !split.is_empty() { - match new_name.chars().last() { - None | Some('_') => (), - _ => new_name.push('_'), - } - new_name.push_str(split); - } - } - - let mut new_name = new_name.replace(|c: char| !c.is_ascii_alphanumeric(), "_"); - if new_name.chars().next().is_none_or(|c| c.is_ascii_digit()) { - new_name.insert(0, '_'); - } - - new_name - } -} - -impl orco::DeclarationBackend for Backend { - fn function( - &self, - name: orco::Symbol, - generics: Vec, - mut params: Vec<(Option, orco::Type)>, - mut return_type: Option, - attrs: orco::attrs::FunctionAttributes, - ) { - let name = self.generic_name(name, &generics); - for (_, ty) in &mut params { - self.intern_type(ty, None); - } - if let Some(rt) = &mut return_type { - self.intern_type(rt, None); - } - self.functions - .pin() - .try_insert( - name, - orco::types::FunctionSignature { - params, - return_type, - attrs, - }, - ) - .unwrap_or_else(|_| panic!("function {name} is already declared")); - } - - fn type_(&self, name: orco::Symbol, generics: Vec, mut ty: orco::Type) { - let name = self.generic_name(name, &generics); - self.intern_type(&mut ty, Some(name)); - self.types - .pin() - .try_insert(name, ty) - .unwrap_or_else(|_| panic!("type {name} is already declared")); - } -} +/// Topologically sorts types in a module. +mod topsort; -// impl orco::CodegenBackend for crate::Backend { -// fn cg_function( -// &self, -// name: orco::Symbol, -// generics: Vec, -// ) -> impl orco::codegen::BodyCodegen { -// let name = self.generic_name(name, &generics); -// codegen::Codegen::new(self, name) -// } -// } - -/// Adds all symbols this type uses into `dependencies` -fn type_dependencies(backend: &Backend, ty: &orco::Type, dependencies: &mut Vec) { - match ty { - orco::Type::Symbol(name, generics) => { - dependencies.push(backend.generic_name(*name, generics)) - } - orco::Type::Array(ty, sz) if *sz > 0 => type_dependencies(backend, ty, dependencies), - orco::Type::Struct { fields } => { - for (_, ty) in fields { - type_dependencies(backend, ty, dependencies); - } - } - _ => (), - } -} +/// Generate C code for one [`orco::Module`]. +pub struct FmtModule<'a>(pub &'a orco::Module); -impl std::fmt::Display for Backend { +impl std::fmt::Display for FmtModule<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let FmtModule(module) = self; + writeln!(f, "#include ")?; writeln!(f, "#include ")?; writeln!(f, "#include ")?; writeln!(f)?; - use std::collections::HashMap; - #[derive(Default)] - struct TopSorter { - deps: HashMap>, - /// If name isn't present - not visited, - /// otherwise stores whether it has finished processing - /// (if false is encountered, loop is detected) - visited: HashMap, - order: Vec, - } - - let types = self.types.pin(); - let mut sorter = TopSorter::default(); - for (name, ty) in types.iter() { - let mut dependencies = Vec::new(); - type_dependencies(self, ty, &mut dependencies); - sorter.deps.insert(*name, dependencies); - - if matches!(ty, orco::Type::Struct { .. }) { - let name = self.cname(*name); + let mut any = false; + for (name, alias) in module.types.pin().iter() { + if matches!(alias.type_, orco::Type::Struct { .. }) { + let name = cname(*name); writeln!(f, "typedef struct {name} {name};")?; + any = true; } } - fn topsort(name: orco::Symbol, sorter: &mut TopSorter) { - use std::collections::hash_map::Entry; - match sorter.visited.entry(name) { - Entry::Occupied(finished) => { - if *finished.get() { - return; - } - panic!( - "type dependency cycle detected on {name}, possibly an infinitely-recursive type", - ); - } - Entry::Vacant(entry) => entry.insert(false), - }; - - let deps = sorter.deps.remove(&name); - for dep in deps.into_iter().flat_map(Vec::into_iter) { - topsort(dep, sorter); - } - - sorter.visited.insert(name, true); - sorter.order.push(name); - } - - for (name, _) in types.iter() { - topsort(*name, &mut sorter); + if any { + writeln!(f)?; + any = false; } - writeln!(f)?; - - for name in sorter.order { - let Some(ty) = types.get(&name) else { - continue; - }; + topsort::visit(module, |name, ty| { + any = true; writeln!( f, "typedef {};", FmtType { - backend: self, ty: &ty, constant: false, - name: Some(&self.cname(name)) + name: Some(&cname(name)) } - )?; - } + ) + })?; - writeln!(f)?; + if any { + writeln!(f)?; + any = false; + } - for (name, signature) in self.functions.pin().iter() { + for (name, function) in module.functions.pin().iter() { + any = true; writeln!( f, "{};", symbols::FmtFunction { - backend: self, - name: &self.cname(*name), - signature, + name: &cname(*name), + function, name_all_args: false, } )?; } - writeln!(f)?; - - for def in self.definitions.lock().unwrap().iter() { - writeln!(f, "{def}\n")?; + if any { + writeln!(f)?; } Ok(()) } } + +/// Get the name of the symbol used in generated C code ("mangling") +pub fn cname(name: orco::Symbol) -> String { + // Take only the method name, not the path + // FIXME: conflicts... + let mut new_name = String::new(); + for split in name.split([',', '<', '>', '{', '}']) { + let split = &split[split.rfind([':', '.']).map_or(0, |i| i + 1)..]; + if !split.is_empty() { + match new_name.chars().last() { + None | Some('_') => (), + _ => new_name.push('_'), + } + new_name.push_str(split); + } + } + + let mut new_name = new_name.replace(|c: char| !c.is_ascii_alphanumeric(), "_"); + if new_name.chars().next().is_none_or(|c| c.is_ascii_digit()) { + new_name.insert(0, '_'); + } + + new_name +} diff --git a/backends/orco-cgen/src/symbols.rs b/backends/orco-cgen/src/symbols.rs index 78b6e69..72c1956 100644 --- a/backends/orco-cgen/src/symbols.rs +++ b/backends/orco-cgen/src/symbols.rs @@ -1,14 +1,12 @@ use crate::FmtType; -use orco::types::FunctionSignature; +use orco::Function; /// Formats function signature pub struct FmtFunction<'a> { - /// A reference to the backend (for name conversion/mangling) - pub backend: &'a crate::Backend, - /// Function name + /// Function name. pub name: &'a str, - /// Function signature - pub signature: &'a FunctionSignature, + /// Function itself. + pub function: &'a Function, /// Wether to name all args (assign placeholder names)? pub name_all_args: bool, } @@ -16,14 +14,13 @@ pub struct FmtFunction<'a> { impl std::fmt::Display for FmtFunction<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let FmtFunction { - backend, name, - signature, + function, name_all_args, } = *self; use orco::attrs as oa; - match signature.attrs.inlining { + match function.attrs.inlining { oa::Inlining::Never => write!(f, "__attribute__ ((noinline)) ")?, oa::Inlining::Auto => (), oa::Inlining::Hint => write!(f, "inline ")?, @@ -34,7 +31,7 @@ impl std::fmt::Display for FmtFunction<'_> { use std::fmt::Write as _; write!(&mut sig_noret, "(")?; - for (idx, (name, ty)) in signature.params.iter().enumerate() { + for (idx, (name, ty)) in function.params.iter().enumerate() { if idx > 0 { write!(sig_noret, ", ")?; } @@ -42,7 +39,6 @@ impl std::fmt::Display for FmtFunction<'_> { sig_noret, "{}", FmtType { - backend, ty, constant: false, name: match name { @@ -57,8 +53,7 @@ impl std::fmt::Display for FmtFunction<'_> { write!(sig_noret, ")")?; FmtType { - backend, - ty: signature + ty: function .return_type .as_ref() .unwrap_or(&orco::Type::Symbol("void".into(), Vec::new())), diff --git a/backends/orco-cgen/src/topsort.rs b/backends/orco-cgen/src/topsort.rs new file mode 100644 index 0000000..96d4d06 --- /dev/null +++ b/backends/orco-cgen/src/topsort.rs @@ -0,0 +1,70 @@ +use std::collections::HashMap; + +/// Adds all symbols this type uses into `dependencies` +fn type_dependencies(ty: &orco::Type, dependencies: &mut Vec) { + match ty { + orco::Type::Symbol(name, generics) => { + assert!( + generics.is_empty(), + "generics type encountered in C backend ({ty}), did you forget to monomorphize types?", + ); + dependencies.push(*name); + } + orco::Type::Array(ty, sz) if *sz > 0 => type_dependencies(ty, dependencies), + orco::Type::Struct { fields } => { + for (_, ty) in fields { + type_dependencies(ty, dependencies); + } + } + _ => (), + } +} + +/// `visited` is a map, where if name isn't present - not visited, +/// otherwise stores whether it has finished processing +/// (if false is encountered, loop is detected) +fn topsort( + visited: &mut HashMap, + types: &orco::SymbolMapRef, + callback: &mut impl FnMut(orco::Symbol, &orco::Type) -> Result<(), E>, + name: orco::Symbol, +) -> Result<(), E> { + use std::collections::hash_map::Entry; + match visited.entry(name) { + Entry::Occupied(finished) if *finished.get() => return Ok(()), + Entry::Occupied(_) => panic!( + "type dependency cycle detected on {name}, possibly an infinitely-recursive type", + ), + Entry::Vacant(entry) => entry.insert(false), + }; + + let ty = &types + .get(&name) + .unwrap_or_else(|| panic!("[bug] undeclared type {name}")) + .type_; + let mut dependencies = Vec::new(); + type_dependencies(ty, &mut dependencies); + + for dep in dependencies { + topsort(visited, types, callback, dep)?; + } + + callback(name, ty)?; + visited.insert(name, true); + Ok(()) +} + +/// Visit types in topological order (excluding pointers). +pub fn visit( + module: &orco::Module, + mut callback: impl FnMut(orco::Symbol, &orco::Type) -> Result<(), E>, +) -> Result<(), E> { + let types = module.types.pin(); + let mut visited = HashMap::new(); + + for name in types.keys() { + topsort(&mut visited, &types, &mut callback, *name)?; + } + + Ok(()) +} diff --git a/backends/orco-cgen/src/type_names.rs b/backends/orco-cgen/src/type_names.rs deleted file mode 100644 index 112249c..0000000 --- a/backends/orco-cgen/src/type_names.rs +++ /dev/null @@ -1,95 +0,0 @@ -impl super::Backend { - /// If ty is a type alias (but not a struct), inlines it. - /// Does not inline inner types - pub fn inline_type_aliases<'a>( - &self, - guard: &'a impl papaya::Guard, - mut ty: &'a orco::Type, - inline_struct: bool, - ) -> &'a orco::Type { - while let orco::Type::Symbol(name, generics) = ty { - let name = self.generic_name(*name, generics); - let symbol = self - .types - .get(&name, guard) - .unwrap_or_else(|| panic!("undeclared type {name}")); - if inline_struct || !matches!(*symbol, orco::Type::Struct { .. }) { - ty = symbol; - } else { - return ty; - } - } - - ty - } - - /// Intern the following type and it's insides. - /// If `named` contains a value, it's the name of the current typedef - pub fn intern_type(&self, ty: &mut orco::Type, named: Option) { - use orco::Type; - - // Intern inner types - match ty { - Type::Symbol(name, generics) => { - for ty in generics.iter_mut() { - self.intern_type(ty, None); - } - *ty = Type::Symbol(self.generic_name(*name, generics), Vec::new()); - } - Type::Array(ty, _) => self.intern_type(ty.as_mut(), None), - Type::Struct { fields } => { - for (_, ty) in fields { - self.intern_type(ty, None) - } - } - Type::Ptr(ty, _) => self.intern_type(ty, None), - Type::FnPtr { - params, - return_type, - } => { - for ty in params { - self.intern_type(ty, None); - } - if let Some(ty) = return_type { - self.intern_type(ty, None); - } - } - _ => (), - } - - // Intern this type (if required) - match ty { - Type::Struct { .. } => { - let interned = self.interned.pin(); - if let Some(name) = interned.get(ty) { - *ty = orco::Type::Symbol(*name, Vec::new()); - } else { - if let Some(name) = named { - interned.insert(ty.clone(), name); - } else { - use orco::DeclarationBackend as _; - let name = ty.to_string().into(); - let ty = core::mem::replace(ty, Type::Symbol(name, Vec::new())); - self.type_(name, Vec::new(), ty); - } - } - } - _ => (), - } - } - - /// Embed generics in the symbol name (also handles disambiguation and interning) - pub fn generic_name(&self, name: orco::Symbol, generics: &[orco::Type]) -> orco::Symbol { - if generics.is_empty() { - return name; - } - - for ty in generics { - if ty.has_params() { - panic!("generic params are not supported (encountered {ty})"); - } - } - - format!("{name}{}", orco::types::fmt_generics(generics)).into() - } -} diff --git a/backends/orco-cgen/src/types.rs b/backends/orco-cgen/src/types.rs index 547cd58..1332f7a 100644 --- a/backends/orco-cgen/src/types.rs +++ b/backends/orco-cgen/src/types.rs @@ -1,9 +1,8 @@ /// A thin wrapper around [`orco::Type`] for formatting it as a C type. -/// Because C loves types to influence postfixes (aka arrays and function pointers), +/// Because C loves types to influence suffixes (aka arrays and function pointers), /// also wraps optional name (variable name, parameter name, type name in typedef) #[allow(missing_docs)] pub struct FmtType<'a> { - pub backend: &'a crate::Backend, pub ty: &'a orco::Type, pub constant: bool, pub name: Option<&'a str>, @@ -11,12 +10,7 @@ pub struct FmtType<'a> { impl std::fmt::Display for FmtType<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let FmtType { - backend, - ty, - constant, - name, - } = *self; + let FmtType { ty, constant, name } = *self; use orco::Type as OT; use orco::types::IntegerSize as IS; @@ -62,7 +56,11 @@ impl std::fmt::Display for FmtType<'_> { OT::Char(false) => write!(f, "char"), OT::Char(true) => write!(f, "wchar_t"), OT::Symbol(sym, generics) => { - write!(f, "{}", backend.cname(backend.generic_name(*sym, generics))) + assert!( + generics.is_empty(), + "generics type encountered in C backend ({ty}), did you forget to monomorphize types?", + ); + write!(f, "{}", crate::cname(*sym)) } OT::Array(ty, sz) => { @@ -70,7 +68,6 @@ impl std::fmt::Display for FmtType<'_> { f, "{}[{sz}]", FmtType { - backend, ty, constant: false, name @@ -95,7 +92,6 @@ impl std::fmt::Display for FmtType<'_> { f, " {};", FmtType { - backend, ty, constant: false, name: Some( @@ -116,7 +112,6 @@ impl std::fmt::Display for FmtType<'_> { f, "{}", FmtType { - backend, ty, constant: !*pointee_mutable, name: Some( @@ -141,7 +136,6 @@ impl std::fmt::Display for FmtType<'_> { f, "{}", FmtType { - backend, ty: return_type .as_deref() .unwrap_or(&orco::Type::Symbol("void".into(), Vec::new())), @@ -152,7 +146,6 @@ impl std::fmt::Display for FmtType<'_> { params .iter() .map(|ty| FmtType { - backend, ty, constant: false, name diff --git a/backends/orco-ir/Cargo.toml b/backends/orco-ir/Cargo.toml deleted file mode 100644 index 516e2e8..0000000 --- a/backends/orco-ir/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "orco-ir" -version = "0.1.0" -edition = "2024" -description.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -papaya.workspace = true -orco.workspace = true diff --git a/backends/orco-ir/src/codegen/control_flow.rs b/backends/orco-ir/src/codegen/control_flow.rs deleted file mode 100644 index 6595b9c..0000000 --- a/backends/orco-ir/src/codegen/control_flow.rs +++ /dev/null @@ -1,80 +0,0 @@ -use super::{Codegen, ir, oc}; - -impl oc::AcfCodegen for &mut Codegen<'_> { - fn alloc_label(&mut self) -> oc::Label { - self.body.labels.push(0); - oc::Label(self.body.labels.len() - 1) - } - - fn label(&mut self, label: oc::Label) { - self.body.labels[label.0] = self.body.statements.len(); - } - - fn jump(&mut self, label: oc::Label) { - self.body - .statements - .push(ir::Statement::Acf(ir::AcfStatement::Jump(label))); - } - - fn cjump(&mut self, condition: oc::Value, label: oc::Label) { - let condition = self.use_value(condition); - self.body - .statements - .push(ir::Statement::Acf(ir::AcfStatement::Cjump( - condition, label, - ))); - } -} - -impl oc::BcfCodegen for &mut Codegen<'_> { - fn if_(&mut self, condition: oc::Value) { - let condition = self.use_value(condition); - self.body - .statements - .push(ir::Statement::Bcf(ir::BcfStatement::If(condition))); - } - - fn else_(&mut self) { - self.body - .statements - .push(ir::Statement::Bcf(ir::BcfStatement::Else)); - } - - fn end(&mut self) { - self.body - .statements - .push(ir::Statement::Bcf(ir::BcfStatement::End)); - } - - fn loop_(&mut self) { - self.body - .statements - .push(ir::Statement::Bcf(ir::BcfStatement::Loop)); - } - - fn break_(&mut self) { - self.body - .statements - .push(ir::Statement::Bcf(ir::BcfStatement::Break)); - } - - fn continue_(&mut self) { - self.body - .statements - .push(ir::Statement::Bcf(ir::BcfStatement::Continue)); - } - - fn cbreak(&mut self, condition: oc::Value) { - let condition = self.use_value(condition); - self.body - .statements - .push(ir::Statement::Bcf(ir::BcfStatement::Cbreak(condition))); - } - - fn ccontinue(&mut self, condition: oc::Value) { - let condition = self.use_value(condition); - self.body - .statements - .push(ir::Statement::Bcf(ir::BcfStatement::Ccontinue(condition))); - } -} diff --git a/backends/orco-ir/src/codegen/intrinsics.rs b/backends/orco-ir/src/codegen/intrinsics.rs deleted file mode 100644 index 227b15b..0000000 --- a/backends/orco-ir/src/codegen/intrinsics.rs +++ /dev/null @@ -1,26 +0,0 @@ -use super::{ir, oc}; - -impl oc::Intrinsics for &mut super::Codegen<'_> { - fn add(&mut self, a: oc::Value, b: oc::Value) -> oc::Value { - let a = Box::new(self.use_value(a)); - let b = Box::new(self.use_value(b)); - self.expr(ir::Expression::Intrinsic(ir::Intrinsic::Add(a, b))) - } - - fn mul(&mut self, a: oc::Value, b: oc::Value) -> oc::Value { - let a = Box::new(self.use_value(a)); - let b = Box::new(self.use_value(b)); - self.expr(ir::Expression::Intrinsic(ir::Intrinsic::Mul(a, b))) - } - - fn eq(&mut self, a: oc::Value, b: oc::Value) -> oc::Value { - let a = Box::new(self.use_value(a)); - let b = Box::new(self.use_value(b)); - self.expr(ir::Expression::Intrinsic(ir::Intrinsic::Eq(a, b))) - } - - fn not(&mut self, a: oc::Value) -> oc::Value { - let a = Box::new(self.use_value(a)); - self.expr(ir::Expression::Intrinsic(ir::Intrinsic::Not(a))) - } -} diff --git a/backends/orco-ir/src/codegen/mod.rs b/backends/orco-ir/src/codegen/mod.rs deleted file mode 100644 index ed9b9c1..0000000 --- a/backends/orco-ir/src/codegen/mod.rs +++ /dev/null @@ -1,203 +0,0 @@ -use crate::ir; -use orco::codegen as oc; -use std::collections::HashMap; - -mod control_flow; -mod intrinsics; - -/// Implementation of [`oc::BodyCodegen`] -pub struct Codegen<'a> { - /// Store that will recieve the symbol once codegen is done - pub store: &'a crate::Store, - /// Symbol name - pub name: orco::Symbol, - /// Generic params - pub generic_params: Vec, - /// Currently generated body - pub body: ir::Body, - /// Currently unused values - values: HashMap, - /// Next value ID - next_value_id: usize, -} - -impl<'a> Codegen<'a> { - #[allow(missing_docs)] - pub fn new( - store: &'a crate::Store, - name: orco::Symbol, - generic_params: Vec, - ) -> Self { - let mut body = ir::Body::default(); - let decls = store.functions.pin(); - let function = decls - .get(&name) - .unwrap_or_else(|| panic!("trying to codegen undeclared function {name}")); - - body.variables.reserve(function.signature.params.len()); - for (name, ty) in &function.signature.params { - body.variables.push(ir::Variable { - ty: ty.clone(), - arg: true, - name: name.clone(), - }); - } - - Self { - store, - name, - generic_params, - body, - values: HashMap::new(), - next_value_id: 0, - } - } - - /// Insert an expression and return [`oc::Value`] for it - pub fn expr(&mut self, expr: ir::Expression) -> oc::Value { - let id = self.next_value_id; - self.next_value_id += 1; - self.values.insert(id, expr); - oc::Value(id) - } - - /// Convert [`oc::Value`] back to an expression, taking it out. - /// Opposite to [`Self::expr`] - pub fn use_value(&mut self, value: oc::Value) -> ir::Expression { - self.values - .remove(&value.0) - .unwrap_or_else(|| panic!("invalid or previously used value #{}", value.0)) - } - - /// Convert [`oc::Place`] to [`ir::Place`] - pub fn cvt_place(&mut self, place: oc::Place) -> ir::Place { - match place { - oc::Place::Variable(variable) => ir::Place::Variable(variable), - oc::Place::Global(name, generics) => ir::Place::Global(name, generics), - oc::Place::Deref(value) => ir::Place::Deref(Box::new(self.use_value(value))), - oc::Place::Field(place, idx) => ir::Place::Field(Box::new(self.cvt_place(*place)), idx), - } - } -} - -impl oc::BodyCodegen for Codegen<'_> { - fn comment(&mut self, comment: &str) { - self.body - .statements - .push(ir::Statement::Comment(comment.to_owned())); - } - - fn type_of(&self, id: usize) -> orco::Type { - self.values - .get(&id) - .unwrap_or_else(|| panic!("invalid value id {id}")) - .get_type(self.store, &self.body) - } - - fn declare_var(&mut self, ty: orco::Type, name: Option<&str>) -> oc::Variable { - self.body.variables.push(ir::Variable { - ty, - arg: false, - name: name.map(std::borrow::ToOwned::to_owned), - }); - oc::Variable(self.body.variables.len() - 1) - } - - fn assign(&mut self, target: oc::Place, value: oc::Value) { - let target = self.cvt_place(target); - let value = self.use_value(value); - self.body - .statements - .push(ir::Statement::Assign(target, value)); - } - - fn iconst(&mut self, value: i128, size: orco::types::IntegerSize) -> oc::Value { - self.expr(ir::Expression::IConst(value, size)) - } - - fn uconst(&mut self, value: u128, size: orco::types::IntegerSize) -> oc::Value { - self.expr(ir::Expression::UConst(value, size)) - } - - fn fconst(&mut self, value: f64, size: u16) -> oc::Value { - self.expr(ir::Expression::FConst(value, size)) - } - - fn bconst(&mut self, value: bool) -> oc::Value { - self.expr(ir::Expression::BConst(value)) - } - - fn read(&mut self, place: oc::Place) -> oc::Value { - let place = self.cvt_place(place); - self.expr(ir::Expression::Read(place)) - } - - fn reference(&mut self, place: oc::Place, mutable: bool) -> oc::Value { - let place = self.cvt_place(place); - let can_be_mutable = place.get_type(self.store, &self.body).1; - assert!( - !mutable || can_be_mutable, - "can't create mutable reference to an immutable {place}" - ); - - self.expr(ir::Expression::Reference(place, mutable)) - } - - fn call(&mut self, func: oc::Value, args: Vec) -> Option { - let func = self.use_value(func); - let has_retval = match func.get_type(self.store, &self.body) { - orco::Type::FnPtr { return_type, .. } => return_type.is_some(), - ty => panic!("trying to call non-function {func}, which is of type {ty}"), - }; - - let args = args.into_iter().map(|arg| self.use_value(arg)).collect(); - if has_retval { - Some(self.expr(ir::Expression::Call(Box::new(func), args))) - } else { - self.body.statements.push(ir::Statement::Call(func, args)); - None - } - } - - fn return_(&mut self, value: Option) { - let value = value.map(|value| self.use_value(value)); - self.body.statements.push(ir::Statement::Return(value)); - } - - fn intrinsics(&mut self) -> impl oc::Intrinsics + '_ { - self - } - - fn acf(&mut self) -> impl oc::AcfCodegen + '_ { - self - } - - fn bcf(&mut self) -> impl oc::BcfCodegen + '_ { - self - } -} - -impl core::ops::Drop for Codegen<'_> { - fn drop(&mut self) { - self.store - .function_bodies - .pin() - .get_or_insert_with(self.name, Default::default) - .pin() - .try_insert( - core::mem::take(&mut self.generic_params), - core::mem::take(&mut self.body), - ) - .unwrap_or_else(|_| panic!("function {} is already defined", self.name)); - } -} - -impl orco::CodegenBackend for Store { - fn cg_function( - &self, - name: orco::Symbol, - generic_params: Vec, - ) -> impl orco::codegen::BodyCodegen { - codegen::Codegen::new(self, name, generic_params) - } -} diff --git a/backends/orco-ir/src/forwarding/expression.rs b/backends/orco-ir/src/forwarding/expression.rs deleted file mode 100644 index e21e820..0000000 --- a/backends/orco-ir/src/forwarding/expression.rs +++ /dev/null @@ -1,77 +0,0 @@ -use super::{ir, oc}; - -impl super::FwdCtx<'_, CG> { - #[inline] - pub fn var(&self, var: oc::Variable) -> oc::Variable { - self.variable_map[var.0] - } - - /// Convert [`ir::Place`] into [`oc::Place`], - /// while generating code for inner expressions using - /// [`Self::expr`] - pub fn place(&mut self, place: &ir::Place) -> oc::Place { - match place { - ir::Place::Variable(variable) => self.var(*variable).into(), - ir::Place::Global(symbol, generics) => oc::Place::Global( - *symbol, - generics - .iter() - .map(|ty| ty.copy_instantiate(&self.type_map)) - .collect(), - ), - ir::Place::Deref(expr) => oc::Place::Deref(self.expr(expr)), - ir::Place::Field(place, idx) => self.place(place).field(*idx), - } - } - - /// Codegen [`ir::Expression`] into another [`oc::BodyCodegen`] - pub fn expr(&mut self, expr: &ir::Expression) -> oc::Value { - match expr { - ir::Expression::IConst(value, size) => self.cg.iconst(*value, *size), - ir::Expression::UConst(value, size) => self.cg.uconst(*value, *size), - ir::Expression::FConst(value, size) => self.cg.fconst(*value, *size), - ir::Expression::BConst(value) => self.cg.bconst(*value), - ir::Expression::Read(place) => { - let place = self.place(place); - self.cg.read(place) - } - ir::Expression::Reference(place, mutable) => { - let place = self.place(place); - self.cg.reference(place, *mutable) - } - ir::Expression::Call(func, args) => { - let func = self.expr(func); - let args = args.iter().map(|arg| self.expr(arg)).collect(); - self.cg - .call(func, args) - .unwrap_or_else(|| panic!("trying to use value from calling a void function")) - } - - ir::Expression::Intrinsic(intrinsic) => { - use crate::ir::Intrinsic as I; - use oc::Intrinsics as IT; - match intrinsic { - I::Add(a, b) => { - let a = self.expr(a); - let b = self.expr(b); - self.cg.intrinsics().add(a, b) - } - I::Mul(a, b) => { - let a = self.expr(a); - let b = self.expr(b); - self.cg.intrinsics().mul(a, b) - } - I::Eq(a, b) => { - let a = self.expr(a); - let b = self.expr(b); - self.cg.intrinsics().eq(a, b) - } - I::Not(a) => { - let a = self.expr(a); - self.cg.intrinsics().not(a) - } - } - } - } - } -} diff --git a/backends/orco-ir/src/forwarding/generics.rs b/backends/orco-ir/src/forwarding/generics.rs deleted file mode 100644 index 383c83d..0000000 --- a/backends/orco-ir/src/forwarding/generics.rs +++ /dev/null @@ -1,224 +0,0 @@ -impl crate::Store { - /// Generate monomorphization, see [`crate::Store::type_instances`] - /// and [`crate::Store::function_instances`] - pub fn monomorphize(&self) { - let type_instances = self.type_instances.pin(); - let function_instances = self.type_instances.pin(); - type_instances.clear(); - function_instances.clear(); - - for (name, specs) in self.types.pin().iter() { - for (generics, ty) in specs.pin().iter() { - if generics.iter().any(orco::Type::has_params) { - continue; - } - - if self.type_instances.pin().insert((*name, generics.clone())) { - self.register_type(&ty); - } - } - } - - let bodies = self.function_bodies.pin(); - for (name, decl) in self.functions.pin().iter() { - if !decl.generic_params.iter().any(orco::Type::has_params) { - self.register_funcion(*name, &decl.generic_params); - continue; - } - - let Some(specs) = bodies.get(name) else { - continue; - }; - - for (generics, _) in specs.pin().iter() { - if !decl.generic_params.iter().any(orco::Type::has_params) { - self.register_funcion(*name, generics); - } - } - } - } - - /// Declare all symbols from this IR in another [`orco::DeclarationBackend`], - /// monomorphizing generics - pub fn declare_mono(&self, backend: &impl orco::DeclarationBackend) { - for (name, generics) in self.type_instances.pin().iter() { - self.get_type(*name, generics, |ty, map| { - backend.type_(*name, generics.clone(), ty.copy_instantiate(&map)); - }); - } - - let functions = self.functions.pin(); - for (name, generics) in self.function_instances.pin().iter() { - let decl = functions - .get(name) - .unwrap_or_else(|| panic!("function {name} not found")); - let sig = decl.instantiate(self, generics); - backend.function( - *name, - generics.clone(), - sig.params.clone(), - sig.return_type.clone(), - sig.attrs.clone(), - ); - } - } - - /// Register a type instance for monomorphization, see [`Self::type_instances`] - pub fn register_type(&self, ty: &orco::Type) { - use orco::Type; - match ty { - Type::Integer(..) - | Type::Unsigned(..) - | Type::Float(..) - | Type::Bool - | Type::Char(..) => (), - Type::Symbol(name, generics) => { - if self.type_instances.pin().insert((*name, generics.clone())) { - self.get_type(*name, generics, |ty, map| { - let ty = ty.copy_instantiate(&map); - self.register_type(&ty); - }); - } - } - Type::Array(ty, _) => self.register_type(ty), - Type::Struct { fields } => { - for (_, ty) in fields { - self.register_type(ty); - } - } - Type::Ptr(ty, _) => self.register_type(ty), - Type::FnPtr { - params, - return_type, - } => { - for ty in params { - self.register_type(ty); - } - if let Some(ty) = return_type { - self.register_type(ty); - } - } - Type::Param(name) => { - panic!("encountered a type param #{name} while recording type instances") - } - Type::Error => (), - } - } - - /// Register a type instance for monomorphization, see [`Self::type_instances`] - pub fn register_funcion(&self, name: orco::Symbol, generics: &[orco::Type]) { - self.function_instances - .pin() - .insert((name, generics.to_vec())); - let functions = self.functions.pin(); - let decl = functions - .get(&name) - .unwrap_or_else(|| panic!("function {name} not found")); - - let signature = decl.instantiate(self, generics); - for (_, ty) in &signature.params { - self.register_type(ty); - } - if let Some(ty) = &signature.return_type { - self.register_type(&ty); - } - - let bodies = self.function_bodies.pin(); - let Some(specs) = bodies.get(&name) else { - return; - }; - crate::generics::match_specialization(&specs, generics, self, |body, map| { - for variable in &body.variables { - self.register_type(&variable.ty.copy_instantiate(&map)); - } - - use crate::ir::Expression; - use crate::ir::Place; - use crate::ir::Statement; - fn register_place(store: &crate::Store, place: &Place) { - match place { - Place::Variable(..) => todo!(), - Place::Global(name, generics) => { - store.register_funcion(*name, generics); - } - Place::Deref(expression) => register_expression(store, expression), - Place::Field(place, _) => register_place(store, place), - } - } - - fn register_expression(store: &crate::Store, expression: &Expression) { - match expression { - Expression::IConst(..) - | Expression::UConst(..) - | Expression::FConst(..) - | Expression::BConst(..) => (), - Expression::Read(place) => register_place(store, place), - Expression::Reference(place, _) => register_place(store, place), - Expression::Call(function, args) => { - register_expression(store, function); - for arg in args { - register_expression(store, arg); - } - } - Expression::Intrinsic(intrinsic) => { - use crate::ir::Intrinsic; - match intrinsic { - Intrinsic::Add(a, b) | Intrinsic::Mul(a, b) | Intrinsic::Eq(a, b) => { - register_expression(store, a); - register_expression(store, b); - } - Intrinsic::Not(value) => { - register_expression(store, value); - } - } - } - } - } - - for stmt in &body.statements { - match stmt { - Statement::Comment(..) => (), - Statement::Assign(place, expression) => { - register_place(self, place); - register_expression(self, expression); - } - Statement::Call(function, args) => { - register_expression(self, function); - for arg in args { - register_expression(self, arg); - } - } - Statement::Return(retval) => { - if let Some(expr) = retval { - register_expression(self, expr); - } - } - Statement::Acf(statement) => { - use crate::ir::AcfStatement; - match statement { - AcfStatement::Jump(..) => (), - AcfStatement::Cjump(expression, _) => { - register_expression(self, expression) - } - } - } - Statement::Bcf(statement) => { - use crate::ir::BcfStatement; - match statement { - BcfStatement::Else - | BcfStatement::End - | BcfStatement::Loop - | BcfStatement::Break - | BcfStatement::Continue => (), - BcfStatement::If(expression) - | BcfStatement::Cbreak(expression) - | BcfStatement::Ccontinue(expression) => { - register_expression(self, expression) - } - } - } - } - } - }); - } -} diff --git a/backends/orco-ir/src/forwarding/mod.rs b/backends/orco-ir/src/forwarding/mod.rs deleted file mode 100644 index 460033a..0000000 --- a/backends/orco-ir/src/forwarding/mod.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::ir; -use orco::codegen as oc; - -mod expression; -mod generics; -mod statements; - -impl super::Store { - /// Declare all symbols from this IR in another [`orco::DeclarationBackend`] - pub fn declare(&self, backend: &impl orco::DeclarationBackend) { - for (name, specs) in self.types.pin().iter() { - for (generics, ty) in specs.pin().iter() { - backend.type_(*name, generics.clone(), ty.clone()); - } - } - - for (name, decl) in self.functions.pin().iter() { - backend.function( - *name, - decl.generic_params.clone(), - decl.signature.params.clone(), - decl.signature.return_type.clone(), - decl.signature.attrs.clone(), - ); - } - } - - /// Codegen all functions in another [`orco::CodegenBackend`] - pub fn codegen(&self, backend: &impl orco::CodegenBackend) { - let decls = self.functions.pin(); - for (name, specs) in self.function_bodies.pin().iter() { - let decl = decls - .get(name) - .unwrap_or_else(|| panic!("BUG: unable to find declaration while defining {name}")); - let args = (0..decl.signature.params.len()) - .map(oc::Variable) - .collect::>(); - for (generics, body) in specs.pin().iter() { - body.codegen( - &mut backend.cg_function(*name, generics.clone()), - &args, - crate::generics::TypeMap::new(), - oc::BodyCodegen::return_, - ); - } - } - } - - /// Inline-codegen one function into [`oc::BodyCodegen`] - pub fn inline_call( - &self, - codegen: &mut impl oc::BodyCodegen, - name: orco::Symbol, - generics: &[orco::Type], - args: Vec, - ) -> Option { - // TODO: IMPORTANT! Inline inner function calls and other dependencies on this backend - let decls = self.functions.pin(); - let decl = decls - .get(&name) - .unwrap_or_else(|| panic!("trying to inline an undeclared function {name}")); - let signature = decl.instantiate(self, generics); - - let args = args - .into_iter() - .map(|arg| codegen.mk_tmp(arg)) - .collect::>(); - let retvar = signature.return_type.clone().map(|mut rt| { - rt.instantiate( - &crate::generics::match_type_params(&decl.generic_params, generics, self) - .unwrap_or_else(|| { - panic!( - "generics do not match for {name}{}", - orco::types::fmt_generics(generics) - ) - }), - ); - codegen.declare_var(rt, Some("_retval")) - }); - - use orco::codegen::AcfCodegen; - let return_label = codegen.acf().alloc_label(); - - self.get_function_body(name, generics, |body, map| { - body.codegen(codegen, &args, map, |cg, value| { - if let (Some(retval), Some(value)) = (retvar, value) { - cg.assign(retval.into(), value); - } - cg.acf().jump(return_label); - }); - }); - - codegen.acf().label(return_label); - retvar.map(|rv| codegen.read(rv.into())) - } -} - -/// Context for converting IR to [`oc::BodyCodegen`] calls -struct FwdCtx<'a, CG: oc::BodyCodegen> { - /// The codegen reference - cg: &'a mut CG, - /// Map from type parameters to types - type_map: crate::generics::TypeMap, - /// Map from IR variable indices to codegen variables - variable_map: Vec, - /// Map from IR label indices to codegen labels - label_map: Vec, -} - -impl ir::Body { - /// Codegen this body into another [`oc::BodyCodegen`], - /// mapping all argument variables to `args` (types must be the same). - pub fn codegen( - &self, - codegen: &mut CG, - args: &[oc::Variable], - type_map: crate::generics::TypeMap, - mut codegen_return: impl FnMut(&mut CG, Option), - ) { - let mut ctx = FwdCtx { - cg: codegen, - variable_map: Vec::with_capacity(self.variables.len()), - label_map: Vec::with_capacity(self.labels.len()), - type_map, - }; - - for (idx, variable) in self.variables.iter().enumerate() { - if variable.arg { - ctx.variable_map.push(args[idx]); - } else { - ctx.variable_map.push(ctx.cg.declare_var( - variable.ty.copy_instantiate(&ctx.type_map), - variable.name.as_deref(), - )) - } - } - - use oc::AcfCodegen as _; - let mut statement_idx_to_label = std::collections::HashMap::new(); - for label in &self.labels { - let backend_label = *ctx.label_map.push_mut(ctx.cg.acf().alloc_label()); - statement_idx_to_label.insert(label, backend_label); - } - - for (idx, statement) in self.statements.iter().enumerate() { - if let Some(label) = statement_idx_to_label.get(&idx) { - ctx.cg.acf().label(*label); - } - - if let ir::Statement::Return(expr) = statement { - let expr = expr.as_ref().map(|expr| ctx.expr(expr)); - codegen_return(ctx.cg, expr); - continue; - } - - ctx.stmt(statement); - } - } -} diff --git a/backends/orco-ir/src/forwarding/statements.rs b/backends/orco-ir/src/forwarding/statements.rs deleted file mode 100644 index 2c8af71..0000000 --- a/backends/orco-ir/src/forwarding/statements.rs +++ /dev/null @@ -1,75 +0,0 @@ -use super::{ir, oc}; - -impl super::FwdCtx<'_, CG> { - #[inline] - pub fn label(&self, label: oc::Label) -> oc::Label { - self.label_map[label.0] - } - - /// Codegen [`ir::Statement`] into another [`oc::BodyCodegen`] - pub fn stmt(&mut self, stmt: &ir::Statement) { - match stmt { - ir::Statement::Comment(comment) => self.cg.comment(comment), - ir::Statement::Assign(place, expr) => { - let place = self.place(place); - let expr = self.expr(expr); - self.cg.assign(place, expr) - } - ir::Statement::Call(func, args) => { - let func = self.expr(func); - let args = args.iter().map(|arg| self.expr(arg)).collect(); - if let Some(value) = self.cg.call(func, args) { - self.cg.mk_tmp(value); - } - } - ir::Statement::Return(expr) => { - let value = expr.as_ref().map(|expr| self.expr(expr)); - self.cg.return_(value) - } - - ir::Statement::Acf(acf) => self.acf(acf), - ir::Statement::Bcf(bcf) => self.bcf(bcf), - } - } - - /// Codegen [`ir::AcfStatement`] into another [`oc::BodyCodegen`] - fn acf(&mut self, stmt: &ir::AcfStatement) { - use oc::AcfCodegen as _; - match stmt { - ir::AcfStatement::Jump(label) => { - let label = self.label(*label); - self.cg.acf().jump(label) - } - ir::AcfStatement::Cjump(expr, label) => { - let expr = self.expr(expr); - let label = self.label(*label); - self.cg.acf().cjump(expr, label) - } - } - } - - /// Codegen this statement into another [`oc::BodyCodegen`], - /// mapping all variables and labels (ACF) - fn bcf(&mut self, stmt: &ir::BcfStatement) { - use oc::BcfCodegen as _; - match stmt { - ir::BcfStatement::If(expr) => { - let expr = self.expr(expr); - self.cg.bcf().if_(expr) - } - ir::BcfStatement::Else => self.cg.bcf().else_(), - ir::BcfStatement::End => self.cg.bcf().end(), - ir::BcfStatement::Loop => self.cg.bcf().loop_(), - ir::BcfStatement::Break => self.cg.bcf().break_(), - ir::BcfStatement::Continue => self.cg.bcf().continue_(), - ir::BcfStatement::Cbreak(expr) => { - let expr = self.expr(expr); - self.cg.bcf().cbreak(expr) - } - ir::BcfStatement::Ccontinue(expr) => { - let expr = self.expr(expr); - self.cg.bcf().ccontinue(expr) - } - } - } -} diff --git a/backends/orco-ir/src/generics.rs b/backends/orco-ir/src/generics.rs deleted file mode 100644 index e9b2f4b..0000000 --- a/backends/orco-ir/src/generics.rs +++ /dev/null @@ -1,112 +0,0 @@ -// FIXME: Horrible -use crate::Store; -use orco::Type; - -/// A map from a specialization (`Vec`, each type can hold named params) to the symbol -pub type Specialized = papaya::HashMap, T>; - -/// A type alias for a map from type parameter names to their types. -/// See [`match_ty`] -pub type TypeMap = std::collections::HashMap; - -/// Match generic argument type to parameter type, inferring [`Type::Param`] and -/// writing it into `map` -pub fn match_ty(param: &Type, arg: &Type, map: &mut TypeMap, store: &Store) -> Option<()> { - use Type::*; - let param = store.inline_type_aliases(param.clone()); - let original_arg = arg.clone(); - let arg = store.inline_type_aliases(arg.clone()); - match (param, arg) { - (param @ (Integer(_) | Unsigned(_) | Float(_) | Bool | Char(_)), arg) if arg == param => { - Some(()) - } - (Symbol(..), _) => unreachable!(), - (Array(ty, size), Array(arg_ty, arg_size)) if arg_size == size => { - match_ty(&ty, &arg_ty, map, store) - } - (Struct { fields }, Struct { fields: arg_fields }) if arg_fields.len() == fields.len() => { - for ((name, ty), (arg_name, arg_ty)) in fields.iter().zip(arg_fields.iter()) { - if name != arg_name { - return None; - } - match_ty(ty, arg_ty, map, store)?; - } - Some(()) - } - (Ptr(ty, mutability), Ptr(arg_ty, arg_mutability)) if arg_mutability == mutability => { - match_ty(&ty, &arg_ty, map, store) - } - ( - FnPtr { - params, - return_type, - }, - FnPtr { - params: arg_params, - return_type: arg_return_type, - }, - ) => todo!(), - (Param(name), arg) if !matches!(original_arg, Error) => { - map.insert(name, original_arg.clone()); - Some(()) - } - _ => None, - } -} - -/// Matches a generic to argumens and returns the match map. -/// See [`match_ty`] -pub fn match_type_params(params: &[Type], args: &[Type], store: &Store) -> Option { - if params.len() != args.len() { - return None; - } - - let mut map = TypeMap::new(); - for (param, arg) in params.iter().zip(args.iter()) { - match_ty(param, arg, &mut map, store)? - } - - Some(map) -} - -/// Find a specialization that matches best to set of generic arguments, -/// providing the matched type parameter map. -/// See also: [`match_ty`] -pub fn match_specialization( - specs: &Specialized, - args: &[Type], - store: &Store, - callback: impl FnOnce(&T, TypeMap) -> R, -) -> Option { - let specs = specs.pin(); - let mut best = None; - for (params, spec) in specs.iter() { - let Some(map) = match_type_params(params, args, store) else { - continue; - }; - - if best - .as_ref() - .is_none_or(|(_, best_map): &(_, TypeMap)| map.len() > best_map.len()) - { - best = Some((spec, map)); - } - } - - best.map(|(spec, map)| callback(spec, map)) -} - -impl crate::FunctionDecl { - /// See [Type::instantiate] - pub fn instantiate( - &self, - store: &crate::Store, - generic_args: &[Type], - ) -> orco::types::FunctionSignature { - let map = match_type_params(&self.generic_params, generic_args, store) - .expect("failed to instantiate function decl: generics did not match"); - let mut sig = self.signature.clone(); - sig.instantiate(&map); - sig - } -} diff --git a/backends/orco-ir/src/ir/expressions.rs b/backends/orco-ir/src/ir/expressions.rs deleted file mode 100644 index ce439b7..0000000 --- a/backends/orco-ir/src/ir/expressions.rs +++ /dev/null @@ -1,140 +0,0 @@ -use orco::Type; -use orco::codegen as oc; - -/// Alternate version of [`oc::Place`] that uses -/// [Expression] instead of [`oc::Value`]. -/// See also [`crate::codegen::Codegen::cvt_place`] -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub enum Place { - /// Just variable access - Variable(oc::Variable), - /// Global symbol access, includes generics - Global(orco::Symbol, Vec), - /// Pointer dereference - Deref(Box), - /// Field access, using 0-based field index - Field(Box, usize), -} - -impl Place { - /// Returns type and mutability - pub fn get_type(&self, store: &crate::Store, body: &super::Body) -> (Type, bool) { - match self { - Self::Variable(variable) => { - let variable = body.get_variable(*variable); - (variable.ty.clone(), true) - } - Self::Global(name, generics) => ( - store - .functions - .pin() - .get(name) - .unwrap_or_else(|| panic!("undeclared symbol {name}")) - .instantiate(store, generics) - .ptr_type(), - false, - ), - Self::Deref(expr) => match store.inline_type_aliases(expr.get_type(store, body)) { - Type::Ptr(ty, mutable) => (*ty, mutable), - ty => panic!("trying to dereference non-pointer type {ty}"), - }, - Self::Field(place, idx) => { - let (ty, mutable) = place.get_type(store, body); - ( - match store.inline_type_aliases(ty) { - Type::Struct { mut fields } => fields.swap_remove(*idx).1, - ty => panic!("trying to access field _{idx} on non-struct type {ty}"), - }, - mutable, - ) - } - } - } -} - -impl std::fmt::Display for Place { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Place::Variable(var) => write!(f, "_{}", var.0), - Place::Global(name, generics) => { - write!(f, "{name}{}", orco::types::fmt_generics(generics)) - } - Place::Deref(expr) => write!(f, "*{expr}"), - Place::Field(place, idx) => write!(f, "{place}._{idx}"), - } - } -} - -/// Basic expressions -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub enum Expression { - /// See [`oc::BodyCodegen::iconst`] - IConst(i128, orco::types::IntegerSize), - /// See [`oc::BodyCodegen::uconst`] - UConst(u128, orco::types::IntegerSize), - /// See [`oc::BodyCodegen::fconst`] - FConst(f64, u16), - /// See [`oc::BodyCodegen::fconst`] - BConst(bool), - /// See [`oc::BodyCodegen::read`] - Read(Place), - /// See [`oc::BodyCodegen::reference`] - Reference(Place, bool), - /// See [`oc::BodyCodegen::call`]. - Call(Box, Vec), - - /// See [`oc::BodyCodegen::intrinsics`] - Intrinsic(super::Intrinsic), -} - -impl Expression { - /// Get type of the value this statement produces - pub fn get_type(&self, store: &crate::Store, body: &super::Body) -> Type { - match self { - Self::IConst(_, size) => Type::Integer(*size), - Self::UConst(_, size) => Type::Unsigned(*size), - Self::FConst(_, size) => Type::Float(*size), - Self::BConst(_) => Type::Bool, - Self::Read(place) => place.get_type(store, body).0, - Self::Reference(place, mutable) => { - Type::Ptr(Box::new(place.get_type(store, body).0), *mutable) - } - Self::Call(func, ..) => match func.get_type(store, body) { - Type::FnPtr { return_type, .. } => { - return_type.map_or(Type::Error, |ty| *ty.clone()) - } - _ => Type::Error, - }, - - Self::Intrinsic(intrinsic) => intrinsic.get_type(store, body), - } - } -} - -impl std::fmt::Display for Expression { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::IConst(value, size) => write!(f, "{value} as i{size}")?, - Self::UConst(value, size) => write!(f, "{value} as u{size}")?, - Self::FConst(value, size) => write!(f, "{value} as f{size}")?, - Self::BConst(value) => write!(f, "{value}")?, - Self::Read(place) => write!(f, "{place}")?, - Self::Reference(place, mutable) => { - write!(f, "&{} {place}", if *mutable { "mut" } else { "const" })? - } - Self::Call(func, args) => { - write!(f, "{func}(")?; - for (idx, arg) in args.iter().enumerate() { - if idx > 0 { - write!(f, ", ")?; - } - write!(f, "{arg}")?; - } - write!(f, ")")?; - } - - Self::Intrinsic(intrinsic) => write!(f, "{intrinsic}")?, - } - Ok(()) - } -} diff --git a/backends/orco-ir/src/ir/intrinsics.rs b/backends/orco-ir/src/ir/intrinsics.rs deleted file mode 100644 index f019acb..0000000 --- a/backends/orco-ir/src/ir/intrinsics.rs +++ /dev/null @@ -1,45 +0,0 @@ -use super::Expression; - -/// Intrinsic function calls, see [`oc::Intrinsics`] -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub enum Intrinsic { - /// See [`oc::Intrinsics::add`] - Add(Box, Box), - /// See [`oc::Intrinsics::mul`] - Mul(Box, Box), - /// See [`oc::Intrinsics::eq`] - Eq(Box, Box), - /// See [`oc::Intrinsics::not`] - Not(Box), -} - -impl Intrinsic { - /// Weather this intrinsic produces a return value. - /// Similar to [`super::Statement::is_expression`] - #[must_use] - pub fn is_expression(&self) -> bool { - true - } - - /// Get type of the value this intrinsic produces. - /// Similar to [`super::Statement::get_type`] - pub fn get_type(&self, store: &crate::Store, body: &super::Body) -> orco::Type { - match self { - Self::Add(a, _) => a.get_type(store, body), - Self::Mul(a, _) => a.get_type(store, body), - Self::Eq(a, _) => a.get_type(store, body), - Self::Not(a) => a.get_type(store, body), - } - } -} - -impl std::fmt::Display for Intrinsic { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Intrinsic::Add(a, b) => write!(f, "{a} + {b}"), - Intrinsic::Mul(a, b) => write!(f, "{a} * {b}"), - Intrinsic::Eq(a, b) => write!(f, "{a} == {b}"), - Intrinsic::Not(a) => write!(f, "!{a}"), - } - } -} diff --git a/backends/orco-ir/src/ir/mod.rs b/backends/orco-ir/src/ir/mod.rs deleted file mode 100644 index f8924c5..0000000 --- a/backends/orco-ir/src/ir/mod.rs +++ /dev/null @@ -1,94 +0,0 @@ -mod expressions; -pub use expressions::{Expression, Place}; - -mod statements; -pub use statements::{AcfStatement, BcfStatement, Statement}; - -mod intrinsics; -pub use intrinsics::Intrinsic; - -/// Info about one variable in a body -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Variable { - /// Type of this variable - pub ty: orco::Type, - /// Wether this variable comes from function arguments - pub arg: bool, - /// Debug name - pub name: Option, -} - -/// A function body -#[derive(Clone, Debug, Default, PartialEq, PartialOrd)] -pub struct Body { - /// All variables used in the body. - /// Index this with [`orco::codegen::Variable::0`] - pub variables: Vec, - /// Labels for ACF (see [`orco::codegen::AcfCodegen`]). - /// [`orco::codegen::Label::0`] is an index into this vector, - /// while values are indices into [`Self::statements`] - pub labels: Vec, - /// See [Statement] - pub statements: Vec, -} - -impl Body { - /// Shortcut to access [`Self::variables`] - #[must_use] - pub fn get_variable(&self, variable: orco::codegen::Variable) -> &Variable { - self.variables - .get(variable.0) - .unwrap_or_else(|| panic!("invalid variable _{}", variable.0)) - } -} - -impl std::fmt::Display for Body { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "{{")?; - for (idx, var) in self.variables.iter().enumerate() { - write!(f, " let _{idx}: {}", var.ty)?; - if var.arg { - write!(f, " = ")?; - } - write!(f, ";")?; - if let Some(name) = &var.name { - write!(f, " // {name}")?; - } - writeln!(f)?; - } - - let mut statement_idx_to_label = std::collections::HashMap::new(); - for (idx, label) in self.labels.iter().enumerate() { - statement_idx_to_label.insert(label, idx); - } - - let mut indent = 1; - for (idx, statement) in self.statements.iter().enumerate() { - if let Some(label) = statement_idx_to_label.get(&idx) { - writeln!(f, "label{label}:")?; - } - - if matches!( - statement, - Statement::Bcf(BcfStatement::Else | BcfStatement::End) - ) { - indent -= 1; - } - - for line in statement.to_string().split('\n') { - for _ in 0..indent { - write!(f, " ")?; - } - writeln!(f, "{line}")?; - } - - if matches!( - statement, - Statement::Bcf(BcfStatement::If(..) | BcfStatement::Else | BcfStatement::Loop) - ) { - indent += 1 - } - } - write!(f, "}}") - } -} diff --git a/backends/orco-ir/src/ir/statements.rs b/backends/orco-ir/src/ir/statements.rs deleted file mode 100644 index 76f4775..0000000 --- a/backends/orco-ir/src/ir/statements.rs +++ /dev/null @@ -1,114 +0,0 @@ -use super::{Expression, Place}; -use orco::codegen as oc; - -/// Basic statements -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub enum Statement { - /// See [`oc::BodyCodegen::comment`] - Comment(String), - /// See [`oc::BodyCodegen::assign`] - Assign(Place, Expression), - /// See [`oc::BodyCodegen::call`]. - /// For functions which don't return a value - Call(Expression, Vec), - /// See [`oc::BodyCodegen::return`] - Return(Option), - - /// See [`oc::BodyCodegen::acf`] - Acf(AcfStatement), - /// See [`oc::BodyCodegen::bcf`] - Bcf(BcfStatement), -} - -impl std::fmt::Display for Statement { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Comment(comment) => { - for (idx, line) in comment.split('\n').enumerate() { - if idx > 0 { - writeln!(f)?; - } - write!(f, "// {line}")?; - } - } - Self::Assign(target, value) => write!(f, "{target} = {value};")?, - Self::Call(func, args) => { - write!(f, "{func}(")?; - for (idx, arg) in args.iter().enumerate() { - if idx > 0 { - write!(f, ", ")?; - } - write!(f, "{arg}")?; - } - write!(f, ")")?; - } - Self::Return(value) => { - write!(f, "return")?; - if let Some(value) = value { - write!(f, " {value}")?; - } - write!(f, ";")?; - } - - Self::Acf(acf) => write!(f, "{acf}")?, - Self::Bcf(bcf) => write!(f, "{bcf}")?, - } - Ok(()) - } -} - -/// Arbitrary control flow statements. -/// See [`oc::AcfCodegen`] -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub enum AcfStatement { - /// See [`oc::AcfCodegen::jump`] - Jump(oc::Label), - /// See [`oc::AcfCodegen::cjump`] - Cjump(Expression, oc::Label), -} - -impl std::fmt::Display for AcfStatement { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Jump(label) => write!(f, "jump label{};", label.0), - Self::Cjump(value, label) => write!(f, "jump label{} if {value};", label.0), - } - } -} - -/// Block-like control flow statements (classic, flattened). -/// See [`oc::BcfCodegen`] -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub enum BcfStatement { - /// See [`oc::BcfCodegen::if_`] - If(Expression), - /// See [`oc::BcfCodegen::else_`] - Else, - /// See [`oc::BcfCodegen::end`] - End, - /// See [`oc::BcfCodegen::loop_`] - Loop, - /// See [`oc::BcfCodegen::break`] - Break, - /// See [`oc::BcfCodegen::continue`] - Continue, - /// See [`oc::BcfCodegen::cbreak`] - Cbreak(Expression), - /// See [`oc::BcfCodegen::ccontinue`] - Ccontinue(Expression), -} - -impl std::fmt::Display for BcfStatement { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::If(condition) => write!(f, "if {condition} {{"), - Self::Else => write!(f, "}} else {{"), - Self::End => write!(f, "}}"), - Self::Loop => write!(f, "loop {{"), - Self::Break => write!(f, "break;"), - Self::Continue => write!(f, "continue;"), - Self::Cbreak(value) => write!(f, "break if {value};"), - Self::Ccontinue(value) => write!(f, "continue if {value};"), - } - } -} diff --git a/backends/orco-ir/src/lib.rs b/backends/orco-ir/src/lib.rs deleted file mode 100644 index 66ab1e2..0000000 --- a/backends/orco-ir/src/lib.rs +++ /dev/null @@ -1,184 +0,0 @@ -//! Intermediate representation backend for orco. Does -//! not compile to anything, just a way to store the code. -//! See [Store] -#![warn(missing_docs)] - -/// Intermediate representation for code -pub mod ir; - -// /// Code generation impl -// pub mod codegen; - -/// Utilities to work with generics and specializations -pub mod generics; - -// /// IR forwarding - invoking another backend -// /// to generate code from the IR -// mod forwarding; - -use generics::Specialized; -use papaya::{HashMap, HashSet}; - -/// Function declaration, see [`Store::functions`] -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Function { - #[allow(missing_docs)] - pub generic_params: Vec, - #[allow(missing_docs)] - pub signature: orco::types::FunctionSignature, - /// List of definitions (generic specializations) - pub bodies: Specialized, -} - -/// The heart storage -#[derive(Clone, Debug, Default)] -pub struct Store { - /// Type aliases - pub types: HashMap>, - /// Function declarations - pub functions: HashMap, - - /// List of generic params to monomorphize types - type_instances: HashSet<(orco::Symbol, Vec)>, - /// List of generic params to monomorphize functions - function_instances: HashSet<(orco::Symbol, Vec)>, -} - -impl Store { - #[allow(missing_docs)] - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// If `ty` is a type alias, will be replaced by what is aliased. - /// Inner aliases (f.e. struct field types) are not replaced! - pub fn inline_type_aliases(&self, mut ty: orco::Type) -> orco::Type { - let types = self.types.pin(); - while let orco::Type::Symbol(name, generics) = ty { - let specs = types - .get(&name) - .unwrap_or_else(|| panic!("undeclared type {name}")); - ty = generics::match_specialization(specs, &generics, self, |ty, map| { - let mut ty = ty.clone(); - ty.instantiate(&map); - ty - }) - .unwrap_or_else(move || { - panic!( - "no matching specialization for type {}", - orco::Type::Symbol(name, generics) - ) - }); - } - ty - } - - /// Find a best-matching type for a set of generics - pub fn get_type( - &self, - name: orco::Symbol, - generics: &[orco::Type], - callback: impl FnOnce(&orco::Type, generics::TypeMap), - ) { - let types = self.types.pin(); - let specs = types - .get(&name) - .unwrap_or_else(|| panic!("undeclared type {name}")); - generics::match_specialization(&specs, generics, self, callback).unwrap_or_else(|| { - panic!( - "no matching specialization for {name}{}", - orco::types::fmt_generics(generics) - ) - }) - } - - /// Find a best-matching function body for a set of generics - pub fn get_function_body( - &self, - name: orco::Symbol, - generics: &[orco::Type], - callback: impl FnOnce(&ir::Body, generics::TypeMap), - ) { - let bodies = self.function_bodies.pin(); - let specs = bodies - .get(&name) - .unwrap_or_else(|| panic!("undeclared function {name}")); - generics::match_specialization(&specs, generics, self, callback).unwrap_or_else(|| { - panic!( - "no matching specialization for {name}{}", - orco::types::fmt_generics(generics) - ) - }) - } -} - -impl orco::DeclarationBackend for Store { - fn function( - &self, - name: orco::Symbol, - generic_params: Vec, - params: Vec<(Option, orco::Type)>, - return_type: Option, - attrs: orco::attrs::FunctionAttributes, - ) { - self.functions - .pin() - .try_insert( - name, - FunctionDecl { - generic_params, - signature: orco::types::FunctionSignature { - params, - return_type, - attrs, - }, - }, - ) - .unwrap_or_else(|_| panic!("function {name} is already declared")); - } - - fn type_(&self, name: orco::Symbol, generic_params: Vec, ty: orco::Type) { - self.types - .pin() - .get_or_insert_with(name, Default::default) - .pin() - .try_insert(generic_params, ty) - .unwrap_or_else(|_| panic!("type {name} is already declared")); - } -} - -impl std::fmt::Display for Store { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for (name, specs) in self.types.pin().iter() { - for (spec, ty) in specs.pin().iter() { - writeln!(f, "type {name}{} = {ty};", orco::types::fmt_generics(spec))?; - } - } - - writeln!(f)?; - - let bodies = self.function_bodies.pin(); - for (name, decl) in self.functions.pin().iter() { - writeln!( - f, - "{}fn {name}{}{};", - decl.signature.attrs, - orco::types::fmt_generics(&decl.generic_params), - decl.signature, - )?; - - let Some(defs) = bodies.get(name) else { - continue; - }; - - for (spec, body) in defs.pin().iter() { - writeln!(f, "for {} {body}", orco::types::fmt_generics(spec)).unwrap(); - } - - writeln!(f)?; - } - - Ok(()) - } -} diff --git a/frontends/orco-rustc/Cargo.toml b/frontends/orco-rustc/Cargo.toml index d0319a2..cab1db0 100644 --- a/frontends/orco-rustc/Cargo.toml +++ b/frontends/orco-rustc/Cargo.toml @@ -11,5 +11,4 @@ rustc_private = true [dependencies] orco.workspace = true -orco-ir.workspace = true orco-cgen.workspace = true diff --git a/frontends/orco-rustc/samples/minimal.rs b/frontends/orco-rustc/samples/minimal.rs new file mode 100644 index 0000000..b6b62f2 --- /dev/null +++ b/frontends/orco-rustc/samples/minimal.rs @@ -0,0 +1,5 @@ +pub fn f() -> i32 { + 0 +} + +pub fn main() {} diff --git a/frontends/orco-rustc/samples/structs.rs b/frontends/orco-rustc/samples/structs.rs index 5ecc4d6..95b270f 100644 --- a/frontends/orco-rustc/samples/structs.rs +++ b/frontends/orco-rustc/samples/structs.rs @@ -2,11 +2,14 @@ #![allow(dead_code)] // #[derive(Debug)] -struct Person { - name: String, +struct Person { + // name: String, + name: (T, u8), age: u8, } +type PersonI32 = Person; + // A unit struct struct Unit; diff --git a/frontends/orco-rustc/src/codegen/mod.rs b/frontends/orco-rustc/src/codegen/mod.rs index 9756817..e1b3d50 100644 --- a/frontends/orco-rustc/src/codegen/mod.rs +++ b/frontends/orco-rustc/src/codegen/mod.rs @@ -1,36 +1,31 @@ use crate::TyCtxt; -use orco::codegen as oc; -use orco::codegen::AcfCodegen as _; +use ir::{Instr, Intrinsic}; +use orco::ir; use std::collections::HashMap; mod operand; -struct CodegenCtx<'a, 'tcx: 'a, B, CG> { - tcx: TyCtxt<'tcx>, - backend: &'a B, - codegen: CG, - body: &'a rustc_middle::mir::Body<'tcx>, - map: crate::types::GenericMap<'a>, - variables: HashMap>, - labels: HashMap, +struct CodegenCtx<'tcx, 'a> { + ctx: super::Context<'tcx, 'a>, + ir_body: ir::Body, + rs_body: &'a rustc_middle::mir::Body<'tcx>, + variables: HashMap, } -impl<'tcx, B: orco::DeclarationBackend<'tcx>, CG: oc::BodyCodegen> CodegenCtx<'_, 'tcx, B, CG> { - fn generic_name( - &self, - key: rustc_hir::def_id::DefId, - args: &rustc_middle::ty::GenericArgs, - ) -> orco::Symbol { - crate::names::generic_name(self.tcx, self.backend, key, self.map, args) +impl<'tcx, 'a> std::ops::Deref for CodegenCtx<'tcx, 'a> { + type Target = super::Context<'tcx, 'a>; + + fn deref(&self) -> &Self::Target { + &self.ctx } +} - fn convert_ty(&self, ty: rustc_middle::ty::Ty) -> Option { - crate::types::convert(self.tcx, self.backend, ty, self.map) +impl<'tcx> CodegenCtx<'tcx, '_> { + fn instr(&mut self, instr: impl Into) { + self.ir_body.instructions.push(instr.into()); } fn codegen_statement(&mut self, stmt: &rustc_middle::mir::Statement<'tcx>) { - // self.codegen.comment(&format!("{stmt:#?}")); - use rustc_middle::mir::StatementKind; let (place, rvalue) = match &stmt.kind { StatementKind::Assign(assign) => assign.as_ref(), @@ -42,13 +37,18 @@ impl<'tcx, B: orco::DeclarationBackend<'tcx>, CG: oc::BodyCodegen> CodegenCtx<'_ return; } }; + let is_unit = place.ty(self.rs_body, self.tcx).ty.is_unit(); use rustc_middle::mir::Rvalue; match rvalue { Rvalue::Use(op, _) => { - if let (Some(place), Some(value)) = (self.place(*place), self.op(op)) { - self.codegen.assign(place, value); + if is_unit { + return; } + + self.instr(Instr::Assign); + self.place(*place); + self.op(op); } Rvalue::Aggregate(kind, fields) => { use rustc_middle::mir::AggregateKind as AK; @@ -56,16 +56,17 @@ impl<'tcx, B: orco::DeclarationBackend<'tcx>, CG: oc::BodyCodegen> CodegenCtx<'_ AK::Array(..) => todo!(), AK::Tuple => { for (idx, op) in fields.iter_enumerated() { - let place = place.project_deeper( - &[rustc_middle::mir::PlaceElem::Field( - idx, - op.ty(&self.body.local_decls, self.tcx), - )], - self.tcx, - ); - if let (Some(place), Some(value)) = (self.place(place), self.op(op)) { - self.codegen.assign(place, value); + let ty = op.ty(&self.rs_body.local_decls, self.tcx); + if ty.is_unit() { + continue; } + + self.instr(Instr::Assign); + self.place(place.project_deeper( + &[rustc_middle::mir::PlaceElem::Field(idx, ty)], + self.tcx, + )); + self.op(op); } } AK::Adt(key, variant, ..) => { @@ -73,16 +74,23 @@ impl<'tcx, B: orco::DeclarationBackend<'tcx>, CG: oc::BodyCodegen> CodegenCtx<'_ let variant = &adt.variants()[*variant]; for (idx, op) in fields.iter_enumerated() { let field = &variant.fields[idx]; + let ty = self + .tcx + .type_of(field.did) + .instantiate_identity() + .skip_norm_wip(); + if ty.is_unit() { + continue; + } + let place = place.project_deeper( - &[rustc_middle::mir::PlaceElem::Field( - idx, - self.tcx.type_of(field.did).skip_binder(), // TODO: Generics?!!! - )], + &[rustc_middle::mir::PlaceElem::Field(idx, ty)], self.tcx, ); - if let (Some(place), Some(value)) = (self.place(place), self.op(op)) { - self.codegen.assign(place, value); - } + + self.instr(Instr::Assign); + self.place(place); + self.op(op); } } AK::Closure(..) => todo!(), @@ -92,72 +100,98 @@ impl<'tcx, B: orco::DeclarationBackend<'tcx>, CG: oc::BodyCodegen> CodegenCtx<'_ } } Rvalue::BinaryOp(op, operands) => { - let params: Vec<_> = self - .op(&operands.0) - .into_iter() - .chain(self.op(&operands.1)) - .collect(); - - let ty = operands.0.ty(self.body, self.tcx).to_string(); - let value = crate::intrinsics().inline_call( - &mut self.codegen, - format!("__{op:?}#{ty}").into(), - params, - ); - if let (Some(place), Some(value)) = (self.place(*place), value) { - self.codegen.assign(place, value); - } + // let params: Vec<_> = self + // .op(&operands.0) + // .into_iter() + // .chain(self.op(&operands.1)) + // .collect(); + + // let ty = operands.0.ty(self.rs_body, self.tcx).to_string(); + // let value = crate::intrinsics().inline_call( + // &mut self.codegen, + // format!("__{op:?}#{ty}").into(), + // params, + // ); + // if let (Some(place), Some(value)) = (self.place(*place), value) { + // self.codegen.assign(place, value); + // } } - _ => self.codegen.comment(&format!("TODO: {stmt:?}")), // TODO + _ => println!("TODO: {stmt:?}"), // TODO } } - fn codegen_block(&mut self, block: rustc_middle::mir::BasicBlock) { - self.codegen.acf().label(self.labels[&block]); - let block = &self.body[block]; + /// Codegen a basic block, inserting a label to it. + /// Previous and next blocks are needed for optimization of jumps. + fn codegen_block( + &mut self, + block: rustc_middle::mir::BasicBlock, + prev: Option, + next: Option, + ) { + let predecessors = self.rs_body.basic_blocks.predecessors(); + type Pred<'a> = &'a [rustc_middle::mir::BasicBlock]; + if &*predecessors[block] != prev.as_ref().map_or::(&[], core::slice::from_ref) { + self.instr(Instr::AcfLabel(ir::LabelId(block.as_u32()))); + } + let block = &self.rs_body[block]; for stmt in &block.statements { self.codegen_statement(stmt); } - // self.codegen.comment(&format!("{:#?}", block.terminator())); + let next_block = move |this: &mut Self, block| { + if next != Some(block) { + this.instr(Instr::AcfJump(ir::LabelId(block.as_u32()))); + } + }; + use rustc_middle::mir::TerminatorKind; match &block.terminator().kind { - TerminatorKind::Goto { target } => self.codegen.acf().jump(self.labels[target]), + TerminatorKind::Goto { target } => next_block(self, *target), TerminatorKind::SwitchInt { discr, targets } => { - use oc::Intrinsics as _; for (value, target) in targets.iter() { - let discr = self.op(discr).expect("SwitchInt on unit discriminant"); - let value = match self.codegen.type_of(discr.0) { - orco::Type::Integer(is) => self.codegen.iconst(value as _, is), - orco::Type::Unsigned(is) => self.codegen.uconst(value as _, is), + self.instr(Instr::AcfCJump(ir::LabelId(target.as_u32()))); + self.instr(Intrinsic::Eq); + + let idx = self.ir_body.instructions.len(); + self.op(discr); + match self.ir_body.value_ty(idx) { + orco::Type::Integer(is) => self.instr(Instr::IConst(value as _, is)), + orco::Type::Unsigned(is) => self.instr(Instr::UConst(value as _, is)), orco::Type::Bool => { assert!( [0, 1].contains(&value), "invalid bool branch in SwitchInt: {value} (expected 0 or 1)" ); - self.codegen.bconst(value != 0) + self.instr(Instr::BConst(value != 0)) } - orco::Type::Symbol(name) => { + orco::Type::Symbol(name, _) => { todo!("symbol discriminant type in SwitchInt ({name})") } ty => panic!("invalid discriminant type in SwitchInt: {ty}"), - }; - let condition = self.codegen.intrinsics().eq(discr, value); - self.codegen.acf().cjump(condition, self.labels[&target]); + } } - self.codegen.acf().jump(self.labels[&targets.otherwise()]); + + next_block(self, targets.otherwise()) } TerminatorKind::UnwindResume => (), TerminatorKind::UnwindTerminate(..) => todo!(), TerminatorKind::Return => { - let value = self.variables[&rustc_middle::mir::RETURN_PLACE] - .map(|var| self.codegen.read(var.into())); - self.codegen.return_(value) + let value = self + .variables + .get(&rustc_middle::mir::RETURN_PLACE) + .copied(); + if next.is_none() && value.is_none() { + return; // TODO: Idk if it's useful or not + } + self.instr(Instr::Return(value.is_some())); + if let Some(value) = value { + self.instr(Instr::Var(value)); + } } TerminatorKind::Unreachable => todo!(), TerminatorKind::Drop { target, .. } => { - self.codegen.acf().jump(self.labels[target]); + self.instr(Instr::AcfJump(ir::LabelId(target.as_u32()))); // TODO } TerminatorKind::Call { @@ -167,28 +201,31 @@ impl<'tcx, B: orco::DeclarationBackend<'tcx>, CG: oc::BodyCodegen> CodegenCtx<'_ target, .. } => { - let func = self.op(func).expect("trying to call a unit value"); - let args = args.iter().filter_map(|arg| self.op(&arg.node)).collect(); - let retval = self.codegen.call(func, args); - if let Some(place) = self.place(*destination) { - self.codegen.assign( - place, - retval.expect("can't use the return value of a unit function"), - ); + if !destination.ty(self.rs_body, self.tcx).ty.is_unit() { + self.instr(Instr::Assign); + self.place(*destination); + } + self.instr(Instr::Call(args.len() as _)); // TODO: Check for unit args + self.op(func); + for arg in args { + self.op(&arg.node); } + if let Some(target) = target { - self.codegen.acf().jump(oc::Label(target.index())); + next_block(self, *target); } } TerminatorKind::TailCall { func, args, .. } => { - let func = self.op(func).expect("trying to call a unit value"); - let args = args.iter().filter_map(|arg| self.op(&arg.node)).collect(); - let retval = self.codegen.call(func, args); - self.codegen.return_(retval); + self.instr(Instr::Return(!self.rs_body.return_ty().is_unit())); + self.instr(Instr::Call(args.len() as _)); // TODO: Check for unit args + self.op(func); + for arg in args { + self.op(&arg.node); + } } TerminatorKind::Assert { target, .. } => { - self.codegen.acf().jump(self.labels[target]); // TODO + next_block(self, *target); } TerminatorKind::Yield { .. } => todo!(), TerminatorKind::CoroutineDrop => todo!(), @@ -201,149 +238,139 @@ impl<'tcx, B: orco::DeclarationBackend<'tcx>, CG: oc::BodyCodegen> CodegenCtx<'_ /// Codegen a body /// Note: Generates dirty code, not meant to be human-readable -pub fn body<'a, 'tcx: 'a>( - tcx: TyCtxt<'tcx>, - backend: &impl orco::DeclarationBackend<'tcx>, - codegen: impl oc::BodyCodegen, - body: &'a rustc_middle::mir::Body<'tcx>, - map: crate::types::GenericMap, -) { +pub fn body<'tcx>( + ctx: super::Context<'tcx, '_>, + ir_body: ir::Body, + rs_body: &rustc_middle::mir::Body<'tcx>, +) -> ir::Body { let mut ctx = CodegenCtx { - tcx, - backend, - codegen, - body, - map, - variables: HashMap::with_capacity(body.local_decls.len()), - labels: HashMap::with_capacity(body.basic_blocks.len()), + ctx, + ir_body, + rs_body, + variables: HashMap::new(), }; - let mut local_names = HashMap::new(); - for info in &body.var_debug_info { + for (idx, local) in rs_body.local_decls.iter_enumerated() { + let var = if (1..rs_body.arg_count + 1).contains(&idx.index()) { + // An argument + Some(ir::VariableId(idx.index() as u32 - 1)) + } else { + ctx.convert_ty(local.ty) + .map(|ty| ctx.ir_body.declare_var(ty, None)) + }; + + if let Some(var) = var { + ctx.variables.insert(idx, var); + } + } + + for info in &rs_body.var_debug_info { use rustc_middle::mir::VarDebugInfoContents as VDIC; match info.value { VDIC::Place(place) => { - if !place.projection.is_empty() && local_names.contains_key(&place.local) { + let var = ctx.ir_body.var_mut(ctx.variables[&place.local]); + if !place.projection.is_empty() && var.name.is_some() { continue; } - local_names.insert(place.local, info.name); + var.name = Some(info.name.to_string()); } VDIC::Const(..) => (), } } - for (idx, local) in body.local_decls.iter_enumerated() { - let var = if (1..body.arg_count + 1).contains(&idx.index()) { - // An argument - Some(oc::Variable(idx.index() - 1)) - } else if !local.ty.is_unit() { - ctx.convert_ty(local.ty).map(|ty| { - ctx.codegen - .declare_var(ty, local_names.get(&idx).map(rustc_span::Symbol::as_str)) - }) - } else { - None - }; - ctx.variables.insert(idx, var); + for _ in rs_body.basic_blocks.indices() { + ctx.ir_body.alloc_label(Some("bb".to_owned())); } - for idx in body.basic_blocks.indices() { - ctx.labels.insert(idx, ctx.codegen.acf().alloc_label()); + let blocks = rs_body.basic_blocks.reverse_postorder(); + let mut prev = None; + for (idx, &block) in blocks.iter().enumerate() { + let next = blocks.get(idx + 1).copied(); + ctx.codegen_block(block, prev, next); + prev = Some(block); } - for block in body.basic_blocks.reverse_postorder() { - ctx.codegen_block(*block); - } + ctx.ir_body } -pub fn cg_function<'tcx, B>(tcx: TyCtxt<'tcx>, backend: &B, key: DefId) -where - B: orco::DeclarationBackend + orco::CodegenBackend, -{ - crate::types::wrap_generics( - tcx, - backend, - key.into(), - key.into(), - "cg_", - move |tcx, backend, name, map| { - if map.generic() { - backend.invoke_macro(crate::names::convert_path(tcx, key), map.args()); - } - body( - tcx, - backend, - backend.cg_function(name), - tcx.optimized_mir(key), - map, - ); - }, - ) +/// Codegen a single function by key, inserting it's body into the module +pub fn cg_function(ctx: super::Context, key: rustc_hir::def_id::DefId) { + let functions = ctx.module.functions.pin(); + let path = ctx.convert_path(key); + let function = functions + .get(&path) + .unwrap_or_else(|| panic!("trying to define an undeclared function {path}")); + let ir_body = body(ctx, function.create_def(), ctx.tcx.optimized_mir(key)); + function + .body + .set(ir_body) + .unwrap_or_else(|_| panic!("trying to define function {path} twice")); } /// Codegen all the functions using the backend provided. /// See [`crate::declare`] -pub fn codegen<'a, B>(tcx: TyCtxt<'a>, backend: &B, items: &rustc_middle::hir::ModuleItems) -where - B: oc::CodegenBackend + orco::DeclarationBackend, -{ - let backend = rustc_data_structures::sync::IntoDynSyncSend(backend); +pub fn codegen(tcx: TyCtxt, module: &orco::Module, items: &rustc_middle::hir::ModuleItems) { + let module = rustc_data_structures::sync::IntoDynSyncSend(module); items .par_items(|item| { let item = tcx.hir_item(item); + let ctx = super::Context { + tcx, + module: *module, + }; let key = item.owner_id.def_id; use rustc_hir::ItemKind as IK; match item.kind { IK::Static(..) => (), IK::Const(..) => (), - IK::Fn { .. } => cg_func(tcx, backend, key), + IK::Fn { .. } => cg_function(ctx, key.to_def_id()), IK::GlobalAsm { .. } => todo!("global_asm!"), IK::Impl(impl_) if let Some(trait_) = impl_.of_trait => { - let Some(trait_key) = trait_.trait_ref.trait_def_id() else { + let Some(_trait_key) = trait_.trait_ref.trait_def_id() else { panic!("[bug?] trait impl of a non-trait?!"); }; - // TODO: Generics - let map = tcx.impl_item_implementor_ids(key); - for item in tcx.associated_items(trait_key).in_definition_order() { - let (impl_key, is_default_impl) = map - .get(&item.def_id) - .map_or((item.def_id, true), |key| (*key, false)); - let mut name = crate::names::convert_path(tcx, item.def_id); - let trait_name = name.as_str().into(); - - let self_ty = crate::types::convert( - tcx, - backend, - tcx.type_of(key).instantiate_identity().skip_norm_wip(), - crate::types::GenericMap::default(), - ); - if let Some(ty) = &self_ty { - name.push('_'); - name.push_str(&ty.hashable_name()); - } + // // TODO: Generics + // let map = tcx.impl_item_implementor_ids(key); + // for item in tcx.associated_items(trait_key).in_definition_order() { + // let (impl_key, is_default_impl) = map + // .get(&item.def_id) + // .map_or((item.def_id, true), |key| (*key, false)); + // let mut name = crate::names::convert_path(tcx, item.def_id); + // let trait_name = name.as_str().into(); - let trait_generic_args = self_ty.into_iter().collect::>(); - backend.invoke_macro(trait_name, &trait_generic_args); - let map = if is_default_impl { - crate::types::GenericMap(1, &trait_generic_args) - } else { - crate::types::GenericMap::default() - }; - - body( - tcx, - backend, - backend.cg_function(name.into()), - tcx.optimized_mir(impl_key), - map, - ); - } + // let self_ty = crate::types::convert( + // tcx, + // backend, + // tcx.type_of(key).instantiate_identity().skip_norm_wip(), + // crate::types::GenericMap::default(), + // ); + // if let Some(ty) = &self_ty { + // name.push('_'); + // name.push_str(&ty.hashable_name()); + // } + + // let trait_generic_args = self_ty.into_iter().collect::>(); + // backend.invoke_macro(trait_name, &trait_generic_args); + // let map = if is_default_impl { + // crate::types::GenericMap(1, &trait_generic_args) + // } else { + // crate::types::GenericMap::default() + // }; + + // body( + // tcx, + // backend, + // backend.cg_function(name.into()), + // tcx.optimized_mir(impl_key), + // map, + // ); + // } } IK::Impl(impl_) => { for item in impl_.items { - cg_func(tcx, backend, item.owner_id.to_def_id()); + cg_function(ctx, item.owner_id.to_def_id()); } } _ => (), diff --git a/frontends/orco-rustc/src/codegen/operand.rs b/frontends/orco-rustc/src/codegen/operand.rs index 967e7a0..24a831e 100644 --- a/frontends/orco-rustc/src/codegen/operand.rs +++ b/frontends/orco-rustc/src/codegen/operand.rs @@ -1,13 +1,12 @@ -use super::{CodegenCtx, oc}; +use super::{CodegenCtx, Instr}; -impl<'tcx, B: orco::DeclarationBackend<'tcx>, CG: oc::BodyCodegen> CodegenCtx<'_, 'tcx, B, CG> { - pub(super) fn place(&mut self, place: rustc_middle::mir::Place<'tcx>) -> Option { - let mut res = oc::Place::Variable(self.variables[&place.local]?); - for (_, proj) in place.iter_projections() { +impl CodegenCtx<'_, '_> { + pub(super) fn place(&mut self, place: rustc_middle::mir::Place) { + for (_, proj) in place.iter_projections().rev() { use rustc_middle::mir::ProjectionElem as PE; match proj { - PE::Deref => res = oc::Place::Deref(self.codegen.read(res)), - PE::Field(field, _) => res = oc::Place::Field(Box::new(res), field.index()), + PE::Deref => todo!(), + PE::Field(field, _) => self.instr(Instr::Field(field.as_u32())), PE::Index(_) => todo!(), PE::ConstantIndex { .. } => todo!(), PE::Subslice { .. } => todo!(), @@ -16,79 +15,91 @@ impl<'tcx, B: orco::DeclarationBackend<'tcx>, CG: oc::BodyCodegen> CodegenCtx<'_ PE::UnwrapUnsafeBinder(..) => todo!(), } } - Some(res) + + self.variables + .get(&place.local) + .copied() + .map(|var| self.instr(Instr::Var(var))); } - pub(super) fn op(&mut self, op: &rustc_middle::mir::Operand<'tcx>) -> Option { + fn constant(&mut self, value: rustc_middle::mir::ConstValue, ty: rustc_middle::ty::Ty) { use rustc_const_eval::interpret::Scalar; - use rustc_middle::mir::{Const, ConstValue, Operand}; - Some(match op { - Operand::Copy(place) | Operand::Move(place) => { - let place = self.place(*place)?; - self.codegen.read(place) - } - Operand::Constant(value) => { - let (value, ty) = match value.const_ { - Const::Ty(..) => todo!(), - Const::Unevaluated(uc, ..) => { - panic!("unevaluated const encountered ({uc:?})") - } - Const::Val(value, ty) => (value, ty), - }; - // TODO: Handle chars & bools - match value { - ConstValue::Scalar(scalar) => match scalar { - Scalar::Int(value) => { - if ty.is_floating_point() { - self.codegen.fconst( - match value.size().bytes() { - 4 => f32::from_bits(value.to_u32()).into(), - 8 => f64::from_bits(value.to_u64()) as _, - sz => panic!( - "invalid or unsupported floating point literal size: {sz}" - ), - }, - value.size().bits() as _, - ) - } else if ty.is_signed() { - self.codegen.iconst( - value.to_int(value.size()), - if ty.is_ptr_sized_integral() { - orco::types::IntegerSize::Size - } else { - orco::types::IntegerSize::Bits(value.size().bits() as _) - }, - ) - } else { - self.codegen.uconst( - value.to_uint(value.size()), - if ty.is_ptr_sized_integral() { - orco::types::IntegerSize::Size - } else { - orco::types::IntegerSize::Bits(value.size().bits() as _) - }, - ) + use rustc_middle::mir::ConstValue; + use rustc_middle::ty::TyKind; + + // TODO: Handle chars & bools + match value { + ConstValue::Scalar(Scalar::Int(value)) => { + if ty.is_floating_point() { + self.instr(Instr::FConst( + match value.size().bytes() { + 4 => f32::from_bits(value.to_u32()).into(), + 8 => f64::from_bits(value.to_u64()) as _, + sz => { + panic!("invalid or unsupported floating point literal size: {sz}") } - } - Scalar::Ptr(..) => todo!(), - }, - ConstValue::ZeroSized => match ty.kind() { - // TODO: We might need to do more - // TODO: Generics - rustc_middle::ty::TyKind::FnDef(func, generics) => self - .codegen - .read(oc::Place::Global(self.generic_name(*func, generics))), - rustc_middle::ty::TyKind::Adt(..) => { - let var = self.codegen.declare_var(self.convert_ty(ty)?, Some("zst")); - self.codegen.read(var.into()) - } - _ => panic!("Unknown zero-sized const {op:?}"), - }, - ConstValue::Slice { .. } => todo!(), - ConstValue::Indirect { .. } => todo!(), + }, + value.size().bits() as _, + )); + } else if ty.is_signed() { + self.ir_body.int_literal( + value.to_int(value.size()), + if ty.is_ptr_sized_integral() { + orco::types::IntegerSize::Size + } else { + orco::types::IntegerSize::Bits(value.size().bits() as _) + }, + ); + } else { + self.ir_body.uint_literal( + value.to_uint(value.size()), + if ty.is_ptr_sized_integral() { + orco::types::IntegerSize::Size + } else { + orco::types::IntegerSize::Bits(value.size().bits() as _) + }, + ); } } + ConstValue::Scalar(Scalar::Ptr(..)) => todo!(), + ConstValue::ZeroSized => match ty.kind() { + // TODO: We might need to do more + // TODO: Generics + TyKind::FnDef(func, generics) => { + let symbol = self.ir_body.use_symbol( + self.convert_path(*func), + self.convert_generic_args(generics.skip_binder()), + self.module, + ); + self.instr(Instr::Global(symbol)); + } + TyKind::Adt(..) => { + self.convert_ty(ty).map(|ty| { + let var = self.ir_body.declare_var(ty, Some("zst".to_owned())); + self.instr(Instr::Var(var)); + }); + } + _ => panic!("Unknown zero-sized const {value:?}"), + }, + ConstValue::Slice { .. } => todo!(), + ConstValue::Indirect { .. } => todo!(), + } + } + + pub(super) fn op(&mut self, op: &rustc_middle::mir::Operand) { + use rustc_middle::mir::{Const, Operand}; + match op { + Operand::Copy(place) | Operand::Move(place) => { + self.place(*place); + } + Operand::Constant(value) => match value.const_ { + Const::Ty(..) => todo!(), + Const::Unevaluated(uc, ..) => { + panic!("unevaluated const encountered ({uc:?})") + } + Const::Val(value, ty) => self.constant(value, ty), + }, Operand::RuntimeChecks(..) => todo!(), - }) + } } } diff --git a/frontends/orco-rustc/src/lib.rs b/frontends/orco-rustc/src/lib.rs index b134323..df948c9 100644 --- a/frontends/orco-rustc/src/lib.rs +++ b/frontends/orco-rustc/src/lib.rs @@ -39,23 +39,15 @@ pub use codegen::codegen; /// Base context for all declaration/codegen operations #[allow(missing_docs)] -pub struct Context<'tcx, 'b, B> { +#[derive(Clone, Copy)] +pub struct Context<'tcx, 'module> { pub tcx: TyCtxt<'tcx>, - pub backend: &'b B, + pub module: &'module orco::Module, } -impl Copy for Context<'_, '_, B> {} -impl Clone for Context<'_, '_, B> { - fn clone(&self) -> Self { - Self { - tcx: self.tcx, - backend: self.backend, - } - } -} - -impl Context<'_, '_, B> { +impl Context<'_, '_> { /// Shorthand for calling [`names::convert_path`] + #[inline(always)] pub fn convert_path( self, key: impl rustc_middle::query::IntoQueryKey, @@ -64,32 +56,36 @@ impl Context<'_, '_, B> { } /// Shorthand for calling [`types::convert`] + #[inline(always)] pub fn convert_ty(self, ty: rustc_middle::ty::Ty) -> Option { types::convert(self.tcx, ty) } /// Shorthand for calling [`types::convert_generic_params`] + #[inline(always)] pub fn convert_generics( self, key: impl rustc_middle::query::IntoQueryKey, - ) -> Vec { + ) -> Vec { types::convert_generic_params(self.tcx, key.into_query_key()) } + + /// Shorthand for calling [`types::convert_generic_args`] + pub fn convert_generic_args(self, args: &rustc_middle::ty::GenericArgs) -> Vec { + types::convert_generic_args(self.tcx, args) + } } /// Declare all the items using the backend provided. /// See [`TyCtxt::hir_crate_items`] -pub fn declare(tcx: TyCtxt, backend: &B, items: &rustc_middle::hir::ModuleItems) -where - B: orco::DeclarationBackend + Send + Sync, -{ - let backend = rustc_data_structures::sync::IntoDynSyncSend(backend); +pub fn declare(tcx: TyCtxt, module: &orco::Module, items: &rustc_middle::hir::ModuleItems) { + let module = rustc_data_structures::sync::IntoDynSyncSend(module); items .par_items(|item| { let item = tcx.hir_item(item); let ctx = Context { tcx, - backend: *backend, + module: *module, }; use rustc_hir::ItemKind as IK; @@ -104,23 +100,38 @@ where IK::Mod(..) => (), IK::ForeignMod { .. } => (), IK::GlobalAsm { .. } => (), - IK::TyAlias(..) => (), + IK::TyAlias(..) => { + if let Some(ty) = ctx.convert_ty( + ctx.tcx + .type_of(item.owner_id) + .instantiate_identity() + .skip_norm_wip(), + ) { + ctx.module.types.pin().insert( + ctx.convert_path(item.owner_id), + orco::TypeAlias { + generics: ctx.convert_generics(item.owner_id), + type_: ty, + }, + ); + } + } IK::Enum(..) => (), IK::Struct(..) => ctx.struct_(item.owner_id.to_def_id()), IK::Union(..) => (), IK::Trait { items, .. } => { - for item in items { - use rustc_hir::TraitItemKind as TIK; - match ctx.tcx.hir_trait_item(*item).kind { - TIK::Fn(_, rustc_hir::TraitFn::Required(idents)) => { - ctx.function_decl(item.owner_id.to_def_id(), idents) - } - TIK::Fn(_, rustc_hir::TraitFn::Provided(..)) => { - ctx.function(item.owner_id.def_id) - } - _ => (), - } - } + // for item in items { + // use rustc_hir::TraitItemKind as TIK; + // match ctx.tcx.hir_trait_item(*item).kind { + // TIK::Fn(_, rustc_hir::TraitFn::Required(idents)) => { + // ctx.function_decl(item.owner_id.to_def_id(), idents) + // } + // TIK::Fn(_, rustc_hir::TraitFn::Provided(..)) => { + // ctx.function(item.owner_id.def_id) + // } + // _ => (), + // } + // } } IK::TraitAlias(..) => (), IK::Impl(..) => (), @@ -138,7 +149,7 @@ where let ctx = Context { tcx, - backend: *backend, + module: *module, }; use rustc_hir::ImplItemKind as IIK; @@ -158,7 +169,7 @@ where let item = tcx.hir_foreign_item(item); let ctx = Context { tcx, - backend: *backend, + module: *module, }; use rustc_hir::ForeignItemKind as FIK; diff --git a/frontends/orco-rustc/src/rustc_backend.rs b/frontends/orco-rustc/src/rustc_backend.rs index 11daec5..9a01c99 100644 --- a/frontends/orco-rustc/src/rustc_backend.rs +++ b/frontends/orco-rustc/src/rustc_backend.rs @@ -24,14 +24,23 @@ impl rustc_codegen_ssa::traits::CodegenBackend for OrcoCodegenBackend { // rustc_middle::mir::write_mir_pretty(tcx, &mut std::io::stdout()).unwrap(); let items = tcx.hir_crate_items(()); - let ir = orco_ir::Store::new(); - crate::declare(tcx, &ir, items); - // crate::codegen(tcx, &ir, items); - - let backend = orco_cgen::Backend::new(); - ir.monomorphize(); - ir.declare_mono(&backend); - print!("{backend}"); + let module = orco::Module::new(); + // module.functions.pin().insert( + // "core::mem::drop".into(), + // orco::Function { + // generics: vec!["T".into()], + // params: vec![(None, orco::Type::Param("T".into()))], + // return_type: None, + // attrs: Default::default(), + // body: std::sync::OnceLock::new(), + // }, + // ); + crate::declare(tcx, &module, items); + crate::codegen(tcx, &module, items); + module.monomorphize(); + module.name_anonymous_structs(); + print!("{module}"); + print!("{}", orco_cgen::FmtModule(&module)); std::process::exit(0) } diff --git a/frontends/orco-rustc/src/symbols.rs b/frontends/orco-rustc/src/symbols.rs index 954d20a..6850dfe 100644 --- a/frontends/orco-rustc/src/symbols.rs +++ b/frontends/orco-rustc/src/symbols.rs @@ -14,10 +14,7 @@ fn convert_fn_attrs( } } -impl crate::Context<'_, '_, B> -where - B: orco::DeclarationBackend, -{ +impl crate::Context<'_, '_> { /// Declare a function from MIR by [`rustc_hir::def_id::LocalDefId`]. /// The function MUST have a body. For bodyless functions, see [`Self::function_decl`] pub fn function(self, key: rustc_hir::def_id::LocalDefId) { @@ -34,12 +31,16 @@ where params.push((name, ty)); } - self.backend.function( + self.module.functions.pin().insert( self.convert_path(key), - self.convert_generics(key), - params, - self.convert_ty(sig.output()), - attrs.clone(), + orco::Function { + generics: self.convert_generics(key), + type_params: std::collections::HashMap::new(), + params, + return_type: self.convert_ty(sig.output()), + attrs, + body: std::sync::OnceLock::new().into(), + }, ); } @@ -62,12 +63,16 @@ where params.push((idents[i].map(|ident| ident.as_str().to_owned()), ty)); } - self.backend.function( + self.module.functions.pin().insert( self.convert_path(key), - self.convert_generics(key), - params, - self.convert_ty(sig.output()), - attrs.clone(), + orco::Function { + generics: self.convert_generics(key), + type_params: std::collections::HashMap::new(), + params, + return_type: self.convert_ty(sig.output()), + attrs, + body: std::sync::OnceLock::new().into(), + }, ); } @@ -96,10 +101,12 @@ where ty, )); } - self.backend.type_( + self.module.types.pin().insert( self.convert_path(key), - self.convert_generics(key), - orco::Type::Struct { fields }, + orco::TypeAlias { + generics: self.convert_generics(key), + type_: orco::Type::Struct { fields }, + }, ); } } diff --git a/frontends/orco-rustc/src/types.rs b/frontends/orco-rustc/src/types.rs index 26d2758..e6c746e 100644 --- a/frontends/orco-rustc/src/types.rs +++ b/frontends/orco-rustc/src/types.rs @@ -98,13 +98,14 @@ pub fn convert_generic_args(tcx: TyCtxt, args: &rustc_middle::ty::GenericArgs) - .collect() } -pub fn convert_generic_params(tcx: TyCtxt, key: rustc_hir::def_id::DefId) -> Vec { +/// Get a list of generic param names +pub fn convert_generic_params(tcx: TyCtxt, key: rustc_hir::def_id::DefId) -> Vec { let generics = tcx.generics_of(key); let mut types = generics .parent .map_or_else(Default::default, |key| convert_generic_params(tcx, key)); for param in &generics.own_params { - types.push(orco::Type::Param(param.name.as_str().into())); + types.push(param.name.as_str().into()); } types } diff --git a/orco/Cargo.toml b/orco/Cargo.toml index f6d42cb..99aa178 100644 --- a/orco/Cargo.toml +++ b/orco/Cargo.toml @@ -9,3 +9,4 @@ repository.workspace = true [dependencies] sinter = "0.1.1" +papaya.workspace = true diff --git a/orco/src/codegen/control_flow.rs b/orco/src/codegen/control_flow.rs deleted file mode 100644 index 458c911..0000000 --- a/orco/src/codegen/control_flow.rs +++ /dev/null @@ -1,78 +0,0 @@ -use super::Value; - -/// A label ID. See [`AcfCodegen::label`] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Label(pub usize); - -/// Arbitrary control flow instructions, such as jumps. -/// Warning: Not all codegens implement arbitrary control flow -pub trait AcfCodegen { - /// Allocates a label to be placed later - fn alloc_label(&mut self) -> Label { - unimplemented!("arbitrary control flow is not supported by this backend") - } - - /// Places a label in the current position. - fn label(&mut self, label: Label) { - unimplemented!("arbitrary control flow is not supported by this backend") - } - - /// Jump to a label. - /// See [`AcfCodegen::label`] - fn jump(&mut self, label: Label) { - unimplemented!("arbitrary control flow is not supported by this backend") - } - - /// Jumps if condition is true. - /// See [`AcfCodegen::label`] - fn cjump(&mut self, condition: Value, label: Label) { - unimplemented!("arbitrary control flow is not supported by this backend") - } -} - -/// Block control flow (somewhat traditional/wasm style). -pub trait BcfCodegen { - /// Starts a block that will only be executed if the condition is met - fn if_(&mut self, condition: Value) { - todo!("block control flow, use BCF2ACF if not supported natively") - } - - /// Attaches an else block to the current if block - fn else_(&mut self) { - todo!("block control flow, use BCF2ACF if not supported natively") - } - - /// Ends the current block - fn end(&mut self) { - todo!("block control flow, use BCF2ACF if not supported natively") - } - - /// Creates a loop - fn loop_(&mut self) { - todo!("block control flow, use BCF2ACF if not supported natively") - } - - /// Break from the current loop - fn break_(&mut self) { - todo!("block control flow, use BCF2ACF if not supported natively") - } - - /// Continue loop iteration - fn continue_(&mut self) { - todo!("block control flow, use BCF2ACF if not supported natively") - } - - /// Conditional break from the current loop - fn cbreak(&mut self, condition: Value) { - self.if_(condition); - self.break_(); - self.end(); - } - - /// Conditional continue loop iteration - fn ccontinue(&mut self, condition: Value) { - self.if_(condition); - self.continue_(); - self.end(); - } -} diff --git a/orco/src/codegen/impls/bcf_to_acf.rs b/orco/src/codegen/impls/bcf_to_acf.rs deleted file mode 100644 index 5c6b6a9..0000000 --- a/orco/src/codegen/impls/bcf_to_acf.rs +++ /dev/null @@ -1,138 +0,0 @@ -use crate::codegen as cg; -use cg::{AcfCodegen as _, Intrinsics as _}; - -/// BCF block types -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum BlockType { - If { end: cg::Label }, - Else { end: cg::Label }, - Loop { start: cg::Label, end: cg::Label }, -} - -/// Convert block-based control flow to ACF. -/// To use this, add it as a field to your codegen and return -/// ``` -/// BcfToAcf::bcf(self, |this| &mut this.acf_to_bcf) -/// ``` -/// in your [`cg::BodyCodegen::bcf`] implementation -#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct BcfToAcf { - stack: Vec, -} - -impl BcfToAcf { - #[allow(missing_docs)] - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Returns the implementation of [`cg::BcfCodegen`], - /// referencing `codegen`. You must also supply a getter - /// for the [`BcfToAcf`] instance - pub fn bcf( - codegen: &mut CG, - getter: fn(&mut CG) -> &mut BcfToAcf, - ) -> impl cg::BcfCodegen + '_ { - Wrapper { codegen, getter } - } - - fn last_loop(&self) -> Option<(cg::Label, cg::Label)> { - for block in self.stack.iter().rev() { - let BlockType::Loop { start, end } = block else { - continue; - }; - - return Some((*start, *end)); - } - - None - } -} - -struct Wrapper<'a, CG: cg::BodyCodegen> { - codegen: &'a mut CG, - getter: fn(&mut CG) -> &mut BcfToAcf, -} - -impl Wrapper<'_, CG> { - fn state(&mut self) -> &mut BcfToAcf { - (self.getter)(self.codegen) - } -} - -impl cg::BcfCodegen for Wrapper<'_, CG> { - fn if_(&mut self, condition: cg::Value) { - let end = self.codegen.acf().alloc_label(); - let uncondition = self.codegen.intrinsics().not(condition); - self.codegen.acf().cjump(uncondition, end); - self.state().stack.push(BlockType::If { end }); - } - - fn else_(&mut self) { - match self.state().stack.pop() { - Some(BlockType::If { end }) => { - let end2 = self.codegen.acf().alloc_label(); - self.codegen.acf().jump(end2); - self.codegen.acf().label(end); - self.state().stack.push(BlockType::Else { end: end2 }); - } - block => { - panic!("expected last block to be `if` while generating else, but it was {block:?}") - } - } - } - - fn end(&mut self) { - let Some(block) = self.state().stack.pop() else { - panic!("calling end() on an empty stack"); - }; - match block { - BlockType::If { end } => self.codegen.acf().label(end), - BlockType::Else { end } => self.codegen.acf().label(end), - BlockType::Loop { start, end } => { - self.codegen.acf().jump(start); - self.codegen.acf().label(end) - } - } - } - - fn loop_(&mut self) { - let start = self.codegen.acf().alloc_label(); - let end = self.codegen.acf().alloc_label(); - self.codegen.acf().label(start); - self.state().stack.push(BlockType::Loop { start, end }); - } - - fn break_(&mut self) { - let Some((_, end)) = self.state().last_loop() else { - panic!("can't break() here, no loop blocks are open") - }; - - self.codegen.acf().jump(end); - } - - fn continue_(&mut self) { - let Some((start, _)) = self.state().last_loop() else { - panic!("can't continue() here, no loop blocks are open") - }; - - self.codegen.acf().jump(start); - } - - fn cbreak(&mut self, condition: cg::Value) { - let Some((_, end)) = self.state().last_loop() else { - panic!("can't cbreak() here, no loop blocks are open") - }; - - self.codegen.acf().cjump(condition, end); - } - - fn ccontinue(&mut self, condition: cg::Value) { - let Some((start, _)) = self.state().last_loop() else { - panic!("can't ccontinue() here, no loop blocks are open") - }; - - self.codegen.acf().cjump(condition, start); - } -} diff --git a/orco/src/codegen/impls/mod.rs b/orco/src/codegen/impls/mod.rs deleted file mode 100644 index b9a5baa..0000000 --- a/orco/src/codegen/impls/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -// mod bcf_to_acf; -// pub use bcf_to_acf::BcfToAcf; diff --git a/orco/src/codegen/intrinsics.rs b/orco/src/codegen/intrinsics.rs deleted file mode 100644 index 545f114..0000000 --- a/orco/src/codegen/intrinsics.rs +++ /dev/null @@ -1,28 +0,0 @@ -use super::Value; - -/// Interface providing intrinsic function implementations. -pub trait Intrinsics { - /// Integer/float addition - #[allow(unused_variables)] - fn add(&mut self, a: Value, b: Value) -> Value { - unimplemented!("add operation"); - } - - /// Integer/float multiplication - #[allow(unused_variables)] - fn mul(&mut self, a: Value, b: Value) -> Value { - unimplemented!("mul operation"); - } - - /// Primitive type equality check - #[allow(unused_variables)] - fn eq(&mut self, a: Value, b: Value) -> Value { - unimplemented!("eq operation"); - } - - /// Logical/Bitwise not - #[allow(unused_variables)] - fn not(&mut self, a: Value) -> Value { - unimplemented!("not operation"); - } -} diff --git a/orco/src/codegen/mod.rs b/orco/src/codegen/mod.rs deleted file mode 100644 index c2325a0..0000000 --- a/orco/src/codegen/mod.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Code generation APIs, used to actually define functions and generate code. -use crate::types::IntegerSize; -use crate::{Symbol, Type}; - -/// Implementations of codegen features -pub mod impls; - -mod values; -pub use values::*; - -mod intrinsics; -pub use intrinsics::*; - -mod control_flow; -pub use control_flow::*; - -/// Trait for generating code within a function. -/// NOTE: whenever an instruction yields a value, -/// it may be reordered or removed, until the value gets used. -/// Use [`Self::mk_tmp`] to convert values to variables -pub trait BodyCodegen: Intrinsics + AcfCodegen + BcfCodegen { - /// Leave a comment. Mainly for source2source backends - fn comment(&mut self, comment: &str) { - let _ = comment; - } - - /// Get type of the value. Takes in [`Value::0`] to not consume the value. - /// Only applicable to unused values (rust type system will make sure) - fn type_of(&self, id: usize) -> Type; - - /// Declare a variable, see [Variable]. - /// Takes optional name (for debugging purposes), - /// which does not have to be unique - fn declare_var(&mut self, ty: Type, name: Option<&str>) -> Variable; - - /// Assign a value into a place, which makes it reusable - fn assign(&mut self, target: Place, value: Value); - /// Makes a temporary variable and assigns the value to it. Utility function - fn mk_tmp(&mut self, value: Value) -> Variable { - let tmp = self.declare_var(self.type_of(value.0), None); - self.assign(tmp.into(), value); - tmp - } - - /// Signed integer constant - fn iconst(&mut self, value: i128, size: IntegerSize) -> Value; - /// Unsigned integer constant - fn uconst(&mut self, value: u128, size: IntegerSize) -> Value; - /// Float constant - fn fconst(&mut self, value: f64, size: u16) -> Value; - /// Bool constant - fn bconst(&mut self, value: bool) -> Value; - - /// Read value from a [Place] - fn read(&mut self, place: Place) -> Value; - /// Get memory address of a [Place], returns a pointer with set mutability - fn reference(&mut self, place: Place, mutable: bool) -> Value; - - /// Call a function (or an intrinsic) - fn call(&mut self, func: Value, args: Vec) -> Option; - - /// Return a value from the current function. - fn return_(&mut self, value: Option); -} - -/// Interface for generating actual code. -/// All the items defined must be declared using [`crate::DeclarationBackend`] first. -pub trait CodegenBackend: Sync { - /// Define a function - fn cg_function(&self, name: Symbol, generic_params: Vec) -> Box; -} diff --git a/orco/src/codegen/values.rs b/orco/src/codegen/values.rs deleted file mode 100644 index e29704e..0000000 --- a/orco/src/codegen/values.rs +++ /dev/null @@ -1,47 +0,0 @@ -use super::Symbol; - -/// Variable is a mutable storage, either in RAM or CPU registers -/// Arguments are declared as variables before codegen, and so they -/// can be accessed using `Variable()` -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Variable(pub usize); - -/// Values are immutable results of operations. They can't be reused -/// unless stored in temporary variables, see [`BodyCodegen::mk_tmp`] -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Value(pub usize); - -/// A variable or symbol with projection (aka field access, dereferences, etc.) -#[derive(Debug, PartialEq, PartialOrd)] -pub enum Place { - /// Just variable access - Variable(Variable), - /// Global symbol access, includes generics - Global(Symbol, Vec), - /// Pointer dereference - Deref(Value), - /// Field access, using 0-based field index - Field(Box, usize), -} - -impl Place { - /// A helper function to create [`Self::Field`] - #[must_use] - pub fn field(self, index: usize) -> Self { - Self::Field(Box::new(self), index) - } -} - -impl From for Place { - fn from(value: Variable) -> Self { - Self::Variable(value) - } -} - -impl Variable { - /// Quickly convert a variable to [Place] - #[must_use] - pub fn place(self) -> Place { - self.into() - } -} diff --git a/orco/src/ir/instr.rs b/orco/src/ir/instr.rs new file mode 100644 index 0000000..533f116 --- /dev/null +++ b/orco/src/ir/instr.rs @@ -0,0 +1,88 @@ +/// Single instruction can be thought of a node in the AST-like IR, +/// with it's children being flat written into a list of instructions right after. +/// See [`super::Body::instructions`]. +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +pub enum Instruction { + /// Signed integer constant. + IConst(i32, crate::types::IntegerSize), + /// Unsigned integer constant. + UConst(u32, crate::types::IntegerSize), + /// Float constant. + FConst(f32, u16), + /// Bool constant. + BConst(bool), + + /// Load a global value (function, variable, etc.). + Global(super::SymbolId), + /// Load the variable. + Var(super::VariableId), + /// Access a field at index. + Field(u32), + /// Assign the value to a place last expression references. + Assign, + + /// Just places a label here, allowing jump to this point. + AcfLabel(super::LabelId), + /// Unconditionally jump to a label. + AcfJump(super::LabelId), + /// Conditionally jump to a label. + AcfCJump(super::LabelId), + + /// Call a function with a specified number of arguments. + Call(u32), + /// Returns the value (if any). + Return(bool), + /// Intrinsic. See [`super::Intrinsic`]. + Intrinsic(super::Intrinsic), + /// Error value. + Error, +} + +impl Instruction { + /// Number of arguments to follow this instruction with in + /// [`super::Body::instructions`] + pub fn arg_count(self) -> u32 { + match self { + Self::IConst(..) | Self::UConst(..) | Self::FConst(..) | Self::BConst(..) => 0, + + Self::Global(..) => 0, + Self::Var(..) => 0, + Self::Field(..) => 1, + Self::Assign => 2, + + Self::AcfLabel(..) => 0, + Self::AcfJump(..) => 0, + Self::AcfCJump(..) => 1, + + Self::Call(args) => args + 1, + Self::Return(has_value) => has_value as _, + Self::Intrinsic(intr) => intr.arg_count(), + Self::Error => 0, + } + } +} + +impl std::fmt::Display for Instruction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::IConst(value, size) => write!(f, "{value}_i{size}"), + Self::UConst(value, size) => write!(f, "{value}_u{size}"), + Self::FConst(value, size) => write!(f, "{value}_f{size}"), + Self::BConst(value) => write!(f, "{value}"), + + Self::Global(symbol) => write!(f, "{symbol}"), + Self::Var(id) => write!(f, "?{id}"), + Self::Field(idx) => write!(f, "field_{idx}"), + Self::Assign => write!(f, "assign"), + + Self::AcfLabel(label) => write!(f, "label {label}"), + Self::AcfJump(label) => write!(f, "jump {label}"), + Self::AcfCJump(label) => write!(f, "cjump {label}"), + + Self::Call(_) => write!(f, "call"), + Self::Return(..) => write!(f, "return"), + Self::Intrinsic(intr) => intr.fmt(f), + Self::Error => write!(f, "error"), + } + } +} diff --git a/orco/src/ir/intrinsics.rs b/orco/src/ir/intrinsics.rs new file mode 100644 index 0000000..9899ac5 --- /dev/null +++ b/orco/src/ir/intrinsics.rs @@ -0,0 +1,131 @@ +use crate::types::IntegerSize; + +/// Intrinsics are operations built into the compier. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Intrinsic { + /// Adds two numbers. Ints and floats supported. + Add, + /// Subtracts two numbers. Ints and floats supported. + Sub, + /// Subtracts two numbers. Ints and floats supported. + Mul, + /// Subtracts two numbers. Ints and floats supported. + Div, + /// Subtracts two numbers. Ints and floats supported. + Mod, + /// Compares two arbitrary values. Any type supported, + /// pointers will be compared by address. + Eq, + /// Constructs a bigger integer from smaller literals, + /// useful for large constants. Type is inherited from the literals. + /// Only argument is the number of literals to bitwise concatenate. + AggregateInt(u8), +} + +impl Intrinsic { + /// Returns the number of arguments this intrinsic requires. + pub fn arg_count(self) -> u32 { + match self { + Self::Add => 2, + Self::Sub => 2, + Self::Mul => 2, + Self::Div => 2, + Self::Mod => 2, + Self::Eq => 2, + Self::AggregateInt(count) => count as _, + } + } + + /// Weather debug display should use infix notation for this intrinsic. + pub fn infix(self) -> bool { + match self { + Self::Add => true, + Self::Sub => true, + Self::Mul => true, + Self::Div => true, + Self::Mod => true, + Self::Eq => true, + Self::AggregateInt(..) => false, + } + } + + /// For some intrinsics yields their return type, + /// for others type must be derived from the arguments. + pub fn type_override(self) -> Option { + use crate::Type; + Some(match self { + Intrinsic::Eq => Type::Bool, + _ => return None, + }) + } +} + +impl std::fmt::Display for Intrinsic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Add => write!(f, "+"), + Self::Sub => write!(f, "-"), + Self::Mul => write!(f, "*"), + Self::Div => write!(f, "/"), + Self::Mod => write!(f, "%"), + Self::Eq => write!(f, "=="), + Self::AggregateInt(..) => { + write!(f, "int") + } + } + } +} + +impl From for super::Instr { + fn from(value: Intrinsic) -> Self { + Self::Intrinsic(value) + } +} + +impl super::Body { + /// Pushes an arbitrarily-sized int literal, + /// possibly making use of [`Intrinsic::AggregateInt`]. + /// See also [`uint_literal`]. + pub fn int_literal(&mut self, mut value: i128, size: IntegerSize) { + let mut segments = Vec::with_capacity(4); + loop { + segments.push((value & 0xffffffff) as i32); + value >>= 32; + if value == -1 || value == 0 { + break; + } + } + + if segments.len() != 1 { + self.instructions + .push(Intrinsic::AggregateInt(segments.len() as _).into()); + } + + for segment in segments.into_iter().rev() { + self.instructions.push(super::Instr::IConst(segment, size)); + } + } + + /// Pushes an arbitrarily-sized unsigned int literal, + /// possibly making use of [`Intrinsic::AggregateInt`]. + /// See also [`int_literal`]. + pub fn uint_literal(&mut self, mut value: u128, size: IntegerSize) { + let mut segments = Vec::with_capacity(4); + loop { + segments.push((value & 0xffffffff) as u32); + value >>= 32; + if value == 0 { + break; + } + } + + if segments.len() != 1 { + self.instructions + .push(Intrinsic::AggregateInt(segments.len() as _).into()); + } + + for segment in segments.into_iter().rev() { + self.instructions.push(super::Instr::UConst(segment, size)); + } + } +} diff --git a/orco/src/ir/label.rs b/orco/src/ir/label.rs new file mode 100644 index 0000000..daa03f6 --- /dev/null +++ b/orco/src/ir/label.rs @@ -0,0 +1,31 @@ +/// Id of a label (index into labels list). +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct LabelId(pub u32); + +impl std::fmt::Display for LabelId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.0) + } +} + +impl super::Body { + /// Allocate a label to be placed in the source code later, + /// returns the newly-allocated ID. ID value order guaranteed + pub fn alloc_label(&mut self, name: Option) -> LabelId { + let id = LabelId(self.label_names.len() as _); + self.label_names.push(name); + id + } + + /// Get a string used to identify the label in debug output + pub fn label_debug_name(&self, id: LabelId) -> String { + format!( + "{}{id}", + self.label_names + .get(id.0 as usize) + .unwrap_or_else(|| panic!("invalid label id {id}")) + .as_deref() + .unwrap_or("_") + ) + } +} diff --git a/orco/src/ir/mod.rs b/orco/src/ir/mod.rs new file mode 100644 index 0000000..6d6194a --- /dev/null +++ b/orco/src/ir/mod.rs @@ -0,0 +1,214 @@ +mod instr; +pub use instr::Instruction as Instr; + +mod variable; +pub use variable::{VariableId, VariableInfo}; + +mod symbol_ref; +pub use symbol_ref::{SymbolId, SymbolUse}; + +mod label; +pub use label::LabelId; + +mod intrinsics; +pub use intrinsics::Intrinsic; + +/// A function body. +/// See also [`FmtBody`]. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Body { + /// All variables used in the body. + /// Index this with [`VariableId::0`]. + pub variables: Vec, + /// All symbols referenced in this body. + pub symbols: Vec, + /// Reverse map of all used symbols inside [`Self::symbols`] + /// to their respecive [`SymbolId`] for quick interning. + interned_symbols: std::collections::HashMap<(crate::Symbol, Vec), SymbolId>, + /// Debug names attached to labels. + /// Index this with [`LabelId::0`]. + pub label_names: Vec>, + /// A list of instructions, with values following inverse stack-based order. + /// See [`Instr`] + pub instructions: Vec, +} + +impl Body { + #[allow(missing_docs)] + pub fn new() -> Self { + Self::default() + } + + /// Get type of a value generated at index. + /// Requires module access for global symbols. + pub fn value_ty(&self, idx: usize) -> crate::Type { + use crate::Type; + match self.instructions[idx] { + Instr::IConst(_, size) => Type::Integer(size), + Instr::UConst(_, size) => Type::Unsigned(size), + Instr::FConst(_, size) => Type::Float(size), + Instr::BConst(_) => Type::Bool, + + Instr::Global(id) => self.symbol(id).ty.clone(), + Instr::Var(id) => self.var(id).ty.clone(), + Instr::Field(field_idx) => { + let ty = self.value_ty(idx + 1); + let Type::Struct { mut fields } = ty else { + panic!("trying to access field #{field_idx} on a non-struct type {ty}"); + }; + fields.swap_remove(field_idx as _).1 + } + Instr::Assign => Type::Error, + + Instr::AcfLabel(..) | Instr::AcfJump(..) | Instr::AcfCJump(..) => Type::Error, + Instr::Call(..) => { + let ty = self.value_ty(idx + 1); + let Type::FnPtr { return_type, .. } = ty else { + panic!("trying to call a non-function of type {ty}"); + }; + return_type.map_or(Type::Error, |ty| *ty) + } + Instr::Intrinsic(intr) => intr + .type_override() + .unwrap_or_else(|| self.value_ty(idx + 1)), + Instr::Return(..) => Type::Error, + Instr::Error => Type::Error, + } + } + + /// Debug-print an instruction at `idx` with it's arguments into `f` + pub fn debug_instr( + &self, + module: &crate::Module, + f: &mut std::fmt::Formatter<'_>, + mut idx: usize, + ) -> Result { + let debug_args = move |mut idx, f: &mut std::fmt::Formatter<'_>, args| { + write!(f, "(")?; + for i in 0..args { + if i > 0 { + write!(f, ", ")?; + } + idx = self.debug_instr(module, f, idx)?; + } + write!(f, ")")?; + Ok(idx) + }; + + match self.instructions[idx] { + Instr::Global(id) => write!(f, "{}", self.symbol(id)).map(|_| idx + 1), + Instr::Var(id) => write!(f, "{}", self.var_debug_name(id)).map(|_| idx + 1), + Instr::Assign => { + idx = self.debug_instr(module, f, idx + 1)?; + write!(f, " = ")?; + self.debug_instr(module, f, idx) + } + Instr::Return(has_value) => { + write!(f, "return")?; + if has_value { + write!(f, " ")?; + self.debug_instr(module, f, idx + 1) + } else { + Ok(idx + 1) + } + } + + Instr::Field(field_idx) => { + let ty = module.inline_ty(self.value_ty(idx + 1)); + idx = self.debug_instr(module, f, idx + 1)?; + let crate::Type::Struct { fields } = ty else { + panic!("trying to access field #{field_idx} on a non-struct type {ty}"); + }; + + match &fields[field_idx as usize].0 { + Some(name) => write!(f, ".{name}")?, + None => write!(f, ".field_{field_idx}")?, + } + + Ok(idx) + } + + Instr::AcfLabel(label) => { + write!(f, "{}:", self.label_debug_name(label)).map(|_| idx + 1) + } + Instr::AcfJump(label) => { + write!(f, "jump {}", self.label_debug_name(label)).map(|_| idx + 1) + } + Instr::AcfCJump(label) => { + write!(f, "if ")?; + idx = self.debug_instr(module, f, idx + 1)?; + write!(f, " jump {}", self.label_debug_name(label))?; + Ok(idx) + } + + Instr::Call(args) => { + idx = self.debug_instr(module, f, idx + 1)?; + debug_args(idx, f, args) + } + + Instr::Intrinsic(intr) if intr.infix() => { + idx += 1; + write!(f, "(")?; + for i in 0..intr.arg_count() { + if i > 0 { + write!(f, " {intr} ")?; + } + idx = self.debug_instr(module, f, idx)?; + } + write!(f, ")")?; + Ok(idx) + } + + instr => { + write!(f, "{instr}")?; + idx += 1; + let args = instr.arg_count(); + if args > 0 { + idx = debug_args(idx, f, args)?; + } + Ok(idx) + } + } + } +} + +/// Small wrapper implementing [`std::fmt::Display`] for [`Body`]. +pub struct FmtBody<'a>(pub &'a crate::Module, pub &'a Body); +impl std::fmt::Display for FmtBody<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let FmtBody(module, body) = self; + if body.variables.is_empty() && body.instructions.is_empty() { + return write!(f, "{{}}"); + } + + writeln!(f, "{{")?; + for (idx, var) in body.variables.iter().enumerate() { + write!( + f, + " let {}: {}", + body.var_debug_name(VariableId(idx as _)), + var.ty + )?; + if var.arg { + write!(f, " = ")?; + } + write!(f, ";")?; + writeln!(f)?; + } + + let mut idx = 0; + while idx < body.instructions.len() { + if matches!(body.instructions[idx], Instr::AcfLabel(..)) { + idx = body.debug_instr(module, f, idx + 1)?; + writeln!(f)?; + continue; + } + + write!(f, " ")?; + idx = body.debug_instr(module, f, idx)?; + writeln!(f, ";")?; + } + + write!(f, "}}") + } +} diff --git a/orco/src/ir/symbol_ref.rs b/orco/src/ir/symbol_ref.rs new file mode 100644 index 0000000..b77fd25 --- /dev/null +++ b/orco/src/ir/symbol_ref.rs @@ -0,0 +1,84 @@ +use crate::{Symbol, Type}; + +/// Id of a symbol (index into list of referenced symbols). +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SymbolId(pub u32); + +impl std::fmt::Display for SymbolId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.0) + } +} + +/// Reference to a symbol. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SymbolUse { + /// Symbol name. + pub name: Symbol, + /// A set of generic arguments. + pub generics: Vec, + /// Cached symbol type (instantiated) + pub ty: Type, +} + +impl std::fmt::Display for SymbolUse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}{}", + self.name, + crate::types::fmt_generic_args(&self.generics) + ) + } +} + +impl super::Body { + /// Retrieve the [`SymbolUse`] by ID. + pub fn symbol(&self, id: SymbolId) -> &SymbolUse { + &self + .symbols + .get(id.0 as usize) + .unwrap_or_else(|| panic!("invalid symbol id {id}")) + } + + /// Reference a symbol from the global namespace, adding it to the list of symbols + /// (unless already there), returns the ID to be used with [`Self::symbol`]. + pub fn use_symbol( + &mut self, + name: Symbol, + generics: Vec, + module: &crate::Module, + ) -> SymbolId { + let symbol = (name, generics); + if let Some(id) = self.interned_symbols.get(&symbol) { + return *id; + } + + let id = SymbolId(self.symbols.len() as _); + self.interned_symbols.insert(symbol.clone(), id); + let (name, generics) = symbol; + self.symbols.push(SymbolUse { + name, + generics, + ty: Type::Error, + }); + self.refresh_symbol_type(id, module); + id + } + + /// Recomputes type of the symbol use, to be up to date with the global. + pub fn refresh_symbol_type(&mut self, id: SymbolId, module: &crate::Module) { + let symbol = self + .symbols + .get_mut(id.0 as usize) + .unwrap_or_else(|| panic!("invalid symbol id {id}")); + + let functions = module.functions.pin(); + let func = functions + .get(&symbol.name) + .unwrap_or_else(|| panic!("undefined symbol {}", symbol.name)); + symbol.ty = func + .ptr_type() + .copy_instantiate(&func.generic_map(&symbol.generics)); + } +} diff --git a/orco/src/ir/variable.rs b/orco/src/ir/variable.rs new file mode 100644 index 0000000..f1b8d70 --- /dev/null +++ b/orco/src/ir/variable.rs @@ -0,0 +1,56 @@ +/// Id of a variable (index into variables list). +/// It is known that all function arguments have sequential IDs, starting from index 0. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct VariableId(pub u32); + +impl std::fmt::Display for VariableId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.0) + } +} + +/// Info about one variable in a body. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct VariableInfo { + /// Type of this variable. + pub ty: crate::Type, + /// Wether this variable comes from function arguments. + pub arg: bool, + /// Debug name. + pub name: Option, +} + +impl super::Body { + /// Declare a variable with a set type and optional debug name, + /// which can later be set using [`Self::var_mut`] + /// Returns the newly-allocated ID to be used with [`Self::var`]. + /// ID value order guaranteed, see note on [`VariableId`]. + pub fn declare_var(&mut self, ty: crate::Type, name: Option) -> VariableId { + let id = VariableId(self.variables.len() as _); + self.variables.push(VariableInfo { + ty, + arg: false, + name, + }); + id + } + + /// Get variable info by ID. + pub fn var(&self, id: VariableId) -> &VariableInfo { + self.variables + .get(id.0 as usize) + .unwrap_or_else(|| panic!("invalid variable id {id}")) + } + + /// Mutable version of [`Self::var`]. + pub fn var_mut(&mut self, id: VariableId) -> &mut VariableInfo { + self.variables + .get_mut(id.0 as usize) + .unwrap_or_else(|| panic!("invalid variable id {id}")) + } + + /// Get a string used to identify the variable in debug output. + pub fn var_debug_name(&self, id: VariableId) -> String { + format!("{}{id}", self.var(id).name.as_deref().unwrap_or("_")) + } +} diff --git a/orco/src/lib.rs b/orco/src/lib.rs index a57befd..eb50292 100644 --- a/orco/src/lib.rs +++ b/orco/src/lib.rs @@ -1,13 +1,10 @@ #![warn(missing_docs)] #![doc = include_str!("../../README.md")] +pub use papaya; pub use sinter; pub use sinter::IStr as Symbol; -/// Code generation, outside of declaration -pub mod codegen; -pub use codegen::CodegenBackend; - /// Type enums pub mod types; pub use types::Type; @@ -15,23 +12,149 @@ pub use types::Type; /// Attributes are a way to pass information about symbols to the backend pub mod attrs; -/// Declare items before defining them. -/// Think of it as an interface to generate C headers (uh oh generics...). -/// For adding generic params, see [`Type::Param`] -pub trait DeclarationBackend { - /// Declare a function (does not have to be defined within this linker unit). - /// Set `return_type` to [None] if require no return value. - /// Specializations declared during codegen - fn function( - &self, - name: Symbol, - generic_params: Vec, - params: Vec<(Option, Type)>, - return_type: Option, - attrs: attrs::FunctionAttributes, - ); - - /// Declre a type alias, can be used to declare compound types as well. - /// Specializations declared using this function as well - fn type_(&self, name: Symbol, generic_params: Vec, ty: Type); +/// Body IR +pub mod ir; +pub use ir::{Body, FmtBody}; + +mod transforms; + +use papaya::HashMap; +/// Shorthand for [`papaya::HashMapRef`] for any of [`orco::Symbol`] -> `V` maps +pub type SymbolMapRef<'a, V> = + papaya::HashMapRef<'a, Symbol, V, std::hash::RandomState, papaya::LocalGuard<'a>>; + +/// A single compilation unit. +/// Note: Be careful with mutating the types, +/// as [`Body`] caches them. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Module { + /// Type declarations (aliases). + pub types: HashMap, + /// Function declarations. + pub functions: HashMap, +} + +impl Module { + #[allow(missing_docs)] + pub fn new() -> Self { + Self::default() + } + + /// Replaces the type alias by it's value until can't anymore. + /// Reveals the true identity of the type. + fn inline_ty(&self, mut ty: Type) -> Type { + let types = self.types.pin(); + while let Type::Symbol(name, generics) = ty { + ty = types + .get(&name) + .unwrap_or_else(|| panic!("undelcared type {name}")) + .instantiate(&generics); + } + + ty + } +} + +impl std::fmt::Display for Module { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (name, alias) in self.types.pin().iter() { + writeln!( + f, + "type {name}{} = {};", + types::fmt_generic_params(&alias.generics), + &alias.type_ + )?; + } + + writeln!(f)?; + + for (name, func) in self.functions.pin().iter() { + write!(f, "{}fn {name}{}", func.attrs, func)?; + if let Some(body) = func.body.get() { + writeln!(f, " {}\n", FmtBody(self, body))?; + } else { + writeln!(f, ";")?; + } + } + + Ok(()) + } +} + +/// Type declaration statement. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TypeAlias { + /// Type parameters. + pub generics: Vec, + /// The type we alias. + pub type_: Type, +} + +impl TypeAlias { + /// Instantiate the type with a set of generic parameters. + /// Shorthand for calling [`Type::instantiate`]. + pub fn instantiate(&self, generics: &[Type]) -> Type { + self.type_ + .copy_instantiate(&self.generics.iter().copied().zip(generics).collect()) + } +} + +/// Function decl & body +#[derive(Clone, Debug, PartialEq)] +pub struct Function { + /// Type parameters. + pub generics: Vec, + /// Extra type parameters for the function. + pub type_params: std::collections::HashMap, + + /// Parameter types with optional names. + pub params: Vec<(Option, Type)>, + /// Return type. + pub return_type: Option, + /// Function attributes. + pub attrs: crate::attrs::FunctionAttributes, + /// Function body. + pub body: std::sync::Arc>, +} + +impl Function { + /// Generate a function body with all argument variables pre-added. + pub fn create_def(&self) -> Body { + let mut body = Body::new(); + for (name, ty) in self.params.iter().cloned() { + body.variables.push(ir::VariableInfo { + ty, + arg: true, + name, + }); + } + body + } +} + +impl std::fmt::Display for Function { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}(", types::fmt_generic_params(&self.generics))?; + + for (idx, (name, ty)) in self.params.iter().enumerate() { + if idx > 0 { + write!(f, ", ")?; + } + + match name { + Some(name) => write!(f, "{name:}: ")?, + None => write!(f, "_{idx}: ")?, + } + + ty.fmt(f)?; + } + + match &self.return_type { + Some(ty) => { + write!(f, ") -> ")?; + ty.fmt(f) + } + None => write!(f, ") -> void"), + } + } } diff --git a/orco/src/transforms/mod.rs b/orco/src/transforms/mod.rs new file mode 100644 index 0000000..e35f2a7 --- /dev/null +++ b/orco/src/transforms/mod.rs @@ -0,0 +1,2 @@ +mod monomorphization; +mod name_anonymous; diff --git a/orco/src/transforms/monomorphization.rs b/orco/src/transforms/monomorphization.rs new file mode 100644 index 0000000..981dd1f --- /dev/null +++ b/orco/src/transforms/monomorphization.rs @@ -0,0 +1,166 @@ +use crate::*; +use std::collections::{HashMap, HashSet}; + +/// Map from symbol to it's possible sets of generic args. +type InstanceMap = HashMap>>; + +/// Monomorphization context. +struct Context { + types: InstanceMap, + functions: InstanceMap, +} + +fn exists(instances: &mut InstanceMap, name: Symbol, args: &[Type]) -> bool { + let instances = instances.entry(name).or_default(); + if instances.contains(args) { + return true; + } + + instances.insert(args.to_vec()); + false +} + +/// Compute instances from a type decl. +fn visit_ty(module: &Module, ctx: &mut Context, ty: &mut Type) { + match ty { + Type::Symbol(name, args) if !args.is_empty() => { + let moname = module.monomorphized_name(*name, args); + if !exists(&mut ctx.types, *name, args) { + let mut new_ty = module + .types + .pin() + .get(name) + .unwrap_or_else(|| panic!("undelcared type {name}")) + .instantiate(&args); + visit_ty(module, ctx, &mut new_ty); + module.types.pin().insert( + moname, + TypeAlias { + generics: Vec::new(), + type_: new_ty, + }, + ); + } + + *ty = Type::Symbol(moname, Vec::new()); + } + Type::Array(ty, _) => visit_ty(module, ctx, ty), + Type::Struct { fields } => { + for (_, ty) in fields { + visit_ty(module, ctx, ty); + } + } + Type::Ptr(ty, _) => visit_ty(module, ctx, ty), + Type::FnPtr { + params, + return_type, + } => { + for ty in params { + visit_ty(module, ctx, ty); + } + + if let Some(ty) = return_type { + visit_ty(module, ctx, ty); + } + } + Type::Param(param) => { + panic!("[bug] generic param #{param} encountered while computing used generic symbols") + } + _ => (), + } +} + +/// Compute type instances from a function. +fn visit_function(module: &Module, ctx: &mut Context, name: Symbol, args: &[Type]) { + let functions = module.functions.pin(); + let func = functions + .get(&name) + .unwrap_or_else(|| panic!("undeclared function {name}")); + + let mut type_params = func.type_params.clone(); + type_params.extend(func.generics.iter().copied().zip(args.iter().cloned())); + + let params = func + .params + .iter() + .map(|(name, ty)| { + let mut ty = ty.copy_instantiate(&type_params); + visit_ty(module, ctx, &mut ty); + (name.clone(), ty) + }) + .collect::>(); + let return_type = func.return_type.as_ref().map(|ty| { + let mut ty = ty.copy_instantiate(&type_params); + visit_ty(module, ctx, &mut ty); + ty + }); + + if let Some(body) = func.body.get() { + for var in &body.variables { + visit_ty(module, ctx, &mut var.ty.clone()); + } + + for symbol in &body.symbols { + visit_function(module, ctx, symbol.name, &symbol.generics); + } + } + + functions.insert( + module.monomorphized_name(name, args), + Function { + generics: Vec::new(), + type_params, + params, + return_type, + attrs: func.attrs.clone(), + body: func.body.clone(), + }, + ); +} + +impl Module { + /// Get a name for a monomorphized version of a symbol. + pub fn monomorphized_name(&self, name: Symbol, args: &[Type]) -> Symbol { + if args.is_empty() { + name + } else { + format!("{name}{}", crate::types::fmt_generic_args(args)).into() + } + } + + /// Monomorphize the module (duplicate generic symbols for all usages). + pub fn monomorphize(&self) { + let mut ctx = Context { + types: HashMap::new(), + functions: HashMap::new(), + }; + + let types = self.types.pin(); + for (name, alias) in types.iter() { + if !alias.generics.is_empty() { + continue; + } + + let mut alias = alias.clone(); + visit_ty(self, &mut ctx, &mut alias.type_); + types.insert(*name, alias); + } + + let functions = self.functions.pin(); + for (name, func) in functions.iter() { + if !func.generics.is_empty() { + continue; + } + + visit_function(self, &mut ctx, *name, &[]); + } + + for (name, _) in ctx.types { + types.remove(&name); + } + + for (name, _) in ctx.functions { + functions.remove(&name); + } + } +} diff --git a/orco/src/transforms/name_anonymous.rs b/orco/src/transforms/name_anonymous.rs new file mode 100644 index 0000000..ea3477e --- /dev/null +++ b/orco/src/transforms/name_anonymous.rs @@ -0,0 +1,94 @@ +use crate::*; +use std::collections::HashSet; + +/// Replaces all anonymous structs by named structs, considers `ty` +/// named if `root` is true. +fn name_anonymous( + types: &SymbolMapRef, + generics: &mut HashSet, + ty: &mut Type, + root: bool, +) { + match ty { + Type::Symbol(_, symbol_generics) => { + for ty in symbol_generics { + name_anonymous(types, generics, ty, false); + } + } + Type::Array(ty, _) => name_anonymous(types, generics, ty, false), + Type::Struct { fields } => { + for (_, ty) in fields { + name_anonymous(types, generics, ty, false); + } + } + Type::Ptr(ty, _) => name_anonymous(types, generics, ty, false), + Type::FnPtr { + params, + return_type, + } => { + for ty in params { + name_anonymous(types, generics, ty, false); + } + + if let Some(ty) = return_type { + name_anonymous(types, generics, ty, false); + } + } + Type::Param(param) => { + generics.insert(*param); + } + _ => (), + } + + if root { + return; + } + + let name = match ty { + Type::Struct { fields } => fields + .iter() + .map(|(_, ty)| ty.to_string()) + .collect::() + .into(), + _ => return, + }; + + let generics = generics.iter().copied().collect::>(); + let ty = std::mem::replace( + ty, + Type::Symbol(name, generics.iter().copied().map(Type::Param).collect()), + ); + types.insert( + name, + TypeAlias { + generics, + type_: ty, + }, + ); +} + +impl Module { + /// Replaces all anonymous structs by named structs. + pub fn name_anonymous_structs(&self) { + let types = self.types.pin(); + for (name, alias) in types.iter() { + let mut alias = alias.clone(); + name_anonymous(&types, &mut HashSet::new(), &mut alias.type_, true); + types.insert(*name, alias); + } + + let functions = self.functions.pin(); + for (name, func) in functions.iter() { + let mut func = func.clone(); + for (_, ty) in &mut func.params { + name_anonymous(&types, &mut HashSet::new(), ty, false); + } + + if let Some(ty) = &mut func.return_type { + name_anonymous(&types, &mut HashSet::new(), ty, false); + } + + functions.insert(*name, func); + } + } +} diff --git a/orco/src/types.rs b/orco/src/types.rs index 99b28fb..c06a9af 100644 --- a/orco/src/types.rs +++ b/orco/src/types.rs @@ -40,7 +40,7 @@ pub enum Type { impl Type { /// Replace all instances of [`Type::Param`] with symbols from `map` (if present) - pub fn instantiate(&mut self, map: &std::collections::HashMap) { + pub fn instantiate(&mut self, map: &std::collections::HashMap>) { match self { Type::Integer(..) | Type::Unsigned(..) @@ -74,7 +74,7 @@ impl Type { } Type::Param(name) => { if let Some(ty) = map.get(name) { - ty.clone_into(self); + ty.as_ref().clone_into(self); } } Type::Error => (), @@ -82,7 +82,10 @@ impl Type { } /// Same as [`Self::instantiate`], but clones the type in the process - pub fn copy_instantiate(&self, map: &std::collections::HashMap) -> Self { + pub fn copy_instantiate( + &self, + map: &std::collections::HashMap>, + ) -> Self { let mut instance = self.clone(); instance.instantiate(map); instance @@ -141,7 +144,7 @@ impl std::fmt::Display for Type { Type::Char(false) => write!(f, "achar"), Type::Char(true) => write!(f, "uchar"), - Type::Symbol(sym, generics) => write!(f, "{sym}{}", fmt_generics(generics)), + Type::Symbol(sym, generics) => write!(f, "{sym}{}", fmt_generic_args(generics)), Type::Array(ty, len) => write!(f, "{ty}[{len}]"), Type::Struct { fields } => { write!(f, "{{{}", if f.alternate() { '\n' } else { ' ' })?; @@ -205,8 +208,32 @@ impl std::fmt::Display for Type { } } -/// Format generic args using <> notation -pub fn fmt_generics(generics: &[Type]) -> String { +impl AsRef for Type { + fn as_ref(&self) -> &Type { + self + } +} + +/// Format generic parameters using <> notation +pub fn fmt_generic_params(generics: &[Symbol]) -> String { + if generics.is_empty() { + return String::new(); + } + + let mut buffer = String::from("<"); + use std::fmt::Write as _; + for (idx, ty) in generics.iter().enumerate() { + if idx > 0 { + buffer.push_str(", "); + } + write!(&mut buffer, "{ty}").unwrap(); + } + buffer.push('>'); + buffer +} + +/// Format generic arguments using <> notation +pub fn fmt_generic_args(generics: &[Type]) -> String { if generics.is_empty() { return String::new(); } @@ -228,7 +255,7 @@ pub fn fmt_generics(generics: &[Type]) -> String { pub enum IntegerSize { /// Number of bits. Not sure if non-powers-of-two /// should be supported. Maybe even non-whole bytes (ex. u6 for 6 bit unsigned integer) - Bits(u16), + Bits(u8), /// Kinda like `usize`/`isize` in rust or `size_t`/`ssize_t` in C Size, } @@ -242,32 +269,7 @@ impl std::fmt::Display for IntegerSize { } } -/// Function signature without a name -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct FunctionSignature { - /// Parameter types with optional names - pub params: Vec<(Option, Type)>, - /// Return type - pub return_type: Option, - /// Function attributes - pub attrs: crate::attrs::FunctionAttributes, -} - -impl FunctionSignature { - #[allow(missing_docs)] - #[must_use] - pub fn new( - params: Vec<(Option, Type)>, - return_type: Option, - attrs: crate::attrs::FunctionAttributes, - ) -> Self { - Self { - params, - return_type, - attrs, - } - } - +impl crate::Function { /// Get function pointer type for this function signature pub fn ptr_type(&self) -> Type { Type::FnPtr { @@ -276,40 +278,13 @@ impl FunctionSignature { } } - /// See [Type::instantiate] - pub fn instantiate(&mut self, map: &std::collections::HashMap) { - for (_, ty) in &mut self.params { - ty.instantiate(map); - } - if let Some(ty) = &mut self.return_type { - ty.instantiate(map); - } - } -} - -impl std::fmt::Display for FunctionSignature { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "(")?; - - for (idx, (name, ty)) in self.params.iter().enumerate() { - if idx > 0 { - write!(f, ", ")?; - } - - match name { - Some(name) => write!(f, "{name:}: ")?, - None => write!(f, "_{idx}: ")?, - } - - ty.fmt(f)?; - } - - match &self.return_type { - Some(ty) => { - write!(f, ") -> ")?; - ty.fmt(f) - } - None => write!(f, ") -> void"), - } + /// Generates generic param to arg map for use with [Type::instantiate]. + pub fn generic_map<'a>(&self, args: &'a [Type]) -> std::collections::HashMap { + assert_eq!( + args.len(), + self.params.len(), + "wrong number of generic arguments supplied" + ); + self.generics.iter().copied().zip(args).collect() } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ce943c1..1eee1e3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-07-15" +channel = "nightly-2026-08-05" components = ["rustc-dev", "llvm-tools-preview"] diff --git a/test.sh b/test.sh index 8d659a2..24adb25 100755 --- a/test.sh +++ b/test.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -cargo build --package orco-rustc && RUSTC_LOG='orco_rustc' rustc "frontends/orco-rustc/samples/$1.rs" -Z codegen-backend=./target/debug/liborco_rustc.so -Z threads=sync +cargo build --package orco-rustc && RUSTC_LOG='orco_rustc' rustc "frontends/orco-rustc/samples/$1.rs" -Z codegen-backend=./target/debug/liborco_rustc.so