From 33c5fe8c4d864aaf55221ae832e84ef3b9310d65 Mon Sep 17 00:00:00 2001 From: Matthew Schneider Date: Wed, 16 Sep 2026 18:11:54 -0400 Subject: [PATCH 01/16] Track start pc, end pc, and pc of last instruction per CFG block --- src/util/CfgBuilder.v3 | 14 ++++++++++++++ test/unittest/ControlStackTest.v3 | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/util/CfgBuilder.v3 b/src/util/CfgBuilder.v3 index 5739fa3dd..702922dd2 100644 --- a/src/util/CfgBuilder.v3 +++ b/src/util/CfgBuilder.v3 @@ -16,9 +16,19 @@ class CfgBuilder { def build(func: FuncDecl) -> CfgBlock { var start = ctl_stack.reset(); bi.reset(func); + bi.dispatchLocalDecls(ctl_stack); // advance bi.pc past locals before ctl_stack.pc is read while (bi.more()) { ctl_stack.pc = bi.pc; + def before = ctl_stack.block, op = bi.current(); + // an `end` or `loop` is a merge point and belongs to the block it merges into + def isMerge = op == Opcode.END || op == Opcode.LOOP; bi.dispatch(ctl_stack); + def owner = if(isMerge, ctl_stack.block, before); + if (owner != null) { + if (owner.start < 0) owner.start = bi.pc; + owner.last_instr_pc = bi.pc; + owner.end = bi.nextPc(); + } bi.next(); } return start; @@ -54,6 +64,10 @@ var labelId = 100; class CfgBlock(decl_pos: int) { def id = labelId++; var bind_pos: int = -1; + // the bytecode range [start ... end) this block owns + var start: int = -1; + var end: int = -1; + var last_instr_pc: int = -1; // last instruction this block owns, or < 0 when it owns none var outgoing: List<(int, CfgBlock)>; var incoming: List<(int, CfgBlock)>; var mark: int; diff --git a/test/unittest/ControlStackTest.v3 b/test/unittest/ControlStackTest.v3 index 80e8a0337..707c0c5b6 100644 --- a/test/unittest/ControlStackTest.v3 +++ b/test/unittest/ControlStackTest.v3 @@ -8,6 +8,7 @@ def X_ = void( T("br1", test_br1), T("br2", test_br2), T("br_if0", test_br_if0), + T("block_extent", test_block_extent), T("unreach", test_unreach), T("return", test_return), T("loop0", test_loop0), @@ -117,6 +118,20 @@ def test_br_if0(t: ControlStackTester) { 3); } +// the entry block starts after the locals declaration and owns both nops; the final `end` is a +// merge point, so it belongs to the block it merges into +def test_block_extent(t: ControlStackTester) { + t.addLocal(ValueType.I32); + t.code([ + u8.!(Opcode.NOP.code), + u8.!(Opcode.NOP.code) + ]); + var entry = CfgBuilder.new().build(t.func); + t.t.asserti(3, entry.start); + t.t.asserti(4, entry.last_instr_pc); + t.t.asserti(5, entry.end); +} + def test_unreach(t: ControlStackTester) { t.test_count2([ ([u8.!(Opcode.UNREACHABLE.code)], 1, 0) From 52dbbfb09912da261daeeada24ef323abfc0efec Mon Sep 17 00:00:00 2001 From: Matthew Schneider Date: Mon, 5 Oct 2026 14:56:15 -0400 Subject: [PATCH 02/16] Redo pc tracking in cfg builder --- src/util/CfgBuilder.v3 | 36 +++++++++++++++++++++--------------- src/util/ControlStack.v3 | 20 +++++++++++++------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/util/CfgBuilder.v3 b/src/util/CfgBuilder.v3 index 857717765..7d6c55787 100644 --- a/src/util/CfgBuilder.v3 +++ b/src/util/CfgBuilder.v3 @@ -5,33 +5,34 @@ class CfgBuilder { private def bi = BytecodeIterator.new(); private def ctl_stack = ControlStack.new(); + private var prev_pc: int; // for recording `last_instr_pc` when closing a block new() { ctl_stack.newLabel = newCfgBlock; + ctl_stack.newBlock = closeCfgBlock; ctl_stack.splitBlock = splitCfgBlock; ctl_stack.mergeBlock = mergeCfgBlock; ctl_stack.bindLabel = bindCfgBlock; } def build(func: FuncDecl) -> CfgBlock { - var start = ctl_stack.reset(); bi.reset(func); - bi.dispatchLocalDecls(ctl_stack); // advance bi.pc past locals before ctl_stack.pc is read + bi.dispatchLocalDecls(ctl_stack); // advance bi.pc past locals + prev_pc = -1; + def entry = ctl_stack.resetAtPc(bi.pc); while (bi.more()) { ctl_stack.pc = bi.pc; - def before = ctl_stack.block, op = bi.current(); - // an `end` or `loop` is a merge point and belongs to the block it merges into - def isMerge = op == Opcode.END || op == Opcode.LOOP; bi.dispatch(ctl_stack); - def owner = if(isMerge, ctl_stack.block, before); - if (owner != null) { - if (owner.start < 0) owner.start = bi.pc; - owner.last_instr_pc = bi.pc; - owner.end = bi.nextPc(); - } + prev_pc = bi.pc; bi.next(); } - return start; + close(ctl_stack.block, bi.pc); // the function's `end` label + return entry; + } + // Ends {b} at {pos}, where whatever follows it begins. + private def close(b: CfgBlock, pos: int) { + b.end = pos; + if (pos > b.bind_pos) b.last_instr_pc = if(pos == bi.pc, prev_pc, bi.pc); } // prepends, so {outgoing} is in reverse branch-label order private def link(pc: int, from: CfgBlock, to: CfgBlock) -> CfgEdge { @@ -44,6 +45,11 @@ class CfgBuilder { private def newCfgBlock(cs: ControlStack) -> CfgBlock { return if(cs.isUnreachable(), null, CfgBlock.new(cs.pc)); } + private def closeCfgBlock(cs: ControlStack) -> CfgBlock { + // control leaves the current block after this instruction + if (cs.block != null) close(cs.block, bi.nextPc()); + return null; // FIXME: a reachable `try` merges into this null block and crashes in {link} + } private def splitCfgBlock(cs: ControlStack, from: CfgBlock) -> (CfgBlock, CfgBlock) { if (from == null || cs.isUnreachable()) return (null, null); var pos = bi.pc; @@ -60,8 +66,9 @@ class CfgBuilder { return to; } private def bindCfgBlock(cs: ControlStack, pc: int, label: CfgBlock) { - if (label == null) return; if (pc < 0) pc = bi.nextPc(); + if (cs.block != null) close(cs.block, pc); // labels are bound before they become current + if (label == null) return; label.bind_pos = pc; if (verbose) Trace.OUT.put2("bind id:%d @%d", label.id, pc).ln(); } @@ -76,9 +83,8 @@ class CfgEdge(pc: int, source: CfgBlock, target: CfgBlock) { class CfgBlock(decl_pos: int) { def id = labelId++; + // the bytecode range [bind_pos ... end) this block owns var bind_pos: int = -1; - // the bytecode range [start ... end) this block owns - var start: int = -1; var end: int = -1; var last_instr_pc: int = -1; // last instruction this block owns, or < 0 when it owns none var outgoing: List; diff --git a/src/util/ControlStack.v3 b/src/util/ControlStack.v3 index 033c01bc1..ea45d5ac2 100644 --- a/src/util/ControlStack.v3 +++ b/src/util/ControlStack.v3 @@ -28,10 +28,16 @@ class ControlStack extends BytecodeVisitor { // Resets this control to the initial state for entry. def reset() -> B { - pc = 0; + return resetAtPc(0); + } + def resetAtPc(new_pc: int) -> B { + pc = new_pc; stack.clear(); - block = newLabel(this); - bindLabel(this, 0, block); + var d: B; + block = d; // no current block while the entry is bound + var entry = newLabel(this); + bindLabel(this, new_pc, entry); + block = entry; push(Opcode.UNREACHABLE); return block; } @@ -63,8 +69,8 @@ class ControlStack extends BytecodeVisitor { var from = stack.peek(); var t = splitBlock(this, block); var ctl = push(Opcode.IF); + bindLabel(this, -1, t.0); block = t.0; - bindLabel(this, -1, block); ctl.else_block = t.1; } def visit_ELSE() { @@ -73,8 +79,8 @@ class ControlStack extends BytecodeVisitor { ctl.start_pos = pc; ctl.start_opcode = Opcode.ELSE; ctl.reachable = true; + bindLabel(this, -1, ctl.else_block); block = ctl.else_block; - bindLabel(this, -1, block); var d: B; ctl.else_block = d; } @@ -90,13 +96,13 @@ class ControlStack extends BytecodeVisitor { var ctl = stack.peek(); ctl.start_pos = pc; ctl.start_opcode = Opcode.CATCH; - block = newLabel(this); + block = newLabel(this); // FIXME: the handler is never bound, nor the previous block ended } def visit_CATCH_ALL() { var ctl = stack.peek(); ctl.start_pos = pc; ctl.start_opcode = Opcode.CATCH_ALL; - block = newLabel(this); + block = newLabel(this); // FIXME: the handler is never bound, nor the previous block ended } def visit_THROW(tag_code: u31) { setUnreachable(); From cfe503fd18df0eb5c3b36bceadd5953fcd75c098 Mon Sep 17 00:00:00 2001 From: Matthew Schneider Date: Mon, 5 Oct 2026 15:14:25 -0400 Subject: [PATCH 03/16] Add `endBlock` callback instead of shoving it into `bindLabel` --- src/util/CfgBuilder.v3 | 38 ++++++++++++++++++++------------------ src/util/ControlStack.v3 | 32 +++++++++++++++++++++++--------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/util/CfgBuilder.v3 b/src/util/CfgBuilder.v3 index 7d6c55787..38349dce5 100644 --- a/src/util/CfgBuilder.v3 +++ b/src/util/CfgBuilder.v3 @@ -5,34 +5,28 @@ class CfgBuilder { private def bi = BytecodeIterator.new(); private def ctl_stack = ControlStack.new(); - private var prev_pc: int; // for recording `last_instr_pc` when closing a block + private var prev_pc: int = -1; // for recording `last_instr_pc` when ending a block new() { + // FIXME: with no {newBlock}, a reachable `try` merges into a null block and crashes in {link} ctl_stack.newLabel = newCfgBlock; - ctl_stack.newBlock = closeCfgBlock; ctl_stack.splitBlock = splitCfgBlock; ctl_stack.mergeBlock = mergeCfgBlock; ctl_stack.bindLabel = bindCfgBlock; + ctl_stack.endBlock = endCfgBlock; } def build(func: FuncDecl) -> CfgBlock { bi.reset(func); bi.dispatchLocalDecls(ctl_stack); // advance bi.pc past locals - prev_pc = -1; - def entry = ctl_stack.resetAtPc(bi.pc); + def start = ctl_stack.resetAtPc(bi.pc); while (bi.more()) { ctl_stack.pc = bi.pc; bi.dispatch(ctl_stack); prev_pc = bi.pc; bi.next(); } - close(ctl_stack.block, bi.pc); // the function's `end` label - return entry; - } - // Ends {b} at {pos}, where whatever follows it begins. - private def close(b: CfgBlock, pos: int) { - b.end = pos; - if (pos > b.bind_pos) b.last_instr_pc = if(pos == bi.pc, prev_pc, bi.pc); + return start; } // prepends, so {outgoing} is in reverse branch-label order private def link(pc: int, from: CfgBlock, to: CfgBlock) -> CfgEdge { @@ -45,11 +39,6 @@ class CfgBuilder { private def newCfgBlock(cs: ControlStack) -> CfgBlock { return if(cs.isUnreachable(), null, CfgBlock.new(cs.pc)); } - private def closeCfgBlock(cs: ControlStack) -> CfgBlock { - // control leaves the current block after this instruction - if (cs.block != null) close(cs.block, bi.nextPc()); - return null; // FIXME: a reachable `try` merges into this null block and crashes in {link} - } private def splitCfgBlock(cs: ControlStack, from: CfgBlock) -> (CfgBlock, CfgBlock) { if (from == null || cs.isUnreachable()) return (null, null); var pos = bi.pc; @@ -66,12 +55,25 @@ class CfgBuilder { return to; } private def bindCfgBlock(cs: ControlStack, pc: int, label: CfgBlock) { - if (pc < 0) pc = bi.nextPc(); - if (cs.block != null) close(cs.block, pc); // labels are bound before they become current if (label == null) return; + if (pc < 0) pc = bi.nextPc(); label.bind_pos = pc; if (verbose) Trace.OUT.put2("bind id:%d @%d", label.id, pc).ln(); } + private def endCfgBlock(cs: ControlStack, b: CfgBlock, pc: int) { + if (b == null) return; + var last: int; + if (pc < 0) { + // end of the block is at the pc of the next instruction + last = bi.pc; + b.end = bi.nextPc(); + } else { + // current instruction at pc belongs to new block + last = prev_pc; + b.end = pc; + } + if (b.end > b.bind_pos) b.last_instr_pc = last; + } } def verbose = false; diff --git a/src/util/ControlStack.v3 b/src/util/ControlStack.v3 index ea45d5ac2..9e9757fdc 100644 --- a/src/util/ControlStack.v3 +++ b/src/util/ControlStack.v3 @@ -21,6 +21,8 @@ class ControlStack extends BytecodeVisitor { var mergeBlock: (ControlStack, B, B) -> B = ControlStack.defaultMergeBlock; // Binds a label position. var bindLabel: (ControlStack, int, B) -> void = ControlStack.defaultBindLabel; + // Ends a block at a position, or after the current bytecode if the position is -1. + var endBlock: (ControlStack, B, int) -> void = ControlStack.defaultEndBlock; // current block var block: B; @@ -33,11 +35,8 @@ class ControlStack extends BytecodeVisitor { def resetAtPc(new_pc: int) -> B { pc = new_pc; stack.clear(); - var d: B; - block = d; // no current block while the entry is bound - var entry = newLabel(this); - bindLabel(this, new_pc, entry); - block = entry; + block = newLabel(this); + bindLabel(this, new_pc, block); push(Opcode.UNREACHABLE); return block; } @@ -62,6 +61,7 @@ class ControlStack extends BytecodeVisitor { def visit_LOOP(btc: BlockTypeCode) { var from = stack.peek(); var ctl = push(Opcode.LOOP); + endBlock(this, block, pc); bindLabel(this, pc, ctl.label); block = ctl.label = mergeBlock(this, block, ctl.label); } @@ -69,8 +69,9 @@ class ControlStack extends BytecodeVisitor { var from = stack.peek(); var t = splitBlock(this, block); var ctl = push(Opcode.IF); - bindLabel(this, -1, t.0); + endBlock(this, block, -1); block = t.0; + bindLabel(this, -1, block); ctl.else_block = t.1; } def visit_ELSE() { @@ -79,8 +80,9 @@ class ControlStack extends BytecodeVisitor { ctl.start_pos = pc; ctl.start_opcode = Opcode.ELSE; ctl.reachable = true; - bindLabel(this, -1, ctl.else_block); + endBlock(this, block, -1); block = ctl.else_block; + bindLabel(this, -1, block); var d: B; ctl.else_block = d; } @@ -96,13 +98,15 @@ class ControlStack extends BytecodeVisitor { var ctl = stack.peek(); ctl.start_pos = pc; ctl.start_opcode = Opcode.CATCH; - block = newLabel(this); // FIXME: the handler is never bound, nor the previous block ended + endBlock(this, block, -1); + block = newLabel(this); // FIXME: the handler is never bound } def visit_CATCH_ALL() { var ctl = stack.peek(); ctl.start_pos = pc; ctl.start_opcode = Opcode.CATCH_ALL; - block = newLabel(this); // FIXME: the handler is never bound, nor the previous block ended + endBlock(this, block, -1); + block = newLabel(this); // FIXME: the handler is never bound } def visit_THROW(tag_code: u31) { setUnreachable(); @@ -186,21 +190,27 @@ class ControlStack extends BytecodeVisitor { end = mergeBlock(this, block, end); // fallthru ctl.reachable = true; end = mergeBlock(this, ctl.else_block, end); // else merge + endBlock(this, block, pc); + bindLabel(this, pc, ctl.else_block); // the empty else clause + endBlock(this, ctl.else_block, pc); bindLabel(this, pc, end); block = end; } _ => { var end = ctl.label; end = mergeBlock(this, block, end); // fallthru + endBlock(this, block, pc); bindLabel(this, pc, end); block = end; } } stack.pop(); + if (stack.top == 0) endBlock(this, block, -1); // the function's `end` } private def setUnreachable() { var from = stack.peek(); from.reachable = false; + endBlock(this, block, -1); block = newBlock(this); } private def br(depth: u31) { @@ -208,6 +218,7 @@ class ControlStack extends BytecodeVisitor { var to = stack.elems[stack.top - depth - 1]; to.label = mergeBlock(this, block, to.label); from.reachable = false; + endBlock(this, block, -1); block = newBlock(this); } private def brIf(depth: u31) { @@ -215,6 +226,7 @@ class ControlStack extends BytecodeVisitor { var to = stack.elems[stack.top - depth - 1]; var t = splitBlockInto(this, block, to.label); to.label = t.0; + endBlock(this, block, -1); block = t.1; } // Default implementations of block management functions. @@ -238,6 +250,8 @@ class ControlStack extends BytecodeVisitor { } def defaultBindLabel(pc: int, b: B) { } + def defaultEndBlock(b: B, pc: int) { + } } // A control stack entry tracked by the control stack. From 993e85d76b978dfc6f3e30d96a041971c277d165 Mon Sep 17 00:00:00 2001 From: Matthew Schneider Date: Mon, 5 Oct 2026 17:37:30 -0400 Subject: [PATCH 04/16] Clean up tests --- test/unittest/ControlStackTest.v3 | 178 ++++++++++++++++++++++++++++-- 1 file changed, 166 insertions(+), 12 deletions(-) diff --git a/test/unittest/ControlStackTest.v3 b/test/unittest/ControlStackTest.v3 index d6a82e434..9bd410a71 100644 --- a/test/unittest/ControlStackTest.v3 +++ b/test/unittest/ControlStackTest.v3 @@ -8,7 +8,6 @@ def X_ = void( T("br1", test_br1), T("br2", test_br2), T("br_if0", test_br_if0), - T("block_extent", test_block_extent), T("br_table_succs", test_br_table_succs), T("loop_backedge", test_loop_backedge), T("unreach", test_unreach), @@ -24,6 +23,21 @@ def X_ = void( T("if_unreach1", test_if_unreach1), () ); +def C = UnitTests.registerT("cfg:", _, ControlStackTester.new, _); +def Y_ = void( + C("range_entry", test_range_entry), + C("range_if", test_range_if), + C("range_if_else", test_range_if_else), + C("range_empty_body", test_range_empty_body), + C("range_loop_in_if", test_range_loop_in_if), + C("range_if_in_loop", test_range_if_in_loop), + C("range_br_outer", test_range_br_outer), + C("range_br_table_loop", test_range_br_table_loop), + C("range_br_if_chain", test_range_br_if_chain), + C("range_if_then_dead", test_range_if_then_dead), + C("range_return_in_if", test_range_return_in_if), + () +); def verbose = false; @@ -76,6 +90,40 @@ class ControlStackTester(t: Tester) extends ModuleBuilder { code(buf.extract()); test_count(expected); } + // Checks the blocks reachable from the entry: those that own instructions against {expected}, + // given as (first pc, last instruction pc, end) in pc order, and that the rest are empty. + def test_ranges(raw: Array, expected: Array<(int, int, int)>) { + code(raw); + def entry = CfgBuilder.new().build(func); + entry.mark = 1; + def blocks = Vector.new().put(entry); + def owners = Vector<(int, int, int)>.new(); + for (i = 0; i < blocks.length; i++) { + def b = blocks[i]; + if (b.last_instr_pc >= 0) owners.put((b.bind_pos, b.last_instr_pc, b.end)); + else if (b.end != b.bind_pos) return t.fail1("block at +%d owns nothing but isn't empty", b.bind_pos); + for (l = b.outgoing; l != null; l = l.tail) { + def s = l.head.target; + if (s.mark == 0) { + s.mark = 1; + blocks.put(s); + } + } + } + def unsorted = owners.extract(); + def got = Arrays.sort(unsorted, 0, unsorted.length, byLastInstr); + if (Arrays.equal(expected, got)) return; + def buf = StringBuilder.new().puts("expected"); + putRanges(buf, expected); + putRanges(buf.puts(", got"), got); + t.fail(buf.toString()); + } +} +def byLastInstr(a: (int, int, int), b: (int, int, int)) -> bool { + return a.1 < b.1; +} +def putRanges(buf: StringBuilder, ranges: Array<(int, int, int)>) { + for (e in ranges) buf.put3(" (%d, %d, %d)", e.0, e.1, e.2); } def test_count0(t: ControlStackTester) { @@ -121,18 +169,124 @@ def test_br_if0(t: ControlStackTester) { 3); } -// the entry block starts after the locals declaration and owns both nops; the final `end` is a -// merge point, so it belongs to the block it merges into -def test_block_extent(t: ControlStackTester) { +def test_range_entry(t: ControlStackTester) { t.addLocal(ValueType.I32); - t.code([ - u8.!(Opcode.NOP.code), - u8.!(Opcode.NOP.code) - ]); - var entry = CfgBuilder.new().build(t.func); - t.t.asserti(3, entry.start); - t.t.asserti(4, entry.last_instr_pc); - t.t.asserti(5, entry.end); + t.test_ranges([ + u8.!(Opcode.NOP.code), // +3 + u8.!(Opcode.NOP.code) // +4 + ], [(3, 4, 5), (5, 5, 6)]); +} + +def test_range_if(t: ControlStackTester) { + t.test_ranges([ + u8.!(Opcode.I32_CONST.code), 0, // +1 + u8.!(Opcode.IF.code), EMPTY, // +3 + u8.!(Opcode.NOP.code), // +5 + u8.!(Opcode.END.code) // +6 + ], [(1, 3, 5), (5, 5, 6), (6, 6, 7), (7, 7, 8)]); +} + +def test_range_if_else(t: ControlStackTester) { + t.test_ranges([ + u8.!(Opcode.I32_CONST.code), 0, // +1 + u8.!(Opcode.IF.code), EMPTY, // +3 + u8.!(Opcode.NOP.code), // +5 + u8.!(Opcode.ELSE.code), // +6 + u8.!(Opcode.NOP.code), // +7 + u8.!(Opcode.END.code) // +8 + ], [(1, 3, 5), (5, 6, 7), (7, 7, 8), (8, 8, 9), (9, 9, 10)]); +} + +def test_range_empty_body(t: ControlStackTester) { + t.test_ranges([], [(1, 1, 2)]); +} + +def test_range_loop_in_if(t: ControlStackTester) { + t.test_ranges([ + u8.!(Opcode.I32_CONST.code), 0, // +1 + u8.!(Opcode.IF.code), EMPTY, // +3 + u8.!(Opcode.LOOP.code), EMPTY, // +5 + u8.!(Opcode.I32_CONST.code), 0, // +7 + u8.!(Opcode.BR_IF.code), 0, // +9 + u8.!(Opcode.END.code), // +11 + u8.!(Opcode.END.code) // +12 + ], [(1, 3, 5), (5, 9, 11), (11, 11, 12), (12, 12, 13), (13, 13, 14)]); +} + +def test_range_if_in_loop(t: ControlStackTester) { + t.test_ranges([ + u8.!(Opcode.LOOP.code), EMPTY, // +1 + u8.!(Opcode.I32_CONST.code), 0, // +3 + u8.!(Opcode.IF.code), EMPTY, // +5 + u8.!(Opcode.BR.code), 1, // +7 + u8.!(Opcode.END.code), // +9 + u8.!(Opcode.NOP.code), // +10 + u8.!(Opcode.END.code) // +11 + ], [(1, 5, 7), (7, 7, 9), (9, 11, 12), (12, 12, 13)]); +} + +// The `nop` after `br 0` is dead, so no block owns it. +def test_range_br_outer(t: ControlStackTester) { + t.test_ranges([ + u8.!(Opcode.BLOCK.code), EMPTY, // +1 + u8.!(Opcode.BLOCK.code), EMPTY, // +3 + u8.!(Opcode.I32_CONST.code), 0, // +5 + u8.!(Opcode.BR_IF.code), 1, // +7 + u8.!(Opcode.BR.code), 0, // +9 + u8.!(Opcode.NOP.code), // +11 + u8.!(Opcode.END.code), // +12 + u8.!(Opcode.NOP.code), // +13 + u8.!(Opcode.END.code) // +14 + ], [(1, 7, 9), (9, 9, 11), (12, 13, 14), (14, 14, 15), (15, 15, 16)]); +} + +// The loop body ends in `br_table`, so no block owns the loop's `end`. +def test_range_br_table_loop(t: ControlStackTester) { + t.test_ranges([ + u8.!(Opcode.BLOCK.code), EMPTY, // +1 + u8.!(Opcode.LOOP.code), EMPTY, // +3 + u8.!(Opcode.I32_CONST.code), 0, // +5 + u8.!(Opcode.BR_TABLE.code), 2, 0, 1, 1, // +7 + u8.!(Opcode.END.code), // +12 + u8.!(Opcode.END.code) // +13 + ], [(1, 1, 3), (3, 7, 12), (13, 13, 14), (14, 14, 15)]); +} + +def test_range_br_if_chain(t: ControlStackTester) { + t.test_ranges([ + u8.!(Opcode.BLOCK.code), EMPTY, // +1 + u8.!(Opcode.I32_CONST.code), 0, // +3 + u8.!(Opcode.BR_IF.code), 0, // +5 + u8.!(Opcode.I32_CONST.code), 0, // +7 + u8.!(Opcode.BR_IF.code), 0, // +9 + u8.!(Opcode.NOP.code), // +11 + u8.!(Opcode.END.code) // +12 + ], [(1, 5, 7), (7, 9, 11), (11, 11, 12), (12, 12, 13), (13, 13, 14)]); +} + +// The then-arm branches out, so no block owns the `else`. +def test_range_if_then_dead(t: ControlStackTester) { + t.test_ranges([ + u8.!(Opcode.BLOCK.code), EMPTY, // +1 + u8.!(Opcode.I32_CONST.code), 0, // +3 + u8.!(Opcode.IF.code), EMPTY, // +5 + u8.!(Opcode.BR.code), 1, // +7 + u8.!(Opcode.ELSE.code), // +9 + u8.!(Opcode.NOP.code), // +10 + u8.!(Opcode.END.code), // +11 + u8.!(Opcode.END.code) // +12 + ], [(1, 5, 7), (7, 7, 9), (10, 10, 11), (11, 11, 12), (12, 12, 13), (13, 13, 14)]); +} + + +def test_range_return_in_if(t: ControlStackTester) { + t.test_ranges([ + u8.!(Opcode.I32_CONST.code), 0, // +1 + u8.!(Opcode.IF.code), EMPTY, // +3 + u8.!(Opcode.RETURN.code), // +5 + u8.!(Opcode.END.code), // +6 + u8.!(Opcode.NOP.code) // +7 + ], [(1, 3, 5), (5, 5, 6), (6, 7, 8), (8, 8, 9)]); } def test_br_table_succs(t: ControlStackTester) { From 472d5075c0b0a051d998dde7f25b4007313986b6 Mon Sep 17 00:00:00 2001 From: Matthew Schneider Date: Mon, 5 Oct 2026 18:06:56 -0400 Subject: [PATCH 05/16] Punt on the FIXMEs --- src/util/CfgBuilder.v3 | 1 - src/util/ControlStack.v3 | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/util/CfgBuilder.v3 b/src/util/CfgBuilder.v3 index 38349dce5..ce0e79f3b 100644 --- a/src/util/CfgBuilder.v3 +++ b/src/util/CfgBuilder.v3 @@ -8,7 +8,6 @@ class CfgBuilder { private var prev_pc: int = -1; // for recording `last_instr_pc` when ending a block new() { - // FIXME: with no {newBlock}, a reachable `try` merges into a null block and crashes in {link} ctl_stack.newLabel = newCfgBlock; ctl_stack.splitBlock = splitCfgBlock; ctl_stack.mergeBlock = mergeCfgBlock; diff --git a/src/util/ControlStack.v3 b/src/util/ControlStack.v3 index 9e9757fdc..fa3258c81 100644 --- a/src/util/ControlStack.v3 +++ b/src/util/ControlStack.v3 @@ -99,14 +99,14 @@ class ControlStack extends BytecodeVisitor { ctl.start_pos = pc; ctl.start_opcode = Opcode.CATCH; endBlock(this, block, -1); - block = newLabel(this); // FIXME: the handler is never bound + block = newLabel(this); } def visit_CATCH_ALL() { var ctl = stack.peek(); ctl.start_pos = pc; ctl.start_opcode = Opcode.CATCH_ALL; endBlock(this, block, -1); - block = newLabel(this); // FIXME: the handler is never bound + block = newLabel(this); } def visit_THROW(tag_code: u31) { setUnreachable(); From c53d36463dc108fdfff58b076fedeccdffc2c913 Mon Sep 17 00:00:00 2001 From: Matthew Schneider Date: Thu, 1 Oct 2026 13:09:14 -0400 Subject: [PATCH 06/16] Add a monitor for Ball-Larus path profiling --- src/monitors/PathProfilingMonitor.v3 | 144 ++ src/util/PathProfile.v3 | 416 +++++ test/monitors/demo_paths.wasm | Bin 0 -> 752 bytes test/monitors/demo_paths.wat | 184 +++ .../expected/allocs0.wasm.path-profile.out | 14 + .../branch_monitor.wasm.path-profile.out | 94 ++ .../branch_monitor0.wasm.path-profile.out | 26 + .../branch_monitor_gc.wasm.path-profile.out | 119 ++ .../call_monitor1.wasm.path-profile.out | 90 ++ .../expected/const_arg0.wasm.path-profile.out | 20 + .../coverage_monitor0.wasm.path-profile.out | 64 + .../coverage_monitor1.wasm.path-profile.out | 65 + .../expected/demo_paths.wasm.path-profile.out | 326 ++++ .../globals_monitor.wasm.path-profile.out | 54 + .../expected/load_store.wasm.path-profile.out | 8 + .../loop_monitor0.wasm.path-profile.out | 49 + .../loop_monitor1.wasm.path-profile.out | 61 + .../memory_monitor.wasm.path-profile.out | 22 + ...ath_profile_br_outer.wasm.path-profile.out | 65 + ...th_profile_dead_code.wasm.path-profile.out | 39 + ...th_profile_dead_pred.wasm.path-profile.out | 56 + ...th_profile_deep_nest.wasm.path-profile.out | 61 + ...th_profile_demo_only.wasm.path-profile.out | 41 + ...profile_diamond_only.wasm.path-profile.out | 27 + ...h_profile_dup_labels.wasm.path-profile.out | 34 + ...h_profile_empty_then.wasm.path-profile.out | 21 + .../path_profile_freq.wasm.path-profile.out | 180 +++ ...h_profile_freq_recur.wasm.path-profile.out | 55 + ..._profile_freq_shapes.wasm.path-profile.out | 153 ++ ..._profile_loop_nested.wasm.path-profile.out | 52 + ...th_profile_loop_skip.wasm.path-profile.out | 106 ++ ...e_loop_two_backedges.wasm.path-profile.out | 46 + ...h_profile_loop_types.wasm.path-profile.out | 37 + ...h_profile_many_paths.wasm.path-profile.out | 175 +++ ...h_profile_mixed_nest.wasm.path-profile.out | 89 ++ ...th_profile_one_armed.wasm.path-profile.out | 25 + .../path_profile_recur.wasm.path-profile.out | 34 + ...path_profile_returns.wasm.path-profile.out | 61 + ..._profile_select_call.wasm.path-profile.out | 53 + ...ath_profile_selfloop.wasm.path-profile.out | 46 + ...profile_selfloop_dup.wasm.path-profile.out | 24 + .../path_profile_shapes.wasm.path-profile.out | 54 + ...ath_profile_straight.wasm.path-profile.out | 44 + .../path_profile_table1.wasm.path-profile.out | 22 + .../path_profile_table2.wasm.path-profile.out | 33 + ...rofile_table_default.wasm.path-profile.out | 34 + .../path_profile_trap.wasm.path-profile.out | 20 + ...h_profile_wide_merge.wasm.path-profile.out | 68 + ...th_profile_wide_only.wasm.path-profile.out | 57 + .../profile_bytecode.wasm.path-profile.out | 281 ++++ .../profile_bytecode_ai.wasm.path-profile.out | 1394 +++++++++++++++++ .../profile_monitor0.wasm.path-profile.out | 60 + test/monitors/path_profile_br_outer.wasm | Bin 0 -> 177 bytes test/monitors/path_profile_br_outer.wat | 57 + test/monitors/path_profile_dead_code.wasm | Bin 0 -> 138 bytes test/monitors/path_profile_dead_code.wat | 32 + test/monitors/path_profile_dead_pred.wasm | Bin 0 -> 154 bytes test/monitors/path_profile_dead_pred.wat | 42 + test/monitors/path_profile_deep_nest.wasm | Bin 0 -> 165 bytes test/monitors/path_profile_deep_nest.wat | 30 + test/monitors/path_profile_demo_only.wasm | Bin 0 -> 93 bytes test/monitors/path_profile_demo_only.wat | 23 + test/monitors/path_profile_diamond_only.wasm | Bin 0 -> 73 bytes test/monitors/path_profile_diamond_only.wat | 11 + test/monitors/path_profile_dup_labels.wasm | Bin 0 -> 83 bytes test/monitors/path_profile_dup_labels.wat | 17 + test/monitors/path_profile_empty_then.wasm | Bin 0 -> 70 bytes test/monitors/path_profile_empty_then.wat | 10 + test/monitors/path_profile_freq.wasm | Bin 0 -> 214 bytes test/monitors/path_profile_freq.wat | 43 + test/monitors/path_profile_freq_recur.wasm | Bin 0 -> 130 bytes test/monitors/path_profile_freq_recur.wat | 26 + test/monitors/path_profile_freq_shapes.wasm | Bin 0 -> 412 bytes test/monitors/path_profile_freq_shapes.wat | 86 + test/monitors/path_profile_loop_nested.wasm | Bin 0 -> 119 bytes test/monitors/path_profile_loop_nested.wat | 31 + test/monitors/path_profile_loop_skip.wasm | Bin 0 -> 248 bytes test/monitors/path_profile_loop_skip.wat | 59 + .../path_profile_loop_two_backedges.wasm | Bin 0 -> 96 bytes .../path_profile_loop_two_backedges.wat | 32 + test/monitors/path_profile_loop_types.wasm | Bin 0 -> 91 bytes test/monitors/path_profile_loop_types.wat | 30 + test/monitors/path_profile_many_paths.wasm | Bin 0 -> 333 bytes test/monitors/path_profile_many_paths.wat | 61 + test/monitors/path_profile_mixed_nest.wasm | Bin 0 -> 249 bytes test/monitors/path_profile_mixed_nest.wat | 55 + test/monitors/path_profile_one_armed.wasm | Bin 0 -> 74 bytes test/monitors/path_profile_one_armed.wat | 11 + test/monitors/path_profile_recur.wasm | Bin 0 -> 91 bytes test/monitors/path_profile_recur.wat | 13 + test/monitors/path_profile_returns.wasm | Bin 0 -> 148 bytes test/monitors/path_profile_returns.wat | 29 + test/monitors/path_profile_select_call.wasm | Bin 0 -> 175 bytes test/monitors/path_profile_select_call.wat | 33 + test/monitors/path_profile_selfloop.wasm | Bin 0 -> 140 bytes test/monitors/path_profile_selfloop.wat | 38 + test/monitors/path_profile_selfloop_dup.wasm | Bin 0 -> 88 bytes test/monitors/path_profile_selfloop_dup.wat | 29 + test/monitors/path_profile_shapes.wasm | Bin 0 -> 123 bytes test/monitors/path_profile_shapes.wat | 13 + test/monitors/path_profile_straight.wasm | Bin 0 -> 127 bytes test/monitors/path_profile_straight.wat | 26 + test/monitors/path_profile_table1.wasm | Bin 0 -> 73 bytes test/monitors/path_profile_table1.wat | 17 + test/monitors/path_profile_table2.wasm | Bin 0 -> 94 bytes test/monitors/path_profile_table2.wat | 25 + test/monitors/path_profile_table_default.wasm | Bin 0 -> 92 bytes test/monitors/path_profile_table_default.wat | 26 + test/monitors/path_profile_trap.wasm | Bin 0 -> 60 bytes test/monitors/path_profile_trap.wat | 6 + test/monitors/path_profile_wide_merge.wasm | Bin 0 -> 190 bytes test/monitors/path_profile_wide_merge.wat | 46 + test/monitors/path_profile_wide_only.wasm | Bin 0 -> 122 bytes test/monitors/path_profile_wide_only.wat | 32 + test/monitors/pathbug_adjacent_brif.wasm | Bin 0 -> 71 bytes test/monitors/pathbug_adjacent_brif.wat | 30 + test/monitors/pathbug_br_on_cast.wat | 42 + test/monitors/pathbug_br_on_null.wat | 35 + test/monitors/pathbug_deadend_enum.wasm | Bin 0 -> 236 bytes test/monitors/pathbug_deadend_enum.wat | 53 + test/monitors/pathbug_names_a.wasm | Bin 0 -> 60 bytes test/monitors/pathbug_names_a.wat | 19 + test/monitors/pathbug_names_b.wasm | Bin 0 -> 77 bytes test/monitors/pathbug_names_b.wat | 5 + test/monitors/pathbug_path_explosion.wasm | Bin 0 -> 300 bytes test/monitors/pathbug_path_explosion.wat | 51 + test/monitors/pathbug_trap_counted.wasm | Bin 0 -> 60 bytes test/monitors/pathbug_trap_counted.wat | 23 + test/monitors/pathbug_try.wat | 34 + test/monitors/test.sh | 1 + test/monitors/update-expected.sh | 1 + 131 files changed, 6586 insertions(+) create mode 100644 src/monitors/PathProfilingMonitor.v3 create mode 100644 src/util/PathProfile.v3 create mode 100644 test/monitors/demo_paths.wasm create mode 100644 test/monitors/demo_paths.wat create mode 100644 test/monitors/expected/allocs0.wasm.path-profile.out create mode 100644 test/monitors/expected/branch_monitor.wasm.path-profile.out create mode 100644 test/monitors/expected/branch_monitor0.wasm.path-profile.out create mode 100644 test/monitors/expected/branch_monitor_gc.wasm.path-profile.out create mode 100644 test/monitors/expected/call_monitor1.wasm.path-profile.out create mode 100644 test/monitors/expected/const_arg0.wasm.path-profile.out create mode 100644 test/monitors/expected/coverage_monitor0.wasm.path-profile.out create mode 100644 test/monitors/expected/coverage_monitor1.wasm.path-profile.out create mode 100644 test/monitors/expected/demo_paths.wasm.path-profile.out create mode 100644 test/monitors/expected/globals_monitor.wasm.path-profile.out create mode 100644 test/monitors/expected/load_store.wasm.path-profile.out create mode 100644 test/monitors/expected/loop_monitor0.wasm.path-profile.out create mode 100644 test/monitors/expected/loop_monitor1.wasm.path-profile.out create mode 100644 test/monitors/expected/memory_monitor.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_br_outer.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_dead_code.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_dead_pred.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_deep_nest.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_demo_only.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_diamond_only.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_dup_labels.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_empty_then.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_freq.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_freq_recur.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_freq_shapes.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_loop_nested.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_loop_skip.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_loop_two_backedges.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_loop_types.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_many_paths.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_mixed_nest.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_one_armed.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_recur.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_returns.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_select_call.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_selfloop.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_selfloop_dup.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_shapes.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_straight.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_table1.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_table2.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_table_default.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_trap.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_wide_merge.wasm.path-profile.out create mode 100644 test/monitors/expected/path_profile_wide_only.wasm.path-profile.out create mode 100644 test/monitors/expected/profile_bytecode.wasm.path-profile.out create mode 100644 test/monitors/expected/profile_bytecode_ai.wasm.path-profile.out create mode 100644 test/monitors/expected/profile_monitor0.wasm.path-profile.out create mode 100644 test/monitors/path_profile_br_outer.wasm create mode 100644 test/monitors/path_profile_br_outer.wat create mode 100644 test/monitors/path_profile_dead_code.wasm create mode 100644 test/monitors/path_profile_dead_code.wat create mode 100644 test/monitors/path_profile_dead_pred.wasm create mode 100644 test/monitors/path_profile_dead_pred.wat create mode 100644 test/monitors/path_profile_deep_nest.wasm create mode 100644 test/monitors/path_profile_deep_nest.wat create mode 100644 test/monitors/path_profile_demo_only.wasm create mode 100644 test/monitors/path_profile_demo_only.wat create mode 100644 test/monitors/path_profile_diamond_only.wasm create mode 100644 test/monitors/path_profile_diamond_only.wat create mode 100644 test/monitors/path_profile_dup_labels.wasm create mode 100644 test/monitors/path_profile_dup_labels.wat create mode 100644 test/monitors/path_profile_empty_then.wasm create mode 100644 test/monitors/path_profile_empty_then.wat create mode 100644 test/monitors/path_profile_freq.wasm create mode 100644 test/monitors/path_profile_freq.wat create mode 100644 test/monitors/path_profile_freq_recur.wasm create mode 100644 test/monitors/path_profile_freq_recur.wat create mode 100644 test/monitors/path_profile_freq_shapes.wasm create mode 100644 test/monitors/path_profile_freq_shapes.wat create mode 100644 test/monitors/path_profile_loop_nested.wasm create mode 100644 test/monitors/path_profile_loop_nested.wat create mode 100644 test/monitors/path_profile_loop_skip.wasm create mode 100644 test/monitors/path_profile_loop_skip.wat create mode 100644 test/monitors/path_profile_loop_two_backedges.wasm create mode 100644 test/monitors/path_profile_loop_two_backedges.wat create mode 100644 test/monitors/path_profile_loop_types.wasm create mode 100644 test/monitors/path_profile_loop_types.wat create mode 100644 test/monitors/path_profile_many_paths.wasm create mode 100644 test/monitors/path_profile_many_paths.wat create mode 100644 test/monitors/path_profile_mixed_nest.wasm create mode 100644 test/monitors/path_profile_mixed_nest.wat create mode 100644 test/monitors/path_profile_one_armed.wasm create mode 100644 test/monitors/path_profile_one_armed.wat create mode 100644 test/monitors/path_profile_recur.wasm create mode 100644 test/monitors/path_profile_recur.wat create mode 100644 test/monitors/path_profile_returns.wasm create mode 100644 test/monitors/path_profile_returns.wat create mode 100644 test/monitors/path_profile_select_call.wasm create mode 100644 test/monitors/path_profile_select_call.wat create mode 100644 test/monitors/path_profile_selfloop.wasm create mode 100644 test/monitors/path_profile_selfloop.wat create mode 100644 test/monitors/path_profile_selfloop_dup.wasm create mode 100644 test/monitors/path_profile_selfloop_dup.wat create mode 100644 test/monitors/path_profile_shapes.wasm create mode 100644 test/monitors/path_profile_shapes.wat create mode 100644 test/monitors/path_profile_straight.wasm create mode 100644 test/monitors/path_profile_straight.wat create mode 100644 test/monitors/path_profile_table1.wasm create mode 100644 test/monitors/path_profile_table1.wat create mode 100644 test/monitors/path_profile_table2.wasm create mode 100644 test/monitors/path_profile_table2.wat create mode 100644 test/monitors/path_profile_table_default.wasm create mode 100644 test/monitors/path_profile_table_default.wat create mode 100644 test/monitors/path_profile_trap.wasm create mode 100644 test/monitors/path_profile_trap.wat create mode 100644 test/monitors/path_profile_wide_merge.wasm create mode 100644 test/monitors/path_profile_wide_merge.wat create mode 100644 test/monitors/path_profile_wide_only.wasm create mode 100644 test/monitors/path_profile_wide_only.wat create mode 100644 test/monitors/pathbug_adjacent_brif.wasm create mode 100644 test/monitors/pathbug_adjacent_brif.wat create mode 100644 test/monitors/pathbug_br_on_cast.wat create mode 100644 test/monitors/pathbug_br_on_null.wat create mode 100644 test/monitors/pathbug_deadend_enum.wasm create mode 100644 test/monitors/pathbug_deadend_enum.wat create mode 100644 test/monitors/pathbug_names_a.wasm create mode 100644 test/monitors/pathbug_names_a.wat create mode 100644 test/monitors/pathbug_names_b.wasm create mode 100644 test/monitors/pathbug_names_b.wat create mode 100644 test/monitors/pathbug_path_explosion.wasm create mode 100644 test/monitors/pathbug_path_explosion.wat create mode 100644 test/monitors/pathbug_trap_counted.wasm create mode 100644 test/monitors/pathbug_trap_counted.wat create mode 100644 test/monitors/pathbug_try.wat diff --git a/src/monitors/PathProfilingMonitor.v3 b/src/monitors/PathProfilingMonitor.v3 new file mode 100644 index 000000000..ec8d3beae --- /dev/null +++ b/src/monitors/PathProfilingMonitor.v3 @@ -0,0 +1,144 @@ +// Implements a monitor that profiles dynamic execution paths through functions. +def monitor_ = MonitorRegistry.add( + "path-profile", "Profiles dynamic execution paths through functions.", + PathProfilingMonitor.new()); + +class PathProfilingMonitor extends Monitor { + def entries = Vector.new(); + + def onParse(m: Module, err: ErrorGen) { + for (j < m.functions.length) { + def func: FuncDecl = m.functions[j]; + if (func.imp != null) continue; // skip imported functions + def e = PathProfileEntry.new(m, func, PathProfile.new(func)); + entries.put(e); + if (e.profile.reason != null) continue; + e.counts = PathCounts.new(e.profile.numPaths); + // a probe where an arm changes {r} or the counts, or is a self-loop + for (site in e.profile.sites) { + var loop = false; + for (te in site.edges) if (te.source == te.target) loop = true; + if (!site.acts() && !loop) continue; + def probe = PathMonitorProbe.new(site, e.counts); + Instrumentation.insertLocalProbe(m, func.func_index, site.pc, probe); + e.probes.put(probe); + } + } + } + def onFinish(i: Instance, r: Result) { + for (j < entries.length) { + def e = entries[j], p = e.profile; + out.put1("func %q", e.func.render(e.module.names, _)); + if (p.reason != null) { + out.put1(": not profiled, %s", p.reason).ln(); + continue; + } + out.ln(); + printCfg(out, p.blocks); + // each call site lists only the paths its calls took + for (s < e.counts.sites.length) { + def site = e.counts.sites[s], counts = e.counts.counts[s]; + match (site.0) { + null => out.puts(" caller engine"); + x: WasmFunction => out.put2(" caller %q +%d", x.renderShort, site.1); + x: HostFunction => out.put1(" caller %q", x.render); + } + out.ln(); + for (k < counts.length) { + if (counts[k] > 0) out.put2(" counts[%d] = %d", k, counts[k]).ln(); + } + } + // each path number with the route it denotes, so counts[k] can be read back to a route + for (k < p.numPaths) { + out.put2(" counts[%d] = %d", k, e.counts.total(k)).ln(); + out.puts(" path:"); + p.renderPath(k, out); + out.ln(); + } + // (Section 4.1) a self-loop is counted, not path-profiled. One line per branch arm that + // reaches one. + for (i < e.probes.length) { + def probe = e.probes[i], edges = probe.site.edges; + for (arm < edges.length) { + def te = edges[arm]; + if (te.source != te.target) continue; + out.put2(" selfloop[block %d +%d", te.source.id, te.source.last_instr_pc); + out.put2(" arm %d] = %d", arm, probe.loops[arm]).ln(); + } + } + } + } +} + +// {module} is per-entry, not a monitor field: onParse runs once per module while {entries} +// accumulates across all of them, so a shared field would render every name against the last one +private class PathProfileEntry(module: Module, func: FuncDecl, profile: PathProfile) { + var counts: PathCounts; + def probes = Vector.new(); +} +// A function's path counts, kept separately for each call site: the calling function and the pc +// of its call, where a host caller has no pc and the engine is a null caller. +private class PathCounts(numPaths: int) { + def sites = Vector<(Function, int)>.new(); + def counts = Vector>.new(); + + def inc(accessor: FrameAccessor, path: int) { + var site: (Function, int); + match (accessor.caller()) { + None => site = (null, -1); + Wasm(func, pc, frame) => site = (func, pc); + Host(func, frame) => site = (func, -1); + } + for (i < sites.length) { + if (sites[i] == site) { + counts[i][path]++; + return; + } + } + sites.put(site); + def c = Array.new(numPaths); + c[path] = 1; + counts.put(c); + } + // the count for {path} across every call site + def total(path: int) -> int { + var sum = 0; + for (i < counts.length) sum += counts[i][path]; + return sum; + } +} +// Runs the action of the arm its site's decision takes against the caller's counts, and counts +// the trips of an arm that is a self-loop, whose action is nothing. +private class PathMonitorProbe(site: PathSite, counts: PathCounts) extends Probe { + def loops = Array.new(site.edges.length); + def fire(loc: DynamicLoc) -> ProbeAction { + def accessor = loc.frame.getFrameAccessor(); + def arm = site.selectArm(accessor), te = site.edges[arm]; + if (te.source == te.target) loops[arm]++; + def path = site.apply(accessor, arm); + if (path >= 0) counts.inc(accessor, path); + return ProbeAction.Continue; + } +} + +// CFG + val(e) printing +def printCfg(out: TraceBuilder, blocks: Vector) { + for (i < blocks.length) printBlock(out, blocks[i]); +} +def printBlock(out: TraceBuilder, b: ProfBlock) { + // the range of bytes this block owns; its outgoing edges are probed at {last_instr_pc} + out.put3(" block %d +%d ... %d", b.id, b.bind_pos, b.end).ln(); + Lists.apply(b.outgoing, fun (e: ProfEdge) { + // Increment(e) is defined for chords only; a tree edge carries none + if (e.isChord) { + out.put3(" -> block %d val=%d inc=%d", e.target.id, e.val, e.increment); + } else { + out.put2(" -> block %d val=%d", e.target.id, e.val); + } + // Section 4 edge kinds: without these a dummy is indistinguishable from a real val=0 edge + if (e.isDummy) out.puts(" DUMMY"); + // skip backedges to convert CFG to DAG (Section 4) + if (e.isBackedge) out.puts(" BACKEDGE"); + out.ln(); + }); +} diff --git a/src/util/PathProfile.v3 b/src/util/PathProfile.v3 new file mode 100644 index 000000000..0fa911422 --- /dev/null +++ b/src/util/PathProfile.v3 @@ -0,0 +1,416 @@ +// Copyright 2026 Wizard authors. All rights reserved. +// See LICENSE for details of Apache 2.0 license. + +// The Ball-Larus path profile of one function, shared by the path-profile monitor and +// --trace-paths: the number of each acyclic path, and what each edge does to the path register +// {r} and the counts. Section and figure numbers refer to "Efficient Path Profiling" (Ball and +// Larus, MICRO 1996). +class PathProfile(func: FuncDecl) { + def MAX_PATHS = 1 << 16; // more paths than this are not worth a counter each + var reason: string; // why the function is not profiled, or null if it is + var numPaths: int; // the paths are numbered 0 .. numPaths - 1 + var sites: Array; // one per block with outgoing edges, where its probe goes + var entry: ProfBlock; + var exit: ProfBlock; + def blocks = Vector.new(); // a copy of the function's CFG, in DFS preorder + private def tree = Vector.new(); // the spanning tree's edges + + new() { + // the CFG builder faults on exception handlers, and a frame that is suspended does not + // keep its frame variables once compiled code is involved + for (bi = BytecodeIterator.new().reset(func); bi.more(); bi.next()) { + match (bi.current()) { + TRY, TRY_TABLE => { + reason = "has exception handlers"; + return; + } + CONT_NEW, CONT_BIND, SUSPEND, RESUME, RESUME_THROW, RESUME_THROW_REF, SWITCH => { + reason = "uses stack switching"; + return; + } + _ => ; + } + } + // the function's final `end` is its last byte; the block that owns it is EXIT + def last = func.cur_bytecode.length - 1, originals = Vector.new(); + entry = copy(reach(CfgBuilder.new().build(func), false).0, last, originals); + for (i < originals.length) originals[i].mark = 0; + // a function whose end no path reaches still needs EXIT as a vertex + if (exit == null) { + exit = ProfBlock.new(-1, last, last + 1, last); + blocks.put(exit); + } + addReturnEdges(); + + addDummies(); + if (!assignVals()) return; + numPaths = entry.numPaths; + def chords = kruskals(); + computeIncrements(chords); + placeActions(chords); + placeSites(); + } + // Copies {b} and every block it reaches, recording each copy's index in the original's {mark} + // so that a block is copied once. Edges into empty blocks are followed to the block they reach. + private def copy(b: CfgBlock, last: int, originals: Vector) -> ProfBlock { + if (b.mark > 0) return blocks[b.mark - 1]; + def n = ProfBlock.new(b.id, b.bind_pos, b.end, b.last_instr_pc); + blocks.put(n); + originals.put(b); + b.mark = blocks.length; + if (b.bind_pos == last) exit = n; + // link prepends, so linking the edges in reverse keeps {outgoing}'s order, which gives the arms + var rev: List; + for (l = b.outgoing; l != null; l = l.tail) rev = List.new(l.head, rev); + for (l = rev; l != null; l = l.tail) { + def t = reach(l.head.target, l.head.isBackedge); + link(l.head.pc, n, copy(t.0, last, originals)).isBackedge = t.1; + } + return n; + } + // Follows {b} through empty blocks, which own no instruction and only pass control on, to the + // block it reaches, and whether a backedge was crossed on the way. + private def reach(b: CfgBlock, back: bool) -> (CfgBlock, bool) { + while (b.last_instr_pc < 0 && b.outgoing != null && b.outgoing.tail == null) { + back = back || b.outgoing.head.isBackedge; + b = b.outgoing.head.target; + } + return (b, back); + } + // every path must end at EXIT, so route all `return`s to EXIT + private def addReturnEdges() { + def bi = BytecodeIterator.new().reset(func); + for (i < blocks.length) { + def b = blocks[i]; + if (b.outgoing != null || b == exit || b.last_instr_pc < 0) continue; + bi.at(b.last_instr_pc, -1); + match (bi.current()) { + RETURN, RETURN_CALL, RETURN_CALL_INDIRECT, RETURN_CALL_REF => link(b.last_instr_pc, b, exit); + _ => ; + } + } + } + // prepends, so {outgoing} is in reverse branch-label order + private def link(pc: int, from: ProfBlock, to: ProfBlock) -> ProfEdge { + def e = ProfEdge.new(pc, from, to); + from.outgoing = List.new(e, from.outgoing); + to.incoming = List.new(e, to.incoming); + return e; + } + // (Section 3.2) regenerates the route from its path number: at each vertex, the edge whose + // range of path sums holds what remains. Blocks print as their start pc; a dummy edge is a + // piece of a loop and prints as "..". + def renderPath(k: int, out: TraceBuilder) { + var v = entry, dummy = false; + while (v != exit) { + var pick: ProfEdge; + for (l = v.outgoing; l != null; l = l.tail) { + def e = l.head; + if (!e.isBackedge && e.val <= k && k < e.val + e.target.numPaths) pick = e; + } + if (pick == null) return; + if (!pick.isDummy || v != entry) out.put1(" +%d", v.bind_pos); + if (pick.isDummy) out.puts(" .."); + dummy = pick.isDummy; + k -= pick.val; + v = pick.target; + } + if (!dummy) out.put1(" +%d", v.bind_pos); + } + + // (Section 4) a dummy ENTRY->v for each backedge target and a dummy w->EXIT for each source, + // which stand in for the backedge and make the graph acyclic + private def addDummies() { + for (i < blocks.length) { + def b = blocks[i]; + for (l = b.incoming; l != null; l = l.tail) { + def e = l.head; + if (e.isBackedge && e.source != b && b != entry) { + b.entryDummy = link(-1, entry, b); + b.entryDummy.isDummy = true; + break; + } + } + for (l = b.outgoing; l != null; l = l.tail) { + def e = l.head; + if (e.isBackedge && e.target != b && b != exit) { + b.exitDummy = link(-1, b, exit); + b.exitDummy.isDummy = true; + break; + } + } + } + } + // (Figure 5) Val(e) for each edge, so that no two paths have the same sum; false if there are + // too many paths to count + private def assignVals() -> bool { + def order = Vector.new(); + postorder(entry, order); + for (i < order.length) order[i].mark = 0; + for (i < order.length) { + def v = order[i]; + if (v == exit) { + v.numPaths = 1; + continue; + } + for (l = v.outgoing; l != null; l = l.tail) { + if (l.head.isBackedge) continue; + // another arm to a target already reached runs the same code, so it is the same path + def twin = sameTarget(v.outgoing, l); + if (twin != null) { + l.head.val = twin.val; + continue; + } + l.head.val = v.numPaths; + v.numPaths += l.head.target.numPaths; + if (v.numPaths > MAX_PATHS) { + reason = "more paths than the counter limit"; + return false; + } + } + } + if (entry.numPaths == 0) { + reason = "no acyclic path reaches the exit"; + return false; + } + return true; + } + // Reverse topological order of the graph without its backedges. A walk that followed them + // could reach a loop header from inside the loop and finish it before the blocks ahead of it. + private def postorder(v: ProfBlock, order: Vector) { + if (v.mark != 0) return; + v.mark = 1; + for (l = v.outgoing; l != null; l = l.tail) { + if (!l.head.isBackedge) postorder(l.head.target, order); + } + order.put(v); + } + // (Section 3.3) a spanning tree by Kruskal's algorithm, seeded with EXIT->ENTRY; returns the + // chords left over, which are the edges to instrument + private def kruskals() -> Vector { + def chords = Vector.new(); + if (exit != entry) exit.parent = entry; + tree.put(ProfEdge.new(-1, exit, entry)); + for (i < blocks.length) { + for (l = blocks[i].outgoing; l != null; l = l.tail) { + def e = l.head; + if (e.isBackedge) continue; + def u = e.source.findSet(), v = e.target.findSet(); + if (u == v) { + e.isChord = true; + chords.put(e); + } else { + v.parent = u; + tree.put(e); + } + } + } + return chords; + } + // (Figure 4) Increment(e) for each chord, by a walk of the spanning tree + private def computeIncrements(chords: Vector) { + walk(0, entry, null, chords); + for (i < chords.length) chords[i].increment += chords[i].val; + } + private def walk(events: int, v: ProfBlock, e: ProfEdge, chords: Vector) { + for (i < tree.length) { + def f = tree[i]; + if (f != e && v == f.target) walk(dir(e, f) * events + f.val, f.source, f, chords); + } + for (i < tree.length) { + def f = tree[i]; + if (f != e && v == f.source) walk(dir(e, f) * events + f.val, f.target, f, chords); + } + for (i < chords.length) { + def f = chords[i]; + if (v == f.source || v == f.target) f.increment += dir(e, f) * events; + } + } + // 1 if {e} and {f}, which share an endpoint, point the same way along the walk, else -1 + private def dir(e: ProfEdge, f: ProfEdge) -> int { + if (e == null) return 1; + return if(e.source == f.target || e.target == f.source, 1, -1); + } + // (Figure 8) the register initializations, the count increments, and the register increments, + // then (Section 4) each backedge, which counts a path and starts the next + private def placeActions(chords: Vector) { + def fromEntry = Vector.new(), fromExit = Vector.new(); + fromEntry.put(entry); + Vectors.drain(fromEntry, 0, fun (v: ProfBlock) { + for (l = v.outgoing; l != null; l = l.tail) { + def e = l.head; + if (e.isBackedge || e.isDummy) continue; + if (e.isChord) e.action = ProfAction.RSet(e.increment); + else if (e.target.incoming.tail == null) fromEntry.put(e.target); + else e.action = ProfAction.RSet(0); + } + }); + fromExit.put(exit); + Vectors.drain(fromExit, 0, fun (w: ProfBlock) { + for (l = w.incoming; l != null; l = l.tail) { + def e = l.head; + if (e.isBackedge || e.isDummy) continue; + if (e.isChord) { + if (ProfAction.RSet.?(e.action)) e.action = ProfAction.CountConst(e.increment); + else e.action = ProfAction.CountRPlus(e.increment); + } else if (e.source.outgoing.tail == null) { + fromExit.put(e.source); + } else { + e.action = ProfAction.CountRPlus(0); + } + } + }); + for (i < chords.length) { + def e = chords[i]; + if (!e.isDummy && ProfAction.Nothing.?(e.action)) e.action = ProfAction.RAdd(e.increment); + } + for (i < blocks.length) { + for (l = blocks[i].outgoing; l != null; l = l.tail) { + def e = l.head; + // (Section 4.1) a self-loop repeats a vertex without changing the path + if (!e.isBackedge || e.source == e.target) continue; + def ex = e.source.exitDummy, en = e.target.entryDummy; + e.action = ProfAction.CountReset(if(ex != null, ex.increment), if(en != null, en.increment)); + } + } + } + // An earlier real edge in {outgoing} to the same target as {l}'s, or null; a dummy stands for + // a different path, so it never has one. + private def sameTarget(outgoing: List, l: List) -> ProfEdge { + if (l.head.isDummy) return null; + for (k = outgoing; k != l; k = k.tail) { + def e = k.head; + if (!e.isBackedge && !e.isDummy && e.target == l.head.target) return e; + } + return null; + } + // A site for each block with a real outgoing edge, at the block's last instruction, with {r} + // in a new frame variable so that every activation has its own. + private def placeSites() { + def rvar = if(func.frame_var_tags != null, func.frame_var_tags.length); + def tag = [BpTypeCode.I32.code]; + func.frame_var_tags = if(func.frame_var_tags == null, tag, Arrays.concat(func.frame_var_tags, tag)); + def bi = BytecodeIterator.new().reset(func); + def placed = Vector.new(); + for (i < blocks.length) { + def b = blocks[i]; + // dummy edges are linked last, so they are first in {outgoing} + var real = b.outgoing; + while (real != null && real.head.isDummy) real = real.tail; + if (real == null) continue; + def n = Lists.length(real); + var kind = PathProbeKind.DIRECT, imm: BrOnCastImm; + if (n > 1) { + bi.at(b.last_instr_pc, -1); + match (bi.current()) { + BR_TABLE => kind = PathProbeKind.TABLE; + BR_ON_NULL => kind = PathProbeKind.NULL; + BR_ON_NON_NULL => kind = PathProbeKind.NONNULL; + BR_ON_CAST => { + imm = bi.immptr().read_BrOnCastImm(); + kind = PathProbeKind.CAST; + } + BR_ON_CAST_FAIL => { + imm = bi.immptr().read_BrOnCastImm(); + kind = PathProbeKind.CAST_FAIL; + } + _ => kind = PathProbeKind.IF; + } + } + // each position in {outgoing} is an arm, and {outgoing} is in reverse label order + def edges = Array.new(n); + def table = kind == PathProbeKind.TABLE; + var pos = 0; + for (l = real; l != null; l = l.tail) { + edges[if(table, n - 1 - pos, pos)] = l.head; + pos++; + } + placed.put(PathSite.new(b.last_instr_pc, rvar, kind, imm, edges)); + } + sites = placed.extract(); + } +} + +// How the probe's decision selects its arm, so the compiler can emit the selection inline. +enum PathProbeKind { DIRECT, IF, TABLE, NULL, NONNULL, CAST, CAST_FAIL } + +// A block's decision at {pc}, its last instruction: {edges} is indexed by the arm it takes, and +// {r}, the path sum so far, lives in the frame variable {rvar}. +class PathSite(pc: int, rvar: int, kind: PathProbeKind, imm: BrOnCastImm, edges: Array) { + // whether any arm changes {r} or the counts + def acts() -> bool { + for (e in edges) if (!ProfAction.Nothing.?(e.action)) return true; + return false; + } + // The arm the decision is about to take, read from its operand before it executes. + def selectArm(accessor: FrameAccessor) -> int { + if (kind == PathProbeKind.DIRECT) return 0; // one outgoing edge, so there is no operand + def v = accessor.getOperand(0); + match (kind) { + TABLE => { + def u = Values.unbox_u(v); + return if(u >= u32.!(edges.length), edges.length - 1, int.!(u)); + } + NULL => return if(Values.isNull(v), 1, 0); + NONNULL => return if(Values.isNull(v), 0, 1); + CAST => return if(Runtime.cast(accessor.func().instance, imm.null2(), imm.ht2, v), 1, 0); + CAST_FAIL => return if(Runtime.cast(accessor.func().instance, imm.null2(), imm.ht2, v), 0, 1); + _ => return if(Values.unbox_u(v) != 0, 1, 0); + } + } + // Applies the action of {arm} to {r}; returns the path it completes, or -1 if none. + def apply(accessor: FrameAccessor, arm: int) -> int { + match (edges[arm].action) { + Nothing => ; + RSet(value) => setR(accessor, value); + RAdd(value) => setR(accessor, getR(accessor) + value); + CountConst(value) => return value; + CountRPlus(value) => return getR(accessor) + value; + CountReset(add, set) => { + def path = getR(accessor) + add; + setR(accessor, set); + return path; + } + } + return -1; + } + private def getR(accessor: FrameAccessor) -> int { + return Values.unbox_i(accessor.getFrameVar(rvar)); + } + private def setR(accessor: FrameAccessor, v: int) { + accessor.getWriter().setFrameVar(rvar, Values.box_i(v)); + } +} + +// What an edge does to the path register {r} and the path counts when it is taken. +type ProfAction { + case Nothing; + case RSet(value: int); // r = value + case RAdd(value: int); // r += value + case CountConst(value: int); // counts[value]++ + case CountRPlus(value: int); // counts[r + value]++ + case CountReset(add: int, set: int); // counts[r + add]++; r = set +} + +// A copy of one block of the function's CFG: the original's id and bytecode range [bind_pos ... end). +class ProfBlock(id: int, bind_pos: int, end: int, last_instr_pc: int) { + var outgoing: List; + var incoming: List; + var mark: int; + var numPaths: int; // NumPaths(v): paths from here to EXIT + var entryDummy: ProfEdge; // ENTRY->this, if this is a backedge target + var exitDummy: ProfEdge; // this->EXIT, if this is a backedge source + var parent: ProfBlock; // the spanning tree's disjoint-set forest; null at a root + + def findSet() -> ProfBlock { + return if(parent == null, this, parent.findSet()); + } +} +class ProfEdge(pc: int, source: ProfBlock, target: ProfBlock) { + var isBackedge: bool; // branches to an enclosing loop label + var val: int; // Val(e) + var increment: int; // Increment(e), for a chord + var isChord: bool; // not in the spanning tree + var isDummy: bool; // stands in for a backedge; has no instruction + var action: ProfAction; +} diff --git a/test/monitors/demo_paths.wasm b/test/monitors/demo_paths.wasm new file mode 100644 index 0000000000000000000000000000000000000000..f1a77cd76aff09a01039df0756eb9425e973d583 GIT binary patch literal 752 zcmZ8eORk$h5bT~|z;+Xdg%^=mLli07EVJ}nAXmWlgXCcuc=AVY$q90jTqO5Mq+BD_ z19^!8>aL#ZuBHGwTstNr-nu=}o<|KH_h!^=M+KV;v9UH`gDd;Wl5~f*Craw38;G;I zJ#<4~lUdip{djJwKZITO&GpvQRr{He_3hF={nK>v+}2HXeQs|LkCZOX!|+sH?;T}% zEWtmja8ca*2dr;z?a&|2)$u~h#qm;g&E-o&tJSweelP_pI55H>EFwWrVYVQVv~Lwy zK|2xRNqfoE186S-Po#ETTmWq>bYdB98 zi^BpBDwgUQiHen0bCNBY-E@0eTm2*QdcJe=l#GF0Fd}c6M;v#7O9P@b;yD?;39FMs z-kd}>abD?3 1 path; x=1,y=0 -> 1 path; x=1,y=1 -> 1 path. + (func $nested_if (export "nested_if") (param $x i32) (param $y i32) (result i32) + (if (result i32) (local.get $x) + (then + (if (result i32) (local.get $y) + (then (i32.const 1)) + (else (i32.const 2)) + ) + ) + (else (i32.const 3)) + ) + ) + ;; three sequential ifs sharing one running value: 2^3 = 8 distinct paths. + (func $triple_diamond (export "triple_diamond") (param $x i32) (param $y i32) (param $z i32) (result i32) + (local $r i32) + (if (local.get $x) + (then (local.set $r (i32.const 1))) + (else (local.set $r (i32.const 2))) + ) + (if (local.get $y) + (then (local.set $r (i32.add (local.get $r) (i32.const 10)))) + (else (local.set $r (i32.add (local.get $r) (i32.const 20)))) + ) + (if (result i32) (local.get $z) + (then (i32.add (local.get $r) (i32.const 100))) + (else (i32.add (local.get $r) (i32.const 200))) + ) + ) + ;; br_table with 6 labels (5 indexed + 1 default): indices 0-4 map 1:1, index >= 5 collapses + ;; onto the default (L5). Exercises a wider dispatch chain than $demo's 4-label table. + (func $wide_switch (export "wide_switch") (param $x i32) + (block $L5 + (block $L4 + (block $L3 + (block $L2 + (block $L1 + (block $L0 + (br_table $L0 $L1 $L2 $L3 $L4 $L5 (local.get $x)) + ) + (return) + ) + (return) + ) + (return) + ) + (return) + ) + (return) + ) + (return) + ) + ;; simple counting loop (has a backedge) - path numbering must detect this and skip + ;; instrumentation entirely rather than crash or produce bogus counts. + (func $loop_sum (export "loop_sum") (param $n i32) (result i32) + (local $i i32) (local $acc i32) + (local.set $i (i32.const 0)) + (block $exit + (loop $top + (br_if $exit (i32.ge_s (local.get $i) (local.get $n))) + (local.set $acc (i32.add (local.get $acc) (local.get $i))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $top) + ) + ) + (local.get $acc) + ) + ;; loop containing a nested if (still has a backedge despite the extra branching) - checks + ;; that backedge detection isn't fooled by nested acyclic control flow inside a cyclic one. + (func $loop_with_if (export "loop_with_if") (param $n i32) (result i32) + (local $i i32) + (local.set $i (i32.const 0)) + (block $exit + (loop $top + (br_if $exit (i32.ge_s (local.get $i) (local.get $n))) + (if (i32.rem_s (local.get $i) (i32.const 2)) + (then (local.set $i (i32.add (local.get $i) (i32.const 2)))) + (else (local.set $i (i32.add (local.get $i) (i32.const 1)))) + ) + (br $top) + ) + ) + (local.get $i) + ) + ;; unconditional br skipping over dead code: no runtime decision at all, so this should be + ;; a single-path function (no chord/tree edge needs a probe, same as $main). + (func $unconditional_br (export "unconditional_br") (param $x i32) (result i32) + (local $r i32) + (local.set $r (i32.const 1)) + (block $skip + (br $skip) + (local.set $r (i32.const 999)) + ) + (local.get $r) + ) + ;; unconditional br used as an early exit from a single-armed if, merging with the + ;; fallthrough path at $done - 2 distinct paths (x=1 via br, x=0 via fallthrough). + (func $br_merge (export "br_merge") (param $x i32) (result i32) + (local $r i32) + (block $done + (if (local.get $x) + (then + (local.set $r (i32.const 1)) + (br $done) + ) + ) + (local.set $r (i32.const 2)) + ) + (local.get $r) + ) +) diff --git a/test/monitors/expected/allocs0.wasm.path-profile.out b/test/monitors/expected/allocs0.wasm.path-profile.out new file mode 100644 index 000000000..a65f0f502 --- /dev/null +++ b/test/monitors/expected/allocs0.wasm.path-profile.out @@ -0,0 +1,14 @@ +func #0 + block 100 +3 ... 7 + -> block 102 val=0 + block 102 +7 ... 51 + -> block 103 val=0 + -> block 102 val=0 BACKEDGE + block 103 +51 ... 52 + -> block 101 val=0 inc=0 + block 101 +52 ... 53 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +3 +7 +51 +52 + selfloop[block 102 +49 arm 1] = 10 diff --git a/test/monitors/expected/branch_monitor.wasm.path-profile.out b/test/monitors/expected/branch_monitor.wasm.path-profile.out new file mode 100644 index 000000000..fb9c4aa21 --- /dev/null +++ b/test/monitors/expected/branch_monitor.wasm.path-profile.out @@ -0,0 +1,94 @@ +func "main" + block 100 +1 ... 3 + -> block 101 val=0 inc=0 + block 101 +3 ... 4 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +3 +func "start" + block 102 +3 ... 7 + -> block 104 val=0 DUMMY + -> block 104 val=2 inc=2 + block 104 +7 ... 20 + -> block 105 val=0 + -> block 104 val=0 BACKEDGE + block 105 +20 ... 24 + -> block 103 val=0 inc=0 DUMMY + -> block 106 val=1 + -> block 104 val=0 BACKEDGE + block 106 +24 ... 27 + -> block 103 val=0 inc=1 + block 103 +27 ... 28 + caller "main" +1 + counts[3] = 1 + counts[0] = 0 + path: .. +7 +20 .. + counts[1] = 0 + path: .. +7 +20 +24 +27 + counts[2] = 0 + path: +3 +7 +20 .. + counts[3] = 1 + path: +3 +7 +20 +24 +27 + selfloop[block 104 +18 arm 1] = 4 +func "foo" + block 107 +3 ... 7 + -> block 109 val=0 DUMMY + -> block 109 val=8 inc=8 + block 109 +7 ... 28 + -> block 113 val=0 + -> block 112 val=2 + -> block 111 val=4 + -> block 110 val=6 + block 110 +31 ... 43 + -> block 108 val=0 inc=6 DUMMY + -> block 114 val=1 + -> block 109 val=0 BACKEDGE + block 114 +43 ... 44 + -> block 108 val=0 inc=7 + block 108 +44 ... 45 + block 111 +30 ... 31 + -> block 110 val=0 inc=-2 + block 112 +29 ... 30 + -> block 111 val=0 inc=-2 + block 113 +28 ... 29 + -> block 112 val=0 inc=-2 + caller "start" +25 + counts[0] = 8 + counts[2] = 4 + counts[4] = 3 + counts[5] = 1 + counts[6] = 3 + counts[14] = 1 + counts[0] = 8 + path: .. +7 +28 +29 +30 +31 .. + counts[1] = 0 + path: .. +7 +28 +29 +30 +31 +43 +44 + counts[2] = 4 + path: .. +7 +29 +30 +31 .. + counts[3] = 0 + path: .. +7 +29 +30 +31 +43 +44 + counts[4] = 3 + path: .. +7 +30 +31 .. + counts[5] = 1 + path: .. +7 +30 +31 +43 +44 + counts[6] = 3 + path: .. +7 +31 .. + counts[7] = 0 + path: .. +7 +31 +43 +44 + counts[8] = 0 + path: +3 +7 +28 +29 +30 +31 .. + counts[9] = 0 + path: +3 +7 +28 +29 +30 +31 +43 +44 + counts[10] = 0 + path: +3 +7 +29 +30 +31 .. + counts[11] = 0 + path: +3 +7 +29 +30 +31 +43 +44 + counts[12] = 0 + path: +3 +7 +30 +31 .. + counts[13] = 0 + path: +3 +7 +30 +31 +43 +44 + counts[14] = 1 + path: +3 +7 +31 .. + counts[15] = 0 + path: +3 +7 +31 +43 +44 diff --git a/test/monitors/expected/branch_monitor0.wasm.path-profile.out b/test/monitors/expected/branch_monitor0.wasm.path-profile.out new file mode 100644 index 000000000..9cb3d0f58 --- /dev/null +++ b/test/monitors/expected/branch_monitor0.wasm.path-profile.out @@ -0,0 +1,26 @@ +func "main" + block 100 +3 ... 7 + -> block 102 val=0 + block 102 +7 ... 20 + -> block 103 val=0 + -> block 102 val=0 BACKEDGE + block 103 +20 ... 21 + -> block 101 val=0 inc=0 + block 101 +21 ... 22 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +3 +7 +20 +21 + selfloop[block 102 +18 arm 1] = 4 +func "start" + block 104 +3 ... 7 + -> block 106 val=0 + block 106 +7 ... 20 + -> block 107 val=0 + -> block 106 val=0 BACKEDGE + block 107 +20 ... 21 + -> block 105 val=0 inc=0 + block 105 +21 ... 22 + counts[0] = 0 + path: +3 +7 +20 +21 + selfloop[block 106 +18 arm 1] = 0 diff --git a/test/monitors/expected/branch_monitor_gc.wasm.path-profile.out b/test/monitors/expected/branch_monitor_gc.wasm.path-profile.out new file mode 100644 index 000000000..376bbfe76 --- /dev/null +++ b/test/monitors/expected/branch_monitor_gc.wasm.path-profile.out @@ -0,0 +1,119 @@ +func #0 + block 100 +1 ... 3 + -> block 101 val=0 inc=0 + block 101 +3 ... 4 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +3 +func #1 + block 102 +1 ... 57 + -> block 103 val=0 inc=0 + block 103 +57 ... 58 + caller #0 +1 + counts[0] = 1 + counts[0] = 1 + path: +1 +57 +func #2 + block 104 +1 ... 5 + -> block 108 val=0 + -> block 108 val=0 inc=0 + block 108 +5 ... 6 + -> block 105 val=0 inc=0 + block 105 +6 ... 7 + caller #1 +3 + counts[0] = 1 + caller #1 +7 + counts[0] = 1 + caller #1 +11 + counts[0] = 1 + counts[0] = 3 + path: +1 +5 +6 +func #3 + block 109 +1 ... 5 + -> block 110 val=0 inc=0 + -> block 110 val=0 inc=0 + block 110 +5 ... 6 + caller #1 +15 + counts[0] = 1 + caller #1 +19 + counts[0] = 1 + caller #1 +23 + counts[0] = 1 + counts[0] = 3 + path: +1 +5 +func #4 + block 112 +1 ... 17 + -> block 117 val=0 + -> block 116 val=1 + -> block 115 val=2 + -> block 114 val=3 + block 114 +20 ... 21 + -> block 113 val=0 inc=3 + block 113 +21 ... 22 + block 115 +19 ... 20 + -> block 114 val=0 inc=-1 + block 116 +18 ... 19 + -> block 115 val=0 inc=-1 + block 117 +17 ... 18 + -> block 116 val=0 inc=-1 + caller #1 +27 + counts[3] = 1 + caller #1 +31 + counts[2] = 1 + caller #1 +35 + counts[1] = 1 + caller #1 +39 + counts[1] = 1 + caller #1 +43 + counts[0] = 1 + caller #1 +47 + counts[0] = 1 + counts[0] = 2 + path: +1 +17 +18 +19 +20 +21 + counts[1] = 2 + path: +1 +18 +19 +20 +21 + counts[2] = 1 + path: +1 +19 +20 +21 + counts[3] = 1 + path: +1 +20 +21 +func #5 + block 118 +1 ... 5 + -> block 120 val=0 + -> block 119 val=1 inc=1 + block 119 +6 ... 7 + block 120 +5 ... 6 + -> block 119 val=0 inc=0 + caller #1 +51 + counts[1] = 1 + counts[0] = 0 + path: +1 +5 +6 + counts[1] = 1 + path: +1 +6 +func #6 + block 121 +1 ... 7 + -> block 124 val=0 + -> block 123 val=1 + block 123 +9 ... 11 + -> block 122 val=0 inc=1 + block 122 +11 ... 12 + block 124 +7 ... 9 + -> block 122 val=0 inc=0 + caller #1 +55 + counts[0] = 1 + counts[0] = 1 + path: +1 +7 +11 + counts[1] = 0 + path: +1 +9 +11 +func #7 + block 125 +3 ... 7 + -> block 127 val=0 + block 127 +7 ... 20 + -> block 128 val=0 + -> block 127 val=0 BACKEDGE + block 128 +20 ... 21 + -> block 126 val=0 inc=0 + block 126 +21 ... 22 + counts[0] = 0 + path: +3 +7 +20 +21 + selfloop[block 127 +18 arm 1] = 0 diff --git a/test/monitors/expected/call_monitor1.wasm.path-profile.out b/test/monitors/expected/call_monitor1.wasm.path-profile.out new file mode 100644 index 000000000..cf342fa92 --- /dev/null +++ b/test/monitors/expected/call_monitor1.wasm.path-profile.out @@ -0,0 +1,90 @@ +func #0 + block 100 +3 ... 7 + -> block 102 val=0 DUMMY + -> block 102 val=4 inc=4 + block 102 +7 ... 26 + -> block 104 val=0 + -> block 103 val=2 + block 103 +26 ... 31 + -> block 105 val=0 + block 105 +35 ... 57 + -> block 106 val=0 + block 106 +57 ... 61 + -> block 101 val=0 inc=2 DUMMY + -> block 107 val=1 + -> block 102 val=0 BACKEDGE + block 107 +61 ... 62 + -> block 101 val=0 inc=3 + block 101 +62 ... 63 + block 104 +31 ... 35 + -> block 105 val=0 inc=-2 + caller engine + counts[2] = 1 + counts[3] = 1 + counts[6] = 1 + counts[0] = 0 + path: .. +7 +31 +35 +57 .. + counts[1] = 0 + path: .. +7 +31 +35 +57 +61 +62 + counts[2] = 1 + path: .. +7 +26 +35 +57 .. + counts[3] = 1 + path: .. +7 +26 +35 +57 +61 +62 + counts[4] = 0 + path: +3 +7 +31 +35 +57 .. + counts[5] = 0 + path: +3 +7 +31 +35 +57 +61 +62 + counts[6] = 1 + path: +3 +7 +26 +35 +57 .. + counts[7] = 0 + path: +3 +7 +26 +35 +57 +61 +62 +func #1 + block 108 +1 ... 5 + -> block 109 val=0 inc=0 + block 109 +5 ... 6 + caller #0 +9 + counts[0] = 3 + counts[0] = 3 + path: +1 +5 +func #2 + block 110 +1 ... 3 + -> block 111 val=0 inc=0 + block 111 +3 ... 4 + caller #0 +11 + counts[0] = 3 + caller #0 +15 + counts[0] = 3 + caller #0 +38 + counts[0] = 1 + caller #0 +43 + counts[0] = 1 + counts[0] = 8 + path: +1 +3 +func #3 + block 113 +1 ... 2 + counts[0] = 0 + path: +1 +func #4 + block 114 +1 ... 3 + -> block -1 val=0 inc=0 + block -1 +3 ... 4 + caller #0 +15 + counts[0] = 3 + counts[0] = 3 + path: +1 +3 +func #5 + block 116 +1 ... 5 + -> block -1 val=0 inc=0 + block -1 +5 ... 6 + caller #0 +17 + counts[0] = 3 + counts[0] = 3 + path: +1 +5 +func #6 + block 118 +1 ... 6 + -> block -1 val=0 inc=0 + block -1 +6 ... 7 + caller #0 +43 + counts[0] = 3 + counts[0] = 3 + path: +1 +6 diff --git a/test/monitors/expected/const_arg0.wasm.path-profile.out b/test/monitors/expected/const_arg0.wasm.path-profile.out new file mode 100644 index 000000000..0fd413400 --- /dev/null +++ b/test/monitors/expected/const_arg0.wasm.path-profile.out @@ -0,0 +1,20 @@ +func #0 + block 100 +1 ... 3 + -> block 101 val=0 inc=0 + block 101 +3 ... 4 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +3 +func #1 + block 102 +1 ... 13 + -> block 103 val=0 inc=0 + block 103 +13 ... 14 + caller #0 +1 + counts[0] = 1 + counts[0] = 1 + path: +1 +13 +func #2 + block 105 +1 ... 2 + counts[0] = 0 + path: +1 diff --git a/test/monitors/expected/coverage_monitor0.wasm.path-profile.out b/test/monitors/expected/coverage_monitor0.wasm.path-profile.out new file mode 100644 index 000000000..dfd833ee0 --- /dev/null +++ b/test/monitors/expected/coverage_monitor0.wasm.path-profile.out @@ -0,0 +1,64 @@ +func #0 + block 100 +1 ... 3 + -> block 101 val=0 inc=0 + block 101 +3 ... 4 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +3 +func "unreachable" + block 102 +1 ... 4 + -> block 103 val=0 inc=0 + block 103 +4 ... 5 + counts[0] = 0 + path: +1 +4 +func "start" + block 104 +3 ... 8 + -> block 106 val=0 DUMMY + -> block 106 val=4 inc=4 + block 106 +8 ... 14 + -> block 108 val=0 + -> block 107 val=2 + block 107 +14 ... 19 + -> block 109 val=0 + block 109 +23 ... 24 + -> block 110 val=0 + block 110 +24 ... 37 + -> block 111 val=0 + -> block 110 val=0 BACKEDGE + block 111 +37 ... 49 + -> block 105 val=0 inc=2 DUMMY + -> block 112 val=1 + -> block 106 val=0 BACKEDGE + block 112 +49 ... 55 + -> block 113 val=0 + block 113 +55 ... 68 + -> block 114 val=0 + -> block 113 val=0 BACKEDGE + block 114 +68 ... 69 + -> block 105 val=0 inc=3 + block 105 +69 ... 70 + block 108 +19 ... 23 + -> block 109 val=0 inc=-2 + caller #0 +1 + counts[2] = 131 + counts[3] = 1 + counts[6] = 1 + counts[0] = 0 + path: .. +8 +19 +23 +24 +37 .. + counts[1] = 0 + path: .. +8 +19 +23 +24 +37 +49 +55 +68 +69 + counts[2] = 131 + path: .. +8 +14 +23 +24 +37 .. + counts[3] = 1 + path: .. +8 +14 +23 +24 +37 +49 +55 +68 +69 + counts[4] = 0 + path: +3 +8 +19 +23 +24 +37 .. + counts[5] = 0 + path: +3 +8 +19 +23 +24 +37 +49 +55 +68 +69 + counts[6] = 1 + path: +3 +8 +14 +23 +24 +37 .. + counts[7] = 0 + path: +3 +8 +14 +23 +24 +37 +49 +55 +68 +69 + selfloop[block 110 +35 arm 1] = 266 + selfloop[block 113 +66 arm 1] = 122 diff --git a/test/monitors/expected/coverage_monitor1.wasm.path-profile.out b/test/monitors/expected/coverage_monitor1.wasm.path-profile.out new file mode 100644 index 000000000..0ae22e098 --- /dev/null +++ b/test/monitors/expected/coverage_monitor1.wasm.path-profile.out @@ -0,0 +1,65 @@ +func #0 + block 100 +3 ... 7 + -> block 102 val=0 DUMMY + -> block 102 val=2 inc=2 + block 102 +7 ... 23 + -> block 103 val=0 + -> block 102 val=0 BACKEDGE + block 103 +23 ... 27 + -> block 101 val=0 inc=0 DUMMY + -> block 104 val=1 + -> block 102 val=0 BACKEDGE + block 104 +27 ... 38 + -> block 101 val=0 inc=1 + block 101 +38 ... 39 + caller engine + counts[3] = 1 + counts[0] = 0 + path: .. +7 +23 .. + counts[1] = 0 + path: .. +7 +23 +27 +38 + counts[2] = 0 + path: +3 +7 +23 .. + counts[3] = 1 + path: +3 +7 +23 +27 +38 + selfloop[block 102 +21 arm 1] = 0 +func #1 + block 105 +1 ... 5 + -> block 108 val=0 + -> block 107 val=1 + block 107 +5 ... 8 + -> block 109 val=0 + block 109 +9 ... 10 + -> block 106 val=0 inc=1 + block 106 +10 ... 11 + block 108 +8 ... 9 + -> block 109 val=0 inc=-1 + caller #0 +30 + counts[1] = 1 + counts[0] = 0 + path: +1 +8 +9 +10 + counts[1] = 1 + path: +1 +5 +9 +10 +func #2 + block 110 +1 ... 5 + -> block 112 val=0 + -> block 111 val=2 inc=2 + block 111 +11 ... 12 + block 112 +5 ... 10 + -> block 113 val=0 + -> block 111 val=1 inc=1 + block 113 +10 ... 11 + -> block 111 val=0 inc=0 + caller #0 +36 + counts[1] = 1 + counts[0] = 0 + path: +1 +5 +10 +11 + counts[1] = 1 + path: +1 +5 +11 + counts[2] = 0 + path: +1 +11 +func #3 + block 115 +1 ... 2 + counts[0] = 0 + path: +1 +func #4: not profiled, no acyclic path reaches the exit diff --git a/test/monitors/expected/demo_paths.wasm.path-profile.out b/test/monitors/expected/demo_paths.wasm.path-profile.out new file mode 100644 index 000000000..36c63a60d --- /dev/null +++ b/test/monitors/expected/demo_paths.wasm.path-profile.out @@ -0,0 +1,326 @@ +func #0 + block 100 +1 ... 226 + -> block 101 val=0 inc=0 + block 101 +226 ... 227 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +226 +func #1 + block 102 +1 ... 17 + -> block 104 val=0 + -> block 105 val=1 + -> block 106 val=2 + -> block 107 val=3 + block 107 +17 ... 19 + -> block -1 val=0 inc=3 + block 106 +19 ... 21 + -> block -1 val=0 inc=2 + block 105 +21 ... 23 + -> block -1 val=0 inc=1 + block 104 +23 ... 25 + -> block -1 val=0 inc=0 + block -1 +25 ... 26 + caller #0 +3 + counts[3] = 1 + caller #0 +7 + counts[2] = 1 + caller #0 +11 + counts[1] = 1 + caller #0 +15 + counts[0] = 1 + caller #0 +19 + counts[0] = 1 + counts[0] = 2 + path: +1 +23 +25 + counts[1] = 1 + path: +1 +21 +25 + counts[2] = 1 + path: +1 +19 +25 + counts[3] = 1 + path: +1 +17 +25 +func #2 + block 108 +1 ... 5 + -> block 111 val=0 + -> block 110 val=1 + block 110 +5 ... 8 + -> block 112 val=0 + block 112 +10 ... 11 + -> block 109 val=0 inc=1 + block 109 +11 ... 12 + block 111 +8 ... 10 + -> block 112 val=0 inc=-1 + caller #0 +23 + counts[0] = 1 + caller #0 +28 + counts[1] = 1 + counts[0] = 1 + path: +1 +8 +10 +11 + counts[1] = 1 + path: +1 +5 +10 +11 +func #3 + block 113 +3 ... 7 + -> block 116 val=0 + -> block 115 val=2 + block 115 +7 ... 12 + -> block 117 val=0 + block 117 +16 ... 21 + -> block 119 val=0 + -> block 118 val=1 + block 118 +21 ... 27 + -> block 120 val=0 + block 120 +32 ... 33 + -> block 114 val=0 inc=3 + block 114 +33 ... 34 + block 119 +27 ... 32 + -> block 120 val=0 inc=-1 + block 116 +12 ... 16 + -> block 117 val=0 inc=-2 + caller #0 +35 + counts[0] = 1 + caller #0 +42 + counts[1] = 1 + caller #0 +49 + counts[2] = 1 + caller #0 +56 + counts[3] = 1 + counts[0] = 1 + path: +3 +12 +16 +27 +32 +33 + counts[1] = 1 + path: +3 +12 +16 +21 +32 +33 + counts[2] = 1 + path: +3 +7 +16 +27 +32 +33 + counts[3] = 1 + path: +3 +7 +16 +21 +32 +33 +func #4 + block 121 +1 ... 5 + -> block 124 val=0 + -> block 123 val=1 + block 123 +5 ... 9 + -> block 127 val=0 + -> block 126 val=1 + block 126 +9 ... 12 + -> block 128 val=0 + block 128 +14 ... 16 + -> block 125 val=0 + block 125 +18 ... 19 + -> block 122 val=0 inc=2 + block 122 +19 ... 20 + block 127 +12 ... 14 + -> block 128 val=0 inc=-1 + block 124 +16 ... 18 + -> block 125 val=0 inc=-2 + caller #0 +63 + counts[0] = 1 + caller #0 +70 + counts[0] = 1 + caller #0 +77 + counts[1] = 1 + caller #0 +84 + counts[2] = 1 + counts[0] = 2 + path: +1 +16 +18 +19 + counts[1] = 1 + path: +1 +5 +12 +14 +18 +19 + counts[2] = 1 + path: +1 +5 +9 +14 +18 +19 +func #5 + block 129 +3 ... 7 + -> block 132 val=0 + -> block 131 val=4 + block 131 +7 ... 12 + -> block 133 val=0 + block 133 +16 ... 21 + -> block 135 val=0 + -> block 134 val=2 + block 134 +21 ... 29 + -> block 136 val=0 + block 136 +36 ... 41 + -> block 138 val=0 + -> block 137 val=1 + block 137 +41 ... 48 + -> block 139 val=0 + block 139 +54 ... 55 + -> block 130 val=0 inc=7 + block 130 +55 ... 56 + block 138 +48 ... 54 + -> block 139 val=0 inc=-1 + block 135 +29 ... 36 + -> block 136 val=0 inc=-2 + block 132 +12 ... 16 + -> block 133 val=0 inc=-4 + caller #0 +93 + counts[0] = 1 + caller #0 +102 + counts[1] = 1 + caller #0 +111 + counts[2] = 1 + caller #0 +120 + counts[3] = 1 + caller #0 +129 + counts[4] = 1 + caller #0 +138 + counts[5] = 1 + caller #0 +147 + counts[6] = 1 + caller #0 +156 + counts[7] = 1 + counts[0] = 1 + path: +3 +12 +16 +29 +36 +48 +54 +55 + counts[1] = 1 + path: +3 +12 +16 +29 +36 +41 +54 +55 + counts[2] = 1 + path: +3 +12 +16 +21 +36 +48 +54 +55 + counts[3] = 1 + path: +3 +12 +16 +21 +36 +41 +54 +55 + counts[4] = 1 + path: +3 +7 +16 +29 +36 +48 +54 +55 + counts[5] = 1 + path: +3 +7 +16 +29 +36 +41 +54 +55 + counts[6] = 1 + path: +3 +7 +16 +21 +36 +48 +54 +55 + counts[7] = 1 + path: +3 +7 +16 +21 +36 +41 +54 +55 +func #6 + block 140 +1 ... 23 + -> block 142 val=0 + -> block 143 val=1 + -> block 144 val=2 + -> block 145 val=3 + -> block 146 val=4 + -> block 147 val=5 + block 147 +23 ... 25 + -> block -1 val=0 inc=5 + block 146 +25 ... 27 + -> block -1 val=0 inc=4 + block 145 +27 ... 29 + -> block -1 val=0 inc=3 + block 144 +29 ... 31 + -> block -1 val=0 inc=2 + block 143 +31 ... 33 + -> block -1 val=0 inc=1 + block 142 +33 ... 35 + -> block -1 val=0 inc=0 + block -1 +35 ... 36 + caller #0 +161 + counts[5] = 1 + caller #0 +165 + counts[4] = 1 + caller #0 +169 + counts[3] = 1 + caller #0 +173 + counts[2] = 1 + caller #0 +177 + counts[1] = 1 + caller #0 +181 + counts[0] = 1 + caller #0 +185 + counts[0] = 1 + caller #0 +189 + counts[0] = 1 + counts[0] = 3 + path: +1 +33 +35 + counts[1] = 1 + path: +1 +31 +35 + counts[2] = 1 + path: +1 +29 +35 + counts[3] = 1 + path: +1 +27 +35 + counts[4] = 1 + path: +1 +25 +35 + counts[5] = 1 + path: +1 +23 +35 +func #7 + block 148 +3 ... 9 + -> block 151 val=0 DUMMY + -> block 151 val=2 inc=2 + block 151 +9 ... 18 + -> block 152 val=0 + -> block 150 val=1 + block 150 +35 ... 38 + -> block 149 val=0 inc=1 + block 149 +38 ... 39 + block 152 +18 ... 34 + -> block 149 val=0 inc=0 DUMMY + -> block 151 val=0 BACKEDGE + caller #0 +193 + counts[0] = 4 + counts[1] = 1 + counts[2] = 1 + counts[0] = 4 + path: .. +9 +18 .. + counts[1] = 1 + path: .. +9 +35 +38 + counts[2] = 1 + path: +3 +9 +18 .. + counts[3] = 0 + path: +3 +9 +35 +38 +func #8 + block 153 +3 ... 9 + -> block 156 val=0 DUMMY + -> block 156 val=3 inc=3 + block 156 +9 ... 18 + -> block 157 val=0 + -> block 155 val=2 + block 155 +44 ... 47 + -> block 154 val=0 inc=2 + block 154 +47 ... 48 + block 157 +18 ... 25 + -> block 159 val=0 + -> block 158 val=1 + block 158 +25 ... 33 + -> block 160 val=0 + block 160 +40 ... 43 + -> block 154 val=0 inc=1 DUMMY + -> block 156 val=0 BACKEDGE + block 159 +33 ... 40 + -> block 160 val=0 inc=-1 + caller #0 +198 + counts[1] = 3 + counts[2] = 1 + counts[3] = 1 + counts[0] = 0 + path: .. +9 +18 +33 +40 .. + counts[1] = 3 + path: .. +9 +18 +25 +40 .. + counts[2] = 1 + path: .. +9 +44 +47 + counts[3] = 1 + path: +3 +9 +18 +33 +40 .. + counts[4] = 0 + path: +3 +9 +18 +25 +40 .. + counts[5] = 0 + path: +3 +9 +44 +47 +func #9 + block 161 +3 ... 11 + -> block 163 val=0 + block 163 +16 ... 19 + -> block 162 val=0 inc=0 + block 162 +19 ... 20 + caller #0 +203 + counts[0] = 1 + caller #0 +208 + counts[0] = 1 + counts[0] = 2 + path: +3 +16 +19 +func #10 + block 164 +3 ... 9 + -> block 169 val=0 + -> block 167 val=1 + block 167 +9 ... 15 + -> block 166 val=0 + block 166 +20 ... 23 + -> block 165 val=0 inc=1 + block 165 +23 ... 24 + block 169 +15 ... 20 + -> block 166 val=0 inc=-1 + caller #0 +213 + counts[0] = 1 + caller #0 +218 + counts[1] = 1 + caller #0 +223 + counts[1] = 1 + counts[0] = 1 + path: +3 +15 +20 +23 + counts[1] = 2 + path: +3 +9 +20 +23 diff --git a/test/monitors/expected/globals_monitor.wasm.path-profile.out b/test/monitors/expected/globals_monitor.wasm.path-profile.out new file mode 100644 index 000000000..662309b0c --- /dev/null +++ b/test/monitors/expected/globals_monitor.wasm.path-profile.out @@ -0,0 +1,54 @@ +func "_start" + block 100 +3 ... 7 + -> block 102 val=0 + block 102 +7 ... 38 + -> block 103 val=0 + -> block 102 val=0 BACKEDGE + block 103 +38 ... 39 + -> block 101 val=0 inc=0 + block 101 +39 ... 40 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +3 +7 +38 +39 + selfloop[block 102 +36 arm 1] = 9 +func "foo" + block 104 +1 ... 4 + -> block 105 val=0 inc=0 + block 105 +4 ... 5 + caller "_start" +9 + counts[0] = 10 + caller "_start" +13 + counts[0] = 10 + caller "_start" +17 + counts[0] = 10 + caller "_start" +19 + counts[0] = 10 + caller "_start" +21 + counts[0] = 10 + caller "_start" +27 + counts[0] = 10 + counts[0] = 60 + path: +1 +4 +func "bar" + block 106 +1 ... 3 + -> block 107 val=0 inc=0 + block 107 +3 ... 4 + caller "_start" +11 + counts[0] = 10 + caller "_start" +23 + counts[0] = 10 + caller "_start" +25 + counts[0] = 10 + counts[0] = 30 + path: +1 +3 +func "baz" + block 108 +1 ... 6 + -> block 109 val=0 inc=0 + block 109 +6 ... 7 + caller "bar" +1 + counts[0] = 30 + caller "_start" +15 + counts[0] = 10 + counts[0] = 40 + path: +1 +6 diff --git a/test/monitors/expected/load_store.wasm.path-profile.out b/test/monitors/expected/load_store.wasm.path-profile.out new file mode 100644 index 000000000..dfdc0dba8 --- /dev/null +++ b/test/monitors/expected/load_store.wasm.path-profile.out @@ -0,0 +1,8 @@ +func #0 + block 100 +1 ... 53 + -> block 101 val=0 inc=0 + block 101 +53 ... 54 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +53 diff --git a/test/monitors/expected/loop_monitor0.wasm.path-profile.out b/test/monitors/expected/loop_monitor0.wasm.path-profile.out new file mode 100644 index 000000000..b94860bdf --- /dev/null +++ b/test/monitors/expected/loop_monitor0.wasm.path-profile.out @@ -0,0 +1,49 @@ +func #0 + block 100 +1 ... 3 + -> block 101 val=0 inc=0 + block 101 +3 ... 4 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +3 +func #1 + block 102 +3 ... 8 + -> block 104 val=0 DUMMY + -> block 104 val=2 inc=2 + block 104 +8 ... 14 + -> block 105 val=0 + block 105 +14 ... 27 + -> block 106 val=0 + -> block 105 val=0 BACKEDGE + block 106 +27 ... 39 + -> block 103 val=0 inc=0 DUMMY + -> block 107 val=1 + -> block 104 val=0 BACKEDGE + block 107 +39 ... 45 + -> block 108 val=0 + block 108 +45 ... 58 + -> block 109 val=0 + -> block 108 val=0 BACKEDGE + block 109 +58 ... 59 + -> block 103 val=0 inc=1 + block 103 +59 ... 60 + caller #0 +1 + counts[0] = 131 + counts[1] = 1 + counts[2] = 1 + counts[0] = 131 + path: .. +8 +14 +27 .. + counts[1] = 1 + path: .. +8 +14 +27 +39 +45 +58 +59 + counts[2] = 1 + path: +3 +8 +14 +27 .. + counts[3] = 0 + path: +3 +8 +14 +27 +39 +45 +58 +59 + selfloop[block 105 +25 arm 1] = 266 + selfloop[block 108 +56 arm 1] = 122 +func #2 + block 112 +1 ... 4 + -> block 111 val=0 inc=0 + block 111 +4 ... 5 + counts[0] = 0 + path: +1 +4 diff --git a/test/monitors/expected/loop_monitor1.wasm.path-profile.out b/test/monitors/expected/loop_monitor1.wasm.path-profile.out new file mode 100644 index 000000000..402cfe513 --- /dev/null +++ b/test/monitors/expected/loop_monitor1.wasm.path-profile.out @@ -0,0 +1,61 @@ +func "main" + block 100 +1 ... 15 + -> block 101 val=0 inc=0 + block 101 +15 ... 16 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +15 +func "start" + block 104 +1 ... 14 + -> block 105 val=0 + -> block 104 val=0 BACKEDGE + block 105 +14 ... 15 + -> block 103 val=0 inc=0 + block 103 +15 ... 16 + caller "main" +4 + counts[0] = 1 + caller "main" +9 + counts[0] = 1 + counts[0] = 2 + path: +1 +14 +15 + selfloop[block 104 +12 arm 1] = 598 +func "foo" + block 108 +3 ... 12 + -> block 110 val=0 + -> block 109 val=3 + block 109 +16 ... 31 + -> block 107 val=0 inc=3 DUMMY + -> block 111 val=1 + -> block 108 val=0 BACKEDGE + block 111 +31 ... 37 + -> block 112 val=0 + -> block 107 val=1 inc=5 + block 107 +44 ... 45 + block 112 +37 ... 44 + -> block 107 val=0 inc=4 + block 110 +12 ... 16 + -> block 109 val=0 inc=-3 + caller "foo" +14 + counts[3] = 36 + counts[5] = 12 + caller "main" +13 + counts[0] = 3 + counts[1] = 1 + caller "foo" +42 + counts[0] = 6 + counts[1] = 2 + counts[3] = 3 + counts[5] = 1 + counts[0] = 9 + path: +3 +12 +16 .. + counts[1] = 3 + path: +3 +12 +16 +31 +37 +44 + counts[2] = 0 + path: +3 +12 +16 +31 +44 + counts[3] = 39 + path: +3 +16 .. + counts[4] = 0 + path: +3 +16 +31 +37 +44 + counts[5] = 13 + path: +3 +16 +31 +44 diff --git a/test/monitors/expected/memory_monitor.wasm.path-profile.out b/test/monitors/expected/memory_monitor.wasm.path-profile.out new file mode 100644 index 000000000..269d6f97d --- /dev/null +++ b/test/monitors/expected/memory_monitor.wasm.path-profile.out @@ -0,0 +1,22 @@ +func #0 + block 100 +1 ... 3 + -> block 101 val=0 inc=0 + block 101 +3 ... 4 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +3 +func #1 + block 102 +3 ... 7 + -> block 104 val=0 + block 104 +7 ... 55 + -> block 105 val=0 + -> block 104 val=0 BACKEDGE + block 105 +55 ... 56 + -> block 103 val=0 inc=0 + block 103 +56 ... 57 + caller #0 +1 + counts[0] = 1 + counts[0] = 1 + path: +3 +7 +55 +56 + selfloop[block 104 +53 arm 1] = 3 diff --git a/test/monitors/expected/path_profile_br_outer.wasm.path-profile.out b/test/monitors/expected/path_profile_br_outer.wasm.path-profile.out new file mode 100644 index 000000000..51f5c6003 --- /dev/null +++ b/test/monitors/expected/path_profile_br_outer.wasm.path-profile.out @@ -0,0 +1,65 @@ +func #0 + block 100 +3 ... 14 + -> block 105 val=0 + -> block 102 val=2 + block 102 +42 ... 45 + -> block 101 val=0 inc=2 + block 101 +45 ... 46 + block 105 +14 ... 21 + -> block 106 val=0 + -> block 103 val=1 + block 103 +34 ... 42 + -> block 102 val=0 inc=-1 + block 106 +21 ... 26 + -> block 104 val=0 + block 104 +26 ... 34 + -> block 103 val=0 inc=-1 + caller #2 +3 + counts[2] = 1 + caller #2 +8 + counts[1] = 1 + caller #2 +13 + counts[1] = 1 + caller #2 +18 + counts[0] = 1 + caller #2 +23 + counts[0] = 1 + caller #2 +28 + counts[0] = 1 + counts[0] = 3 + path: +3 +14 +21 +26 +34 +42 +45 + counts[1] = 2 + path: +3 +14 +34 +42 +45 + counts[2] = 1 + path: +3 +42 +45 +func #1 + block 107 +3 ... 13 + -> block 112 val=0 + -> block 110 val=1 + block 110 +20 ... 25 + -> block 109 val=0 + block 109 +25 ... 28 + -> block 108 val=0 inc=1 + block 108 +28 ... 29 + block 112 +13 ... 15 + -> block 109 val=0 inc=-1 + caller #2 +33 + counts[0] = 1 + caller #2 +38 + counts[0] = 1 + caller #2 +43 + counts[0] = 1 + caller #2 +48 + counts[1] = 1 + counts[0] = 3 + path: +3 +13 +25 +28 + counts[1] = 1 + path: +3 +20 +25 +28 +func #2 + block 113 +1 ... 51 + -> block 114 val=0 inc=0 + block 114 +51 ... 52 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +51 diff --git a/test/monitors/expected/path_profile_dead_code.wasm.path-profile.out b/test/monitors/expected/path_profile_dead_code.wasm.path-profile.out new file mode 100644 index 000000000..4dfd6ddf6 --- /dev/null +++ b/test/monitors/expected/path_profile_dead_code.wasm.path-profile.out @@ -0,0 +1,39 @@ +func #0 + block 100 +3 ... 11 + -> block 102 val=0 + block 102 +16 ... 19 + -> block 101 val=0 inc=0 + block 101 +19 ... 20 + caller #3 +1 + counts[0] = 1 + caller #3 +4 + counts[0] = 1 + caller #3 +7 + counts[0] = 1 + counts[0] = 3 + path: +3 +16 +19 +func #1: not profiled, no acyclic path reaches the exit +func #2 + block 105 +1 ... 5 + -> block 108 val=0 + -> block 107 val=0 + block 107 +5 ... 7 + -> block 109 val=0 + block 109 +8 ... 11 + -> block 106 val=0 inc=0 + block 106 +11 ... 12 + block 108 +7 ... 8 + caller #3 +12 + counts[0] = 1 + caller #3 +17 + counts[0] = 1 + counts[0] = 2 + path: +1 +5 +8 +11 +func #3 + block 110 +1 ... 20 + -> block 111 val=0 inc=0 + block 111 +20 ... 21 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +20 diff --git a/test/monitors/expected/path_profile_dead_pred.wasm.path-profile.out b/test/monitors/expected/path_profile_dead_pred.wasm.path-profile.out new file mode 100644 index 000000000..920750e58 --- /dev/null +++ b/test/monitors/expected/path_profile_dead_pred.wasm.path-profile.out @@ -0,0 +1,56 @@ +func #0 + block 100 +1 ... 5 + -> block 103 val=0 + -> block 102 val=1 + block 102 +5 ... 8 + -> block -1 val=0 inc=1 + block 103 +9 ... 12 + -> block -1 val=0 inc=0 + block -1 +16 ... 17 + caller #3 +3 + counts[1] = 1 + caller #3 +8 + counts[1] = 1 + caller #3 +13 + counts[0] = 1 + caller #3 +18 + counts[0] = 1 + caller #3 +23 + counts[0] = 1 + counts[0] = 3 + path: +1 +9 +16 + counts[1] = 2 + path: +1 +5 +16 +func #1 + block 105 +1 ... 5 + -> block 108 val=0 + -> block 107 val=1 + block 107 +5 ... 8 + -> block -1 val=0 inc=1 + block 108 +9 ... 12 + -> block -1 val=0 inc=0 + block -1 +13 ... 14 + caller #3 +28 + counts[1] = 1 + counts[0] = 0 + path: +1 +9 +13 + counts[1] = 1 + path: +1 +5 +13 +func #2 + block 110 +3 ... 15 + -> block 112 val=0 + block 112 +25 ... 28 + -> block 111 val=0 inc=0 + block 111 +28 ... 29 + caller #3 +31 + counts[0] = 1 + counts[0] = 1 + path: +3 +25 +28 +func #3 + block 115 +1 ... 34 + -> block 116 val=0 inc=0 + block 116 +34 ... 35 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +34 diff --git a/test/monitors/expected/path_profile_deep_nest.wasm.path-profile.out b/test/monitors/expected/path_profile_deep_nest.wasm.path-profile.out new file mode 100644 index 000000000..8442809d5 --- /dev/null +++ b/test/monitors/expected/path_profile_deep_nest.wasm.path-profile.out @@ -0,0 +1,61 @@ +func #0 + block 100 +1 ... 5 + -> block 103 val=0 + -> block 102 val=1 + block 102 +5 ... 9 + -> block 106 val=0 + -> block 105 val=1 + block 105 +9 ... 13 + -> block 109 val=0 + -> block 108 val=1 + block 108 +13 ... 16 + -> block 110 val=0 + block 110 +18 ... 20 + -> block 107 val=0 + block 107 +22 ... 24 + -> block 104 val=0 + block 104 +26 ... 27 + -> block 101 val=0 inc=3 + block 101 +27 ... 28 + block 109 +16 ... 18 + -> block 110 val=0 inc=-1 + block 106 +20 ... 22 + -> block 107 val=0 inc=-2 + block 103 +24 ... 26 + -> block 104 val=0 inc=-3 + caller #1 +7 + counts[0] = 1 + caller #1 +16 + counts[1] = 1 + caller #1 +25 + counts[1] = 1 + caller #1 +34 + counts[2] = 1 + caller #1 +43 + counts[2] = 1 + caller #1 +52 + counts[2] = 1 + caller #1 +61 + counts[3] = 1 + caller #1 +70 + counts[3] = 1 + caller #1 +79 + counts[3] = 1 + caller #1 +88 + counts[3] = 1 + counts[0] = 1 + path: +1 +24 +26 +27 + counts[1] = 2 + path: +1 +5 +20 +22 +26 +27 + counts[2] = 3 + path: +1 +5 +9 +16 +18 +22 +26 +27 + counts[3] = 4 + path: +1 +5 +9 +13 +18 +22 +26 +27 +func #1 + block 111 +1 ... 91 + -> block 112 val=0 inc=0 + block 112 +91 ... 92 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +91 diff --git a/test/monitors/expected/path_profile_demo_only.wasm.path-profile.out b/test/monitors/expected/path_profile_demo_only.wasm.path-profile.out new file mode 100644 index 000000000..47db938d4 --- /dev/null +++ b/test/monitors/expected/path_profile_demo_only.wasm.path-profile.out @@ -0,0 +1,41 @@ +func #0 + block 100 +1 ... 17 + -> block 102 val=0 + -> block 103 val=1 + -> block 104 val=2 + -> block 105 val=3 + block 105 +17 ... 19 + -> block -1 val=0 inc=3 + block 104 +19 ... 21 + -> block -1 val=0 inc=2 + block 103 +21 ... 23 + -> block -1 val=0 inc=1 + block 102 +23 ... 25 + -> block -1 val=0 inc=0 + block -1 +25 ... 26 + caller #1 +3 + counts[3] = 1 + caller #1 +7 + counts[2] = 1 + caller #1 +11 + counts[1] = 1 + caller #1 +15 + counts[0] = 1 + caller #1 +19 + counts[0] = 1 + counts[0] = 2 + path: +1 +23 +25 + counts[1] = 1 + path: +1 +21 +25 + counts[2] = 1 + path: +1 +19 +25 + counts[3] = 1 + path: +1 +17 +25 +func #1 + block 106 +1 ... 21 + -> block 107 val=0 inc=0 + block 107 +21 ... 22 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +21 diff --git a/test/monitors/expected/path_profile_diamond_only.wasm.path-profile.out b/test/monitors/expected/path_profile_diamond_only.wasm.path-profile.out new file mode 100644 index 000000000..c8a5ded0c --- /dev/null +++ b/test/monitors/expected/path_profile_diamond_only.wasm.path-profile.out @@ -0,0 +1,27 @@ +func #0 + block 100 +1 ... 5 + -> block 103 val=0 + -> block 102 val=1 + block 102 +5 ... 8 + -> block 104 val=0 + block 104 +10 ... 11 + -> block 101 val=0 inc=1 + block 101 +11 ... 12 + block 103 +8 ... 10 + -> block 104 val=0 inc=-1 + caller #1 +3 + counts[0] = 1 + caller #1 +8 + counts[1] = 1 + counts[0] = 1 + path: +1 +8 +10 +11 + counts[1] = 1 + path: +1 +5 +10 +11 +func #1 + block 105 +1 ... 11 + -> block 106 val=0 inc=0 + block 106 +11 ... 12 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +11 diff --git a/test/monitors/expected/path_profile_dup_labels.wasm.path-profile.out b/test/monitors/expected/path_profile_dup_labels.wasm.path-profile.out new file mode 100644 index 000000000..095c2f780 --- /dev/null +++ b/test/monitors/expected/path_profile_dup_labels.wasm.path-profile.out @@ -0,0 +1,34 @@ +func #0 + block 100 +1 ... 14 + -> block 102 val=0 + -> block 103 val=1 + -> block 102 val=0 inc=0 + -> block 103 val=1 inc=0 + -> block 103 val=1 inc=0 + block 103 +14 ... 16 + -> block -1 val=0 inc=1 + block 102 +16 ... 18 + -> block -1 val=0 inc=0 + block -1 +18 ... 19 + caller #1 +3 + counts[1] = 1 + caller #1 +7 + counts[1] = 1 + caller #1 +11 + counts[0] = 1 + caller #1 +15 + counts[1] = 1 + caller #1 +19 + counts[0] = 1 + counts[0] = 2 + path: +1 +16 +18 + counts[1] = 3 + path: +1 +14 +18 +func #1 + block 104 +1 ... 21 + -> block 105 val=0 inc=0 + block 105 +21 ... 22 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +21 diff --git a/test/monitors/expected/path_profile_empty_then.wasm.path-profile.out b/test/monitors/expected/path_profile_empty_then.wasm.path-profile.out new file mode 100644 index 000000000..0c2fff7aa --- /dev/null +++ b/test/monitors/expected/path_profile_empty_then.wasm.path-profile.out @@ -0,0 +1,21 @@ +func #0 + block 100 +3 ... 11 + -> block 104 val=0 + -> block 104 val=0 inc=0 + block 104 +11 ... 14 + -> block 101 val=0 inc=0 + block 101 +14 ... 15 + caller #1 +3 + counts[0] = 1 + caller #1 +8 + counts[0] = 1 + counts[0] = 2 + path: +3 +11 +14 +func #1 + block 105 +1 ... 11 + -> block 106 val=0 inc=0 + block 106 +11 ... 12 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +11 diff --git a/test/monitors/expected/path_profile_freq.wasm.path-profile.out b/test/monitors/expected/path_profile_freq.wasm.path-profile.out new file mode 100644 index 000000000..a1ab08b18 --- /dev/null +++ b/test/monitors/expected/path_profile_freq.wasm.path-profile.out @@ -0,0 +1,180 @@ +func #0 + block 100 +3 ... 10 + -> block 103 val=0 + -> block 102 val=16 + block 102 +10 ... 18 + -> block 104 val=0 + block 104 +22 ... 30 + -> block 106 val=0 + -> block 105 val=8 + block 105 +30 ... 38 + -> block 107 val=0 + block 107 +42 ... 50 + -> block 109 val=0 + -> block 108 val=4 + block 108 +50 ... 58 + -> block 110 val=0 + block 110 +62 ... 70 + -> block 112 val=0 + -> block 111 val=2 + block 111 +70 ... 78 + -> block 113 val=0 + block 113 +82 ... 90 + -> block 115 val=0 + -> block 114 val=1 + block 114 +90 ... 98 + -> block 116 val=0 + block 116 +102 ... 105 + -> block 101 val=0 inc=31 + block 101 +105 ... 106 + block 115 +98 ... 102 + -> block 116 val=0 inc=-1 + block 112 +78 ... 82 + -> block 113 val=0 inc=-2 + block 109 +58 ... 62 + -> block 110 val=0 inc=-4 + block 106 +38 ... 42 + -> block 107 val=0 inc=-8 + block 103 +18 ... 22 + -> block 104 val=0 inc=-16 + caller #1 +33 + counts[0] = 1 + counts[1] = 17 + counts[2] = 9 + counts[3] = 25 + counts[4] = 5 + counts[5] = 21 + counts[6] = 13 + counts[7] = 29 + counts[8] = 3 + counts[9] = 19 + counts[10] = 11 + counts[11] = 27 + counts[12] = 7 + counts[13] = 23 + counts[14] = 15 + counts[15] = 31 + counts[16] = 2 + counts[17] = 18 + counts[18] = 10 + counts[19] = 26 + counts[20] = 6 + counts[21] = 22 + counts[22] = 14 + counts[23] = 30 + counts[24] = 4 + counts[25] = 20 + counts[26] = 12 + counts[27] = 28 + counts[28] = 8 + counts[29] = 24 + counts[30] = 16 + counts[31] = 32 + counts[0] = 1 + path: +3 +18 +22 +38 +42 +58 +62 +78 +82 +98 +102 +105 + counts[1] = 17 + path: +3 +18 +22 +38 +42 +58 +62 +78 +82 +90 +102 +105 + counts[2] = 9 + path: +3 +18 +22 +38 +42 +58 +62 +70 +82 +98 +102 +105 + counts[3] = 25 + path: +3 +18 +22 +38 +42 +58 +62 +70 +82 +90 +102 +105 + counts[4] = 5 + path: +3 +18 +22 +38 +42 +50 +62 +78 +82 +98 +102 +105 + counts[5] = 21 + path: +3 +18 +22 +38 +42 +50 +62 +78 +82 +90 +102 +105 + counts[6] = 13 + path: +3 +18 +22 +38 +42 +50 +62 +70 +82 +98 +102 +105 + counts[7] = 29 + path: +3 +18 +22 +38 +42 +50 +62 +70 +82 +90 +102 +105 + counts[8] = 3 + path: +3 +18 +22 +30 +42 +58 +62 +78 +82 +98 +102 +105 + counts[9] = 19 + path: +3 +18 +22 +30 +42 +58 +62 +78 +82 +90 +102 +105 + counts[10] = 11 + path: +3 +18 +22 +30 +42 +58 +62 +70 +82 +98 +102 +105 + counts[11] = 27 + path: +3 +18 +22 +30 +42 +58 +62 +70 +82 +90 +102 +105 + counts[12] = 7 + path: +3 +18 +22 +30 +42 +50 +62 +78 +82 +98 +102 +105 + counts[13] = 23 + path: +3 +18 +22 +30 +42 +50 +62 +78 +82 +90 +102 +105 + counts[14] = 15 + path: +3 +18 +22 +30 +42 +50 +62 +70 +82 +98 +102 +105 + counts[15] = 31 + path: +3 +18 +22 +30 +42 +50 +62 +70 +82 +90 +102 +105 + counts[16] = 2 + path: +3 +10 +22 +38 +42 +58 +62 +78 +82 +98 +102 +105 + counts[17] = 18 + path: +3 +10 +22 +38 +42 +58 +62 +78 +82 +90 +102 +105 + counts[18] = 10 + path: +3 +10 +22 +38 +42 +58 +62 +70 +82 +98 +102 +105 + counts[19] = 26 + path: +3 +10 +22 +38 +42 +58 +62 +70 +82 +90 +102 +105 + counts[20] = 6 + path: +3 +10 +22 +38 +42 +50 +62 +78 +82 +98 +102 +105 + counts[21] = 22 + path: +3 +10 +22 +38 +42 +50 +62 +78 +82 +90 +102 +105 + counts[22] = 14 + path: +3 +10 +22 +38 +42 +50 +62 +70 +82 +98 +102 +105 + counts[23] = 30 + path: +3 +10 +22 +38 +42 +50 +62 +70 +82 +90 +102 +105 + counts[24] = 4 + path: +3 +10 +22 +30 +42 +58 +62 +78 +82 +98 +102 +105 + counts[25] = 20 + path: +3 +10 +22 +30 +42 +58 +62 +78 +82 +90 +102 +105 + counts[26] = 12 + path: +3 +10 +22 +30 +42 +58 +62 +70 +82 +98 +102 +105 + counts[27] = 28 + path: +3 +10 +22 +30 +42 +58 +62 +70 +82 +90 +102 +105 + counts[28] = 8 + path: +3 +10 +22 +30 +42 +50 +62 +78 +82 +98 +102 +105 + counts[29] = 24 + path: +3 +10 +22 +30 +42 +50 +62 +78 +82 +90 +102 +105 + counts[30] = 16 + path: +3 +10 +22 +30 +42 +50 +62 +70 +82 +98 +102 +105 + counts[31] = 32 + path: +3 +10 +22 +30 +42 +50 +62 +70 +82 +90 +102 +105 +func #1 + block 117 +3 ... 5 + -> block 123 val=0 DUMMY + -> block 120 val=2 DUMMY + -> block 120 val=5 inc=3 + block 120 +5 ... 14 + -> block 121 val=0 + -> block 119 val=2 + block 119 +59 ... 62 + -> block 118 val=0 inc=4 + block 118 +62 ... 63 + block 121 +14 ... 20 + -> block 123 val=0 inc=2 + block 123 +20 ... 29 + -> block 124 val=0 + -> block 122 val=1 + block 122 +48 ... 58 + -> block 118 val=0 inc=1 DUMMY + -> block 120 val=0 BACKEDGE + block 124 +29 ... 47 + -> block 118 val=0 inc=0 DUMMY + -> block 123 val=0 BACKEDGE + caller engine + counts[0] = 496 + counts[1] = 32 + counts[2] = 31 + counts[4] = 1 + counts[5] = 1 + counts[0] = 496 + path: .. +20 +29 .. + counts[1] = 32 + path: .. +20 +48 .. + counts[2] = 31 + path: .. +5 +14 +20 +29 .. + counts[3] = 0 + path: .. +5 +14 +20 +48 .. + counts[4] = 1 + path: .. +5 +59 +62 + counts[5] = 1 + path: +3 +5 +14 +20 +29 .. + counts[6] = 0 + path: +3 +5 +14 +20 +48 .. + counts[7] = 0 + path: +3 +5 +59 +62 diff --git a/test/monitors/expected/path_profile_freq_recur.wasm.path-profile.out b/test/monitors/expected/path_profile_freq_recur.wasm.path-profile.out new file mode 100644 index 000000000..277697804 --- /dev/null +++ b/test/monitors/expected/path_profile_freq_recur.wasm.path-profile.out @@ -0,0 +1,55 @@ +func #0 + block 100 +3 ... 8 + -> block 104 val=0 + -> block 102 val=4 + block 102 +8 ... 11 + -> block 101 val=0 inc=4 + block 104 +11 ... 19 + -> block 106 val=0 + -> block 105 val=2 + block 105 +19 ... 27 + -> block 107 val=0 + block 107 +31 ... 39 + -> block 109 val=0 + -> block 108 val=1 + block 108 +39 ... 47 + -> block 110 val=0 + block 110 +51 ... 62 + -> block 101 val=0 inc=3 + block 101 +62 ... 63 + block 109 +47 ... 51 + -> block 110 val=0 inc=-1 + block 106 +27 ... 31 + -> block 107 val=0 inc=-2 + caller #0 +59 + counts[0] = 9 + counts[1] = 11 + counts[2] = 11 + counts[3] = 10 + counts[4] = 4 + caller #1 +3 + counts[3] = 1 + caller #1 +8 + counts[2] = 1 + caller #1 +13 + counts[0] = 1 + caller #1 +18 + counts[2] = 1 + counts[0] = 10 + path: +3 +11 +27 +31 +47 +51 +62 + counts[1] = 11 + path: +3 +11 +27 +31 +39 +51 +62 + counts[2] = 13 + path: +3 +11 +19 +31 +47 +51 +62 + counts[3] = 11 + path: +3 +11 +19 +31 +39 +51 +62 + counts[4] = 4 + path: +3 +8 +62 +func #1 + block 111 +1 ... 21 + -> block 112 val=0 inc=0 + block 112 +21 ... 22 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +21 diff --git a/test/monitors/expected/path_profile_freq_shapes.wasm.path-profile.out b/test/monitors/expected/path_profile_freq_shapes.wasm.path-profile.out new file mode 100644 index 000000000..0001f4d49 --- /dev/null +++ b/test/monitors/expected/path_profile_freq_shapes.wasm.path-profile.out @@ -0,0 +1,153 @@ +func #0 + block 100 +3 ... 21 + -> block 103 val=0 + -> block 104 val=1 + -> block 105 val=2 + -> block 106 val=3 + block 106 +21 ... 28 + -> block 102 val=0 + block 102 +48 ... 51 + -> block 101 val=0 inc=3 + block 101 +51 ... 52 + block 105 +28 ... 36 + -> block 101 val=0 inc=2 + block 104 +36 ... 43 + -> block 102 val=0 inc=-2 + block 103 +43 ... 48 + -> block 102 val=0 inc=-3 + caller #1 +3 + counts[3] = 1 + caller #1 +8 + counts[3] = 1 + caller #1 +13 + counts[3] = 1 + caller #1 +18 + counts[3] = 1 + caller #1 +23 + counts[3] = 1 + caller #1 +28 + counts[3] = 1 + caller #1 +33 + counts[3] = 1 + caller #1 +38 + counts[3] = 1 + caller #1 +43 + counts[3] = 1 + caller #1 +48 + counts[3] = 1 + caller #1 +53 + counts[3] = 1 + caller #1 +58 + counts[3] = 1 + caller #1 +63 + counts[3] = 1 + caller #1 +68 + counts[3] = 1 + caller #1 +73 + counts[3] = 1 + caller #1 +78 + counts[3] = 1 + caller #1 +83 + counts[3] = 1 + caller #1 +88 + counts[2] = 1 + caller #1 +93 + counts[2] = 1 + caller #1 +98 + counts[2] = 1 + caller #1 +103 + counts[2] = 1 + caller #1 +108 + counts[2] = 1 + caller #1 +113 + counts[1] = 1 + caller #1 +118 + counts[1] = 1 + caller #1 +123 + counts[1] = 1 + caller #1 +128 + counts[1] = 1 + caller #1 +133 + counts[1] = 1 + caller #1 +138 + counts[1] = 1 + caller #1 +143 + counts[1] = 1 + caller #1 +148 + counts[1] = 1 + caller #1 +153 + counts[1] = 1 + caller #1 +158 + counts[1] = 1 + caller #1 +163 + counts[1] = 1 + caller #1 +168 + counts[1] = 1 + caller #1 +173 + counts[1] = 1 + caller #1 +178 + counts[1] = 1 + caller #1 +183 + counts[1] = 1 + caller #1 +188 + counts[1] = 1 + caller #1 +193 + counts[1] = 1 + caller #1 +198 + counts[1] = 1 + caller #1 +203 + counts[1] = 1 + caller #1 +208 + counts[1] = 1 + caller #1 +213 + counts[1] = 1 + caller #1 +218 + counts[1] = 1 + caller #1 +223 + counts[1] = 1 + caller #1 +228 + counts[0] = 1 + caller #1 +233 + counts[0] = 1 + caller #1 +239 + counts[0] = 1 + caller #1 +245 + counts[0] = 1 + caller #1 +251 + counts[0] = 1 + caller #1 +257 + counts[0] = 1 + caller #1 +263 + counts[0] = 1 + caller #1 +269 + counts[0] = 1 + caller #1 +275 + counts[0] = 1 + caller #1 +281 + counts[0] = 1 + caller #1 +287 + counts[0] = 1 + caller #1 +293 + counts[0] = 1 + caller #1 +299 + counts[0] = 1 + caller #1 +305 + counts[0] = 1 + caller #1 +311 + counts[0] = 1 + counts[0] = 15 + path: +3 +43 +48 +51 + counts[1] = 23 + path: +3 +36 +48 +51 + counts[2] = 5 + path: +3 +28 +51 + counts[3] = 17 + path: +3 +21 +48 +51 +func #1 + block 107 +1 ... 314 + -> block 108 val=0 inc=0 + block 108 +314 ... 315 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +314 diff --git a/test/monitors/expected/path_profile_loop_nested.wasm.path-profile.out b/test/monitors/expected/path_profile_loop_nested.wasm.path-profile.out new file mode 100644 index 000000000..3853bcf3f --- /dev/null +++ b/test/monitors/expected/path_profile_loop_nested.wasm.path-profile.out @@ -0,0 +1,52 @@ +func #0 + block 100 +3 ... 5 + -> block 106 val=0 DUMMY + -> block 103 val=2 DUMMY + -> block 103 val=5 inc=3 + block 103 +5 ... 14 + -> block 104 val=0 + -> block 102 val=2 + block 102 +57 ... 60 + -> block 101 val=0 inc=4 + block 101 +60 ... 61 + block 104 +14 ... 20 + -> block 106 val=0 inc=2 + block 106 +20 ... 29 + -> block 107 val=0 + -> block 105 val=1 + block 105 +46 ... 56 + -> block 101 val=0 inc=1 DUMMY + -> block 103 val=0 BACKEDGE + block 107 +29 ... 45 + -> block 101 val=0 inc=0 DUMMY + -> block 106 val=0 BACKEDGE + caller #1 +5 + counts[0] = 3 + counts[1] = 3 + counts[2] = 2 + counts[4] = 1 + counts[5] = 1 + counts[0] = 3 + path: .. +20 +29 .. + counts[1] = 3 + path: .. +20 +46 .. + counts[2] = 2 + path: .. +5 +14 +20 +29 .. + counts[3] = 0 + path: .. +5 +14 +20 +46 .. + counts[4] = 1 + path: .. +5 +57 +60 + counts[5] = 1 + path: +3 +5 +14 +20 +29 .. + counts[6] = 0 + path: +3 +5 +14 +20 +46 .. + counts[7] = 0 + path: +3 +5 +57 +60 +func #1 + block 108 +1 ... 8 + -> block 109 val=0 inc=0 + block 109 +8 ... 9 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +8 diff --git a/test/monitors/expected/path_profile_loop_skip.wasm.path-profile.out b/test/monitors/expected/path_profile_loop_skip.wasm.path-profile.out new file mode 100644 index 000000000..dbe0b3c6e --- /dev/null +++ b/test/monitors/expected/path_profile_loop_skip.wasm.path-profile.out @@ -0,0 +1,106 @@ +func #0 + block 100 +3 ... 5 + -> block 103 val=0 DUMMY + -> block 103 val=2 inc=2 + block 103 +5 ... 14 + -> block 104 val=0 + -> block 102 val=1 + block 102 +31 ... 34 + -> block 101 val=0 inc=1 + block 101 +34 ... 35 + block 104 +14 ... 30 + -> block 101 val=0 inc=0 DUMMY + -> block 103 val=0 BACKEDGE + caller #4 +3 + counts[0] = 3 + counts[1] = 1 + counts[2] = 1 + counts[0] = 3 + path: .. +5 +14 .. + counts[1] = 1 + path: .. +5 +31 +34 + counts[2] = 1 + path: +3 +5 +14 .. + counts[3] = 0 + path: +3 +5 +31 +34 +func #1 + block 105 +3 ... 5 + -> block 111 val=0 DUMMY + -> block 108 val=2 DUMMY + -> block 108 val=5 inc=3 + block 108 +5 ... 14 + -> block 109 val=0 + -> block 107 val=2 + block 107 +57 ... 60 + -> block 106 val=0 inc=4 + block 106 +60 ... 61 + block 109 +14 ... 20 + -> block 111 val=0 inc=2 + block 111 +20 ... 29 + -> block 112 val=0 + -> block 110 val=1 + block 110 +46 ... 56 + -> block 106 val=0 inc=1 DUMMY + -> block 108 val=0 BACKEDGE + block 112 +29 ... 45 + -> block 106 val=0 inc=0 DUMMY + -> block 111 val=0 BACKEDGE + caller #4 +8 + counts[0] = 6 + counts[1] = 3 + counts[2] = 2 + counts[4] = 1 + counts[5] = 1 + counts[0] = 6 + path: .. +20 +29 .. + counts[1] = 3 + path: .. +20 +46 .. + counts[2] = 2 + path: .. +5 +14 +20 +29 .. + counts[3] = 0 + path: .. +5 +14 +20 +46 .. + counts[4] = 1 + path: .. +5 +57 +60 + counts[5] = 1 + path: +3 +5 +14 +20 +29 .. + counts[6] = 0 + path: +3 +5 +14 +20 +46 .. + counts[7] = 0 + path: +3 +5 +57 +60 +func #2 + block 115 +1 ... 7 + -> block 114 val=0 inc=0 + block 114 +7 ... 8 + caller #4 +11 + counts[0] = 1 + caller #4 +14 + counts[0] = 1 + counts[0] = 2 + path: +1 +7 +func #3 + block 116 +1 ... 5 + -> block 119 val=0 + -> block 118 val=1 + block 118 +5 ... 8 + -> block 120 val=0 + block 120 +10 ... 11 + -> block 117 val=0 inc=1 + block 117 +11 ... 12 + block 119 +8 ... 10 + -> block 120 val=0 inc=-1 + caller #4 +19 + counts[0] = 1 + caller #4 +24 + counts[1] = 1 + counts[0] = 1 + path: +1 +8 +10 +11 + counts[1] = 1 + path: +1 +5 +10 +11 +func #4 + block 121 +1 ... 27 + -> block 122 val=0 inc=0 + block 122 +27 ... 28 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +27 diff --git a/test/monitors/expected/path_profile_loop_two_backedges.wasm.path-profile.out b/test/monitors/expected/path_profile_loop_two_backedges.wasm.path-profile.out new file mode 100644 index 000000000..31c5042d9 --- /dev/null +++ b/test/monitors/expected/path_profile_loop_two_backedges.wasm.path-profile.out @@ -0,0 +1,46 @@ +func #0 + block 100 +3 ... 5 + -> block 103 val=0 DUMMY + -> block 103 val=3 inc=3 + block 103 +5 ... 14 + -> block 104 val=0 + -> block 102 val=2 + block 102 +35 ... 38 + -> block 101 val=0 inc=2 + block 101 +38 ... 39 + block 104 +14 ... 29 + -> block 107 val=0 + -> block 105 val=1 + block 105 +29 ... 31 + -> block 101 val=0 inc=1 DUMMY + -> block 103 val=0 BACKEDGE + block 107 +31 ... 34 + -> block 101 val=0 inc=0 DUMMY + -> block 103 val=0 BACKEDGE + caller #1 +3 + counts[5] = 1 + caller #1 +8 + counts[0] = 2 + counts[1] = 2 + counts[2] = 1 + counts[3] = 1 + counts[0] = 2 + path: .. +5 +14 +31 .. + counts[1] = 2 + path: .. +5 +14 +29 .. + counts[2] = 1 + path: .. +5 +35 +38 + counts[3] = 1 + path: +3 +5 +14 +31 .. + counts[4] = 0 + path: +3 +5 +14 +29 .. + counts[5] = 1 + path: +3 +5 +35 +38 +func #1 + block 108 +1 ... 11 + -> block 109 val=0 inc=0 + block 109 +11 ... 12 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +11 diff --git a/test/monitors/expected/path_profile_loop_types.wasm.path-profile.out b/test/monitors/expected/path_profile_loop_types.wasm.path-profile.out new file mode 100644 index 000000000..a5c59636c --- /dev/null +++ b/test/monitors/expected/path_profile_loop_types.wasm.path-profile.out @@ -0,0 +1,37 @@ +func #0 + block 100 +3 ... 5 + -> block 103 val=0 DUMMY + -> block 103 val=2 inc=2 + block 103 +5 ... 14 + -> block 104 val=0 + -> block 102 val=1 + block 102 +24 ... 27 + -> block 101 val=0 inc=1 + block 101 +27 ... 28 + block 104 +14 ... 23 + -> block 101 val=0 inc=0 DUMMY + -> block 103 val=0 BACKEDGE + caller #1 +3 + counts[3] = 1 + caller #1 +8 + counts[3] = 1 + caller #1 +13 + counts[0] = 4 + counts[1] = 1 + counts[2] = 1 + counts[0] = 4 + path: .. +5 +14 .. + counts[1] = 1 + path: .. +5 +24 +27 + counts[2] = 1 + path: +3 +5 +14 .. + counts[3] = 2 + path: +3 +5 +24 +27 +func #1 + block 105 +1 ... 16 + -> block 106 val=0 inc=0 + block 106 +16 ... 17 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +16 diff --git a/test/monitors/expected/path_profile_many_paths.wasm.path-profile.out b/test/monitors/expected/path_profile_many_paths.wasm.path-profile.out new file mode 100644 index 000000000..1b0dc30dc --- /dev/null +++ b/test/monitors/expected/path_profile_many_paths.wasm.path-profile.out @@ -0,0 +1,175 @@ +func #0 + block 100 +3 ... 10 + -> block 103 val=0 + -> block 102 val=16 + block 102 +10 ... 18 + -> block 104 val=0 + block 104 +25 ... 33 + -> block 106 val=0 + -> block 105 val=8 + block 105 +33 ... 41 + -> block 107 val=0 + block 107 +48 ... 56 + -> block 109 val=0 + -> block 108 val=4 + block 108 +56 ... 64 + -> block 110 val=0 + block 110 +71 ... 79 + -> block 112 val=0 + -> block 111 val=2 + block 111 +79 ... 87 + -> block 113 val=0 + block 113 +94 ... 102 + -> block 115 val=0 + -> block 114 val=1 + block 114 +102 ... 110 + -> block 116 val=0 + block 116 +117 ... 120 + -> block 101 val=0 inc=31 + block 101 +120 ... 121 + block 115 +110 ... 117 + -> block 116 val=0 inc=-1 + block 112 +87 ... 94 + -> block 113 val=0 inc=-2 + block 109 +64 ... 71 + -> block 110 val=0 inc=-4 + block 106 +41 ... 48 + -> block 107 val=0 inc=-8 + block 103 +18 ... 25 + -> block 104 val=0 inc=-16 + caller #1 +3 + counts[0] = 1 + caller #1 +8 + counts[16] = 1 + caller #1 +13 + counts[8] = 1 + caller #1 +18 + counts[24] = 1 + caller #1 +23 + counts[4] = 1 + caller #1 +28 + counts[20] = 1 + caller #1 +33 + counts[12] = 1 + caller #1 +38 + counts[28] = 1 + caller #1 +43 + counts[2] = 1 + caller #1 +48 + counts[18] = 1 + caller #1 +53 + counts[10] = 1 + caller #1 +58 + counts[26] = 1 + caller #1 +63 + counts[6] = 1 + caller #1 +68 + counts[22] = 1 + caller #1 +73 + counts[14] = 1 + caller #1 +78 + counts[30] = 1 + caller #1 +83 + counts[1] = 1 + caller #1 +88 + counts[17] = 1 + caller #1 +93 + counts[9] = 1 + caller #1 +98 + counts[25] = 1 + caller #1 +103 + counts[5] = 1 + caller #1 +108 + counts[21] = 1 + caller #1 +113 + counts[13] = 1 + caller #1 +118 + counts[29] = 1 + caller #1 +123 + counts[3] = 1 + caller #1 +128 + counts[19] = 1 + caller #1 +133 + counts[11] = 1 + caller #1 +138 + counts[27] = 1 + caller #1 +143 + counts[7] = 1 + caller #1 +148 + counts[23] = 1 + caller #1 +153 + counts[15] = 1 + caller #1 +158 + counts[31] = 1 + counts[0] = 1 + path: +3 +18 +25 +41 +48 +64 +71 +87 +94 +110 +117 +120 + counts[1] = 1 + path: +3 +18 +25 +41 +48 +64 +71 +87 +94 +102 +117 +120 + counts[2] = 1 + path: +3 +18 +25 +41 +48 +64 +71 +79 +94 +110 +117 +120 + counts[3] = 1 + path: +3 +18 +25 +41 +48 +64 +71 +79 +94 +102 +117 +120 + counts[4] = 1 + path: +3 +18 +25 +41 +48 +56 +71 +87 +94 +110 +117 +120 + counts[5] = 1 + path: +3 +18 +25 +41 +48 +56 +71 +87 +94 +102 +117 +120 + counts[6] = 1 + path: +3 +18 +25 +41 +48 +56 +71 +79 +94 +110 +117 +120 + counts[7] = 1 + path: +3 +18 +25 +41 +48 +56 +71 +79 +94 +102 +117 +120 + counts[8] = 1 + path: +3 +18 +25 +33 +48 +64 +71 +87 +94 +110 +117 +120 + counts[9] = 1 + path: +3 +18 +25 +33 +48 +64 +71 +87 +94 +102 +117 +120 + counts[10] = 1 + path: +3 +18 +25 +33 +48 +64 +71 +79 +94 +110 +117 +120 + counts[11] = 1 + path: +3 +18 +25 +33 +48 +64 +71 +79 +94 +102 +117 +120 + counts[12] = 1 + path: +3 +18 +25 +33 +48 +56 +71 +87 +94 +110 +117 +120 + counts[13] = 1 + path: +3 +18 +25 +33 +48 +56 +71 +87 +94 +102 +117 +120 + counts[14] = 1 + path: +3 +18 +25 +33 +48 +56 +71 +79 +94 +110 +117 +120 + counts[15] = 1 + path: +3 +18 +25 +33 +48 +56 +71 +79 +94 +102 +117 +120 + counts[16] = 1 + path: +3 +10 +25 +41 +48 +64 +71 +87 +94 +110 +117 +120 + counts[17] = 1 + path: +3 +10 +25 +41 +48 +64 +71 +87 +94 +102 +117 +120 + counts[18] = 1 + path: +3 +10 +25 +41 +48 +64 +71 +79 +94 +110 +117 +120 + counts[19] = 1 + path: +3 +10 +25 +41 +48 +64 +71 +79 +94 +102 +117 +120 + counts[20] = 1 + path: +3 +10 +25 +41 +48 +56 +71 +87 +94 +110 +117 +120 + counts[21] = 1 + path: +3 +10 +25 +41 +48 +56 +71 +87 +94 +102 +117 +120 + counts[22] = 1 + path: +3 +10 +25 +41 +48 +56 +71 +79 +94 +110 +117 +120 + counts[23] = 1 + path: +3 +10 +25 +41 +48 +56 +71 +79 +94 +102 +117 +120 + counts[24] = 1 + path: +3 +10 +25 +33 +48 +64 +71 +87 +94 +110 +117 +120 + counts[25] = 1 + path: +3 +10 +25 +33 +48 +64 +71 +87 +94 +102 +117 +120 + counts[26] = 1 + path: +3 +10 +25 +33 +48 +64 +71 +79 +94 +110 +117 +120 + counts[27] = 1 + path: +3 +10 +25 +33 +48 +64 +71 +79 +94 +102 +117 +120 + counts[28] = 1 + path: +3 +10 +25 +33 +48 +56 +71 +87 +94 +110 +117 +120 + counts[29] = 1 + path: +3 +10 +25 +33 +48 +56 +71 +87 +94 +102 +117 +120 + counts[30] = 1 + path: +3 +10 +25 +33 +48 +56 +71 +79 +94 +110 +117 +120 + counts[31] = 1 + path: +3 +10 +25 +33 +48 +56 +71 +79 +94 +102 +117 +120 +func #1 + block 117 +1 ... 161 + -> block 118 val=0 inc=0 + block 118 +161 ... 162 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +161 diff --git a/test/monitors/expected/path_profile_mixed_nest.wasm.path-profile.out b/test/monitors/expected/path_profile_mixed_nest.wasm.path-profile.out new file mode 100644 index 000000000..290e5b272 --- /dev/null +++ b/test/monitors/expected/path_profile_mixed_nest.wasm.path-profile.out @@ -0,0 +1,89 @@ +func #0 + block 100 +1 ... 14 + -> block 102 val=0 + -> block 103 val=1 + -> block 104 val=2 + block 104 +14 ... 19 + -> block 106 val=0 + -> block 105 val=1 + block 105 +19 ... 22 + -> block 107 val=0 + block 107 +24 ... 26 + -> block 101 val=0 inc=3 + block 106 +22 ... 24 + -> block 107 val=0 inc=-1 + block 103 +26 ... 30 + -> block 101 val=0 inc=1 + block 102 +30 ... 33 + -> block 101 val=0 inc=0 + block 101 +33 ... 34 + caller #2 +5 + counts[2] = 1 + caller #2 +12 + counts[3] = 1 + caller #2 +19 + counts[3] = 1 + caller #2 +26 + counts[1] = 1 + caller #2 +33 + counts[1] = 1 + caller #2 +40 + counts[1] = 1 + caller #2 +47 + counts[0] = 1 + caller #2 +54 + counts[0] = 1 + caller #2 +61 + counts[0] = 1 + caller #2 +68 + counts[0] = 1 + counts[0] = 4 + path: +1 +30 +33 + counts[1] = 3 + path: +1 +26 +33 + counts[2] = 1 + path: +1 +14 +22 +24 +33 + counts[3] = 2 + path: +1 +14 +19 +24 +33 +func #1 + block 108 +1 ... 5 + -> block 111 val=0 + -> block 110 val=1 + block 110 +5 ... 15 + -> block 113 val=0 + -> block 114 val=1 + block 114 +15 ... 19 + -> block 109 val=0 inc=2 + block 113 +19 ... 23 + -> block 109 val=0 inc=1 + block 111 +24 ... 25 + -> block 112 val=0 + block 112 +25 ... 28 + -> block 109 val=0 inc=0 + block 109 +28 ... 29 + caller #2 +75 + counts[2] = 1 + caller #2 +82 + counts[1] = 1 + caller #2 +89 + counts[1] = 1 + caller #2 +96 + counts[0] = 1 + caller #2 +103 + counts[0] = 1 + caller #2 +110 + counts[0] = 1 + counts[0] = 3 + path: +1 +24 +25 +28 + counts[1] = 2 + path: +1 +5 +19 +28 + counts[2] = 1 + path: +1 +5 +15 +28 +func #2 + block 115 +1 ... 113 + -> block 116 val=0 inc=0 + block 116 +113 ... 114 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +113 diff --git a/test/monitors/expected/path_profile_one_armed.wasm.path-profile.out b/test/monitors/expected/path_profile_one_armed.wasm.path-profile.out new file mode 100644 index 000000000..96b3584a5 --- /dev/null +++ b/test/monitors/expected/path_profile_one_armed.wasm.path-profile.out @@ -0,0 +1,25 @@ +func #0 + block 100 +3 ... 11 + -> block 104 val=0 + -> block 102 val=1 + block 102 +11 ... 15 + -> block 104 val=0 inc=1 + block 104 +15 ... 18 + -> block 101 val=0 inc=0 + block 101 +18 ... 19 + caller #1 +3 + counts[0] = 1 + caller #1 +8 + counts[1] = 1 + counts[0] = 1 + path: +3 +15 +18 + counts[1] = 1 + path: +3 +11 +15 +18 +func #1 + block 105 +1 ... 11 + -> block 106 val=0 inc=0 + block 106 +11 ... 12 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +11 diff --git a/test/monitors/expected/path_profile_recur.wasm.path-profile.out b/test/monitors/expected/path_profile_recur.wasm.path-profile.out new file mode 100644 index 000000000..0ea0ca91a --- /dev/null +++ b/test/monitors/expected/path_profile_recur.wasm.path-profile.out @@ -0,0 +1,34 @@ +func #0 + block 100 +1 ... 8 + -> block 103 val=0 + -> block 102 val=1 + block 102 +8 ... 11 + -> block 104 val=0 + block 104 +21 ... 22 + -> block 101 val=0 inc=1 + block 101 +22 ... 23 + block 103 +11 ... 21 + -> block 104 val=0 inc=-1 + caller #1 +3 + counts[1] = 1 + caller #0 +18 + counts[0] = 4 + counts[1] = 3 + caller #1 +8 + counts[0] = 1 + caller #1 +13 + counts[0] = 1 + caller #1 +18 + counts[0] = 1 + counts[0] = 7 + path: +1 +11 +21 +22 + counts[1] = 4 + path: +1 +8 +21 +22 +func #1 + block 105 +1 ... 21 + -> block 106 val=0 inc=0 + block 106 +21 ... 22 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +21 diff --git a/test/monitors/expected/path_profile_returns.wasm.path-profile.out b/test/monitors/expected/path_profile_returns.wasm.path-profile.out new file mode 100644 index 000000000..f2624c705 --- /dev/null +++ b/test/monitors/expected/path_profile_returns.wasm.path-profile.out @@ -0,0 +1,61 @@ +func #0 + block 100 +1 ... 5 + -> block 104 val=0 + -> block 102 val=2 + block 102 +5 ... 8 + -> block 101 val=0 inc=2 + block 104 +8 ... 13 + -> block 107 val=0 + -> block 105 val=1 + block 105 +13 ... 16 + -> block 101 val=0 inc=1 + block 107 +16 ... 19 + -> block 101 val=0 inc=0 + block 101 +19 ... 20 + caller #2 +5 + counts[2] = 1 + caller #2 +12 + counts[1] = 1 + caller #2 +19 + counts[1] = 1 + caller #2 +26 + counts[0] = 1 + caller #2 +33 + counts[0] = 1 + caller #2 +40 + counts[0] = 1 + counts[0] = 3 + path: +1 +8 +16 +19 + counts[1] = 2 + path: +1 +8 +13 +19 + counts[2] = 1 + path: +1 +5 +19 +func #1 + block 108 +1 ... 5 + -> block 111 val=0 + -> block 110 val=1 + block 110 +5 ... 8 + -> block 109 val=0 inc=1 + block 111 +9 ... 10 + -> block 112 val=0 + block 112 +10 ... 13 + -> block 109 val=0 inc=0 + block 109 +13 ... 14 + caller #2 +45 + counts[1] = 1 + caller #2 +50 + counts[0] = 1 + caller #2 +55 + counts[0] = 1 + counts[0] = 2 + path: +1 +9 +10 +13 + counts[1] = 1 + path: +1 +5 +13 +func #2 + block 113 +1 ... 58 + -> block 114 val=0 inc=0 + block 114 +58 ... 59 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +58 diff --git a/test/monitors/expected/path_profile_select_call.wasm.path-profile.out b/test/monitors/expected/path_profile_select_call.wasm.path-profile.out new file mode 100644 index 000000000..410e95937 --- /dev/null +++ b/test/monitors/expected/path_profile_select_call.wasm.path-profile.out @@ -0,0 +1,53 @@ +func #0 + block 100 +1 ... 8 + -> block 101 val=0 inc=0 + block 101 +8 ... 9 + caller #3 +7 + counts[0] = 1 + caller #3 +16 + counts[0] = 1 + caller #3 +25 + counts[0] = 1 + caller #3 +34 + counts[0] = 1 + counts[0] = 4 + path: +1 +8 +func #1 + block 102 +1 ... 6 + -> block 103 val=0 inc=0 + block 103 +6 ... 7 + caller #2 +7 + counts[0] = 5 + counts[0] = 5 + path: +1 +6 +func #2 + block 104 +3 ... 11 + -> block 107 val=0 + -> block 106 val=1 + block 106 +15 ... 18 + -> block 105 val=0 inc=1 + block 105 +18 ... 19 + block 107 +11 ... 15 + -> block 106 val=0 inc=-1 + caller #3 +39 + counts[1] = 1 + caller #3 +44 + counts[1] = 1 + caller #3 +49 + counts[0] = 1 + caller #3 +54 + counts[0] = 1 + caller #3 +59 + counts[0] = 1 + counts[0] = 3 + path: +3 +11 +15 +18 + counts[1] = 2 + path: +3 +15 +18 +func #3 + block 108 +1 ... 62 + -> block 109 val=0 inc=0 + block 109 +62 ... 63 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +62 diff --git a/test/monitors/expected/path_profile_selfloop.wasm.path-profile.out b/test/monitors/expected/path_profile_selfloop.wasm.path-profile.out new file mode 100644 index 000000000..f7601609e --- /dev/null +++ b/test/monitors/expected/path_profile_selfloop.wasm.path-profile.out @@ -0,0 +1,46 @@ +func #0 + block 102 +1 ... 14 + -> block 103 val=0 + -> block 102 val=0 BACKEDGE + block 103 +14 ... 17 + -> block 101 val=0 inc=0 + block 101 +17 ... 18 + caller #3 +3 + counts[0] = 1 + counts[0] = 1 + path: +1 +14 +17 + selfloop[block 102 +12 arm 1] = 2 +func #1 + block 106 +1 ... 17 + -> block 109 val=0 + -> block 107 val=2 + block 107 +17 ... 18 + -> block 109 val=0 inc=2 + block 109 +18 ... 23 + -> block 105 val=0 inc=0 DUMMY + -> block 110 val=1 + -> block 106 val=0 BACKEDGE + block 110 +23 ... 26 + -> block 105 val=0 inc=1 + block 105 +26 ... 27 + caller #3 +8 + counts[0] = 1 + counts[1] = 1 + counts[2] = 1 + counts[0] = 1 + path: +1 +18 .. + counts[1] = 1 + path: +1 +18 +23 +26 + counts[2] = 1 + path: +1 +17 +18 .. + counts[3] = 0 + path: +1 +17 +18 +23 +26 +func #2: not profiled, no acyclic path reaches the exit +func #3 + block 114 +1 ... 11 + -> block 115 val=0 inc=0 + block 115 +11 ... 12 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +11 diff --git a/test/monitors/expected/path_profile_selfloop_dup.wasm.path-profile.out b/test/monitors/expected/path_profile_selfloop_dup.wasm.path-profile.out new file mode 100644 index 000000000..b364169e0 --- /dev/null +++ b/test/monitors/expected/path_profile_selfloop_dup.wasm.path-profile.out @@ -0,0 +1,24 @@ +func #0 + block 100 +3 ... 5 + -> block 103 val=0 + block 103 +5 ... 32 + -> block 102 val=0 + -> block 103 val=0 BACKEDGE + -> block 103 val=0 BACKEDGE + block 102 +33 ... 36 + -> block 101 val=0 inc=0 + block 101 +36 ... 37 + caller #1 +3 + counts[0] = 1 + counts[0] = 1 + path: +3 +5 +33 +36 + selfloop[block 103 +27 arm 0] = 2 + selfloop[block 103 +27 arm 1] = 3 +func #1 + block 104 +1 ... 6 + -> block 105 val=0 inc=0 + block 105 +6 ... 7 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +6 diff --git a/test/monitors/expected/path_profile_shapes.wasm.path-profile.out b/test/monitors/expected/path_profile_shapes.wasm.path-profile.out new file mode 100644 index 000000000..87a5f5f4b --- /dev/null +++ b/test/monitors/expected/path_profile_shapes.wasm.path-profile.out @@ -0,0 +1,54 @@ +func #0 + block 100 +1 ... 3 + -> block 102 val=0 + block 102 +3 ... 6 + -> block 101 val=0 inc=0 + block 101 +6 ... 7 + caller #4 +1 + counts[0] = 1 + counts[0] = 1 + path: +1 +3 +6 +func #1 + block 103 +1 ... 7 + -> block 105 val=0 + -> block 105 val=0 inc=0 + block 105 +7 ... 10 + -> block 104 val=0 inc=0 + block 104 +10 ... 11 + caller #4 +6 + counts[0] = 1 + counts[0] = 1 + path: +1 +7 +10 +func #2 + block 107 +1 ... 5 + -> block 111 val=0 + -> block 111 val=0 inc=0 + block 111 +5 ... 8 + -> block 108 val=0 inc=0 + block 108 +8 ... 9 + caller #4 +11 + counts[0] = 1 + counts[0] = 1 + path: +1 +5 +8 +func #3 + block 112 +1 ... 7 + -> block 116 val=0 + block 116 +7 ... 8 + -> block 115 val=0 + block 115 +8 ... 9 + -> block 114 val=0 + block 114 +9 ... 12 + -> block 113 val=0 inc=0 + block 113 +12 ... 13 + caller #4 +14 + counts[0] = 1 + counts[0] = 1 + path: +1 +7 +8 +9 +12 +func #4 + block 117 +1 ... 17 + -> block 118 val=0 inc=0 + block 118 +17 ... 18 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +17 diff --git a/test/monitors/expected/path_profile_straight.wasm.path-profile.out b/test/monitors/expected/path_profile_straight.wasm.path-profile.out new file mode 100644 index 000000000..081a4229f --- /dev/null +++ b/test/monitors/expected/path_profile_straight.wasm.path-profile.out @@ -0,0 +1,44 @@ +func #0 + block 100 +1 ... 6 + -> block 101 val=0 inc=0 + block 101 +6 ... 7 + caller #3 +3 + counts[0] = 1 + caller #3 +8 + counts[0] = 1 + caller #3 +13 + counts[0] = 1 + caller #2 +3 + counts[0] = 2 + counts[0] = 5 + path: +1 +6 +func #1 + block 102 +1 ... 8 + -> block 103 val=0 inc=0 + block 103 +8 ... 9 + caller #3 +16 + counts[0] = 1 + caller #3 +19 + counts[0] = 1 + caller #2 +5 + counts[0] = 2 + counts[0] = 4 + path: +1 +8 +func #2 + block 104 +1 ... 8 + -> block 105 val=0 inc=0 + block 105 +8 ... 9 + caller #3 +24 + counts[0] = 1 + caller #3 +29 + counts[0] = 1 + counts[0] = 2 + path: +1 +8 +func #3 + block 106 +1 ... 32 + -> block 107 val=0 inc=0 + block 107 +32 ... 33 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +32 diff --git a/test/monitors/expected/path_profile_table1.wasm.path-profile.out b/test/monitors/expected/path_profile_table1.wasm.path-profile.out new file mode 100644 index 000000000..01e6de658 --- /dev/null +++ b/test/monitors/expected/path_profile_table1.wasm.path-profile.out @@ -0,0 +1,22 @@ +func #0 + block 100 +1 ... 8 + -> block 102 val=0 + block 102 +8 ... 11 + -> block 101 val=0 inc=0 + block 101 +11 ... 12 + caller #1 +3 + counts[0] = 1 + caller #1 +8 + counts[0] = 1 + caller #1 +14 + counts[0] = 1 + counts[0] = 3 + path: +1 +8 +11 +func #1 + block 103 +1 ... 17 + -> block 104 val=0 inc=0 + block 104 +17 ... 18 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +17 diff --git a/test/monitors/expected/path_profile_table2.wasm.path-profile.out b/test/monitors/expected/path_profile_table2.wasm.path-profile.out new file mode 100644 index 000000000..5e5808bc7 --- /dev/null +++ b/test/monitors/expected/path_profile_table2.wasm.path-profile.out @@ -0,0 +1,33 @@ +func #0 + block 100 +1 ... 11 + -> block 102 val=0 + -> block 103 val=1 + block 103 +11 ... 15 + -> block 101 val=0 inc=1 + block 102 +15 ... 18 + -> block 101 val=0 inc=0 + block 101 +18 ... 19 + caller #1 +3 + counts[1] = 1 + caller #1 +8 + counts[1] = 1 + caller #1 +13 + counts[0] = 1 + caller #1 +18 + counts[0] = 1 + caller #1 +23 + counts[0] = 1 + caller #1 +28 + counts[0] = 1 + counts[0] = 4 + path: +1 +15 +18 + counts[1] = 2 + path: +1 +11 +18 +func #1 + block 104 +1 ... 31 + -> block 105 val=0 inc=0 + block 105 +31 ... 32 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +31 diff --git a/test/monitors/expected/path_profile_table_default.wasm.path-profile.out b/test/monitors/expected/path_profile_table_default.wasm.path-profile.out new file mode 100644 index 000000000..1475c01da --- /dev/null +++ b/test/monitors/expected/path_profile_table_default.wasm.path-profile.out @@ -0,0 +1,34 @@ +func #0 + block 100 +1 ... 14 + -> block 102 val=0 + -> block 103 val=1 + -> block 104 val=2 + block 104 +14 ... 18 + -> block 101 val=0 inc=2 + block 103 +18 ... 22 + -> block 101 val=0 inc=1 + block 102 +22 ... 25 + -> block 101 val=0 inc=0 + block 101 +25 ... 26 + caller #1 +3 + counts[0] = 1 + caller #1 +8 + counts[0] = 1 + caller #1 +14 + counts[0] = 1 + caller #1 +19 + counts[0] = 1 + counts[0] = 4 + path: +1 +22 +25 + counts[1] = 0 + path: +1 +18 +25 + counts[2] = 0 + path: +1 +14 +25 +func #1 + block 105 +1 ... 22 + -> block 106 val=0 inc=0 + block 106 +22 ... 23 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +22 diff --git a/test/monitors/expected/path_profile_trap.wasm.path-profile.out b/test/monitors/expected/path_profile_trap.wasm.path-profile.out new file mode 100644 index 000000000..a0a777917 --- /dev/null +++ b/test/monitors/expected/path_profile_trap.wasm.path-profile.out @@ -0,0 +1,20 @@ +func #0 + block 100 +1 ... 5 + -> block 104 val=0 + -> block 102 val=1 + block 102 +5 ... 6 + block 104 +6 ... 9 + -> block 101 val=0 inc=0 + block 101 +9 ... 10 + caller #1 +3 + counts[0] = 1 + counts[0] = 1 + path: +1 +6 +9 +func #1 + block 105 +1 ... 6 + -> block 106 val=0 inc=0 + block 106 +6 ... 7 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +6 diff --git a/test/monitors/expected/path_profile_wide_merge.wasm.path-profile.out b/test/monitors/expected/path_profile_wide_merge.wasm.path-profile.out new file mode 100644 index 000000000..9dd0a1926 --- /dev/null +++ b/test/monitors/expected/path_profile_wide_merge.wasm.path-profile.out @@ -0,0 +1,68 @@ +func #0 + block 100 +3 ... 24 + -> block 103 val=0 + -> block 104 val=1 + -> block 105 val=2 + -> block 106 val=3 + -> block 107 val=4 + block 107 +24 ... 31 + -> block 102 val=0 + block 102 +57 ... 63 + -> block 101 val=0 inc=4 + block 101 +63 ... 64 + block 106 +31 ... 38 + -> block 102 val=0 inc=-1 + block 105 +38 ... 45 + -> block 102 val=0 inc=-2 + block 104 +45 ... 52 + -> block 102 val=0 inc=-3 + block 103 +52 ... 57 + -> block 102 val=0 inc=-4 + caller #1 +3 + counts[4] = 1 + caller #1 +8 + counts[3] = 1 + caller #1 +13 + counts[3] = 1 + caller #1 +18 + counts[2] = 1 + caller #1 +23 + counts[2] = 1 + caller #1 +28 + counts[2] = 1 + caller #1 +33 + counts[1] = 1 + caller #1 +38 + counts[1] = 1 + caller #1 +43 + counts[1] = 1 + caller #1 +48 + counts[1] = 1 + caller #1 +53 + counts[0] = 1 + caller #1 +58 + counts[0] = 1 + caller #1 +63 + counts[0] = 1 + caller #1 +68 + counts[0] = 1 + caller #1 +73 + counts[0] = 1 + counts[0] = 5 + path: +3 +52 +57 +63 + counts[1] = 4 + path: +3 +45 +57 +63 + counts[2] = 3 + path: +3 +38 +57 +63 + counts[3] = 2 + path: +3 +31 +57 +63 + counts[4] = 1 + path: +3 +24 +57 +63 +func #1 + block 108 +1 ... 76 + -> block 109 val=0 inc=0 + block 109 +76 ... 77 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +76 diff --git a/test/monitors/expected/path_profile_wide_only.wasm.path-profile.out b/test/monitors/expected/path_profile_wide_only.wasm.path-profile.out new file mode 100644 index 000000000..ba92264f7 --- /dev/null +++ b/test/monitors/expected/path_profile_wide_only.wasm.path-profile.out @@ -0,0 +1,57 @@ +func #0 + block 100 +1 ... 23 + -> block 102 val=0 + -> block 103 val=1 + -> block 104 val=2 + -> block 105 val=3 + -> block 106 val=4 + -> block 107 val=5 + block 107 +23 ... 25 + -> block -1 val=0 inc=5 + block 106 +25 ... 27 + -> block -1 val=0 inc=4 + block 105 +27 ... 29 + -> block -1 val=0 inc=3 + block 104 +29 ... 31 + -> block -1 val=0 inc=2 + block 103 +31 ... 33 + -> block -1 val=0 inc=1 + block 102 +33 ... 35 + -> block -1 val=0 inc=0 + block -1 +35 ... 36 + caller #1 +3 + counts[5] = 1 + caller #1 +7 + counts[4] = 1 + caller #1 +11 + counts[3] = 1 + caller #1 +15 + counts[2] = 1 + caller #1 +19 + counts[1] = 1 + caller #1 +23 + counts[0] = 1 + caller #1 +27 + counts[0] = 1 + caller #1 +31 + counts[0] = 1 + counts[0] = 3 + path: +1 +33 +35 + counts[1] = 1 + path: +1 +31 +35 + counts[2] = 1 + path: +1 +29 +35 + counts[3] = 1 + path: +1 +27 +35 + counts[4] = 1 + path: +1 +25 +35 + counts[5] = 1 + path: +1 +23 +35 +func #1 + block 108 +1 ... 33 + -> block 109 val=0 inc=0 + block 109 +33 ... 34 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +33 diff --git a/test/monitors/expected/profile_bytecode.wasm.path-profile.out b/test/monitors/expected/profile_bytecode.wasm.path-profile.out new file mode 100644 index 000000000..5fa1af132 --- /dev/null +++ b/test/monitors/expected/profile_bytecode.wasm.path-profile.out @@ -0,0 +1,281 @@ +func #0 + block 100 +1 ... 14 + -> block 101 val=0 inc=0 + block 101 +14 ... 15 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +14 +func #1 + block 102 +3 ... 8 + -> block 107 val=0 DUMMY + -> block 106 val=15 + -> block 104 val=30 + block 104 +8 ... 9 + block 106 +9 ... 21 + -> block 107 val=0 inc=15 + block 107 +21 ... 30 + -> block 109 val=0 + -> block 108 val=1 + block 108 +30 ... 71 + -> block 131 val=0 + -> block 129 val=14 + block 129 +71 ... 72 + block 131 +72 ... 83 + -> block 134 val=0 + -> block 132 val=14 + block 132 +83 ... 84 + block 134 +84 ... 115 + -> block 116 val=0 + -> block 114 val=1 + -> block 117 val=2 + -> block 118 val=3 + -> block 113 val=4 + -> block 112 val=4 + -> block 111 val=4 + -> block 119 val=4 + -> block 120 val=5 + -> block 121 val=6 + -> block 122 val=7 + -> block 123 val=8 + -> block 124 val=9 + -> block 125 val=10 + -> block 126 val=11 + -> block 127 val=12 + -> block 128 val=13 + block 128 +115 ... 125 + -> block 115 val=0 + block 115 +368 ... 375 + -> block -1 val=0 inc=14 DUMMY + -> block 107 val=0 BACKEDGE + block 127 +125 ... 135 + -> block 115 val=0 inc=-1 + block 126 +135 ... 145 + -> block 115 val=0 inc=-2 + block 125 +145 ... 155 + -> block 115 val=0 inc=-3 + block 124 +155 ... 165 + -> block 115 val=0 inc=-4 + block 123 +165 ... 175 + -> block 115 val=0 inc=-5 + block 122 +175 ... 185 + -> block 115 val=0 inc=-6 + block 121 +185 ... 211 + -> block 137 val=0 + -> block 135 val=1 + block 135 +211 ... 212 + block 137 +212 ... 236 + -> block 140 val=0 + -> block 138 val=1 + block 138 +236 ... 237 + block 140 +237 ... 259 + -> block 115 val=0 inc=-7 + block 120 +259 ... 274 + -> block 115 val=0 inc=-8 + block 119 +274 ... 289 + -> block 115 val=0 inc=-9 + block 111 +381 ... 383 + block 112 +379 ... 381 + block 113 +377 ... 379 + block 118 +289 ... 311 + -> block 143 val=0 + -> block 141 val=1 + block 141 +311 ... 312 + block 143 +312 ... 325 + -> block 115 val=0 inc=-10 + block 117 +325 ... 347 + -> block 146 val=0 + -> block 144 val=1 + block 144 +347 ... 348 + block 146 +348 ... 361 + -> block 115 val=0 inc=-11 + block 114 +375 ... 377 + -> block -1 val=0 inc=2 + block 116 +361 ... 368 + -> block 115 val=0 inc=-13 + block 109 +384 ... 385 + -> block -1 val=0 inc=0 + block -1 +387 ... 388 + caller #0 +10 + counts[2] = 1 + counts[4] = 1 + counts[7] = 2 + counts[11] = 2 + counts[12] = 2 + counts[13] = 1 + counts[25] = 1 + caller #1 +248 + counts[0] = 1 + counts[2] = 2 + counts[3] = 1 + counts[4] = 1 + counts[5] = 1 + counts[6] = 1 + counts[7] = 1 + counts[8] = 1 + counts[9] = 1 + counts[10] = 1 + counts[11] = 3 + counts[12] = 3 + counts[13] = 1 + counts[19] = 1 + counts[28] = 2 + counts[0] = 1 + path: .. +21 +384 +387 + counts[1] = 0 + path: .. +21 +30 +72 +84 +361 +368 .. + counts[2] = 3 + path: .. +21 +30 +72 +84 +375 +387 + counts[3] = 1 + path: .. +21 +30 +72 +84 +325 +348 +368 .. + counts[4] = 2 + path: .. +21 +30 +72 +84 +289 +312 +368 .. + counts[5] = 1 + path: .. +21 +30 +72 +84 +274 +368 .. + counts[6] = 1 + path: .. +21 +30 +72 +84 +259 +368 .. + counts[7] = 3 + path: .. +21 +30 +72 +84 +185 +212 +237 +368 .. + counts[8] = 1 + path: .. +21 +30 +72 +84 +175 +368 .. + counts[9] = 1 + path: .. +21 +30 +72 +84 +165 +368 .. + counts[10] = 1 + path: .. +21 +30 +72 +84 +155 +368 .. + counts[11] = 5 + path: .. +21 +30 +72 +84 +145 +368 .. + counts[12] = 5 + path: .. +21 +30 +72 +84 +135 +368 .. + counts[13] = 2 + path: .. +21 +30 +72 +84 +125 +368 .. + counts[14] = 0 + path: .. +21 +30 +72 +84 +115 +368 .. + counts[15] = 0 + path: +3 +9 +21 +384 +387 + counts[16] = 0 + path: +3 +9 +21 +30 +72 +84 +361 +368 .. + counts[17] = 0 + path: +3 +9 +21 +30 +72 +84 +375 +387 + counts[18] = 0 + path: +3 +9 +21 +30 +72 +84 +325 +348 +368 .. + counts[19] = 1 + path: +3 +9 +21 +30 +72 +84 +289 +312 +368 .. + counts[20] = 0 + path: +3 +9 +21 +30 +72 +84 +274 +368 .. + counts[21] = 0 + path: +3 +9 +21 +30 +72 +84 +259 +368 .. + counts[22] = 0 + path: +3 +9 +21 +30 +72 +84 +185 +212 +237 +368 .. + counts[23] = 0 + path: +3 +9 +21 +30 +72 +84 +175 +368 .. + counts[24] = 0 + path: +3 +9 +21 +30 +72 +84 +165 +368 .. + counts[25] = 1 + path: +3 +9 +21 +30 +72 +84 +155 +368 .. + counts[26] = 0 + path: +3 +9 +21 +30 +72 +84 +145 +368 .. + counts[27] = 0 + path: +3 +9 +21 +30 +72 +84 +135 +368 .. + counts[28] = 2 + path: +3 +9 +21 +30 +72 +84 +125 +368 .. + counts[29] = 0 + path: +3 +9 +21 +30 +72 +84 +115 +368 .. +func #2 + block 147 +3 ... 12 + -> block 150 val=0 + -> block 149 val=1 + block 149 +12 ... 21 + -> block 151 val=0 + block 151 +27 ... 30 + -> block 148 val=0 inc=1 + block 148 +30 ... 31 + block 150 +21 ... 27 + -> block 151 val=0 inc=-1 + caller #1 +262 + counts[1] = 1 + counts[0] = 0 + path: +3 +21 +27 +30 + counts[1] = 1 + path: +3 +12 +27 +30 +func #3 + block 152 +1 ... 3 + -> block 153 val=0 inc=0 + block 153 +3 ... 4 + caller #1 +277 + counts[0] = 1 + counts[0] = 1 + path: +1 +3 +func #4 + block 154 +1 ... 9 + -> block 155 val=0 inc=0 + block 155 +9 ... 10 + caller #3 +1 + counts[0] = 1 + counts[0] = 1 + path: +1 +9 +func #5 + block 156 +1 ... 4 + -> block 157 val=0 inc=0 + block 157 +4 ... 5 + caller #4 +1 + counts[0] = 1 + counts[0] = 1 + path: +1 +4 +func #6 + block 158 +1 ... 5 + -> block -1 val=0 inc=0 + block -1 +5 ... 6 + caller #4 +4 + counts[0] = 1 + counts[0] = 1 + path: +1 +5 +func #7 + block 160 +1 ... 3 + -> block 161 val=0 inc=0 + block 161 +3 ... 4 + caller #6 +1 + counts[0] = 1 + counts[0] = 1 + path: +1 +3 +func #8 + block 162 +1 ... 5 + -> block 163 val=0 inc=0 + block 163 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #9 + block 164 +1 ... 5 + -> block 165 val=0 inc=0 + block 165 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #10 + block 166 +1 ... 3 + -> block 167 val=0 inc=0 + block 167 +3 ... 4 + counts[0] = 0 + path: +1 +3 +func #11 + block 168 +1 ... 3 + -> block 169 val=0 inc=0 + block 169 +3 ... 4 + counts[0] = 0 + path: +1 +3 +func #12 + block 170 +1 ... 3 + -> block 171 val=0 inc=0 + block 171 +3 ... 4 + counts[0] = 0 + path: +1 +3 +func #13 + block 172 +1 ... 3 + -> block 173 val=0 inc=0 + block 173 +3 ... 4 + counts[0] = 0 + path: +1 +3 +func #14 + block 174 +1 ... 5 + -> block 175 val=0 inc=0 + block 175 +5 ... 6 + counts[0] = 0 + path: +1 +5 diff --git a/test/monitors/expected/profile_bytecode_ai.wasm.path-profile.out b/test/monitors/expected/profile_bytecode_ai.wasm.path-profile.out new file mode 100644 index 000000000..aea666510 --- /dev/null +++ b/test/monitors/expected/profile_bytecode_ai.wasm.path-profile.out @@ -0,0 +1,1394 @@ +func #4 + block 100 +3 ... 44 + -> block 104 val=0 + -> block 102 val=1 + block 102 +44 ... 53 + -> block 101 val=0 inc=1 + block 104 +53 ... 59 + -> block 101 val=0 inc=0 + block 101 +59 ... 60 + caller #7 +40 + counts[0] = 1 + caller #9 +6 + counts[0] = 1 + counts[0] = 2 + path: +3 +53 +59 + counts[1] = 0 + path: +3 +44 +59 +func #5 + block 105 +1 ... 7 + -> block 106 val=0 inc=0 + block 106 +7 ... 8 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +7 +func #6 + block 107 +1 ... 9 + -> block 108 val=0 inc=0 + block 108 +9 ... 10 + caller #5 +5 + counts[0] = 1 + counts[0] = 1 + path: +1 +9 +func #7 + block 109 +3 ... 12 + -> block 117 val=0 DUMMY + -> block 112 val=2 + -> block 111 val=4 + block 111 +12 ... 13 + -> block 113 val=0 + block 113 +17 ... 30 + -> block 116 val=0 + -> block 114 val=2 + block 114 +30 ... 31 + block 116 +31 ... 60 + -> block 117 val=0 inc=4 + block 117 +60 ... 69 + -> block 119 val=0 + -> block 118 val=1 + block 118 +69 ... 80 + -> block 123 val=0 + -> block 121 val=1 + block 121 +80 ... 81 + block 123 +81 ... 111 + -> block 126 val=0 + -> block 124 val=1 + block 124 +111 ... 112 + block 126 +112 ... 150 + -> block 129 val=0 + -> block 127 val=1 + block 127 +150 ... 151 + block 129 +151 ... 173 + -> block -1 val=0 inc=1 DUMMY + -> block 117 val=0 BACKEDGE + block 119 +174 ... 177 + -> block -1 val=0 inc=0 + block 112 +13 ... 17 + -> block 113 val=0 inc=-2 + block -1 +180 ... 181 + caller #5 +3 + counts[2] = 1 + counts[0] = 0 + path: .. +60 +174 +180 + counts[1] = 0 + path: .. +60 +69 +81 +112 +151 .. + counts[2] = 1 + path: +3 +13 +17 +31 +60 +174 +180 + counts[3] = 0 + path: +3 +13 +17 +31 +60 +69 +81 +112 +151 .. + counts[4] = 0 + path: +3 +12 +17 +31 +60 +174 +180 + counts[5] = 0 + path: +3 +12 +17 +31 +60 +69 +81 +112 +151 .. +func #8 + block 130 +1 ... 9 + -> block 131 val=0 inc=0 + block 131 +9 ... 10 + counts[0] = 0 + path: +1 +9 +func #9 + block 132 +3 ... 29 + -> block 134 val=0 DUMMY + -> block 134 val=21 inc=21 + block 134 +29 ... 36 + -> block 137 val=0 + -> block 135 val=21 + block 135 +36 ... 37 + block 137 +37 ... 48 + -> block 139 val=0 + -> block 138 val=1 + block 138 +48 ... 53 + -> block 143 val=0 + -> block 141 val=20 + block 141 +53 ... 54 + block 143 +54 ... 65 + -> block 146 val=0 + -> block 144 val=20 + block 144 +65 ... 66 + block 146 +66 ... 79 + -> block 149 val=0 + -> block 147 val=20 + block 147 +79 ... 80 + block 149 +80 ... 91 + -> block 152 val=0 + -> block 150 val=20 + block 150 +91 ... 92 + block 152 +92 ... 108 + -> block 155 val=0 + -> block 153 val=20 + block 153 +108 ... 109 + block 155 +109 ... 122 + -> block 157 val=0 + -> block 156 val=10 + block 156 +122 ... 127 + -> block 158 val=0 + block 158 +133 ... 160 + -> block 169 val=0 + -> block 167 val=10 + block 167 +160 ... 161 + block 169 +161 ... 187 + -> block 160 val=0 + -> block 161 val=1 + -> block 162 val=2 + -> block 163 val=3 + -> block 164 val=4 + -> block 165 val=7 + -> block 166 val=9 + block 166 +187 ... 203 + -> block 172 val=0 + -> block 170 val=1 + block 170 +203 ... 204 + block 172 +204 ... 225 + -> block 159 val=0 + block 159 +565 ... 576 + -> block -1 val=0 inc=20 DUMMY + -> block 134 val=0 BACKEDGE + block 165 +225 ... 245 + -> block 175 val=0 + -> block 173 val=2 + block 173 +245 ... 246 + block 175 +246 ... 264 + -> block 177 val=0 + -> block 176 val=1 + block 176 +264 ... 277 + -> block 178 val=0 + block 178 +287 ... 314 + -> block 181 val=0 + -> block 179 val=1 + block 179 +314 ... 315 + block 181 +315 ... 336 + -> block 159 val=0 inc=-1 + block 177 +277 ... 287 + -> block 178 val=0 inc=-1 + block 164 +336 ... 356 + -> block 184 val=0 + -> block 182 val=3 + block 182 +356 ... 357 + block 184 +357 ... 389 + -> block 187 val=0 + -> block 185 val=3 + block 185 +389 ... 390 + block 187 +390 ... 408 + -> block 189 val=0 + -> block 188 val=2 + block 188 +408 ... 421 + -> block 190 val=0 + block 190 +454 ... 483 + -> block 196 val=0 + -> block 194 val=1 + block 194 +483 ... 484 + block 196 +484 ... 505 + -> block 159 val=0 inc=-3 + block 189 +421 ... 428 + -> block 192 val=0 + -> block 191 val=1 + block 191 +428 ... 442 + -> block 190 val=0 inc=-1 + block 192 +443 ... 453 + -> block 190 val=0 inc=-2 + block 163 +505 ... 520 + -> block 159 val=0 inc=-6 + block 162 +520 ... 535 + -> block 159 val=0 inc=-7 + block 161 +535 ... 550 + -> block 159 val=0 inc=-8 + block 160 +550 ... 565 + -> block 159 val=0 inc=-9 + block 157 +127 ... 133 + -> block 158 val=0 inc=-10 + block 139 +577 ... 596 + -> block 199 val=0 + -> block 197 val=1 + block 197 +596 ... 597 + block 199 +597 ... 609 + -> block -1 val=0 inc=0 + block -1 +612 ... 613 + caller #6 +7 + counts[0] = 1 + counts[17] = 1 + counts[20] = 1 + counts[41] = 1 + counts[0] = 1 + path: .. +29 +37 +577 +597 +612 + counts[1] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +550 +565 .. + counts[2] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +535 +565 .. + counts[3] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +520 +565 .. + counts[4] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +505 +565 .. + counts[5] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +336 +357 +390 +421 +443 +454 +484 +565 .. + counts[6] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +336 +357 +390 +421 +428 +454 +484 +565 .. + counts[7] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +336 +357 +390 +408 +454 +484 +565 .. + counts[8] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +225 +246 +277 +287 +315 +565 .. + counts[9] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +225 +246 +264 +287 +315 +565 .. + counts[10] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +187 +204 +565 .. + counts[11] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +550 +565 .. + counts[12] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +535 +565 .. + counts[13] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +520 +565 .. + counts[14] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +505 +565 .. + counts[15] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +336 +357 +390 +421 +443 +454 +484 +565 .. + counts[16] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +336 +357 +390 +421 +428 +454 +484 +565 .. + counts[17] = 1 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +336 +357 +390 +408 +454 +484 +565 .. + counts[18] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +225 +246 +277 +287 +315 +565 .. + counts[19] = 0 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +225 +246 +264 +287 +315 +565 .. + counts[20] = 1 + path: .. +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +187 +204 +565 .. + counts[21] = 0 + path: +3 +29 +37 +577 +597 +612 + counts[22] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +550 +565 .. + counts[23] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +535 +565 .. + counts[24] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +520 +565 .. + counts[25] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +505 +565 .. + counts[26] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +336 +357 +390 +421 +443 +454 +484 +565 .. + counts[27] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +336 +357 +390 +421 +428 +454 +484 +565 .. + counts[28] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +336 +357 +390 +408 +454 +484 +565 .. + counts[29] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +225 +246 +277 +287 +315 +565 .. + counts[30] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +225 +246 +264 +287 +315 +565 .. + counts[31] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +127 +133 +161 +187 +204 +565 .. + counts[32] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +550 +565 .. + counts[33] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +535 +565 .. + counts[34] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +520 +565 .. + counts[35] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +505 +565 .. + counts[36] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +336 +357 +390 +421 +443 +454 +484 +565 .. + counts[37] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +336 +357 +390 +421 +428 +454 +484 +565 .. + counts[38] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +336 +357 +390 +408 +454 +484 +565 .. + counts[39] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +225 +246 +277 +287 +315 +565 .. + counts[40] = 0 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +225 +246 +264 +287 +315 +565 .. + counts[41] = 1 + path: +3 +29 +37 +48 +54 +66 +80 +92 +109 +122 +133 +161 +187 +204 +565 .. +func #10 + block 200 +1 ... 6 + -> block 201 val=0 inc=0 + block 201 +6 ... 7 + caller #52 +5 + counts[0] = 1 + counts[0] = 1 + path: +1 +6 +func #11 + block 202 +1 ... 6 + -> block 203 val=0 inc=0 + block 203 +6 ... 7 + counts[0] = 0 + path: +1 +6 +func #12 + block 204 +3 ... 8 + -> block 207 val=0 + -> block 206 val=1 + block 206 +8 ... 13 + -> block 208 val=0 + block 208 +29 ... 42 + -> block 214 val=0 + -> block 212 val=1 + block 212 +42 ... 43 + block 214 +43 ... 53 + -> block -1 val=0 inc=1 + block 207 +13 ... 18 + -> block 211 val=0 + -> block 209 val=1 + block 209 +18 ... 19 + block 211 +19 ... 29 + -> block 208 val=0 inc=-1 + block -1 +53 ... 54 + counts[0] = 0 + path: +3 +13 +19 +29 +43 +53 + counts[1] = 0 + path: +3 +8 +29 +43 +53 +func #13 + block 215 +1 ... 6 + -> block 216 val=0 inc=0 + block 216 +6 ... 7 + counts[0] = 0 + path: +1 +6 +func #14 + block 217 +3 ... 134 + -> block -1 val=0 inc=0 + block -1 +134 ... 135 + caller #7 +13 + counts[0] = 1 + counts[0] = 1 + path: +3 +134 +func #15 + block 219 +3 ... 14 + -> block 230 val=0 DUMMY + -> block 225 val=2 DUMMY + -> block 222 val=7 + -> block 221 val=12 + block 221 +14 ... 33 + -> block 223 val=0 + block 223 +35 ... 97 + -> block 225 val=0 inc=10 + block 225 +97 ... 113 + -> block 227 val=0 + -> block 226 val=3 + block 226 +113 ... 119 + -> block 230 val=0 inc=5 + block 230 +119 ... 135 + -> block 232 val=0 + -> block 231 val=1 + block 231 +135 ... 146 + -> block 220 val=0 inc=1 DUMMY + -> block 230 val=0 BACKEDGE + block 232 +147 ... 149 + -> block 229 val=0 + block 229 +151 ... 168 + -> block 220 val=0 inc=0 DUMMY + -> block 225 val=0 BACKEDGE + block 227 +169 ... 171 + -> block 224 val=0 + block 224 +173 ... 193 + -> block 235 val=0 + -> block 234 val=2 + block 234 +193 ... 202 + -> block 236 val=0 + block 236 +436 ... 437 + -> block 220 val=0 inc=4 + block 220 +437 ... 438 + block 235 +202 ... 300 + -> block 238 val=0 + -> block 237 val=1 + block 237 +300 ... 309 + -> block 239 val=0 + block 239 +435 ... 436 + -> block 236 val=0 inc=-1 + block 238 +309 ... 435 + -> block 239 val=0 inc=-1 + block 222 +33 ... 35 + -> block 223 val=0 inc=-5 + counts[0] = 0 + path: .. +119 +147 +151 .. + counts[1] = 0 + path: .. +119 +135 .. + counts[2] = 0 + path: .. +97 +169 +173 +202 +309 +435 +436 +437 + counts[3] = 0 + path: .. +97 +169 +173 +202 +300 +435 +436 +437 + counts[4] = 0 + path: .. +97 +169 +173 +193 +436 +437 + counts[5] = 0 + path: .. +97 +113 +119 +147 +151 .. + counts[6] = 0 + path: .. +97 +113 +119 +135 .. + counts[7] = 0 + path: +3 +33 +35 +97 +169 +173 +202 +309 +435 +436 +437 + counts[8] = 0 + path: +3 +33 +35 +97 +169 +173 +202 +300 +435 +436 +437 + counts[9] = 0 + path: +3 +33 +35 +97 +169 +173 +193 +436 +437 + counts[10] = 0 + path: +3 +33 +35 +97 +113 +119 +147 +151 .. + counts[11] = 0 + path: +3 +33 +35 +97 +113 +119 +135 .. + counts[12] = 0 + path: +3 +14 +35 +97 +169 +173 +202 +309 +435 +436 +437 + counts[13] = 0 + path: +3 +14 +35 +97 +169 +173 +202 +300 +435 +436 +437 + counts[14] = 0 + path: +3 +14 +35 +97 +169 +173 +193 +436 +437 + counts[15] = 0 + path: +3 +14 +35 +97 +113 +119 +147 +151 .. + counts[16] = 0 + path: +3 +14 +35 +97 +113 +119 +135 .. +func #16 + block 240 +1 ... 28 + -> block 243 val=0 + -> block 242 val=1 + block 242 +28 ... 38 + -> block 244 val=0 + block 244 +75 ... 83 + -> block -1 val=0 inc=1 + block 243 +38 ... 75 + -> block 244 val=0 inc=-1 + block -1 +83 ... 84 + counts[0] = 0 + path: +1 +38 +75 +83 + counts[1] = 0 + path: +1 +28 +75 +83 +func #17 + block 245 +1 ... 31 + -> block -1 val=0 inc=0 + block -1 +31 ... 32 + counts[0] = 0 + path: +1 +31 +func #18 + block 247 +1 ... 21 + -> block 248 val=0 inc=0 + block 248 +21 ... 22 + counts[0] = 0 + path: +1 +21 +func #19 + block 249 +3 ... 7 + -> block 251 val=0 DUMMY + -> block 251 val=2 inc=2 + block 251 +7 ... 16 + -> block 253 val=0 + -> block 252 val=1 + block 252 +16 ... 32 + -> block -1 val=0 inc=1 DUMMY + -> block 251 val=0 BACKEDGE + block 253 +33 ... 34 + -> block -1 val=0 inc=0 + block -1 +36 ... 37 + counts[0] = 0 + path: .. +7 +33 +36 + counts[1] = 0 + path: .. +7 +16 .. + counts[2] = 0 + path: +3 +7 +33 +36 + counts[3] = 0 + path: +3 +7 +16 .. +func #20 + block 255 +3 ... 17 + -> block 258 val=0 + -> block 257 val=4 + block 257 +17 ... 18 + -> block 259 val=0 + block 259 +93 ... 96 + -> block 256 val=0 inc=4 + block 256 +96 ... 97 + block 258 +18 ... 32 + -> block 260 val=0 + -> block 259 val=3 inc=-1 + block 260 +32 ... 55 + -> block 263 val=0 + -> block 262 val=1 + block 262 +55 ... 69 + -> block 266 val=0 + -> block 265 val=1 + block 265 +69 ... 75 + -> block 261 val=0 + block 261 +89 ... 93 + -> block 256 val=0 inc=2 + block 266 +76 ... 78 + -> block 264 val=0 + block 264 +82 ... 89 + -> block 261 val=0 inc=-1 + block 263 +80 ... 82 + -> block 264 val=0 inc=-1 + counts[0] = 0 + path: +3 +18 +32 +80 +82 +89 +96 + counts[1] = 0 + path: +3 +18 +32 +55 +76 +82 +89 +96 + counts[2] = 0 + path: +3 +18 +32 +55 +69 +89 +96 + counts[3] = 0 + path: +3 +18 +93 +96 + counts[4] = 0 + path: +3 +17 +93 +96 +func #21 + block 268 +3 ... 25 + -> block 270 val=0 DUMMY + -> block 270 val=2 inc=2 + block 270 +25 ... 32 + -> block 272 val=0 + -> block 271 val=1 + block 271 +32 ... 33 + -> block -1 val=0 inc=1 + block 272 +34 ... 62 + -> block 276 val=0 + -> block 274 val=1 + block 274 +62 ... 63 + block 276 +63 ... 72 + -> block 278 val=0 + -> block 277 val=1 + block 277 +72 ... 77 + -> block 282 val=0 + -> block 280 val=0 + block 280 +77 ... 78 + block 282 +78 ... 80 + block 278 +81 ... 97 + -> block 285 val=0 + -> block 283 val=1 + block 283 +97 ... 98 + block 285 +98 ... 115 + -> block 279 val=0 + block 279 +115 ... 122 + -> block -1 val=0 inc=0 DUMMY + -> block 270 val=0 BACKEDGE + block -1 +124 ... 125 + counts[0] = 0 + path: .. +25 +34 +63 +81 +98 +115 .. + counts[1] = 0 + path: .. +25 +32 +124 + counts[2] = 0 + path: +3 +25 +34 +63 +81 +98 +115 .. + counts[3] = 0 + path: +3 +25 +32 +124 +func #22 + block 286 +3 ... 30 + -> block 309 val=0 DUMMY + -> block 305 val=2 DUMMY + -> block 300 val=4 DUMMY + -> block 288 val=6 + -> block 289 val=7 + -> block 290 val=9 + -> block 291 val=13 + -> block 292 val=14 + block 292 +30 ... 55 + -> block 294 val=0 + -> block 293 val=1 + block 293 +55 ... 62 + -> block 287 val=0 inc=15 + block 294 +63 ... 87 + -> block 287 val=0 inc=14 + block 291 +88 ... 110 + -> block 287 val=0 inc=13 + block 290 +110 ... 142 + -> block 297 val=0 + -> block 296 val=2 + block 296 +142 ... 152 + -> block 300 val=0 inc=7 + block 300 +152 ... 161 + -> block 302 val=0 + -> block 301 val=1 + block 301 +161 ... 181 + -> block 287 val=0 inc=5 DUMMY + -> block 300 val=0 BACKEDGE + block 302 +182 ... 184 + -> block 299 val=0 + block 299 +186 ... 192 + -> block 298 val=0 + block 298 +264 ... 271 + -> block 287 val=0 inc=4 + block 297 +192 ... 223 + -> block 305 val=0 inc=7 + block 305 +223 ... 232 + -> block 307 val=0 + -> block 306 val=1 + block 306 +232 ... 252 + -> block 287 val=0 inc=3 DUMMY + -> block 305 val=0 BACKEDGE + block 307 +253 ... 255 + -> block 304 val=0 + block 304 +257 ... 264 + -> block 298 val=0 inc=-2 + block 289 +271 ... 297 + -> block 309 val=0 inc=7 + block 309 +297 ... 306 + -> block 311 val=0 + -> block 310 val=1 + block 310 +306 ... 319 + -> block 287 val=0 inc=1 DUMMY + -> block 309 val=0 BACKEDGE + block 311 +320 ... 323 + -> block 287 val=0 inc=0 + block 288 +325 ... 330 + -> block 287 val=0 inc=6 + block 287 +330 ... 331 + counts[0] = 0 + path: .. +297 +320 +330 + counts[1] = 0 + path: .. +297 +306 .. + counts[2] = 0 + path: .. +223 +253 +257 +264 +330 + counts[3] = 0 + path: .. +223 +232 .. + counts[4] = 0 + path: .. +152 +182 +186 +264 +330 + counts[5] = 0 + path: .. +152 +161 .. + counts[6] = 0 + path: +3 +325 +330 + counts[7] = 0 + path: +3 +271 +297 +320 +330 + counts[8] = 0 + path: +3 +271 +297 +306 .. + counts[9] = 0 + path: +3 +110 +192 +223 +253 +257 +264 +330 + counts[10] = 0 + path: +3 +110 +192 +223 +232 .. + counts[11] = 0 + path: +3 +110 +142 +152 +182 +186 +264 +330 + counts[12] = 0 + path: +3 +110 +142 +152 +161 .. + counts[13] = 0 + path: +3 +88 +330 + counts[14] = 0 + path: +3 +30 +63 +330 + counts[15] = 0 + path: +3 +30 +55 +330 +func #23 + block 313 +3 ... 25 + -> block 315 val=0 DUMMY + -> block 315 val=3 inc=3 + block 315 +25 ... 32 + -> block 317 val=0 + -> block 316 val=2 + block 316 +32 ... 33 + -> block -1 val=0 inc=2 + block 317 +34 ... 58 + -> block 320 val=0 + -> block 319 val=1 + block 319 +58 ... 74 + -> block 324 val=0 + -> block 322 val=1 + block 322 +74 ... 75 + block 324 +75 ... 91 + -> block 321 val=0 + block 321 +139 ... 146 + -> block -1 val=0 inc=1 DUMMY + -> block 315 val=0 BACKEDGE + block 320 +91 ... 96 + -> block 327 val=0 + -> block 325 val=1 + block 325 +96 ... 97 + block 327 +97 ... 121 + -> block 330 val=0 + -> block 328 val=1 + block 328 +121 ... 122 + block 330 +122 ... 139 + -> block 321 val=0 inc=-1 + block -1 +148 ... 149 + counts[0] = 0 + path: .. +25 +34 +91 +97 +122 +139 .. + counts[1] = 0 + path: .. +25 +34 +58 +75 +139 .. + counts[2] = 0 + path: .. +25 +32 +148 + counts[3] = 0 + path: +3 +25 +34 +91 +97 +122 +139 .. + counts[4] = 0 + path: +3 +25 +34 +58 +75 +139 .. + counts[5] = 0 + path: +3 +25 +32 +148 +func #24 + block 331 +1 ... 2 + -> block -1 val=0 inc=0 + block -1 +2 ... 3 + counts[0] = 0 + path: +1 +2 +func #25 + block 333 +3 ... 18 + -> block 337 val=0 + -> block 335 val=1 + block 335 +18 ... 19 + block 337 +19 ... 37 + -> block 340 val=0 + -> block 338 val=1 + block 338 +37 ... 38 + block 340 +38 ... 57 + -> block 334 val=0 inc=0 + block 334 +57 ... 58 + counts[0] = 0 + path: +3 +19 +38 +57 +func #26 + block 341 +3 ... 15 + -> block 344 val=0 + -> block 343 val=1 + block 343 +15 ... 20 + -> block 345 val=0 + block 345 +36 ... 49 + -> block 351 val=0 + -> block 349 val=1 + block 349 +49 ... 50 + block 351 +50 ... 61 + -> block 342 val=0 inc=1 + block 342 +61 ... 62 + block 344 +20 ... 25 + -> block 348 val=0 + -> block 346 val=1 + block 346 +25 ... 26 + block 348 +26 ... 36 + -> block 345 val=0 inc=-1 + counts[0] = 0 + path: +3 +20 +26 +36 +50 +61 + counts[1] = 0 + path: +3 +15 +36 +50 +61 +func #27 + block 352 +1 ... 10 + -> block 353 val=0 inc=0 + block 353 +10 ... 11 + counts[0] = 0 + path: +1 +10 +func #28 + block 354 +3 ... 8 + -> block 357 val=0 + -> block 356 val=6 + block 356 +8 ... 13 + -> block 358 val=0 + block 358 +29 ... 42 + -> block 364 val=0 + -> block 362 val=6 + block 362 +42 ... 43 + block 364 +43 ... 56 + -> block 366 val=0 + -> block 365 val=4 + block 365 +56 ... 61 + -> block 367 val=0 + block 367 +97 ... 103 + -> block 375 val=0 + -> block 374 val=1 + block 374 +103 ... 108 + -> block 376 val=0 + block 376 +117 ... 129 + -> block -1 val=0 inc=11 + block 375 +108 ... 117 + -> block 376 val=0 inc=-1 + block 366 +61 ... 66 + -> block 369 val=0 + -> block 368 val=2 + block 368 +66 ... 71 + -> block 370 val=0 + block 370 +87 ... 97 + -> block 367 val=0 inc=-2 + block 369 +71 ... 76 + -> block 373 val=0 + -> block 371 val=2 + block 371 +76 ... 77 + block 373 +77 ... 87 + -> block 370 val=0 inc=-2 + block 357 +13 ... 18 + -> block 361 val=0 + -> block 359 val=6 + block 359 +18 ... 19 + block 361 +19 ... 29 + -> block 358 val=0 inc=-6 + block -1 +129 ... 130 + counts[0] = 0 + path: +3 +13 +19 +29 +43 +61 +71 +77 +87 +97 +108 +117 +129 + counts[1] = 0 + path: +3 +13 +19 +29 +43 +61 +71 +77 +87 +97 +103 +117 +129 + counts[2] = 0 + path: +3 +13 +19 +29 +43 +61 +66 +87 +97 +108 +117 +129 + counts[3] = 0 + path: +3 +13 +19 +29 +43 +61 +66 +87 +97 +103 +117 +129 + counts[4] = 0 + path: +3 +13 +19 +29 +43 +56 +97 +108 +117 +129 + counts[5] = 0 + path: +3 +13 +19 +29 +43 +56 +97 +103 +117 +129 + counts[6] = 0 + path: +3 +8 +29 +43 +61 +71 +77 +87 +97 +108 +117 +129 + counts[7] = 0 + path: +3 +8 +29 +43 +61 +71 +77 +87 +97 +103 +117 +129 + counts[8] = 0 + path: +3 +8 +29 +43 +61 +66 +87 +97 +108 +117 +129 + counts[9] = 0 + path: +3 +8 +29 +43 +61 +66 +87 +97 +103 +117 +129 + counts[10] = 0 + path: +3 +8 +29 +43 +56 +97 +108 +117 +129 + counts[11] = 0 + path: +3 +8 +29 +43 +56 +97 +103 +117 +129 +func #29 + block 377 +1 ... 18 + -> block 378 val=0 inc=0 + block 378 +18 ... 19 + counts[0] = 0 + path: +1 +18 +func #30 + block 379 +1 ... 17 + -> block 380 val=0 inc=0 + block 380 +17 ... 18 + counts[0] = 0 + path: +1 +17 +func #31 + block 381 +3 ... 8 + -> block 386 val=0 DUMMY + -> block 384 val=3 + -> block 383 val=6 + block 383 +8 ... 11 + -> block 385 val=0 + block 385 +70 ... 71 + -> block 382 val=0 inc=6 + block 382 +71 ... 72 + block 384 +11 ... 19 + -> block 386 val=0 inc=3 + block 386 +19 ... 28 + -> block 388 val=0 + -> block 387 val=2 + block 387 +28 ... 31 + -> block 382 val=0 inc=2 + block 388 +32 ... 40 + -> block 391 val=0 + -> block 390 val=1 + block 390 +40 ... 41 + -> block 392 val=0 + block 392 +50 ... 67 + -> block 382 val=0 inc=1 DUMMY + -> block 386 val=0 BACKEDGE + block 391 +41 ... 50 + -> block 392 val=0 inc=-1 + counts[0] = 0 + path: .. +19 +32 +41 +50 .. + counts[1] = 0 + path: .. +19 +32 +40 +50 .. + counts[2] = 0 + path: .. +19 +28 +71 + counts[3] = 0 + path: +3 +11 +19 +32 +41 +50 .. + counts[4] = 0 + path: +3 +11 +19 +32 +40 +50 .. + counts[5] = 0 + path: +3 +11 +19 +28 +71 + counts[6] = 0 + path: +3 +8 +70 +71 +func #32 + block 393 +3 ... 15 + -> block 395 val=0 DUMMY + -> block 395 val=2 inc=2 + block 395 +15 ... 47 + -> block 397 val=0 + -> block 396 val=1 + block 396 +47 ... 50 + -> block -1 val=0 inc=1 + block 397 +51 ... 71 + -> block -1 val=0 inc=0 DUMMY + -> block 395 val=0 BACKEDGE + block -1 +74 ... 75 + counts[0] = 0 + path: .. +15 +51 .. + counts[1] = 0 + path: .. +15 +47 +74 + counts[2] = 0 + path: +3 +15 +51 .. + counts[3] = 0 + path: +3 +15 +47 +74 +func #33 + block 399 +3 ... 25 + -> block 401 val=0 DUMMY + -> block 401 val=2 inc=2 + block 401 +25 ... 34 + -> block 403 val=0 + -> block 402 val=1 + block 402 +34 ... 47 + -> block -1 val=0 inc=1 DUMMY + -> block 401 val=0 BACKEDGE + block 403 +48 ... 62 + -> block -1 val=0 inc=0 + block -1 +64 ... 65 + counts[0] = 0 + path: .. +25 +48 +64 + counts[1] = 0 + path: .. +25 +34 .. + counts[2] = 0 + path: +3 +25 +48 +64 + counts[3] = 0 + path: +3 +25 +34 .. +func #34 + block 405 +3 ... 11 + -> block 412 val=0 DUMMY + -> block 407 val=3 DUMMY + -> block 407 val=7 inc=4 + block 407 +11 ... 24 + -> block 409 val=0 + -> block 408 val=1 + block 408 +24 ... 47 + -> block 412 val=0 inc=4 + block 412 +47 ... 54 + -> block 413 val=0 + -> block 411 val=2 + block 411 +87 ... 97 + -> block -1 val=0 inc=2 DUMMY + -> block 407 val=0 BACKEDGE + block 413 +54 ... 62 + -> block 415 val=0 + -> block 414 val=1 + block 414 +62 ... 63 + -> block 416 val=0 + block 416 +69 ... 86 + -> block -1 val=0 inc=1 DUMMY + -> block 412 val=0 BACKEDGE + block 415 +63 ... 69 + -> block 416 val=0 inc=-1 + block 409 +98 ... 99 + -> block -1 val=0 inc=3 + block -1 +101 ... 102 + counts[0] = 0 + path: .. +47 +54 +63 +69 .. + counts[1] = 0 + path: .. +47 +54 +62 +69 .. + counts[2] = 0 + path: .. +47 +87 .. + counts[3] = 0 + path: .. +11 +98 +101 + counts[4] = 0 + path: .. +11 +24 +47 +54 +63 +69 .. + counts[5] = 0 + path: .. +11 +24 +47 +54 +62 +69 .. + counts[6] = 0 + path: .. +11 +24 +47 +87 .. + counts[7] = 0 + path: +3 +11 +98 +101 + counts[8] = 0 + path: +3 +11 +24 +47 +54 +63 +69 .. + counts[9] = 0 + path: +3 +11 +24 +47 +54 +62 +69 .. + counts[10] = 0 + path: +3 +11 +24 +47 +87 .. +func #35 + block 417 +3 ... 13 + -> block 420 val=0 + -> block 419 val=10 + block 419 +13 ... 14 + -> block -1 val=0 inc=10 + block 420 +15 ... 29 + -> block 423 val=0 + -> block 422 val=3 + block 422 +29 ... 43 + -> block 426 val=0 + -> block 425 val=3 + block 425 +43 ... 66 + -> block 430 val=0 + -> block 429 val=1 + block 429 +66 ... 80 + -> block 433 val=0 + -> block 432 val=1 + block 432 +80 ... 87 + -> block 436 val=0 + -> block 435 val=1 + block 435 +87 ... 88 + -> block 437 val=0 + block 437 +97 ... 100 + -> block 428 val=0 + block 428 +161 ... 163 + -> block -1 val=0 inc=9 + block 436 +88 ... 97 + -> block 437 val=0 inc=-1 + block 433 +101 ... 103 + -> block 431 val=0 + block 431 +107 ... 161 + -> block 428 val=0 inc=-2 + block 430 +105 ... 107 + -> block 431 val=0 inc=-1 + block 426 +164 ... 166 + -> block 424 val=0 + block 424 +170 ... 182 + -> block 439 val=0 + -> block 438 val=1 + block 438 +182 ... 193 + -> block 442 val=0 + -> block 441 val=1 + block 441 +193 ... 194 + -> block -1 val=0 inc=5 + block 442 +195 ... 197 + -> block 440 val=0 + block 440 +201 ... 269 + -> block -1 val=0 inc=4 + block 439 +199 ... 201 + -> block 440 val=0 inc=-1 + block 423 +168 ... 170 + -> block 424 val=0 inc=-3 + block -1 +270 ... 271 + counts[0] = 0 + path: +3 +15 +168 +170 +199 +201 +270 + counts[1] = 0 + path: +3 +15 +168 +170 +182 +195 +201 +270 + counts[2] = 0 + path: +3 +15 +168 +170 +182 +193 +270 + counts[3] = 0 + path: +3 +15 +29 +164 +170 +199 +201 +270 + counts[4] = 0 + path: +3 +15 +29 +164 +170 +182 +195 +201 +270 + counts[5] = 0 + path: +3 +15 +29 +164 +170 +182 +193 +270 + counts[6] = 0 + path: +3 +15 +29 +43 +105 +107 +161 +270 + counts[7] = 0 + path: +3 +15 +29 +43 +66 +101 +107 +161 +270 + counts[8] = 0 + path: +3 +15 +29 +43 +66 +80 +88 +97 +161 +270 + counts[9] = 0 + path: +3 +15 +29 +43 +66 +80 +87 +97 +161 +270 + counts[10] = 0 + path: +3 +13 +270 +func #36 + block 444 +3 ... 23 + -> block 452 val=0 DUMMY + -> block 448 val=2 + -> block 446 val=4 + block 446 +23 ... 24 + block 448 +24 ... 42 + -> block 451 val=0 + -> block 449 val=2 + block 449 +42 ... 43 + block 451 +43 ... 58 + -> block 452 val=0 inc=2 + block 452 +58 ... 67 + -> block 454 val=0 + -> block 453 val=1 + block 453 +67 ... 100 + -> block -1 val=0 inc=1 DUMMY + -> block 452 val=0 BACKEDGE + block 454 +101 ... 116 + -> block 458 val=0 + -> block 456 val=1 + block 456 +116 ... 117 + block 458 +117 ... 137 + -> block -1 val=0 inc=0 + block -1 +140 ... 141 + counts[0] = 0 + path: .. +58 +101 +117 +140 + counts[1] = 0 + path: .. +58 +67 .. + counts[2] = 0 + path: +3 +24 +43 +58 +101 +117 +140 + counts[3] = 0 + path: +3 +24 +43 +58 +67 .. +func #37 + block 459 +3 ... 30 + -> block 461 val=0 + -> block 462 val=1 + -> block 463 val=2 + -> block 464 val=4 + -> block 465 val=5 + block 465 +30 ... 55 + -> block 467 val=0 + -> block 466 val=1 + block 466 +55 ... 60 + -> block 460 val=0 inc=6 + block 467 +61 ... 83 + -> block 460 val=0 inc=5 + block 464 +84 ... 106 + -> block 460 val=0 inc=4 + block 463 +106 ... 131 + -> block 470 val=0 + -> block 469 val=1 + block 469 +131 ... 138 + -> block 471 val=0 + block 471 +163 ... 175 + -> block 460 val=0 inc=3 + block 470 +138 ... 163 + -> block 471 val=0 inc=-1 + block 462 +175 ... 188 + -> block 460 val=0 inc=1 + block 461 +188 ... 193 + -> block 460 val=0 inc=0 + block 460 +193 ... 194 + counts[0] = 0 + path: +3 +188 +193 + counts[1] = 0 + path: +3 +175 +193 + counts[2] = 0 + path: +3 +106 +138 +163 +193 + counts[3] = 0 + path: +3 +106 +131 +163 +193 + counts[4] = 0 + path: +3 +84 +193 + counts[5] = 0 + path: +3 +30 +61 +193 + counts[6] = 0 + path: +3 +30 +55 +193 +func #38 + block 472 +3 ... 18 + -> block 474 val=0 DUMMY + -> block 474 val=2 inc=2 + block 474 +18 ... 27 + -> block 476 val=0 + -> block 475 val=1 + block 475 +27 ... 53 + -> block -1 val=0 inc=1 DUMMY + -> block 474 val=0 BACKEDGE + block 476 +54 ... 55 + -> block -1 val=0 inc=0 + block -1 +57 ... 58 + counts[0] = 0 + path: .. +18 +54 +57 + counts[1] = 0 + path: .. +18 +27 .. + counts[2] = 0 + path: +3 +18 +54 +57 + counts[3] = 0 + path: +3 +18 +27 .. +func #39 + block 478 +3 ... 17 + -> block 481 val=0 + -> block 480 val=1 + block 480 +17 ... 33 + -> block 482 val=0 + block 482 +46 ... 56 + -> block 485 val=0 + -> block 483 val=1 + block 483 +56 ... 57 + block 485 +57 ... 68 + -> block 488 val=0 + -> block 486 val=1 + block 486 +68 ... 69 + block 488 +69 ... 84 + -> block -1 val=0 inc=1 + block 481 +33 ... 46 + -> block 482 val=0 inc=-1 + block -1 +84 ... 85 + counts[0] = 0 + path: +3 +33 +46 +57 +69 +84 + counts[1] = 0 + path: +3 +17 +46 +57 +69 +84 +func #40 + block 489 +3 ... 8 + -> block 494 val=0 DUMMY + -> block 492 val=2 + -> block 491 val=4 + block 491 +8 ... 11 + -> block 493 val=0 + block 493 +51 ... 52 + -> block 490 val=0 inc=4 + block 490 +52 ... 53 + block 492 +11 ... 19 + -> block 494 val=0 inc=2 + block 494 +19 ... 28 + -> block 496 val=0 + -> block 495 val=1 + block 495 +28 ... 31 + -> block 490 val=0 inc=1 + block 496 +32 ... 48 + -> block 490 val=0 inc=0 DUMMY + -> block 494 val=0 BACKEDGE + counts[0] = 0 + path: .. +19 +32 .. + counts[1] = 0 + path: .. +19 +28 +52 + counts[2] = 0 + path: +3 +11 +19 +32 .. + counts[3] = 0 + path: +3 +11 +19 +28 +52 + counts[4] = 0 + path: +3 +8 +51 +52 +func #41 + block 498 +3 ... 11 + -> block 500 val=0 DUMMY + -> block 500 val=2 inc=2 + block 500 +11 ... 30 + -> block 502 val=0 + -> block 501 val=1 + block 501 +30 ... 45 + -> block -1 val=0 inc=1 + block 502 +46 ... 65 + -> block -1 val=0 inc=0 DUMMY + -> block 500 val=0 BACKEDGE + block -1 +68 ... 69 + counts[0] = 0 + path: .. +11 +46 .. + counts[1] = 0 + path: .. +11 +30 +68 + counts[2] = 0 + path: +3 +11 +46 .. + counts[3] = 0 + path: +3 +11 +30 +68 +func #42 + block 504 +1 ... 3 + -> block 505 val=0 inc=0 + block 505 +3 ... 4 + counts[0] = 0 + path: +1 +3 +func #43 + block 506 +1 ... 9 + -> block 507 val=0 inc=0 + block 507 +9 ... 10 + counts[0] = 0 + path: +1 +9 +func #44 + block 508 +1 ... 5 + -> block 509 val=0 inc=0 + block 509 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #45 + block 510 +1 ... 3 + -> block 511 val=0 inc=0 + block 511 +3 ... 4 + counts[0] = 0 + path: +1 +3 +func #46 + block 512 +1 ... 9 + -> block 513 val=0 inc=0 + block 513 +9 ... 10 + counts[0] = 0 + path: +1 +9 +func #47 + block 514 +1 ... 9 + -> block 515 val=0 inc=0 + block 515 +9 ... 10 + counts[0] = 0 + path: +1 +9 +func #48 + block 516 +1 ... 9 + -> block 517 val=0 inc=0 + block 517 +9 ... 10 + counts[0] = 0 + path: +1 +9 +func #49 + block 518 +1 ... 11 + -> block 519 val=0 inc=0 + block 519 +11 ... 12 + counts[0] = 0 + path: +1 +11 +func #50 + block 520 +1 ... 5 + -> block 521 val=0 inc=0 + block 521 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #51 + block 522 +1 ... 5 + -> block 523 val=0 inc=0 + block 523 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #52 + block 524 +1 ... 7 + -> block 525 val=0 inc=0 + block 525 +7 ... 8 + caller #9 +468 + counts[0] = 1 + counts[0] = 1 + path: +1 +7 +func #53 + block 526 +1 ... 5 + -> block 527 val=0 inc=0 + block 527 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #54 + block 528 +1 ... 7 + -> block 529 val=0 inc=0 + block 529 +7 ... 8 + counts[0] = 0 + path: +1 +7 +func #55 + block 530 +1 ... 5 + -> block 531 val=0 inc=0 + block 531 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #56 + block 532 +1 ... 5 + -> block 533 val=0 inc=0 + block 533 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #57 + block 534 +1 ... 5 + -> block 535 val=0 inc=0 + block 535 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #58 + block 536 +1 ... 7 + -> block 537 val=0 inc=0 + block 537 +7 ... 8 + counts[0] = 0 + path: +1 +7 +func #59 + block 538 +1 ... 5 + -> block 539 val=0 inc=0 + block 539 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #60 + block 540 +1 ... 5 + -> block 541 val=0 inc=0 + block 541 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #61 + block 542 +1 ... 7 + -> block 543 val=0 inc=0 + block 543 +7 ... 8 + counts[0] = 0 + path: +1 +7 +func #62 + block 544 +1 ... 7 + -> block 545 val=0 inc=0 + block 545 +7 ... 8 + counts[0] = 0 + path: +1 +7 +func #63 + block 546 +1 ... 3 + -> block 547 val=0 inc=0 + block 547 +3 ... 4 + counts[0] = 0 + path: +1 +3 +func #64 + block 548 +1 ... 5 + -> block 549 val=0 inc=0 + block 549 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #65 + block 550 +1 ... 7 + -> block 551 val=0 inc=0 + block 551 +7 ... 8 + counts[0] = 0 + path: +1 +7 +func #66 + block 552 +1 ... 7 + -> block 553 val=0 inc=0 + block 553 +7 ... 8 + counts[0] = 0 + path: +1 +7 +func #67 + block 554 +1 ... 5 + -> block 555 val=0 inc=0 + block 555 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #68 + block 556 +1 ... 9 + -> block 557 val=0 inc=0 + block 557 +9 ... 10 + counts[0] = 0 + path: +1 +9 +func #69 + block 558 +1 ... 7 + -> block 559 val=0 inc=0 + block 559 +7 ... 8 + counts[0] = 0 + path: +1 +7 +func #70 + block 560 +1 ... 3 + -> block 561 val=0 inc=0 + block 561 +3 ... 4 + counts[0] = 0 + path: +1 +3 +func #71 + block 562 +1 ... 5 + -> block 563 val=0 inc=0 + block 563 +5 ... 6 + counts[0] = 0 + path: +1 +5 +func #72 + block 564 +1 ... 7 + -> block 565 val=0 inc=0 + block 565 +7 ... 8 + counts[0] = 0 + path: +1 +7 +func #73 + block 566 +1 ... 11 + -> block 567 val=0 inc=0 + block 567 +11 ... 12 + counts[0] = 0 + path: +1 +11 diff --git a/test/monitors/expected/profile_monitor0.wasm.path-profile.out b/test/monitors/expected/profile_monitor0.wasm.path-profile.out new file mode 100644 index 000000000..abe103b9c --- /dev/null +++ b/test/monitors/expected/profile_monitor0.wasm.path-profile.out @@ -0,0 +1,60 @@ +func "main" + block 100 +1 ... 3 + -> block 101 val=0 inc=0 + block 101 +3 ... 4 + caller engine + counts[0] = 1 + counts[0] = 1 + path: +1 +3 +func "start" + block 102 +3 ... 7 + -> block 104 val=0 + block 104 +7 ... 40 + -> block 105 val=0 + -> block 104 val=0 BACKEDGE + block 105 +40 ... 41 + -> block 103 val=0 inc=0 + block 103 +41 ... 42 + caller engine + counts[0] = 1 + caller "main" +1 + counts[0] = 1 + counts[0] = 2 + path: +3 +7 +40 +41 + selfloop[block 104 +38 arm 1] = 98 +func "foo" + block 106 +1 ... 3 + -> block 107 val=0 inc=0 + block 107 +3 ... 4 + caller "start" +9 + counts[0] = 100 + caller "start" +13 + counts[0] = 100 + caller "start" +17 + counts[0] = 100 + caller "start" +19 + counts[0] = 100 + caller "start" +21 + counts[0] = 100 + caller "start" +27 + counts[0] = 100 + counts[0] = 600 + path: +1 +3 +func "bar" + block 108 +1 ... 3 + -> block 109 val=0 inc=0 + block 109 +3 ... 4 + caller "foo" +1 + counts[0] = 600 + caller "start" +11 + counts[0] = 100 + caller "start" +23 + counts[0] = 100 + caller "start" +25 + counts[0] = 100 + counts[0] = 900 + path: +1 +3 +func "baz" + block 111 +1 ... 2 + counts[0] = 0 + path: +1 diff --git a/test/monitors/path_profile_br_outer.wasm b/test/monitors/path_profile_br_outer.wasm new file mode 100644 index 0000000000000000000000000000000000000000..5bf0c08d24f5446b605d8fc2ad892a0f1dfe11cb GIT binary patch literal 177 zcmYj{OA5k35Cz}siHKpA(e; literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_br_outer.wat b/test/monitors/path_profile_br_outer.wat new file mode 100644 index 000000000..71bfea94f --- /dev/null +++ b/test/monitors/path_profile_br_outer.wat @@ -0,0 +1,57 @@ +;; br / br_if targeting OUTER labels (depth > 0), including an unconditional branch out of +;; three nesting levels at once. Every pre-existing br/br_if test targets depth 0 only, and +;; nothing pinned br_if *polarity* on a br_if whose two successors are distinguishable +;; (shapes.wat's br_if has both successors converging immediately). +;; +;; $f: br_if $out escapes two levels, br_if $mid escapes one, else fall all the way out. +;; P1 x=0 -> br $out, tails skipped, r = 0 +;; P2 x=1 -> br $mid, then the +1 tail, r = 1 +;; P3 else -> r = 100, +10, +1 = 111 +;; 3 paths. main: P1 once, P2 twice, P3 three times. +;; expected counts: a permutation of {1,2,3}, sum = 6 = calls to $f +;; $g: unconditional `br $a` out of three levels; the br_if $b above it keeps the intermediate +;; tail reachable (so no dead predecessor -- see KNOWN_BUG_dead_pred for the dead variant). +;; P1 x=0 -> br_if not taken -> br $a -> r = 0 +;; P2 x!=0 -> br $b -> r = 3 +;; 2 paths. main: P1 three times, P2 once. +;; expected counts: a permutation of {3,1}, sum = 4 = calls to $g +(module + (func $f (export "f") (param $x i32) (result i32) + (local $r i32) + (block $out + (block $mid + (block $in + (br_if $out (i32.eqz (local.get $x))) + (br_if $mid (i32.eq (local.get $x) (i32.const 1))) + (local.set $r (i32.const 100)) + ) + (local.set $r (i32.add (local.get $r) (i32.const 10))) + ) + (local.set $r (i32.add (local.get $r) (i32.const 1))) + ) + (local.get $r)) + (func $g (export "g") (param $x i32) (result i32) + (local $r i32) + (block $a + (block $b + (block $c + (br_if $b (local.get $x)) + (br $a) + ) + (local.set $r (i32.const 2)) + ) + (local.set $r (i32.const 3)) + ) + (local.get $r)) + (func (export "main") + (drop (call $f (i32.const 0))) + (drop (call $f (i32.const 1))) + (drop (call $f (i32.const 1))) + (drop (call $f (i32.const 5))) + (drop (call $f (i32.const 5))) + (drop (call $f (i32.const 5))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 1)))) +) diff --git a/test/monitors/path_profile_dead_code.wasm b/test/monitors/path_profile_dead_code.wasm new file mode 100644 index 0000000000000000000000000000000000000000..456df617dbf8c4953633e76f3c7a8da7274aa669 GIT binary patch literal 138 zcmXAd!3u&v6aeS#u2?+-4|VaR=e{9uzeHCm5VB(P(rG`p-$)XMVHm=D5=c SRx;E(JyKGwcqvWwm;L|^&K5fW literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_dead_code.wat b/test/monitors/path_profile_dead_code.wat new file mode 100644 index 000000000..24a485333 --- /dev/null +++ b/test/monitors/path_profile_dead_code.wat @@ -0,0 +1,32 @@ +;; Unreachable code following an unconditional `br`, and a function whose entire body is +;; `unreachable`. Dead straight-line code must not create extra blocks/paths, and a body that +;; only traps must still get a well-formed single-path CFG (it is never called here, so all +;; counts stay 0 -- sum(counts) == 0 == activations). +;; +;; $after_br : 1 path (the `local.set 999` after the br is dead). Called 3 times -> counts[0]=3 +;; $trap_body : 1 path, NEVER called -> counts[0] = 0 +;; $trap_tail : `unreachable` as the tail of the else arm; the then arm falls through. +;; 2 paths; only the non-trapping one is executed, twice. +(module + (func $after_br (export "after_br") (result i32) + (local $r i32) + (block $b + (local.set $r (i32.const 1)) + (br $b) + (local.set $r (i32.const 999)) + ) + (local.get $r)) + (func $trap_body (export "trap_body") (result i32) + (unreachable)) + (func $trap_tail (export "trap_tail") (param $x i32) (result i32) + (if (local.get $x) + (then (nop)) + (else (unreachable))) + (i32.const 4)) + (func (export "main") + (drop (call $after_br)) + (drop (call $after_br)) + (drop (call $after_br)) + (drop (call $trap_tail (i32.const 1))) + (drop (call $trap_tail (i32.const 1)))) +) diff --git a/test/monitors/path_profile_dead_pred.wasm b/test/monitors/path_profile_dead_pred.wasm new file mode 100644 index 0000000000000000000000000000000000000000..0972a95bfb0c9fe7d61e988c30c5ede76217e5c6 GIT binary patch literal 154 zcmYL-K?=e^5CpqtCkT$36+A5j68uQ!Ct3C4AzpmIKGu)8OAu+gC<^(#5&(~3VPRR& zj?igsvb!jAl^e)AMA!U$A?zP|#t5CAedp4fe_*Fp%CFc!7Ma_e6Aof76U;-!2)n`= Okv5>&AB;> counts are a permutation of {2,3}, sum 5 = 5 calls. +;; $both_arms_return_typed 2 paths, called once -> sum 1. +;; $br_out_dead_tails 1 path (the two local.set tails after `br $a` are dead) -> counts[0]=1. +(module + (func $both_arms_return (export "a") (param $x i32) (result i32) + (if (local.get $x) + (then (return (i32.const 1))) + (else (return (i32.const 2)))) + (i32.const 99)) + (func $both_arms_return_typed (export "b") (param $x i32) (result i32) + (if (result i32) (local.get $x) + (then (return (i32.const 1))) + (else (return (i32.const 2))))) + (func $br_out_dead_tails (export "c") (result i32) + (local $r i32) + (block $a + (block $b + (block $c + (local.set $r (i32.const 1)) + (br $a) + ) + (local.set $r (i32.const 2)) + ) + (local.set $r (i32.const 3)) + ) + (local.get $r)) + (func (export "main") + (drop (call $both_arms_return (i32.const 1))) + (drop (call $both_arms_return (i32.const 1))) + (drop (call $both_arms_return (i32.const 0))) + (drop (call $both_arms_return (i32.const 0))) + (drop (call $both_arms_return (i32.const 0))) + (drop (call $both_arms_return_typed (i32.const 1))) + (drop (call $br_out_dead_tails))) +) diff --git a/test/monitors/path_profile_deep_nest.wasm b/test/monitors/path_profile_deep_nest.wasm new file mode 100644 index 0000000000000000000000000000000000000000..56bcd7ad3c0116b128b401b1f34b36d9e1ff2d31 GIT binary patch literal 165 zcmZvRF$#b%5CnHG34&)~Ef#*|{$ZhwU=bTji~G7!u@PAYhFQ#eCIG5T!QJT-5Q2bJ sBV2%(=XC??LwXE|KCmFsjmbolg+f#vVfR{e#!Ej^+Ef07@AWTw0vhrY)c^nh literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_deep_nest.wat b/test/monitors/path_profile_deep_nest.wat new file mode 100644 index 000000000..e23a94d92 --- /dev/null +++ b/test/monitors/path_profile_deep_nest.wat @@ -0,0 +1,30 @@ +;; if/else nested three levels deep, each nested inside the *then* arm of its parent. Existing +;; tests only nest two deep (demo_paths $nested_if). Pins that path numbering stays compact +;; when the DAG is deep and unbalanced rather than a chain of sequential diamonds. +;; +;; Paths: P1 x=0 ; P2 x=1,y=0 ; P3 x=1,y=1,z=0 ; P4 x=1,y=1,z=1 -> 4 paths +;; main takes P1 once, P2 twice, P3 three times, P4 four times. +;; expected counts: a permutation of {1,2,3,4}, sum = 10 = calls to $f +(module + (func $f (export "f") (param $x i32) (param $y i32) (param $z i32) (result i32) + (if (result i32) (local.get $x) + (then + (if (result i32) (local.get $y) + (then + (if (result i32) (local.get $z) + (then (i32.const 1)) + (else (i32.const 2)))) + (else (i32.const 3)))) + (else (i32.const 4)))) + (func (export "main") + (drop (call $f (i32.const 0) (i32.const 0) (i32.const 0))) + (drop (call $f (i32.const 1) (i32.const 0) (i32.const 0))) + (drop (call $f (i32.const 1) (i32.const 0) (i32.const 0))) + (drop (call $f (i32.const 1) (i32.const 1) (i32.const 0))) + (drop (call $f (i32.const 1) (i32.const 1) (i32.const 0))) + (drop (call $f (i32.const 1) (i32.const 1) (i32.const 0))) + (drop (call $f (i32.const 1) (i32.const 1) (i32.const 1))) + (drop (call $f (i32.const 1) (i32.const 1) (i32.const 1))) + (drop (call $f (i32.const 1) (i32.const 1) (i32.const 1))) + (drop (call $f (i32.const 1) (i32.const 1) (i32.const 1)))) +) diff --git a/test/monitors/path_profile_demo_only.wasm b/test/monitors/path_profile_demo_only.wasm new file mode 100644 index 0000000000000000000000000000000000000000..0e31b9464a586bc6209338611473cdd214327f8d GIT binary patch literal 93 zcmXZLu?c`M5J1s?cLBwKv9hpo2Ny7#B%p;wv~U1dH=sNo`7Q!Tt4SUbm?_c(b=zJp da3von(k(in>u07nM9qiaNMs;Nt+mx|z`q#93C#ci literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_demo_only.wat b/test/monitors/path_profile_demo_only.wat new file mode 100644 index 000000000..01fcbd5e8 --- /dev/null +++ b/test/monitors/path_profile_demo_only.wat @@ -0,0 +1,23 @@ +(module + (func $demo (export "demo") (param $x i32) + (block $L3 + (block $L2 + (block $L1 + (block $L0 + (br_table $L0 $L1 $L2 $L3 (local.get $x)) + ) + (return) + ) + (return) + ) + (return) + ) + (return) + ) + (func (export "main") + (call $demo (i32.const 0)) + (call $demo (i32.const 1)) + (call $demo (i32.const 2)) + (call $demo (i32.const 3)) + (call $demo (i32.const 4))) +) diff --git a/test/monitors/path_profile_diamond_only.wasm b/test/monitors/path_profile_diamond_only.wasm new file mode 100644 index 0000000000000000000000000000000000000000..cab47692a1cc4b1124d68bc08710d36459479a0f GIT binary patch literal 73 zcmWN>u?>JA5J1s?2ZksV#!?$sxlJIUp#Td9V0C@Pqr6=JSvAX)nZY?l!l9wI(w|Ym X_tFNkU7EP@(_}G?F=6bSCV%k*p;`(g literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_diamond_only.wat b/test/monitors/path_profile_diamond_only.wat new file mode 100644 index 000000000..3613d3a30 --- /dev/null +++ b/test/monitors/path_profile_diamond_only.wat @@ -0,0 +1,11 @@ +(module + (func $diamond (export "diamond") (param $x i32) (result i32) + (if (result i32) (local.get $x) + (then (i32.const 1)) + (else (i32.const 2)) + ) + ) + (func (export "main") + (drop (call $diamond (i32.const 0))) + (drop (call $diamond (i32.const 1)))) +) diff --git a/test/monitors/path_profile_dup_labels.wasm b/test/monitors/path_profile_dup_labels.wasm new file mode 100644 index 0000000000000000000000000000000000000000..39528e2edd9e84715b46ed68f05cecd427c2c369 GIT binary patch literal 83 zcmWN{u?c`M6b9k@Ujm8|4G7i_;1J0s+6Wf0aR68MaeUozzDu?>JA6h+bdJ{Y5rXl-F|6WIhB6ANq{fYtRBhx#@Fnyjd+7jVwd7)=-WR__PR VyDd`B!=Z#fLsE!jR<^9a@Buw~2k8I+ literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_empty_then.wat b/test/monitors/path_profile_empty_then.wat new file mode 100644 index 000000000..4632f9b07 --- /dev/null +++ b/test/monitors/path_profile_empty_then.wat @@ -0,0 +1,10 @@ +(module + (func $f (export "f") (param $x i32) (result i32) + (local $r i32) + (local.set $r (i32.const 5)) + (if (local.get $x) (then)) + (local.get $r)) + (func (export "main") + (drop (call $f (i32.const 0))) + (drop (call $f (i32.const 1)))) +) diff --git a/test/monitors/path_profile_freq.wasm b/test/monitors/path_profile_freq.wasm new file mode 100644 index 0000000000000000000000000000000000000000..a95f47acfe7f69772466d0f98bb9b049e22c588a GIT binary patch literal 214 zcmYk$K?=e!5QX9QCJ~EiAxD^;#AMrrXJ|KqfePXQav=|CY=mYv^9>9?*7rsLyvV}B zvVg@H0nelG4%lsdJnQvC$z)L^zHC$`=D?FO&{dOW(wl8&dmEY!z4>PTFuE&Ek!WIl dt+$B@f%{6uJW)mAk%Rm*Mz~)@Ey6yE`vL{^8c_fM literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_freq.wat b/test/monitors/path_profile_freq.wat new file mode 100644 index 000000000..eafd70d7f --- /dev/null +++ b/test/monitors/path_profile_freq.wat @@ -0,0 +1,43 @@ +;; Frequency accuracy: does counts[k] actually equal the number of times path k ran? +;; The rest of the suite only pins down that the numbering is a bijection (many_paths runs each of +;; 32 paths ONCE, so every counter reads 1) and that sum(counts) == activations. Both of those still +;; pass if every count lands in the wrong bucket, so neither tests the profiler's actual job. +;; +;; $f is five sequential diamonds selected by the five low bits of $x, so path number is a pure +;; function of $x. main calls $f(x) exactly (x+1) times for x in 0..31, giving all 32 paths DISTINCT +;; frequencies 1..32 -- no two counters can be confused, and 528 calls total. +;; +;; Ball-Larus numbering makes the FIRST diamond carry the largest stride (NumPaths of the rest = 16), +;; so the path number is the 5-bit reversal of $x: x=1 -> 16, x=2 -> 8, x=4 -> 4, x=8 -> 2, x=16 -> 1. +;; Hand-derived expectation: counts[bitreverse5(x)] == x+1 for every x in 0..31, summing to 528. +;; main itself contains loops, so the monitor skips it and only $f is reported. +(module + (func $f (export "f") (param $x i32) (result i32) (local $r i32) + (if (i32.and (local.get $x) (i32.const 1)) + (then (local.set $r (i32.add (local.get $r) (i32.const 1)))) + (else (local.set $r (local.get $r)))) + (if (i32.and (local.get $x) (i32.const 2)) + (then (local.set $r (i32.add (local.get $r) (i32.const 2)))) + (else (local.set $r (local.get $r)))) + (if (i32.and (local.get $x) (i32.const 4)) + (then (local.set $r (i32.add (local.get $r) (i32.const 4)))) + (else (local.set $r (local.get $r)))) + (if (i32.and (local.get $x) (i32.const 8)) + (then (local.set $r (i32.add (local.get $r) (i32.const 8)))) + (else (local.set $r (local.get $r)))) + (if (i32.and (local.get $x) (i32.const 16)) + (then (local.set $r (i32.add (local.get $r) (i32.const 16)))) + (else (local.set $r (local.get $r)))) + (local.get $r)) + (func (export "main") (result i32) (local $x i32) (local $j i32) (local $acc i32) + (block $ox (loop $lx + (br_if $ox (i32.ge_s (local.get $x) (i32.const 32))) + (local.set $j (i32.const 0)) + (block $oj (loop $lj + (br_if $oj (i32.gt_s (local.get $j) (local.get $x))) + (local.set $acc (i32.add (local.get $acc) (call $f (local.get $x)))) + (local.set $j (i32.add (local.get $j) (i32.const 1))) + (br $lj))) + (local.set $x (i32.add (local.get $x) (i32.const 1))) + (br $lx))) + (local.get $acc))) diff --git a/test/monitors/path_profile_freq_recur.wasm b/test/monitors/path_profile_freq_recur.wasm new file mode 100644 index 0000000000000000000000000000000000000000..7b07a7135215796c716e02fa0b8b1eb58dfda537 GIT binary patch literal 130 zcmYMqF$#k~6h+bZ{)~tY8Vj+o8dsvfa1WM>q==$mvVhroGNiD%#o=Q490b5jW(I~C zFh;<%6!U(rfIXJi3FedZWbhd6pm;bBFr>=;e{Cbu!3dxgz4(6mCUMNA!& G{Nlcs2M}KX literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_freq_recur.wat b/test/monitors/path_profile_freq_recur.wat new file mode 100644 index 000000000..87eba938e --- /dev/null +++ b/test/monitors/path_profile_freq_recur.wat @@ -0,0 +1,26 @@ +;; Frequency accuracy under RECURSION. Complements path_profile_freq.wat (which is non-recursive): +;; this exercises the per-function register save/restore stack at depth, where a wrong r would put +;; a nested activation's count in the caller's bucket. +;; +;; $rec has 5 paths: a base case (n==0) and 4 recursive paths, chosen by bits 0 and 1 of $n. +;; main calls $rec(7), $rec(13), $rec(20), $rec(5) -> 45 recursive activations + 4 base = 49. +;; For each top-level K, path(n%4) runs once for every n in 1..K; the base path runs once per call. +;; Hand-derived: n%4 counts over 1..7, 1..13, 1..20, 1..5 give recursive totals 10/11/13/11 and +;; base 4, summing to 49 = total activations. Max depth 20 exercises a non-trivial register stack. +(module + ;; base case + 4 recursive paths (two diamonds on bits 0 and 1 of $n) + (func $rec (export "rec") (param $n i32) (result i32) (local $r i32) + (if (i32.eqz (local.get $n)) (then (return (i32.const 0)))) + (if (i32.and (local.get $n) (i32.const 1)) + (then (local.set $r (i32.add (local.get $r) (i32.const 1)))) + (else (local.set $r (local.get $r)))) + (if (i32.and (local.get $n) (i32.const 2)) + (then (local.set $r (i32.add (local.get $r) (i32.const 2)))) + (else (local.set $r (local.get $r)))) + (i32.add (local.get $r) (call $rec (i32.sub (local.get $n) (i32.const 1))))) + (func (export "main") + (drop (call $rec (i32.const 7))) + (drop (call $rec (i32.const 13))) + (drop (call $rec (i32.const 20))) + (drop (call $rec (i32.const 5))) + )) diff --git a/test/monitors/path_profile_freq_shapes.wasm b/test/monitors/path_profile_freq_shapes.wasm new file mode 100644 index 0000000000000000000000000000000000000000..e2fd50def12c3330fafb7bb98087c13dbaae84df GIT binary patch literal 412 zcmZQbEY4+QU|?Y6WJ+MHXRJ?PU|?ouVqj$FVPZ^YU|`8j%*Ts|j66U}M3K=^l%LyC4#;KXcGOU0$outer and ENTRY->$inner, and the two branch sources each get their +;; own ->EXIT. +;; +;; The point is that the inner backedge must NOT end a segment that the outer one also ends. Every +;; backedge execution ends exactly one segment, whichever loop it belongs to, so the two trip counts +;; add rather than nest in the invariant below. +;; +;; $nested runs the inner loop $m times per outer iteration, for $n outer iterations. +;; main calls $nested(3, 2) once: the inner backedge executes 3*2 == 6 times and the outer 3 times. +;; INVARIANT: sum(counts) == activations + backedge executions == 1 + (6 + 3) == 10. +(module + (func $nested (export "nested") (param $n i32) (param $m i32) (result i32) + (local $i i32) (local $j i32) (local $s i32) + (block $outer_exit + (loop $outer + (br_if $outer_exit (i32.ge_s (local.get $i) (local.get $n))) + (local.set $j (i32.const 0)) + (block $inner_exit + (loop $inner + (br_if $inner_exit (i32.ge_s (local.get $j) (local.get $m))) + (local.set $s (i32.add (local.get $s) (i32.const 1))) + (local.set $j (i32.add (local.get $j) (i32.const 1))) + (br $inner))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $outer))) + (local.get $s)) + (func (export "main") + (drop (call $nested (i32.const 3) (i32.const 2)))) +) diff --git a/test/monitors/path_profile_loop_skip.wasm b/test/monitors/path_profile_loop_skip.wasm new file mode 100644 index 0000000000000000000000000000000000000000..c53179ba5735676ebc2d9abcc32e70f6ce9b8c77 GIT binary patch literal 248 zcmYj|O$x$L41_01t6~)1y101aIh5uCUZK`n#Gkf8b>XVF@VvfPH$r9xCi9`}8v;N} zInY4E0a1`i8anWFm(K28_bB>T187_K&-zrv2IR&^+ZD&My49z19p+{AuI`r#k~HO| zMKb?Tx*;Aom)w)beo3A^UDv@Z1TCQIS+^wr8^fAMgnx*{t$2>XBl1U!&LBCdO?3f} YWH3sNRadaYAv1B5IgV#=k{JclFY|9E 4 backedges. 1 + 4 == 5 segments. +;; func #1 $nested_loop(3) : loop inside a loop -> two independent backedges, two loop headers, +;; so step 1 adds a dummy pair for each. +;; func #2 $loop_no_backedge : a `loop` label nothing ever branches to. NOT a backedge, so this is +;; an ordinary acyclic function: 1 path, called twice -> counts[0] == 2. +;; This is the case a positional (bind_pos) backedge guess gets wrong; +;; marking from ControlStack's `to.isLoop()` gets it right. +;; func #3 $acyclic : plain diamond, 2 paths, each taken once. +;; func #4 main : 1 path. +;; INVARIANT: per function, sum(counts) == activations + backedge executions. +(module + (func $loop_sum (export "loop_sum") (param $n i32) (result i32) + (local $i i32) (local $s i32) + (block $exit + (loop $top + (br_if $exit (i32.ge_s (local.get $i) (local.get $n))) + (local.set $s (i32.add (local.get $s) (local.get $i))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $top) + ) + ) + (local.get $s)) + (func $nested_loop (export "nested_loop") (param $n i32) (result i32) + (local $i i32) (local $j i32) (local $s i32) + (block $ei + (loop $oi + (br_if $ei (i32.ge_s (local.get $i) (local.get $n))) + (local.set $j (i32.const 0)) + (block $ej + (loop $oj + (br_if $ej (i32.ge_s (local.get $j) (local.get $n))) + (local.set $s (i32.add (local.get $s) (i32.const 1))) + (local.set $j (i32.add (local.get $j) (i32.const 1))) + (br $oj) + ) + ) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $oi) + ) + ) + (local.get $s)) + (func $loop_no_backedge (export "loop_no_backedge") (result i32) + (loop $l (nop)) + (i32.const 7)) + (func $acyclic (export "acyclic") (param $x i32) (result i32) + (if (result i32) (local.get $x) (then (i32.const 1)) (else (i32.const 2)))) + (func (export "main") + (drop (call $loop_sum (i32.const 4))) + (drop (call $nested_loop (i32.const 3))) + (drop (call $loop_no_backedge)) + (drop (call $loop_no_backedge)) + (drop (call $acyclic (i32.const 0))) + (drop (call $acyclic (i32.const 1)))) +) diff --git a/test/monitors/path_profile_loop_two_backedges.wasm b/test/monitors/path_profile_loop_two_backedges.wasm new file mode 100644 index 0000000000000000000000000000000000000000..5e1717298cee9bbba83deeb54e6edcff818a42cb GIT binary patch literal 96 zcmWl~y$(Py7zNOKzgC2nm`yc#19LlAJp&_&pM=ENSEtK4SnizwSjf!CI0H%vnAf77 o_XPC5tOr;Q(vwlV@{SHuMaRIcOUX5AZ)q%yajAd?ZODGi2Z-kj<^TWy literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_loop_two_backedges.wat b/test/monitors/path_profile_loop_two_backedges.wat new file mode 100644 index 000000000..800acab07 --- /dev/null +++ b/test/monitors/path_profile_loop_two_backedges.wat @@ -0,0 +1,32 @@ +;; Two DISTINCT backedges to the SAME loop header. `loop_types` produces its type-3 segments from a +;; single backedge taken repeatedly; here the segment that ends at a backedge can end at either of +;; two different edges, so type 3 is split across them and each must be told apart. +;; +;; Section 4 step 1 adds dummies per VERTEX, not per backedge -- "for each vertex v that is the +;; target of one or more backedges, add a dummy edge ENTRY -> v" -- so $top gets ONE incoming +;; ENTRY->$top dummy however many backedges arrive, while the two branch sources, being distinct +;; vertices, each get their own ->EXIT dummy. That asymmetry is what this pins: the two backedges +;; must still be told apart even though they share a reinitialization constant. +;; +;; $two increments $i each iteration and takes backedge A when $i is even, backedge B when it is odd. +;; n=0 -> the guard fails immediately: type 1 alone, no backedge. +;; n=5 -> $i runs 1..5, so B fires on 1,3,5 and A on 2,4. +;; main calls $two(0) once and $two(5) once. +;; INVARIANT: sum(counts) == activations + backedge executions == 2 + 5 == 7. +(module + (func $two (export "two") (param $n i32) (result i32) + (local $i i32) + (block $exit + (loop $top + (br_if $exit (i32.ge_s (local.get $i) (local.get $n))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + ;; backedge A, taken on even $i + (if (i32.eqz (i32.rem_u (local.get $i) (i32.const 2))) + (then (br $top))) + ;; backedge B, taken on odd $i + (br $top))) + (local.get $i)) + (func (export "main") + (drop (call $two (i32.const 0))) + (drop (call $two (i32.const 5)))) +) diff --git a/test/monitors/path_profile_loop_types.wasm b/test/monitors/path_profile_loop_types.wasm new file mode 100644 index 0000000000000000000000000000000000000000..0b3d6092fdbcfd41af65b36c3e4882061f0ff45a GIT binary patch literal 91 zcmXBGK@LDL6a~Qdek(!=wqj8?aD?^(&d@F^A%cCcPGU0|#Csw and x->y, a backedge-free path is one of: +;; 1. ENTRY -> EXIT (no backedge taken at all) +;; 2. ENTRY -> v, ending with backedge v->w (the first backedge execution) +;; 3. w -> x, ending with backedge x->y (backedge to backedge; may be the same edge) +;; 4. after v->w, w -> EXIT (the last iteration falling out) +;; +;; $trip is one loop whose body runs $n times, so the backedge executes $n times. +;; n=0 -> the guard fails immediately: type 1 alone, no backedge. +;; n=k>0 -> type 2 once, type 3 (k-1) times, type 4 once. +;; main calls $trip(0) twice and $trip(5) once, giving type 1 = 2, type 2 = 1, type 3 = 4, +;; type 4 = 1. Types 1 and 3 have distinct frequencies, so they cannot be swapped undetected. +;; Types 2 and 4 are both 1 and stay that way for any trip count: a loop with a single exit runs +;; exactly one first-backedge and one final-exit per activation that enters it. Distinguishing +;; those two needs a differently shaped loop, not a different trip count. +;; INVARIANT: sum(counts) == activations + backedge executions == 3 + 5 == 8. +(module + (func $trip (export "trip") (param $n i32) (result i32) + (local $i i32) + (block $exit + (loop $top + (br_if $exit (i32.ge_s (local.get $i) (local.get $n))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $top))) + (local.get $i)) + (func (export "main") + (drop (call $trip (i32.const 0))) + (drop (call $trip (i32.const 0))) + (drop (call $trip (i32.const 5)))) +) diff --git a/test/monitors/path_profile_many_paths.wasm b/test/monitors/path_profile_many_paths.wasm new file mode 100644 index 0000000000000000000000000000000000000000..0ba21fca1c04611f4374bbb817847fdff5e8f6a1 GIT binary patch literal 333 zcmXZQNeaS15JusuO2-WC&~Y|S$rbbh_Dzz7LX-$12(EGz!IPTb^yYi;sC?E;L>x)M z4L1dm=LvC;Nbgd;&c_3hs>|w59DhXK%uPm?AL^8`Z7i`%N(%5CL# z*zM5m&~4LgJ0}hHPE&u=k literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_many_paths.wat b/test/monitors/path_profile_many_paths.wat new file mode 100644 index 000000000..ce5739266 --- /dev/null +++ b/test/monitors/path_profile_many_paths.wat @@ -0,0 +1,61 @@ +;; A function with 32 distinct paths: five sequential if/else diamonds selected by the five low +;; bits of one argument. This is the strongest available check that path numbering is COMPACT and +;; UNIQUE -- main calls $five32 once with each of the 32 bit patterns, so a correct profile is +;; exactly 32 counters, indices 0..31 with no gaps, each holding exactly 1. Any collision shows +;; as a 2 next to a 0; any gap shows as a stray 0. +;; +;; expected counts: counts[0..31] all == 1, sum = 32 = calls to $five32 +(module + (func $five32 (export "five32") (param $m i32) (result i32) + (local $r i32) + (if (i32.and (local.get $m) (i32.const 1)) + (then (local.set $r (i32.add (local.get $r) (i32.const 1)))) + (else (local.set $r (i32.sub (local.get $r) (i32.const 1))))) + (if (i32.and (local.get $m) (i32.const 2)) + (then (local.set $r (i32.add (local.get $r) (i32.const 2)))) + (else (local.set $r (i32.sub (local.get $r) (i32.const 2))))) + (if (i32.and (local.get $m) (i32.const 4)) + (then (local.set $r (i32.add (local.get $r) (i32.const 4)))) + (else (local.set $r (i32.sub (local.get $r) (i32.const 4))))) + (if (i32.and (local.get $m) (i32.const 8)) + (then (local.set $r (i32.add (local.get $r) (i32.const 8)))) + (else (local.set $r (i32.sub (local.get $r) (i32.const 8))))) + (if (i32.and (local.get $m) (i32.const 16)) + (then (local.set $r (i32.add (local.get $r) (i32.const 16)))) + (else (local.set $r (i32.sub (local.get $r) (i32.const 16))))) + (local.get $r)) + (func (export "main") + (drop (call $five32 (i32.const 0))) + (drop (call $five32 (i32.const 1))) + (drop (call $five32 (i32.const 2))) + (drop (call $five32 (i32.const 3))) + (drop (call $five32 (i32.const 4))) + (drop (call $five32 (i32.const 5))) + (drop (call $five32 (i32.const 6))) + (drop (call $five32 (i32.const 7))) + (drop (call $five32 (i32.const 8))) + (drop (call $five32 (i32.const 9))) + (drop (call $five32 (i32.const 10))) + (drop (call $five32 (i32.const 11))) + (drop (call $five32 (i32.const 12))) + (drop (call $five32 (i32.const 13))) + (drop (call $five32 (i32.const 14))) + (drop (call $five32 (i32.const 15))) + (drop (call $five32 (i32.const 16))) + (drop (call $five32 (i32.const 17))) + (drop (call $five32 (i32.const 18))) + (drop (call $five32 (i32.const 19))) + (drop (call $five32 (i32.const 20))) + (drop (call $five32 (i32.const 21))) + (drop (call $five32 (i32.const 22))) + (drop (call $five32 (i32.const 23))) + (drop (call $five32 (i32.const 24))) + (drop (call $five32 (i32.const 25))) + (drop (call $five32 (i32.const 26))) + (drop (call $five32 (i32.const 27))) + (drop (call $five32 (i32.const 28))) + (drop (call $five32 (i32.const 29))) + (drop (call $five32 (i32.const 30))) + (drop (call $five32 (i32.const 31))) + ) +) diff --git a/test/monitors/path_profile_mixed_nest.wasm b/test/monitors/path_profile_mixed_nest.wasm new file mode 100644 index 0000000000000000000000000000000000000000..cfa32f4df5969fc4147fbc9c651e37f9ce903517 GIT binary patch literal 249 zcmZvUK?=e^3`PIXOc5GsUAQVJMerWZN!kh)3Rc0opHq21Gj^c`3E?G=&qp_n1i)Nc ziIK4doOdQxN4I`<=j)}p_s!5xfDV1SKN9p~H{2lki|zn9rKwQ}C|c9Uz$}QC8l0`y s9cuVF#DbuP-2Ar8?Aq%C!DfTXz#njyOJuYBi=2 (default) -> 4 paths +;; main: P1 once, P2 twice, P3 three times, P4 four times. +;; expected counts: a permutation of {1,2,3,4}, sum = 10 = calls to $table_of_ifs +;; $if_of_table: if whose then arm holds a 2-entry br_table on $y; the else arm falls through +;; to the shared tail. +;; P1 x!=0,y=0 (return 10) ; P2 x!=0,y!=0 (return 20) ; P3 x=0 (else -> tail 30) +;; -> 3 paths. main: P1 once, P2 twice, P3 three times. +;; expected counts: a permutation of {1,2,3}, sum = 6 = calls to $if_of_table +(module + (func $table_of_ifs (export "table_of_ifs") (param $x i32) (param $y i32) (result i32) + (block $L2 + (block $L1 + (block $L0 + (br_table $L0 $L1 $L2 (local.get $x)) + ) + (return (if (result i32) (local.get $y) (then (i32.const 1)) (else (i32.const 2)))) + ) + (return (i32.const 3)) + ) + (i32.const 4)) + (func $if_of_table (export "if_of_table") (param $x i32) (param $y i32) (result i32) + (if (local.get $x) + (then + (block $M1 + (block $M0 + (br_table $M0 $M1 (local.get $y)) + ) + (return (i32.const 10)) + ) + (return (i32.const 20))) + (else (nop))) + (i32.const 30)) + (func (export "main") + (drop (call $table_of_ifs (i32.const 0) (i32.const 0))) + (drop (call $table_of_ifs (i32.const 0) (i32.const 1))) + (drop (call $table_of_ifs (i32.const 0) (i32.const 1))) + (drop (call $table_of_ifs (i32.const 1) (i32.const 0))) + (drop (call $table_of_ifs (i32.const 1) (i32.const 0))) + (drop (call $table_of_ifs (i32.const 1) (i32.const 0))) + (drop (call $table_of_ifs (i32.const 5) (i32.const 0))) + (drop (call $table_of_ifs (i32.const 5) (i32.const 0))) + (drop (call $table_of_ifs (i32.const 5) (i32.const 0))) + (drop (call $table_of_ifs (i32.const 5) (i32.const 0))) + (drop (call $if_of_table (i32.const 1) (i32.const 0))) + (drop (call $if_of_table (i32.const 1) (i32.const 1))) + (drop (call $if_of_table (i32.const 1) (i32.const 1))) + (drop (call $if_of_table (i32.const 0) (i32.const 0))) + (drop (call $if_of_table (i32.const 0) (i32.const 0))) + (drop (call $if_of_table (i32.const 0) (i32.const 0)))) +) diff --git a/test/monitors/path_profile_one_armed.wasm b/test/monitors/path_profile_one_armed.wasm new file mode 100644 index 0000000000000000000000000000000000000000..334cc7a21fb5fdab18f94b8df843b076938489f0 GIT binary patch literal 74 zcmWN>u?>JA6h+Z@0b>*j6FVz$68|R9m{?%r0IaU3II3?$5|b-*dXc<$VoZ)p3a#Fc Xm=Cu?w;{7DoJJ<@#3a+A&G?HSVTcG- literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_one_armed.wat b/test/monitors/path_profile_one_armed.wat new file mode 100644 index 000000000..2be839945 --- /dev/null +++ b/test/monitors/path_profile_one_armed.wat @@ -0,0 +1,11 @@ +(module + (func $f (export "f") (param $x i32) (result i32) + (local $r i32) + (local.set $r (i32.const 5)) + (if (local.get $x) + (then (local.set $r (i32.const 1)))) + (local.get $r)) + (func (export "main") + (drop (call $f (i32.const 0))) + (drop (call $f (i32.const 1)))) +) diff --git a/test/monitors/path_profile_recur.wasm b/test/monitors/path_profile_recur.wasm new file mode 100644 index 0000000000000000000000000000000000000000..0f7817662854d994c2ebb6921c6eb3682725fc09 GIT binary patch literal 91 zcmZQbEY4+QU|?Y6WJ+MHXRJ?PU|?ouVqj$FXJSc9OfF$yV98C)%wu5WGGG#CP+)Lm j^kJ!YWMowUv$F*la=5w0fT98nQjSa@f*C}x0ts#aP<{)Z literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_recur.wat b/test/monitors/path_profile_recur.wat new file mode 100644 index 000000000..d1f0ae360 --- /dev/null +++ b/test/monitors/path_profile_recur.wat @@ -0,0 +1,13 @@ +(module + (func $fact (export "fact") (param $n i32) (result i32) + (if (result i32) (i32.le_s (local.get $n) (i32.const 1)) + (then (i32.const 1)) + (else (i32.mul (local.get $n) (call $fact (i32.sub (local.get $n) (i32.const 1))))) + ) + ) + (func (export "main") + (drop (call $fact (i32.const 1))) + (drop (call $fact (i32.const 2))) + (drop (call $fact (i32.const 3))) + (drop (call $fact (i32.const 5)))) +) diff --git a/test/monitors/path_profile_returns.wasm b/test/monitors/path_profile_returns.wasm new file mode 100644 index 0000000000000000000000000000000000000000..59fddbe952ffb61ce50393f1601ef0d609cf1f69 GIT binary patch literal 148 zcmY+4F%H5o5CnI9jvR4P?1GBQ|476AByK<{LX!vh>l`9Tq}f@`F512k0JAKiclL#! z03-?+QV~9UeS&do>kHx1 3 paths +;; main: P1 once, P2 twice, P3 three times. counts = perm of {1,2,3}, sum 6 = calls. +;; $g: if/else where the then arm returns and the else arm falls through to the tail -> +;; 2 paths. main: taken once, not-taken twice. counts = perm of {1,2}, sum 3 = calls. +(module + (func $f (export "f") (param $a i32) (param $b i32) (result i32) + (if (local.get $a) (then (return (i32.const 1)))) + (if (local.get $b) (then (return (i32.const 2)))) + (i32.const 3)) + (func $g (export "g") (param $x i32) (result i32) + (if (local.get $x) + (then (return (i32.const 7))) + (else (nop))) + (i32.const 8)) + (func (export "main") + (drop (call $f (i32.const 1) (i32.const 0))) + (drop (call $f (i32.const 0) (i32.const 1))) + (drop (call $f (i32.const 0) (i32.const 1))) + (drop (call $f (i32.const 0) (i32.const 0))) + (drop (call $f (i32.const 0) (i32.const 0))) + (drop (call $f (i32.const 0) (i32.const 0))) + (drop (call $g (i32.const 1))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0)))) +) diff --git a/test/monitors/path_profile_select_call.wasm b/test/monitors/path_profile_select_call.wasm new file mode 100644 index 0000000000000000000000000000000000000000..ff27c9040ed12297aa80fed5bb566062ba5ad069 GIT binary patch literal 175 zcmXxZO%8%E6a?Uz*M|fflP2zPQ{zFjClQ2b8o)me(5p+MZoZk!pq(cIpfin0DKRz7 z0H#ooQXW*Fu^WIQ&X2watIIoH&s{r?5S`k1hgsjWf~Oacoh{)J__A9v$;rcUgHY^g eJ?#-;`GPpqOZ*Cg3Hn|B#Yr7ZMlzaNE!YnQ(HOD- literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_select_call.wat b/test/monitors/path_profile_select_call.wat new file mode 100644 index 000000000..90cf126a6 --- /dev/null +++ b/test/monitors/path_profile_select_call.wat @@ -0,0 +1,33 @@ +;; `select` (a data-flow conditional, NOT control flow -> must stay a single path) and a br_if +;; whose condition is produced by a CALL. The call returns before the br_if executes, so the +;; BoolProbe must read the branch operand off the frame *after* the callee's own exit probe has +;; run and restored the callee register -- registers are per function, so the two must not +;; interfere. +;; +;; $pick : select only -> 1 path. Called 4 times -> counts[0] = 4 +;; $isodd : straight line -> 1 path. Called 5 times (once per $brif_call) -> counts[0] = 5 +;; $brif_call: br_if on (call $isodd n) -> 2 paths. main: taken twice (n odd), not taken +;; three times (n even). expected counts: perm of {2,3}, sum = 5 = calls +(module + (func $pick (export "pick") (param $c i32) (param $a i32) (param $b i32) (result i32) + (select (local.get $a) (local.get $b) (local.get $c))) + (func $isodd (export "isodd") (param $n i32) (result i32) + (i32.and (local.get $n) (i32.const 1))) + (func $brif_call (export "brif_call") (param $n i32) (result i32) + (local $r i32) + (block $done + (br_if $done (call $isodd (local.get $n))) + (local.set $r (i32.const 5)) + ) + (local.get $r)) + (func (export "main") + (drop (call $pick (i32.const 0) (i32.const 1) (i32.const 2))) + (drop (call $pick (i32.const 1) (i32.const 1) (i32.const 2))) + (drop (call $pick (i32.const 0) (i32.const 3) (i32.const 4))) + (drop (call $pick (i32.const 1) (i32.const 3) (i32.const 4))) + (drop (call $brif_call (i32.const 1))) + (drop (call $brif_call (i32.const 3))) + (drop (call $brif_call (i32.const 2))) + (drop (call $brif_call (i32.const 4))) + (drop (call $brif_call (i32.const 6)))) +) diff --git a/test/monitors/path_profile_selfloop.wasm b/test/monitors/path_profile_selfloop.wasm new file mode 100644 index 0000000000000000000000000000000000000000..db4004dfe4cec80564ca9985157251f3770bab38 GIT binary patch literal 140 zcmYj`K?;K~6b0W;ehK<5lrD9XE_(~J@Ej_w(jX>aZC!fx2HbQrFf-`iGXbDXb+pso z0s6$r$=o@A#7|h;XWRl{Hov}FKZ48{m*G1sP(1fOK<5o#p=%3A(+w79h=o-D$*IUS SrF=Y-UICE;PG3+aP$@t3<`rcC literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_selfloop.wat b/test/monitors/path_profile_selfloop.wat new file mode 100644 index 000000000..fb746d033 --- /dev/null +++ b/test/monitors/path_profile_selfloop.wat @@ -0,0 +1,38 @@ +;; SELF-LOOPS: a backedge whose source and target are the same block (Ball-Larus Section 4). +;; A `loop` whose body has no internal branching collapses the header and the branch into one +;; block, so `br_if $L` becomes an edge from that block to itself. Section 4 handles these with a +;; plain iteration counter instead of [count[r]++; r=0], because removing the edge leaves no edge +;; inside the loop to carry a path value. +;; +;; func #0 $count_down: self-loop, called once with n=3 -> n goes 3,2,1 so the br_if is taken +;; twice. The self-loop counter must read 2, and the PATH count must be 1 -- +;; iterating a self-loop does not change which path ran, so the trip count +;; must not leak into counts[]. +;; func #1 $with_if : the same loop with an `if` in the body, which splits the block, so this is +;; an ORDINARY backedge (w != v) and is path-profiled normally. Called with +;; n=3: body runs 3 times, so the backedge is taken twice (the last iteration +;; falls out) -> 1 + 2 == 3 segments. +;; func #2 $forever : unconditional self-branch, so the block's ONLY outgoing edge is the +;; self-loop and EXIT is unreachable -- the function never returns. It must +;; report NO path slots at all (a path that never reaches EXIT does not +;; exist), and only its self-loop counter. Never called -- it only has to be +;; analyzable. This is the case that pins both the EXIT vertex needing a +;; disjoint set of its own and Fig. 5's leaf test being `v == EXIT`. +;; func #3 main : straight-line, 1 path. +(module + (func $count_down (export "count_down") (param $n i32) (result i32) + (loop $L + (local.set $n (i32.sub (local.get $n) (i32.const 1))) + (br_if $L (local.get $n))) + (local.get $n)) + (func $with_if (export "with_if") (param $n i32) (result i32) + (loop $L + (local.set $n (i32.sub (local.get $n) (i32.const 1))) + (if (i32.rem_s (local.get $n) (i32.const 2)) (then (nop))) + (br_if $L (local.get $n))) + (local.get $n)) + (func $forever (export "forever") (loop $L (br $L))) + (func (export "main") + (drop (call $count_down (i32.const 3))) + (drop (call $with_if (i32.const 3)))) +) diff --git a/test/monitors/path_profile_selfloop_dup.wasm b/test/monitors/path_profile_selfloop_dup.wasm new file mode 100644 index 0000000000000000000000000000000000000000..b789d1c410e7f94d0713c21a539fe6fe2c39bb8d GIT binary patch literal 88 zcmWN}u?>JA00hAQ$4HcDthK?y+7)C1SCCQ@j0pp b. +;; +;; Both arms are the same CFG transition -- whichever is taken, control goes b -> b -- so neither +;; affects the path number. Each edge reports its own line, naming the arm that reaches it. +;; +;; $ms(6): $i counts 1..6. The table index is 2 once $i reaches $n (exit via $out), otherwise +;; $i % 2 -- so arm 1 fires on odd $i (1, 3, 5) and arm 0 on even $i (2, 4), alternating +;; 1,0,1,0,1: `arm 1] = 3` and `arm 0] = 2`, five trips through the block in total. +;; INVARIANT: sum(counts) == activations + non-self backedge executions == 1 + 0 == 1; +;; the 5 self-loop trips are excluded, and reported separately. +(module + (func $ms (export "ms") (param $n i32) (result i32) + (local $i i32) + (block $out + (loop $L + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br_table $L $L $out + (select (i32.const 2) (i32.rem_u (local.get $i) (i32.const 2)) + (i32.ge_u (local.get $i) (local.get $n)))))) + (local.get $i)) + (func (export "main") + (drop (call $ms (i32.const 6)))) +) diff --git a/test/monitors/path_profile_shapes.wasm b/test/monitors/path_profile_shapes.wasm new file mode 100644 index 0000000000000000000000000000000000000000..d215d6b4e0a1815e0922062f97da6bcf66a1352a GIT binary patch literal 123 zcmWlOK?=h#5Cf%MyP*{Z(^K&&PlPb&jw2sOP)~YxC0dZ># Ah5!Hn literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_shapes.wat b/test/monitors/path_profile_shapes.wat new file mode 100644 index 000000000..5424c3132 --- /dev/null +++ b/test/monitors/path_profile_shapes.wat @@ -0,0 +1,13 @@ +(module + (func $empty_block (export "a") (result i32) (block) (i32.const 1)) + (func $brif_then_end (export "b") (param $x i32) (result i32) + (block $L (br_if $L (local.get $x))) + (i32.const 2)) + (func $empty_then (export "c") (param $x i32) (result i32) + (if (local.get $x) (then)) + (i32.const 3)) + (func $nested_empty (export "d") (result i32) (block (block (block))) (i32.const 4)) + (func (export "main") + (drop (call $empty_block)) (drop (call $brif_then_end (i32.const 0))) + (drop (call $empty_then (i32.const 0))) (drop (call $nested_empty))) +) diff --git a/test/monitors/path_profile_straight.wasm b/test/monitors/path_profile_straight.wasm new file mode 100644 index 0000000000000000000000000000000000000000..f5f8dc3ad4b8b4847f9fe03abeaa3d4f7096d61c GIT binary patch literal 127 zcmWlOQ3}E^5JYFP8;TZTzw;sO5j=tHO&aWv(pDh{=+)i8%;PbG+I9lqO%|R!7xY51 zf=I(%wXW*}(Bu1SV1CtYYe>4+_W`*+b-)DYXS{$eg^sWZ28(Tu$51?VNWj?RYN>b* FjDNd65L^HN literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_straight.wat b/test/monitors/path_profile_straight.wat new file mode 100644 index 000000000..3ec3d7448 --- /dev/null +++ b/test/monitors/path_profile_straight.wat @@ -0,0 +1,26 @@ +;; Straight-line functions with no decision at all. The single-path case is the base case of +;; the whole scheme: counts[0] must equal the activation count exactly, and there must be +;; exactly one counter. +;; +;; $add1 : pure arithmetic, called 5 times (3 from main + 1 per $calls) -> counts[0] = 5 +;; $nops : nothing but nop/drop, called 4 times (2 from main + 1 per $calls) -> counts[0] = 4 +;; $calls : straight line containing calls (calls are not control flow for this analysis), +;; called twice -> counts[0] = 2 +;; main itself is straight-line -> counts[0] = 1 +(module + (func $add1 (export "add1") (param $x i32) (result i32) + (i32.add (local.get $x) (i32.const 1))) + (func $nops (export "nops") (result i32) + (nop) (drop (i32.const 3)) (nop) + (i32.const 0)) + (func $calls (export "calls") (param $x i32) (result i32) + (i32.add (call $add1 (local.get $x)) (call $nops))) + (func (export "main") + (drop (call $add1 (i32.const 1))) + (drop (call $add1 (i32.const 2))) + (drop (call $add1 (i32.const 3))) + (drop (call $nops)) + (drop (call $nops)) + (drop (call $calls (i32.const 4))) + (drop (call $calls (i32.const 5)))) +) diff --git a/test/monitors/path_profile_table1.wasm b/test/monitors/path_profile_table1.wasm new file mode 100644 index 0000000000000000000000000000000000000000..86acb7952550e7ad5c68e25778425cf6a9ec52ea GIT binary patch literal 73 zcmWm0u?>JA6h+bdJ|;#X##ma(Cb9`MCKlK@0K>Y7p*cB}w+hf?Sxqg2bB4xXx?rf~ We$X5(VCTUCKFMz+R`#UM*Xw^xR|tUs literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_table1.wat b/test/monitors/path_profile_table1.wat new file mode 100644 index 000000000..a58b91002 --- /dev/null +++ b/test/monitors/path_profile_table1.wat @@ -0,0 +1,17 @@ +;; br_table with a single entry: the label list is just the default, so the block has ONE +;; outgoing edge and no runtime decision at all. Pins that a degenerate br_table is treated as +;; an unconditional branch (single-path function), not as a 0- or 2-way dispatch. +;; +;; Paths: 1. main calls $f 3 times with unrelated indices; all take it. +;; expected counts: {0 => 3}, sum = 3 = calls to $f +(module + (func $f (export "f") (param $x i32) (result i32) + (block $L0 + (br_table $L0 (local.get $x)) + ) + (i32.const 1)) + (func (export "main") + (drop (call $f (i32.const 0))) + (drop (call $f (i32.const 1))) + (drop (call $f (i32.const 99)))) +) diff --git a/test/monitors/path_profile_table2.wasm b/test/monitors/path_profile_table2.wasm new file mode 100644 index 0000000000000000000000000000000000000000..353a1f091ea2c215783ee0a799588e5cd470677f GIT binary patch literal 94 zcmYMnF%CdL7zN>ZKgos_Bvh(Bg?jfUG!g}k1MJlih33l)>vafFWs9=%0!3lSHKq;9 fW8F_w&o*GuVxSfBsqTCleIU^@edw(p_pHAG4rd9@ literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_table2.wat b/test/monitors/path_profile_table2.wat new file mode 100644 index 000000000..eaf25a0c2 --- /dev/null +++ b/test/monitors/path_profile_table2.wat @@ -0,0 +1,25 @@ +;; br_table with exactly 2 entries (1 explicit label + the default). read_labels().length == 2, +;; so this has the same out-degree as a br_if -- the probe must be a TableProbe (dispatch on the +;; index operand, clamped) and NOT a BoolProbe (dispatch on truthiness). No other test covers +;; the 2-edge br_table shape. +;; +;; Paths: A = index 0 -> $L0 -> return 10 ; B = index >= 1 -> $L1 (default) -> 20 +;; main: x=0 twice (A), x=1 three times (B), x=7 once (B, clamped) +;; expected counts: {A=2, B=4}, sum = 6 = calls to $f +(module + (func $f (export "f") (param $x i32) (result i32) + (block $L1 + (block $L0 + (br_table $L0 $L1 (local.get $x)) + ) + (return (i32.const 10)) + ) + (i32.const 20)) + (func (export "main") + (drop (call $f (i32.const 0))) + (drop (call $f (i32.const 0))) + (drop (call $f (i32.const 1))) + (drop (call $f (i32.const 1))) + (drop (call $f (i32.const 1))) + (drop (call $f (i32.const 7)))) +) diff --git a/test/monitors/path_profile_table_default.wasm b/test/monitors/path_profile_table_default.wasm new file mode 100644 index 0000000000000000000000000000000000000000..2584e6e6507c2baaf499346f7c179f21b2d171f5 GIT binary patch literal 92 zcmWm3u?>JQ5JOR)4++5`phHT<5DdU=k~ByZp<#e5%OE7O{%ef%W&t1rqZBC-P$i_F jC>PM>bU%>sU;xjy9do^DE{i#9Y8#v@f@|YLvsCN{1#=48 literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_table_default.wat b/test/monitors/path_profile_table_default.wat new file mode 100644 index 000000000..b2726ae6d --- /dev/null +++ b/test/monitors/path_profile_table_default.wat @@ -0,0 +1,26 @@ +;; br_table where EVERY executed index falls through to the default label, including the +;; out-of-range clamp in TableProbe (v >= dispatch.length -> last entry) and an index whose +;; i32 value is negative (0xffffffff), which must be read as an unsigned u32 and clamped, not +;; used as a negative array index. +;; +;; 3 labels ($L0, $L1, default $L2) -> 3 paths. main only ever passes 2, 3, 100 and -1, +;; so the two indexed paths are never taken. +;; expected counts: one path = 4, the other two = 0; sum = 4 = calls to $f +(module + (func $f (export "f") (param $x i32) (result i32) + (block $L2 + (block $L1 + (block $L0 + (br_table $L0 $L1 $L2 (local.get $x)) + ) + (return (i32.const 1)) + ) + (return (i32.const 2)) + ) + (i32.const 3)) + (func (export "main") + (drop (call $f (i32.const 2))) + (drop (call $f (i32.const 3))) + (drop (call $f (i32.const 100))) + (drop (call $f (i32.const -1)))) +) diff --git a/test/monitors/path_profile_trap.wasm b/test/monitors/path_profile_trap.wasm new file mode 100644 index 0000000000000000000000000000000000000000..f3558c2229c5629f3e30633c3649c1a17f82c37b GIT binary patch literal 60 zcmV~$F%Ezr5JkcF7m|&vg@u)sN0FOAV`71g2jJ??(7z!-i#1Kn1}Pcx)pSE1{drN@ Ot#FbrqIx)$jU#^PBL=4c literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_trap.wat b/test/monitors/path_profile_trap.wat new file mode 100644 index 000000000..b01f5ec1f --- /dev/null +++ b/test/monitors/path_profile_trap.wat @@ -0,0 +1,6 @@ +(module + (func $f (export "f") (param $x i32) (result i32) + (if (local.get $x) (then (unreachable))) + (i32.const 7)) + (func (export "main") (drop (call $f (i32.const 0)))) +) diff --git a/test/monitors/path_profile_wide_merge.wasm b/test/monitors/path_profile_wide_merge.wasm new file mode 100644 index 0000000000000000000000000000000000000000..0cfa31d0eb185d16ee4affd5f87bef2dd9888287 GIT binary patch literal 190 zcmZvQF$%&!6hvqKUzFsFC|Fs9q_Y<6^AF%1k|K!0TId1x3OTa7jRBE?_hts;I}!jZ z8JL&`Kvlr?URH5>-KGl=;&^|8^-Sr>B>j>W+W?9N^Sa|E>yV}9O_rFSvS@zpXf2%& VyTY-<(fNgxwm{4LK~=s#un*=X6$AhP literal 0 HcmV?d00001 diff --git a/test/monitors/path_profile_wide_merge.wat b/test/monitors/path_profile_wide_merge.wat new file mode 100644 index 000000000..3f9617386 --- /dev/null +++ b/test/monitors/path_profile_wide_merge.wat @@ -0,0 +1,46 @@ +;; A single block with FIVE predecessors. wide_only/demo cover a wide br_table whose arms all +;; `return` (so the fan-in lands on EXIT); here the five arms instead `br` to a common join +;; block that then continues, so the join is a real interior merge with 5 incoming edges. +;; +;; Paths: one per br_table index (0..3 indexed, >=4 default) -> 5 paths. +;; main: index 0 once, 1 twice, 2 three times, 3 four times, 9 five times (default). +;; expected counts: a permutation of {1,2,3,4,5}, sum = 15 = calls to $merge5 +(module + (func $merge5 (export "merge5") (param $x i32) (result i32) + (local $r i32) + (block $done + (block $L4 + (block $L3 + (block $L2 + (block $L1 + (block $L0 + (br_table $L0 $L1 $L2 $L3 $L4 (local.get $x)) + ) + (local.set $r (i32.const 10)) (br $done) + ) + (local.set $r (i32.const 20)) (br $done) + ) + (local.set $r (i32.const 30)) (br $done) + ) + (local.set $r (i32.const 40)) (br $done) + ) + (local.set $r (i32.const 50)) + ) + (i32.add (local.get $r) (i32.const 1))) + (func (export "main") + (drop (call $merge5 (i32.const 0))) + (drop (call $merge5 (i32.const 1))) + (drop (call $merge5 (i32.const 1))) + (drop (call $merge5 (i32.const 2))) + (drop (call $merge5 (i32.const 2))) + (drop (call $merge5 (i32.const 2))) + (drop (call $merge5 (i32.const 3))) + (drop (call $merge5 (i32.const 3))) + (drop (call $merge5 (i32.const 3))) + (drop (call $merge5 (i32.const 3))) + (drop (call $merge5 (i32.const 9))) + (drop (call $merge5 (i32.const 9))) + (drop (call $merge5 (i32.const 9))) + (drop (call $merge5 (i32.const 9))) + (drop (call $merge5 (i32.const 9)))) +) diff --git a/test/monitors/path_profile_wide_only.wasm b/test/monitors/path_profile_wide_only.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6dee70ca1fe0c4bbb140c321130aa0f71014b8b5 GIT binary patch literal 122 zcmYkrF$#k~5J1sCv%67Th*qhCcn~c;g^CHdg*FP7xq7Bk9*=2X1&~}v`7wfXiexX1 sXZn1H{Y=NafM2JyAw{nZ+Fsr~1ES7{Sl-lhRCHq1I_qt)51UoQKRJA5J1s?hY3L>8cPeqDBdQ}m{?%r0IV)~<)eO00GYLD@6ro6r-)xvZkSqq W-m$0d=#|NmNSI%vg&445 [])] taken 6, not taken 3 +;; func #8: x 9 br_on_cast_fail[...] taken 3, not taken 6 +;; +;; INVARIANT: sum(counts) == activations. Measured: +;; func #5 9 executions sum(counts) = 9 correct +;; func #6 9 executions sum(counts) = 9 correct +;; func #7 9 executions sum(counts) = 6 3 lost +;; func #8 9 executions sum(counts) = 0 all 9 lost +;; +;; The two functions holding a cast opcode are exactly the two that break the invariant, while +;; their neighbours with an identical call count report correctly. +;; +;; No .wasm is committed for this file: the wabt in this environment rejects `anyref`, `(ref extern)` +;; and the GC type syntax, so a minimal case cannot be assembled here. +(module + (func $f (export "f") (param $r externref) (result i32) + (block $b (result (ref extern)) + (br_on_cast $b externref (ref extern) (local.get $r)) + (drop) + (return (i32.const 0))) + (drop) + (i32.const 1)) + (func (export "main") + (drop (call $f (ref.null extern)))) +) diff --git a/test/monitors/pathbug_br_on_null.wat b/test/monitors/pathbug_br_on_null.wat new file mode 100644 index 000000000..333638b30 --- /dev/null +++ b/test/monitors/pathbug_br_on_null.wat @@ -0,0 +1,35 @@ +;; BUG 3 (PathProfilingMonitor.placeProbes / BoolProbe): a two-successor block is always given a +;; BoolProbe, which unboxes operand 0 as an i32. +;; +;; ControlStack routes br_on_null, br_on_non_null, br_on_cast and br_on_cast_fail through brIf(), +;; so each produces an ordinary two-successor decision block. placeProbes special-cases only +;; BR_TABLE, so those blocks get a BoolProbe, and BoolProbe.fire does +;; Values.unbox_u(loc.frame.getFrameAccessor().getOperand(0)) +;; which is `Value.I32.!(v).val` -- a checked variant cast. The operand is a reference. +;; +;; EXPECTED: the branch is profiled like any other two-way decision. +;; ACTUAL: !TypeCheckException +;; in Values.unbox_u() [src/engine/Value.v3 @ 116:52] +;; in BoolProbe.fire() [src/monitors/PathProfilingMonitor.v3 @ 542:39] +;; +;; SECOND DEFECT, hidden behind the crash: even with a correct operand read, BoolProbe picks +;; `if(v != 0, trueEdge, falseEdge)`. br_on_null branches when the reference IS null and +;; br_on_cast_fail when the cast FAILS, so the two arms are swapped for those opcodes -- a silent +;; miscount rather than a crash. +;; +;; NOTE: the wabt in this environment cannot assemble br_on_non_null, so this .wat has no committed +;; .wasm. Reproduce with modules already in the repo: +;; ./bin/wizeng.x86-64-linux --monitors=path-profile test/microbench/100ms/br_on_null0.wasm +;; (also test/monitors/branch_monitor_gc.wasm, test/monitors/apps/branch2.wasm, +;; test/monitors/apps/branch_gc.wasm) +;; Once the toolchain supports it, the module below is the minimal case: $f is called once with a +;; null reference, so the branch is taken and the probe fires on a Value.Ref. +(module + (func $f (export "f") (param $r externref) (result i32) + (block $nn + (br_on_non_null $nn (local.get $r)) + (return (i32.const 9))) + (i32.const 7)) + (func (export "main") + (drop (call $f (ref.null extern)))) +) diff --git a/test/monitors/pathbug_deadend_enum.wasm b/test/monitors/pathbug_deadend_enum.wasm new file mode 100644 index 0000000000000000000000000000000000000000..09f6d227a35ae43c14eb3b0c583781b212a85ee0 GIT binary patch literal 236 zcmdVKu?>JA6h+bdK17M1!Vw$=H-W~)0viW#FKY)gj$v^Q?d$@Stm>jd2!_UN+939} WA82}Hw^)efBmc{xWZZpVWyW8VRunG) literal 0 HcmV?d00001 diff --git a/test/monitors/pathbug_deadend_enum.wat b/test/monitors/pathbug_deadend_enum.wat new file mode 100644 index 000000000..ab5bd73a1 --- /dev/null +++ b/test/monitors/pathbug_deadend_enum.wat @@ -0,0 +1,53 @@ +;; BUG (PathProfilingMonitor.enumPaths:561 / walkPath): the MAX_PATHS_TO_LIST guard is measured +;; on numPaths -- EXIT-reaching paths only -- while walkPath enumerates every ENTRY-rooted path. +;; +;; Every path through the nest below dead-ends at a block whose only successor is a backedge. +;; walkPath does `if (e.isBackedge) continue`, so that block is a leaf that never reaches EXIT +;; and contributes nothing to numPaths. numPaths(ENTRY) is therefore 2, the <= 1024 guard +;; passes, and enumPaths runs -- but walkPath still explores 2^N prefixes before discarding +;; them all. +;; +;; EXPECTED: output is two lines, so it should be instant. +;; ACTUAL (measured on this branch, onFinish wall time for a module of ~200 bytes): +;; N=22 -> 0.14 s N=26 -> 1.96 s N=29 -> 15.1 s for 165 lines of output. +;; Each added branch doubles it; N=40 extrapolates to hours. +;; +;; This is not the output-volume problem (see pathbug_path_explosion) and not recursion +;; depth -- it is unbounded WORK, and the guard written to prevent exactly this measures the +;; wrong quantity. +(module + (func $f (export "f") (param $x i32) + (if (local.get $x) (then + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (if (local.get $x) (then (nop))) + (loop $L (br $L)) + )) + ) + (func (export "main") (call $f (i32.const 0)))) diff --git a/test/monitors/pathbug_names_a.wasm b/test/monitors/pathbug_names_a.wasm new file mode 100644 index 0000000000000000000000000000000000000000..a2832443299bba957ff04495c0ee651f4399ad20 GIT binary patch literal 60 zcmZQbEY4+QU|?WmWlUgTtY>CoWMJoHWKGN|$Vg;h;9_HBVQ^&RW)NY?OUzAW6-&Kn5+yQy>II(yVsZ%fXr- Y9T_JzcFmE6EwftpRwW5P)2Ls;3zeG+IsgCw literal 0 HcmV?d00001 diff --git a/test/monitors/pathbug_names_b.wat b/test/monitors/pathbug_names_b.wat new file mode 100644 index 000000000..78bc92f06 --- /dev/null +++ b/test/monitors/pathbug_names_b.wat @@ -0,0 +1,5 @@ +;; Second module for pathbug_names_a.wat -- see that file for the defect description. +(module + (func $zeta (export "zeta") (result i32) (i32.const 2)) + (func (export "main") (drop (call $zeta))) +) diff --git a/test/monitors/pathbug_path_explosion.wasm b/test/monitors/pathbug_path_explosion.wasm new file mode 100644 index 0000000000000000000000000000000000000000..13ecf5e302d6838f0dc41d0750e1a0418b2850d0 GIT binary patch literal 300 zcmZQbEY4+QU|?Y6U`k-DXGmaRU}k1wU}WcEVoYOTV98C)%wu5WYGGpf#mJz*z~aEj R%E&#^Xl^zJM+N}~ZU8=W6#)PM literal 0 HcmV?d00001 diff --git a/test/monitors/pathbug_path_explosion.wat b/test/monitors/pathbug_path_explosion.wat new file mode 100644 index 000000000..defd7620a --- /dev/null +++ b/test/monitors/pathbug_path_explosion.wat @@ -0,0 +1,51 @@ +;; BUG (PathProfilingMonitor.onParse:47): counts is allocated with ONE SLOT PER STATIC PATH. +;; numPaths is exponential in the branch count, with no cap and no overflow check. +;; +;; 31 sequential if/else gives numPaths = 2^31, which wraps NEGATIVE in Array.new. +;; EXPECTED: the function is profiled, or the monitor declines to profile it. +;; ACTUAL: !LengthCheckException in PathProfilingMonitor.onParse() [@ 47:64] +;; -- the engine aborts at PARSE time, before the module ever runs. +;; +;; Neighbouring regimes, same root cause (vary the count below to reproduce): +;; N=34 numPaths wraps to a small positive -> allocation succeeds, then +;; !BoundsCheckException in execEdge() [@ 434:68] at RUN time. +;; N=26 succeeds but allocates a 2^26-int (268 MB) array and prints 67 million lines. +;; N=18 succeeds but prints 262,280 lines from a 190-byte module. +;; +;; 26-31 branches in one function is ordinary for compiled Wasm. enumPaths guards itself +;; with MAX_PATHS_TO_LIST=1024; this allocation and the onFinish print loop do not. +(module + (func $f (export "f") (param $x i32) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + (if (local.get $x) (then (nop)) (else (nop))) + ) + (func (export "main") (call $f (i32.const 0)))) diff --git a/test/monitors/pathbug_trap_counted.wasm b/test/monitors/pathbug_trap_counted.wasm new file mode 100644 index 0000000000000000000000000000000000000000..072d335639cabff96c0f9de42beeaa501e039b25 GIT binary patch literal 60 zcmV~$F%Ezr5JkcF7m|&vg@u)sN0FOAV`71g2jJ??(7z!-i#1Kn1}Pcx)pSE1{drN@ Ot#FbrqIx)08%O- CfgBuilder.gotoExit): a block ending in `unreachable` (or +;; throw / rethrow / throw_ref) is linked straight to EXIT, so an execution that TRAPS is recorded +;; as a completed path. +;; +;; setUnreachable now calls endBlock(this, block), which CfgBuilder binds to gotoExit; gotoExit +;; links any block with no outgoing edge to EXIT. For a trapping block that edge is a fiction -- +;; control never reaches EXIT from there. The block's EdgeProbe sits on the `unreachable` +;; instruction and fires just BEFORE the trap, so the count is committed anyway. +;; +;; $f(1) takes the `unreachable` arm and traps. It never returns. +;; EXPECTED: no completed path recorded; sum(counts) == 0 (no activation completed). +;; ACTUAL: counts[1] = 1 with path: 100 103 101 -- block 103 is the `unreachable` block and +;; it has an edge to EXIT (101). A trapped run is counted as a finished path. +;; +;; INVARIANT VIOLATED: sum(counts) == activations, in the "too many" direction. The same fiction +;; applies to throw / rethrow / throw_ref, which route to a handler rather than to EXIT. +(module + (func $f (export "f") (param $x i32) (result i32) + (if (local.get $x) (then (unreachable))) + (i32.const 7)) + (func (export "main") + (drop (call $f (i32.const 1)))) +) diff --git a/test/monitors/pathbug_try.wat b/test/monitors/pathbug_try.wat new file mode 100644 index 000000000..e9ff1cd70 --- /dev/null +++ b/test/monitors/pathbug_try.wat @@ -0,0 +1,34 @@ +;; BUG 2 (CfgBuilder.new): `ctl_stack.newBlock` is never assigned, so it keeps ControlStack's +;; default, which returns the zero value of B -- null for CfgBlock. +;; +;; ControlStack.visit_TRY and visit_TRY_TABLE both do +;; block = mergeBlock(this, block, newBlock(this)); +;; passing that null as the merge TARGET. mergeCfgBlock guards `from == null` but never `to == null`, +;; so it walks into the empty-block splice and dereferences `to.incoming` (CfgBuilder.v3:93). +;; +;; EXPECTED: the function is analyzed like any other. +;; ACTUAL: the engine aborts before the module runs -- +;; !NullCheckException +;; in CfgBuilder.mergeCfgBlock() [src/util/CfgBuilder.v3 @ 93:61] +;; in ControlStack.visit_TRY_TABLE() [src/util/ControlStack.v3 @ 104:35] +;; +;; Latent on master, where CfgBuilder is reached only through BytecodeProfilingMonitor's narrow +;; dispatch-source search. path-profile calls build() on every non-imported function of every +;; module, so any module containing `try` or `try_table` now kills the engine. +;; +;; NOTE: the wabt in this environment assembles neither legacy `try` (needs "legacy-eh") nor +;; `try_table`, so this .wat has no committed .wasm. Reproduce with modules already in the repo: +;; ./bin/wizeng.x86-64-linux --monitors=path-profile test/microbench/100ms/throw_catch0.wasm +;; (also throw_catch1.wasm, throw_catch2.wasm) +;; Once the toolchain supports it, the module below is the minimal case. +(module + (tag $t) + (func $f (export "f") (param i32) (result i32) + (block $h + (try_table (result i32) (catch_all $h) + (i32.const 7)) + (return)) + (i32.const 9)) + (func (export "main") + (drop (call $f (i32.const 0)))) +) diff --git a/test/monitors/test.sh b/test/monitors/test.sh index 002a942ae..c94397a47 100755 --- a/test/monitors/test.sh +++ b/test/monitors/test.sh @@ -33,6 +33,7 @@ else "profile_bytecode{switch_size=7,abstract_interpreter=true}" "alloc" "opcodes" + "path-profile" "ssevent") fi diff --git a/test/monitors/update-expected.sh b/test/monitors/update-expected.sh index 2ddfff1df..09ca6cd0a 100755 --- a/test/monitors/update-expected.sh +++ b/test/monitors/update-expected.sh @@ -26,6 +26,7 @@ else "globals" "memstats" "profile_bytecode{switch_size=16}" + "path-profile" "ssevent") fi From b6b21b94a2c23a3d470e2a9f0dfb6424305c69a6 Mon Sep 17 00:00:00 2001 From: Matthew Schneider Date: Thu, 1 Oct 2026 01:30:40 -0400 Subject: [PATCH 07/16] Add `--trace-paths` to profile paths in the engine --- src/WasmMode.v3 | 1 + src/engine/PathTracing.v3 | 54 +++++++++ src/engine/TraceOptions.v3 | 13 +++ src/util/BasicTracing.v3 | 8 +- .../x86-64-linux/X86_64LinuxExeTest.v3 | 107 ++++++++++++++++++ test/wizeng/trace_paths.wasm | Bin 0 -> 74 bytes test/wizeng/trace_paths.wasm.flags | 1 + test/wizeng/trace_paths.wasm.out | 5 + test/wizeng/trace_paths.wat | 10 ++ test/wizeng/trace_paths_br_on.wasm | Bin 0 -> 270 bytes test/wizeng/trace_paths_br_on.wasm.flags | 1 + test/wizeng/trace_paths_br_on.wasm.out | 14 +++ test/wizeng/trace_paths_br_on.wat | 42 +++++++ test/wizeng/trace_paths_recur.wasm | Bin 0 -> 76 bytes test/wizeng/trace_paths_recur.wasm.flags | 1 + test/wizeng/trace_paths_recur.wasm.out | 5 + test/wizeng/trace_paths_recur.wat | 12 ++ 17 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 src/engine/PathTracing.v3 create mode 100644 test/wizeng/trace_paths.wasm create mode 100644 test/wizeng/trace_paths.wasm.flags create mode 100644 test/wizeng/trace_paths.wasm.out create mode 100644 test/wizeng/trace_paths.wat create mode 100644 test/wizeng/trace_paths_br_on.wasm create mode 100644 test/wizeng/trace_paths_br_on.wasm.flags create mode 100644 test/wizeng/trace_paths_br_on.wasm.out create mode 100644 test/wizeng/trace_paths_br_on.wat create mode 100644 test/wizeng/trace_paths_recur.wasm create mode 100644 test/wizeng/trace_paths_recur.wasm.flags create mode 100644 test/wizeng/trace_paths_recur.wasm.out create mode 100644 test/wizeng/trace_paths_recur.wat diff --git a/src/WasmMode.v3 b/src/WasmMode.v3 index 049738783..ae106b84a 100644 --- a/src/WasmMode.v3 +++ b/src/WasmMode.v3 @@ -117,6 +117,7 @@ component WasmMode { var t = executeMainFunction(entry.0, entry.1); exit_code = t.1; for (monitor in monitors) monitor.onFinish(ins, t.0); + PathTracing.report(); // prints the --trace-paths counts, whatever the exit code } } return exit_code; diff --git a/src/engine/PathTracing.v3 b/src/engine/PathTracing.v3 new file mode 100644 index 000000000..f66db3ff7 --- /dev/null +++ b/src/engine/PathTracing.v3 @@ -0,0 +1,54 @@ +// Copyright 2026 Wizard authors. All rights reserved. +// See LICENSE for details of Apache 2.0 license. + +// Ball-Larus path profiling for the functions matching --trace-paths: each profiled function +// counts the acyclic paths its executions take, in any tier, and the counts print at exit. +component PathTracing { + private def traced = Vector.new(); + def instrument(module: Module, func: FuncDecl) { + if (func.imp != null) return; // an imported function has no body + traced.put(PathTrace.new(module, func)); + } + def report() { + for (i < traced.length) traced[i].report(Trace.OUT); + } +} + +// One profiled function and the executions of each of its paths. +class PathTrace(module: Module, func: FuncDecl) { + def profile = PathProfile.new(func); + var counts: Array; // null if the function is not profiled + + new() { + if (profile.reason != null) return; + counts = Array.new(profile.numPaths); + for (site in profile.sites) { + if (site.acts()) Instrumentation.insertLocalProbe(module, func.func_index, site.pc, PathProbe.new(site, counts)); + } + } + // Prints the nonzero path counts, each with the route its path number denotes. + def report(out: TraceBuilder) { + out.put1("path profile of %q", func.render(module.names, _)); + if (counts == null) { + out.put1(": not profiled, %s", profile.reason).ln(); + return; + } + out.put1(": %d paths", counts.length).ln(); + for (k < counts.length) { + if (counts[k] == 0) continue; + out.put2(" counts[%d] = %d\tpath:", k, counts[k]); + profile.renderPath(k, out); + out.ln(); + } + } +} + +// Runs the action of whichever edge its site's decision is about to take. +class PathProbe(site: PathSite, counts: Array) extends Probe { + def fire(loc: DynamicLoc) -> ProbeAction { + def accessor = loc.frame.getFrameAccessor(); + def path = site.apply(accessor, site.selectArm(accessor)); + if (path >= 0) counts[path]++; + return ProbeAction.Continue; + } +} diff --git a/src/engine/TraceOptions.v3 b/src/engine/TraceOptions.v3 index 723922e55..f23b42549 100644 --- a/src/engine/TraceOptions.v3 +++ b/src/engine/TraceOptions.v3 @@ -60,6 +60,15 @@ component TraceOptions { BasicTracing.callsFilter = DeclFilters.parseString(pat); return true; } + var tracePaths = "--trace-paths"; + if (Strings.startsWith(arg, tracePaths)) { + var pat: string; + if (arg.length >= (tracePaths.length + 1) && arg[tracePaths.length] == '=') { + pat = Arrays.range(arg, tracePaths.length + 1, arg.length); + } + BasicTracing.pathsFilter = DeclFilters.parseString(pat); + return true; + } } else if (Strings.startsWith(arg, "--trace=")) { var p = TextReader.new("