forked from AbleOS/holey-bytes
making the identifiers accessible if they are captured
Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
parent
1621d93e86
commit
af19f4e30d
|
@ -1,4 +1,4 @@
|
||||||
# The journey to optimizing compiler
|
# The journey to an optimizing compiler
|
||||||
|
|
||||||
It's been years since I was continuously trying to make a compiler to implement language of my dreams. Problem was tho that I wanted something similar to Rust, which if you did not know, `rustc` far exceeded the one million lines of code mark some time ago, so implementing such language would take me years if not decades, but I still tired it.
|
It's been years since I was continuously trying to make a compiler to implement language of my dreams. Problem was tho that I wanted something similar to Rust, which if you did not know, `rustc` far exceeded the one million lines of code mark some time ago, so implementing such language would take me years if not decades, but I still tired it.
|
||||||
|
|
||||||
|
@ -58,4 +58,4 @@ Its stupid but its the world we live in, optimizers are usually a black box you
|
||||||
|
|
||||||
But wait its worse! Since optimizers wont ever share the fact you are stupid, we end up with other people painstakingly writing complex linters, that will do a shitty job detecting things that matter, and instead whine about style and other bullcrap (and they suck even at that). If the people who write linters and people who write optimizers swapped the roles, I would be ranting about optimizers instead.
|
But wait its worse! Since optimizers wont ever share the fact you are stupid, we end up with other people painstakingly writing complex linters, that will do a shitty job detecting things that matter, and instead whine about style and other bullcrap (and they suck even at that). If the people who write linters and people who write optimizers swapped the roles, I would be ranting about optimizers instead.
|
||||||
|
|
||||||
And so, this is the area where I want to innovate, lets report the dead code to the frontend, and let the compiler frontend filter out the noise and show relevant information in the diagnostics. Refuse to compile the program if you `i /= 0`. Refuse to compile if you `arr[arr.len]`. This is the level of stupid optimizer sees, once it normalizes your code, but proceeds to protect your feeling. And hblang will relay this to you as much as possible. If we can query for optimizations, we can query for bugs too.
|
And so, this is the area where I want to innovate, lets report the dead code to the frontend, and let the compiler frontend filter out the noise and show relevant information in the diagnostics. Refuse to compile the program if you `i /= 0`. Refuse to compile if you `arr[arr.len]`. This is the level of stupid optimizer sees, once it normalizes your code, but proceeds to protect your feelings. My goal so for hblang to relay this to you as much as possible. If we can query for optimizations, we can query for bugs too.
|
||||||
|
|
|
@ -774,6 +774,25 @@ main := fn(): uint {
|
||||||
|
|
||||||
### Purely Testing Examples
|
### Purely Testing Examples
|
||||||
|
|
||||||
|
#### proper_ident_propagation
|
||||||
|
```hb
|
||||||
|
A := fn($T: type): type return struct {a: T}
|
||||||
|
|
||||||
|
$make_a := fn(a: @Any()): A(@TypeOf(a)) {
|
||||||
|
return .(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
$make_b := fn(a: @Any()): struct {b: @TypeOf(a)} {
|
||||||
|
return .(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
main := fn(): uint {
|
||||||
|
a := make_a(100)
|
||||||
|
b := make_b(100)
|
||||||
|
return a.a - b.b
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### method_receiver_by_value
|
#### method_receiver_by_value
|
||||||
```hb
|
```hb
|
||||||
$log := fn(ptr: ^u8): void return @eca(37, ptr)
|
$log := fn(ptr: ^u8): void return @eca(37, ptr)
|
||||||
|
|
|
@ -80,6 +80,7 @@ struct ScopeIdent {
|
||||||
declared: bool,
|
declared: bool,
|
||||||
ordered: bool,
|
ordered: bool,
|
||||||
used: bool,
|
used: bool,
|
||||||
|
is_ct: bool,
|
||||||
flags: IdentFlags,
|
flags: IdentFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,8 +197,8 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
|
|
||||||
fn declare_rec(&mut self, expr: &Expr, top_level: bool) {
|
fn declare_rec(&mut self, expr: &Expr, top_level: bool) {
|
||||||
match *expr {
|
match *expr {
|
||||||
Expr::Ident { pos, id, is_first, .. } => {
|
Expr::Ident { pos, id, is_first, is_ct, .. } => {
|
||||||
self.declare(pos, id, !top_level, is_first || top_level)
|
self.declare(pos, id, !top_level, is_first || top_level, is_ct)
|
||||||
}
|
}
|
||||||
Expr::Ctor { fields, .. } => {
|
Expr::Ctor { fields, .. } => {
|
||||||
for CtorField { value, .. } in fields {
|
for CtorField { value, .. } in fields {
|
||||||
|
@ -208,7 +209,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare(&mut self, pos: Pos, id: Ident, ordered: bool, valid_order: bool) {
|
fn declare(&mut self, pos: Pos, id: Ident, ordered: bool, valid_order: bool, is_ct: bool) {
|
||||||
if !valid_order {
|
if !valid_order {
|
||||||
self.report(
|
self.report(
|
||||||
pos,
|
pos,
|
||||||
|
@ -230,7 +231,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
self.ctx.idents[index].is_ct = is_ct;
|
||||||
self.ctx.idents[index].ordered = ordered;
|
self.ctx.idents[index].ordered = ordered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,6 +268,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
declared: false,
|
declared: false,
|
||||||
used: false,
|
used: false,
|
||||||
ordered: false,
|
ordered: false,
|
||||||
|
is_ct: false,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
});
|
});
|
||||||
(self.ctx.idents.len() - 1, self.ctx.idents.last_mut().unwrap(), true)
|
(self.ctx.idents.len() - 1, self.ctx.idents.last_mut().unwrap(), true)
|
||||||
|
@ -276,7 +278,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
id.flags |= idfl::COMPTIME * is_ct as u32;
|
id.flags |= idfl::COMPTIME * is_ct as u32;
|
||||||
if id.declared && id.ordered && self.ns_bound > i {
|
if id.declared && id.ordered && self.ns_bound > i {
|
||||||
id.flags |= idfl::COMPTIME;
|
id.flags |= idfl::COMPTIME;
|
||||||
self.ctx.captured.push(id.ident);
|
self.ctx.captured.push(CapturedIdent { id: id.ident, is_ct: id.is_ct });
|
||||||
}
|
}
|
||||||
|
|
||||||
(id.ident, bl)
|
(id.ident, bl)
|
||||||
|
@ -472,7 +474,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
self.collect_list(T::Comma, T::RParen, |s| {
|
self.collect_list(T::Comma, T::RParen, |s| {
|
||||||
let name = s.advance_ident()?;
|
let name = s.advance_ident()?;
|
||||||
let (id, _) = s.resolve_ident(name);
|
let (id, _) = s.resolve_ident(name);
|
||||||
s.declare(name.start, id, true, true);
|
s.declare(name.start, id, true, true, name.kind == T::CtIdent);
|
||||||
s.expect_advance(T::Colon)?;
|
s.expect_advance(T::Colon)?;
|
||||||
Some(Arg {
|
Some(Arg {
|
||||||
pos: name.start,
|
pos: name.start,
|
||||||
|
@ -666,7 +668,11 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_captures(&mut self, prev_captured: usize, prev_boundary: usize) -> &'a [Ident] {
|
fn collect_captures(
|
||||||
|
&mut self,
|
||||||
|
prev_captured: usize,
|
||||||
|
prev_boundary: usize,
|
||||||
|
) -> &'a [CapturedIdent] {
|
||||||
self.ns_bound = prev_boundary;
|
self.ns_bound = prev_boundary;
|
||||||
let captured = &mut self.ctx.captured[prev_captured..];
|
let captured = &mut self.ctx.captured[prev_captured..];
|
||||||
crate::quad_sort(captured, core::cmp::Ord::cmp);
|
crate::quad_sort(captured, core::cmp::Ord::cmp);
|
||||||
|
@ -1029,7 +1035,7 @@ generate_expr! {
|
||||||
Struct {
|
Struct {
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
fields: FieldList<'a, StructField<'a>>,
|
fields: FieldList<'a, StructField<'a>>,
|
||||||
captured: &'a [Ident],
|
captured: &'a [CapturedIdent],
|
||||||
trailing_comma: bool,
|
trailing_comma: bool,
|
||||||
packed: bool,
|
packed: bool,
|
||||||
},
|
},
|
||||||
|
@ -1037,14 +1043,14 @@ generate_expr! {
|
||||||
Union {
|
Union {
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
fields: FieldList<'a, UnionField<'a>>,
|
fields: FieldList<'a, UnionField<'a>>,
|
||||||
captured: &'a [Ident],
|
captured: &'a [CapturedIdent],
|
||||||
trailing_comma: bool,
|
trailing_comma: bool,
|
||||||
},
|
},
|
||||||
/// `'enum' LIST('{', ',', '}', Ident)`
|
/// `'enum' LIST('{', ',', '}', Ident)`
|
||||||
Enum {
|
Enum {
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
variants: FieldList<'a, EnumField<'a>>,
|
variants: FieldList<'a, EnumField<'a>>,
|
||||||
captured: &'a [Ident],
|
captured: &'a [CapturedIdent],
|
||||||
trailing_comma: bool,
|
trailing_comma: bool,
|
||||||
},
|
},
|
||||||
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`
|
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`
|
||||||
|
@ -1118,6 +1124,12 @@ generate_expr! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
|
||||||
|
pub struct CapturedIdent {
|
||||||
|
pub id: Ident,
|
||||||
|
pub is_ct: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum ListKind {
|
pub enum ListKind {
|
||||||
Tuple,
|
Tuple,
|
||||||
|
@ -1320,7 +1332,7 @@ pub struct Ctx {
|
||||||
symbols: Vec<Symbol>,
|
symbols: Vec<Symbol>,
|
||||||
stack: StackAlloc,
|
stack: StackAlloc,
|
||||||
idents: Vec<ScopeIdent>,
|
idents: Vec<ScopeIdent>,
|
||||||
captured: Vec<Ident>,
|
captured: Vec<CapturedIdent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ctx {
|
impl Ctx {
|
||||||
|
|
|
@ -11,7 +11,8 @@ use {
|
||||||
parser::{
|
parser::{
|
||||||
self,
|
self,
|
||||||
idfl::{self},
|
idfl::{self},
|
||||||
CommentOr, CtorField, DeclId, Expr, ExprRef, FieldList, ListKind, MatchBranch, Pos,
|
CapturedIdent, CommentOr, CtorField, DeclId, Expr, ExprRef, FieldList, ListKind,
|
||||||
|
MatchBranch, Pos,
|
||||||
},
|
},
|
||||||
ty::{
|
ty::{
|
||||||
self, Arg, ArrayLen, CompState, ConstData, EnumData, EnumField, FTask, FuncData,
|
self, Arg, ArrayLen, CompState, ConstData, EnumData, EnumField, FTask, FuncData,
|
||||||
|
@ -30,6 +31,7 @@ use {
|
||||||
format_args as fa, mem,
|
format_args as fa, mem,
|
||||||
},
|
},
|
||||||
hbbytecode::DisasmError,
|
hbbytecode::DisasmError,
|
||||||
|
std::panic,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_ACLASS: usize = 0;
|
const DEFAULT_ACLASS: usize = 0;
|
||||||
|
@ -841,6 +843,31 @@ impl<'a> Codegen<'a> {
|
||||||
|
|
||||||
Some(Value::var(index).ty(var.ty))
|
Some(Value::var(index).ty(var.ty))
|
||||||
}
|
}
|
||||||
|
Expr::Ident { id, .. }
|
||||||
|
if let Some(vl) = {
|
||||||
|
let mut piter = self.ci.parent;
|
||||||
|
let f = self.file();
|
||||||
|
loop {
|
||||||
|
if let Some((captures, capture_tuple)) = self.tys.captures_of(piter, f)
|
||||||
|
&& let Some(idx) = captures.iter().position(|&cid| cid.id == id)
|
||||||
|
{
|
||||||
|
let ty = if captures[idx].is_ct {
|
||||||
|
ty::Id::TYPE
|
||||||
|
} else {
|
||||||
|
self.tys.ins.args[capture_tuple.range().start + idx]
|
||||||
|
};
|
||||||
|
break Some(Value::new(NEVER).ty(ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
piter = match self.tys.parent_of(piter) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => break None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} =>
|
||||||
|
{
|
||||||
|
Some(vl)
|
||||||
|
}
|
||||||
Expr::Ident { id, pos, .. } => self.find_type_as_value(pos, self.ci.parent, id, ctx),
|
Expr::Ident { id, pos, .. } => self.find_type_as_value(pos, self.ci.parent, id, ctx),
|
||||||
Expr::Comment { .. } => Some(Value::VOID),
|
Expr::Comment { .. } => Some(Value::VOID),
|
||||||
Expr::Char { pos, literal } | Expr::String { pos, literal } => {
|
Expr::Char { pos, literal } | Expr::String { pos, literal } => {
|
||||||
|
@ -3053,7 +3080,7 @@ impl<'a> Codegen<'a> {
|
||||||
|
|
||||||
self.tys.tmp.args.push(ty);
|
self.tys.tmp.args.push(ty);
|
||||||
let sym = parser::find_symbol(&fast.symbols, carg.id);
|
let sym = parser::find_symbol(&fast.symbols, carg.id);
|
||||||
let ty = if ty == ty::Id::ANY_TYPE {
|
if ty == ty::Id::ANY_TYPE {
|
||||||
let ty = self.infer_type(arg);
|
let ty = self.infer_type(arg);
|
||||||
*self.tys.tmp.args.last_mut().unwrap() = ty;
|
*self.tys.tmp.args.last_mut().unwrap() = ty;
|
||||||
self.ci.scope.vars.push(Variable::new(
|
self.ci.scope.vars.push(Variable::new(
|
||||||
|
@ -3063,10 +3090,14 @@ impl<'a> Codegen<'a> {
|
||||||
NEVER,
|
NEVER,
|
||||||
&mut self.ci.nodes,
|
&mut self.ci.nodes,
|
||||||
));
|
));
|
||||||
continue;
|
|
||||||
} else if sym.flags & idfl::COMPTIME == 0 {
|
} else if sym.flags & idfl::COMPTIME == 0 {
|
||||||
// FIXME: could fuck us
|
self.ci.scope.vars.push(Variable::new(
|
||||||
continue;
|
carg.id,
|
||||||
|
ty,
|
||||||
|
false,
|
||||||
|
NEVER,
|
||||||
|
&mut self.ci.nodes,
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
if ty != ty::Id::TYPE {
|
if ty != ty::Id::TYPE {
|
||||||
self.error(
|
self.error(
|
||||||
|
@ -3082,16 +3113,14 @@ impl<'a> Codegen<'a> {
|
||||||
}
|
}
|
||||||
let ty = self.ty(arg);
|
let ty = self.ty(arg);
|
||||||
self.tys.tmp.args.push(ty);
|
self.tys.tmp.args.push(ty);
|
||||||
ty
|
self.ci.scope.vars.push(Variable::new(
|
||||||
};
|
carg.id,
|
||||||
|
ty::Id::TYPE,
|
||||||
self.ci.scope.vars.push(Variable::new(
|
false,
|
||||||
carg.id,
|
self.ci.nodes.new_const(ty::Id::TYPE, ty),
|
||||||
ty::Id::TYPE,
|
&mut self.ci.nodes,
|
||||||
false,
|
));
|
||||||
self.ci.nodes.new_const(ty::Id::TYPE, ty),
|
}
|
||||||
&mut self.ci.nodes,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(args) = self.tys.pack_args(arg_base) else {
|
let Some(args) = self.tys.pack_args(arg_base) else {
|
||||||
|
@ -3806,13 +3835,13 @@ impl<'a> Codegen<'a> {
|
||||||
ty::Id::NEVER
|
ty::Id::NEVER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_local_ty(&mut self, ident: Ident) -> Option<ty::Id> {
|
fn find_local_ty(&mut self, ident: CapturedIdent) -> Option<ty::Id> {
|
||||||
self.ci
|
self.ci
|
||||||
.scope
|
.scope
|
||||||
.vars
|
.vars
|
||||||
.iter()
|
.iter()
|
||||||
.rfind(|v| (v.id == ident && v.ty == ty::Id::TYPE))
|
.rfind(|v| v.id == ident.id && (!ident.is_ct || v.ty == ty::Id::TYPE))
|
||||||
.map(|v| self.ci.nodes.as_ty(v.value()))
|
.map(|v| if ident.is_ct { self.ci.nodes.as_ty(v.value()) } else { v.ty })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_type_as_value(
|
fn find_type_as_value(
|
||||||
|
@ -3847,7 +3876,7 @@ impl<'a> Codegen<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let ty = if let DeclId::Ident(id) = id
|
let ty = if let DeclId::Ident(id) = id
|
||||||
&& let Some(ty) = self.find_local_ty(id)
|
&& let Some(ty) = self.find_local_ty(CapturedIdent { id, is_ct: true })
|
||||||
{
|
{
|
||||||
ty
|
ty
|
||||||
} else if let DeclId::Ident(id) = id
|
} else if let DeclId::Ident(id) = id
|
||||||
|
@ -3867,7 +3896,8 @@ impl<'a> Codegen<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((captures, capture_tuple)) = self.tys.captures_of(piter, f)
|
if let Some((captures, capture_tuple)) = self.tys.captures_of(piter, f)
|
||||||
&& let Some(idx) = captures.iter().position(|&cid| DeclId::Ident(cid) == id)
|
&& let Some(idx) =
|
||||||
|
captures.iter().position(|&cid| cid.is_ct && DeclId::Ident(cid.id) == id)
|
||||||
{
|
{
|
||||||
return self.tys.ins.args[capture_tuple.range().start + idx];
|
return self.tys.ins.args[capture_tuple.range().start + idx];
|
||||||
}
|
}
|
||||||
|
@ -3875,9 +3905,6 @@ impl<'a> Codegen<'a> {
|
||||||
piter = match self.tys.parent_of(piter) {
|
piter = match self.tys.parent_of(piter) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => {
|
None => {
|
||||||
if let ty::Kind::Struct(_) = piter.expand() {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
break None;
|
break None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -4135,7 +4162,13 @@ impl<'a> Codegen<'a> {
|
||||||
self.eval_global(sc.file, sc.parent, name, expr)
|
self.eval_global(sc.file, sc.parent, name, expr)
|
||||||
}
|
}
|
||||||
_ if sc.alloc_const => {
|
_ if sc.alloc_const => {
|
||||||
ty::Id::from(self.eval_const(sc.file, sc.parent, expr, ty::Id::TYPE))
|
let prev_file = mem::replace(&mut self.ci.file, sc.file);
|
||||||
|
let prev_parent = mem::replace(&mut self.ci.parent, sc.parent);
|
||||||
|
let id = self.expr(expr).unwrap().id;
|
||||||
|
self.ci.file = prev_file;
|
||||||
|
self.ci.parent = prev_parent;
|
||||||
|
|
||||||
|
self.ci.nodes.as_ty(id)
|
||||||
}
|
}
|
||||||
ref e => {
|
ref e => {
|
||||||
self.error_unhandled_ast(e, "bruh");
|
self.error_unhandled_ast(e, "bruh");
|
||||||
|
@ -4149,7 +4182,7 @@ impl<'a> Codegen<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
captured: &[Ident],
|
captured: &[CapturedIdent],
|
||||||
fields: FieldList<A>,
|
fields: FieldList<A>,
|
||||||
sc: TyScope,
|
sc: TyScope,
|
||||||
get_fields: impl Fn(&mut Types) -> [&mut Vec<F>; 2],
|
get_fields: impl Fn(&mut Types) -> [&mut Vec<F>; 2],
|
||||||
|
@ -4314,6 +4347,7 @@ mod tests {
|
||||||
fb_driver;
|
fb_driver;
|
||||||
|
|
||||||
// Purely Testing Examples;
|
// Purely Testing Examples;
|
||||||
|
proper_ident_propagation;
|
||||||
method_receiver_by_value;
|
method_receiver_by_value;
|
||||||
comparing_floating_points;
|
comparing_floating_points;
|
||||||
pointer_comparison;
|
pointer_comparison;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
ctx_map,
|
ctx_map,
|
||||||
lexer::TokenKind,
|
lexer::TokenKind,
|
||||||
parser::{self, CommentOr, Expr, ExprRef, Pos},
|
parser::{self, CapturedIdent, CommentOr, Expr, ExprRef, Pos},
|
||||||
utils::{self, Ent, EntSlice, EntVec},
|
utils::{self, Ent, EntSlice, EntVec},
|
||||||
Ident,
|
Ident,
|
||||||
},
|
},
|
||||||
|
@ -1243,7 +1243,11 @@ 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], List)> {
|
pub fn captures_of<'a>(
|
||||||
|
&self,
|
||||||
|
ty: Id,
|
||||||
|
file: &'a parser::Ast,
|
||||||
|
) -> Option<(&'a [CapturedIdent], List)> {
|
||||||
let base = self.type_base_of(ty)?;
|
let base = self.type_base_of(ty)?;
|
||||||
|
|
||||||
let (Expr::Struct { captured, .. }
|
let (Expr::Struct { captured, .. }
|
||||||
|
|
6
lang/tests/son_tests_proper_ident_propagation.txt
Normal file
6
lang/tests/son_tests_proper_ident_propagation.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