nasty bug with rescheduled load
This commit is contained in:
parent
2e36f32ae0
commit
3338d50672
|
@ -593,6 +593,17 @@ main := fn(): uint {
|
||||||
|
|
||||||
### Purely Testing Examples
|
### Purely Testing Examples
|
||||||
|
|
||||||
|
#### needless_unwrap
|
||||||
|
```hb
|
||||||
|
main := fn(): uint {
|
||||||
|
always_nn := @as(?^uint, &0)
|
||||||
|
ptr := @unwrap(always_nn)
|
||||||
|
always_n := @as(?^uint, null)
|
||||||
|
ptr = @unwrap(always_n)
|
||||||
|
return *ptr
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### inlining_issues
|
#### inlining_issues
|
||||||
```hb
|
```hb
|
||||||
main := fn(): void {
|
main := fn(): void {
|
||||||
|
@ -1349,3 +1360,23 @@ opaque := fn(): Foo {
|
||||||
return .(3, 2)
|
return .(3, 2)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### more_if_opts
|
||||||
|
```hb
|
||||||
|
main := fn(): uint {
|
||||||
|
opq1 := opaque()
|
||||||
|
opq2 := opaque()
|
||||||
|
a := 0
|
||||||
|
|
||||||
|
if opq1 == null {
|
||||||
|
} else a = *opq1
|
||||||
|
if opq1 != null a = *opq1
|
||||||
|
//if opq1 == null | opq2 == null {
|
||||||
|
//} else a = *opq1
|
||||||
|
//if opq1 != null & opq2 != null a = *opq1
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
opaque := fn(): ?^uint return null
|
||||||
|
```
|
||||||
|
|
313
lang/src/son.rs
313
lang/src/son.rs
|
@ -18,7 +18,7 @@ use {
|
||||||
alloc::{string::String, vec::Vec},
|
alloc::{string::String, vec::Vec},
|
||||||
core::{
|
core::{
|
||||||
assert_matches::debug_assert_matches,
|
assert_matches::debug_assert_matches,
|
||||||
cell::RefCell,
|
cell::{Cell, RefCell},
|
||||||
fmt::{self, Debug, Display, Write},
|
fmt::{self, Debug, Display, Write},
|
||||||
format_args as fa, mem,
|
format_args as fa, mem,
|
||||||
ops::{self, Deref},
|
ops::{self, Deref},
|
||||||
|
@ -145,20 +145,21 @@ impl Nodes {
|
||||||
self[target].loop_depth
|
self[target].loop_depth
|
||||||
}
|
}
|
||||||
|
|
||||||
fn idepth(&mut self, target: Nid) -> IDomDepth {
|
fn idepth(&self, target: Nid) -> IDomDepth {
|
||||||
if target == VOID {
|
if target == VOID {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if self[target].depth == 0 {
|
if self[target].depth.get() == 0 {
|
||||||
self[target].depth = match self[target].kind {
|
let depth = match self[target].kind {
|
||||||
Kind::End | Kind::Start => unreachable!("{:?}", self[target].kind),
|
Kind::End | Kind::Start => unreachable!("{:?}", self[target].kind),
|
||||||
Kind::Region => {
|
Kind::Region => {
|
||||||
self.idepth(self[target].inputs[0]).max(self.idepth(self[target].inputs[1]))
|
self.idepth(self[target].inputs[0]).max(self.idepth(self[target].inputs[1]))
|
||||||
}
|
}
|
||||||
_ => self.idepth(self[target].inputs[0]),
|
_ => self.idepth(self[target].inputs[0]),
|
||||||
} + 1;
|
} + 1;
|
||||||
|
self[target].depth.set(depth);
|
||||||
}
|
}
|
||||||
self[target].depth
|
self[target].depth.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fix_loops(&mut self) {
|
fn fix_loops(&mut self) {
|
||||||
|
@ -214,8 +215,10 @@ impl Nodes {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = self[0].outputs.iter().position(|&p| p == node).unwrap();
|
let current = self[node].inputs[0];
|
||||||
self[0].outputs.remove(index);
|
|
||||||
|
let index = self[current].outputs.iter().position(|&p| p == node).unwrap();
|
||||||
|
self[current].outputs.remove(index);
|
||||||
self[node].inputs[0] = deepest;
|
self[node].inputs[0] = deepest;
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!self[deepest].outputs.contains(&node)
|
!self[deepest].outputs.contains(&node)
|
||||||
|
@ -424,7 +427,7 @@ impl Nodes {
|
||||||
self[self[from].inputs[0]].inputs[index - 1]
|
self[self[from].inputs[0]].inputs[index - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn idom(&mut self, target: Nid) -> Nid {
|
fn idom(&self, target: Nid) -> Nid {
|
||||||
match self[target].kind {
|
match self[target].kind {
|
||||||
Kind::Start => VOID,
|
Kind::Start => VOID,
|
||||||
Kind::End => unreachable!(),
|
Kind::End => unreachable!(),
|
||||||
|
@ -436,7 +439,7 @@ impl Nodes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common_dom(&mut self, mut a: Nid, mut b: Nid) -> Nid {
|
fn common_dom(&self, mut a: Nid, mut b: Nid) -> Nid {
|
||||||
while a != b {
|
while a != b {
|
||||||
let [ldepth, rdepth] = [self.idepth(a), self.idepth(b)];
|
let [ldepth, rdepth] = [self.idepth(a), self.idepth(b)];
|
||||||
if ldepth >= rdepth {
|
if ldepth >= rdepth {
|
||||||
|
@ -893,81 +896,18 @@ impl Nodes {
|
||||||
return Some(self.new_const(ty, op.apply_unop(value, is_float)));
|
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 => {
|
K::If => {
|
||||||
if self[target].ty == ty::Id::VOID {
|
if self[target].ty == ty::Id::VOID {
|
||||||
let &[ctrl, cond] = self[target].inputs.as_slice() else { unreachable!() };
|
match self.try_opt_cond(target) {
|
||||||
if let K::CInt { value } = self[cond].kind {
|
CondOptRes::Unknown => {}
|
||||||
let ty = if value == 0 {
|
CondOptRes::Known { value, .. } => {
|
||||||
ty::Id::LEFT_UNREACHABLE
|
let ty = if value {
|
||||||
} else {
|
|
||||||
ty::Id::RIGHT_UNREACHABLE
|
ty::Id::RIGHT_UNREACHABLE
|
||||||
|
} else {
|
||||||
|
ty::Id::LEFT_UNREACHABLE
|
||||||
};
|
};
|
||||||
return Some(self.new_node_nop(ty, K::If, [ctrl, cond]));
|
return Some(self.new_node_nop(ty, K::If, self[target].inputs.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
'b: {
|
|
||||||
let mut cursor = ctrl;
|
|
||||||
let ty = loop {
|
|
||||||
if cursor == ENTRY {
|
|
||||||
break 'b;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: do more inteligent checks on the condition
|
|
||||||
if self[cursor].kind == Kind::Then
|
|
||||||
&& self[self[cursor].inputs[0]].inputs[1] == cond
|
|
||||||
{
|
|
||||||
break ty::Id::RIGHT_UNREACHABLE;
|
|
||||||
}
|
|
||||||
if self[cursor].kind == Kind::Else
|
|
||||||
&& self[self[cursor].inputs[0]].inputs[1] == cond
|
|
||||||
{
|
|
||||||
break ty::Id::LEFT_UNREACHABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor = self.idom(cursor);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Some(self.new_node_nop(ty, K::If, [ctrl, cond]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1284,6 +1224,58 @@ impl Nodes {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_opt_cond(&self, target: Nid) -> CondOptRes {
|
||||||
|
let &[ctrl, cond, ..] = self[target].inputs.as_slice() else { unreachable!() };
|
||||||
|
if let Kind::CInt { value } = self[cond].kind {
|
||||||
|
return CondOptRes::Known { value: value != 0, pin: None };
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cursor = ctrl;
|
||||||
|
while cursor != ENTRY {
|
||||||
|
let ctrl = &self[cursor];
|
||||||
|
// TODO: do more inteligent checks on the condition
|
||||||
|
if matches!(ctrl.kind, Kind::Then | Kind::Else) {
|
||||||
|
let other_cond = self[ctrl.inputs[0]].inputs[1];
|
||||||
|
if let Some(value) = self.matches_cond(cond, other_cond) {
|
||||||
|
return CondOptRes::Known {
|
||||||
|
value: (ctrl.kind == Kind::Then) ^ !value,
|
||||||
|
pin: Some(cursor),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = self.idom(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
CondOptRes::Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches_cond(&self, to_match: Nid, matches: Nid) -> Option<bool> {
|
||||||
|
use TokenKind as K;
|
||||||
|
let [tn, mn] = [&self[to_match], &self[matches]];
|
||||||
|
match (tn.kind, mn.kind) {
|
||||||
|
_ if to_match == matches => Some(true),
|
||||||
|
(Kind::BinOp { op: K::Ne }, Kind::BinOp { op: K::Eq })
|
||||||
|
| (Kind::BinOp { op: K::Eq }, Kind::BinOp { op: K::Ne })
|
||||||
|
if tn.inputs[1..] == mn.inputs[1..] =>
|
||||||
|
{
|
||||||
|
Some(false)
|
||||||
|
}
|
||||||
|
(_, Kind::BinOp { op: K::Band }) => self
|
||||||
|
.matches_cond(to_match, mn.inputs[1])
|
||||||
|
.or(self.matches_cond(to_match, mn.inputs[2])),
|
||||||
|
(_, Kind::BinOp { op: K::Bor }) => match (
|
||||||
|
self.matches_cond(to_match, mn.inputs[1]),
|
||||||
|
self.matches_cond(to_match, mn.inputs[2]),
|
||||||
|
) {
|
||||||
|
(None, Some(a)) | (Some(a), None) => Some(a),
|
||||||
|
(Some(b), Some(a)) if a == b => Some(a),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_const(&self, id: Nid) -> bool {
|
fn is_const(&self, id: Nid) -> bool {
|
||||||
matches!(self[id].kind, Kind::CInt { .. })
|
matches!(self[id].kind, Kind::CInt { .. })
|
||||||
}
|
}
|
||||||
|
@ -1326,7 +1318,6 @@ impl Nodes {
|
||||||
self[prev].outputs.swap_remove(index);
|
self[prev].outputs.swap_remove(index);
|
||||||
self[with].outputs.push(target);
|
self[with].outputs.push(target);
|
||||||
self.remove(prev);
|
self.remove(prev);
|
||||||
|
|
||||||
target
|
target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1366,7 +1357,7 @@ impl Nodes {
|
||||||
write!(out, "{:>4}: ", op.name())
|
write!(out, "{:>4}: ", op.name())
|
||||||
}
|
}
|
||||||
Kind::Call { func, args: _ } => {
|
Kind::Call { func, args: _ } => {
|
||||||
write!(out, "call: {func} {} ", self[node].depth)
|
write!(out, "call: {func} {} ", self[node].depth.get())
|
||||||
}
|
}
|
||||||
Kind::Global { global } => write!(out, "glob: {global:<5}"),
|
Kind::Global { global } => write!(out, "glob: {global:<5}"),
|
||||||
Kind::Entry => write!(out, "ctrl: {:<5}", "entry"),
|
Kind::Entry => write!(out, "ctrl: {:<5}", "entry"),
|
||||||
|
@ -1396,7 +1387,7 @@ impl Nodes {
|
||||||
while self.visited.set(node) {
|
while self.visited.set(node) {
|
||||||
match self[node].kind {
|
match self[node].kind {
|
||||||
Kind::Start => {
|
Kind::Start => {
|
||||||
writeln!(out, "start: {}", self[node].depth)?;
|
writeln!(out, "start: {}", self[node].depth.get())?;
|
||||||
let mut cfg_index = Nid::MAX;
|
let mut cfg_index = Nid::MAX;
|
||||||
for o in iter(self, node) {
|
for o in iter(self, node) {
|
||||||
self.basic_blocks_instr(out, o)?;
|
self.basic_blocks_instr(out, o)?;
|
||||||
|
@ -1415,7 +1406,9 @@ impl Nodes {
|
||||||
writeln!(
|
writeln!(
|
||||||
out,
|
out,
|
||||||
"region{node}: {} {} {:?}",
|
"region{node}: {} {} {:?}",
|
||||||
self[node].depth, self[node].loop_depth, self[node].inputs
|
self[node].depth.get(),
|
||||||
|
self[node].loop_depth,
|
||||||
|
self[node].inputs
|
||||||
)?;
|
)?;
|
||||||
let mut cfg_index = Nid::MAX;
|
let mut cfg_index = Nid::MAX;
|
||||||
for o in iter(self, node) {
|
for o in iter(self, node) {
|
||||||
|
@ -1430,7 +1423,9 @@ impl Nodes {
|
||||||
writeln!(
|
writeln!(
|
||||||
out,
|
out,
|
||||||
"loop{node}: {} {} {:?}",
|
"loop{node}: {} {} {:?}",
|
||||||
self[node].depth, self[node].loop_depth, self[node].outputs
|
self[node].depth.get(),
|
||||||
|
self[node].loop_depth,
|
||||||
|
self[node].outputs
|
||||||
)?;
|
)?;
|
||||||
let mut cfg_index = Nid::MAX;
|
let mut cfg_index = Nid::MAX;
|
||||||
for o in iter(self, node) {
|
for o in iter(self, node) {
|
||||||
|
@ -1448,7 +1443,9 @@ impl Nodes {
|
||||||
writeln!(
|
writeln!(
|
||||||
out,
|
out,
|
||||||
"b{node}: {} {} {:?}",
|
"b{node}: {} {} {:?}",
|
||||||
self[node].depth, self[node].loop_depth, self[node].outputs
|
self[node].depth.get(),
|
||||||
|
self[node].loop_depth,
|
||||||
|
self[node].outputs
|
||||||
)?;
|
)?;
|
||||||
let mut cfg_index = Nid::MAX;
|
let mut cfg_index = Nid::MAX;
|
||||||
for o in iter(self, node) {
|
for o in iter(self, node) {
|
||||||
|
@ -1605,6 +1602,11 @@ impl Nodes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CondOptRes {
|
||||||
|
Unknown,
|
||||||
|
Known { value: bool, pin: Option<Nid> },
|
||||||
|
}
|
||||||
|
|
||||||
impl ops::Index<Nid> for Nodes {
|
impl ops::Index<Nid> for Nodes {
|
||||||
type Output = Node;
|
type Output = Node;
|
||||||
|
|
||||||
|
@ -1622,6 +1624,7 @@ impl ops::IndexMut<Nid> for Nodes {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub enum AssertKind {
|
pub enum AssertKind {
|
||||||
NullCheck,
|
NullCheck,
|
||||||
|
UnwrapCheck,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
|
||||||
|
@ -1649,11 +1652,6 @@ pub enum Kind {
|
||||||
Return,
|
Return,
|
||||||
// [ctrl]
|
// [ctrl]
|
||||||
Die,
|
Die,
|
||||||
// [ctrl, cond]
|
|
||||||
Assert {
|
|
||||||
kind: AssertKind,
|
|
||||||
pos: Pos,
|
|
||||||
},
|
|
||||||
// [ctrl]
|
// [ctrl]
|
||||||
CInt {
|
CInt {
|
||||||
value: i64,
|
value: i64,
|
||||||
|
@ -1678,6 +1676,11 @@ pub enum Kind {
|
||||||
func: ty::Func,
|
func: ty::Func,
|
||||||
args: ty::Tuple,
|
args: ty::Tuple,
|
||||||
},
|
},
|
||||||
|
// [ctrl, cond, value]
|
||||||
|
Assert {
|
||||||
|
kind: AssertKind,
|
||||||
|
pos: Pos,
|
||||||
|
},
|
||||||
// [ctrl]
|
// [ctrl]
|
||||||
Stck,
|
Stck,
|
||||||
// [ctrl, memory]
|
// [ctrl, memory]
|
||||||
|
@ -1742,7 +1745,7 @@ pub struct Node {
|
||||||
ty: ty::Id,
|
ty: ty::Id,
|
||||||
offset: Offset,
|
offset: Offset,
|
||||||
ralloc_backref: RallocBRef,
|
ralloc_backref: RallocBRef,
|
||||||
depth: IDomDepth,
|
depth: Cell<IDomDepth>,
|
||||||
lock_rc: LockRc,
|
lock_rc: LockRc,
|
||||||
loop_depth: LoopDepth,
|
loop_depth: LoopDepth,
|
||||||
aclass: AClassId,
|
aclass: AClassId,
|
||||||
|
@ -2020,7 +2023,9 @@ impl ItemCtx {
|
||||||
mem::take(&mut self.ctrl).soft_remove(&mut self.nodes);
|
mem::take(&mut self.ctrl).soft_remove(&mut self.nodes);
|
||||||
|
|
||||||
self.nodes.iter_peeps(1000, stack);
|
self.nodes.iter_peeps(1000, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlock(&mut self) {
|
||||||
self.nodes.unlock(MEM);
|
self.nodes.unlock(MEM);
|
||||||
self.nodes.unlock(NEVER);
|
self.nodes.unlock(NEVER);
|
||||||
self.nodes.unlock(LOOPS);
|
self.nodes.unlock(LOOPS);
|
||||||
|
@ -2583,7 +2588,7 @@ impl<'a> Codegen<'a> {
|
||||||
Expr::Field { target, name, pos } => {
|
Expr::Field { target, name, pos } => {
|
||||||
let mut vtarget = self.raw_expr(target)?;
|
let mut vtarget = self.raw_expr(target)?;
|
||||||
self.strip_var(&mut vtarget);
|
self.strip_var(&mut vtarget);
|
||||||
self.unwrap_opt(pos, &mut vtarget);
|
self.implicit_unwrap(pos, &mut vtarget);
|
||||||
let tty = vtarget.ty;
|
let tty = vtarget.ty;
|
||||||
|
|
||||||
if let ty::Kind::Module(m) = tty.expand() {
|
if let ty::Kind::Module(m) = tty.expand() {
|
||||||
|
@ -2657,7 +2662,7 @@ impl<'a> Codegen<'a> {
|
||||||
let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) };
|
let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) };
|
||||||
let mut vl = self.expr_ctx(val, ctx)?;
|
let mut vl = self.expr_ctx(val, ctx)?;
|
||||||
|
|
||||||
self.unwrap_opt(val.pos(), &mut vl);
|
self.implicit_unwrap(val.pos(), &mut vl);
|
||||||
|
|
||||||
let Some(base) = self.tys.base_of(vl.ty) else {
|
let Some(base) = self.tys.base_of(vl.ty) else {
|
||||||
self.report(
|
self.report(
|
||||||
|
@ -2753,7 +2758,7 @@ impl<'a> Codegen<'a> {
|
||||||
{
|
{
|
||||||
let mut lhs = self.raw_expr_ctx(left, ctx)?;
|
let mut lhs = self.raw_expr_ctx(left, ctx)?;
|
||||||
self.strip_var(&mut lhs);
|
self.strip_var(&mut lhs);
|
||||||
self.unwrap_opt(left.pos(), &mut lhs);
|
self.implicit_unwrap(left.pos(), &mut lhs);
|
||||||
|
|
||||||
match lhs.ty.expand() {
|
match lhs.ty.expand() {
|
||||||
_ if lhs.ty.is_pointer()
|
_ if lhs.ty.is_pointer()
|
||||||
|
@ -2767,7 +2772,7 @@ impl<'a> Codegen<'a> {
|
||||||
self.ci.nodes.unlock(lhs.id);
|
self.ci.nodes.unlock(lhs.id);
|
||||||
let mut rhs = rhs?;
|
let mut rhs = rhs?;
|
||||||
self.strip_var(&mut rhs);
|
self.strip_var(&mut rhs);
|
||||||
self.unwrap_opt(right.pos(), &mut rhs);
|
self.implicit_unwrap(right.pos(), &mut rhs);
|
||||||
let (ty, aclass) = self.binop_ty(pos, &mut lhs, &mut rhs, op);
|
let (ty, aclass) = self.binop_ty(pos, &mut lhs, &mut rhs, op);
|
||||||
let inps = [VOID, lhs.id, rhs.id];
|
let inps = [VOID, lhs.id, rhs.id];
|
||||||
let bop =
|
let bop =
|
||||||
|
@ -2896,7 +2901,7 @@ impl<'a> Codegen<'a> {
|
||||||
let mut val = self.raw_expr(expr)?;
|
let mut val = self.raw_expr(expr)?;
|
||||||
self.strip_var(&mut val);
|
self.strip_var(&mut val);
|
||||||
|
|
||||||
let Some(ty) = self.tys.inner_of(val.ty) else {
|
if !val.ty.is_optional() {
|
||||||
self.report(
|
self.report(
|
||||||
expr.pos(),
|
expr.pos(),
|
||||||
fa!(
|
fa!(
|
||||||
|
@ -2907,8 +2912,7 @@ impl<'a> Codegen<'a> {
|
||||||
return Value::NEVER;
|
return Value::NEVER;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.unwrap_opt_unchecked(ty, val.ty, &mut val);
|
self.explicit_unwrap(expr.pos(), &mut val);
|
||||||
val.ty = ty;
|
|
||||||
Some(val)
|
Some(val)
|
||||||
}
|
}
|
||||||
Expr::Directive { name: "intcast", args: [expr], pos } => {
|
Expr::Directive { name: "intcast", args: [expr], pos } => {
|
||||||
|
@ -4078,30 +4082,76 @@ impl<'a> Codegen<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize(&mut self, prev_err_len: usize) -> bool {
|
fn finalize(&mut self, prev_err_len: usize) -> bool {
|
||||||
|
use {AssertKind as AK, CondOptRes as CR};
|
||||||
|
|
||||||
self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files);
|
self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files);
|
||||||
for (_, node) in self.ci.nodes.iter() {
|
let mut to_remove = vec![];
|
||||||
|
for (id, node) in self.ci.nodes.iter() {
|
||||||
let Kind::Assert { kind, pos } = node.kind else { continue };
|
let Kind::Assert { kind, pos } = node.kind else { continue };
|
||||||
|
|
||||||
match kind {
|
let res = self.ci.nodes.try_opt_cond(id);
|
||||||
AssertKind::NullCheck => match node.ty {
|
|
||||||
ty::Id::NEVER => {
|
// TODO: highlight the pin position
|
||||||
self.report(
|
let msg = match (kind, res) {
|
||||||
pos,
|
(AK::UnwrapCheck, CR::Known { value: false, .. }) => {
|
||||||
"the value is always null, some checks might need to be inverted",
|
"unwrap is not needed since the value is (provably) never null, \
|
||||||
);
|
remove it, or replace with '@as(<expr_ty>, <opt_expr>)'"
|
||||||
}
|
}
|
||||||
_ => {
|
(AK::UnwrapCheck, CR::Known { value: true, .. }) => {
|
||||||
self.report(
|
"unwrap is incorrect since the value is (provably) always null, \
|
||||||
pos,
|
make sure your logic is correct"
|
||||||
|
}
|
||||||
|
(AK::NullCheck, CR::Known { value: true, .. }) => {
|
||||||
|
"the value is always null, some checks might need to be inverted"
|
||||||
|
}
|
||||||
|
(AK::NullCheck, CR::Unknown) => {
|
||||||
"can't prove the value is not 'null', \
|
"can't prove the value is not 'null', \
|
||||||
use '@unwrap(<opt>)' if you believe compiler is stupid, \
|
use '@unwrap(<opt>)' if you believe compiler is stupid, \
|
||||||
or explicitly check for null and handle it \
|
or explicitly check for null and handle it \
|
||||||
('if <opt> == null { /* handle */ } else { /* use opt */ }')",
|
('if <opt> == null { /* handle */ } else { /* use opt */ }')"
|
||||||
|
}
|
||||||
|
(AK::NullCheck, CR::Known { value: false, pin }) => {
|
||||||
|
to_remove.push((id, pin));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(AK::UnwrapCheck, CR::Unknown) => {
|
||||||
|
to_remove.push((id, None));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.report(pos, msg);
|
||||||
|
}
|
||||||
|
to_remove.into_iter().for_each(|(n, pin)| {
|
||||||
|
let pin = pin.unwrap_or_else(|| {
|
||||||
|
let mut pin = self.ci.nodes[n].inputs[0];
|
||||||
|
while matches!(self.ci.nodes[pin].kind, Kind::Assert { .. }) {
|
||||||
|
pin = self.ci.nodes[n].inputs[0];
|
||||||
|
}
|
||||||
|
pin
|
||||||
|
});
|
||||||
|
for mut out in self.ci.nodes[n].outputs.clone() {
|
||||||
|
if self.ci.nodes.is_cfg(out) {
|
||||||
|
let index = self.ci.nodes[out].inputs.iter().position(|&p| p == n).unwrap();
|
||||||
|
self.ci.nodes.modify_input(out, index, self.ci.nodes[n].inputs[0]);
|
||||||
|
} else {
|
||||||
|
if !self.ci.nodes[out].kind.is_pinned() {
|
||||||
|
out = self.ci.nodes.modify_input(out, 0, pin);
|
||||||
|
}
|
||||||
|
let index =
|
||||||
|
self.ci.nodes[out].inputs[1..].iter().position(|&p| p == n).unwrap() + 1;
|
||||||
|
self.ci.nodes.modify_input(out, index, self.ci.nodes[n].inputs[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_assert!(
|
||||||
|
self.ci.nodes.values[n as usize]
|
||||||
|
.as_ref()
|
||||||
|
.map_or(true, |n| !matches!(n.kind, Kind::Assert { .. })),
|
||||||
|
"{:?} {:?}",
|
||||||
|
self.ci.nodes[n],
|
||||||
|
self.ci.nodes[n].outputs.iter().map(|&o| &self.ci.nodes[o]).collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
},
|
self.ci.unlock();
|
||||||
}
|
|
||||||
}
|
|
||||||
self.errors.borrow().len() == prev_err_len
|
self.errors.borrow().len() == prev_err_len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4200,21 +4250,32 @@ impl<'a> Codegen<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap_opt(&mut self, pos: Pos, opt: &mut Value) {
|
fn implicit_unwrap(&mut self, pos: Pos, opt: &mut Value) {
|
||||||
|
self.unwrap_low(pos, opt, AssertKind::NullCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn explicit_unwrap(&mut self, pos: Pos, opt: &mut Value) {
|
||||||
|
self.unwrap_low(pos, opt, AssertKind::UnwrapCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwrap_low(&mut self, pos: Pos, opt: &mut Value, kind: AssertKind) {
|
||||||
let Some(ty) = self.tys.inner_of(opt.ty) else { return };
|
let Some(ty) = self.tys.inner_of(opt.ty) else { return };
|
||||||
let null_check = self.gen_null_check(*opt, ty, TokenKind::Eq);
|
let null_check = self.gen_null_check(*opt, ty, TokenKind::Eq);
|
||||||
|
|
||||||
// TODO: extract the if check int a fucntion
|
|
||||||
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);
|
let oty = mem::replace(&mut opt.ty, ty);
|
||||||
self.unwrap_opt_unchecked(ty, oty, opt);
|
self.unwrap_opt_unchecked(ty, oty, opt);
|
||||||
|
|
||||||
|
// TODO: extract the if check int a fucntion
|
||||||
|
self.ci.ctrl.set(
|
||||||
|
self.ci.nodes.new_node(oty, Kind::Assert { kind, pos }, [
|
||||||
|
self.ci.ctrl.get(),
|
||||||
|
null_check,
|
||||||
|
opt.id,
|
||||||
|
]),
|
||||||
|
&mut self.ci.nodes,
|
||||||
|
);
|
||||||
|
self.ci.nodes.pass_aclass(self.ci.nodes.aclass_index(opt.id).1, self.ci.ctrl.get());
|
||||||
|
opt.id = self.ci.ctrl.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap_opt_unchecked(&mut self, ty: ty::Id, oty: ty::Id, opt: &mut Value) {
|
fn unwrap_opt_unchecked(&mut self, ty: ty::Id, oty: ty::Id, opt: &mut Value) {
|
||||||
|
@ -4293,7 +4354,7 @@ impl<'a> Codegen<'a> {
|
||||||
if let Some(inner) = self.tys.inner_of(src.ty)
|
if let Some(inner) = self.tys.inner_of(src.ty)
|
||||||
&& inner.try_upcast(expected) == Some(expected)
|
&& inner.try_upcast(expected) == Some(expected)
|
||||||
{
|
{
|
||||||
self.unwrap_opt(pos, src);
|
self.implicit_unwrap(pos, src);
|
||||||
return self.assert_ty(pos, src, expected, hint);
|
return self.assert_ty(pos, src, expected, hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4480,6 +4541,7 @@ mod tests {
|
||||||
fb_driver;
|
fb_driver;
|
||||||
|
|
||||||
// Purely Testing Examples;
|
// Purely Testing Examples;
|
||||||
|
needless_unwrap;
|
||||||
inlining_issues;
|
inlining_issues;
|
||||||
null_check_test;
|
null_check_test;
|
||||||
only_break_loop;
|
only_break_loop;
|
||||||
|
@ -4519,5 +4581,6 @@ mod tests {
|
||||||
aliasing_overoptimization;
|
aliasing_overoptimization;
|
||||||
global_aliasing_overptimization;
|
global_aliasing_overptimization;
|
||||||
overwrite_aliasing_overoptimization;
|
overwrite_aliasing_overoptimization;
|
||||||
|
more_if_opts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -350,7 +350,7 @@ impl ItemCtx {
|
||||||
let &[dst, lhs, rhs] = allocs else { unreachable!() };
|
let &[dst, lhs, rhs] = allocs else { unreachable!() };
|
||||||
self.emit(op(atr(dst), atr(lhs), atr(rhs)));
|
self.emit(op(atr(dst), atr(lhs), atr(rhs)));
|
||||||
} else if let Some(against) = op.cmp_against() {
|
} else if let Some(against) = op.cmp_against() {
|
||||||
let op_ty = fuc.nodes[lh].ty;
|
let op_ty = fuc.nodes[rh].ty;
|
||||||
|
|
||||||
self.emit(extend(fuc.nodes[lh].ty, fuc.nodes[lh].ty.extend(), 0, 0));
|
self.emit(extend(fuc.nodes[lh].ty, fuc.nodes[lh].ty.extend(), 0, 0));
|
||||||
self.emit(extend(fuc.nodes[rh].ty, fuc.nodes[rh].ty.extend(), 1, 1));
|
self.emit(extend(fuc.nodes[rh].ty, fuc.nodes[rh].ty.extend(), 1, 1));
|
||||||
|
@ -365,7 +365,7 @@ impl ItemCtx {
|
||||||
let op_fn = opop.float_cmp(op_ty).unwrap();
|
let op_fn = opop.float_cmp(op_ty).unwrap();
|
||||||
self.emit(op_fn(atr(dst), atr(lhs), atr(rhs)));
|
self.emit(op_fn(atr(dst), atr(lhs), atr(rhs)));
|
||||||
self.emit(instrs::not(atr(dst), atr(dst)));
|
self.emit(instrs::not(atr(dst), atr(dst)));
|
||||||
} else if op_ty.is_integer() {
|
} else {
|
||||||
let op_fn =
|
let op_fn =
|
||||||
if op_ty.is_signed() { instrs::cmps } else { instrs::cmpu };
|
if op_ty.is_signed() { instrs::cmps } else { instrs::cmpu };
|
||||||
self.emit(op_fn(atr(dst), atr(lhs), atr(rhs)));
|
self.emit(op_fn(atr(dst), atr(lhs), atr(rhs)));
|
||||||
|
@ -373,8 +373,6 @@ impl ItemCtx {
|
||||||
if matches!(op, TokenKind::Eq | TokenKind::Lt | TokenKind::Gt) {
|
if matches!(op, TokenKind::Eq | TokenKind::Lt | TokenKind::Gt) {
|
||||||
self.emit(instrs::not(atr(dst), atr(dst)));
|
self.emit(instrs::not(atr(dst), atr(dst)));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
todo!("unhandled operator: {op}");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
todo!("unhandled operator: {op}");
|
todo!("unhandled operator: {op}");
|
||||||
|
|
27
lang/tests/son_tests_more_if_opts.txt
Normal file
27
lang/tests/son_tests_more_if_opts.txt
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
main:
|
||||||
|
ADDI64 r254, r254, -16d
|
||||||
|
ST r31, r254, 0a, 16h
|
||||||
|
JAL r31, r0, :opaque
|
||||||
|
CP r32, r1
|
||||||
|
JAL r31, r0, :opaque
|
||||||
|
LI64 r6, 0d
|
||||||
|
CP r1, r32
|
||||||
|
JNE r1, r6, :0
|
||||||
|
CP r32, r1
|
||||||
|
LI64 r1, 0d
|
||||||
|
CP r3, r32
|
||||||
|
JMP :1
|
||||||
|
0: CP r3, r1
|
||||||
|
LD r1, r3, 0a, 8h
|
||||||
|
1: JEQ r3, r6, :2
|
||||||
|
LD r1, r3, 0a, 8h
|
||||||
|
JMP :2
|
||||||
|
2: LD r31, r254, 0a, 16h
|
||||||
|
ADDI64 r254, r254, 16d
|
||||||
|
JALA r0, r31, 0a
|
||||||
|
opaque:
|
||||||
|
LI64 r1, 0d
|
||||||
|
JALA r0, r31, 0a
|
||||||
|
code size: 183
|
||||||
|
ret: 0
|
||||||
|
status: Ok(())
|
6
lang/tests/son_tests_needless_unwrap.txt
Normal file
6
lang/tests/son_tests_needless_unwrap.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
test.hb:4:17: unwrap is not needed since the value is (provably) never null, remove it, or replace with '@as(<expr_ty>, <opt_expr>)'
|
||||||
|
ptr := @unwrap(always_nn)
|
||||||
|
^
|
||||||
|
test.hb:6:16: unwrap is incorrect since the value is (provably) always null, make sure your logic is correct
|
||||||
|
ptr = @unwrap(always_n)
|
||||||
|
^
|
Loading…
Reference in a new issue