Restructuring the compiler

Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
Jakub Doka 2024-12-01 14:01:44 +01:00
parent cf672beb79
commit 3b4b30b2bd
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
12 changed files with 1600 additions and 1484 deletions

View file

@ -6,7 +6,8 @@ use {
alloc::{string::String, vec::Vec},
core::ffi::CStr,
hblang::{
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
backend::hbvm::HbvmBackend,
son::{Codegen, CodegenCtx},
ty::Module,
Ent,
},

View file

@ -244,7 +244,13 @@ main := fn(): uint {
#### enums
```hb
Enum := enum {A, B, C}
Enum := enum {
A,
B,
C,
$default := Self.A
}
some_enum := fn(): Enum return .A
@ -252,7 +258,7 @@ main := fn(): uint {
e := some_enum()
match e {
.A => return 0,
Enum.default => return 0,
_ => return 100,
}
}

View file

@ -1,15 +1,14 @@
use {
super::{AssemblySpec, Backend, Nid, Node, Nodes, VOID},
super::{AssemblySpec, Backend},
crate::{
lexer::TokenKind,
parser,
son::{debug_assert_matches, Kind, MEM},
ty::{self, Arg, Loc, Module},
son::{Kind, Nid, Node, Nodes, MEM, VOID},
ty::{self, Arg, Loc, Module, Offset, Sig, Size, Types},
utils::{Ent, EntVec},
Offset, Sig, Size, Types,
},
alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec},
core::{mem, ops::Range},
core::{assert_matches::debug_assert_matches, mem, ops::Range},
hbbytecode::{self as instrs, *},
reg::Reg,
};
@ -254,30 +253,24 @@ impl Backend for HbvmBackend {
hbbytecode::disasm(&mut sluce, &functions, output, eca_handler)
}
fn emit_ct_body(
&mut self,
id: ty::Func,
nodes: &mut Nodes,
tys: &Types,
files: &[parser::Ast],
) {
fn emit_ct_body(&mut self, id: ty::Func, nodes: &Nodes, tys: &Types, files: &[parser::Ast]) {
self.emit_body(id, nodes, tys, files);
let fd = &mut self.funcs[id];
fd.code.truncate(fd.code.len() - instrs::jala(0, 0, 0).0);
emit(&mut fd.code, instrs::tx());
}
fn emit_body(&mut self, id: ty::Func, nodes: &mut Nodes, tys: &Types, files: &[parser::Ast]) {
fn emit_body(&mut self, id: ty::Func, nodes: &Nodes, tys: &Types, files: &[parser::Ast]) {
let sig = tys.ins.funcs[id].sig.unwrap();
debug_assert!(self.code.is_empty());
self.offsets.clear();
self.offsets.resize(nodes.values.len(), Offset::MAX);
self.offsets.resize(nodes.len(), Offset::MAX);
let mut stack_size = 0;
'_compute_stack: {
let mems = mem::take(&mut nodes[MEM].outputs);
let mems = &nodes[MEM].outputs;
for &stck in mems.iter() {
if !matches!(nodes[stck].kind, Kind::Stck | Kind::Arg) {
debug_assert_matches!(
@ -300,7 +293,6 @@ impl Backend for HbvmBackend {
}
self.offsets[stck as usize] = stack_size - self.offsets[stck as usize];
}
nodes[MEM].outputs = mems;
}
let (saved, tail) = self.emit_body_code(nodes, sig, tys, files);

View file

@ -1,20 +1,16 @@
use {
crate::{
parser, quad_sort,
son::{
debug_assert_matches,
hbvm::{
reg::{self, Reg},
HbvmBackend, Nid, Nodes, PLoc,
},
Kind, ARG_START, MEM, VOID,
backend::hbvm::{
reg::{self, Reg},
HbvmBackend, Nid, Nodes, PLoc,
},
ty::{self, Arg, Loc},
parser, quad_sort,
son::{Kind, ARG_START, MEM, VOID},
ty::{self, Arg, Loc, Sig, Types},
utils::BitSet,
Sig, Types,
},
alloc::{borrow::ToOwned, vec::Vec},
core::{mem, ops::Range, u8, usize},
core::{assert_matches::debug_assert_matches, mem, ops::Range},
hbbytecode::{self as instrs},
};
@ -27,7 +23,6 @@ impl HbvmBackend {
files: &[parser::Ast],
) -> (usize, bool) {
let tail = Function::build(nodes, tys, &mut self.ralloc, sig);
nodes.basic_blocks();
let strip_load = |value| match nodes[value].kind {
Kind::Load { .. } if nodes[value].ty.loc(tys) == Loc::Stack => nodes[value].inputs[1],
@ -69,9 +64,8 @@ impl HbvmBackend {
let atr = |allc: Nid| {
let allc = strip_load(allc);
debug_assert_eq!(
nodes[allc].lock_rc.get(),
0,
debug_assert!(
nodes.is_unlocked(allc),
"{:?} {}",
nodes[allc],
ty::Display::new(tys, files, nodes[allc].ty)
@ -114,9 +108,8 @@ impl HbvmBackend {
let atr = |allc: Nid| {
let allc = strip_load(allc);
debug_assert_eq!(
nodes[allc].lock_rc.get(),
0,
debug_assert!(
nodes.is_unlocked(allc),
"{:?} {}",
nodes[allc],
ty::Display::new(tys, files, nodes[allc].ty)
@ -164,12 +157,23 @@ impl HbvmBackend {
}
}
debug_assert_eq!(moves.len(), {
moves.sort_unstable();
moves.dedup();
moves.len()
});
// code makes sure all moves are ordered so that register is only moved
// into after all its uses
//
// in case of cycles, swaps are used instead in which case the conflicting
// move is removed and remining moves are replaced with swaps
const CYCLE_SENTINEL: u8 = u8::MAX;
debug_assert_eq!(
{
let mut dests = moves.iter().map(|&[d, ..]| d).collect::<Vec<_>>();
dests.sort_unstable();
dests.dedup();
dests.len()
},
moves.len()
);
let mut graph = [u8::MAX; 256];
for &[d, s, _] in moves.iter() {
graph[d as usize] = s;
@ -193,18 +197,20 @@ impl HbvmBackend {
// cut the cycle
graph[c as usize] = u8::MAX;
// mark cycyle
*depth = u8::MAX;
*depth = CYCLE_SENTINEL;
}
quad_sort(&mut moves, |a, b| a[2].cmp(&b[2]).reverse());
for [mut d, mut s, depth] in moves {
if depth == u8::MAX {
if depth == CYCLE_SENTINEL {
while graph[s as usize] != u8::MAX {
self.emit(instrs::swa(d, s));
d = s;
mem::swap(&mut graph[s as usize], &mut s);
}
// trivial cycle denotes this move was already generated in a
// cycyle
graph[s as usize] = s;
} else if graph[s as usize] != s {
self.emit(instrs::cp(d, s));
@ -383,8 +389,8 @@ impl<'a> Function<'a> {
fn build(nodes: &'a Nodes, tys: &'a Types, func: &'a mut Res, sig: Sig) -> bool {
func.blocks.clear();
func.instrs.clear();
func.backrefs.resize(nodes.values.len(), u16::MAX);
func.visited.clear(nodes.values.len());
func.backrefs.resize(nodes.len(), u16::MAX);
func.visited.clear(nodes.len());
let mut s = Self { tail: true, nodes, tys, sig, func };
s.emit_node(VOID);
debug_assert!(s.func.blocks.array_chunks().all(|[a, b]| a.end == b.start));
@ -528,7 +534,7 @@ impl<'a> Function<'a> {
impl Nodes {
fn vreg_count(&self) -> usize {
self.values.len()
self.len()
}
fn use_block_of(&self, inst: Nid, uinst: Nid) -> Nid {
@ -576,7 +582,7 @@ impl Nodes {
.flat_map(|(p, ls)| ls.iter().map(move |l| (p, l)))
.filter(|&(o, &n)| self.is_data_dep(o, n))
.map(|(p, &n)| (self.use_block_of(p, n), n))
.inspect(|&(_, n)| debug_assert_eq!(self[n].lock_rc.get(), 0)),
.inspect(|&(_, n)| debug_assert!(self.is_unlocked(n))),
)
.into_iter()
.flatten()
@ -616,7 +622,7 @@ impl<'a> Regalloc<'a> {
debug_assert!(self.res.dfs_buf.is_empty());
let mut bundle = Bundle::new(self.res.instrs.len());
self.res.visited.clear(self.nodes.values.len());
self.res.visited.clear(self.nodes.len());
for i in (0..self.res.blocks.len()).rev() {
for [a, rest @ ..] in self.nodes.phi_inputs_of(self.res.blocks[i].entry) {
@ -650,7 +656,7 @@ impl<'a> Regalloc<'a> {
fn collect_bundle(&mut self, inst: Nid, into: &mut Bundle) {
let dom = self.nodes.idom_of(inst);
self.res.dfs_seem.clear(self.nodes.values.len());
self.res.dfs_seem.clear(self.nodes.len());
for (cursor, uinst) in self.nodes.uses_of(inst) {
if !self.res.dfs_seem.set(uinst) {
continue;

View file

@ -1,7 +1,9 @@
use {
crate::{
lexer::{self, Lexer, TokenKind},
parser::{self, CommentOr, CtorField, EnumField, Expr, Poser, Radix, StructField},
parser::{
self, CommentOr, CtorField, EnumField, Expr, FieldList, Poser, Radix, StructField,
},
},
core::{
fmt::{self},
@ -260,6 +262,32 @@ impl<'a> Formatter<'a> {
}
}
fn fmt_fields<F: core::fmt::Write, T: Poser + Copy>(
&mut self,
f: &mut F,
keyword: &str,
trailing_comma: bool,
fields: FieldList<T>,
fmt: impl Fn(&mut Self, &T, &mut F) -> Result<(), fmt::Error>,
) -> fmt::Result {
f.write_str(keyword)?;
f.write_str(" {")?;
self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| {
match field {
CommentOr::Or(Ok(field)) => fmt(s, field, f)?,
CommentOr::Or(Err(scope)) => {
s.fmt_list(f, true, "", "", scope, Self::fmt)?;
return Ok(false);
}
CommentOr::Comment { literal, .. } => {
f.write_str(literal)?;
f.write_str("\n")?;
}
}
Ok(field.or().is_some())
})
}
pub fn fmt<F: core::fmt::Write>(&mut self, expr: &Expr, f: &mut F) -> fmt::Result {
macro_rules! impl_parenter {
($($name:ident => $pat:pat,)*) => {
@ -305,41 +333,25 @@ impl<'a> Formatter<'a> {
f.write_str("packed ")?;
}
f.write_str("struct {")?;
self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| {
match field {
CommentOr::Or(Ok(StructField { name, ty, .. })) => {
f.write_str(name)?;
f.write_str(": ")?;
s.fmt(ty, f)?
}
CommentOr::Or(Err(scope)) => {
s.fmt_list(f, true, "", "", scope, Self::fmt)?;
return Ok(false);
}
CommentOr::Comment { literal, .. } => {
f.write_str(literal)?;
f.write_str("\n")?;
}
}
Ok(field.or().is_some())
})
}
Expr::Enum { variants, trailing_comma, .. } => {
f.write_str("enum {")?;
self.fmt_list_low(f, trailing_comma, "}", ",", variants, |_, var, f| {
match var {
CommentOr::Or(EnumField { name, .. }) => {
f.write_str(name)?;
}
CommentOr::Comment { literal, .. } => {
f.write_str(literal)?;
f.write_str("\n")?;
}
}
Ok(var.or().is_some())
})
self.fmt_fields(
f,
"struct",
trailing_comma,
fields,
|s, StructField { name, ty, .. }, f| {
f.write_str(name)?;
f.write_str(": ")?;
s.fmt(ty, f)
},
)
}
Expr::Enum { variants, trailing_comma, .. } => self.fmt_fields(
f,
"enum",
trailing_comma,
variants,
|_, EnumField { name, .. }, f| f.write_str(name),
),
Expr::Ctor { ty, fields, trailing_comma, .. } => {
if let Some(ty) = ty {
self.fmt_paren(ty, f, unary)?;

View file

@ -1,7 +1,8 @@
use {
crate::{
backend::hbvm::HbvmBackend,
parser::{Ast, Ctx, FileKind},
son::{self, hbvm::HbvmBackend},
son::{self},
ty, FnvBuildHasher,
},
alloc::{string::String, vec::Vec},

View file

@ -1,8 +1,9 @@
use {
crate::{
backend::hbvm::HbvmBackend,
lexer::TokenKind,
parser,
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
son::{Codegen, CodegenCtx},
ty::Module,
},
alloc::string::String,

File diff suppressed because it is too large Load diff

View file

@ -369,62 +369,35 @@ impl<'a, 'b> Parser<'a, 'b> {
expr
}
T::Struct => E::Struct {
pos,
packed: core::mem::take(&mut self.packed),
fields: {
self.ns_bound = self.ctx.idents.len();
self.expect_advance(T::LBrace)?;
self.collect_list(T::Comma, T::RBrace, |s| {
let tok = s.token;
Some(if s.advance_if(T::Comment) {
CommentOr::Comment { literal: s.tok_str(tok), pos: tok.start }
} else if s.lexer.taste().kind == T::Colon {
let name = s.expect_advance(T::Ident)?;
s.expect_advance(T::Colon)?;
CommentOr::Or(Ok(StructField {
pos: name.start,
name: s.tok_str(name),
ty: s.expr()?,
}))
} else {
must_trail = true;
CommentOr::Or(Err(
s.collect_list_low(T::Semi, T::RBrace, true, |s| s.expr_low(true))
))
})
})
},
captured: {
self.ns_bound = prev_boundary;
let captured = &mut self.ctx.captured[prev_captured..];
crate::quad_sort(captured, core::cmp::Ord::cmp);
let preserved = captured.partition_dedup().0.len();
self.ctx.captured.truncate(prev_captured + preserved);
self.arena.alloc_slice(&self.ctx.captured[prev_captured..])
},
pos: {
if self.ns_bound == 0 {
// we might save some memory
self.ctx.captured.clear();
fields: self.collect_fields(&mut must_trail, |s| {
if s.lexer.taste().kind != T::Colon {
return Some(None);
}
pos
},
let name = s.expect_advance(T::Ident)?;
s.expect_advance(T::Colon)?;
Some(Some(StructField {
pos: name.start,
name: s.tok_str(name),
ty: s.expr()?,
}))
})?,
captured: self.collect_captures(prev_boundary, prev_captured),
trailing_comma: core::mem::take(&mut self.trailing_sep) || must_trail,
},
T::Enum => E::Enum {
pos,
variants: {
self.expect_advance(T::LBrace)?;
self.collect_list(T::Comma, T::RBrace, |s| {
let tok = s.token;
Some(if s.advance_if(T::Comment) {
CommentOr::Comment { literal: s.tok_str(tok), pos: tok.start }
} else {
let name = s.expect_advance(T::Ident)?;
CommentOr::Or(EnumField { pos: name.start, name: s.tok_str(name) })
})
})
},
trailing_comma: core::mem::take(&mut self.trailing_sep),
variants: self.collect_fields(&mut must_trail, |s| {
if !matches!(s.lexer.taste().kind, T::Comma | T::RBrace) {
return Some(None);
}
let name = s.expect_advance(T::Ident)?;
Some(Some(EnumField { pos: name.start, name: s.tok_str(name) }))
})?,
captured: self.collect_captures(prev_boundary, prev_captured),
trailing_comma: core::mem::take(&mut self.trailing_sep) || must_trail,
},
T::Ident | T::CtIdent => {
let (id, is_first) = self.resolve_ident(token);
@ -624,6 +597,45 @@ impl<'a, 'b> Parser<'a, 'b> {
}
}
fn collect_fields<T: Copy>(
&mut self,
must_trail: &mut bool,
mut parse_field: impl FnMut(&mut Self) -> Option<Option<T>>,
) -> Option<FieldList<'a, T>> {
use TokenKind as T;
self.ns_bound = self.ctx.idents.len();
self.expect_advance(T::LBrace)?;
Some(self.collect_list(T::Comma, T::RBrace, |s| {
let tok = s.token;
Some(if s.advance_if(T::Comment) {
CommentOr::Comment { literal: s.tok_str(tok), pos: tok.start }
} else if let Some(field) = parse_field(s)? {
CommentOr::Or(Ok(field))
} else {
*must_trail = true;
CommentOr::Or(Err(
s.collect_list_low(T::Semi, T::RBrace, true, |s| s.expr_low(true))
))
})
}))
}
fn collect_captures(&mut self, prev_captured: usize, prev_boundary: usize) -> &'a [Ident] {
self.ns_bound = prev_boundary;
let captured = &mut self.ctx.captured[prev_captured..];
crate::quad_sort(captured, core::cmp::Ord::cmp);
let preserved = captured.partition_dedup().0.len();
self.ctx.captured.truncate(prev_captured + preserved);
let slc = self.arena.alloc_slice(&self.ctx.captured[prev_captured..]);
if self.ns_bound == 0 {
// we might save some memory
self.ctx.captured.clear();
}
slc
}
fn advance_ident(&mut self) -> Option<Token> {
let next = self.next();
if matches!(next.kind, TokenKind::Ident | TokenKind::CtIdent) {
@ -841,6 +853,8 @@ pub enum Radix {
Decimal = 10,
}
pub type FieldList<'a, T> = &'a [CommentOr<'a, Result<T, &'a [Expr<'a>]>>];
generate_expr! {
/// `LIST(start, sep, end, elem) => start { elem sep } [elem] end`
/// `OP := grep for `#define OP:`
@ -958,7 +972,7 @@ generate_expr! {
/// `'struct' LIST('{', ',', '}', Ident ':' Expr)`
Struct {
pos: Pos,
fields: &'a [CommentOr<'a, Result<StructField<'a>, &'a[Self]>>],
fields: FieldList<'a, StructField<'a>>,
captured: &'a [Ident],
trailing_comma: bool,
packed: bool,
@ -966,7 +980,8 @@ generate_expr! {
/// `'enum' LIST('{', ',', '}', Ident)`
Enum {
pos: Pos,
variants: &'a [CommentOr<'a, EnumField<'a>>],
variants: FieldList<'a, EnumField<'a>>,
captured: &'a [Ident],
trailing_comma: bool,
},
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`

View file

@ -1,21 +1,25 @@
use {
self::{
hbvm::{Comptime, HbvmBackend},
strong_ref::StrongRef,
},
self::strong_ref::StrongRef,
crate::{
backend::{
hbvm::{Comptime, HbvmBackend},
Backend,
},
ctx_map::CtxEntry,
debug,
lexer::{self, TokenKind},
parser::{
self,
idfl::{self},
CommentOr, CtorField, Expr, ExprRef, MatchBranch, Pos,
CommentOr, CtorField, Expr, ExprRef, FieldList, MatchBranch, Pos,
},
ty::{
self, Arg, ArrayLen, CompState, ConstData, EnumData, EnumField, FTask, FuncData,
GlobalData, Loc, Module, Offset, OffsetIter, OptLayout, Sig, StringRef, StructData,
StructField, SymKey, Tuple, TypeBase, TypeIns, Types,
},
ty::{self, Arg, ArrayLen, Loc, Module, Tuple},
utils::{BitSet, Ent, Vc},
CompState, Const, Enum, EnumField, FTask, Func, Global, Ident, Offset, OffsetIter,
OptLayout, Sig, StringRef, Struct, StructField, SymKey, Types,
Ident,
},
alloc::{string::String, vec::Vec},
core::{
@ -29,52 +33,18 @@ use {
hbbytecode::DisasmError,
};
const VOID: Nid = 0;
const NEVER: Nid = 1;
const ENTRY: Nid = 2;
const MEM: Nid = 3;
const LOOPS: Nid = 4;
const ARG_START: usize = 3;
pub const VOID: Nid = 0;
pub const NEVER: Nid = 1;
pub const ENTRY: Nid = 2;
pub const MEM: Nid = 3;
pub const LOOPS: Nid = 4;
pub const ARG_START: usize = 3;
const DEFAULT_ACLASS: usize = 0;
const GLOBAL_ACLASS: usize = 1;
pub mod hbvm;
type Nid = u16;
pub type Nid = u16;
type AClassId = i16;
pub struct AssemblySpec {
entry: u32,
code_length: u64,
data_length: u64,
}
pub trait Backend {
fn assemble_reachable(
&mut self,
from: ty::Func,
types: &Types,
to: &mut Vec<u8>,
) -> AssemblySpec;
fn disasm<'a>(
&'a self,
sluce: &[u8],
eca_handler: &mut dyn FnMut(&mut &[u8]),
types: &'a Types,
files: &'a [parser::Ast],
output: &mut String,
) -> Result<(), hbbytecode::DisasmError<'a>>;
fn emit_body(&mut self, id: ty::Func, ci: &mut Nodes, tys: &Types, files: &[parser::Ast]);
fn emit_ct_body(&mut self, id: ty::Func, ci: &mut Nodes, tys: &Types, files: &[parser::Ast]) {
self.emit_body(id, ci, tys, files);
}
fn assemble_bin(&mut self, from: ty::Func, types: &Types, to: &mut Vec<u8>) {
self.assemble_reachable(from, types, to);
}
}
type Lookup = crate::ctx_map::CtxMap<Nid>;
impl crate::ctx_map::CtxEntry for Nid {
@ -124,7 +94,25 @@ impl Default for Nodes {
}
impl Nodes {
fn loop_depth(&self, target: Nid, scheds: Option<&[Nid]>) -> LoopDepth {
#[inline]
pub fn len(&self) -> usize {
self.values.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
fn as_ty(&self, cint: Nid) -> ty::Id {
debug_assert_eq!(self[cint].ty, ty::Id::TYPE);
ty::Id::from(match self[cint].kind {
Kind::CInt { value } => value as u64,
_ => unreachable!("triing to cast non constant to a type: {:?}", self[cint]),
})
}
pub fn loop_depth(&self, target: Nid, scheds: Option<&[Nid]>) -> LoopDepth {
self[target].loop_depth.set(match self[target].kind {
Kind::Region | Kind::Entry | Kind::Then | Kind::Else | Kind::Call { .. } | Kind::If => {
if self[target].loop_depth.get() != 0 {
@ -552,7 +540,7 @@ impl Nodes {
self[to].inputs.push(from);
}
fn use_block(&self, target: Nid, from: Nid, scheds: Option<&[Nid]>) -> Nid {
pub fn use_block(&self, target: Nid, from: Nid, scheds: Option<&[Nid]>) -> Nid {
if self[from].kind != Kind::Phi {
return self.idom(from, scheds);
}
@ -563,7 +551,7 @@ impl Nodes {
self[self[from].inputs[0]].inputs[index - 1]
}
fn idom(&self, target: Nid, scheds: Option<&[Nid]>) -> Nid {
pub fn idom(&self, target: Nid, scheds: Option<&[Nid]>) -> Nid {
match self[target].kind {
Kind::Start => unreachable!(),
Kind::End => unreachable!(),
@ -839,20 +827,22 @@ impl Nodes {
Value::new(self.new_node(ty, kind, inps, tys)).ty(ty)
}
fn is_locked(&self, target: Nid) -> bool {
// TODO: make this internal to son and force backends to track locks thelself
pub fn is_locked(&self, target: Nid) -> bool {
self[target].lock_rc.get() != 0
}
fn is_unlocked(&self, target: Nid) -> bool {
pub fn is_unlocked(&self, target: Nid) -> bool {
self[target].lock_rc.get() == 0
}
fn lock(&self, target: Nid) {
pub fn lock(&self, target: Nid) {
self[target].lock_rc.set(self[target].lock_rc.get() + 1);
}
#[track_caller]
fn unlock(&self, target: Nid) {
pub fn unlock(&self, target: Nid) {
self[target].lock_rc.set(self[target].lock_rc.get() - 1);
}
@ -1650,7 +1640,7 @@ impl Nodes {
}
}
fn is_const(&self, id: Nid) -> bool {
pub fn is_const(&self, id: Nid) -> bool {
matches!(self[id].kind, Kind::CInt { .. })
}
@ -2007,7 +1997,7 @@ impl Nodes {
}
}
fn dominates(&self, dominator: Nid, mut dominated: Nid, scheds: Option<&[Nid]>) -> bool {
pub fn dominates(&self, dominator: Nid, mut dominated: Nid, scheds: Option<&[Nid]>) -> bool {
loop {
if dominator == dominated {
break true;
@ -2023,7 +2013,7 @@ impl Nodes {
}
}
fn is_data_dep(&self, val: Nid, user: Nid) -> bool {
pub fn is_data_dep(&self, val: Nid, user: Nid) -> bool {
match self[user].kind {
Kind::Return { .. } => self[user].inputs[1] == val,
_ if self.is_cfg(user) && !matches!(self[user].kind, Kind::Call { .. } | Kind::If) => {
@ -2050,7 +2040,7 @@ impl Nodes {
}
}
fn this_or_delegates<'a>(&'a self, source: Nid, target: &'a Nid) -> (Nid, &'a [Nid]) {
pub fn this_or_delegates<'a>(&'a self, source: Nid, target: &'a Nid) -> (Nid, &'a [Nid]) {
if self.is_unlocked(*target) {
(source, core::slice::from_ref(target))
} else {
@ -2058,7 +2048,7 @@ impl Nodes {
}
}
fn is_hard_zero(&self, nid: Nid) -> bool {
pub fn is_hard_zero(&self, nid: Nid) -> bool {
self[nid].kind == Kind::CInt { value: 0 }
&& self[nid].outputs.iter().all(|&n| self[n].kind != Kind::Phi)
}
@ -2178,7 +2168,7 @@ impl Kind {
matches!(self, Self::Arg | Self::Mem | Self::Loops | Self::Entry)
}
fn is_cfg(&self) -> bool {
pub fn is_cfg(&self) -> bool {
matches!(
self,
Self::Start
@ -2199,7 +2189,7 @@ impl Kind {
matches!(self, Self::Return { .. } | Self::If | Self::End | Self::Die)
}
fn starts_basic_block(&self) -> bool {
pub fn starts_basic_block(&self) -> bool {
matches!(self, Self::Region | Self::Loop | Self::Start | Kind::Then | Kind::Else)
}
@ -2224,13 +2214,14 @@ impl fmt::Display for Kind {
#[derive(Debug, Default, Clone)]
pub struct Node {
kind: Kind,
inputs: Vc,
outputs: Vc,
peep_triggers: Vc,
clobbers: BitSet,
ty: ty::Id,
pos: Pos,
pub kind: Kind,
pub inputs: Vc,
pub outputs: Vc,
pub peep_triggers: Vc,
pub clobbers: BitSet,
pub ty: ty::Id,
pub pos: Pos,
depth: Cell<IDomDepth>,
lock_rc: Cell<LockRc>,
loop_depth: Cell<LoopDepth>,
@ -2260,11 +2251,11 @@ impl Node {
matches!(self.kind, Kind::Stre | Kind::Load | Kind::Stck)
}
fn is_data_phi(&self) -> bool {
pub fn is_data_phi(&self) -> bool {
self.kind == Kind::Phi && self.ty != ty::Id::VOID
}
fn has_no_value(&self) -> bool {
pub fn has_no_value(&self) -> bool {
(self.kind.is_cfg() && (!self.kind.is_call() || self.ty == ty::Id::VOID))
|| matches!(self.kind, Kind::Stre)
}
@ -2712,7 +2703,7 @@ impl<'a> Codegen<'a> {
pub fn push_embeds(&mut self, embeds: Vec<Vec<u8>>) {
for data in embeds {
let g = Global {
let g = GlobalData {
ty: self.tys.make_array(ty::Id::U8, data.len() as _),
data,
..Default::default()
@ -2740,13 +2731,13 @@ impl<'a> Codegen<'a> {
return 1;
}
let fuc = self.tys.ins.funcs.push(Func {
let fuc = self.tys.ins.funcs.push(FuncData {
file,
sig: Some(Sig { args: Tuple::empty(), ret }),
..Default::default()
});
self.ct_backend.emit_ct_body(fuc, &mut self.ci.nodes, self.tys, self.files);
self.ct_backend.emit_ct_body(fuc, &self.ci.nodes, self.tys, self.files);
// TODO: return them back
@ -2948,7 +2939,7 @@ impl<'a> Codegen<'a> {
let literal = &literal[1..literal.len() - 1];
let report = |bytes: &core::str::Bytes, message: &str| {
self.error(pos + (literal.len() - bytes.len()) as u32 - 1, message)
self.error(pos + (literal.len() - bytes.len()) as u32 - 1, message);
};
let mut data = Vec::<u8>::with_capacity(literal.len());
@ -2960,8 +2951,11 @@ impl<'a> Codegen<'a> {
occupied_entry.get_key_value().0.value.0
}
(hash_map::RawEntryMut::Vacant(vacant_entry), hash) => {
let global =
self.tys.ins.globals.push(Global { data, ty, ..Default::default() });
let global = self.tys.ins.globals.push(GlobalData {
data,
ty,
..Default::default()
});
vacant_entry
.insert(crate::ctx_map::Key { value: StringRef(global), hash }, ())
.0
@ -4217,20 +4211,13 @@ impl<'a> Codegen<'a> {
let tty = vtarget.ty;
match self.tys.base_of(tty).unwrap_or(tty).expand() {
ty::Kind::Module(m) => {
match self.find_type(pos, self.ci.file, m, self.ci.parent, Err(name)).expand() {
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
v => Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, v.compress())),
}
}
ty::Kind::Module(m) => self.find_type_as_value(pos, m, m, Err(name), ctx),
ty::Kind::Enum(e) => {
let intrnd = self.tys.names.project(name);
self.gen_enum_variant(pos, e, intrnd)
}
ty::Kind::Struct(s) => {
let Struct { ast, file, .. } = self.tys.ins.structs[s];
let TypeBase { ast, file, .. } = *self.tys.ins.structs[s];
if let Some((offset, ty)) = OffsetIter::offset_of(self.tys, s, name) {
Some(Value::ptr(self.offset(vtarget.id, offset)).ty(ty))
} else if let Expr::Struct { fields: [.., CommentOr::Or(Err(_))], .. } =
@ -4258,51 +4245,39 @@ impl<'a> Codegen<'a> {
Value::NEVER
}
}
ty::Kind::TYPE => match ty::Id::from(match self.ci.nodes[vtarget.id].kind {
Kind::CInt { value } => value as u64,
_ => unreachable!(),
})
.expand()
{
ty::Kind::TYPE => match self.ci.nodes.as_ty(vtarget.id).expand() {
ty::Kind::Struct(s) => {
let Struct { file, .. } = self.tys.ins.structs[s];
match self.find_type(pos, self.ci.file, file, s.into(), Err(name)).expand() {
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
v => Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, v.compress())),
let TypeBase { file, .. } = *self.tys.ins.structs[s];
self.find_type_as_value(pos, file, s, Err(name), ctx)
}
ty::Kind::Module(m) => self.find_type_as_value(pos, m, m, Err(name), ctx),
ty::Kind::Enum(e) => {
let intrnd = self.tys.names.project(name);
if let Some(index) =
self.tys.enum_fields(e).iter().position(|f| Some(f.name) == intrnd)
{
Some(self.ci.nodes.new_const_lit(e.into(), index as i64))
} else {
let TypeBase { file, .. } = *self.tys.ins.enums[e];
self.find_type_as_value(pos, file, e, Err(name), ctx)
}
}
ty::Kind::Module(m) => {
match self.find_type(pos, self.ci.file, m, m.into(), Err(name)).expand() {
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
v => Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, v.compress())),
}
}
ty => {
self.error(
pos,
fa!(
"accesing scope on '{}' is not supported yet",
self.ty_display(ty.compress())
),
);
Value::NEVER
}
},
_ => {
self.error(
ty => self.error(
pos,
fa!(
"the '{}' is not a struct, or pointer to one, or enum, \
fo field access does not make sense",
self.ty_display(tty)
"accesing scope on '{}' is not supported yet",
self.ty_display(ty.compress())
),
);
Value::NEVER
}
),
},
_ => self.error(
pos,
fa!(
"the '{}' is not a struct, or pointer to one, or enum, \
fo field access does not make sense",
self.ty_display(tty)
),
),
}
.map(Ok)
}
@ -4385,13 +4360,7 @@ impl<'a> Codegen<'a> {
match self.gen_field(Ctx::default(), target, pos, name)? {
Ok(mut fexpr) => {
self.assert_ty(func.pos(), &mut fexpr, ty::Id::TYPE, "function");
(
ty::Id::from(match self.ci.nodes[fexpr.id].kind {
Kind::CInt { value } => value as u64,
_ => unreachable!(),
}),
None,
)
(self.ci.nodes.as_ty(fexpr.id), None)
}
Err((ty, val)) => (ty, Some(val)),
}
@ -4410,7 +4379,7 @@ impl<'a> Codegen<'a> {
inline |= sig.ret == ty::Id::TYPE;
let Func { expr, file, is_inline, parent, .. } = self.tys.ins.funcs[fu];
let FuncData { expr, file, is_inline, parent, .. } = self.tys.ins.funcs[fu];
let ast = &self.files[file.index()];
let &Expr::Closure { args: cargs, body, .. } = expr.get(ast) else { unreachable!() };
@ -4700,7 +4669,9 @@ impl<'a> Codegen<'a> {
);
}
}
_ => self.error(pos, fa!("'{0} {op} {0}' is not supported", self.ty_display(ty))),
_ => {
_ = self.error(pos, fa!("'{0} {op} {0}' is not supported", self.ty_display(ty)))
}
}
}
@ -4769,7 +4740,7 @@ impl<'a> Codegen<'a> {
}
fn compute_signature(&mut self, func: &mut ty::Func, pos: Pos, args: &[Expr]) -> Option<Sig> {
let Func { file, expr, sig, parent, .. } = self.tys.ins.funcs[*func];
let FuncData { file, expr, sig, parent, .. } = self.tys.ins.funcs[*func];
let fast = &self.files[file.index()];
let &Expr::Closure { args: cargs, ret, .. } = expr.get(fast) else {
unreachable!();
@ -4825,11 +4796,11 @@ impl<'a> Codegen<'a> {
self.ci.scope.vars.drain(base..).for_each(|v| v.remove(&mut self.ci.nodes));
let sym = SymKey::FuncInst(*func, args);
let ct = |ins: &mut crate::TypeIns| {
let ct = |ins: &mut TypeIns| {
let fuc = ins.funcs[*func];
debug_assert!(fuc.comp_state.iter().all(|&s| s == CompState::default()));
ins.funcs
.push(Func { base: Some(*func), sig: Some(Sig { args, ret }), ..fuc })
.push(FuncData { base: Some(*func), sig: Some(Sig { args, ret }), ..fuc })
.into()
};
let ty::Kind::Func(f) =
@ -5078,7 +5049,7 @@ impl<'a> Codegen<'a> {
if self.finalize(prev_err_len) {
let backend = if !cct { &mut *self.backend } else { &mut *self.ct_backend };
backend.emit_body(id, &mut self.ci.nodes, self.tys, self.files);
backend.emit_body(id, &self.ci.nodes, self.tys, self.files);
}
self.ci.pos.pop();
@ -5396,9 +5367,10 @@ impl<'a> Codegen<'a> {
}
#[track_caller]
fn error(&self, pos: Pos, msg: impl core::fmt::Display) {
fn error(&self, pos: Pos, msg: impl core::fmt::Display) -> Option<Value> {
let mut buf = self.errors.borrow_mut();
write!(buf, "{}", self.file().report(pos, msg)).unwrap();
Value::NEVER
}
#[track_caller]
@ -5419,10 +5391,7 @@ impl<'a> Codegen<'a> {
.vars
.iter()
.filter(|v| v.ty == ty::Id::TYPE)
.map(|v| match self.ci.nodes[v.value.get()].kind {
Kind::CInt { value } => (value, v.id),
_ => unreachable!(),
})
.map(|v| (self.ci.nodes.as_ty(v.value()), v.id))
.collect::<Vec<_>>();
self.pool.push_ci(file, self.ci.parent, Some(ret), self.tys.tasks.len(), &mut self.ci);
self.ci.scope.vars = scope
@ -5475,7 +5444,7 @@ impl<'a> Codegen<'a> {
fn eval_global(&mut self, file: Module, name: Ident, expr: &Expr) -> ty::Id {
self.ct.activate();
let gid = self.tys.ins.globals.push(Global { file, name, ..Default::default() });
let gid = self.tys.ins.globals.push(GlobalData { file, name, ..Default::default() });
self.pool.push_ci(file, self.ci.parent, None, self.tys.tasks.len(), &mut self.ci);
let prev_err_len = self.errors.borrow().len();
@ -5509,12 +5478,12 @@ impl<'a> Codegen<'a> {
}
fn find_local_ty(&mut self, ident: Ident) -> Option<ty::Id> {
self.ci.scope.vars.iter().rfind(|v| (v.id == ident && v.ty == ty::Id::TYPE)).map(|v| {
match self.ci.nodes[v.value.get()].kind {
Kind::CInt { value } => ty::Id::from(value as u64),
k => unreachable!("{k:?}"),
}
})
self.ci
.scope
.vars
.iter()
.rfind(|v| (v.id == ident && v.ty == ty::Id::TYPE))
.map(|v| self.ci.nodes.as_ty(v.value()))
}
fn find_type_in_file(&mut self, pos: Pos, file: Module, id: Result<Ident, &str>) -> ty::Id {
@ -5525,6 +5494,22 @@ impl<'a> Codegen<'a> {
self.find_type(pos, self.ci.file, self.ci.file, self.ci.parent, id)
}
fn find_type_as_value(
&mut self,
pos: Pos,
file: Module,
parent: impl Into<ty::Id>,
id: Result<Ident, &str>,
ctx: Ctx,
) -> Option<Value> {
match self.find_type(pos, self.ci.file, file, parent.into(), id).expand() {
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
v => Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, v.compress())),
}
}
fn find_type(
&mut self,
pos: Pos,
@ -5548,7 +5533,7 @@ impl<'a> Codegen<'a> {
let mut piter = parent;
let Some((expr @ Expr::BinOp { left, right, .. }, name)) = (loop {
if let Some(f) =
parser::find_decl(self.tys.scope_of(piter, f).unwrap_or(f.exprs()), &f.file, id)
parser::find_decl(self.tys.scope_of(piter, f).expect("TODO"), &f.file, id)
{
break Some(f);
}
@ -5556,7 +5541,6 @@ impl<'a> Codegen<'a> {
if let Some((captures, capture_tuple)) = self.tys.captures_of(piter, f)
&& let Some(idx) = captures.iter().position(|&cid| Ok(cid) == id)
{
debug_assert_eq!(captures.len(), capture_tuple.len());
return self.tys.ins.args[capture_tuple.range().start + idx];
}
@ -5605,7 +5589,7 @@ impl<'a> Codegen<'a> {
self.tys
.ins
.consts
.push(Const { ast: ExprRef::new(expr), name, file, parent })
.push(ConstData { ast: ExprRef::new(expr), name, file, parent })
.into()
} else {
self.parse_ty(
@ -5687,78 +5671,37 @@ impl<'a> Codegen<'a> {
.map_or(ArrayLen::MAX, |expr| self.eval_const(sc.file, expr, ty::Id::U32) as _);
self.tys.make_array(ty, len)
}
Expr::Struct { pos, fields, packed, captured, .. } => {
let captures_start = self.tys.tmp.args.len();
for &cp in captured {
let ty = self.find_local_ty(cp).expect("TODO");
self.tys.tmp.args.push(ty);
}
let captured = self.tys.pack_args(captures_start).expect("TODO");
let sym = SymKey::Struct(sc.file, pos, captured);
if let Some(&ty) = self.tys.syms.get(sym, &self.tys.ins) {
return ty;
}
let prev_tmp = self.tys.tmp.struct_fields.len();
for field in fields.iter().filter_map(CommentOr::or).filter_map(Result::ok) {
let ty = self.parse_ty(sc.anon(), &field.ty);
let field = StructField { name: self.tys.names.intern(field.name), ty };
self.tys.tmp.struct_fields.push(field);
}
let ty = self
.tys
.ins
.structs
.push(Struct {
file: sc.file,
pos,
captured,
name: sc.name.unwrap_or_default(),
field_start: self.tys.ins.struct_fields.len() as _,
Expr::Struct { pos, fields, packed, captured, .. } => self.parse_base_ty(
pos,
expr,
captured,
fields,
sc,
|s| [&mut s.ins.struct_fields, &mut s.tmp.struct_fields],
|s, field| {
let ty = s.parse_ty(sc.anon(), &field.ty);
StructField { name: s.tys.names.intern(field.name), ty }
},
|s, base| {
s.ins.structs.push(StructData {
base,
explicit_alignment: packed.then_some(1),
ast: ExprRef::new(expr),
..Default::default()
})
.into();
self.tys.ins.struct_fields.extend(self.tys.tmp.struct_fields.drain(prev_tmp..));
self.tys.syms.insert(sym, ty, &self.tys.ins);
ty
}
Expr::Enum { pos, variants, .. } => {
let sym = SymKey::Enum(sc.file, pos);
if let Some(&ty) = self.tys.syms.get(sym, &self.tys.ins) {
return ty;
}
let prev_tmp = self.tys.tmp.enum_fields.len();
for field in variants.iter().filter_map(CommentOr::or) {
let field = EnumField { name: self.tys.names.intern(field.name) };
self.tys.tmp.enum_fields.push(field);
}
let ty = self
.tys
.ins
.enums
.push(Enum {
file: sc.file,
pos,
name: sc.name.unwrap_or_default(),
field_start: self.tys.ins.enum_fields.len() as _,
})
.into();
self.tys.ins.enum_fields.extend(self.tys.tmp.enum_fields.drain(prev_tmp..));
self.tys.syms.insert(sym, ty, &self.tys.ins);
ty
}
},
),
Expr::Enum { pos, variants, captured, .. } => self.parse_base_ty(
pos,
expr,
captured,
variants,
sc,
|s| [&mut s.ins.enum_fields, &mut s.tmp.enum_fields],
|s, field| EnumField { name: s.tys.names.intern(field.name) },
|s, base| s.ins.enums.push(EnumData { base }),
),
Expr::Closure { pos, args, ret, .. } if let Some(name) = sc.name => {
let func = Func {
let func = FuncData {
file: sc.file,
parent: sc.parent,
name,
@ -5801,6 +5744,56 @@ impl<'a> Codegen<'a> {
}
}
}
#[expect(clippy::too_many_arguments)]
fn parse_base_ty<A: Copy, F, T: Into<ty::Id>>(
&mut self,
pos: Pos,
expr: &Expr,
captured: &[Ident],
fields: FieldList<A>,
sc: TyScope,
get_fields: impl Fn(&mut Types) -> [&mut Vec<F>; 2],
check_field: impl Fn(&mut Self, A) -> F,
check: impl Fn(&mut Types, TypeBase) -> T,
) -> ty::Id {
let captures_start = self.tys.tmp.args.len();
for &cp in captured {
let ty = self.find_local_ty(cp).expect("TODO");
self.tys.tmp.args.push(ty);
}
let captured = self.tys.pack_args(captures_start).expect("TODO");
let sym = SymKey::Type(sc.file, pos, captured);
if let Some(&ty) = self.tys.syms.get(sym, &self.tys.ins) {
return ty;
}
let prev_tmp = get_fields(self.tys)[1].len();
for field in fields.iter().filter_map(CommentOr::or).filter_map(Result::ok) {
let field = check_field(self, field);
get_fields(self.tys)[1].push(field);
}
let base = TypeBase {
file: sc.file,
parent: sc.parent,
pos,
captured,
name: sc.name.unwrap_or_default(),
field_start: self.tys.ins.struct_fields.len() as _,
ast: ExprRef::new(expr),
};
let [ins, tmp] = get_fields(self.tys);
ins.extend(tmp.drain(prev_tmp..));
let ty = check(self.tys, base).into();
self.tys.syms.insert(sym, ty, &self.tys.ins);
ty
}
}
#[derive(Clone, Copy)]
@ -5820,8 +5813,11 @@ impl TyScope {
#[cfg(test)]
mod tests {
use {
super::{hbvm::HbvmBackend, CodegenCtx},
crate::ty,
crate::{
backend::hbvm::{self, HbvmBackend},
son::CodegenCtx,
ty,
},
alloc::{string::String, vec::Vec},
core::fmt::Write,
};
@ -5856,7 +5852,7 @@ mod tests {
} else {
log::info!("================ running {ident} ==============");
log::trace!("{output}");
super::hbvm::test_run_vm(&out, output);
hbvm::test_run_vm(&out, output);
}
}

1077
lang/src/ty.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -14,9 +14,9 @@ main:
CP r35, r0
LI64 r36, 30d
LI64 r37, 100d
CP r34, r35
CP r32, r35
CP r33, r35
CP r34, r35
5: JLTU r34, r36, :0
ADDI64 r32, r32, 1d
CP r2, r35