deferring all null checks after the peepholes
This commit is contained in:
parent
798000c756
commit
44fc9c3e2e
47
f.txt
47
f.txt
|
@ -1,47 +0,0 @@
|
|||
start: 0
|
||||
46-c65535: +: [0, 38, 45] [47]
|
||||
45-c65535: &: [0, 43, 44] [46]
|
||||
44-c65535: cint: #255 [0] [45]
|
||||
43-c65535: load: [0, 42, 23] [45]
|
||||
42-c65535: +: [0, 22, 18] [43]
|
||||
39-c65535: stre: [0, 0, 36, 37] [47]
|
||||
39-c65535: stre: [0, 0, 36, 37] [47]
|
||||
37-c65535: stre: [0, 35, 36, 3] [38, 39]
|
||||
36-c65535: stck: [0, 3] [37, 38, 39]
|
||||
35-c65535: load: [0, 24, 34] [37]
|
||||
34-c65535: stre: [0, 31, 33, 30] [35, 47]
|
||||
33-c65535: +: [0, 24, 32] [34]
|
||||
32-c65535: cint: #16 [0] [33]
|
||||
31-c65535: cint: #4 [0] [34]
|
||||
30-c65535: stre: [0, 27, 29, 26] [34]
|
||||
29-c65535: +: [0, 24, 28] [30]
|
||||
28-c65535: cint: #8 [0] [29]
|
||||
27-c65535: cint: #2 [0] [30]
|
||||
26-c65535: stre: [0, 25, 24, 3] [30]
|
||||
25-c65535: cint: #1 [0] [26]
|
||||
24-c65535: stck: [0, 3] [26, 29, 33, 35]
|
||||
23-c65535: stre: [0, 21, 22, 3] [43, 47]
|
||||
22-c65535: stck: [0, 3] [23, 42]
|
||||
21-c65535: load: [0, 6, 20] [23]
|
||||
20-c65535: stre: [0, 17, 19, 14] [21, 47]
|
||||
19-c65535: +: [0, 6, 18] [20]
|
||||
18-c65535: cint: #3 [0] [19, 42]
|
||||
17-c65535: cint: #1 [0] [20]
|
||||
14-c65535: stre: [0, 5, 13, 11] [20]
|
||||
13-c65535: +: [0, 6, 12] [14]
|
||||
12-c65535: cint: #2 [0] [13]
|
||||
11-c65535: stre: [0, 7, 10, 8] [14]
|
||||
10-c65535: +: [0, 6, 9] [11]
|
||||
9-c65535: cint: #1 [0] [10]
|
||||
8-c65535: stre: [0, 7, 6, 3] [11]
|
||||
7-c65535: cint: #0 [0] [8, 11]
|
||||
6-c65535: stck: [0, 3] [8, 10, 13, 19, 21]
|
||||
5-c65535: cint: #511 [0] [14]
|
||||
4-c65535: loops: [0] []
|
||||
3-c65535: mem: [0] [6, 8, 22, 23, 24, 26, 36, 37, 47]
|
||||
2-c65535: ctrl: entry [0] [38]
|
||||
b2: 0 0 [38]
|
||||
38-c65535: call: 1 0 [2, 36, 37] [46, 47]
|
||||
47-c65535: ret: [38, 46, 3, 20, 23, 34, 39] [1]
|
||||
|
||||
|
38
f1.txt
38
f1.txt
|
@ -1,38 +0,0 @@
|
|||
|
||||
start: 0
|
||||
34-c65535: stre: [0, 31, 51, 30] [39, 47, 38]
|
||||
51-c65535: +: [0, 36, 32] [34]
|
||||
32-c65535: cint: #16 [0] [51]
|
||||
31-c65535: cint: #4 [0] [34]
|
||||
30-c65535: stre: [0, 27, 50, 26] [34]
|
||||
50-c65535: +: [0, 36, 28] [30]
|
||||
28-c65535: cint: #8 [0] [50]
|
||||
27-c65535: cint: #2 [0] [30]
|
||||
26-c65535: stre: [0, 25, 36, 3] [30]
|
||||
25-c65535: cint: #1 [0] [26, 46]
|
||||
39-c65535: stre: [0, 0, 36, 34] [47]
|
||||
36-c65535: stck: [0, 3] [51, 38, 39, 26, 50]
|
||||
22-c65535: stck: [0, 3] [49, 42, 8, 48]
|
||||
39-c65535: stre: [0, 0, 36, 34] [47]
|
||||
20-c65535: stre: [0, 17, 42, 14] [47, 47]
|
||||
46-c65535: +: [0, 38, 25] [47]
|
||||
18-c65535: cint: #3 [0] [42]
|
||||
17-c65535: cint: #1 [0] [20]
|
||||
14-c65535: stre: [0, 5, 49, 11] [20]
|
||||
49-c65535: +: [0, 22, 12] [14]
|
||||
12-c65535: cint: #2 [0] [49]
|
||||
11-c65535: stre: [0, 7, 48, 8] [14]
|
||||
48-c65535: +: [0, 22, 9] [11]
|
||||
9-c65535: cint: #1 [0] [48]
|
||||
8-c65535: stre: [0, 7, 22, 3] [11]
|
||||
7-c65535: cint: #0 [0] [8, 11]
|
||||
42-c65535: +: [0, 22, 18] [20]
|
||||
5-c65535: cint: #511 [0] [14]
|
||||
4-c65535: loops: [0] []
|
||||
3-c65535: mem: [0] [47, 8, 22, 26, 36]
|
||||
2-c65535: ctrl: entry [0] [38]
|
||||
b2: 0 0 [38]
|
||||
38-c65535: call: 1 0 [2, 36, 34] [46, 47]
|
||||
47-c65535: ret: [38, 46, 3, 20, 20, 34, 39] [1]
|
||||
|
||||
|
39
f2.txt
39
f2.txt
|
@ -1,39 +0,0 @@
|
|||
start: 0
|
||||
42-c65535: +: [0, 22, 18] [43, 20]
|
||||
36-c65535: stck: [0, 3] [51, 38, 39, 26, 50]
|
||||
39-c65535: stre: [0, 0, 36, 34] [47]
|
||||
34-c65535: stre: [0, 31, 51, 30] [39, 47, 38]
|
||||
51-c65535: +: [0, 36, 32] [34]
|
||||
32-c65535: cint: #16 [0] [51]
|
||||
31-c65535: cint: #4 [0] [34]
|
||||
30-c65535: stre: [0, 27, 50, 26] [34]
|
||||
50-c65535: +: [0, 36, 28] [30]
|
||||
28-c65535: cint: #8 [0] [50]
|
||||
27-c65535: cint: #2 [0] [30]
|
||||
26-c65535: stre: [0, 25, 36, 3] [30]
|
||||
25-c65535: cint: #1 [0] [26]
|
||||
39-c65535: stre: [0, 0, 36, 34] [47]
|
||||
45-c65535: &: [0, 43, 44] [46]
|
||||
22-c65535: stck: [0, 3] [49, 42, 8, 48]
|
||||
44-c65535: cint: #255 [0] [45]
|
||||
20-c65535: stre: [0, 17, 42, 14] [47, 47, 43]
|
||||
46-c65535: +: [0, 38, 45] [47]
|
||||
18-c65535: cint: #3 [0] [42]
|
||||
17-c65535: cint: #1 [0] [20]
|
||||
14-c65535: stre: [0, 5, 49, 11] [20]
|
||||
49-c65535: +: [0, 22, 12] [14]
|
||||
12-c65535: cint: #2 [0] [49]
|
||||
11-c65535: stre: [0, 7, 48, 8] [14]
|
||||
48-c65535: +: [0, 22, 9] [11]
|
||||
9-c65535: cint: #1 [0] [48]
|
||||
8-c65535: stre: [0, 7, 22, 3] [11]
|
||||
7-c65535: cint: #0 [0] [8, 11]
|
||||
43-c65535: load: [0, 42, 20] [45]
|
||||
5-c65535: cint: #511 [0] [14]
|
||||
4-c65535: loops: [0] []
|
||||
3-c65535: mem: [0] [47, 8, 22, 26, 36]
|
||||
2-c65535: ctrl: entry [0] [38]
|
||||
b2: 0 0 [38]
|
||||
38-c65535: call: 1 0 [2, 36, 34] [46, 47]
|
||||
47-c65535: ret: [38, 46, 3, 20, 20, 34, 39] [1]
|
||||
|
|
@ -1044,6 +1044,28 @@ main := fn(): uint {
|
|||
|
||||
### Just Testing Optimizations
|
||||
|
||||
#### null_check_test
|
||||
```hb
|
||||
get_ptr := fn(): ?^uint {
|
||||
value := 0
|
||||
return &value
|
||||
}
|
||||
|
||||
main := fn(): uint {
|
||||
ptr := get_ptr()
|
||||
|
||||
if ptr == null {
|
||||
return 0
|
||||
}
|
||||
|
||||
loop if *ptr != 10 {
|
||||
*ptr += 1
|
||||
} else break
|
||||
|
||||
return *ptr
|
||||
}
|
||||
```
|
||||
|
||||
#### const_folding_with_arg
|
||||
```hb
|
||||
main := fn(arg: uint): uint {
|
||||
|
|
125
lang/src/son.rs
125
lang/src/son.rs
|
@ -879,6 +879,46 @@ impl Nodes {
|
|||
return Some(self.new_const(ty, op.apply_unop(value, is_float)));
|
||||
}
|
||||
}
|
||||
K::Assert { kind, pos } => {
|
||||
if self[target].ty == ty::Id::VOID {
|
||||
let &[ctrl, cond] = self[target].inputs.as_slice() else { unreachable!() };
|
||||
if let K::CInt { value } = self[cond].kind {
|
||||
let ty = if value != 0 {
|
||||
ty::Id::NEVER
|
||||
} else {
|
||||
return Some(ctrl);
|
||||
};
|
||||
return Some(self.new_node_nop(ty, K::Assert { kind, pos }, [ctrl, cond]));
|
||||
}
|
||||
|
||||
'b: {
|
||||
let mut cursor = ctrl;
|
||||
loop {
|
||||
if cursor == ENTRY {
|
||||
break 'b;
|
||||
}
|
||||
|
||||
// TODO: do more inteligent checks on the condition
|
||||
if self[cursor].kind == Kind::Else
|
||||
&& self[self[cursor].inputs[0]].inputs[1] == cond
|
||||
{
|
||||
return Some(ctrl);
|
||||
}
|
||||
if self[cursor].kind == Kind::Then
|
||||
&& self[self[cursor].inputs[0]].inputs[1] == cond
|
||||
{
|
||||
return Some(self.new_node_nop(
|
||||
ty::Id::NEVER,
|
||||
K::Assert { kind, pos },
|
||||
[ctrl, cond],
|
||||
));
|
||||
}
|
||||
|
||||
cursor = self.idom(cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
K::If => {
|
||||
if self[target].ty == ty::Id::VOID {
|
||||
let &[ctrl, cond] = self[target].inputs.as_slice() else { unreachable!() };
|
||||
|
@ -1295,7 +1335,7 @@ impl Nodes {
|
|||
write!(out, " {node:>2}-c{:>2}: ", self[node].ralloc_backref)?;
|
||||
}
|
||||
match self[node].kind {
|
||||
Kind::Start => unreachable!(),
|
||||
Kind::Assert { .. } | Kind::Start => unreachable!(),
|
||||
Kind::End => return Ok(()),
|
||||
Kind::If => write!(out, " if: "),
|
||||
Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"),
|
||||
|
@ -1561,6 +1601,11 @@ impl ops::IndexMut<Nid> for Nodes {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum AssertKind {
|
||||
NullCheck,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
|
||||
#[repr(u8)]
|
||||
pub enum Kind {
|
||||
|
@ -1586,6 +1631,11 @@ pub enum Kind {
|
|||
Return,
|
||||
// [ctrl]
|
||||
Die,
|
||||
// [ctrl, cond]
|
||||
Assert {
|
||||
kind: AssertKind,
|
||||
pos: Pos,
|
||||
},
|
||||
// [ctrl]
|
||||
CInt {
|
||||
value: i64,
|
||||
|
@ -1634,6 +1684,7 @@ impl Kind {
|
|||
| Self::Then
|
||||
| Self::Else
|
||||
| Self::Call { .. }
|
||||
| Self::Assert { .. }
|
||||
| Self::If
|
||||
| Self::Region
|
||||
| Self::Loop
|
||||
|
@ -3981,9 +4032,7 @@ impl<'a> Codegen<'a> {
|
|||
|
||||
self.ci.scope.vars.drain(..).for_each(|v| v.remove_ignore_arg(&mut self.ci.nodes));
|
||||
|
||||
self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files);
|
||||
|
||||
if self.errors.borrow().len() == prev_err_len {
|
||||
if self.finalize(prev_err_len) {
|
||||
self.ci.emit_body(self.tys, self.files, sig, self.pool);
|
||||
self.tys.ins.funcs[id as usize].code.append(&mut self.ci.code);
|
||||
self.tys.ins.funcs[id as usize].relocs.append(&mut self.ci.relocs);
|
||||
|
@ -3992,6 +4041,32 @@ impl<'a> Codegen<'a> {
|
|||
self.pool.pop_ci(&mut self.ci);
|
||||
}
|
||||
|
||||
fn finalize(&mut self, prev_err_len: usize) -> bool {
|
||||
self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files);
|
||||
for (_, node) in self.ci.nodes.iter() {
|
||||
if let Kind::Assert { kind: AssertKind::NullCheck, pos } = node.kind {
|
||||
match node.ty {
|
||||
ty::Id::NEVER => {
|
||||
self.report(
|
||||
pos,
|
||||
"the value is always null, some checks might need to be inverted",
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
self.report(
|
||||
pos,
|
||||
"can't prove the value is not 'null', \
|
||||
use '@unwrap(<opt>)' if you believe compiler is stupid, \
|
||||
or explicitly check for null and handle it \
|
||||
('if <opt> == null { /* handle */ } else { /* use opt */ }')",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.errors.borrow().len() == prev_err_len
|
||||
}
|
||||
|
||||
fn ty(&mut self, expr: &Expr) -> ty::Id {
|
||||
self.parse_ty(self.ci.file, expr, None, self.files)
|
||||
}
|
||||
|
@ -4096,27 +4171,16 @@ impl<'a> Codegen<'a> {
|
|||
let null_check = self.gen_null_check(*opt, ty, TokenKind::Eq);
|
||||
|
||||
// TODO: extract the if check int a fucntion
|
||||
let ctrl = self.ci.nodes.new_node(ty::Id::VOID, Kind::If, [self.ci.ctrl.get(), null_check]);
|
||||
let ctrl_ty = self.ci.nodes[ctrl].ty;
|
||||
self.ci.nodes.remove(ctrl);
|
||||
let oty = mem::replace(&mut opt.ty, ty);
|
||||
match ctrl_ty {
|
||||
ty::Id::LEFT_UNREACHABLE => {
|
||||
self.unwrap_opt_unchecked(ty, oty, opt);
|
||||
}
|
||||
ty::Id::RIGHT_UNREACHABLE => {
|
||||
self.report(pos, "the value is always null, some checks might need to be inverted");
|
||||
}
|
||||
_ => {
|
||||
self.report(
|
||||
pos,
|
||||
"can't prove the value is not 'null', \
|
||||
use '@unwrap(<opt>)' if you believe compiler is stupid, \
|
||||
or explicitly check for null and handle it \
|
||||
('if <opt> == null { /* handle */ } else { /* use opt */ }')",
|
||||
self.ci.ctrl.set(
|
||||
self.ci.nodes.new_node(
|
||||
ty::Id::VOID,
|
||||
Kind::Assert { kind: AssertKind::NullCheck, pos },
|
||||
[self.ci.ctrl.get(), null_check],
|
||||
),
|
||||
&mut self.ci.nodes,
|
||||
);
|
||||
}
|
||||
}
|
||||
let oty = mem::replace(&mut opt.ty, ty);
|
||||
self.unwrap_opt_unchecked(ty, oty, opt);
|
||||
}
|
||||
|
||||
fn unwrap_opt_unchecked(&mut self, ty: ty::Id, oty: ty::Id, opt: &mut Value) {
|
||||
|
@ -4247,13 +4311,9 @@ impl TypeParser for Codegen<'_> {
|
|||
self.expr(&Expr::Return { pos: expr.pos(), val: Some(expr) });
|
||||
|
||||
scope = mem::take(&mut self.ci.scope.vars);
|
||||
self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files);
|
||||
|
||||
let res = if self.errors.borrow().len() == prev_err_len {
|
||||
self.emit_and_eval(file, ret, &mut [])
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let res =
|
||||
if self.finalize(prev_err_len) { self.emit_and_eval(file, ret, &mut []) } else { 1 };
|
||||
|
||||
self.pool.pop_ci(&mut self.ci);
|
||||
self.ci.scope.vars = scope;
|
||||
|
@ -4291,10 +4351,8 @@ impl TypeParser for Codegen<'_> {
|
|||
|
||||
self.expr(&(Expr::Return { pos: expr.pos(), val: Some(expr) }));
|
||||
|
||||
self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files);
|
||||
|
||||
let ret = self.ci.ret.expect("for return type to be infered");
|
||||
if self.errors.borrow().len() == prev_err_len {
|
||||
if self.finalize(prev_err_len) {
|
||||
let mut mem = vec![0u8; self.tys.size_of(ret) as usize];
|
||||
self.emit_and_eval(file, ret, &mut mem);
|
||||
self.tys.ins.globals[gid as usize].data = mem;
|
||||
|
@ -4388,6 +4446,7 @@ mod tests {
|
|||
fb_driver;
|
||||
|
||||
// Purely Testing Examples;
|
||||
null_check_test;
|
||||
only_break_loop;
|
||||
reading_idk;
|
||||
nonexistent_ident_import;
|
||||
|
|
|
@ -493,6 +493,7 @@ impl ItemCtx {
|
|||
}
|
||||
}
|
||||
Kind::Start
|
||||
| Kind::Assert { .. }
|
||||
| Kind::Entry
|
||||
| Kind::Mem
|
||||
| Kind::End
|
||||
|
@ -1021,6 +1022,7 @@ impl<'a> Function<'a> {
|
|||
let ops = vec![self.drg(nid)];
|
||||
self.add_instr(nid, ops);
|
||||
}
|
||||
Kind::Assert { .. } => unreachable!(),
|
||||
Kind::End |
|
||||
Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops => {}
|
||||
Kind::Load { .. } if node.ty.loc(self.tys) == Loc::Stack => {
|
||||
|
|
0
lang/tests/son_tests_inlining_loops.txt
Normal file
0
lang/tests/son_tests_inlining_loops.txt
Normal file
26
lang/tests/son_tests_null_check_test.txt
Normal file
26
lang/tests/son_tests_null_check_test.txt
Normal file
|
@ -0,0 +1,26 @@
|
|||
get_ptr:
|
||||
ADDI64 r254, r254, -8d
|
||||
ADDI64 r1, r254, 0d
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :get_ptr
|
||||
LI64 r3, 0d
|
||||
JNE r1, r3, :0
|
||||
LI64 r1, 0d
|
||||
JMP :1
|
||||
0: LI64 r10, 10d
|
||||
CP r2, r1
|
||||
2: LD r1, r2, 0a, 8h
|
||||
JEQ r1, r10, :1
|
||||
ADDI64 r3, r1, 1d
|
||||
ST r3, r2, 0a, 8h
|
||||
JMP :2
|
||||
1: LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 208
|
||||
ret: 10
|
||||
status: Ok(())
|
Loading…
Reference in a new issue