adding tuples

Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
Jakub Doka 2024-12-19 23:08:36 +01:00
parent 969ea57e3f
commit 6e8eb059f6
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
8 changed files with 268 additions and 74 deletions

View file

@ -254,7 +254,7 @@ pub fn disasm<'a>(
|| global_offset > off + len || global_offset > off + len
|| prev || prev
.get(global_offset as usize) .get(global_offset as usize)
.map_or(true, |&b| instr_from_byte(b).is_err()); .is_none_or(|&b| instr_from_byte(b).is_err());
has_oob |= local_has_oob; has_oob |= local_has_oob;
let label = labels.get(&global_offset).unwrap(); let label = labels.get(&global_offset).unwrap();
if local_has_oob { if local_has_oob {

View file

@ -214,6 +214,14 @@ odher_pass := fn(t: Ty2): Ty2 {
} }
``` ```
#### tuples
```hb
main := fn(): uint {
tupl := .(1, 1)
return tupl[0] - tupl[1]
}
```
#### struct_scopes #### struct_scopes
```hb ```hb
$zr := 0 $zr := 0
@ -475,7 +483,7 @@ arbitrary text
- `@embed(<string>)`: include relative file as an array of bytes - `@embed(<string>)`: include relative file as an array of bytes
- `@inline(<func>, ...<args>)`: equivalent to `<func>(...<args>)` but function is guaranteed to inline, compiler will otherwise never inline - `@inline(<func>, ...<args>)`: equivalent to `<func>(...<args>)` but function is guaranteed to inline, compiler will otherwise never inline
- `@len(<ty>)`: reports a length of the type of indexing purposes or length ot a string constant - `@len(<ty>)`: reports a length of the type of indexing purposes or length ot a string constant
- `@kindof(<ty>)`: gives an u8 integer describing the kind of type as an index to array `[Builtin, Struct, Enum, Union, Ptr, Slice, Opt, Func, Template, Global, Const, Module]` - `@kindof(<ty>)`: gives an u8 integer describing the kind of type as an index to array `[Builtin, Struct, Tuple, Enum, Union, Ptr, Slice, Opt, Func, Template, Global, Const, Module]`
- `@Any()`: generic parameter based on inference, TBD: this will ake arguments in the future that restrict what is accepted - `@Any()`: generic parameter based on inference, TBD: this will ake arguments in the future that restrict what is accepted
- `@error(...<expr>)`: emit compiler error, if reachable, and use arguments to construct a message, can display strings and types - `@error(...<expr>)`: emit compiler error, if reachable, and use arguments to construct a message, can display strings and types
- `@ChildOf(<ty>)`: returns the child type of the `<ty>`, works for pointers and optionals (`@ChildOf(?u8) == u8`) - `@ChildOf(<ty>)`: returns the child type of the `<ty>`, works for pointers and optionals (`@ChildOf(?u8) == u8`)

View file

@ -8,7 +8,7 @@ use {
utils::{EntSlice, EntVec}, utils::{EntSlice, EntVec},
}, },
alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec}, alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec},
core::{assert_matches::debug_assert_matches, mem, ops::Range, usize}, core::{assert_matches::debug_assert_matches, mem, ops::Range},
hbbytecode::{self as instrs, *}, hbbytecode::{self as instrs, *},
reg::Reg, reg::Reg,
}; };
@ -314,7 +314,7 @@ impl Backend for HbvmBackend {
&& self && self
.jump_relocs .jump_relocs
.last() .last()
.map_or(true, |&(r, _)| self.offsets[r as usize] as usize != self.code.len()) .is_none_or(|&(r, _)| self.offsets[r as usize] as usize != self.code.len())
{ {
self.code.truncate(self.code.len() - 5); self.code.truncate(self.code.len() - 5);
self.ret_relocs.pop(); self.ret_relocs.pop();
@ -653,6 +653,7 @@ enum PLoc {
WideReg(Reg, u16), WideReg(Reg, u16),
Ref(Reg, u32), Ref(Reg, u32),
} }
impl PLoc { impl PLoc {
fn reg(self) -> u8 { fn reg(self) -> u8 {
match self { match self {

View file

@ -804,6 +804,7 @@ impl Nodes {
} }
} }
#[expect(unused)]
fn init_loc_of(&self, def: Nid, types: &Types) -> Reg { fn init_loc_of(&self, def: Nid, types: &Types) -> Reg {
if self[def].kind == Kind::Arg { if self[def].kind == Kind::Arg {
let mut parama = ParamAlloc(0..11); let mut parama = ParamAlloc(0..11);
@ -821,8 +822,9 @@ impl Nodes {
255 255
} }
#[expect(unused)]
fn use_reg_of(&self, def: Nid, usage: Nid) -> Reg { fn use_reg_of(&self, def: Nid, usage: Nid) -> Reg {
if matches!(self[usage].kind, Kind::Return { .. }) {} //if matches!(self[usage].kind, Kind::Return { .. }) {}
255 255
} }
@ -852,7 +854,7 @@ impl<'a> Regalloc<'a> {
Self { nodes: ctx, tys, res }.run_low(special_count); Self { nodes: ctx, tys, res }.run_low(special_count);
} }
fn run_low(&mut self, special_count: usize) { fn run_low(&mut self, #[expect(unused)] special_count: usize) {
self.res.general_bundles.clear(); self.res.general_bundles.clear();
self.res.node_to_reg.clear(); self.res.node_to_reg.clear();
#[cfg(debug_assertions)] #[cfg(debug_assertions)]

View file

@ -2089,7 +2089,7 @@ pub enum Kind {
Call { Call {
unreachable: bool, unreachable: bool,
func: ty::Func, func: ty::Func,
args: ty::Tuple, args: ty::List,
}, },
// [ctrl] // [ctrl]
Die, Die,

View file

@ -15,9 +15,9 @@ use {
}, },
ty::{ ty::{
self, Arg, ArrayLen, CompState, ConstData, EnumData, EnumField, FTask, FuncData, self, Arg, ArrayLen, CompState, ConstData, EnumData, EnumField, FTask, FuncData,
GlobalData, Loc, Module, Offset, OffsetIter, OptLayout, Sig, StringRef, StructData, GlobalData, List, Loc, Module, Offset, OffsetIter, OptLayout, Sig, StringRef,
StructField, SymKey, TemplateData, Tuple, TypeBase, TypeIns, Types, UnionData, StructData, StructField, SymKey, TemplateData, TupleData, TypeBase, TypeIns, Types,
UnionField, UnionData, UnionField,
}, },
utils::{BitSet, EntSlice, Vc}, utils::{BitSet, EntSlice, Vc},
Ident, Ident,
@ -657,7 +657,7 @@ impl<'a> Codegen<'a> {
let fuc = self.tys.ins.funcs.push(FuncData { let fuc = self.tys.ins.funcs.push(FuncData {
file, file,
sig: Sig { args: Tuple::empty(), ret }, sig: Sig { args: List::empty(), ret },
..Default::default() ..Default::default()
}); });
@ -1274,10 +1274,37 @@ impl<'a> Codegen<'a> {
Some(Value::ptr(self.offset(bs.id, offset)).ty(f)) Some(Value::ptr(self.offset(bs.id, offset)).ty(f))
} }
ty::Kind::Tuple(t) => {
let Kind::CInt { value: idx } = self.ci.nodes[idx.id].kind else {
return self.error(
index.pos(),
"field index needs to be known at compile time",
);
};
let Some((f, offset)) = OffsetIter::new(t, self.tys)
.into_iter(self.tys)
.nth(idx as _)
.map(|(&f, off)| (f, off))
else {
return self.error(
index.pos(),
fa!(
"struct '{}' has only `{}' fields, \
but index was '{}'",
self.ty_display(bs.ty),
self.tys.tuple_fields(t).len(),
idx
),
);
};
Some(Value::ptr(self.offset(bs.id, offset)).ty(f))
}
_ => self.error( _ => self.error(
base.pos(), base.pos(),
fa!( fa!(
"cant index into '{}' which is not array nor slice", "cant index into '{}' which is not array nor slice or tuple or struct",
self.ty_display(bs.ty) self.ty_display(bs.ty)
), ),
), ),
@ -1581,14 +1608,51 @@ impl<'a> Codegen<'a> {
self.gen_call(func, args, true) self.gen_call(func, args, true)
} }
Expr::Tupl { pos, ty, fields, .. } => { Expr::Tupl { pos, ty, fields, .. } => {
ctx.ty = ty let ty = ty
.map(|ty| self.ty(ty)) .map(|ty| self.ty(ty))
.or(ctx.ty.map(|ty| self.tys.inner_of(ty).unwrap_or(ty))); .or(ctx.ty.map(|ty| self.tys.inner_of(ty).unwrap_or(ty)))
inference!(sty, ctx, self, pos, "struct or slice", "<struct_ty>.(...)"); .map(ty::Id::expand);
match sty.expand() { match ty {
ty::Kind::Struct(s) => { None => {
let mem = self.new_stack(pos, sty); let arg_base = self.tys.tmp.args.len();
let mut values = Vec::with_capacity(fields.len());
for field in fields {
let val = self.expr(field)?;
self.tys.tmp.args.push(val.ty);
self.ci.nodes.lock(val.id);
values.push(val);
}
let Some(fields) = self.tys.pack_args(arg_base) else {
return self.error(pos, "this tuple exceeded the reasonable limit");
};
let key = SymKey::Tuple(fields);
let ty::Kind::Tuple(tupl) = self
.tys
.syms
.get_or_insert(key, &mut self.tys.ins, |ins| {
ins.tuples.push(TupleData { fields, ..Default::default() }).into()
})
.expand()
else {
unreachable!()
};
let mem = self.new_stack(pos, tupl.into());
let mut offs = OffsetIter::new(tupl, self.tys);
for value in values {
let (ty, offset) = offs.next_ty(self.tys).unwrap();
let mem = self.offset(mem, offset);
self.ci.nodes.unlock(value.id);
self.store_mem(mem, ty, value.id);
}
Some(Value::ptr(mem).ty(tupl.into()))
}
Some(ty::Kind::Struct(s)) => {
let mem = self.new_stack(pos, s.into());
let mut offs = OffsetIter::new(s, self.tys); let mut offs = OffsetIter::new(s, self.tys);
for field in fields { for field in fields {
let Some((ty, offset)) = offs.next_ty(self.tys) else { let Some((ty, offset)) = offs.next_ty(self.tys) else {
@ -1617,16 +1681,17 @@ impl<'a> Codegen<'a> {
(append them to the end of the constructor)"), (append them to the end of the constructor)"),
); );
} }
Some(Value::ptr(mem).ty(sty)) Some(Value::ptr(mem).ty(s.into()))
} }
ty::Kind::Slice(s) => { Some(ty::Kind::Slice(s)) => {
let slice = &self.tys.ins.slices[s]; let slice = &self.tys.ins.slices[s];
let len = slice.len().unwrap_or(fields.len()); let len = slice.len().unwrap_or(fields.len());
let elem = slice.elem; let elem = slice.elem;
let elem_size = self.tys.size_of(elem); let elem_size = self.tys.size_of(elem);
let aty = slice let aty = slice.len().map_or_else(
.len() || self.tys.make_array(elem, len as ArrayLen),
.map_or_else(|| self.tys.make_array(elem, len as ArrayLen), |_| sty); |_| s.into(),
);
if len != fields.len() { if len != fields.len() {
return self.error( return self.error(
@ -1651,13 +1716,13 @@ impl<'a> Codegen<'a> {
Some(Value::ptr(mem).ty(aty)) Some(Value::ptr(mem).ty(aty))
} }
_ => self.error( Some(t) => self.error(
pos, pos,
fa!( fa!(
"the {}type of the constructor is `{}`, \ "the {}type of the constructor is `{}`, \
but thats not a struct nor slice or array", but thats not a struct nor slice or array",
if ty.is_some() { "" } else { "inferred " }, if ty.is_some() { "" } else { "inferred " },
self.ty_display(sty), self.ty_display(t.compress()),
), ),
), ),
} }
@ -2393,6 +2458,7 @@ impl<'a> Codegen<'a> {
| ty::Kind::Func(_) | ty::Kind::Func(_)
| ty::Kind::Template(_) | ty::Kind::Template(_)
| ty::Kind::Global(_) | ty::Kind::Global(_)
| ty::Kind::Tuple(_)
| ty::Kind::Const(_)) => self.error( | ty::Kind::Const(_)) => self.error(
pos, pos,
fa!( fa!(
@ -4146,6 +4212,7 @@ mod tests {
loops; loops;
pointers; pointers;
structs; structs;
tuples;
struct_scopes; struct_scopes;
enums; enums;
unions; unions;

View file

@ -38,9 +38,9 @@ pub type Offset = u32;
pub type Size = u32; pub type Size = u32;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, PartialOrd, Ord)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, PartialOrd, Ord)]
pub struct Tuple(pub u32); pub struct List(pub u32);
impl Tuple { impl List {
const LEN_BITS: u32 = 5; const LEN_BITS: u32 = 5;
const LEN_MASK: usize = Self::MAX_LEN - 1; const LEN_MASK: usize = Self::MAX_LEN - 1;
const MAX_LEN: usize = 1 << Self::LEN_BITS; const MAX_LEN: usize = 1 << Self::LEN_BITS;
@ -104,6 +104,12 @@ impl ArgIter {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Id(NonZeroU32); pub struct Id(NonZeroU32);
impl AsRef<Id> for Id {
fn as_ref(&self) -> &Id {
self
}
}
impl From<Id> for i64 { impl From<Id> for i64 {
fn from(value: Id) -> Self { fn from(value: Id) -> Self {
value.0.get() as _ value.0.get() as _
@ -150,6 +156,7 @@ impl crate::ctx_map::CtxEntry for Id {
SymKey::Decl(gb.file.into(), gb.name) SymKey::Decl(gb.file.into(), gb.name)
} }
Kind::Slice(s) => SymKey::Array(&ctx.slices[s]), Kind::Slice(s) => SymKey::Array(&ctx.slices[s]),
Kind::Tuple(t) => SymKey::Tuple(ctx.tuples[t].fields),
Kind::Module(_) | Kind::Builtin(_) => { Kind::Module(_) | Kind::Builtin(_) => {
SymKey::Decl(Module::default().into(), Ident::INVALID) SymKey::Decl(Module::default().into(), Ident::INVALID)
} }
@ -266,22 +273,19 @@ impl Id {
} }
pub(crate) fn loc(&self, tys: &Types) -> Loc { pub(crate) fn loc(&self, tys: &Types) -> Loc {
use Kind as K;
match self.expand() { match self.expand() {
Kind::Opt(o) K::Opt(o)
if let ty = tys.ins.opts[o].base if let ty = tys.ins.opts[o].base
&& ty.loc(tys) == Loc::Reg && ty.loc(tys) == Loc::Reg
&& (ty.is_pointer() || tys.size_of(ty) < 8) => && (ty.is_pointer() || tys.size_of(ty) < 8) =>
{ {
Loc::Reg Loc::Reg
} }
Kind::Ptr(_) | Kind::Enum(_) | Kind::Builtin(_) => Loc::Reg, K::Ptr(_) | K::Enum(_) | K::Builtin(_) => Loc::Reg,
Kind::Struct(_) | Kind::Union(_) if tys.size_of(*self) == 0 => Loc::Reg, K::Struct(_) | K::Tuple(_) | K::Union(_) if tys.size_of(*self) == 0 => Loc::Reg,
Kind::Struct(_) | Kind::Union(_) | Kind::Slice(_) | Kind::Opt(_) => Loc::Stack, K::Struct(_) | K::Tuple(_) | K::Union(_) | K::Slice(_) | K::Opt(_) => Loc::Stack,
c @ (Kind::Func(_) c @ (K::Func(_) | K::Global(_) | K::Module(_) | K::Const(_) | K::Template(_)) => {
| Kind::Global(_)
| Kind::Module(_)
| Kind::Const(_)
| Kind::Template(_)) => {
unreachable!("{c:?}") unreachable!("{c:?}")
} }
} }
@ -450,6 +454,7 @@ type_kind! {
pub enum Kind { pub enum Kind {
Builtin, Builtin,
Struct, Struct,
Tuple,
Enum, Enum,
Union, Union,
Ptr, Ptr,
@ -514,25 +519,25 @@ impl<'a> Display<'a> {
impl core::fmt::Display for Display<'_> { impl core::fmt::Display for Display<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use Kind as TK; use Kind as K;
match TK::from_ty(self.ty) { match K::from_ty(self.ty) {
TK::Module(idx) => { K::Module(idx) => {
f.write_str("@use(\"")?; f.write_str("@use(\"")?;
self.files[idx].path.fmt(f)?; self.files[idx].path.fmt(f)?;
f.write_str(")[")?; f.write_str(")[")?;
idx.fmt(f)?; idx.fmt(f)?;
f.write_str("]") f.write_str("]")
} }
TK::Builtin(ty) => f.write_str(to_str(ty)), K::Builtin(ty) => f.write_str(to_str(ty)),
TK::Opt(ty) => { K::Opt(ty) => {
f.write_str("?")?; f.write_str("?")?;
self.rety(self.tys.ins.opts[ty].base).fmt(f) self.rety(self.tys.ins.opts[ty].base).fmt(f)
} }
TK::Ptr(ty) => { K::Ptr(ty) => {
f.write_str("^")?; f.write_str("^")?;
self.rety(self.tys.ins.ptrs[ty].base).fmt(f) self.rety(self.tys.ins.ptrs[ty].base).fmt(f)
} }
TK::Struct(idx) => { K::Struct(idx) => {
let record = &self.tys.ins.structs[idx]; let record = &self.tys.ins.structs[idx];
if record.name.is_null() { if record.name.is_null() {
f.write_str("[")?; f.write_str("[")?;
@ -554,7 +559,19 @@ impl core::fmt::Display for Display<'_> {
f.write_str(file.ident_str(record.name)) f.write_str(file.ident_str(record.name))
} }
} }
TK::Union(idx) => { K::Tuple(idx) => {
f.write_str(".(")?;
for (i, &ty) in
self.tys.ins.args[self.tys.ins.tuples[idx].fields.range()].iter().enumerate()
{
if i != 0 {
f.write_str(", ")?;
}
self.rety(ty).fmt(f)?;
}
f.write_str(")")
}
K::Union(idx) => {
let record = &self.tys.ins.unions[idx]; let record = &self.tys.ins.unions[idx];
if record.name.is_null() { if record.name.is_null() {
f.write_str("[")?; f.write_str("[")?;
@ -576,27 +593,27 @@ impl core::fmt::Display for Display<'_> {
f.write_str(file.ident_str(record.name)) f.write_str(file.ident_str(record.name))
} }
} }
TK::Enum(idx) => { K::Enum(idx) => {
let enm = &self.tys.ins.enums[idx]; let enm = &self.tys.ins.enums[idx];
debug_assert!(!enm.name.is_null()); debug_assert!(!enm.name.is_null());
let file = &self.files[enm.file]; let file = &self.files[enm.file];
f.write_str(file.ident_str(enm.name)) f.write_str(file.ident_str(enm.name))
} }
TK::Func(idx) => { K::Func(idx) => {
f.write_str("fn")?; f.write_str("fn")?;
idx.fmt(f) idx.fmt(f)
} }
TK::Template(idx) => { K::Template(idx) => {
f.write_str("fn")?; f.write_str("fn")?;
idx.fmt(f) idx.fmt(f)
} }
TK::Global(idx) => { K::Global(idx) => {
let global = &self.tys.ins.globals[idx]; let global = &self.tys.ins.globals[idx];
let file = &self.files[global.file]; let file = &self.files[global.file];
f.write_str(file.ident_str(global.name))?; f.write_str(file.ident_str(global.name))?;
f.write_str(" (global)") f.write_str(" (global)")
} }
TK::Slice(idx) => { K::Slice(idx) => {
let array = self.tys.ins.slices[idx]; let array = self.tys.ins.slices[idx];
f.write_str("[")?; f.write_str("[")?;
self.rety(array.elem).fmt(f)?; self.rety(array.elem).fmt(f)?;
@ -606,7 +623,7 @@ impl core::fmt::Display for Display<'_> {
} }
f.write_str("]") f.write_str("]")
} }
TK::Const(idx) => { K::Const(idx) => {
let cnst = &self.tys.ins.consts[idx]; let cnst = &self.tys.ins.consts[idx];
let file = &self.files[cnst.file]; let file = &self.files[cnst.file];
f.write_str(file.ident_str(cnst.name))?; f.write_str(file.ident_str(cnst.name))?;
@ -618,9 +635,10 @@ impl core::fmt::Display for Display<'_> {
#[derive(PartialEq, Eq, Hash, Clone, Copy)] #[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub enum SymKey<'a> { pub enum SymKey<'a> {
Tuple(List),
Pointer(&'a PtrData), Pointer(&'a PtrData),
Optional(&'a OptData), Optional(&'a OptData),
Type(Id, Pos, Tuple), Type(Id, Pos, List),
Decl(Id, Ident), Decl(Id, Ident),
Array(&'a ArrayData), Array(&'a ArrayData),
Constant(&'a ConstData), Constant(&'a ConstData),
@ -628,7 +646,7 @@ pub enum SymKey<'a> {
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
pub struct Sig { pub struct Sig {
pub args: Tuple, pub args: List,
pub ret: Id, pub ret: Id,
} }
@ -722,7 +740,7 @@ pub struct TypeBase {
pub pos: Pos, pub pos: Pos,
pub name: Ident, pub name: Ident,
pub field_start: u32, pub field_start: u32,
pub captured: Tuple, pub captured: List,
pub ast: ExprRef, pub ast: ExprRef,
} }
@ -764,6 +782,13 @@ pub struct StructData {
impl_deref!(StructData { base: TypeBase }); impl_deref!(StructData { base: TypeBase });
#[derive(Default)]
pub struct TupleData {
pub fields: List,
pub size: Cell<Size>,
pub align: Cell<u8>,
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)] #[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct OptData { pub struct OptData {
pub base: Id, pub base: Id,
@ -854,6 +879,7 @@ pub struct TypeIns {
pub ptrs: EntVec<Ptr, PtrData>, pub ptrs: EntVec<Ptr, PtrData>,
pub opts: EntVec<Opt, OptData>, pub opts: EntVec<Opt, OptData>,
pub slices: EntVec<Slice, ArrayData>, pub slices: EntVec<Slice, ArrayData>,
pub tuples: EntVec<Tuple, TupleData>,
} }
pub struct FTask { pub struct FTask {
@ -897,6 +923,7 @@ impl Types {
| Kind::Builtin(_) | Kind::Builtin(_)
| Kind::Ptr(_) | Kind::Ptr(_)
| Kind::Slice(_) | Kind::Slice(_)
| Kind::Tuple(_)
| Kind::Opt(_) => utils::is_pascal_case, | Kind::Opt(_) => utils::is_pascal_case,
Kind::Func(f) Kind::Func(f)
if let &Expr::Closure { ret: &Expr::Ident { id, .. }, .. } = if let &Expr::Closure { ret: &Expr::Ident { id, .. }, .. } =
@ -919,19 +946,19 @@ impl Types {
} }
} }
pub fn pack_args(&mut self, arg_base: usize) -> Option<Tuple> { pub fn pack_args(&mut self, arg_base: usize) -> Option<List> {
let base = self.ins.args.len(); let base = self.ins.args.len();
self.ins.args.extend(self.tmp.args.drain(arg_base..)); self.ins.args.extend(self.tmp.args.drain(arg_base..));
let needle = &self.ins.args[base..]; let needle = &self.ins.args[base..];
if needle.is_empty() { if needle.is_empty() {
return Some(Tuple::empty()); return Some(List::empty());
} }
let len = needle.len(); let len = needle.len();
// FIXME: maybe later when this becomes a bottleneck we use more // FIXME: maybe later when this becomes a bottleneck we use more
// efficient search (SIMD?, indexing?) // efficient search (SIMD?, indexing?)
let sp = self.ins.args.windows(needle.len()).position(|val| val == needle).unwrap(); let sp = self.ins.args.windows(needle.len()).position(|val| val == needle).unwrap();
self.ins.args.truncate((sp + needle.len()).max(base)); self.ins.args.truncate((sp + needle.len()).max(base));
Tuple::new(sp, len) List::new(sp, len)
} }
pub fn union_fields(&self, union: Union) -> &[UnionField] { pub fn union_fields(&self, union: Union) -> &[UnionField] {
@ -1014,6 +1041,16 @@ impl Types {
self.ins.structs[stru].size.set(oiter.offset); self.ins.structs[stru].size.set(oiter.offset);
oiter.offset oiter.offset
} }
Kind::Tuple(tuple) => {
if self.ins.tuples[tuple].size.get() != 0 {
return self.ins.tuples[tuple].size.get();
}
let mut oiter = OffsetIter::new(tuple, self);
while oiter.next(self).is_some() {}
self.ins.tuples[tuple].size.set(oiter.offset);
oiter.offset
}
Kind::Union(union) => { Kind::Union(union) => {
if self.ins.unions[union].size.get() != 0 { if self.ins.unions[union].size.get() != 0 {
return self.ins.unions[union].size.get(); return self.ins.unions[union].size.get();
@ -1033,8 +1070,12 @@ impl Types {
self.size_of(base) + self.align_of(base) self.size_of(base) + self.align_of(base)
} }
} }
_ if let Some(size) = ty.simple_size() => size, Kind::Ptr(_) | Kind::Builtin(_) => ty.simple_size().unwrap(),
ty => unimplemented!("size_of: {:?}", ty), Kind::Func(_)
| Kind::Template(_)
| Kind::Global(_)
| Kind::Const(_)
| Kind::Module(_) => unreachable!(),
} }
} }
@ -1066,6 +1107,15 @@ impl Types {
self.ins.structs[stru].align.set(align.try_into().unwrap()); self.ins.structs[stru].align.set(align.try_into().unwrap());
align align
} }
Kind::Tuple(tuple) => {
if self.ins.tuples[tuple].align.get() != 0 {
return self.ins.tuples[tuple].align.get() as _;
}
let align =
self.tuple_fields(tuple).iter().map(|&f| self.align_of(f)).max().unwrap_or(1);
self.ins.tuples[tuple].align.set(align.try_into().unwrap());
align
}
Kind::Slice(arr) => { Kind::Slice(arr) => {
let arr = &self.ins.slices[arr]; let arr = &self.ins.slices[arr];
match arr.len { match arr.len {
@ -1073,7 +1123,14 @@ impl Types {
_ => self.align_of(arr.elem), _ => self.align_of(arr.elem),
} }
} }
_ => self.size_of(ty).max(1), Kind::Opt(opt) => self.align_of(self.ins.opts[opt].base),
Kind::Builtin(_) | Kind::Enum(_) | Kind::Ptr(_) => self.size_of(ty),
Kind::Func(_)
| Kind::Template(_)
| Kind::Global(_)
| Kind::Const(_)
| Kind::Module(_) => unreachable!(),
//_ => self.size_of(ty).max(1),
} }
} }
@ -1161,6 +1218,7 @@ impl Types {
| Kind::Template(_) | Kind::Template(_)
| Kind::Global(_) | Kind::Global(_)
| Kind::Module(_) | Kind::Module(_)
| Kind::Tuple(_)
| Kind::Const(_) => return None, | Kind::Const(_) => return None,
}) })
} }
@ -1186,7 +1244,7 @@ impl Types {
self.type_base_of(ty).map(|b| b.parent) self.type_base_of(ty).map(|b| b.parent)
} }
pub fn captures_of<'a>(&self, ty: Id, file: &'a parser::Ast) -> Option<(&'a [Ident], Tuple)> { pub fn captures_of<'a>(&self, ty: Id, file: &'a parser::Ast) -> Option<(&'a [Ident], List)> {
let base = self.type_base_of(ty)?; let base = self.type_base_of(ty)?;
let (Expr::Struct { captured, .. } let (Expr::Struct { captured, .. }
@ -1212,6 +1270,10 @@ impl Types {
let str = unsafe { core::mem::transmute::<&mut Vec<u8>, &mut String>(data) }; let str = unsafe { core::mem::transmute::<&mut Vec<u8>, &mut String>(data) };
write!(str, "{}", Display::new(self, files, ty)).unwrap(); write!(str, "{}", Display::new(self, files, ty)).unwrap();
} }
pub fn tuple_fields(&self, tuple: Tuple) -> &[Id] {
&self.ins.args[self.ins.tuples[tuple].fields.range()]
}
} }
pub struct OptLayout { pub struct OptLayout {
@ -1220,17 +1282,57 @@ pub struct OptLayout {
pub payload_offset: Offset, pub payload_offset: Offset,
} }
pub struct OffsetIter { pub trait Agregate: Copy {
strct: Struct, type Field: AsRef<Id> + 'static;
fn fields(self, tys: &Types) -> Range<usize>;
fn field_by_idx(tys: &Types, index: usize) -> &Self::Field;
fn align_override(self, _: &Types) -> Option<u8> {
None
}
}
impl Agregate for Tuple {
type Field = Id;
fn fields(self, tys: &Types) -> Range<usize> {
tys.ins.tuples[self].fields.range()
}
fn field_by_idx(tys: &Types, index: usize) -> &Self::Field {
&tys.ins.args[index]
}
}
impl Agregate for Struct {
type Field = StructField;
fn fields(self, tys: &Types) -> Range<usize> {
tys.struct_field_range(self)
}
fn field_by_idx(tys: &Types, index: usize) -> &Self::Field {
&tys.ins.struct_fields[index]
}
fn align_override(self, tys: &Types) -> Option<u8> {
tys.ins.structs[self].explicit_alignment
}
}
impl AsRef<Id> for StructField {
fn as_ref(&self) -> &Id {
&self.ty
}
}
pub struct OffsetIter<T> {
strct: T,
offset: Offset, offset: Offset,
fields: Range<usize>, fields: Range<usize>,
} }
impl OffsetIter { impl OffsetIter<Struct> {
pub fn new(strct: Struct, tys: &Types) -> Self {
Self { strct, offset: 0, fields: tys.struct_field_range(strct) }
}
pub fn offset_of(tys: &Types, idx: Struct, field: &str) -> Option<(Offset, Id)> { pub fn offset_of(tys: &Types, idx: Struct, field: &str) -> Option<(Offset, Id)> {
let field_id = tys.names.project(field)?; let field_id = tys.names.project(field)?;
OffsetIter::new(idx, tys) OffsetIter::new(idx, tys)
@ -1238,25 +1340,33 @@ impl OffsetIter {
.find(|(f, _)| f.name == field_id) .find(|(f, _)| f.name == field_id)
.map(|(f, off)| (off, f.ty)) .map(|(f, off)| (off, f.ty))
} }
}
fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a StructField, Offset)> { impl<T: Agregate> OffsetIter<T> {
let stru = &tys.ins.structs[self.strct]; pub fn new(strct: T, tys: &Types) -> Self {
let field = &tys.ins.struct_fields[self.fields.next()?]; Self { strct, offset: 0, fields: strct.fields(tys) }
}
let align = stru.explicit_alignment.map_or_else(|| tys.align_of(field.ty), |a| a as u32); fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a T::Field, Offset)> {
let field = &T::field_by_idx(tys, self.fields.next()?);
let align = self
.strct
.align_override(tys)
.map_or_else(|| tys.align_of(*field.as_ref()), |a| a as u32);
self.offset = (self.offset + align - 1) & !(align - 1); self.offset = (self.offset + align - 1) & !(align - 1);
let off = self.offset; let off = self.offset;
self.offset += tys.size_of(field.ty); self.offset += tys.size_of(*field.as_ref());
Some((field, off)) Some((field, off))
} }
pub fn next_ty(&mut self, tys: &Types) -> Option<(Id, Offset)> { pub fn next_ty(&mut self, tys: &Types) -> Option<(Id, Offset)> {
let (field, off) = self.next(tys)?; let (field, off) = self.next(tys)?;
Some((field.ty, off)) Some((*field.as_ref(), off))
} }
pub fn into_iter(mut self, tys: &Types) -> impl Iterator<Item = (&StructField, Offset)> { pub fn into_iter(mut self, tys: &Types) -> impl Iterator<Item = (&T::Field, Offset)> {
core::iter::from_fn(move || self.next(tys)) core::iter::from_fn(move || self.next(tys))
} }
} }

View file

@ -0,0 +1,6 @@
main:
CP r1, r0
JALA r0, r31, 0a
code size: 22
ret: 0
status: Ok(())