adding bfn to ast nodes

This commit is contained in:
mlokr 2024-09-03 17:51:28 +02:00
parent 7279ed88e9
commit b956cc78bb
No known key found for this signature in database
GPG key ID: DEA147DDEE644993
8 changed files with 658 additions and 97 deletions

View file

@ -18,6 +18,7 @@ Holey-Bytes-Language (hblang for short) (*.hb) is the only true language targeti
hblang knows what it isn't, because it knows what it is, hblang computes this by sub... hblang knows what it isn't, because it knows what it is, hblang computes this by sub...
## Examples ## Examples
Examples are also used in tests. To add an example that runs during testing add: Examples are also used in tests. To add an example that runs during testing add:
@ -627,6 +628,16 @@ min := fn(a: int, b: int): int {
} }
``` ```
### Just Testing Optimizations
#### const_folding_with_arg
```hb
main := fn(arg: int): int {
// reduces to 0
return arg + 0 - arg * 1 + arg + 1 + arg + 2 + arg + 3 - arg * 3 - 6
}
```
#### inline_test #### inline_test
```hb ```hb
Point := struct {x: int, y: int} Point := struct {x: int, y: int}

View file

@ -2006,7 +2006,7 @@ impl Codegen {
} }
E::Number { value, .. } => Some(Value { E::Number { value, .. } => Some(Value {
ty: ctx.ty.map(ty::Id::strip_pointer).unwrap_or(ty::INT.into()), ty: ctx.ty.map(ty::Id::strip_pointer).unwrap_or(ty::INT.into()),
loc: Loc::ct(value), loc: Loc::ct(value as u64),
}), }),
E::If { cond, then, else_, .. } => { E::If { cond, then, else_, .. } => {
log::dbg!("if-cond"); log::dbg!("if-cond");

View file

@ -77,7 +77,7 @@ macro_rules! gen_token_kind {
}; };
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
#[repr(u8)] #[repr(u8)]
pub enum TokenKind { pub enum TokenKind {
Not = b'!', Not = b'!',
@ -169,6 +169,22 @@ impl TokenKind {
} }
Some(unsafe { std::mem::transmute::<u8, Self>(id) }) Some(unsafe { std::mem::transmute::<u8, Self>(id) })
} }
pub fn is_comutative(self) -> bool {
use TokenKind as S;
matches!(self, S::Eq | S::Ne | S::Bor | S::Xor | S::Band | S::Add | S::Mul)
}
pub fn apply(self, a: i64, b: i64) -> i64 {
match self {
TokenKind::Add => a.wrapping_add(b),
TokenKind::Sub => a.wrapping_sub(b),
TokenKind::Mul => a.wrapping_mul(b),
TokenKind::Div => a.wrapping_div(b),
TokenKind::Shl => a.wrapping_shl(b as _),
s => todo!("{s}"),
}
}
} }
gen_token_kind! { gen_token_kind! {
@ -192,8 +208,10 @@ gen_token_kind! {
#[punkt] #[punkt]
Ctor = ".{", Ctor = ".{",
Tupl = ".(", Tupl = ".(",
// #define OP: each `#[prec]` delimeters a level of precedence from lowest to highest
#[ops] #[ops]
#[prec] #[prec]
// this also includess all `<op>=` tokens
Decl = ":=", Decl = ":=",
Assign = "=", Assign = "=",
#[prec] #[prec]

View file

@ -13,6 +13,7 @@
ptr_metadata, ptr_metadata,
slice_ptr_get slice_ptr_get
)] )]
#![feature(map_try_insert)]
#![allow(internal_features, clippy::format_collect)] #![allow(internal_features, clippy::format_collect)]
use { use {

View file

@ -399,7 +399,7 @@ impl<'a, 'b> Parser<'a, 'b> {
}; };
E::Number { E::Number {
pos: token.start, pos: token.start,
value: match u64::from_str_radix(slice, radix as u32) { value: match i64::from_str_radix(slice, radix as u32) {
Ok(value) => value, Ok(value) => value,
Err(e) => self.report(format_args!("invalid number: {e}")), Err(e) => self.report(format_args!("invalid number: {e}")),
}, },
@ -592,11 +592,13 @@ macro_rules! generate_expr {
($(#[$meta:meta])* $vis:vis enum $name:ident<$lt:lifetime> {$( ($(#[$meta:meta])* $vis:vis enum $name:ident<$lt:lifetime> {$(
$(#[$field_meta:meta])* $(#[$field_meta:meta])*
$variant:ident { $variant:ident {
$($field:ident: $ty:ty,)* $($field:ident: $ty:ty,)*
}, },
)*}) => { )*}) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
$vis enum $name<$lt> {$( $vis enum $name<$lt> {$(
$(#[$field_meta])*
$variant { $variant {
$($field: $ty,)* $($field: $ty,)*
}, },
@ -639,43 +641,54 @@ pub enum Radix {
Decimal = 10, Decimal = 10,
} }
// it would be real nice if we could use relative pointers and still pattern match easily
generate_expr! { generate_expr! {
/// `LIST(start, sep, end, elem) => start { elem sep } [elem] end`
/// `OP := grep for `#define OP:`
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Expr<'a> { pub enum Expr<'a> {
/// `'ct' Expr`
Ct { Ct {
pos: Pos, pos: Pos,
value: &'a Self, value: &'a Self,
}, },
/// `'"([^"]|\\")"'`
String { String {
pos: Pos, pos: Pos,
literal: &'a str, literal: &'a str,
}, },
/// `'//[^\n]' | '/*' { '([^/*]|*/)*' | Comment } '*/'
Comment { Comment {
pos: Pos, pos: Pos,
literal: &'a str, literal: &'a str,
}, },
/// `'break'`
Break { Break {
pos: Pos, pos: Pos,
}, },
/// `'continue'`
Continue { Continue {
pos: Pos, pos: Pos,
}, },
/// `'fn' LIST('(', ',', ')', Ident ':' Expr) ':' Expr Expr`
Closure { Closure {
pos: Pos, pos: Pos,
args: &'a [Arg<'a>], args: &'a [Arg<'a>],
ret: &'a Self, ret: &'a Self,
body: &'a Self, body: &'a Self,
}, },
/// `Expr LIST('(', ',', ')', Expr)`
Call { Call {
func: &'a Self, func: &'a Self,
args: &'a [Self], args: &'a [Self],
trailing_comma: bool, trailing_comma: bool,
}, },
/// `'return' [Expr]`
Return { Return {
pos: Pos, pos: Pos,
val: Option<&'a Self>, val: Option<&'a Self>,
}, },
/// note: ':unicode:' is any utf-8 character except ascii
/// `'[a-zA-Z_:unicode:][a-zA-Z0-9_:unicode:]*'`
Ident { Ident {
pos: Pos, pos: Pos,
is_ct: bool, is_ct: bool,
@ -683,75 +696,91 @@ generate_expr! {
name: &'a str, name: &'a str,
index: IdentIndex, index: IdentIndex,
}, },
/// `LIST('{', [';'], '}', Expr)`
Block { Block {
pos: Pos, pos: Pos,
stmts: &'a [Self], stmts: &'a [Self],
}, },
/// `'0b[01]+' | '0o[0-7]+' | '[0-9]+' | '0b[01]+'`
Number { Number {
pos: Pos, pos: Pos,
value: u64, value: i64,
radix: Radix, radix: Radix,
}, },
/// node: precedence defined in `OP` applies
/// `Expr OP Expr`
BinOp { BinOp {
left: &'a Self, left: &'a Self,
op: TokenKind, op: TokenKind,
right: &'a Self, right: &'a Self,
}, },
/// `'if' Expr Expr [else Expr]`
If { If {
pos: Pos, pos: Pos,
cond: &'a Self, cond: &'a Self,
then: &'a Self, then: &'a Self,
else_: Option<&'a Self>, else_: Option<&'a Self>,
}, },
/// `'loop' Expr`
Loop { Loop {
pos: Pos, pos: Pos,
body: &'a Self, body: &'a Self,
}, },
/// `('&' | '*' | '^') Expr`
UnOp { UnOp {
pos: Pos, pos: Pos,
op: TokenKind, op: TokenKind,
val: &'a Self, val: &'a Self,
}, },
/// `'struct' LIST('{', ',', '}', Ident ':' Expr)`
Struct { Struct {
pos: Pos, pos: Pos,
fields: &'a [(&'a str, Self)], fields: &'a [(&'a str, Self)],
captured: &'a [Ident], captured: &'a [Ident],
trailing_comma: bool, trailing_comma: bool,
}, },
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`
Ctor { Ctor {
pos: Pos, pos: Pos,
ty: Option<&'a Self>, ty: Option<&'a Self>,
fields: &'a [CtorField<'a>], fields: &'a [CtorField<'a>],
trailing_comma: bool, trailing_comma: bool,
}, },
/// `[Expr] LIST('.(', ',', ')', Ident [':' Expr])`
Tupl { Tupl {
pos: Pos, pos: Pos,
ty: Option<&'a Self>, ty: Option<&'a Self>,
fields: &'a [Self], fields: &'a [Self],
trailing_comma: bool, trailing_comma: bool,
}, },
/// `'[' Expr [';' Expr] ']'`
Slice { Slice {
pos: Pos, pos: Pos,
size: Option<&'a Self>, size: Option<&'a Self>,
item: &'a Self, item: &'a Self,
}, },
/// `Expr '[' Expr ']'`
Index { Index {
base: &'a Self, base: &'a Self,
index: &'a Self, index: &'a Self,
}, },
/// `Expr '.' Ident`
Field { Field {
target: &'a Self, target: &'a Self,
name: &'a str, name: &'a str,
}, },
/// `'true' | 'false'`
Bool { Bool {
pos: Pos, pos: Pos,
value: bool, value: bool,
}, },
/// `'@' Ident List('(', ',', ')', Expr)`
Directive { Directive {
pos: u32, pos: u32,
name: &'a str, name: &'a str,
args: &'a [Self], args: &'a [Self],
}, },
/// `'@use' '(' String ')'`
Mod { Mod {
pos: Pos, pos: Pos,
id: FileId, id: FileId,
@ -1155,6 +1184,10 @@ impl Ast {
_ => None, _ => None,
}) })
} }
pub fn ident_str(&self, ident: Ident) -> &str {
&self.file[ident::range(ident)]
}
} }
impl std::fmt::Display for Ast { impl std::fmt::Display for Ast {

View file

@ -1,13 +1,13 @@
#![allow(dead_code)] #![allow(dead_code)]
use { use {
crate::{ crate::{
ident::Ident, ident::{self, Ident},
instrs::{self}, lexer::{self, TokenKind},
lexer::{self},
log, log,
parser::{self, Expr, ExprRef, FileId, Pos}, parser::{self, idfl, Expr, ExprRef, FileId, Pos},
HashMap, HashMap,
}, },
core::fmt,
std::{ std::{
mem, mem,
ops::{self, Range}, ops::{self, Range},
@ -16,20 +16,22 @@ use {
}; };
type Nid = u32; type Nid = u32;
const NILL: u32 = u32::MAX;
pub struct PoolVec<T> { pub struct Nodes {
values: Vec<PoolSlot<T>>, values: Vec<PoolSlot>,
free: u32, free: u32,
lookup: HashMap<(Kind, [Nid; MAX_INPUTS]), Nid>,
} }
impl<T> Default for PoolVec<T> { impl Default for Nodes {
fn default() -> Self { fn default() -> Self {
Self { values: Default::default(), free: u32::MAX } Self { values: Default::default(), free: u32::MAX, lookup: Default::default() }
} }
} }
impl<T> PoolVec<T> { impl Nodes {
pub fn add(&mut self, value: T) -> u32 { pub fn add(&mut self, value: Node) -> u32 {
if self.free == u32::MAX { if self.free == u32::MAX {
self.free = self.values.len() as _; self.free = self.values.len() as _;
self.values.push(PoolSlot::Next(u32::MAX)); self.values.push(PoolSlot::Next(u32::MAX));
@ -43,7 +45,7 @@ impl<T> PoolVec<T> {
free free
} }
pub fn remove(&mut self, id: u32) -> T { pub fn remove_low(&mut self, id: u32) -> Node {
let value = match mem::replace(&mut self.values[id as usize], PoolSlot::Next(self.free)) { let value = match mem::replace(&mut self.values[id as usize], PoolSlot::Next(self.free)) {
PoolSlot::Value(value) => value, PoolSlot::Value(value) => value,
PoolSlot::Next(_) => unreachable!(), PoolSlot::Next(_) => unreachable!(),
@ -56,10 +58,287 @@ impl<T> PoolVec<T> {
self.values.clear(); self.values.clear();
self.free = u32::MAX; self.free = u32::MAX;
} }
fn new_node<const SIZE: usize>(
&mut self,
ty: impl Into<ty::Id>,
kind: Kind,
inps: [Nid; SIZE],
) -> Nid {
let mut inputs = [NILL; MAX_INPUTS];
inputs[..inps.len()].copy_from_slice(&inps);
if let Some(&id) = self.lookup.get(&(kind, inputs)) {
debug_assert_eq!(self[id].kind, kind);
debug_assert_eq!(self[id].inputs, inputs);
return id;
}
let id = self.add(Node {
inputs,
kind,
depth: u32::MAX,
lock_rc: 0,
ty: ty.into(),
outputs: vec![],
});
let prev = self.lookup.insert((kind, inputs), id);
debug_assert_eq!(prev, None);
self.add_deps(id, &inps);
if let Some(opt) = self.peephole(id) {
debug_assert_ne!(opt, id);
self.lock(opt);
self.remove(id);
self.unlock(opt);
opt
} else {
id
}
}
fn lock(&mut self, target: Nid) {
self[target].lock_rc += 1;
}
fn unlock(&mut self, target: Nid) {
self[target].lock_rc -= 1;
}
fn remove(&mut self, target: Nid) {
if !self[target].is_dangling() {
return;
}
for i in 0..self[target].inputs().len() {
let inp = self[target].inputs[i];
let index = self[inp].outputs.iter().position(|&p| p == target).unwrap();
self[inp].outputs.swap_remove(index);
self.remove(inp);
}
let res = self.lookup.remove(&(self[target].kind, self[target].inputs));
debug_assert_eq!(res, Some(target));
self.remove_low(target);
}
fn peephole(&mut self, target: Nid) -> Option<Nid> {
match self[target].kind {
Kind::Start => {}
Kind::End => {}
Kind::BinOp { op } => return self.peephole_binop(target, op),
Kind::Return => {}
Kind::Tuple { index } => {}
Kind::ConstInt { value } => {}
}
None
}
fn peephole_binop(&mut self, target: Nid, op: TokenKind) -> Option<Nid> {
use TokenKind as T;
let [mut lhs, mut rhs, ..] = self[target].inputs;
if lhs == rhs {
match op {
T::Sub => {
return Some(self.new_node(self[target].ty, Kind::ConstInt { value: 0 }, []));
}
T::Add => {
let rhs = self.new_node(self[target].ty, Kind::ConstInt { value: 2 }, []);
return Some(
self.new_node(self[target].ty, Kind::BinOp { op: T::Mul }, [lhs, rhs]),
);
}
_ => {}
}
}
if let (Kind::ConstInt { value: a }, Kind::ConstInt { value: b }) =
(self[lhs].kind, self[rhs].kind)
{
return Some(self.new_node(
self[target].ty,
Kind::ConstInt { value: op.apply(a, b) },
[],
));
}
let mut changed = false;
if op.is_comutative() && self[lhs].kind < self[rhs].kind {
std::mem::swap(&mut lhs, &mut rhs);
changed = true;
}
if let Kind::ConstInt { value } = self[rhs].kind {
match (op, value) {
(T::Add | T::Sub | T::Shl, 0) | (T::Mul | T::Div, 1) => return Some(lhs),
(T::Mul, 0) => return Some(rhs),
_ => {}
}
}
if op.is_comutative() && self[lhs].kind == (Kind::BinOp { op }) {
if let Kind::ConstInt { value: a } = self[self[lhs].inputs[1]].kind
&& let Kind::ConstInt { value: b } = self[rhs].kind
{
let new_rhs =
self.new_node(self[target].ty, Kind::ConstInt { value: op.apply(a, b) }, []);
return Some(self.new_node(self[target].ty, Kind::BinOp { op }, [
self[lhs].inputs[0],
new_rhs,
]));
}
if self.is_const(self[lhs].inputs[1]) {
let new_lhs =
self.new_node(self[target].ty, Kind::BinOp { op }, [self[lhs].inputs[0], rhs]);
return Some(self.new_node(self[target].ty, Kind::BinOp { op }, [
new_lhs,
self[lhs].inputs[1],
]));
}
}
if op == T::Add
&& self[lhs].kind == (Kind::BinOp { op: T::Mul })
&& self[lhs].inputs[0] == rhs
&& let Kind::ConstInt { value } = self[self[lhs].inputs[1]].kind
{
let new_rhs = self.new_node(self[target].ty, Kind::ConstInt { value: value + 1 }, []);
return Some(
self.new_node(self[target].ty, Kind::BinOp { op: T::Mul }, [rhs, new_rhs]),
);
}
if op == T::Sub && self[lhs].kind == (Kind::BinOp { op }) {
// (a - b) - c => a - (b + c)
let [a, b, ..] = self[lhs].inputs;
let c = rhs;
let new_rhs = self.new_node(self[target].ty, Kind::BinOp { op: T::Add }, [b, c]);
return Some(self.new_node(self[target].ty, Kind::BinOp { op }, [a, new_rhs]));
}
if changed {
return Some(self.new_node(self[target].ty, self[target].kind, [lhs, rhs]));
}
None
}
fn is_const(&self, id: Nid) -> bool {
matches!(self[id].kind, Kind::ConstInt { .. })
}
fn replace(&mut self, target: Nid, with: Nid) {
//for i in 0..self[target].inputs().len() {
// let inp = self[target].inputs[i];
// let index = self[inp].outputs.iter().position(|&p| p == target).unwrap();
// self[inp].outputs[index] = with;
//}
for i in 0..self[target].outputs.len() {
let out = self[target].outputs[i];
let index = self[out].inputs().iter().position(|&p| p == target).unwrap();
let rpl = self.modify_input(out, index, with);
self[with].outputs.push(rpl);
}
self.remove_low(target);
}
fn modify_input(&mut self, target: Nid, inp_index: usize, with: Nid) -> Nid {
let out = self.lookup.remove(&(self[target].kind, self[target].inputs));
debug_assert!(out == Some(target));
debug_assert_ne!(self[target].inputs[inp_index], with);
self[target].inputs[inp_index] = with;
if let Err(other) = self.lookup.try_insert((self[target].kind, self[target].inputs), target)
{
let rpl = *other.entry.get();
self.replace(target, rpl);
return rpl;
}
target
}
fn add_deps(&mut self, id: Nid, deps: &[Nid]) {
for &d in deps {
debug_assert_ne!(d, id);
self[d].outputs.push(id);
}
}
fn unlock_free(&mut self, id: Nid) {
self[id].lock_rc -= 1;
if self[id].is_dangling() {
self.remove_low(id);
}
}
fn fmt(&self, f: &mut fmt::Formatter, node: Nid, rcs: &mut [usize]) -> fmt::Result {
let mut is_ready = || {
if rcs[node as usize] == 0 {
return false;
}
rcs[node as usize] = rcs[node as usize].saturating_sub(1);
rcs[node as usize] == 0
};
match self[node].kind {
Kind::BinOp { op } => {
write!(f, "(")?;
self.fmt(f, self[node].inputs[0], rcs)?;
write!(f, " {op} ")?;
self.fmt(f, self[node].inputs[1], rcs)?;
write!(f, ")")?;
}
Kind::Return => {
write!(f, "{}: return [{:?}] ", node, self[node].inputs[0])?;
self.fmt(f, self[node].inputs[1], rcs)?;
writeln!(f)?;
self.fmt(f, self[node].inputs[2], rcs)?;
}
Kind::ConstInt { value } => write!(f, "{}", value)?,
Kind::End => {
if is_ready() {
writeln!(f, "{}: {:?}", node, self[node].kind)?;
}
}
Kind::Tuple { index } => {
if index != 0 && self[self[node].inputs[0]].kind == Kind::Start {
write!(f, "{:?}.{}", self[self[node].inputs[0]].kind, index)?;
} else if is_ready() {
writeln!(f, "{}: {:?}", node, self[node].kind)?;
for &o in &self[node].outputs {
if self.is_cfg(o) {
self.fmt(f, o, rcs)?;
}
}
}
}
Kind::Start => 'b: {
if !is_ready() {
break 'b;
}
writeln!(f, "{}: {:?}", node, self[node].kind)?;
for &o in &self[node].outputs {
self.fmt(f, o, rcs)?;
}
}
}
Ok(())
}
fn is_cfg(&self, o: Nid) -> bool {
matches!(self[o].kind, Kind::Start | Kind::End | Kind::Return | Kind::Tuple { .. })
}
} }
impl<T> ops::Index<u32> for PoolVec<T> { impl ops::Index<u32> for Nodes {
type Output = T; type Output = Node;
fn index(&self, index: u32) -> &Self::Output { fn index(&self, index: u32) -> &Self::Output {
match &self.values[index as usize] { match &self.values[index as usize] {
@ -69,7 +348,7 @@ impl<T> ops::Index<u32> for PoolVec<T> {
} }
} }
impl<T> ops::IndexMut<u32> for PoolVec<T> { impl ops::IndexMut<u32> for Nodes {
fn index_mut(&mut self, index: u32) -> &mut Self::Output { fn index_mut(&mut self, index: u32) -> &mut Self::Output {
match &mut self.values[index as usize] { match &mut self.values[index as usize] {
PoolSlot::Value(value) => value, PoolSlot::Value(value) => value,
@ -78,28 +357,75 @@ impl<T> ops::IndexMut<u32> for PoolVec<T> {
} }
} }
enum PoolSlot<T> { #[derive(Debug)]
Value(T), enum PoolSlot {
Value(Node),
Next(u32), Next(u32),
} }
pub enum Inputs { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(u8)]
pub enum Kind {
Start, Start,
End, End,
BinOp { op: lexer::TokenKind, lhs: Nid, rhs: Nid }, Return,
Return { value: Nid, cfg: Nid },
Tuple { on: Nid, index: Nid },
ConstInt { value: i64 }, ConstInt { value: i64 },
Tuple { index: u32 },
BinOp { op: lexer::TokenKind },
} }
impl Kind {
fn disc(&self) -> u8 {
unsafe { *(self as *const _ as *const u8) }
}
}
const MAX_INPUTS: usize = 3;
#[derive(Debug)]
pub struct Node { pub struct Node {
pub inputs: Inputs, pub inputs: [Nid; MAX_INPUTS],
pub kind: Kind,
pub depth: u32, pub depth: u32,
pub lock_rc: u32,
pub ty: ty::Id, pub ty: ty::Id,
pub outputs: Vec<Nid>, pub outputs: Vec<Nid>,
} }
pub type Nodes = PoolVec<Node>; impl Node {
fn is_dangling(&self) -> bool {
self.outputs.len() + self.lock_rc as usize == 0
}
fn inputs(&self) -> &[Nid] {
let len = self.inputs.iter().position(|&n| n == NILL).unwrap_or(MAX_INPUTS);
&self.inputs[..len]
}
fn inputs_mut(&mut self) -> &mut [Nid] {
let len = self.inputs.iter().position(|&n| n == NILL).unwrap_or(MAX_INPUTS);
&mut self.inputs[..len]
}
}
impl fmt::Display for Nodes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt(
f,
0,
&mut self
.values
.iter()
.map(|s| match s {
PoolSlot::Value(Node { kind: Kind::Start, .. }) => 1,
PoolSlot::Value(Node { kind: Kind::End, ref outputs, .. }) => outputs.len(),
PoolSlot::Value(val) => val.inputs().len(),
PoolSlot::Next(_) => 0,
})
.collect::<Vec<_>>(),
)
}
}
type Offset = u32; type Offset = u32;
type Size = u32; type Size = u32;
@ -411,6 +737,14 @@ mod ty {
} }
} }
} }
pub fn bin_ret(ty: Id, op: TokenKind) -> Id {
use TokenKind as T;
match op {
T::Lt | T::Gt | T::Le | T::Ge | T::Ne | T::Eq => BOOL.into(),
_ => ty,
}
}
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -430,10 +764,14 @@ struct ItemCtx {
file: FileId, file: FileId,
id: ty::Kind, id: ty::Kind,
ret: Option<ty::Id>, ret: Option<ty::Id>,
start: Nid,
end: Nid,
cfg: Nid,
task_base: usize, task_base: usize,
snap: Snapshot, snap: Snapshot,
nodes: Nodes,
loops: Vec<Loop>, loops: Vec<Loop>,
vars: Vec<Variable>, vars: Vec<Variable>,
} }
@ -685,7 +1023,7 @@ struct Ctx {
impl Ctx { impl Ctx {
pub fn with_ty(self, ty: impl Into<ty::Id>) -> Self { pub fn with_ty(self, ty: impl Into<ty::Id>) -> Self {
Self { ty: Some(ty.into()), ..self } Self { ty: Some(ty.into()) }
} }
} }
@ -694,69 +1032,6 @@ struct Pool {
cis: Vec<ItemCtx>, cis: Vec<ItemCtx>,
} }
#[derive(Default)]
pub struct LoggedMem {
pub mem: hbvm::mem::HostMemory,
}
impl hbvm::mem::Memory for LoggedMem {
unsafe fn load(
&mut self,
addr: hbvm::mem::Address,
target: *mut u8,
count: usize,
) -> Result<(), hbvm::mem::LoadError> {
log::dbg!(
"load: {:x} {:?}",
addr.get(),
core::slice::from_raw_parts(addr.get() as *const u8, count)
.iter()
.rev()
.map(|&b| format!("{b:02x}"))
.collect::<String>()
);
self.mem.load(addr, target, count)
}
unsafe fn store(
&mut self,
addr: hbvm::mem::Address,
source: *const u8,
count: usize,
) -> Result<(), hbvm::mem::StoreError> {
log::dbg!(
"store: {:x} {:?}",
addr.get(),
core::slice::from_raw_parts(source, count)
.iter()
.rev()
.map(|&b| format!("{b:02x}"))
.collect::<String>()
);
self.mem.store(addr, source, count)
}
unsafe fn prog_read<T: Copy>(&mut self, addr: hbvm::mem::Address) -> T {
log::dbg!(
"read-typed: {:x} {} {:?}",
addr.get(),
std::any::type_name::<T>(),
if core::mem::size_of::<T>() == 1
&& let Some(nm) =
instrs::NAMES.get(std::ptr::read(addr.get() as *const u8) as usize)
{
nm.to_string()
} else {
core::slice::from_raw_parts(addr.get() as *const u8, core::mem::size_of::<T>())
.iter()
.map(|&b| format!("{:02x}", b))
.collect::<String>()
}
);
self.mem.prog_read(addr)
}
}
#[derive(Default)] #[derive(Default)]
pub struct Codegen { pub struct Codegen {
pub files: Vec<parser::Ast>, pub files: Vec<parser::Ast>,
@ -770,9 +1045,18 @@ pub struct Codegen {
impl Codegen { impl Codegen {
pub fn generate(&mut self) { pub fn generate(&mut self) {
self.find_or_declare(0, 0, None, "main"); self.find_or_declare(0, 0, None, "main");
self.make_func_reachable(0);
self.complete_call_graph_low(); self.complete_call_graph_low();
} }
fn make_func_reachable(&mut self, func: ty::Func) {
let fuc = &mut self.tys.funcs[func as usize];
if fuc.offset == u32::MAX {
fuc.offset = task::id(self.tasks.len() as _);
self.tasks.push(Some(FTask { file: fuc.file, id: func }));
}
}
fn expr(&mut self, expr: &Expr) -> Option<Nid> { fn expr(&mut self, expr: &Expr) -> Option<Nid> {
self.expr_ctx(expr, Ctx::default()) self.expr_ctx(expr, Ctx::default())
} }
@ -787,7 +1071,80 @@ impl Codegen {
} }
fn expr_ctx(&mut self, expr: &Expr, ctx: Ctx) -> Option<Nid> { fn expr_ctx(&mut self, expr: &Expr, ctx: Ctx) -> Option<Nid> {
self.report_unhandled_ast(expr, "bruh"); match *expr {
Expr::Comment { .. } => Some(NILL),
Expr::Ident { pos, id, .. } => {
let msg = "i know nothing about this name gal which is vired\
because we parsed succesfully";
Some(
self.ci
.vars
.iter()
.find(|v| v.id == id)
.unwrap_or_else(|| self.report(pos, msg))
.value,
)
}
Expr::BinOp { left, op, right } => {
let lhs = self.expr_ctx(left, ctx)?;
self.ci.nodes.lock(lhs);
let rhs = self.expr_ctx(right, Ctx::default().with_ty(self.tof(lhs)));
self.ci.nodes.unlock(lhs);
let rhs = rhs?;
let ty = self.assert_ty(left.pos(), self.tof(rhs), self.tof(lhs), false);
let id =
self.ci.nodes.new_node(ty::bin_ret(ty, op), Kind::BinOp { op }, [lhs, rhs]);
Some(id)
}
Expr::Return { pos, val } => {
let ty = if let Some(val) = val {
let value = self.expr_ctx(val, Ctx { ty: self.ci.ret })?;
let inps = [self.ci.cfg, value, self.ci.end];
self.ci.cfg = self.ci.nodes.new_node(ty::VOID, Kind::Return, inps);
self.tof(value)
} else {
ty::VOID.into()
};
let expected = *self.ci.ret.get_or_insert(ty);
_ = self.assert_ty(pos, ty, expected, true);
None
}
Expr::Block { stmts, .. } => {
let base = self.ci.vars.len();
let mut ret = Some(NILL);
for stmt in stmts {
ret = ret.and(self.expr(stmt));
if let Some(id) = ret {
_ = self.assert_ty(stmt.pos(), self.tof(id), ty::VOID.into(), true);
} else {
break;
}
}
for var in self.ci.vars.drain(base..) {
self.ci.nodes.unlock_free(var.value);
}
ret
}
Expr::Number { value, .. } => Some(self.ci.nodes.new_node(
ctx.ty.unwrap_or(ty::UINT.into()),
Kind::ConstInt { value },
[],
)),
ref e => self.report_unhandled_ast(e, "bruh"),
}
}
#[inline(always)]
fn tof(&self, id: Nid) -> ty::Id {
if id == NILL {
return ty::VOID.into();
}
self.ci.nodes[id].ty
} }
//#[must_use] //#[must_use]
@ -805,12 +1162,60 @@ impl Codegen {
} }
fn handle_task(&mut self, FTask { file, id }: FTask) { fn handle_task(&mut self, FTask { file, id }: FTask) {
todo!(); let func = self.tys.funcs[id as usize];
debug_assert!(func.file == file);
let sig = func.sig.unwrap();
let ast = self.files[file as usize].clone();
let expr = func.expr.get(&ast).unwrap();
let repl = ItemCtx {
file,
id: ty::Kind::Func(id),
ret: Some(sig.ret),
..self.pool.cis.pop().unwrap_or_default()
};
let prev_ci = std::mem::replace(&mut self.ci, repl);
self.ci.start = self.ci.nodes.new_node(ty::VOID, Kind::Start, []);
self.ci.end = self.ci.nodes.new_node(ty::VOID, Kind::End, []);
self.ci.cfg = self.ci.nodes.new_node(ty::VOID, Kind::Tuple { index: 0 }, [self.ci.start]);
let Expr::BinOp {
left: Expr::Ident { .. },
op: TokenKind::Decl,
right: &Expr::Closure { body, args, .. },
} = expr
else {
unreachable!("{expr}")
};
let mut sig_args = sig.args.range();
for (arg, index) in args.iter().zip(1u32..) {
let ty = self.tys.args[sig_args.next().unwrap()];
let value = self.ci.nodes.new_node(ty, Kind::Tuple { index }, [self.ci.start]);
self.ci.nodes.lock(value);
let sym = parser::find_symbol(&ast.symbols, arg.id);
assert!(sym.flags & idfl::COMPTIME == 0, "TODO");
self.ci.vars.push(Variable { id: arg.id, value });
}
if self.expr(body).is_some() {
self.report(body.pos(), "expected all paths in the fucntion to return");
}
for var in self.ci.vars.drain(..) {
self.ci.nodes.unlock(var.value);
}
//self.pool.cis.push(std::mem::replace(&mut self.ci, prev_ci));
} }
// TODO: sometimes its better to do this in bulk // TODO: sometimes its better to do this in bulk
fn ty(&mut self, expr: &Expr) -> ty::Id { fn ty(&mut self, expr: &Expr) -> ty::Id {
todo!() match *expr {
Expr::Ident { id, .. } if ident::is_null(id) => id.into(),
ref e => self.report_unhandled_ast(e, "type"),
}
} }
fn find_or_declare( fn find_or_declare(
@ -820,7 +1225,89 @@ impl Codegen {
name: Option<Ident>, name: Option<Ident>,
lit_name: &str, lit_name: &str,
) -> ty::Kind { ) -> ty::Kind {
todo!() log::dbg!("find_or_declare: {lit_name} {file}");
let f = self.files[file as usize].clone();
let Some((expr, ident)) = f.find_decl(name.ok_or(lit_name)) else {
match name.ok_or(lit_name) {
Ok(name) => {
let name = self.cfile().ident_str(name);
self.report(pos, format_args!("undefined indentifier: {name}"))
}
Err("main") => self.report(
pos,
format_args!(
"missing main function in '{}', compiler can't \
emmit libraries since such concept is not defined",
f.path
),
),
Err(name) => self.report(pos, format_args!("undefined indentifier: {name}")),
}
};
if let Some(existing) = self.tys.syms.get(&SymKey { file, ident }) {
if let ty::Kind::Func(id) = existing.expand()
&& let func = &mut self.tys.funcs[id as usize]
&& func.offset != u32::MAX
&& let Err(idx) = task::unpack(func.offset)
{
func.offset = task::id(self.tasks.len());
let task = self.tasks[idx].take();
self.tasks.push(task);
}
return existing.expand();
}
let prev_file = std::mem::replace(&mut self.ci.file, file);
let sym = match expr {
Expr::BinOp {
left: &Expr::Ident { .. },
op: TokenKind::Decl,
right: &Expr::Closure { pos, args, ret, .. },
} => {
let func = Func {
file,
sig: '_b: {
let arg_base = self.tys.args.len();
for arg in args {
let sym = parser::find_symbol(&f.symbols, arg.id);
assert!(sym.flags & idfl::COMPTIME == 0, "TODO");
let ty = self.ty(&arg.ty);
self.tys.args.push(ty);
}
let args = self.pack_args(pos, arg_base);
let ret = self.ty(ret);
Some(Sig { args, ret })
},
expr: {
let refr = ExprRef::new(expr);
debug_assert!(refr.get(&f).is_some());
refr
},
offset: u32::MAX,
};
let id = self.tys.funcs.len() as _;
self.tys.funcs.push(func);
ty::Kind::Func(id)
}
Expr::BinOp {
left: &Expr::Ident { .. },
op: TokenKind::Decl,
right: Expr::Struct { fields, .. },
} => ty::Kind::Struct(self.build_struct(fields)),
Expr::BinOp { .. } => {
todo!()
}
e => unimplemented!("{e:#?}"),
};
self.ci.file = prev_file;
self.tys.syms.insert(SymKey { ident, file }, sym.compress());
sym
} }
fn ty_display(&self, ty: ty::Id) -> ty::Display { fn ty_display(&self, ty: ty::Id) -> ty::Display {
@ -829,8 +1316,10 @@ impl Codegen {
#[must_use] #[must_use]
#[track_caller] #[track_caller]
fn assert_ty(&self, pos: Pos, ty: ty::Id, expected: ty::Id) -> ty::Id { fn assert_ty(&self, pos: Pos, ty: ty::Id, expected: ty::Id, preserve_expected: bool) -> ty::Id {
if let Some(res) = ty.try_upcast(expected) { if let Some(res) = ty.try_upcast(expected)
&& (!preserve_expected || res == expected)
{
res res
} else { } else {
let ty = self.ty_display(ty); let ty = self.ty_display(ty);
@ -947,10 +1436,15 @@ mod tests {
}; };
codegen.generate(); codegen.generate();
use std::fmt::Write;
write!(output, "{}", codegen.ci.nodes).unwrap();
} }
crate::run_tests! { generate: crate::run_tests! { generate:
arithmetic => README; arithmetic => README;
const_folding_with_arg => README;
//variables => README; //variables => README;
//functions => README; //functions => README;
//comments => README; //comments => README;

View file

@ -0,0 +1,4 @@
0: Start
2: Tuple { index: 0 }
4: return [2] 1
1: End