forked from AbleOS/holey-bytes
adding unrolled loops, struct indexing and @len
directive
Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
parent
bfac81c807
commit
b2be007ef0
|
@ -568,7 +568,6 @@ main := fn(): uint {
|
||||||
return big_array[42]
|
return big_array[42]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
note: this does not work on scalar values
|
|
||||||
|
|
||||||
#### generic_functions
|
#### generic_functions
|
||||||
```hb
|
```hb
|
||||||
|
@ -678,6 +677,21 @@ main := fn(): uint {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### unrolled_loops
|
||||||
|
```hb
|
||||||
|
Nums := struct {a: uint, b: u32, c: u16, d: u8}
|
||||||
|
|
||||||
|
main := fn(): uint {
|
||||||
|
nums := Nums.(1, 2, 3, 4)
|
||||||
|
i := 0
|
||||||
|
sum := 0
|
||||||
|
$loop if i == @len(Nums) break else {
|
||||||
|
sum += nums[i]
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
return sum - 10
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Incomplete Examples
|
### Incomplete Examples
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ fn token_group(kind: TokenKind) -> TokenGroup {
|
||||||
| ShrAss | ShlAss => TG::Assign,
|
| ShrAss | ShlAss => TG::Assign,
|
||||||
DQuote | Quote => TG::String,
|
DQuote | Quote => TG::String,
|
||||||
Slf | Defer | Return | If | Else | Loop | Break | Continue | Fn | Idk | Die | Struct
|
Slf | Defer | Return | If | Else | Loop | Break | Continue | Fn | Idk | Die | Struct
|
||||||
| Packed | True | False | Null | Match | Enum | Union => TG::Keyword,
|
| Packed | True | False | Null | Match | Enum | Union | CtLoop => TG::Keyword,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ pub fn minify(source: &mut str) -> usize {
|
||||||
let mut token = lexer::Lexer::new(reader).eat();
|
let mut token = lexer::Lexer::new(reader).eat();
|
||||||
match token.kind {
|
match token.kind {
|
||||||
TokenKind::Eof => break,
|
TokenKind::Eof => break,
|
||||||
TokenKind::CtIdent | TokenKind::Directive => token.start -= 1,
|
TokenKind::CtIdent | TokenKind::CtLoop | TokenKind::Directive => token.start -= 1,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,8 +448,8 @@ impl<'a> Formatter<'a> {
|
||||||
s.fmt(&br.body, f)
|
s.fmt(&br.body, f)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Expr::Loop { body, .. } => {
|
Expr::Loop { body, unrolled, .. } => {
|
||||||
f.write_str("loop ")?;
|
f.write_str(if unrolled { "$loop " } else { "loop " })?;
|
||||||
self.fmt(body, f)
|
self.fmt(body, f)
|
||||||
}
|
}
|
||||||
Expr::Closure { ret, body, args, .. } => {
|
Expr::Closure { ret, body, args, .. } => {
|
||||||
|
|
|
@ -32,6 +32,9 @@ macro_rules! gen_token_kind {
|
||||||
#[keywords] $(
|
#[keywords] $(
|
||||||
$keyword:ident = $keyword_lit:literal,
|
$keyword:ident = $keyword_lit:literal,
|
||||||
)*
|
)*
|
||||||
|
#[const_keywords] $(
|
||||||
|
$const_keyword:ident = $const_keyword_lit:literal,
|
||||||
|
)*
|
||||||
#[punkt] $(
|
#[punkt] $(
|
||||||
$punkt:ident = $punkt_lit:literal,
|
$punkt:ident = $punkt_lit:literal,
|
||||||
)*
|
)*
|
||||||
|
@ -56,6 +59,7 @@ macro_rules! gen_token_kind {
|
||||||
match *self {
|
match *self {
|
||||||
$( Self::$pattern => concat!('<', stringify!($pattern), '>'), )*
|
$( Self::$pattern => concat!('<', stringify!($pattern), '>'), )*
|
||||||
$( Self::$keyword => stringify!($keyword_lit), )*
|
$( Self::$keyword => stringify!($keyword_lit), )*
|
||||||
|
$( Self::$const_keyword => concat!('$', $const_keyword_lit), )*
|
||||||
$( Self::$punkt => stringify!($punkt_lit), )*
|
$( Self::$punkt => stringify!($punkt_lit), )*
|
||||||
$($( Self::$op => $op_lit,
|
$($( Self::$op => $op_lit,
|
||||||
$(Self::$assign => concat!($op_lit, "="),)?)*)*
|
$(Self::$assign => concat!($op_lit, "="),)?)*)*
|
||||||
|
@ -72,12 +76,23 @@ macro_rules! gen_token_kind {
|
||||||
} + 1)
|
} + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
fn from_ident(ident: &[u8]) -> Self {
|
fn from_ident(ident: &[u8]) -> Self {
|
||||||
|
$(const $keyword: &[u8] = $keyword_lit.as_bytes();)*
|
||||||
match ident {
|
match ident {
|
||||||
$($keyword_lit => Self::$keyword,)*
|
$($keyword => Self::$keyword,)*
|
||||||
_ => Self::Ident,
|
_ => Self::Ident,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
fn from_ct_ident(ident: &[u8]) -> Self {
|
||||||
|
$(const $const_keyword: &[u8] = $const_keyword_lit.as_bytes();)*
|
||||||
|
match ident {
|
||||||
|
$($const_keyword => Self::$const_keyword,)*
|
||||||
|
_ => Self::CtIdent,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -156,6 +171,8 @@ pub enum TokenKind {
|
||||||
Die,
|
Die,
|
||||||
Defer,
|
Defer,
|
||||||
|
|
||||||
|
CtLoop,
|
||||||
|
|
||||||
// Unused = a-z
|
// Unused = a-z
|
||||||
LBrace = b'{',
|
LBrace = b'{',
|
||||||
Bor = b'|',
|
Bor = b'|',
|
||||||
|
@ -305,26 +322,28 @@ gen_token_kind! {
|
||||||
Eof,
|
Eof,
|
||||||
Directive,
|
Directive,
|
||||||
#[keywords]
|
#[keywords]
|
||||||
Slf = b"Self",
|
Slf = "Self",
|
||||||
Return = b"return",
|
Return = "return",
|
||||||
If = b"if",
|
If = "if",
|
||||||
Match = b"match",
|
Match = "match",
|
||||||
Else = b"else",
|
Else = "else",
|
||||||
Loop = b"loop",
|
Loop = "loop",
|
||||||
Break = b"break",
|
Break = "break",
|
||||||
Continue = b"continue",
|
Continue = "continue",
|
||||||
Fn = b"fn",
|
Fn = "fn",
|
||||||
Struct = b"struct",
|
Struct = "struct",
|
||||||
Packed = b"packed",
|
Packed = "packed",
|
||||||
Enum = b"enum",
|
Enum = "enum",
|
||||||
Union = b"union",
|
Union = "union",
|
||||||
True = b"true",
|
True = "true",
|
||||||
False = b"false",
|
False = "false",
|
||||||
Null = b"null",
|
Null = "null",
|
||||||
Idk = b"idk",
|
Idk = "idk",
|
||||||
Die = b"die",
|
Die = "die",
|
||||||
Defer = b"defer",
|
Defer = "defer",
|
||||||
Under = b"_",
|
Under = "_",
|
||||||
|
#[const_keywords]
|
||||||
|
CtLoop = "loop",
|
||||||
#[punkt]
|
#[punkt]
|
||||||
Ctor = ".{",
|
Ctor = ".{",
|
||||||
Tupl = ".(",
|
Tupl = ".(",
|
||||||
|
@ -535,11 +554,17 @@ impl<'a> Lexer<'a> {
|
||||||
b'&' if self.advance_if(b'&') => T::And,
|
b'&' if self.advance_if(b'&') => T::And,
|
||||||
b'|' if self.advance_if(b'|') => T::Or,
|
b'|' if self.advance_if(b'|') => T::Or,
|
||||||
b'$' if self.advance_if(b':') => T::Ct,
|
b'$' if self.advance_if(b':') => T::Ct,
|
||||||
b'@' | b'$' => {
|
b'@' => {
|
||||||
start += 1;
|
start += 1;
|
||||||
advance_ident(self);
|
advance_ident(self);
|
||||||
identity(c)
|
identity(c)
|
||||||
}
|
}
|
||||||
|
b'$' => {
|
||||||
|
start += 1;
|
||||||
|
advance_ident(self);
|
||||||
|
let ident = &self.source[start as usize..self.pos as usize];
|
||||||
|
T::from_ct_ident(ident)
|
||||||
|
}
|
||||||
b'<' | b'>' if self.advance_if(c) => {
|
b'<' | b'>' if self.advance_if(c) => {
|
||||||
identity(c - 5 + 128 * self.advance_if(b'=') as u8)
|
identity(c - 5 + 128 * self.advance_if(b'=') as u8)
|
||||||
}
|
}
|
||||||
|
|
|
@ -447,7 +447,8 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
T::Loop => E::Loop { pos, body: self.ptr_expr()? },
|
T::Loop => E::Loop { pos, unrolled: false, body: self.ptr_expr()? },
|
||||||
|
T::CtLoop => E::Loop { pos, unrolled: true, body: self.ptr_expr()? },
|
||||||
T::Break => E::Break { pos },
|
T::Break => E::Break { pos },
|
||||||
T::Continue => E::Continue { pos },
|
T::Continue => E::Continue { pos },
|
||||||
T::Return => E::Return {
|
T::Return => E::Return {
|
||||||
|
@ -984,6 +985,7 @@ generate_expr! {
|
||||||
/// `'loop' Expr`
|
/// `'loop' Expr`
|
||||||
Loop {
|
Loop {
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
|
unrolled: bool,
|
||||||
body: &'a Self,
|
body: &'a Self,
|
||||||
},
|
},
|
||||||
/// `('&' | '*' | '^') Expr`
|
/// `('&' | '*' | '^') Expr`
|
||||||
|
|
310
lang/src/son.rs
310
lang/src/son.rs
|
@ -2010,8 +2010,11 @@ impl Nodes {
|
||||||
debug_assert!(!var.ptr);
|
debug_assert!(!var.ptr);
|
||||||
|
|
||||||
let [loops @ .., loob] = loops else { unreachable!() };
|
let [loops @ .., loob] = loops else { unreachable!() };
|
||||||
let node = loob.node;
|
let &mut Loop::Runtime { node, ref mut scope, .. } = loob else {
|
||||||
let lvar = &mut loob.scope.vars[index];
|
self.load_loop_var(index, var, loops);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let lvar = &mut scope.vars[index];
|
||||||
|
|
||||||
debug_assert!(!lvar.ptr);
|
debug_assert!(!lvar.ptr);
|
||||||
|
|
||||||
|
@ -2033,8 +2036,11 @@ impl Nodes {
|
||||||
}
|
}
|
||||||
|
|
||||||
let [loops @ .., loob] = loops else { unreachable!() };
|
let [loops @ .., loob] = loops else { unreachable!() };
|
||||||
let node = loob.node;
|
let &mut Loop::Runtime { node, ref mut scope, .. } = loob else {
|
||||||
let lvar = &mut loob.scope.aclasses[index];
|
self.load_loop_aclass(index, aclass, loops);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let lvar = &mut scope.aclasses[index];
|
||||||
|
|
||||||
self.load_loop_aclass(index, lvar, loops);
|
self.load_loop_aclass(index, lvar, loops);
|
||||||
|
|
||||||
|
@ -2312,13 +2318,24 @@ type LoopDepth = u16;
|
||||||
type LockRc = u16;
|
type LockRc = u16;
|
||||||
type IDomDepth = u16;
|
type IDomDepth = u16;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum CtLoopState {
|
||||||
|
Terminated,
|
||||||
|
Continued,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Loop {
|
enum Loop {
|
||||||
node: Nid,
|
Comptime {
|
||||||
ctrl: [StrongRef; 2],
|
state: Option<(CtLoopState, Pos)>,
|
||||||
ctrl_scope: [Scope; 2],
|
},
|
||||||
scope: Scope,
|
Runtime {
|
||||||
defer_base: usize,
|
node: Nid,
|
||||||
|
ctrl: [StrongRef; 2],
|
||||||
|
ctrl_scope: [Scope; 2],
|
||||||
|
scope: Scope,
|
||||||
|
defer_base: usize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mod strong_ref {
|
mod strong_ref {
|
||||||
|
@ -2628,14 +2645,16 @@ impl Pool {
|
||||||
fn restore_ci(&mut self, dst: &mut ItemCtx) {
|
fn restore_ci(&mut self, dst: &mut ItemCtx) {
|
||||||
self.used_cis -= 1;
|
self.used_cis -= 1;
|
||||||
dst.scope.clear(&mut dst.nodes);
|
dst.scope.clear(&mut dst.nodes);
|
||||||
dst.loops.drain(..).for_each(|mut l| {
|
dst.loops.drain(..).for_each(|l| {
|
||||||
l.ctrl.map(|c| {
|
if let Loop::Runtime { ctrl, ctrl_scope, mut scope, .. } = l {
|
||||||
if c.is_live() {
|
ctrl.map(|c| {
|
||||||
c.remove(&mut dst.nodes);
|
if c.is_live() {
|
||||||
}
|
c.remove(&mut dst.nodes);
|
||||||
});
|
}
|
||||||
l.scope.clear(&mut dst.nodes);
|
});
|
||||||
l.ctrl_scope.map(|mut s| s.clear(&mut dst.nodes));
|
scope.clear(&mut dst.nodes);
|
||||||
|
ctrl_scope.map(|mut s| s.clear(&mut dst.nodes));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
mem::take(&mut dst.ctrl).remove(&mut dst.nodes);
|
mem::take(&mut dst.ctrl).remove(&mut dst.nodes);
|
||||||
*dst = mem::take(&mut self.cis[self.used_cis]);
|
*dst = mem::take(&mut self.cis[self.used_cis]);
|
||||||
|
@ -3399,38 +3418,67 @@ impl<'a> Codegen<'a> {
|
||||||
bs.ty = base;
|
bs.ty = base;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ty::Kind::Slice(s) = bs.ty.expand() else {
|
let mut idx = self.expr_ctx(index, Ctx::default().with_ty(ty::Id::DINT))?;
|
||||||
return self.error(
|
|
||||||
|
match bs.ty.expand() {
|
||||||
|
ty::Kind::Slice(s) => {
|
||||||
|
let elem = self.tys.ins.slices[s].elem;
|
||||||
|
let size = self.ci.nodes.new_const(ty::Id::INT, self.tys.size_of(elem));
|
||||||
|
self.assert_ty(index.pos(), &mut idx, ty::Id::DINT, "subscript");
|
||||||
|
let inps = [VOID, idx.id, size];
|
||||||
|
let offset = self.ci.nodes.new_node(
|
||||||
|
ty::Id::INT,
|
||||||
|
Kind::BinOp { op: TokenKind::Mul },
|
||||||
|
inps,
|
||||||
|
self.tys,
|
||||||
|
);
|
||||||
|
let aclass = self.ci.nodes.aclass_index(bs.id).1;
|
||||||
|
let inps = [VOID, bs.id, offset];
|
||||||
|
let ptr = self.ci.nodes.new_node(
|
||||||
|
ty::Id::INT,
|
||||||
|
Kind::BinOp { op: TokenKind::Add },
|
||||||
|
inps,
|
||||||
|
self.tys,
|
||||||
|
);
|
||||||
|
self.ci.nodes.pass_aclass(aclass, ptr);
|
||||||
|
|
||||||
|
Some(Value::ptr(ptr).ty(elem))
|
||||||
|
}
|
||||||
|
ty::Kind::Struct(s) => {
|
||||||
|
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(s, self.tys)
|
||||||
|
.into_iter(self.tys)
|
||||||
|
.nth(idx as _)
|
||||||
|
.map(|(f, off)| (f.ty, off))
|
||||||
|
else {
|
||||||
|
return self.error(
|
||||||
|
index.pos(),
|
||||||
|
fa!(
|
||||||
|
"struct '{}' has only `{}' fields, \
|
||||||
|
but index was '{}'",
|
||||||
|
self.ty_display(bs.ty),
|
||||||
|
self.tys.struct_fields(s).len(),
|
||||||
|
idx
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Value::ptr(self.offset(bs.id, offset)).ty(f))
|
||||||
|
}
|
||||||
|
_ => 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",
|
||||||
self.ty_display(bs.ty)
|
self.ty_display(bs.ty)
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
};
|
}
|
||||||
|
|
||||||
let elem = self.tys.ins.slices[s].elem;
|
|
||||||
let mut idx = self.expr_ctx(index, Ctx::default().with_ty(ty::Id::DINT))?;
|
|
||||||
self.assert_ty(index.pos(), &mut idx, ty::Id::DINT, "subscript");
|
|
||||||
let size = self.ci.nodes.new_const(ty::Id::INT, self.tys.size_of(elem));
|
|
||||||
let inps = [VOID, idx.id, size];
|
|
||||||
let offset = self.ci.nodes.new_node(
|
|
||||||
ty::Id::INT,
|
|
||||||
Kind::BinOp { op: TokenKind::Mul },
|
|
||||||
inps,
|
|
||||||
self.tys,
|
|
||||||
);
|
|
||||||
let aclass = self.ci.nodes.aclass_index(bs.id).1;
|
|
||||||
let inps = [VOID, bs.id, offset];
|
|
||||||
let ptr = self.ci.nodes.new_node(
|
|
||||||
ty::Id::INT,
|
|
||||||
Kind::BinOp { op: TokenKind::Add },
|
|
||||||
inps,
|
|
||||||
self.tys,
|
|
||||||
);
|
|
||||||
self.ci.nodes.pass_aclass(aclass, ptr);
|
|
||||||
|
|
||||||
Some(Value::ptr(ptr).ty(elem))
|
|
||||||
}
|
}
|
||||||
Expr::Embed { id, .. } => {
|
Expr::Embed { id, .. } => {
|
||||||
let glob = &self.tys.ins.globals[id];
|
let glob = &self.tys.ins.globals[id];
|
||||||
|
@ -3447,6 +3495,20 @@ impl<'a> Codegen<'a> {
|
||||||
let align = self.tys.align_of(ty);
|
let align = self.tys.align_of(ty);
|
||||||
self.gen_inferred_const(ctx, ty::Id::DINT, align, ty::Id::is_integer)
|
self.gen_inferred_const(ctx, ty::Id::DINT, align, ty::Id::is_integer)
|
||||||
}
|
}
|
||||||
|
Expr::Directive { name: "len", args: [ety], .. } => {
|
||||||
|
let ty = self.ty(ety);
|
||||||
|
let Some(len) = self.tys.len_of(ty) else {
|
||||||
|
return self.error(
|
||||||
|
ety.pos(),
|
||||||
|
fa!(
|
||||||
|
"'@len' only supports structs and arrays, \
|
||||||
|
'{}' is neither",
|
||||||
|
self.ty_display(ty)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
self.gen_inferred_const(ctx, ty::Id::DINT, len, ty::Id::is_integer)
|
||||||
|
}
|
||||||
Expr::Directive { name: "bitcast", args: [val], pos } => {
|
Expr::Directive { name: "bitcast", args: [val], pos } => {
|
||||||
let mut val = self.raw_expr(val)?;
|
let mut val = self.raw_expr(val)?;
|
||||||
self.strip_var(&mut val);
|
self.strip_var(&mut val);
|
||||||
|
@ -3886,7 +3948,47 @@ impl<'a> Codegen<'a> {
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
Expr::Loop { body, .. } => {
|
Expr::Loop { unrolled: true, body, pos } => {
|
||||||
|
let mut loop_fuel = 100;
|
||||||
|
|
||||||
|
self.ci.loops.push(Loop::Comptime { state: None });
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if loop_fuel == 0 {
|
||||||
|
return self.error(
|
||||||
|
pos,
|
||||||
|
"unrolled loop exceeded 100 iterations, use normal loop instead, TODO: add escape hatch",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
loop_fuel -= 1;
|
||||||
|
|
||||||
|
let terminated = self.expr(body).is_none();
|
||||||
|
|
||||||
|
let Some(&Loop::Comptime { state }) = self.ci.loops.last() else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
if !terminated && let Some((_, prev)) = state {
|
||||||
|
self.error(
|
||||||
|
pos,
|
||||||
|
"reached a constrol flow keyword inside an unrolled loop, \
|
||||||
|
as well ast the end of the loop, make sure control flow is \
|
||||||
|
not dependant on a runtime value",
|
||||||
|
);
|
||||||
|
return self.error(prev, "previous reachable control flow found here");
|
||||||
|
}
|
||||||
|
|
||||||
|
match state {
|
||||||
|
Some((CtLoopState::Terminated, _)) => break,
|
||||||
|
Some((CtLoopState::Continued, _)) | None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ci.loops.pop().unwrap();
|
||||||
|
|
||||||
|
Some(Value::VOID)
|
||||||
|
}
|
||||||
|
Expr::Loop { body, unrolled: false, .. } => {
|
||||||
self.ci.ctrl.set(
|
self.ci.ctrl.set(
|
||||||
self.ci.nodes.new_node(
|
self.ci.nodes.new_node(
|
||||||
ty::Id::VOID,
|
ty::Id::VOID,
|
||||||
|
@ -3897,7 +3999,7 @@ impl<'a> Codegen<'a> {
|
||||||
&mut self.ci.nodes,
|
&mut self.ci.nodes,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.ci.loops.push(Loop {
|
self.ci.loops.push(Loop::Runtime {
|
||||||
node: self.ci.ctrl.get(),
|
node: self.ci.ctrl.get(),
|
||||||
ctrl: [StrongRef::DEFAULT; 2],
|
ctrl: [StrongRef::DEFAULT; 2],
|
||||||
ctrl_scope: core::array::from_fn(|_| Default::default()),
|
ctrl_scope: core::array::from_fn(|_| Default::default()),
|
||||||
|
@ -3920,8 +4022,11 @@ impl<'a> Codegen<'a> {
|
||||||
|
|
||||||
self.expr(body);
|
self.expr(body);
|
||||||
|
|
||||||
let Loop { ctrl: [con, ..], ctrl_scope: [cons, ..], .. } =
|
let Some(Loop::Runtime { ctrl: [con, ..], ctrl_scope: [cons, ..], .. }) =
|
||||||
self.ci.loops.last_mut().unwrap();
|
self.ci.loops.last_mut()
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
let mut cons = mem::take(cons);
|
let mut cons = mem::take(cons);
|
||||||
|
|
||||||
if let Some(con) = mem::take(con).unwrap(&mut self.ci.nodes) {
|
if let Some(con) = mem::take(con).unwrap(&mut self.ci.nodes) {
|
||||||
|
@ -3944,13 +4049,16 @@ impl<'a> Codegen<'a> {
|
||||||
cons.clear(&mut self.ci.nodes);
|
cons.clear(&mut self.ci.nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Loop {
|
let Some(Loop::Runtime {
|
||||||
node,
|
node,
|
||||||
ctrl: [.., bre],
|
ctrl: [.., bre],
|
||||||
ctrl_scope: [.., mut bres],
|
ctrl_scope: [.., mut bres],
|
||||||
mut scope,
|
mut scope,
|
||||||
defer_base,
|
defer_base,
|
||||||
} = self.ci.loops.pop().unwrap();
|
}) = self.ci.loops.pop()
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
self.gen_defers(defer_base);
|
self.gen_defers(defer_base);
|
||||||
self.ci.defers.truncate(defer_base);
|
self.ci.defers.truncate(defer_base);
|
||||||
|
@ -5006,52 +5114,75 @@ impl<'a> Codegen<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn jump_to(&mut self, pos: Pos, id: usize) -> Option<Value> {
|
fn jump_to(&mut self, pos: Pos, id: usize) -> Option<Value> {
|
||||||
let Some(&mut Loop { defer_base, .. }) = self.ci.loops.last_mut() else {
|
let Some(loob) = self.ci.loops.last_mut() else {
|
||||||
self.error(pos, "break outside a loop");
|
self.error(pos, "break outside a loop");
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.gen_defers(defer_base)?;
|
match loob {
|
||||||
|
&mut Loop::Comptime { state: Some((_, prev)) } => {
|
||||||
|
self.error(
|
||||||
|
pos,
|
||||||
|
"reached multiple control flow keywords inside an unrolled loop, \
|
||||||
|
make sure control flow is not dependant on a runtime value",
|
||||||
|
);
|
||||||
|
self.error(prev, "previous reachable control flow found here");
|
||||||
|
}
|
||||||
|
Loop::Comptime { state: state @ None } => {
|
||||||
|
*state = Some(([CtLoopState::Continued, CtLoopState::Terminated][id], pos))
|
||||||
|
}
|
||||||
|
&mut Loop::Runtime { defer_base, .. } => {
|
||||||
|
self.gen_defers(defer_base)?;
|
||||||
|
|
||||||
let mut loob = self.ci.loops.last_mut().unwrap();
|
let Loop::Runtime { ctrl: lctrl, ctrl_scope, scope, .. } =
|
||||||
|
self.ci.loops.last_mut().unwrap()
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
if loob.ctrl[id].is_live() {
|
if lctrl[id].is_live() {
|
||||||
loob.ctrl[id].set(
|
lctrl[id].set(
|
||||||
self.ci.nodes.new_node(
|
self.ci.nodes.new_node(
|
||||||
ty::Id::VOID,
|
ty::Id::VOID,
|
||||||
Kind::Region,
|
Kind::Region,
|
||||||
[self.ci.ctrl.get(), loob.ctrl[id].get()],
|
[self.ci.ctrl.get(), lctrl[id].get()],
|
||||||
self.tys,
|
self.tys,
|
||||||
),
|
),
|
||||||
&mut self.ci.nodes,
|
&mut self.ci.nodes,
|
||||||
);
|
);
|
||||||
let mut scope = mem::take(&mut loob.ctrl_scope[id]);
|
let mut scope = mem::take(&mut ctrl_scope[id]);
|
||||||
let ctrl = mem::take(&mut loob.ctrl[id]);
|
let ctrl = mem::take(&mut lctrl[id]);
|
||||||
|
|
||||||
self.ci.nodes.merge_scopes(
|
self.ci.nodes.merge_scopes(
|
||||||
&mut self.ci.loops,
|
&mut self.ci.loops,
|
||||||
&ctrl,
|
&ctrl,
|
||||||
&mut scope,
|
&mut scope,
|
||||||
&mut self.ci.scope,
|
&mut self.ci.scope,
|
||||||
self.tys,
|
self.tys,
|
||||||
);
|
);
|
||||||
|
|
||||||
loob = self.ci.loops.last_mut().unwrap();
|
let Loop::Runtime { ctrl: lctrl, ctrl_scope, .. } =
|
||||||
loob.ctrl_scope[id] = scope;
|
self.ci.loops.last_mut().unwrap()
|
||||||
loob.ctrl[id] = ctrl;
|
else {
|
||||||
self.ci.ctrl.set(NEVER, &mut self.ci.nodes);
|
unreachable!()
|
||||||
} else {
|
};
|
||||||
let term = StrongRef::new(NEVER, &mut self.ci.nodes);
|
ctrl_scope[id] = scope;
|
||||||
loob.ctrl[id] = mem::replace(&mut self.ci.ctrl, term);
|
lctrl[id] = ctrl;
|
||||||
loob.ctrl_scope[id] = self.ci.scope.dup(&mut self.ci.nodes);
|
self.ci.ctrl.set(NEVER, &mut self.ci.nodes);
|
||||||
loob.ctrl_scope[id]
|
} else {
|
||||||
.vars
|
let term = StrongRef::new(NEVER, &mut self.ci.nodes);
|
||||||
.drain(loob.scope.vars.len()..)
|
lctrl[id] = mem::replace(&mut self.ci.ctrl, term);
|
||||||
.for_each(|v| v.remove(&mut self.ci.nodes));
|
ctrl_scope[id] = self.ci.scope.dup(&mut self.ci.nodes);
|
||||||
loob.ctrl_scope[id]
|
ctrl_scope[id]
|
||||||
.aclasses
|
.vars
|
||||||
.drain(loob.scope.aclasses.len()..)
|
.drain(scope.vars.len()..)
|
||||||
.for_each(|v| v.remove(&mut self.ci.nodes));
|
.for_each(|v| v.remove(&mut self.ci.nodes));
|
||||||
|
ctrl_scope[id]
|
||||||
|
.aclasses
|
||||||
|
.drain(scope.aclasses.len()..)
|
||||||
|
.for_each(|v| v.remove(&mut self.ci.nodes));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
@ -6024,6 +6155,7 @@ mod tests {
|
||||||
generic_functions;
|
generic_functions;
|
||||||
die;
|
die;
|
||||||
defer;
|
defer;
|
||||||
|
unrolled_loops;
|
||||||
|
|
||||||
// Incomplete Examples;
|
// Incomplete Examples;
|
||||||
//comptime_pointers;
|
//comptime_pointers;
|
||||||
|
|
|
@ -1176,6 +1176,14 @@ impl Types {
|
||||||
debug_assert_eq!(captured.len(), base.captured.len());
|
debug_assert_eq!(captured.len(), base.captured.len());
|
||||||
Some((captured, base.captured))
|
Some((captured, base.captured))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len_of(&self, ty: Id) -> Option<u32> {
|
||||||
|
Some(match ty.expand() {
|
||||||
|
Kind::Struct(s) => self.struct_field_range(s).len() as _,
|
||||||
|
Kind::Slice(s) => self.ins.slices[s].len()? as _,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OptLayout {
|
pub struct OptLayout {
|
||||||
|
|
6
lang/tests/son_tests_unrolled_loops.txt
Normal file
6
lang/tests/son_tests_unrolled_loops.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
main:
|
||||||
|
CP r1, r0
|
||||||
|
JALA r0, r31, 0a
|
||||||
|
code size: 22
|
||||||
|
ret: 0
|
||||||
|
status: Ok(())
|
Loading…
Reference in a new issue