os

An operating system
git clone https://erai.gay/code/os/
Log | Files | Refs | README | LICENSE

commit 86c229545c585a7098edfc7bc2452033766ae6e5
parent 3c2cd14fc90f271cecd3eab71c77dcdd9cb218f5
Author: erai <erai@omiltem.net>
Date:   Sat, 15 Feb 2025 20:54:29 +0000

backlink ir

Diffstat:
Mir.om | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mircout.om | 55+------------------------------------------------------
2 files changed, 135 insertions(+), 68 deletions(-)

diff --git a/ir.om b/ir.om @@ -66,6 +66,8 @@ struct irblock { ops_len: int; ops_cap: int; done: int; + back: **irblock; // WARNING: if the flow graph changes, this is stale. + back_len: int; out: *irblock; alt: *irblock; label: *label; @@ -92,6 +94,7 @@ struct irvar { t: *type; n: int; offset: int; + dead: int; mark: int; } @@ -107,6 +110,8 @@ struct irfunc { blocks: **irblock; blocks_len: int; blocks_cap: int; + returns: **irblock; // WARNING: if the flow graph changes, this is stale. + returns_len: int; cur: *irblock; labels_tree: *irlabel; vars_tree: *irvar; @@ -2441,16 +2446,27 @@ func irblock_fold(ic: *irfunc, b: *irblock) { func irblock_flow2(ic: *irfunc, ib: *irblock) { var kind: int; + var op: *irop; + // If we visit a block twice, mark it as having two incoming links if ib.mark { ib.mark = 2; return; } - ib.mark = 1; - kind = ib.ops[ib.ops_len - 1].kind; + op = ib.ops[ib.ops_len - 1]; + kind = op.kind; + + // Convert useless branches to jumps + if kind == IOP_BRANCH && ib.out == ib.alt { + kind = IOP_JUMP; + op.kind = IOP_JUMP; + op.a = nil; + ib.alt = nil; + } + // Follow the alt link of a branch if kind == IOP_BRANCH { irblock_flow2(ic, ib.alt); @@ -2459,6 +2475,7 @@ func irblock_flow2(ic: *irfunc, ib: *irblock) { } } + // Follow the out link of a jump/call/branch if kind == IOP_BRANCH || kind == IOP_CALL || kind == IOP_JUMP { irblock_flow2(ic, ib.out); @@ -2473,42 +2490,47 @@ func irblock_flow3(ic: *irfunc, ib: *irblock) { var kind: int; var i: int; + // Attempt to merge once if ib.mark == 3 { return; } - ib.mark = 3; loop { kind = ib.ops[ib.ops_len - 1].kind; + // Merge down the alt branch first if kind == IOP_BRANCH { irblock_flow3(ic, ib.alt); break; } + // Only merge blocks that end in a jump if kind != IOP_JUMP { break; } out = ib.out; + // If a block has two incoming edges, then don't merge. + // But continue attempting to merge down the chain. if out.mark == 2 { ib = out; ib.mark = 3; continue; } + // If this chain reached a merge point then go to the next chain. if out.mark == 3 { break; } - // Delete the jump + // Delete the jump. ib.out = nil; ib.ops_len = ib.ops_len - 1; ib.done = 0; - // Concatenate the block + // Concatenate the blocks. i = 0; loop { if i == out.ops_len { @@ -2520,7 +2542,7 @@ func irblock_flow3(ic: *irfunc, ib: *irblock) { i = i + 1; } - // Possible branch + // Update our links. ib.out = out.out; ib.alt = out.alt; ib.done = 1; @@ -2566,7 +2588,7 @@ func irfunc_fold(ic: *irfunc) { } } -func irblock_value(ic: *irfunc, ib: *irblock) { +func irblock_basic_value(ic: *irfunc, ib: *irblock) { var i: int; i = 0; @@ -2581,7 +2603,7 @@ func irblock_value(ic: *irfunc, ib: *irblock) { } } -func irfunc_value(ic: *irfunc) { +func irfunc_basic_value(ic: *irfunc) { var i: int; // Forward simple stores to their use @@ -2591,28 +2613,126 @@ func irfunc_value(ic: *irfunc) { break; } - irblock_value(ic, ic.blocks[i]); + irblock_basic_value(ic, ic.blocks[i]); i = i + 1; } } +func irblock_backlink(ic: *irfunc, ib: *irblock, from: *irblock) { + var kind: int; + + // Add an incoming edge. + if from { + if ib.back { + ib.back[ib.back_len] = from; + } + ib.back_len = ib.back_len + 1; + } + + // Descend to out/alt blocks once. + if ib.mark { + return; + } + ib.mark = 1; + + // Add incoming edges for unique blocks. + kind = ib.ops[ib.ops_len - 1].kind; + if kind == IOP_CALL || kind == IOP_JUMP { + irblock_backlink(ic, ib.out, ib); + } else if kind == IOP_BRANCH { + irblock_backlink(ic, ib.out, ib); + if ib.out != ib.alt { + irblock_backlink(ic, ib.alt, ib); + } else { + die("useless branch"); + } + } else if kind == IOP_RETURN { + if ic.returns { + ic.returns[ic.returns_len] = ib; + } + ic.returns_len = ic.returns_len + 1; + } else { + die("invalid block"); + } +} + +func irfunc_backlink(ic: *irfunc) { + var ib: *irblock; + var i: int; + var j: int; + + // Clear the reachable returns + if ic.returns { + free(ic.a, ic.returns as *byte); + } + ic.returns = nil; + ic.returns_len = 0; + + // Clear the known backlinks + i = 0; + loop { + if i == ic.blocks_len { + break; + } + + ib = ic.blocks[i]; + + if ib.back { + free(ic.a, ib.back as *byte); + } + ib.back = nil; + ib.back_len = 0; + + i = i + 1; + } + + // Walk the call graph to find the reachable blocks + irblock_backlink(ic, ic.blocks[0], nil); + + // Allocate space for returns + ic.returns = alloc(ic.a, ic.returns_len * sizeof(*ic.returns)) as **irblock; + + // Find returns, clear marks, and allocate backlinks + i = 0; + j = 0; + loop { + if i == ic.blocks_len { + break; + } + + ib = ic.blocks[i]; + + if ib.back_len { + ib.back = alloc(ic.a, ib.back_len * sizeof(*ib.back)) as **irblock; + } + + ib.back_len = 0; + ib.mark = 0; + + i = i + 1; + } + + // Fill in the backlink arrays + irblock_backlink(ic, ic.blocks[0], nil); + irreset(ic.blocks[0]); +} + func ir_optimize(ic: *irfunc) { // Do the easy simplifications first. irfunc_dead(ic); irfunc_fold(ic); irfunc_flow(ic); + irfunc_backlink(ic); - irfunc_value(ic); + // Do some data flow analysis within a basic block + irfunc_basic_value(ic); // Do them again. - irfunc_dead(ic); irfunc_fold(ic); irfunc_flow(ic); + irfunc_backlink(ic); - // Do some data flow analysis - // Do value forwarding - // Do common expression elimination // Do instruction selection // Do register allocation } diff --git a/ircout.om b/ircout.om @@ -104,8 +104,6 @@ func ircdefine(c: *compiler, d: *decl) { iv = ic.vars[i]; - iv.mark = 0; - i = i + 1; } @@ -123,7 +121,7 @@ func ircdefine(c: *compiler, d: *decl) { iv = ic.vars[i]; - if !iv.mark { + if iv.dead { i = i + 1; continue; } @@ -162,19 +160,6 @@ func ircdefine(c: *compiler, d: *decl) { irreset(top); fputs(c.cout, "}\n"); - - i = 0; - loop { - if i == ic.vars_len { - break; - } - - iv = ic.vars[i]; - - iv.mark = 0; - - i = i + 1; - } } func ircuse(c: *compiler, ic: *irfunc, ib: *irblock) { @@ -191,17 +176,6 @@ func ircuse(c: *compiler, ic: *irfunc, ib: *irblock) { ib.mark = 2; - i = 0; - loop { - if i == ib.ops_len { - break; - } - - ircuseop(c, ic, ib, ib.ops[i]); - - i = i + 1; - } - if ib.out { ircuse(c, ic, ib.out); } @@ -212,33 +186,6 @@ func ircuse(c: *compiler, ic: *irfunc, ib: *irblock) { } } -func ircuseop(c: *compiler, ic: *irfunc, ib: *irblock, op: *irop) { - var kind: int; - - kind = op.kind; - if kind == IOP_VAR || kind == IOP_VARREF { - ic.vars[op.n].mark = 1; - } else if kind == IOP_STORE || kind == IOP_ADD || kind == IOP_AND - || kind == IOP_OR || kind == IOP_XOR || kind == IOP_DIV - || kind == IOP_MOD || kind == IOP_LSH || kind == IOP_RSH - || kind == IOP_MUL || kind == IOP_SUB || kind == IOP_EQ - || kind == IOP_NE || kind == IOP_GT || kind == IOP_GE - || kind == IOP_LT || kind == IOP_LE { - // Binary operators - ircuseop(c, ic, ib, op.a); - ircuseop(c, ic, ib, op.b); - } else if kind == IOP_NEG || kind == IOP_NOT || kind == IOP_LOAD - || kind == IOP_RETVAL || kind == IOP_ARG || kind == IOP_CALL - || kind == IOP_BRANCH || kind == IOP_RETURN { - // Unary operators - ircuseop(c, ic, ib, op.a); - } else if kind == IOP_FUNC || kind == IOP_CONST || kind == IOP_STR || kind == IOP_JUMP { - // No variable possible - } else { - die("invalid op"); - } -} - func ircbody(c: *compiler, ic: *irfunc, ib: *irblock) { if !ib || ib.mark & 1 { return;