removing false positives

This commit is contained in:
mlokr 2024-09-12 18:42:21 +02:00
parent dc418bd5e0
commit 641d344d2d
No known key found for this signature in database
GPG key ID: DEA147DDEE644993
12 changed files with 300 additions and 124 deletions

View file

@ -337,6 +337,8 @@ foo := fn(a: int, b: int, c: int): int {
#### idk
```hb
_edge_case := @as(int, idk)
main := fn(): int {
big_array := @as([u8; 128], idk)
i := 0

View file

@ -1648,12 +1648,15 @@ impl Codegen {
};
if ctx.loc.is_some() {
self.report(
pos,
"`idk` would be written to an existing memory location \
which at ths point does notthing so its prohibited. TODO: make debug \
builds write 0xAA instead.",
);
// self.report(
// pos,
// format_args!(
// "`idk` would be written to an existing memory location \
// which at ths point does notthing so its prohibited. TODO: make debug \
// builds write 0xAA instead. Info for weak people: {:?}",
// ctx.loc
// ),
// );
}
let loc = match self.tys.size_of(ty) {

View file

@ -15,7 +15,8 @@
slice_ptr_get,
slice_take,
map_try_insert,
extract_if
extract_if,
ptr_internals
)]
#![allow(internal_features, clippy::format_collect)]

View file

@ -18,8 +18,9 @@ use {
collections::{hash_map, BTreeMap},
fmt::{Display, Write},
hash::{Hash as _, Hasher},
mem,
ops::{self, Range},
mem::{self, MaybeUninit},
ops::{self, Deref, DerefMut, Range},
ptr::Unique,
rc::Rc,
},
};
@ -44,6 +45,108 @@ impl Drop for Drom {
}
}
const VC_SIZE: usize = std::mem::size_of::<AllocedVc>();
const INLINE_ELEMS: usize = VC_SIZE / std::mem::size_of::<Nid>() - 1;
union Vc {
inline: InlineVc,
alloced: AllocedVc,
}
impl Vc {
fn is_inline(&self) -> bool {
unsafe { self.inline.len <= INLINE_ELEMS as u32 }
}
fn layout(&self) -> Option<std::alloc::Layout> {
unsafe {
self.is_inline()
.then(|| std::alloc::Layout::array::<Nid>(self.alloced.cap as _).unwrap_unchecked())
}
}
fn len(&self) -> usize {
unsafe { self.inline.len as _ }
}
fn as_ptr(&self) -> *mut Nid {
unsafe {
match self.is_inline() {
true => self.alloced.base.as_ptr(),
false => { self.inline.elems }.as_mut_ptr().cast(),
}
}
}
fn as_slice(&self) -> &[Nid] {
unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) }
}
fn as_slice_mut(&mut self) -> &mut [Nid] {
unsafe { std::slice::from_raw_parts_mut(self.as_ptr(), self.len()) }
}
}
impl Drop for Vc {
fn drop(&mut self) {
if let Some(layout) = self.layout() {
unsafe {
std::alloc::dealloc(self.alloced.base.as_ptr().cast(), layout);
}
}
}
}
impl Clone for Vc {
fn clone(&self) -> Self {
if self.is_inline() {
unsafe { std::ptr::read(self) }
} else {
let layout = unsafe { std::alloc::Layout::array::<Nid>(self.len()).unwrap_unchecked() };
let alloc = unsafe { std::alloc::alloc(layout) };
unsafe { std::ptr::copy_nonoverlapping(self.as_ptr(), alloc.cast(), self.len()) };
unsafe {
Vc {
alloced: AllocedVc {
base: Unique::new_unchecked(alloc.cast()),
len: self.alloced.len,
cap: self.alloced.cap,
},
}
}
}
}
}
impl Deref for Vc {
type Target = [Nid];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl DerefMut for Vc {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_slice_mut()
}
}
#[derive(Clone, Copy)]
#[repr(C)]
struct InlineVc {
elems: MaybeUninit<[Nid; INLINE_ELEMS]>,
len: Nid,
}
#[derive(Clone, Copy)]
#[repr(C)]
struct AllocedVc {
base: Unique<Nid>,
cap: u32,
len: u32,
}
#[derive(Default)]
struct BitSet {
data: Vec<usize>,
@ -539,8 +642,7 @@ impl Nodes {
) -> Nid {
let ty = ty.into();
let node =
Node { inputs: inps.into(), kind, color: 0, depth: 0, lock_rc: 0, ty, outputs: vec![] };
let node = Node { inputs: inps.into(), kind, ty, ..Default::default() };
let mut lookup_meta = None;
if !node.is_lazy_phi() {
@ -916,17 +1018,15 @@ impl Nodes {
#[allow(clippy::format_in_format_args)]
fn basic_blocks_instr(&mut self, out: &mut String, node: Nid) -> std::fmt::Result {
if !self.visited.set(node as _) {
return Ok(());
if self[node].kind != Kind::Loop && self[node].kind != Kind::Region {
write!(out, " {node:>2}: ")?;
}
write!(out, " {node:>2}: ")?;
match self[node].kind {
Kind::Start => unreachable!(),
Kind::End => unreachable!(),
Kind::If => write!(out, " if: "),
Kind::Region => unreachable!(),
Kind::Loop => unreachable!(),
Kind::Return => write!(out, " ret: "),
Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"),
Kind::Return => write!(out, " ret: "),
Kind::CInt { value } => write!(out, "cint: #{value:<4}"),
Kind::Phi => write!(out, " phi: "),
Kind::Tuple { index } => write!(out, " arg: {index:<5}"),
@ -934,66 +1034,65 @@ impl Nodes {
write!(out, "{:>4}: ", op.name())
}
Kind::Call { func } => {
write!(out, "call: {func} {}", self[node].depth)
write!(out, "call: {func} {} ", self[node].depth)
}
}?;
writeln!(
out,
" {:<14} {}",
format!("{:?}", self[node].inputs),
format!("{:?}", self[node].outputs)
)
if self[node].kind != Kind::Loop && self[node].kind != Kind::Region {
writeln!(
out,
" {:<14} {}",
format!("{:?}", self[node].inputs),
format!("{:?}", self[node].outputs)
)?;
}
Ok(())
}
fn basic_blocks_low(&mut self, out: &mut String, mut node: Nid) -> std::fmt::Result {
let iter = |nodes: &Nodes, node| nodes[node].outputs.clone().into_iter().rev();
while self.visited.set(node as _) {
match dbg!(self[node].kind) {
match self[node].kind {
Kind::Start => {
writeln!(out, "start: {}", self[node].depth)?;
let mut cfg_index = Nid::MAX;
for o in self[node].outputs.clone() {
for o in iter(self, node) {
self.basic_blocks_instr(out, o)?;
if self[o].kind == (Kind::Tuple { index: 0 }) {
cfg_index = o;
continue;
}
self.basic_blocks_instr(out, o)?;
}
node = cfg_index;
}
Kind::End => break,
Kind::If => {
self.visited.unset(node as _);
self.basic_blocks_instr(out, node)?;
self.basic_blocks_low(out, self[node].outputs[0])?;
node = self[node].outputs[1];
}
Kind::Region => {
writeln!(out, "region{node}: {}", self[node].depth)?;
let mut cfg_index = Nid::MAX;
for o in self[node].outputs.clone().into_iter() {
for o in iter(self, node) {
self.basic_blocks_instr(out, o)?;
if self.is_cfg(o) {
cfg_index = o;
continue;
}
self.basic_blocks_instr(out, o)?;
}
node = cfg_index;
}
Kind::Loop => {
writeln!(out, "loop{node} {}", self[node].depth)?;
writeln!(out, "loop{node}: {}", self[node].depth)?;
let mut cfg_index = Nid::MAX;
for o in self[node].outputs.clone().into_iter() {
for o in iter(self, node) {
self.basic_blocks_instr(out, o)?;
if self.is_cfg(o) {
cfg_index = o;
continue;
}
self.basic_blocks_instr(out, o)?;
}
node = cfg_index;
}
Kind::Return => {
self.visited.unset(node as _);
self.basic_blocks_instr(out, node)?;
node = self[node].outputs[0];
}
Kind::CInt { .. } => unreachable!(),
@ -1001,34 +1100,24 @@ impl Nodes {
Kind::Tuple { .. } => {
writeln!(out, "b{node}: {}", self[node].depth)?;
let mut cfg_index = Nid::MAX;
for o in self[node].outputs.clone().into_iter() {
for o in iter(self, node) {
self.basic_blocks_instr(out, o)?;
if self.is_cfg(o) {
cfg_index = o;
continue;
}
self.basic_blocks_instr(out, o)?;
}
if !self[cfg_index].kind.ends_basic_block()
&& !matches!(self[cfg_index].kind, Kind::Call { .. })
{
writeln!(out, " goto: {cfg_index}")?;
}
node = cfg_index;
}
Kind::BinOp { .. } => unreachable!(),
Kind::Call { .. } => {
self.visited.unset(node as _);
self.basic_blocks_instr(out, node)?;
let mut cfg_index = Nid::MAX;
for o in self[node].outputs.clone().into_iter() {
if self.is_cfg(o) {
cfg_index = o;
continue;
}
for o in iter(self, node) {
if self[o].inputs[0] == node {
self.basic_blocks_instr(out, o)?;
}
if self.is_cfg(o) {
cfg_index = o;
}
}
node = cfg_index;
}
@ -1042,7 +1131,7 @@ impl Nodes {
let mut out = String::new();
self.visited.clear(self.values.len());
self.basic_blocks_low(&mut out, 0).unwrap();
println!("{out}");
eprintln!("{out}");
}
fn graphviz(&self) {
@ -1182,20 +1271,29 @@ impl ops::IndexMut<u32> for Nodes {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[repr(u8)]
pub enum Kind {
#[default]
Start,
End,
If,
Region,
Loop,
Return,
CInt { value: i64 },
CInt {
value: i64,
},
Phi,
Tuple { index: u32 },
BinOp { op: lexer::TokenKind },
Call { func: ty::Func },
Tuple {
index: u32,
},
BinOp {
op: lexer::TokenKind,
},
Call {
func: ty::Func,
},
}
impl Kind {
@ -1238,7 +1336,7 @@ impl fmt::Display for Kind {
}
}
#[derive(Debug)]
#[derive(Debug, Default)]
struct Node {
inputs: Vec<Nid>,
outputs: Vec<Nid>,
@ -1247,6 +1345,7 @@ struct Node {
depth: u32,
lock_rc: u32,
ty: ty::Id,
loop_depth: u32,
}
impl Node {
@ -2012,6 +2111,13 @@ impl Codegen {
self.ci.nodes.modify_input(node, 1, self.ci.ctrl);
let idx = self.ci.nodes[node]
.outputs
.iter()
.position(|&n| self.ci.nodes.is_cfg(n))
.unwrap();
self.ci.nodes[node].outputs.swap(idx, 0);
self.ci.ctrl = bre;
if bre == Nid::MAX {
return None;
@ -3255,6 +3361,47 @@ impl Codegen {
}
fn gcm(&mut self) {
fn loop_depth(target: Nid, nodes: &mut Nodes) -> u32 {
if nodes[target].loop_depth != 0 {
return nodes[target].loop_depth;
}
nodes[target].loop_depth = match nodes[target].kind {
Kind::Tuple { .. } | Kind::Call { .. } | Kind::Return | Kind::If => {
loop_depth(nodes[target].inputs[0], nodes)
}
Kind::Region => loop_depth(idom(nodes, target), nodes),
Kind::Loop => {
let depth = loop_depth(nodes[target].inputs[0], nodes) + 1;
let mut cursor = nodes[target].inputs[1];
while cursor != target {
nodes[cursor].loop_depth = depth;
let next = idom(nodes, cursor);
if let Kind::Tuple { index } = nodes[cursor].kind
&& nodes[next].kind == Kind::If
{
let other = *nodes[next].outputs.iter().find(|&&n| matches!(nodes[n].kind, Kind::Tuple { index: oi } if index != oi)).unwrap();
nodes[other].loop_depth = depth - 1;
}
cursor = next;
}
depth
}
Kind::Start | Kind::End => 1,
Kind::CInt { .. } | Kind::Phi | Kind::BinOp { .. } => {
unreachable!()
}
};
nodes[target].loop_depth
}
fn better(nodes: &mut Nodes, is: Nid, then: Nid) -> bool {
loop_depth(is, nodes) < loop_depth(then, nodes)
|| idepth(nodes, is) > idepth(nodes, then)
|| nodes[then].kind == Kind::If
}
fn idepth(nodes: &mut Nodes, target: Nid) -> u32 {
if target == 0 {
return 0;
@ -3279,21 +3426,8 @@ impl Codegen {
| Kind::Return
| Kind::If => nodes[target].inputs[0],
Kind::Region => {
let &[mut lcfg, mut rcfg] = nodes[target].inputs.as_slice() else {
unreachable!()
};
while lcfg != rcfg {
let [ldepth, rdepth] = [idepth(nodes, lcfg), idepth(nodes, rcfg)];
if ldepth >= rdepth {
lcfg = idom(nodes, lcfg);
}
if ldepth <= rdepth {
rcfg = idom(nodes, rcfg);
}
}
lcfg
let &[lcfg, rcfg] = nodes[target].inputs.as_slice() else { unreachable!() };
common_dom(lcfg, rcfg, nodes)
}
}
}
@ -3340,7 +3474,7 @@ impl Codegen {
}
}
fn push_down(nodes: &mut Nodes, node: Nid, lowest_pos: &mut [u32]) {
fn push_down(nodes: &mut Nodes, node: Nid) {
if !nodes.visited.set(node as _) {
return;
}
@ -3348,47 +3482,72 @@ impl Codegen {
// TODO: handle memory nodes first
if nodes[node].kind.is_pinned() {
for i in 0..nodes[node].inputs.len() {
let i = nodes[node].inputs[i];
push_up(nodes, i);
// TODO: use buffer to avoid allocation or better yet queue the location changes
for i in nodes[node].outputs.clone() {
push_down(nodes, i);
}
} else {
let mut max = 0;
for i in 0..nodes[node].inputs.len() {
let i = nodes[node].inputs[i];
let is_call = matches!(nodes[i].kind, Kind::Call { .. });
if nodes.is_cfg(i) && !is_call {
continue;
let mut min = None::<Nid>;
for i in 0..nodes[node].outputs.len() {
let i = nodes[node].outputs[i];
push_down(nodes, i);
let i = use_block(node, i, nodes);
min = min.map(|m| common_dom(i, m, nodes)).or(Some(i));
}
let mut min = min.unwrap();
let mut cursor = min;
loop {
if better(nodes, cursor, min) {
min = cursor;
}
push_up(nodes, i);
if idepth(nodes, i) > idepth(nodes, max) {
max = if is_call { i } else { idom(nodes, i) };
if cursor == nodes[node].inputs[0] {
break;
}
cursor = idom(nodes, cursor);
}
if max == 0 {
return;
if nodes[min].kind.ends_basic_block() {
min = idom(nodes, min);
}
let index = nodes[0].outputs.iter().position(|&p| p == node).unwrap();
nodes[0].outputs.remove(index);
nodes[node].inputs[0] = max;
debug_assert!(
!nodes[max].outputs.contains(&node)
|| matches!(nodes[max].kind, Kind::Call { .. }),
"{node} {:?} {max} {:?}",
nodes[node],
nodes[max]
);
nodes[max].outputs.push(node);
let prev = nodes[node].inputs[0];
if min != prev {
let index = nodes[prev].outputs.iter().position(|&p| p == node).unwrap();
nodes[prev].outputs.remove(index);
nodes[node].inputs[0] = min;
nodes[min].outputs.push(node);
}
}
}
fn use_block(target: Nid, from: Nid, nodes: &mut Nodes) -> Nid {
if nodes[from].kind != Kind::Phi {
return idom(nodes, from);
}
let index = nodes[from].inputs.iter().position(|&n| n == target).unwrap();
nodes[nodes[from].inputs[0]].inputs[index - 1]
}
fn common_dom(mut a: Nid, mut b: Nid, nodes: &mut Nodes) -> Nid {
while a != b {
let [ldepth, rdepth] = [idepth(nodes, a), idepth(nodes, b)];
if ldepth >= rdepth {
a = idom(nodes, a);
}
if ldepth <= rdepth {
b = idom(nodes, b);
}
}
a
}
self.ci.nodes.visited.clear(self.ci.nodes.values.len());
push_up(&mut self.ci.nodes, self.ci.end);
// TODO: handle infinte loops
self.ci.nodes.visited.clear(self.ci.nodes.values.len());
//push_down(&mut self.ci.nodes, self.ci.start);
push_down(&mut self.ci.nodes, self.ci.start);
}
}
@ -3626,6 +3785,6 @@ mod tests {
const_folding_with_arg => README;
// FIXME: contains redundant copies
branch_assignments => README;
//exhaustive_loop_testing => README;
exhaustive_loop_testing => README;
}
}

View file

@ -6,6 +6,6 @@ main:
LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
code size: 236
code size: 239
ret: 50
status: Ok(())

View file

@ -11,6 +11,6 @@ main:
LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
code size: 296
code size: 299
ret: 55
status: Ok(())

View file

@ -14,7 +14,8 @@ fib:
JGTS r32, r33, :0
LI64 r1, 1d
JMP :1
0: ADDI64 r33, r32, -1d
0: CP r33, r32
ADDI64 r33, r33, -1d
CP r2, r33
JAL r31, r0, :fib
CP r33, r1
@ -26,6 +27,6 @@ fib:
1: LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
code size: 228
code size: 231
ret: 55
status: Ok(())

View file

@ -57,10 +57,11 @@ example:
CMPUI r35, r35, 0d
OR r34, r34, r35
JEQ r34, r0, :0
CP r34, r33
LI64 r35, 1024d
ADDI64 r35, r35, 0d
ADDI64 r35, r35, 1d
DIRS64 r0, r34, r33, r35
DIRS64 r0, r34, r34, r35
ADDI64 r32, r34, 0d
JMP :1
0: CP r32, r33
@ -82,17 +83,20 @@ integer:
LI64 r3, 4d
ECA
CP r34, r1
CP r35, r32
LI64 r36, 0d
CMPS r35, r32, r36
CMPS r35, r35, r36
CMPUI r35, r35, 0d
CP r36, r33
LI64 r37, 0d
CMPS r36, r33, r37
CMPS r36, r36, r37
CMPUI r36, r36, 0d
OR r35, r35, r36
JEQ r35, r0, :0
CP r35, r34
SUB64 r33, r33, r32
ADDI64 r33, r33, 1d
DIRS64 r0, r35, r34, r33
DIRS64 r0, r35, r35, r33
ADD64 r1, r35, r32
JMP :1
0: CP r1, r34
@ -152,6 +156,6 @@ line:
2: LD r31, r254, 48a, 32h
ADDI64 r254, r254, 80d
JALA r0, r31, 0a
code size: 1404
code size: 1416
ret: 0
status: Ok(())

View file

@ -16,13 +16,14 @@ integer_range:
LI64 r2, 3d
LI64 r3, 4d
ECA
CP r34, r1
SUB64 r33, r33, r32
ADDI64 r33, r33, 1d
DIRU64 r0, r34, r1, r33
DIRU64 r0, r34, r34, r33
ADD64 r1, r34, r32
LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
code size: 211
code size: 214
ret: 42
status: Ok(())

View file

@ -15,7 +15,8 @@ fib:
2: LI64 r35, 0d
JNE r32, r35, :0
JMP :1
0: ADD64 r35, r33, r34
0: CP r35, r33
ADD64 r35, r35, r34
CP r33, r34
CP r34, r35
ADDI64 r32, r32, -1d
@ -24,6 +25,6 @@ fib:
LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
code size: 215
code size: 218
ret: 55
status: Ok(())

View file

@ -43,7 +43,8 @@ request_page:
ST r31, r254, 0a, 32h
CP r32, r2
LRA r33, r0, :"\0\u{1}xxxxxxxx"
ADDI64 r34, r33, 1d
CP r34, r33
ADDI64 r34, r34, 1d
ST r32, r34, 0a, 1h
LI64 r2, 3d
LI64 r3, 2d
@ -53,6 +54,6 @@ request_page:
LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
code size: 440
code size: 443
ret: 42
status: Ok(())

View file

@ -28,7 +28,8 @@ fib_iter:
2: LI64 r35, 0d
JNE r32, r35, :0
JMP :1
0: ADD64 r35, r33, r34
0: CP r35, r33
ADD64 r35, r35, r34
CP r33, r34
CP r34, r35
ADDI64 r32, r32, -1d
@ -43,11 +44,13 @@ fib:
CP r32, r2
LI64 r33, 2d
JLTS r32, r33, :0
ADDI64 r33, r32, -1d
CP r33, r32
ADDI64 r33, r33, -1d
CP r2, r33
JAL r31, r0, :fib
CP r33, r1
ADDI64 r34, r32, -2d
CP r34, r32
ADDI64 r34, r34, -2d
CP r2, r34
JAL r31, r0, :fib
CP r34, r1
@ -57,6 +60,6 @@ fib:
1: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
code size: 455
code size: 464
ret: 0
status: Ok(())