diff --git a/src/monitors/PathProfilingMonitor.v3 b/src/monitors/PathProfilingMonitor.v3 new file mode 100644 index 000000000..dfba8a39d --- /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 { + // counts for each path + def entries = Vector.new(); + + def onParse(m: Module, err: ErrorGen) { + def bi = BytecodeIterator.new(); + for (i < m.functions.length) { + def func: FuncDecl = m.functions[i]; + if (func.imp != null) continue; // skip imported functions + def profile = PathProfile.new(func); + if (profile.success) { + // define entry probe for the function to get index for the per-call site path counts + def entry = PathCountsProbe.new(m, func, profile); + Instrumentation.insertFuncEntryProbe(m, func.func_index, entry); + entries.put(entry); + + // place Ball-Larus instrumentation + def pathRegVar = PathInstrumentation.newFrameVar(func); + bi.reset(func); + profile.entry.dfs(fun (b: PathProfilingBlock) { + if (!b.instrumented()) return; + def probe = PathMonitorProbe.new(bi, b, pathRegVar, entry); + // XXX can be combiend with the probe above? this is only for one probe on `ENTRY -> head` edge + if (b.last_instr_pc < 0) Instrumentation.insertFuncEntryProbe(m, func.func_index, probe); + else Instrumentation.insertLocalProbe(m, func.func_index, b.last_instr_pc, probe); + }, null, null); + } + } + } + def onFinish(i: Instance, r: Result) { + for (j < entries.length) { + def entry = entries[j]; + def profile = entry.profile; + out.put1("func %q", entry.func.render(entry.module.names, _)).ln(); + printCfg(out, profile); + // each call site lists only the paths its calls took + for (s < entry.sites.length) { + def site = entry.sites[s]; + def counts = entry.counts[s]; + // a call site no call counted under + var hit = false; + for (c in counts) if (c > 0) hit = true; + if (!hit) continue; + if (site.0 == null) out.puts(" caller engine"); + else out.put2(" caller %q +%d", site.0.render(entry.module.names, _), site.1); + out.ln(); + for (k < profile.numPaths) { + 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 < profile.numPaths) { + out.put2(" counts[%d] = %d", k, entry.total(k)).ln(); + out.puts(" path:"); + profile.renderPath(k, out); + out.ln(); + } + // (Section 4.1) a self-loop is counted, not path-profiled + for (i < profile.selfLoops.length) { + def block = profile.selfLoops[i]; + out.put3(" selfloop[block %d +%d] = %d", block.id, block.last_instr_pc, entry.total(profile.numPaths + i)).ln(); + } + } + } +} + + +private class PathCountsProbe(module: Module, func: FuncDecl, profile: PathProfile) extends Probe { + def numPaths = profile.numPaths + profile.selfLoops.length; + def callSiteVar = PathInstrumentation.newFrameVar(func); // holds the activation's call site index + + // (func, pc) of the call sites TO this func, added as their first call enters + def sites = Vector<(FuncDecl, int)>.new(); // list of callers + def counts = Vector>.new(); // corresponding list of path counts + + new() { + // add misc calls: the engine and host functions, which have no wasm frame + addSite((null, -1)); + } + + def fire(loc: DynamicLoc) -> ProbeAction { + def accessor = loc.frame.getFrameAccessor(); + def site = siteOf(accessor.caller()); + accessor.getWriter().setFrameVar(callSiteVar, Values.box_i(site)); + return ProbeAction.Continue; + } + // Returns the index of the call site that {caller} describes, adding it if new. + private def siteOf(caller: FrameLoc) -> int { + match (caller) { + Wasm(func, pc, frame) => { + for (i < sites.length) if (sites[i] == (func.decl, pc)) return i; // XXX: linear search + return addSite((func.decl, pc)); + } + _ => return 0; + } + } + // Adds {site} and returns its index. + private def addSite(site: (FuncDecl, int)) -> int { + sites.put(site); + counts.put(Array.new(numPaths)); + return sites.length - 1; + } + // Returns the count of {path} summed over every call site. + def total(path: int) -> int { + var sum = 0; + for (i < counts.length) sum += counts[i][path]; + return sum; + } +} +// The monitor's probe, which counts each completed path under the activation's call site. +private class PathMonitorProbe extends PathSiteProbe { + def counts: PathCountsProbe; + new(bi: BytecodeIterator, block: PathProfilingBlock, pathRegVar: int, counts) super(bi, block, pathRegVar) { } + def incCount(accessor: FrameAccessor, path: int) { + counts.counts[Values.unbox_i(accessor.getFrameVar(counts.callSiteVar))][path]++; + } +} + +// Prints each block of the CFG and its outgoing edges, with their Val, increment and kind. +def printCfg(out: TraceBuilder, p: PathProfile) { + p.entry.dfs(printBlock(out, _), null, null); +} +def printBlock(out: TraceBuilder, b: PathProfilingBlock) { + // 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(); + b.outgoing.apply(fun (e: PathProfilingEdge) { + // 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"); + // a backedge is left out of the acyclic graph the paths are numbered in (Section 4) + if (e.isBackedge) out.puts(" BACKEDGE"); + out.ln(); + }); +} diff --git a/src/util/CfgBuilder.v3 b/src/util/CfgBuilder.v3 index cb4a2de8b..ce0e79f3b 100644 --- a/src/util/CfgBuilder.v3 +++ b/src/util/CfgBuilder.v3 @@ -5,20 +5,24 @@ class CfgBuilder { private def bi = BytecodeIterator.new(); private def ctl_stack = ControlStack.new(); + private var prev_pc: int = -1; // for recording `last_instr_pc` when ending a block new() { ctl_stack.newLabel = newCfgBlock; ctl_stack.splitBlock = splitCfgBlock; ctl_stack.mergeBlock = mergeCfgBlock; ctl_stack.bindLabel = bindCfgBlock; + ctl_stack.endBlock = endCfgBlock; } def build(func: FuncDecl) -> CfgBlock { - var start = ctl_stack.reset(); bi.reset(func); + bi.dispatchLocalDecls(ctl_stack); // advance bi.pc past locals + 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(); } return start; @@ -55,6 +59,20 @@ class CfgBuilder { 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; @@ -66,7 +84,10 @@ 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; + var end: int = -1; + var last_instr_pc: int = -1; // last instruction this block owns, or < 0 when it owns none var outgoing: List; var incoming: List; var mark: int; diff --git a/src/util/ControlStack.v3 b/src/util/ControlStack.v3 index 033c01bc1..fa3258c81 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; @@ -28,10 +30,13 @@ 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); + bindLabel(this, new_pc, block); push(Opcode.UNREACHABLE); return block; } @@ -56,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); } @@ -63,6 +69,7 @@ class ControlStack extends BytecodeVisitor { var from = stack.peek(); var t = splitBlock(this, block); var ctl = push(Opcode.IF); + endBlock(this, block, -1); block = t.0; bindLabel(this, -1, block); ctl.else_block = t.1; @@ -73,6 +80,7 @@ class ControlStack extends BytecodeVisitor { ctl.start_pos = pc; ctl.start_opcode = Opcode.ELSE; ctl.reachable = true; + endBlock(this, block, -1); block = ctl.else_block; bindLabel(this, -1, block); var d: B; @@ -90,12 +98,14 @@ class ControlStack extends BytecodeVisitor { var ctl = stack.peek(); ctl.start_pos = pc; ctl.start_opcode = Opcode.CATCH; + endBlock(this, block, -1); 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); } def visit_THROW(tag_code: u31) { @@ -180,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) { @@ -202,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) { @@ -209,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. @@ -232,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. diff --git a/src/util/PathInstrumentation.v3 b/src/util/PathInstrumentation.v3 new file mode 100644 index 000000000..9e46bf530 --- /dev/null +++ b/src/util/PathInstrumentation.v3 @@ -0,0 +1,106 @@ +// Copyright 2026 Wizard authors. All rights reserved. +// See LICENSE for details of Apache 2.0 license. + +// How a probe's branch selects its outgoing edge. +enum PathProbeKind { DIRECT, IF, TABLE, NULL, NONNULL, CAST, CAST_FAIL } + +component PathInstrumentation { + // Appends an i32 frame variable to {func} and returns its index. + def newFrameVar(func: FuncDecl) -> int { + def index = 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)); + return index; + } +} + +// The probe at one block's last instruction: applies the instrumentation of the edge its branch +// is about to take, and passes any path it completes to {count}; {r} lives in the frame variable +// {pathRegVar}. +class PathSiteProbe extends Probe { + def pathRegVar: int; // index into the frame variables to hold the register + + def var kind = PathProbeKind.DIRECT; // how the block's last instruction selects an outgoing edge + def var imm: BrOnCastImm; + def var edges: Array; // the outgoing edges from this block + + // bi must be set to this function + new(bi: BytecodeIterator, block: PathProfilingBlock, pathRegVar) { + // skip dummy edges which don't get instrumentation + // addDummies appends them last, so the real edges are a prefix + edges = Ranges.dup(block.outgoing.send(Ranges.skipTrailing(_, PathProfilingEdge.isDummy))); + + if (edges.length > 1) { + bi.at(block.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; + } + IF, BR_IF => kind = PathProbeKind.IF; + _ => ; // no other instruction gives a block two or more edges + } + } + if (kind == PathProbeKind.TABLE) Ranges.reverse(edges); // XXX br_table places its edges backwards + } + def fire(loc: DynamicLoc) -> ProbeAction { + def accessor = loc.frame.getFrameAccessor(); + def edge = selectEdge(accessor); // edge to be taken + def path = traverseEdge(accessor, edge); + // path >= 0 means we have reached the stage at the end of the function where we can record the final count + if (path >= 0) + incCount(accessor, path); + return ProbeAction.Continue; + } + // Records one execution of {path}. Abstract depending on the implementation (monitor or trace) + def incCount(accessor: FrameAccessor, path: int); + + // Returns the edge the probed instruction is about to take, read from its operand. + private def selectEdge(accessor: FrameAccessor) -> PathProfilingEdge { + // a direct probe has only one outgoing edge + if (kind == PathProbeKind.DIRECT) return edges[0]; + + def v = accessor.getOperand(0); + match (kind) { + TABLE => { + def u = Values.unbox_u(v); + return edges[if(u >= u32.!(edges.length), edges.length - 1, int.!(u))]; + } + NULL => return edges[if(Values.isNull(v), 1, 0)]; + NONNULL => return edges[if(Values.isNull(v), 0, 1)]; + CAST => return edges[if(Runtime.cast(accessor.func().instance, imm.null2(), imm.ht2, v), 1, 0)]; + CAST_FAIL => return edges[if(Runtime.cast(accessor.func().instance, imm.null2(), imm.ht2, v), 0, 1)]; + IF => return edges[if(Values.unbox_u(v) != 0, 1, 0)]; + _ => return null; // cannot happen, DIRECT returns early above + } + } + // Applies the instrumentation of {edge} to {r}; returns the path it completes, or -1 if none. + private def traverseEdge(accessor: FrameAccessor, edge: PathProfilingEdge) -> int { + match (edge.instrumentation) { + None => ; + InitReg(value) => setR(accessor, value); + AddReg(value) => setR(accessor, getR(accessor) + value); + IncCount(index) => return index; + IncCountAtReg(offset) => return getR(accessor) + offset; + IncCountAndInitReg(offset, init) => { + def path = getR(accessor) + offset; + setR(accessor, init); + return path; + } + } + return -1; // no count + } + private def getR(accessor: FrameAccessor) -> int { + return Values.unbox_i(accessor.getFrameVar(pathRegVar)); + } + private def setR(accessor: FrameAccessor, v: int) { + accessor.getWriter().setFrameVar(pathRegVar, Values.box_i(v)); + } +} diff --git a/src/util/PathProfile.v3 b/src/util/PathProfile.v3 new file mode 100644 index 000000000..1ebaacace --- /dev/null +++ b/src/util/PathProfile.v3 @@ -0,0 +1,455 @@ +// Copyright 2026 Wizard authors. All rights reserved. +// See LICENSE for details of Apache 2.0 license. + +// Computes the Ball-Larus path numbering of one function and the instrumentation each edge applies to the +// path register {r} and the counts, for the path-profile monitor. +// Section and figure numbers refer to "Efficient Path Profiling" (Ball and Larus, MICRO 1996). +def MAX_PATHS = 1 << 16; // more paths than this are not worth a counter each +component PathProfiling { + def PROFILE_BACKEDGES = false; // true profiles functions with loops (Section 4); false rejects them +} +class PathProfile(func: FuncDecl) { + def var success: bool = true;// false if the function can be profiled + def var numPaths: int; // the paths are numbered 0 .. numPaths - 1 + def var entry: PathProfilingBlock; + def var exit: PathProfilingBlock; + def var selfLoops: Array; // blocks that branch to themselves; block i counts at numPaths + i + private def tree = Vector.new(); // the spanning tree's edges + private var base: int; // the id of the CFG's first block; {CfgBuilder} numbers the rest consecutively + private def bi = BytecodeIterator.new().reset(func); + private var hasBackedge: bool; // set by convert, to reject the function when !PROFILE_BACKEDGES + + new() { + // reject functions that use exception handling and stack switching instructions + while (bi.more()) { + match (bi.current()) { + TRY, TRY_TABLE => { + if (Trace.compiler) Trace.OUT.put1("path profile: func #%d not profiled, has exception handlers", func.func_index).ln(); + success = false; + return; + } + CONT_NEW, CONT_BIND, SUSPEND, RESUME, RESUME_THROW, RESUME_THROW_REF, SWITCH => { + if (Trace.compiler) Trace.OUT.put1("path profile: func #%d not profiled, uses stack switching", func.func_index).ln(); + success = false; + return; + } + _ => ; + } + bi.next(); + } + + // build cfg for the function, then convert its representation to path profiling blocks/edges + // that we can attach the Ball-Larus information to + def cfg = CfgBuilder.new().build(func); + // ENTRY and EXIT are vertices of their own, which no instruction belongs to + entry = PathProfilingBlock.new(CfgBlock.new(-1)); + + // nodes = set of converted nodes (for caching in the conversion process) + base = cfg.id; + def nodes = Array.new(entry.id - base); + def body = convert(cfg, nodes); + if (!PathProfiling.PROFILE_BACKEDGES && hasBackedge) { + if (Trace.compiler) Trace.OUT.put1("path profile: func #%d not profiled, has backedges", func.func_index).ln(); + success = false; + return; + } + link(entry, body); + + exit = PathProfilingBlock.new(CfgBlock.new(-1)); + // every path must end at EXIT, so route all `return`s to EXIT + addReturnEdges(); + + // (Section 4) general CFG to DAG, by dummy edges around each backedge + if (PathProfiling.PROFILE_BACKEDGES) addDummies(); + + // (Section 3.2) assign values to edges to make sums across all paths unique + + // Figure 5. Algorithm for assigning values to edges in a Dag. + if (!assignVals()) { + // return if invalid CFG for profiling: too many paths or no path exits (i.e. all trap) + success = false; + return; + } + numPaths = entry.numPaths; + + // (Section 3.3) use spanning trees to select edges to instrument and increment + + // any spanning tree of the CFG, since there are no edge weights to maximize + def chords = spanningTree(); + // Determine Increment(e) for each chord in the spanning tree (Ball Fig. 4) + computeIncrements(chords); + + // (Section 3.4) Figure 8. label where the instrumentation should go on the edges + assignInstrumentation(chords); + } + // Converts {b} and every block it reaches, recording each node in {nodes} so that a block is + // converted once. An empty block is not converted: it stands for the block it leads to. + private def convert(cfg_block: CfgBlock, nodes: Array) -> PathProfilingBlock { + // skip empty blocks + while (cfg_block.last_instr_pc < 0) + cfg_block = cfg_block.outgoing.head.target; + // check if block to convert has already been done (cached) + def index = cfg_block.id - base; + if (nodes[index] != null) return nodes[index]; + + // actually convert the block now + def block = PathProfilingBlock.new(cfg_block); + nodes[index] = block; + + // convert CFG edges to path profiling edges + for (l = cfg_block.outgoing; l != null; l = l.tail) { + def cfg_edge = l.head; + def edge = link(block, convert(cfg_edge.target, nodes)); + if (PathProfiling.PROFILE_BACKEDGES) edge.isBackedge = cfg_edge.isBackedge; + else if (cfg_edge.isBackedge) hasBackedge = true; + } + return block; + } + // every path must end at EXIT, so route all `return`s to EXIT + private def addReturnEdges() { + entry.dfs(fun (b: PathProfilingBlock) { + // filter out blocks which are not the end of the function + if (b.outgoing.length > 0 || b.last_instr_pc < 0) return; + + bi.at(b.last_instr_pc, -1); + match (bi.current()) { + END, RETURN, RETURN_CALL, RETURN_CALL_INDIRECT, RETURN_CALL_REF => link(b, exit); + _ => ; // avoid linking UNREACHABLE, THROW, or THROW_REF + } + }, null, null); + } + // appends, so {outgoing} keeps the order of {CfgBlock.outgoing}, which is reverse branch-label order + private def link(from: PathProfilingBlock, to: PathProfilingBlock) -> PathProfilingEdge { + def e = PathProfilingEdge.new(from, to); + from.outgoing.put(e); + to.incoming.put(e); + return e; + } + + // Section 4 step 1, a dummy ENTRY->v per backedge target and a dummy w->EXIT per backedge source + private def addDummies() { + entry.dfs(fun (b: PathProfilingBlock) { + // target of a backedge, excluding a self-loop + for (i < b.incoming.length) { + def e = b.incoming[i]; + if (e.isBackedge && e.source != b) { + link(entry, b).isDummy = true; + break; + } + } + // source of a backedge, excluding a self-loop + for (i < b.outgoing.length) { + def e = b.outgoing[i]; + if (e.isBackedge && e.target != b) { + link(b, exit).isDummy = true; + break; + } + } + }, null, null); + } + // Val(e) / NumPaths(v) assignment (Ball-Larus). Algorithm for assigning values to edges in a DAG, Fig. 5 + // Assigning integer values to edges such that no two paths compute the same path sum: Section 3.2 + private def assignVals() -> bool { + // get reverse topological order of the DAG + def order = entry.reverseTopo(); + + // if v is a leaf vertex: NumPaths(v) = 1. EXIT is the only leaf; a block ending in a trap has + // no successors either, but no path through it completes, so it keeps 0 + exit.numPaths = 1; + // for each vertex v in reverse topological order + for (i < order.length) { + def v = order[i]; + // for each edge e = v -> w + for (j < v.outgoing.length) { + def e = v.outgoing[j]; + // SKIP (but do not remove) backedges (Section 4) + if (e.isBackedge) continue; + + // br_table may have multiple branches to the same offset, thus we have two + // identical edges (i.e. two edges e1 = v1 -> w1 and e2 = v2 -> w2 where + // v1 = v2 and w1 = w2). Here, we don't combine the edges, as that makes + // assigning probes based on `br_table`'s actual offset harder to do later. + // Instead, we make sure that the Value assignment to each edge is idential + // when the edge is the same + + // check for duplicate edges from the previously iterated edges which have been previously assigned + var val = -1; + for (k < j) { + def twin = v.outgoing[k]; + if (!twin.isDummy && twin.target == e.target) { + val = twin.val; + break; + } + } + if (val >= 0) { + // if edge already assigned value use it and move on + e.val = val; + continue; + } + + // assign new value to edge + e.val = v.numPaths; + v.numPaths += e.target.numPaths; + } + if (v.numPaths > MAX_PATHS) { + if (Trace.compiler) Trace.OUT.put1("path profile: func #%d not profiled, more paths than the counter limit", func.func_index).ln(); + return false; + } + } + if (entry.numPaths == 0) { + if (Trace.compiler) Trace.OUT.put1("path profile: func #%d not profiled, no acyclic path reaches the exit", func.func_index).ln(); + return false; + } + return true; + } + // (Section 3.3) a spanning tree of the CFG, seeded with EXIT->ENTRY; the chords left over are the + // edges to instrument. With no edge weights any spanning tree will do, so a DFS from ENTRY builds one. + private def spanningTree() -> Vector { + def chords = Vector.new(); + // Seed T with EXIT->ENTRY so it never becomes a chord. Detached from both edge lists, since it + // belongs to the spanning tree rather than the CFG. + tree.put(PathProfilingEdge.new(exit, entry)); + // EXIT is reached through the seed, so no other edge joins it to the tree + exit.mark = 1; + entry.dfs(null, fun (e: PathProfilingEdge) { + // skip backedges to convert CFG to DAG (Section 4) + if (e.isBackedge) return; + // an edge into a block the tree has not reached yet joins the tree; the DFS enters it next + if (e.target.mark == 0) { + tree.put(e); + } else { + e.isChord = true; + chords.put(e); + } + }, null); + return chords; + } + // Algorithm for determining Increment(e) for each chord e using a DFS of spanning tree T of + // graph G, Fig. 4 (increment loop, DFS, and Dir below are all part of this same algorithm). + private def computeIncrements(chords: Vector) { + // for each e in E - T do Increment(e) := 0, which is a new edge's default + // DFS(0, ENTRY, NULL) + walk(0, entry, null, chords); + // for each e in E - T: + // Increment(e) := Increment(e) + Events(e) + for (i < chords.length) chords[i].increment += chords[i].val; + } + // Walks the spanning tree from {v}, reached along {e} with {events}, adding Dir(e, f) * events + // to the increment of each chord f at {v}. + // DFS procedure, Fig. 4 + private def walk(events: int, v: PathProfilingBlock, e: PathProfilingEdge, chords: Vector) { + // for each f in T: + for (i < tree.length) { + def f = tree[i]; + // f != e and v = tgt(f) + // DFS(Dir(e,f)*events + Events(f), src(f), f) + if (f != e && v == f.target) walk(dir(e, f) * events + f.val, f.source, f, chords); + } + // for each f in T: + for (i < tree.length) { + def f = tree[i]; + // f != e and v = src(f) + // DFS(Dir(e,f)*events + Events(f), tgt(f), f) + if (f != e && v == f.source) walk(dir(e, f) * events + f.val, f.target, f, chords); + } + // for each f in E - T: + for (i < chords.length) { + def f = chords[i]; + // v = src(f) or v = tgt(f) do Increment(f) := Increment(f) + Dir(e,f)*events + if (v == f.source || v == f.target) f.increment += dir(e, f) * events; + } + } + // Dir function, Fig. 4: 1 if e and f point the same direction (or e is NULL), else -1 + private def dir(e: PathProfilingEdge, f: PathProfilingEdge) -> int { + // precondition: e and f share a common endpoint + if (e == null) return 1; + if (e.source == f.target || e.target == f.source) return 1; // e and f in same direction + return -1; // e and f in opposite directions + } + // Sets each edge's instrumentation: the register initializations, count increments and register + // increments (Figure 8), then each backedge's count and restart or self-loop count (Section 4). + private def assignInstrumentation(chords: Vector) { + def ws = Vector.new(); + + // Fig. 8, Pass 1: register initialization code + ws.put(entry); + Vectors.drain(ws, 0, fun (v: PathProfilingBlock) { + // for each edge e = v -> w + for (i < v.outgoing.length) { + def e = v.outgoing[i]; + // skip backedges to convert CFG to DAG (Section 4) + if (e.isBackedge || e.isDummy) continue; + // if e is a chord edge + // instrument(e, 'r=Inc(e)') + if (e.isChord) e.instrumentation = PathProfilingInstrumentation.InitReg(e.increment); + // else if e is the only incoming edge of w + else if (e.target.incoming.length == 1) ws.put(e.target); + // instrument(e, 'r=0') + else e.instrumentation = PathProfilingInstrumentation.InitReg(0); + } + }); + + // Fig. 8, Pass 2: memory increment code + ws.clear(); + ws.put(exit); + Vectors.drain(ws, 0, fun (w: PathProfilingBlock) { + // for each edge e = v -> w + for (i < w.incoming.length) { + def e = w.incoming[i]; + // skip backedges to convert CFG to DAG (Section 4) + if (e.isBackedge || e.isDummy) continue; + // if e is a chord edge + if (e.isChord) { + // if e's instrumentation is 'r=Inc(e)' + if (PathProfilingInstrumentation.InitReg.?(e.instrumentation)) { + // instrument(e, 'count[Inc(e)]++') + e.instrumentation = PathProfilingInstrumentation.IncCount(e.increment); // this is an optimization + // instrument(e, 'count[r+Inc(e)]++') + } else { + e.instrumentation = PathProfilingInstrumentation.IncCountAtReg(e.increment); + } + // else if e is the only outgoing edge of v + } else if (e.source.outgoing.length == 1) { + ws.put(e.source); + // else + } else { + // instrument(e, 'count[r]++') + e.instrumentation = PathProfilingInstrumentation.IncCountAtReg(0); + } + } + }); + + // Fig. 8, Pass 3: register increment code + // for all _____ chords c + for (i < chords.length) { + def e = chords[i]; + // ^ uninstrumented chords + if (!e.isDummy && PathProfilingInstrumentation.None.?(e.instrumentation)) + // instrument(e, 'r+=Inc(e)') + e.instrumentation = PathProfilingInstrumentation.AddReg(e.increment); + } + + // (Section 4) instrument each backedge with [count[r]++; r=0], combining in the increments of its + // two dummy edges. (Section 4.1) a self-loop instead counts its iterations, past the path counts. + def loops = Vector.new(); + if (PathProfiling.PROFILE_BACKEDGES) { + entry.dfs(fun (b: PathProfilingBlock) { + for (i < b.outgoing.length) { + def e = b.outgoing[i]; + if (!e.isBackedge) continue; + if (e.source == e.target) { + // (Section 4.1) a self-loop repeats a vertex without changing the path, so it + // counts its trips in a slot of its own, past the path counts + if (loops.length == 0 || loops[loops.length - 1] != b) loops.put(b); + e.instrumentation = PathProfilingInstrumentation.IncCount(numPaths + loops.length - 1); + continue; + } + // the loop bottom's ->EXIT dummy increments the counter, the loop head's ENTRY-> dummy + // reinitializes {r}; a dummy in the spanning tree has increment 0 + // addDummies linked the dummies last, so each is last in its vector + def ex = e.source.outgoing[e.source.outgoing.length - 1]; // e.source->EXIT + def en = e.target.incoming[e.target.incoming.length - 1]; // ENTRY->e.target + e.instrumentation = PathProfilingInstrumentation.IncCountAndInitReg(ex.increment, en.increment); + } + }, null, null); + } + selfLoops = loops.extract(); + } + // Prints the route of path {k} as the start pc of each block, with ".." where a dummy edge + // enters or leaves a loop (Section 3.2). + def renderPath(k: int, out: TraceBuilder) { + var v = entry; + var dummy = false; + while (v != exit) { + var pick: PathProfilingEdge; + for (i < v.outgoing.length) { + def e = v.outgoing[i]; + if (!e.isBackedge && e.val <= k && k < e.val + e.target.numPaths) { + pick = e; + break; + } + } + if (v == entry) out.puts(" ENTRY"); + else out.put1(" +%d", v.bind_pos); + if (pick.isDummy) out.puts(" .."); + dummy = pick.isDummy; + k -= pick.val; + v = pick.target; + } + if (!dummy) out.puts(" EXIT"); + } +} + +// runtime instrumentation an edge can carry, executed against the per-activation register {r} and the +// path-count histogram {counts} of the activation's call site +type PathProfilingInstrumentation { + case None; // no instrumentation placed on this edge (the default) + case InitReg(value: int); // r = value + case AddReg(value: int); // r += value + case IncCount(index: int); // counts[index]++ (optimization) + case IncCountAtReg(offset: int); // counts[r + offset]++ + case IncCountAndInitReg(offset: int, init: int); // counts[r + offset]++ ; r = init (needed for backedges) +} + +// the CFG nodes this monitor analyses, carrying its own Ball-Larus state +// Wraps one block of the function's CFG, keeping its id and bytecode range [bind_pos ... end). +class PathProfilingBlock(cfg_block: CfgBlock) { + // from CfgBlock + def id = cfg_block.id; + def bind_pos = cfg_block.bind_pos; + def end = cfg_block.end; + def last_instr_pc = cfg_block.last_instr_pc; + + def outgoing = Vector.new(); + def incoming = Vector.new(); + private var mark: int; + + def var numPaths: int; // NumPaths(v): number of paths from this vertex to EXIT + + // Returns true if any outgoing edge changes {r} or the counts. + def instrumented() -> bool { + for (i < outgoing.length) { + if (!PathProfilingInstrumentation.None.?(outgoing[i].instrumentation)) return true; + } + return false; + } + // Returns the reachable blocks in reverse topological order of the DAG, skipping backedges. + def reverseTopo() -> Array { + def order = Vector.new(); + dfs0(null, null, order.put); + clearMarks(); + return order.extract(); + } + def dfs(pre: PathProfilingBlock -> R, onEdge: PathProfilingEdge -> R, post: PathProfilingBlock -> R) { + dfs0(pre, onEdge, post); + clearMarks(); + } + private def dfs0(pre: PathProfilingBlock -> R, onEdge: PathProfilingEdge -> R, post: PathProfilingBlock -> R) { + if (mark != 0) return; + mark = 1; + + if (pre != null) pre(this); + outgoing.apply(fun (e: PathProfilingEdge) { + def child = e.target; + + if (onEdge != null) onEdge(e); + // do not follow backedges + if (!e.isBackedge) child.dfs0(pre, onEdge, post); + }); + if (post != null) post(this); + } + private def clearMarks() { + if (mark == 0) return; + mark = 0; + for (i < outgoing.length) outgoing[i].target.clearMarks(); + } +} +class PathProfilingEdge(source: PathProfilingBlock, target: PathProfilingBlock) { + var isBackedge: bool; // branches to an enclosing loop label + var val: int; // Val(e), the weight assigned by Fig. 5; also Events(e) in Fig. 4 + var isDummy: bool; // added by Section 4 step 1; carries a Val but no instruction + var increment: int; // Increment(e), for chords only (Section 3.3) + var isChord: bool; // in E - T rather than T, assigned by spanningTree + // instrumentation for this edge + var instrumentation: PathProfilingInstrumentation; +} 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..ce28f0ba8 --- /dev/null +++ b/test/monitors/expected/branch_monitor.wasm.path-profile.out @@ -0,0 +1,12 @@ +func "main" + block 102 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 3 + -> block 101 val=0 + block 101 +3 ... 4 + -> block 103 val=0 inc=0 + block 103 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +3 EXIT 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..9305c53b7 --- /dev/null +++ b/test/monitors/expected/branch_monitor_gc.wasm.path-profile.out @@ -0,0 +1,135 @@ +func #0 + block 102 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 3 + -> block 101 val=0 + block 101 +3 ... 4 + -> block 103 val=0 inc=0 + block 103 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +3 EXIT +func #1 + block 106 +-1 ... -1 + -> block 104 val=0 + block 104 +1 ... 57 + -> block 105 val=0 + block 105 +57 ... 58 + -> block 107 val=0 inc=0 + block 107 +-1 ... -1 + caller #0 +1 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +57 EXIT +func #2 + block 113 +-1 ... -1 + -> block 108 val=0 + block 108 +1 ... 5 + -> block 112 val=0 + -> block 112 val=0 inc=0 + block 112 +5 ... 6 + -> block 109 val=0 + block 109 +6 ... 7 + -> block 114 val=0 inc=0 + block 114 +-1 ... -1 + caller #1 +3 + counts[0] = 1 + caller #1 +7 + counts[0] = 1 + caller #1 +11 + counts[0] = 1 + counts[0] = 3 + path: ENTRY +1 +5 +6 EXIT +func #3 + block 118 +-1 ... -1 + -> block 115 val=0 + block 115 +1 ... 5 + -> block 116 val=0 + -> block 116 val=0 inc=0 + block 116 +5 ... 6 + -> block 119 val=0 inc=0 + block 119 +-1 ... -1 + caller #1 +15 + counts[0] = 1 + caller #1 +19 + counts[0] = 1 + caller #1 +23 + counts[0] = 1 + counts[0] = 3 + path: ENTRY +1 +5 EXIT +func #4 + block 126 +-1 ... -1 + -> block 120 val=0 + block 120 +1 ... 17 + -> block 125 val=0 + -> block 124 val=1 inc=1 + -> block 123 val=2 inc=2 + -> block 122 val=3 inc=3 + block 125 +17 ... 18 + -> block 124 val=0 + block 124 +18 ... 19 + -> block 123 val=0 + block 123 +19 ... 20 + -> block 122 val=0 + block 122 +20 ... 21 + -> block 121 val=0 + block 121 +21 ... 22 + -> block 127 val=0 inc=0 + block 127 +-1 ... -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: ENTRY +1 +17 +18 +19 +20 +21 EXIT + counts[1] = 2 + path: ENTRY +1 +18 +19 +20 +21 EXIT + counts[2] = 1 + path: ENTRY +1 +19 +20 +21 EXIT + counts[3] = 1 + path: ENTRY +1 +20 +21 EXIT +func #5 + block 131 +-1 ... -1 + -> block 128 val=0 + block 128 +1 ... 5 + -> block 130 val=0 + -> block 129 val=1 inc=1 + block 130 +5 ... 6 + -> block 129 val=0 + block 129 +6 ... 7 + -> block 132 val=0 inc=0 + block 132 +-1 ... -1 + caller #1 +51 + counts[1] = 1 + counts[0] = 0 + path: ENTRY +1 +5 +6 EXIT + counts[1] = 1 + path: ENTRY +1 +6 EXIT +func #6 + block 137 +-1 ... -1 + -> block 133 val=0 + block 133 +1 ... 7 + -> block 136 val=0 + -> block 135 val=1 + block 136 +7 ... 9 + -> block 134 val=0 + block 134 +11 ... 12 + -> block 138 val=0 inc=0 + block 138 +-1 ... -1 + block 135 +9 ... 11 + -> block 134 val=0 inc=1 + caller #1 +55 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +7 +11 EXIT + counts[1] = 0 + path: ENTRY +1 +9 +11 EXIT 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..5cd8ae2ab --- /dev/null +++ b/test/monitors/expected/call_monitor1.wasm.path-profile.out @@ -0,0 +1,80 @@ +func #1 + block 111 +-1 ... -1 + -> block 109 val=0 + block 109 +1 ... 5 + -> block 110 val=0 + block 110 +5 ... 6 + -> block 112 val=0 inc=0 + block 112 +-1 ... -1 + caller #0 +9 + counts[0] = 3 + counts[0] = 3 + path: ENTRY +1 +5 EXIT +func #2 + block 115 +-1 ... -1 + -> block 113 val=0 + block 113 +1 ... 3 + -> block 114 val=0 + block 114 +3 ... 4 + -> block 116 val=0 inc=0 + block 116 +-1 ... -1 + 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: ENTRY +1 +3 EXIT +func #3 + block 119 +-1 ... -1 + -> block 118 val=0 + block 118 +1 ... 2 + -> block 120 val=0 inc=0 + block 120 +-1 ... -1 + caller #1 +3 + counts[0] = 3 + caller #2 +1 + counts[0] = 8 + caller #0 +13 + counts[0] = 3 + caller #0 +17 + counts[0] = 3 + caller #0 +38 + counts[0] = 2 + caller #0 +43 + counts[0] = 2 + counts[0] = 21 + path: ENTRY +1 EXIT +func #4 + block 123 +-1 ... -1 + -> block 121 val=0 + block 121 +1 ... 3 + -> block 124 val=0 inc=0 + block 124 +-1 ... -1 + caller #0 +15 + counts[0] = 3 + counts[0] = 3 + path: ENTRY +1 EXIT +func #5 + block 127 +-1 ... -1 + -> block 125 val=0 + block 125 +1 ... 5 + -> block 128 val=0 inc=0 + block 128 +-1 ... -1 + caller #0 +17 + counts[0] = 3 + counts[0] = 3 + path: ENTRY +1 EXIT +func #6 + block 131 +-1 ... -1 + -> block 129 val=0 + block 129 +1 ... 6 + -> block 132 val=0 inc=0 + block 132 +-1 ... -1 + caller #0 +43 + counts[0] = 3 + counts[0] = 3 + path: ENTRY +1 EXIT 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..9c749fc5d --- /dev/null +++ b/test/monitors/expected/const_arg0.wasm.path-profile.out @@ -0,0 +1,36 @@ +func #0 + block 102 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 3 + -> block 101 val=0 + block 101 +3 ... 4 + -> block 103 val=0 inc=0 + block 103 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +3 EXIT +func #1 + block 106 +-1 ... -1 + -> block 104 val=0 + block 104 +1 ... 13 + -> block 105 val=0 + block 105 +13 ... 14 + -> block 107 val=0 inc=0 + block 107 +-1 ... -1 + caller #0 +1 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +13 EXIT +func #2 + block 110 +-1 ... -1 + -> block 109 val=0 + block 109 +1 ... 2 + -> block 111 val=0 inc=0 + block 111 +-1 ... -1 + caller #1 +5 + counts[0] = 1 + caller #1 +11 + counts[0] = 1 + counts[0] = 2 + path: ENTRY +1 EXIT 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..19a8f5f54 --- /dev/null +++ b/test/monitors/expected/coverage_monitor0.wasm.path-profile.out @@ -0,0 +1,22 @@ +func #0 + block 102 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 3 + -> block 101 val=0 + block 101 +3 ... 4 + -> block 103 val=0 inc=0 + block 103 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +3 EXIT +func "unreachable" + block 106 +-1 ... -1 + -> block 104 val=0 + block 104 +1 ... 4 + -> block 105 val=0 + block 105 +4 ... 5 + -> block 107 val=0 inc=0 + block 107 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +4 EXIT 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..61b24c26b --- /dev/null +++ b/test/monitors/expected/coverage_monitor1.wasm.path-profile.out @@ -0,0 +1,51 @@ +func #1 + block 111 +-1 ... -1 + -> block 106 val=0 + block 106 +1 ... 5 + -> block 109 val=0 + -> block 108 val=1 + block 109 +8 ... 9 + -> block 110 val=0 + block 110 +9 ... 10 + -> block 107 val=0 + block 107 +10 ... 11 + -> block 112 val=0 inc=0 + block 112 +-1 ... -1 + block 108 +5 ... 8 + -> block 110 val=0 inc=1 + caller #0 +30 + counts[1] = 1 + counts[0] = 0 + path: ENTRY +1 +8 +9 +10 EXIT + counts[1] = 1 + path: ENTRY +1 +5 +9 +10 EXIT +func #2 + block 117 +-1 ... -1 + -> block 113 val=0 + block 113 +1 ... 5 + -> block 115 val=0 + -> block 114 val=2 inc=2 + block 115 +5 ... 10 + -> block 116 val=0 + -> block 114 val=1 inc=1 + block 116 +10 ... 11 + -> block 114 val=0 + block 114 +11 ... 12 + -> block 118 val=0 inc=0 + block 118 +-1 ... -1 + caller #0 +36 + counts[1] = 1 + counts[0] = 0 + path: ENTRY +1 +5 +10 +11 EXIT + counts[1] = 1 + path: ENTRY +1 +5 +11 EXIT + counts[2] = 0 + path: ENTRY +1 +11 EXIT +func #3 + block 121 +-1 ... -1 + -> block 120 val=0 + block 120 +1 ... 2 + -> block 122 val=0 inc=0 + block 122 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 EXIT 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..9788031e6 --- /dev/null +++ b/test/monitors/expected/load_store.wasm.path-profile.out @@ -0,0 +1,12 @@ +func #0 + block 102 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 53 + -> block 101 val=0 + block 101 +53 ... 54 + -> block 103 val=0 inc=0 + block 103 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +53 EXIT 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..b70b3e3df --- /dev/null +++ b/test/monitors/expected/loop_monitor0.wasm.path-profile.out @@ -0,0 +1,22 @@ +func #0 + block 102 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 3 + -> block 101 val=0 + block 101 +3 ... 4 + -> block 103 val=0 inc=0 + block 103 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +3 EXIT +func #2 + block 116 +-1 ... -1 + -> block 115 val=0 + block 115 +1 ... 4 + -> block 114 val=0 + block 114 +4 ... 5 + -> block 117 val=0 inc=0 + block 117 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +4 EXIT 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..55a682b43 --- /dev/null +++ b/test/monitors/expected/loop_monitor1.wasm.path-profile.out @@ -0,0 +1,12 @@ +func "main" + block 102 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 15 + -> block 101 val=0 + block 101 +15 ... 16 + -> block 103 val=0 inc=0 + block 103 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +15 EXIT 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..5ed4c652c --- /dev/null +++ b/test/monitors/expected/path_profile_br_outer.wasm.path-profile.out @@ -0,0 +1,77 @@ +func #0 + block 107 +-1 ... -1 + -> block 100 val=0 + block 100 +3 ... 14 + -> block 105 val=0 + -> block 102 val=2 inc=2 + block 105 +14 ... 21 + -> block 106 val=0 + -> block 103 val=1 inc=1 + block 106 +21 ... 26 + -> block 104 val=0 + block 104 +26 ... 34 + -> block 103 val=0 + block 103 +34 ... 42 + -> block 102 val=0 + block 102 +42 ... 45 + -> block 101 val=0 + block 101 +45 ... 46 + -> block 108 val=0 inc=0 + block 108 +-1 ... -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: ENTRY +3 +14 +21 +26 +34 +42 +45 EXIT + counts[1] = 2 + path: ENTRY +3 +14 +34 +42 +45 EXIT + counts[2] = 1 + path: ENTRY +3 +42 +45 EXIT +func #1 + block 115 +-1 ... -1 + -> block 109 val=0 + block 109 +3 ... 13 + -> block 114 val=0 + -> block 112 val=1 + block 114 +13 ... 15 + -> block 111 val=0 + block 111 +25 ... 28 + -> block 110 val=0 + block 110 +28 ... 29 + -> block 116 val=0 inc=0 + block 116 +-1 ... -1 + block 112 +20 ... 25 + -> block 111 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: ENTRY +3 +13 +25 +28 EXIT + counts[1] = 1 + path: ENTRY +3 +20 +25 +28 EXIT +func #2 + block 119 +-1 ... -1 + -> block 117 val=0 + block 117 +1 ... 51 + -> block 118 val=0 + block 118 +51 ... 52 + -> block 120 val=0 inc=0 + block 120 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +51 EXIT 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..bad2a1264 --- /dev/null +++ b/test/monitors/expected/path_profile_dead_code.wasm.path-profile.out @@ -0,0 +1,50 @@ +func #0 + block 103 +-1 ... -1 + -> block 100 val=0 + block 100 +3 ... 11 + -> block 102 val=0 + block 102 +16 ... 19 + -> block 101 val=0 + block 101 +19 ... 20 + -> block 104 val=0 inc=0 + block 104 +-1 ... -1 + caller #3 +1 + counts[0] = 1 + caller #3 +4 + counts[0] = 1 + caller #3 +7 + counts[0] = 1 + counts[0] = 3 + path: ENTRY +3 +16 +19 EXIT +func #2 + block 114 +-1 ... -1 + -> block 109 val=0 + block 109 +1 ... 5 + -> block 112 val=0 + -> block 111 val=0 + block 112 +7 ... 8 + block 111 +5 ... 7 + -> block 113 val=0 + block 113 +8 ... 11 + -> block 110 val=0 + block 110 +11 ... 12 + -> block 115 val=0 inc=0 + block 115 +-1 ... -1 + caller #3 +12 + counts[0] = 1 + caller #3 +17 + counts[0] = 1 + counts[0] = 2 + path: ENTRY +1 +5 +8 +11 EXIT +func #3 + block 118 +-1 ... -1 + -> block 116 val=0 + block 116 +1 ... 20 + -> block 117 val=0 + block 117 +20 ... 21 + -> block 119 val=0 inc=0 + block 119 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +20 EXIT 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..242bbbe84 --- /dev/null +++ b/test/monitors/expected/path_profile_dead_pred.wasm.path-profile.out @@ -0,0 +1,68 @@ +func #0 + block 105 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 5 + -> block 103 val=0 + -> block 102 val=1 + block 103 +9 ... 12 + -> block 106 val=0 inc=0 + block 106 +-1 ... -1 + block 102 +5 ... 8 + -> block 106 val=0 inc=1 + 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: ENTRY +1 +9 EXIT + counts[1] = 2 + path: ENTRY +1 +5 EXIT +func #1 + block 112 +-1 ... -1 + -> block 107 val=0 + block 107 +1 ... 5 + -> block 110 val=0 + -> block 109 val=1 + block 110 +9 ... 12 + -> block 113 val=0 inc=0 + block 113 +-1 ... -1 + block 109 +5 ... 8 + -> block 113 val=0 inc=1 + caller #3 +28 + counts[1] = 1 + counts[0] = 0 + path: ENTRY +1 +9 EXIT + counts[1] = 1 + path: ENTRY +1 +5 EXIT +func #2 + block 119 +-1 ... -1 + -> block 114 val=0 + block 114 +3 ... 15 + -> block 116 val=0 + block 116 +25 ... 28 + -> block 115 val=0 + block 115 +28 ... 29 + -> block 120 val=0 inc=0 + block 120 +-1 ... -1 + caller #3 +31 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +3 +25 +28 EXIT +func #3 + block 123 +-1 ... -1 + -> block 121 val=0 + block 121 +1 ... 34 + -> block 122 val=0 + block 122 +34 ... 35 + -> block 124 val=0 inc=0 + block 124 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +34 EXIT 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..bfd2abea6 --- /dev/null +++ b/test/monitors/expected/path_profile_deep_nest.wasm.path-profile.out @@ -0,0 +1,69 @@ +func #0 + block 111 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 5 + -> block 103 val=0 + -> block 102 val=1 + block 103 +24 ... 26 + -> block 104 val=0 + block 104 +26 ... 27 + -> block 101 val=0 + block 101 +27 ... 28 + -> block 112 val=0 inc=0 + block 112 +-1 ... -1 + block 102 +5 ... 9 + -> block 106 val=0 + -> block 105 val=1 + block 106 +20 ... 22 + -> block 107 val=0 + block 107 +22 ... 24 + -> block 104 val=0 inc=1 + block 105 +9 ... 13 + -> block 109 val=0 + -> block 108 val=1 + block 109 +16 ... 18 + -> block 110 val=0 + block 110 +18 ... 20 + -> block 107 val=0 inc=1 + block 108 +13 ... 16 + -> block 110 val=0 inc=1 + 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: ENTRY +1 +24 +26 +27 EXIT + counts[1] = 2 + path: ENTRY +1 +5 +20 +22 +26 +27 EXIT + counts[2] = 3 + path: ENTRY +1 +5 +9 +16 +18 +22 +26 +27 EXIT + counts[3] = 4 + path: ENTRY +1 +5 +9 +13 +18 +22 +26 +27 EXIT +func #1 + block 115 +-1 ... -1 + -> block 113 val=0 + block 113 +1 ... 91 + -> block 114 val=0 + block 114 +91 ... 92 + -> block 116 val=0 inc=0 + block 116 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +91 EXIT 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..8a97720e4 --- /dev/null +++ b/test/monitors/expected/path_profile_demo_only.wasm.path-profile.out @@ -0,0 +1,47 @@ +func #0 + block 106 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 17 + -> block 102 val=0 + -> block 103 val=1 + -> block 104 val=2 + -> block 105 val=3 + block 102 +23 ... 25 + -> block 107 val=0 inc=0 + block 107 +-1 ... -1 + block 103 +21 ... 23 + -> block 107 val=0 inc=1 + block 104 +19 ... 21 + -> block 107 val=0 inc=2 + block 105 +17 ... 19 + -> block 107 val=0 inc=3 + 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: ENTRY +1 +23 EXIT + counts[1] = 1 + path: ENTRY +1 +21 EXIT + counts[2] = 1 + path: ENTRY +1 +19 EXIT + counts[3] = 1 + path: ENTRY +1 +17 EXIT +func #1 + block 110 +-1 ... -1 + -> block 108 val=0 + block 108 +1 ... 21 + -> block 109 val=0 + block 109 +21 ... 22 + -> block 111 val=0 inc=0 + block 111 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +21 EXIT 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..753588fec --- /dev/null +++ b/test/monitors/expected/path_profile_diamond_only.wasm.path-profile.out @@ -0,0 +1,35 @@ +func #0 + block 105 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 5 + -> block 103 val=0 + -> block 102 val=1 + block 103 +8 ... 10 + -> block 104 val=0 + block 104 +10 ... 11 + -> block 101 val=0 + block 101 +11 ... 12 + -> block 106 val=0 inc=0 + block 106 +-1 ... -1 + block 102 +5 ... 8 + -> block 104 val=0 inc=1 + caller #1 +3 + counts[0] = 1 + caller #1 +8 + counts[1] = 1 + counts[0] = 1 + path: ENTRY +1 +8 +10 +11 EXIT + counts[1] = 1 + path: ENTRY +1 +5 +10 +11 EXIT +func #1 + block 109 +-1 ... -1 + -> block 107 val=0 + block 107 +1 ... 11 + -> block 108 val=0 + block 108 +11 ... 12 + -> block 110 val=0 inc=0 + block 110 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +11 EXIT 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..afa78ab40 --- /dev/null +++ b/test/monitors/expected/path_profile_dup_labels.wasm.path-profile.out @@ -0,0 +1,40 @@ +func #0 + block 104 +-1 ... -1 + -> block 100 val=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 102 +16 ... 18 + -> block 105 val=0 inc=0 + block 105 +-1 ... -1 + block 103 +14 ... 16 + -> block 105 val=0 inc=1 + 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: ENTRY +1 +16 EXIT + counts[1] = 3 + path: ENTRY +1 +14 EXIT +func #1 + block 108 +-1 ... -1 + -> block 106 val=0 + block 106 +1 ... 21 + -> block 107 val=0 + block 107 +21 ... 22 + -> block 109 val=0 inc=0 + block 109 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +21 EXIT 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..bf48b4bc7 --- /dev/null +++ b/test/monitors/expected/path_profile_empty_then.wasm.path-profile.out @@ -0,0 +1,29 @@ +func #0 + block 105 +-1 ... -1 + -> block 100 val=0 + block 100 +3 ... 11 + -> block 104 val=0 + -> block 104 val=0 inc=0 + block 104 +11 ... 14 + -> block 101 val=0 + block 101 +14 ... 15 + -> block 106 val=0 inc=0 + block 106 +-1 ... -1 + caller #1 +3 + counts[0] = 1 + caller #1 +8 + counts[0] = 1 + counts[0] = 2 + path: ENTRY +3 +11 +14 EXIT +func #1 + block 109 +-1 ... -1 + -> block 107 val=0 + block 107 +1 ... 11 + -> block 108 val=0 + block 108 +11 ... 12 + -> block 110 val=0 inc=0 + block 110 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +11 EXIT 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..daa5e40f4 --- /dev/null +++ b/test/monitors/expected/path_profile_freq_recur.wasm.path-profile.out @@ -0,0 +1,63 @@ +func #0 + block 111 +-1 ... -1 + -> block 100 val=0 + block 100 +3 ... 8 + -> block 104 val=0 + -> block 102 val=4 + block 104 +11 ... 19 + -> block 106 val=0 + -> block 105 val=2 + block 106 +27 ... 31 + -> block 107 val=0 + block 107 +31 ... 39 + -> block 109 val=0 + -> block 108 val=1 + block 109 +47 ... 51 + -> block 110 val=0 + block 110 +51 ... 62 + -> block 101 val=0 + block 101 +62 ... 63 + -> block 112 val=0 inc=0 + block 112 +-1 ... -1 + block 108 +39 ... 47 + -> block 110 val=0 inc=1 + block 105 +19 ... 27 + -> block 107 val=0 inc=2 + block 102 +8 ... 11 + -> block 112 val=0 inc=4 + caller #1 +3 + counts[3] = 1 + caller #0 +59 + counts[0] = 9 + counts[1] = 11 + counts[2] = 11 + counts[3] = 10 + counts[4] = 4 + caller #1 +8 + counts[2] = 1 + caller #1 +13 + counts[0] = 1 + caller #1 +18 + counts[2] = 1 + counts[0] = 10 + path: ENTRY +3 +11 +27 +31 +47 +51 +62 EXIT + counts[1] = 11 + path: ENTRY +3 +11 +27 +31 +39 +51 +62 EXIT + counts[2] = 13 + path: ENTRY +3 +11 +19 +31 +47 +51 +62 EXIT + counts[3] = 11 + path: ENTRY +3 +11 +19 +31 +39 +51 +62 EXIT + counts[4] = 4 + path: ENTRY +3 +8 EXIT +func #1 + block 115 +-1 ... -1 + -> block 113 val=0 + block 113 +1 ... 21 + -> block 114 val=0 + block 114 +21 ... 22 + -> block 116 val=0 inc=0 + block 116 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +21 EXIT 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..3637734c5 --- /dev/null +++ b/test/monitors/expected/path_profile_freq_shapes.wasm.path-profile.out @@ -0,0 +1,161 @@ +func #0 + block 107 +-1 ... -1 + -> block 100 val=0 + block 100 +3 ... 21 + -> block 103 val=0 + -> block 104 val=1 + -> block 105 val=2 + -> block 106 val=3 + block 103 +43 ... 48 + -> block 102 val=0 + block 102 +48 ... 51 + -> block 101 val=0 + block 101 +51 ... 52 + -> block 108 val=0 inc=0 + block 108 +-1 ... -1 + block 104 +36 ... 43 + -> block 102 val=0 inc=1 + block 105 +28 ... 36 + -> block 108 val=0 inc=2 + block 106 +21 ... 28 + -> 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: ENTRY +3 +43 +48 +51 EXIT + counts[1] = 23 + path: ENTRY +3 +36 +48 +51 EXIT + counts[2] = 5 + path: ENTRY +3 +28 EXIT + counts[3] = 17 + path: ENTRY +3 +21 +48 +51 EXIT +func #1 + block 111 +-1 ... -1 + -> block 109 val=0 + block 109 +1 ... 314 + -> block 110 val=0 + block 110 +314 ... 315 + -> block 112 val=0 inc=0 + block 112 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +314 EXIT 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..487c5c417 --- /dev/null +++ b/test/monitors/expected/path_profile_many_paths.wasm.path-profile.out @@ -0,0 +1,183 @@ +func #0 + block 117 +-1 ... -1 + -> block 100 val=0 + block 100 +3 ... 10 + -> block 103 val=0 + -> block 102 val=16 + block 103 +18 ... 25 + -> block 104 val=0 + block 104 +25 ... 33 + -> block 106 val=0 + -> block 105 val=8 + block 106 +41 ... 48 + -> block 107 val=0 + block 107 +48 ... 56 + -> block 109 val=0 + -> block 108 val=4 + block 109 +64 ... 71 + -> block 110 val=0 + block 110 +71 ... 79 + -> block 112 val=0 + -> block 111 val=2 + block 112 +87 ... 94 + -> block 113 val=0 + block 113 +94 ... 102 + -> block 115 val=0 + -> block 114 val=1 + block 115 +110 ... 117 + -> block 116 val=0 + block 116 +117 ... 120 + -> block 101 val=0 + block 101 +120 ... 121 + -> block 118 val=0 inc=0 + block 118 +-1 ... -1 + block 114 +102 ... 110 + -> block 116 val=0 inc=1 + block 111 +79 ... 87 + -> block 113 val=0 inc=2 + block 108 +56 ... 64 + -> block 110 val=0 inc=4 + block 105 +33 ... 41 + -> block 107 val=0 inc=8 + block 102 +10 ... 18 + -> 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: ENTRY +3 +18 +25 +41 +48 +64 +71 +87 +94 +110 +117 +120 EXIT + counts[1] = 1 + path: ENTRY +3 +18 +25 +41 +48 +64 +71 +87 +94 +102 +117 +120 EXIT + counts[2] = 1 + path: ENTRY +3 +18 +25 +41 +48 +64 +71 +79 +94 +110 +117 +120 EXIT + counts[3] = 1 + path: ENTRY +3 +18 +25 +41 +48 +64 +71 +79 +94 +102 +117 +120 EXIT + counts[4] = 1 + path: ENTRY +3 +18 +25 +41 +48 +56 +71 +87 +94 +110 +117 +120 EXIT + counts[5] = 1 + path: ENTRY +3 +18 +25 +41 +48 +56 +71 +87 +94 +102 +117 +120 EXIT + counts[6] = 1 + path: ENTRY +3 +18 +25 +41 +48 +56 +71 +79 +94 +110 +117 +120 EXIT + counts[7] = 1 + path: ENTRY +3 +18 +25 +41 +48 +56 +71 +79 +94 +102 +117 +120 EXIT + counts[8] = 1 + path: ENTRY +3 +18 +25 +33 +48 +64 +71 +87 +94 +110 +117 +120 EXIT + counts[9] = 1 + path: ENTRY +3 +18 +25 +33 +48 +64 +71 +87 +94 +102 +117 +120 EXIT + counts[10] = 1 + path: ENTRY +3 +18 +25 +33 +48 +64 +71 +79 +94 +110 +117 +120 EXIT + counts[11] = 1 + path: ENTRY +3 +18 +25 +33 +48 +64 +71 +79 +94 +102 +117 +120 EXIT + counts[12] = 1 + path: ENTRY +3 +18 +25 +33 +48 +56 +71 +87 +94 +110 +117 +120 EXIT + counts[13] = 1 + path: ENTRY +3 +18 +25 +33 +48 +56 +71 +87 +94 +102 +117 +120 EXIT + counts[14] = 1 + path: ENTRY +3 +18 +25 +33 +48 +56 +71 +79 +94 +110 +117 +120 EXIT + counts[15] = 1 + path: ENTRY +3 +18 +25 +33 +48 +56 +71 +79 +94 +102 +117 +120 EXIT + counts[16] = 1 + path: ENTRY +3 +10 +25 +41 +48 +64 +71 +87 +94 +110 +117 +120 EXIT + counts[17] = 1 + path: ENTRY +3 +10 +25 +41 +48 +64 +71 +87 +94 +102 +117 +120 EXIT + counts[18] = 1 + path: ENTRY +3 +10 +25 +41 +48 +64 +71 +79 +94 +110 +117 +120 EXIT + counts[19] = 1 + path: ENTRY +3 +10 +25 +41 +48 +64 +71 +79 +94 +102 +117 +120 EXIT + counts[20] = 1 + path: ENTRY +3 +10 +25 +41 +48 +56 +71 +87 +94 +110 +117 +120 EXIT + counts[21] = 1 + path: ENTRY +3 +10 +25 +41 +48 +56 +71 +87 +94 +102 +117 +120 EXIT + counts[22] = 1 + path: ENTRY +3 +10 +25 +41 +48 +56 +71 +79 +94 +110 +117 +120 EXIT + counts[23] = 1 + path: ENTRY +3 +10 +25 +41 +48 +56 +71 +79 +94 +102 +117 +120 EXIT + counts[24] = 1 + path: ENTRY +3 +10 +25 +33 +48 +64 +71 +87 +94 +110 +117 +120 EXIT + counts[25] = 1 + path: ENTRY +3 +10 +25 +33 +48 +64 +71 +87 +94 +102 +117 +120 EXIT + counts[26] = 1 + path: ENTRY +3 +10 +25 +33 +48 +64 +71 +79 +94 +110 +117 +120 EXIT + counts[27] = 1 + path: ENTRY +3 +10 +25 +33 +48 +64 +71 +79 +94 +102 +117 +120 EXIT + counts[28] = 1 + path: ENTRY +3 +10 +25 +33 +48 +56 +71 +87 +94 +110 +117 +120 EXIT + counts[29] = 1 + path: ENTRY +3 +10 +25 +33 +48 +56 +71 +87 +94 +102 +117 +120 EXIT + counts[30] = 1 + path: ENTRY +3 +10 +25 +33 +48 +56 +71 +79 +94 +110 +117 +120 EXIT + counts[31] = 1 + path: ENTRY +3 +10 +25 +33 +48 +56 +71 +79 +94 +102 +117 +120 EXIT +func #1 + block 121 +-1 ... -1 + -> block 119 val=0 + block 119 +1 ... 161 + -> block 120 val=0 + block 120 +161 ... 162 + -> block 122 val=0 inc=0 + block 122 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +161 EXIT 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..c173a7c28 --- /dev/null +++ b/test/monitors/expected/path_profile_mixed_nest.wasm.path-profile.out @@ -0,0 +1,101 @@ +func #0 + block 108 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 14 + -> block 102 val=0 + -> block 103 val=1 + -> block 104 val=2 + block 102 +30 ... 33 + -> block 101 val=0 + block 101 +33 ... 34 + -> block 109 val=0 inc=0 + block 109 +-1 ... -1 + block 103 +26 ... 30 + -> block 109 val=0 inc=1 + block 104 +14 ... 19 + -> block 106 val=0 + -> block 105 val=1 + block 106 +22 ... 24 + -> block 107 val=0 + block 107 +24 ... 26 + -> block 109 val=0 inc=2 + block 105 +19 ... 22 + -> block 107 val=0 inc=1 + 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: ENTRY +1 +30 +33 EXIT + counts[1] = 3 + path: ENTRY +1 +26 EXIT + counts[2] = 1 + path: ENTRY +1 +14 +22 +24 EXIT + counts[3] = 2 + path: ENTRY +1 +14 +19 +24 EXIT +func #1 + block 117 +-1 ... -1 + -> block 110 val=0 + block 110 +1 ... 5 + -> block 113 val=0 + -> block 112 val=1 + block 113 +24 ... 25 + -> block 114 val=0 + block 114 +25 ... 28 + -> block 111 val=0 + block 111 +28 ... 29 + -> block 118 val=0 inc=0 + block 118 +-1 ... -1 + block 112 +5 ... 15 + -> block 115 val=0 + -> block 116 val=1 + block 115 +19 ... 23 + -> block 118 val=0 inc=1 + block 116 +15 ... 19 + -> block 118 val=0 inc=2 + 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: ENTRY +1 +24 +25 +28 EXIT + counts[1] = 2 + path: ENTRY +1 +5 +19 EXIT + counts[2] = 1 + path: ENTRY +1 +5 +15 EXIT +func #2 + block 121 +-1 ... -1 + -> block 119 val=0 + block 119 +1 ... 113 + -> block 120 val=0 + block 120 +113 ... 114 + -> block 122 val=0 inc=0 + block 122 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +113 EXIT 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..bb8f0a641 --- /dev/null +++ b/test/monitors/expected/path_profile_one_armed.wasm.path-profile.out @@ -0,0 +1,33 @@ +func #0 + block 105 +-1 ... -1 + -> block 100 val=0 + block 100 +3 ... 11 + -> block 104 val=0 + -> block 102 val=1 + block 104 +15 ... 18 + -> block 101 val=0 + block 101 +18 ... 19 + -> block 106 val=0 inc=0 + block 106 +-1 ... -1 + block 102 +11 ... 15 + -> block 104 val=0 inc=1 + caller #1 +3 + counts[0] = 1 + caller #1 +8 + counts[1] = 1 + counts[0] = 1 + path: ENTRY +3 +15 +18 EXIT + counts[1] = 1 + path: ENTRY +3 +11 +15 +18 EXIT +func #1 + block 109 +-1 ... -1 + -> block 107 val=0 + block 107 +1 ... 11 + -> block 108 val=0 + block 108 +11 ... 12 + -> block 110 val=0 inc=0 + block 110 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +11 EXIT 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..90b45d9b6 --- /dev/null +++ b/test/monitors/expected/path_profile_recur.wasm.path-profile.out @@ -0,0 +1,42 @@ +func #0 + block 105 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 8 + -> block 103 val=0 + -> block 102 val=1 + block 103 +11 ... 21 + -> block 104 val=0 + block 104 +21 ... 22 + -> block 101 val=0 + block 101 +22 ... 23 + -> block 106 val=0 inc=0 + block 106 +-1 ... -1 + block 102 +8 ... 11 + -> block 104 val=0 inc=1 + caller #1 +3 + counts[1] = 1 + caller #1 +8 + counts[0] = 1 + caller #0 +18 + counts[0] = 4 + counts[1] = 3 + caller #1 +13 + counts[0] = 1 + caller #1 +18 + counts[0] = 1 + counts[0] = 7 + path: ENTRY +1 +11 +21 +22 EXIT + counts[1] = 4 + path: ENTRY +1 +8 +21 +22 EXIT +func #1 + block 109 +-1 ... -1 + -> block 107 val=0 + block 107 +1 ... 21 + -> block 108 val=0 + block 108 +21 ... 22 + -> block 110 val=0 inc=0 + block 110 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +21 EXIT 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..f61319f66 --- /dev/null +++ b/test/monitors/expected/path_profile_returns.wasm.path-profile.out @@ -0,0 +1,73 @@ +func #0 + block 108 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 5 + -> block 104 val=0 + -> block 102 val=2 + block 104 +8 ... 13 + -> block 107 val=0 + -> block 105 val=1 + block 107 +16 ... 19 + -> block 101 val=0 + block 101 +19 ... 20 + -> block 109 val=0 inc=0 + block 109 +-1 ... -1 + block 105 +13 ... 16 + -> block 109 val=0 inc=1 + block 102 +5 ... 8 + -> block 109 val=0 inc=2 + 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: ENTRY +1 +8 +16 +19 EXIT + counts[1] = 2 + path: ENTRY +1 +8 +13 EXIT + counts[2] = 1 + path: ENTRY +1 +5 EXIT +func #1 + block 115 +-1 ... -1 + -> block 110 val=0 + block 110 +1 ... 5 + -> block 113 val=0 + -> block 112 val=1 + block 113 +9 ... 10 + -> block 114 val=0 + block 114 +10 ... 13 + -> block 111 val=0 + block 111 +13 ... 14 + -> block 116 val=0 inc=0 + block 116 +-1 ... -1 + block 112 +5 ... 8 + -> block 116 val=0 inc=1 + caller #2 +45 + counts[1] = 1 + caller #2 +50 + counts[0] = 1 + caller #2 +55 + counts[0] = 1 + counts[0] = 2 + path: ENTRY +1 +9 +10 +13 EXIT + counts[1] = 1 + path: ENTRY +1 +5 EXIT +func #2 + block 119 +-1 ... -1 + -> block 117 val=0 + block 117 +1 ... 58 + -> block 118 val=0 + block 118 +58 ... 59 + -> block 120 val=0 inc=0 + block 120 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +58 EXIT 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..89a94722c --- /dev/null +++ b/test/monitors/expected/path_profile_select_call.wasm.path-profile.out @@ -0,0 +1,69 @@ +func #0 + block 102 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 8 + -> block 101 val=0 + block 101 +8 ... 9 + -> block 103 val=0 inc=0 + block 103 +-1 ... -1 + 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: ENTRY +1 +8 EXIT +func #1 + block 106 +-1 ... -1 + -> block 104 val=0 + block 104 +1 ... 6 + -> block 105 val=0 + block 105 +6 ... 7 + -> block 107 val=0 inc=0 + block 107 +-1 ... -1 + caller #2 +7 + counts[0] = 5 + counts[0] = 5 + path: ENTRY +1 +6 EXIT +func #2 + block 112 +-1 ... -1 + -> block 108 val=0 + block 108 +3 ... 11 + -> block 111 val=0 + -> block 110 val=1 inc=1 + block 111 +11 ... 15 + -> block 110 val=0 + block 110 +15 ... 18 + -> block 109 val=0 + block 109 +18 ... 19 + -> block 113 val=0 inc=0 + block 113 +-1 ... -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: ENTRY +3 +11 +15 +18 EXIT + counts[1] = 2 + path: ENTRY +3 +15 +18 EXIT +func #3 + block 116 +-1 ... -1 + -> block 114 val=0 + block 114 +1 ... 62 + -> block 115 val=0 + block 115 +62 ... 63 + -> block 117 val=0 inc=0 + block 117 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +62 EXIT 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..665e097ae --- /dev/null +++ b/test/monitors/expected/path_profile_shapes.wasm.path-profile.out @@ -0,0 +1,74 @@ +func #0 + block 103 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 3 + -> block 102 val=0 + block 102 +3 ... 6 + -> block 101 val=0 + block 101 +6 ... 7 + -> block 104 val=0 inc=0 + block 104 +-1 ... -1 + caller #4 +1 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +3 +6 EXIT +func #1 + block 109 +-1 ... -1 + -> block 105 val=0 + block 105 +1 ... 7 + -> block 107 val=0 + -> block 107 val=0 inc=0 + block 107 +7 ... 10 + -> block 106 val=0 + block 106 +10 ... 11 + -> block 110 val=0 inc=0 + block 110 +-1 ... -1 + caller #4 +6 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +7 +10 EXIT +func #2 + block 116 +-1 ... -1 + -> block 111 val=0 + block 111 +1 ... 5 + -> block 115 val=0 + -> block 115 val=0 inc=0 + block 115 +5 ... 8 + -> block 112 val=0 + block 112 +8 ... 9 + -> block 117 val=0 inc=0 + block 117 +-1 ... -1 + caller #4 +11 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +5 +8 EXIT +func #3 + block 123 +-1 ... -1 + -> block 118 val=0 + block 118 +1 ... 7 + -> block 122 val=0 + block 122 +7 ... 8 + -> block 121 val=0 + block 121 +8 ... 9 + -> block 120 val=0 + block 120 +9 ... 12 + -> block 119 val=0 + block 119 +12 ... 13 + -> block 124 val=0 inc=0 + block 124 +-1 ... -1 + caller #4 +14 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +7 +8 +9 +12 EXIT +func #4 + block 127 +-1 ... -1 + -> block 125 val=0 + block 125 +1 ... 17 + -> block 126 val=0 + block 126 +17 ... 18 + -> block 128 val=0 inc=0 + block 128 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +17 EXIT 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..b226e8023 --- /dev/null +++ b/test/monitors/expected/path_profile_straight.wasm.path-profile.out @@ -0,0 +1,60 @@ +func #0 + block 102 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 6 + -> block 101 val=0 + block 101 +6 ... 7 + -> block 103 val=0 inc=0 + block 103 +-1 ... -1 + 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: ENTRY +1 +6 EXIT +func #1 + block 106 +-1 ... -1 + -> block 104 val=0 + block 104 +1 ... 8 + -> block 105 val=0 + block 105 +8 ... 9 + -> block 107 val=0 inc=0 + block 107 +-1 ... -1 + caller #3 +16 + counts[0] = 1 + caller #3 +19 + counts[0] = 1 + caller #2 +5 + counts[0] = 2 + counts[0] = 4 + path: ENTRY +1 +8 EXIT +func #2 + block 110 +-1 ... -1 + -> block 108 val=0 + block 108 +1 ... 8 + -> block 109 val=0 + block 109 +8 ... 9 + -> block 111 val=0 inc=0 + block 111 +-1 ... -1 + caller #3 +24 + counts[0] = 1 + caller #3 +29 + counts[0] = 1 + counts[0] = 2 + path: ENTRY +1 +8 EXIT +func #3 + block 114 +-1 ... -1 + -> block 112 val=0 + block 112 +1 ... 32 + -> block 113 val=0 + block 113 +32 ... 33 + -> block 115 val=0 inc=0 + block 115 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +32 EXIT 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..362231c6b --- /dev/null +++ b/test/monitors/expected/path_profile_table1.wasm.path-profile.out @@ -0,0 +1,30 @@ +func #0 + block 103 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 8 + -> block 102 val=0 + block 102 +8 ... 11 + -> block 101 val=0 + block 101 +11 ... 12 + -> block 104 val=0 inc=0 + block 104 +-1 ... -1 + caller #1 +3 + counts[0] = 1 + caller #1 +8 + counts[0] = 1 + caller #1 +14 + counts[0] = 1 + counts[0] = 3 + path: ENTRY +1 +8 +11 EXIT +func #1 + block 107 +-1 ... -1 + -> block 105 val=0 + block 105 +1 ... 17 + -> block 106 val=0 + block 106 +17 ... 18 + -> block 108 val=0 inc=0 + block 108 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +17 EXIT 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..52f7c46ff --- /dev/null +++ b/test/monitors/expected/path_profile_table2.wasm.path-profile.out @@ -0,0 +1,41 @@ +func #0 + block 104 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 11 + -> block 102 val=0 + -> block 103 val=1 + block 102 +15 ... 18 + -> block 101 val=0 + block 101 +18 ... 19 + -> block 105 val=0 inc=0 + block 105 +-1 ... -1 + block 103 +11 ... 15 + -> block 105 val=0 inc=1 + 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: ENTRY +1 +15 +18 EXIT + counts[1] = 2 + path: ENTRY +1 +11 EXIT +func #1 + block 108 +-1 ... -1 + -> block 106 val=0 + block 106 +1 ... 31 + -> block 107 val=0 + block 107 +31 ... 32 + -> block 109 val=0 inc=0 + block 109 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +31 EXIT 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..7cc99ff78 --- /dev/null +++ b/test/monitors/expected/path_profile_table_default.wasm.path-profile.out @@ -0,0 +1,42 @@ +func #0 + block 105 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 14 + -> block 102 val=0 + -> block 103 val=1 + -> block 104 val=2 + block 102 +22 ... 25 + -> block 101 val=0 + block 101 +25 ... 26 + -> block 106 val=0 inc=0 + block 106 +-1 ... -1 + block 103 +18 ... 22 + -> block 106 val=0 inc=1 + block 104 +14 ... 18 + -> block 106 val=0 inc=2 + 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: ENTRY +1 +22 +25 EXIT + counts[1] = 0 + path: ENTRY +1 +18 EXIT + counts[2] = 0 + path: ENTRY +1 +14 EXIT +func #1 + block 109 +-1 ... -1 + -> block 107 val=0 + block 107 +1 ... 22 + -> block 108 val=0 + block 108 +22 ... 23 + -> block 110 val=0 inc=0 + block 110 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +22 EXIT 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..b58796b49 --- /dev/null +++ b/test/monitors/expected/path_profile_trap.wasm.path-profile.out @@ -0,0 +1,28 @@ +func #0 + block 105 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 5 + -> block 104 val=0 + -> block 102 val=1 + block 104 +6 ... 9 + -> block 101 val=0 + block 101 +9 ... 10 + -> block 106 val=0 inc=0 + block 106 +-1 ... -1 + block 102 +5 ... 6 + caller #1 +3 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +6 +9 EXIT +func #1 + block 109 +-1 ... -1 + -> block 107 val=0 + block 107 +1 ... 6 + -> block 108 val=0 + block 108 +6 ... 7 + -> block 110 val=0 inc=0 + block 110 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +6 EXIT 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..0161169b7 --- /dev/null +++ b/test/monitors/expected/path_profile_wide_merge.wasm.path-profile.out @@ -0,0 +1,76 @@ +func #0 + block 108 +-1 ... -1 + -> block 100 val=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 103 +52 ... 57 + -> block 102 val=0 + block 102 +57 ... 63 + -> block 101 val=0 + block 101 +63 ... 64 + -> block 109 val=0 inc=0 + block 109 +-1 ... -1 + block 104 +45 ... 52 + -> block 102 val=0 inc=1 + block 105 +38 ... 45 + -> block 102 val=0 inc=2 + block 106 +31 ... 38 + -> block 102 val=0 inc=3 + block 107 +24 ... 31 + -> 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: ENTRY +3 +52 +57 +63 EXIT + counts[1] = 4 + path: ENTRY +3 +45 +57 +63 EXIT + counts[2] = 3 + path: ENTRY +3 +38 +57 +63 EXIT + counts[3] = 2 + path: ENTRY +3 +31 +57 +63 EXIT + counts[4] = 1 + path: ENTRY +3 +24 +57 +63 EXIT +func #1 + block 112 +-1 ... -1 + -> block 110 val=0 + block 110 +1 ... 76 + -> block 111 val=0 + block 111 +76 ... 77 + -> block 113 val=0 inc=0 + block 113 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +76 EXIT 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..5711d6230 --- /dev/null +++ b/test/monitors/expected/path_profile_wide_only.wasm.path-profile.out @@ -0,0 +1,63 @@ +func #0 + block 108 +-1 ... -1 + -> block 100 val=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 102 +33 ... 35 + -> block 109 val=0 inc=0 + block 109 +-1 ... -1 + block 103 +31 ... 33 + -> block 109 val=0 inc=1 + block 104 +29 ... 31 + -> block 109 val=0 inc=2 + block 105 +27 ... 29 + -> block 109 val=0 inc=3 + block 106 +25 ... 27 + -> block 109 val=0 inc=4 + block 107 +23 ... 25 + -> block 109 val=0 inc=5 + 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: ENTRY +1 +33 EXIT + counts[1] = 1 + path: ENTRY +1 +31 EXIT + counts[2] = 1 + path: ENTRY +1 +29 EXIT + counts[3] = 1 + path: ENTRY +1 +27 EXIT + counts[4] = 1 + path: ENTRY +1 +25 EXIT + counts[5] = 1 + path: ENTRY +1 +23 EXIT +func #1 + block 112 +-1 ... -1 + -> block 110 val=0 + block 110 +1 ... 33 + -> block 111 val=0 + block 111 +33 ... 34 + -> block 113 val=0 inc=0 + block 113 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +33 EXIT 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..512d02855 --- /dev/null +++ b/test/monitors/expected/profile_bytecode.wasm.path-profile.out @@ -0,0 +1,161 @@ +func #0 + block 102 +-1 ... -1 + -> block 100 val=0 + block 100 +1 ... 14 + -> block 101 val=0 + block 101 +14 ... 15 + -> block 103 val=0 inc=0 + block 103 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +14 EXIT +func #2 + block 155 +-1 ... -1 + -> block 150 val=0 + block 150 +3 ... 12 + -> block 153 val=0 + -> block 152 val=1 + block 153 +21 ... 27 + -> block 154 val=0 + block 154 +27 ... 30 + -> block 151 val=0 + block 151 +30 ... 31 + -> block 156 val=0 inc=0 + block 156 +-1 ... -1 + block 152 +12 ... 21 + -> block 154 val=0 inc=1 + caller #1 +262 + counts[1] = 1 + counts[0] = 0 + path: ENTRY +3 +21 +27 +30 EXIT + counts[1] = 1 + path: ENTRY +3 +12 +27 +30 EXIT +func #3 + block 159 +-1 ... -1 + -> block 157 val=0 + block 157 +1 ... 3 + -> block 158 val=0 + block 158 +3 ... 4 + -> block 160 val=0 inc=0 + block 160 +-1 ... -1 + caller #1 +277 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +3 EXIT +func #4 + block 163 +-1 ... -1 + -> block 161 val=0 + block 161 +1 ... 9 + -> block 162 val=0 + block 162 +9 ... 10 + -> block 164 val=0 inc=0 + block 164 +-1 ... -1 + caller #3 +1 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +9 EXIT +func #5 + block 167 +-1 ... -1 + -> block 165 val=0 + block 165 +1 ... 4 + -> block 166 val=0 + block 166 +4 ... 5 + -> block 168 val=0 inc=0 + block 168 +-1 ... -1 + caller #4 +1 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +4 EXIT +func #6 + block 171 +-1 ... -1 + -> block 169 val=0 + block 169 +1 ... 5 + -> block 172 val=0 inc=0 + block 172 +-1 ... -1 + caller #4 +4 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 EXIT +func #7 + block 175 +-1 ... -1 + -> block 173 val=0 + block 173 +1 ... 3 + -> block 174 val=0 + block 174 +3 ... 4 + -> block 176 val=0 inc=0 + block 176 +-1 ... -1 + caller #6 +1 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +3 EXIT +func #8 + block 179 +-1 ... -1 + -> block 177 val=0 + block 177 +1 ... 5 + -> block 178 val=0 + block 178 +5 ... 6 + -> block 180 val=0 inc=0 + block 180 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #9 + block 183 +-1 ... -1 + -> block 181 val=0 + block 181 +1 ... 5 + -> block 182 val=0 + block 182 +5 ... 6 + -> block 184 val=0 inc=0 + block 184 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #10 + block 187 +-1 ... -1 + -> block 185 val=0 + block 185 +1 ... 3 + -> block 186 val=0 + block 186 +3 ... 4 + -> block 188 val=0 inc=0 + block 188 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +3 EXIT +func #11 + block 191 +-1 ... -1 + -> block 189 val=0 + block 189 +1 ... 3 + -> block 190 val=0 + block 190 +3 ... 4 + -> block 192 val=0 inc=0 + block 192 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +3 EXIT +func #12 + block 195 +-1 ... -1 + -> block 193 val=0 + block 193 +1 ... 3 + -> block 194 val=0 + block 194 +3 ... 4 + -> block 196 val=0 inc=0 + block 196 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +3 EXIT +func #13 + block 199 +-1 ... -1 + -> block 197 val=0 + block 197 +1 ... 3 + -> block 198 val=0 + block 198 +3 ... 4 + -> block 200 val=0 inc=0 + block 200 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +3 EXIT +func #14 + block 203 +-1 ... -1 + -> block 201 val=0 + block 201 +1 ... 5 + -> block 202 val=0 + block 202 +5 ... 6 + -> block 204 val=0 inc=0 + block 204 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT 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..f985d03f3 --- /dev/null +++ b/test/monitors/expected/profile_bytecode_ai.wasm.path-profile.out @@ -0,0 +1,825 @@ +func #4 + block 105 +-1 ... -1 + -> block 100 val=0 + block 100 +3 ... 44 + -> block 104 val=0 + -> block 102 val=1 + block 104 +53 ... 59 + -> block 101 val=0 + block 101 +59 ... 60 + -> block 106 val=0 inc=0 + block 106 +-1 ... -1 + block 102 +44 ... 53 + -> block 106 val=0 inc=1 + caller #7 +40 + counts[0] = 1 + caller #9 +6 + counts[0] = 1 + counts[0] = 2 + path: ENTRY +3 +53 +59 EXIT + counts[1] = 0 + path: ENTRY +3 +44 EXIT +func #5 + block 109 +-1 ... -1 + -> block 107 val=0 + block 107 +1 ... 7 + -> block 108 val=0 + block 108 +7 ... 8 + -> block 110 val=0 inc=0 + block 110 +-1 ... -1 + caller engine + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +7 EXIT +func #6 + block 113 +-1 ... -1 + -> block 111 val=0 + block 111 +1 ... 9 + -> block 112 val=0 + block 112 +9 ... 10 + -> block 114 val=0 inc=0 + block 114 +-1 ... -1 + caller #5 +5 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +9 EXIT +func #8 + block 139 +-1 ... -1 + -> block 137 val=0 + block 137 +1 ... 9 + -> block 138 val=0 + block 138 +9 ... 10 + -> block 140 val=0 inc=0 + block 140 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +9 EXIT +func #10 + block 212 +-1 ... -1 + -> block 210 val=0 + block 210 +1 ... 6 + -> block 211 val=0 + block 211 +6 ... 7 + -> block 213 val=0 inc=0 + block 213 +-1 ... -1 + caller #52 +5 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +6 EXIT +func #11 + block 216 +-1 ... -1 + -> block 214 val=0 + block 214 +1 ... 6 + -> block 215 val=0 + block 215 +6 ... 7 + -> block 217 val=0 inc=0 + block 217 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +6 EXIT +func #12 + block 229 +-1 ... -1 + -> block 218 val=0 + block 218 +3 ... 8 + -> block 221 val=0 + -> block 220 val=1 + block 221 +13 ... 18 + -> block 225 val=0 + -> block 223 val=1 + block 225 +19 ... 29 + -> block 222 val=0 + block 222 +29 ... 42 + -> block 228 val=0 + -> block 226 val=1 + block 228 +43 ... 53 + -> block 230 val=0 inc=0 + block 230 +-1 ... -1 + block 226 +42 ... 43 + block 223 +18 ... 19 + block 220 +8 ... 13 + -> block 222 val=0 inc=1 + counts[0] = 0 + path: ENTRY +3 +13 +19 +29 +43 EXIT + counts[1] = 0 + path: ENTRY +3 +8 +29 +43 EXIT +func #13 + block 233 +-1 ... -1 + -> block 231 val=0 + block 231 +1 ... 6 + -> block 232 val=0 + block 232 +6 ... 7 + -> block 234 val=0 inc=0 + block 234 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +6 EXIT +func #14 + block 237 +-1 ... -1 + -> block 235 val=0 + block 235 +3 ... 134 + -> block 238 val=0 inc=0 + block 238 +-1 ... -1 + caller #7 +13 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +3 EXIT +func #16 + block 266 +-1 ... -1 + -> block 261 val=0 + block 261 +1 ... 28 + -> block 264 val=0 + -> block 263 val=1 + block 264 +38 ... 75 + -> block 265 val=0 + block 265 +75 ... 83 + -> block 267 val=0 inc=0 + block 267 +-1 ... -1 + block 263 +28 ... 38 + -> block 265 val=0 inc=1 + counts[0] = 0 + path: ENTRY +1 +38 +75 EXIT + counts[1] = 0 + path: ENTRY +1 +28 +75 EXIT +func #17 + block 270 +-1 ... -1 + -> block 268 val=0 + block 268 +1 ... 31 + -> block 271 val=0 inc=0 + block 271 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 EXIT +func #18 + block 274 +-1 ... -1 + -> block 272 val=0 + block 272 +1 ... 21 + -> block 273 val=0 + block 273 +21 ... 22 + -> block 275 val=0 inc=0 + block 275 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +21 EXIT +func #20 + block 296 +-1 ... -1 + -> block 283 val=0 + block 283 +3 ... 17 + -> block 286 val=0 + -> block 285 val=4 + block 286 +18 ... 32 + -> block 288 val=0 + -> block 287 val=3 + block 288 +32 ... 55 + -> block 291 val=0 + -> block 290 val=1 + block 291 +80 ... 82 + -> block 292 val=0 + block 292 +82 ... 89 + -> block 289 val=0 + block 289 +89 ... 93 + -> block 297 val=0 inc=0 + block 297 +-1 ... -1 + block 290 +55 ... 69 + -> block 294 val=0 + -> block 293 val=1 + block 294 +76 ... 78 + -> block 292 val=0 inc=1 + block 293 +69 ... 75 + -> block 289 val=0 inc=2 + block 287 +93 ... 96 + -> block 284 val=0 + block 284 +96 ... 97 + -> block 297 val=0 inc=3 + block 285 +17 ... 18 + -> block 287 val=0 inc=1 + counts[0] = 0 + path: ENTRY +3 +18 +32 +80 +82 +89 EXIT + counts[1] = 0 + path: ENTRY +3 +18 +32 +55 +76 +82 +89 EXIT + counts[2] = 0 + path: ENTRY +3 +18 +32 +55 +69 +89 EXIT + counts[3] = 0 + path: ENTRY +3 +18 +93 +96 EXIT + counts[4] = 0 + path: ENTRY +3 +17 +93 +96 EXIT +func #24 + block 366 +-1 ... -1 + -> block 364 val=0 + block 364 +1 ... 2 + -> block 367 val=0 inc=0 + block 367 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 EXIT +func #25 + block 376 +-1 ... -1 + -> block 368 val=0 + block 368 +3 ... 18 + -> block 372 val=0 + -> block 370 val=1 + block 372 +19 ... 37 + -> block 375 val=0 + -> block 373 val=1 + block 375 +38 ... 57 + -> block 369 val=0 + block 369 +57 ... 58 + -> block 377 val=0 inc=0 + block 377 +-1 ... -1 + block 373 +37 ... 38 + block 370 +18 ... 19 + counts[0] = 0 + path: ENTRY +3 +19 +38 +57 EXIT +func #26 + block 389 +-1 ... -1 + -> block 378 val=0 + block 378 +3 ... 15 + -> block 381 val=0 + -> block 380 val=1 + block 381 +20 ... 25 + -> block 385 val=0 + -> block 383 val=1 + block 385 +26 ... 36 + -> block 382 val=0 + block 382 +36 ... 49 + -> block 388 val=0 + -> block 386 val=1 + block 388 +50 ... 61 + -> block 379 val=0 + block 379 +61 ... 62 + -> block 390 val=0 inc=0 + block 390 +-1 ... -1 + block 386 +49 ... 50 + block 383 +25 ... 26 + block 380 +15 ... 20 + -> block 382 val=0 inc=1 + counts[0] = 0 + path: ENTRY +3 +20 +26 +36 +50 +61 EXIT + counts[1] = 0 + path: ENTRY +3 +15 +36 +50 +61 EXIT +func #27 + block 393 +-1 ... -1 + -> block 391 val=0 + block 391 +1 ... 10 + -> block 392 val=0 + block 392 +10 ... 11 + -> block 394 val=0 inc=0 + block 394 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +10 EXIT +func #28 + block 418 +-1 ... -1 + -> block 395 val=0 + block 395 +3 ... 8 + -> block 398 val=0 + -> block 397 val=6 + block 398 +13 ... 18 + -> block 402 val=0 + -> block 400 val=6 + block 402 +19 ... 29 + -> block 399 val=0 + block 399 +29 ... 42 + -> block 405 val=0 + -> block 403 val=6 + block 405 +43 ... 56 + -> block 407 val=0 + -> block 406 val=4 + block 407 +61 ... 66 + -> block 410 val=0 + -> block 409 val=2 + block 410 +71 ... 76 + -> block 414 val=0 + -> block 412 val=2 + block 414 +77 ... 87 + -> block 411 val=0 + block 411 +87 ... 97 + -> block 408 val=0 + block 408 +97 ... 103 + -> block 416 val=0 + -> block 415 val=1 + block 416 +108 ... 117 + -> block 417 val=0 + block 417 +117 ... 129 + -> block 419 val=0 inc=0 + block 419 +-1 ... -1 + block 415 +103 ... 108 + -> block 417 val=0 inc=1 + block 412 +76 ... 77 + block 409 +66 ... 71 + -> block 411 val=0 inc=2 + block 406 +56 ... 61 + -> block 408 val=0 inc=4 + block 403 +42 ... 43 + block 400 +18 ... 19 + block 397 +8 ... 13 + -> block 399 val=0 inc=6 + counts[0] = 0 + path: ENTRY +3 +13 +19 +29 +43 +61 +71 +77 +87 +97 +108 +117 EXIT + counts[1] = 0 + path: ENTRY +3 +13 +19 +29 +43 +61 +71 +77 +87 +97 +103 +117 EXIT + counts[2] = 0 + path: ENTRY +3 +13 +19 +29 +43 +61 +66 +87 +97 +108 +117 EXIT + counts[3] = 0 + path: ENTRY +3 +13 +19 +29 +43 +61 +66 +87 +97 +103 +117 EXIT + counts[4] = 0 + path: ENTRY +3 +13 +19 +29 +43 +56 +97 +108 +117 EXIT + counts[5] = 0 + path: ENTRY +3 +13 +19 +29 +43 +56 +97 +103 +117 EXIT + counts[6] = 0 + path: ENTRY +3 +8 +29 +43 +61 +71 +77 +87 +97 +108 +117 EXIT + counts[7] = 0 + path: ENTRY +3 +8 +29 +43 +61 +71 +77 +87 +97 +103 +117 EXIT + counts[8] = 0 + path: ENTRY +3 +8 +29 +43 +61 +66 +87 +97 +108 +117 EXIT + counts[9] = 0 + path: ENTRY +3 +8 +29 +43 +61 +66 +87 +97 +103 +117 EXIT + counts[10] = 0 + path: ENTRY +3 +8 +29 +43 +56 +97 +108 +117 EXIT + counts[11] = 0 + path: ENTRY +3 +8 +29 +43 +56 +97 +103 +117 EXIT +func #29 + block 422 +-1 ... -1 + -> block 420 val=0 + block 420 +1 ... 18 + -> block 421 val=0 + block 421 +18 ... 19 + -> block 423 val=0 inc=0 + block 423 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +18 EXIT +func #30 + block 426 +-1 ... -1 + -> block 424 val=0 + block 424 +1 ... 17 + -> block 425 val=0 + block 425 +17 ... 18 + -> block 427 val=0 inc=0 + block 427 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +17 EXIT +func #35 + block 495 +-1 ... -1 + -> block 468 val=0 + block 468 +3 ... 13 + -> block 471 val=0 + -> block 470 val=10 + block 471 +15 ... 29 + -> block 474 val=0 + -> block 473 val=3 + block 474 +168 ... 170 + -> block 475 val=0 + block 475 +170 ... 182 + -> block 490 val=0 + -> block 489 val=1 + block 490 +199 ... 201 + -> block 491 val=0 + block 491 +201 ... 269 + -> block 496 val=0 inc=0 + block 496 +-1 ... -1 + block 489 +182 ... 193 + -> block 493 val=0 + -> block 492 val=1 + block 493 +195 ... 197 + -> block 491 val=0 inc=1 + block 492 +193 ... 194 + -> block 496 val=0 inc=2 + block 473 +29 ... 43 + -> block 477 val=0 + -> block 476 val=3 + block 477 +164 ... 166 + -> block 475 val=0 inc=3 + block 476 +43 ... 66 + -> block 481 val=0 + -> block 480 val=1 + block 481 +105 ... 107 + -> block 482 val=0 + block 482 +107 ... 161 + -> block 479 val=0 + block 479 +161 ... 163 + -> block 496 val=0 inc=6 + block 480 +66 ... 80 + -> block 484 val=0 + -> block 483 val=1 + block 484 +101 ... 103 + -> block 482 val=0 inc=1 + block 483 +80 ... 87 + -> block 487 val=0 + -> block 486 val=1 + block 487 +88 ... 97 + -> block 488 val=0 + block 488 +97 ... 100 + -> block 479 val=0 inc=2 + block 486 +87 ... 88 + -> block 488 val=0 inc=1 + block 470 +13 ... 14 + -> block 496 val=0 inc=10 + counts[0] = 0 + path: ENTRY +3 +15 +168 +170 +199 +201 EXIT + counts[1] = 0 + path: ENTRY +3 +15 +168 +170 +182 +195 +201 EXIT + counts[2] = 0 + path: ENTRY +3 +15 +168 +170 +182 +193 EXIT + counts[3] = 0 + path: ENTRY +3 +15 +29 +164 +170 +199 +201 EXIT + counts[4] = 0 + path: ENTRY +3 +15 +29 +164 +170 +182 +195 +201 EXIT + counts[5] = 0 + path: ENTRY +3 +15 +29 +164 +170 +182 +193 EXIT + counts[6] = 0 + path: ENTRY +3 +15 +29 +43 +105 +107 +161 EXIT + counts[7] = 0 + path: ENTRY +3 +15 +29 +43 +66 +101 +107 +161 EXIT + counts[8] = 0 + path: ENTRY +3 +15 +29 +43 +66 +80 +88 +97 +161 EXIT + counts[9] = 0 + path: ENTRY +3 +15 +29 +43 +66 +80 +87 +97 +161 EXIT + counts[10] = 0 + path: ENTRY +3 +13 EXIT +func #37 + block 526 +-1 ... -1 + -> block 513 val=0 + block 513 +3 ... 30 + -> block 515 val=0 + -> block 516 val=1 + -> block 517 val=2 + -> block 518 val=4 + -> block 519 val=5 + block 515 +188 ... 193 + -> block 514 val=0 + block 514 +193 ... 194 + -> block 527 val=0 inc=0 + block 527 +-1 ... -1 + block 516 +175 ... 188 + -> block 527 val=0 inc=1 + block 517 +106 ... 131 + -> block 524 val=0 + -> block 523 val=1 + block 524 +138 ... 163 + -> block 525 val=0 + block 525 +163 ... 175 + -> block 527 val=0 inc=2 + block 523 +131 ... 138 + -> block 525 val=0 inc=1 + block 518 +84 ... 106 + -> block 527 val=0 inc=4 + block 519 +30 ... 55 + -> block 521 val=0 + -> block 520 val=1 + block 521 +61 ... 83 + -> block 527 val=0 inc=5 + block 520 +55 ... 60 + -> block 527 val=0 inc=6 + counts[0] = 0 + path: ENTRY +3 +188 +193 EXIT + counts[1] = 0 + path: ENTRY +3 +175 EXIT + counts[2] = 0 + path: ENTRY +3 +106 +138 +163 EXIT + counts[3] = 0 + path: ENTRY +3 +106 +131 +163 EXIT + counts[4] = 0 + path: ENTRY +3 +84 EXIT + counts[5] = 0 + path: ENTRY +3 +30 +61 EXIT + counts[6] = 0 + path: ENTRY +3 +30 +55 EXIT +func #39 + block 546 +-1 ... -1 + -> block 535 val=0 + block 535 +3 ... 17 + -> block 538 val=0 + -> block 537 val=1 + block 538 +33 ... 46 + -> block 539 val=0 + block 539 +46 ... 56 + -> block 542 val=0 + -> block 540 val=1 + block 542 +57 ... 68 + -> block 545 val=0 + -> block 543 val=1 + block 545 +69 ... 84 + -> block 547 val=0 inc=0 + block 547 +-1 ... -1 + block 543 +68 ... 69 + block 540 +56 ... 57 + block 537 +17 ... 33 + -> block 539 val=0 inc=1 + counts[0] = 0 + path: ENTRY +3 +33 +46 +57 +69 EXIT + counts[1] = 0 + path: ENTRY +3 +17 +46 +57 +69 EXIT +func #42 + block 567 +-1 ... -1 + -> block 565 val=0 + block 565 +1 ... 3 + -> block 566 val=0 + block 566 +3 ... 4 + -> block 568 val=0 inc=0 + block 568 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +3 EXIT +func #43 + block 571 +-1 ... -1 + -> block 569 val=0 + block 569 +1 ... 9 + -> block 570 val=0 + block 570 +9 ... 10 + -> block 572 val=0 inc=0 + block 572 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +9 EXIT +func #44 + block 575 +-1 ... -1 + -> block 573 val=0 + block 573 +1 ... 5 + -> block 574 val=0 + block 574 +5 ... 6 + -> block 576 val=0 inc=0 + block 576 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #45 + block 579 +-1 ... -1 + -> block 577 val=0 + block 577 +1 ... 3 + -> block 578 val=0 + block 578 +3 ... 4 + -> block 580 val=0 inc=0 + block 580 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +3 EXIT +func #46 + block 583 +-1 ... -1 + -> block 581 val=0 + block 581 +1 ... 9 + -> block 582 val=0 + block 582 +9 ... 10 + -> block 584 val=0 inc=0 + block 584 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +9 EXIT +func #47 + block 587 +-1 ... -1 + -> block 585 val=0 + block 585 +1 ... 9 + -> block 586 val=0 + block 586 +9 ... 10 + -> block 588 val=0 inc=0 + block 588 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +9 EXIT +func #48 + block 591 +-1 ... -1 + -> block 589 val=0 + block 589 +1 ... 9 + -> block 590 val=0 + block 590 +9 ... 10 + -> block 592 val=0 inc=0 + block 592 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +9 EXIT +func #49 + block 595 +-1 ... -1 + -> block 593 val=0 + block 593 +1 ... 11 + -> block 594 val=0 + block 594 +11 ... 12 + -> block 596 val=0 inc=0 + block 596 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +11 EXIT +func #50 + block 599 +-1 ... -1 + -> block 597 val=0 + block 597 +1 ... 5 + -> block 598 val=0 + block 598 +5 ... 6 + -> block 600 val=0 inc=0 + block 600 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #51 + block 603 +-1 ... -1 + -> block 601 val=0 + block 601 +1 ... 5 + -> block 602 val=0 + block 602 +5 ... 6 + -> block 604 val=0 inc=0 + block 604 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #52 + block 607 +-1 ... -1 + -> block 605 val=0 + block 605 +1 ... 7 + -> block 606 val=0 + block 606 +7 ... 8 + -> block 608 val=0 inc=0 + block 608 +-1 ... -1 + caller #9 +468 + counts[0] = 1 + counts[0] = 1 + path: ENTRY +1 +7 EXIT +func #53 + block 611 +-1 ... -1 + -> block 609 val=0 + block 609 +1 ... 5 + -> block 610 val=0 + block 610 +5 ... 6 + -> block 612 val=0 inc=0 + block 612 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #54 + block 615 +-1 ... -1 + -> block 613 val=0 + block 613 +1 ... 7 + -> block 614 val=0 + block 614 +7 ... 8 + -> block 616 val=0 inc=0 + block 616 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +7 EXIT +func #55 + block 619 +-1 ... -1 + -> block 617 val=0 + block 617 +1 ... 5 + -> block 618 val=0 + block 618 +5 ... 6 + -> block 620 val=0 inc=0 + block 620 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #56 + block 623 +-1 ... -1 + -> block 621 val=0 + block 621 +1 ... 5 + -> block 622 val=0 + block 622 +5 ... 6 + -> block 624 val=0 inc=0 + block 624 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #57 + block 627 +-1 ... -1 + -> block 625 val=0 + block 625 +1 ... 5 + -> block 626 val=0 + block 626 +5 ... 6 + -> block 628 val=0 inc=0 + block 628 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #58 + block 631 +-1 ... -1 + -> block 629 val=0 + block 629 +1 ... 7 + -> block 630 val=0 + block 630 +7 ... 8 + -> block 632 val=0 inc=0 + block 632 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +7 EXIT +func #59 + block 635 +-1 ... -1 + -> block 633 val=0 + block 633 +1 ... 5 + -> block 634 val=0 + block 634 +5 ... 6 + -> block 636 val=0 inc=0 + block 636 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #60 + block 639 +-1 ... -1 + -> block 637 val=0 + block 637 +1 ... 5 + -> block 638 val=0 + block 638 +5 ... 6 + -> block 640 val=0 inc=0 + block 640 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #61 + block 643 +-1 ... -1 + -> block 641 val=0 + block 641 +1 ... 7 + -> block 642 val=0 + block 642 +7 ... 8 + -> block 644 val=0 inc=0 + block 644 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +7 EXIT +func #62 + block 647 +-1 ... -1 + -> block 645 val=0 + block 645 +1 ... 7 + -> block 646 val=0 + block 646 +7 ... 8 + -> block 648 val=0 inc=0 + block 648 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +7 EXIT +func #63 + block 651 +-1 ... -1 + -> block 649 val=0 + block 649 +1 ... 3 + -> block 650 val=0 + block 650 +3 ... 4 + -> block 652 val=0 inc=0 + block 652 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +3 EXIT +func #64 + block 655 +-1 ... -1 + -> block 653 val=0 + block 653 +1 ... 5 + -> block 654 val=0 + block 654 +5 ... 6 + -> block 656 val=0 inc=0 + block 656 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #65 + block 659 +-1 ... -1 + -> block 657 val=0 + block 657 +1 ... 7 + -> block 658 val=0 + block 658 +7 ... 8 + -> block 660 val=0 inc=0 + block 660 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +7 EXIT +func #66 + block 663 +-1 ... -1 + -> block 661 val=0 + block 661 +1 ... 7 + -> block 662 val=0 + block 662 +7 ... 8 + -> block 664 val=0 inc=0 + block 664 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +7 EXIT +func #67 + block 667 +-1 ... -1 + -> block 665 val=0 + block 665 +1 ... 5 + -> block 666 val=0 + block 666 +5 ... 6 + -> block 668 val=0 inc=0 + block 668 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #68 + block 671 +-1 ... -1 + -> block 669 val=0 + block 669 +1 ... 9 + -> block 670 val=0 + block 670 +9 ... 10 + -> block 672 val=0 inc=0 + block 672 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +9 EXIT +func #69 + block 675 +-1 ... -1 + -> block 673 val=0 + block 673 +1 ... 7 + -> block 674 val=0 + block 674 +7 ... 8 + -> block 676 val=0 inc=0 + block 676 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +7 EXIT +func #70 + block 679 +-1 ... -1 + -> block 677 val=0 + block 677 +1 ... 3 + -> block 678 val=0 + block 678 +3 ... 4 + -> block 680 val=0 inc=0 + block 680 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +3 EXIT +func #71 + block 683 +-1 ... -1 + -> block 681 val=0 + block 681 +1 ... 5 + -> block 682 val=0 + block 682 +5 ... 6 + -> block 684 val=0 inc=0 + block 684 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +5 EXIT +func #72 + block 687 +-1 ... -1 + -> block 685 val=0 + block 685 +1 ... 7 + -> block 686 val=0 + block 686 +7 ... 8 + -> block 688 val=0 inc=0 + block 688 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +7 EXIT +func #73 + block 691 +-1 ... -1 + -> block 689 val=0 + block 689 +1 ... 11 + -> block 690 val=0 + block 690 +11 ... 12 + -> block 692 val=0 inc=0 + block 692 +-1 ... -1 + counts[0] = 0 + path: ENTRY +1 +11 EXIT diff --git a/test/monitors/path_profile_br_outer.wasm b/test/monitors/path_profile_br_outer.wasm new file mode 100644 index 000000000..5bf0c08d2 Binary files /dev/null and b/test/monitors/path_profile_br_outer.wasm differ diff --git a/test/monitors/path_profile_br_outer.wat b/test/monitors/path_profile_br_outer.wat new file mode 100644 index 000000000..049ab931e --- /dev/null +++ b/test/monitors/path_profile_br_outer.wat @@ -0,0 +1,41 @@ +;; Branches to outer labels (depth > 0), including an unconditional `br` out of three levels. +(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 000000000..456df617d Binary files /dev/null and b/test/monitors/path_profile_dead_code.wasm differ diff --git a/test/monitors/path_profile_dead_code.wat b/test/monitors/path_profile_dead_code.wat new file mode 100644 index 000000000..aacca8daf --- /dev/null +++ b/test/monitors/path_profile_dead_code.wat @@ -0,0 +1,24 @@ +;; Dead code after an unconditional `br`, a body that only traps, and an else arm that traps. +(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 000000000..0972a95bf Binary files /dev/null and b/test/monitors/path_profile_dead_pred.wasm differ diff --git a/test/monitors/path_profile_dead_pred.wat b/test/monitors/path_profile_dead_pred.wat new file mode 100644 index 000000000..18837aefe --- /dev/null +++ b/test/monitors/path_profile_dead_pred.wat @@ -0,0 +1,33 @@ +;; Dead blocks that still own instructions: tails after both arms return, and after a `br` out. +(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 000000000..56bcd7ad3 Binary files /dev/null and b/test/monitors/path_profile_deep_nest.wasm differ diff --git a/test/monitors/path_profile_deep_nest.wat b/test/monitors/path_profile_deep_nest.wat new file mode 100644 index 000000000..724ff3614 --- /dev/null +++ b/test/monitors/path_profile_deep_nest.wat @@ -0,0 +1,24 @@ +;; if/else nested three levels deep, each in its parent's then arm. +(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 000000000..0e31b9464 Binary files /dev/null and b/test/monitors/path_profile_demo_only.wasm differ 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 000000000..cab47692a Binary files /dev/null and b/test/monitors/path_profile_diamond_only.wasm differ 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 000000000..39528e2ed Binary files /dev/null and b/test/monitors/path_profile_dup_labels.wasm differ diff --git a/test/monitors/path_profile_dup_labels.wat b/test/monitors/path_profile_dup_labels.wat new file mode 100644 index 000000000..fc91041fd --- /dev/null +++ b/test/monitors/path_profile_dup_labels.wat @@ -0,0 +1,17 @@ +(module + (func $f (export "f") (param $x i32) + (block $L1 + (block $L0 + (br_table $L0 $L0 $L1 $L0 $L1 (local.get $x)) + ) + (return) + ) + (return) + ) + (func (export "main") + (call $f (i32.const 0)) + (call $f (i32.const 1)) + (call $f (i32.const 2)) + (call $f (i32.const 3)) + (call $f (i32.const 4))) +) diff --git a/test/monitors/path_profile_empty_then.wasm b/test/monitors/path_profile_empty_then.wasm new file mode 100644 index 000000000..32e139a7f Binary files /dev/null and b/test/monitors/path_profile_empty_then.wasm differ 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_recur.wasm b/test/monitors/path_profile_freq_recur.wasm new file mode 100644 index 000000000..7b07a7135 Binary files /dev/null and b/test/monitors/path_profile_freq_recur.wasm differ diff --git a/test/monitors/path_profile_freq_recur.wat b/test/monitors/path_profile_freq_recur.wat new file mode 100644 index 000000000..004abaf62 --- /dev/null +++ b/test/monitors/path_profile_freq_recur.wat @@ -0,0 +1,18 @@ +;; Exact path counts in a recursive function, recursing up to 20 deep. +(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 000000000..e2fd50def Binary files /dev/null and b/test/monitors/path_profile_freq_shapes.wasm differ diff --git a/test/monitors/path_profile_freq_shapes.wat b/test/monitors/path_profile_freq_shapes.wat new file mode 100644 index 000000000..8d53695b0 --- /dev/null +++ b/test/monitors/path_profile_freq_shapes.wat @@ -0,0 +1,79 @@ +;; Exact path counts in one function mixing a br_table, branches to outer labels and a `return`. +(module + (func $g (export "g") (param $x i32) (result i32) (local $r i32) + (block $out + (block $d + (block $c2 + (block $c1 + (block $c0 + (br_table $c0 $c1 $c2 $d (local.get $x))) + (local.set $r (i32.const 10)) + (br $out)) ;; br to outer label, depth 3 + (local.set $r (i32.const 20)) + (return (i32.const 21))) ;; mid-body return + (local.set $r (i32.const 30)) + (br $out)) ;; br to outer label, depth 1 + (local.set $r (i32.const 40))) ;; default arm falls through + (local.get $r)) + (func (export "main") + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 0))) + (drop (call $g (i32.const 1))) + (drop (call $g (i32.const 1))) + (drop (call $g (i32.const 1))) + (drop (call $g (i32.const 1))) + (drop (call $g (i32.const 1))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 2))) + (drop (call $g (i32.const 3))) + (drop (call $g (i32.const 3))) + (drop (call $g (i32.const 77))) + (drop (call $g (i32.const 77))) + (drop (call $g (i32.const 77))) + (drop (call $g (i32.const 77))) + (drop (call $g (i32.const 77))) + (drop (call $g (i32.const 77))) + (drop (call $g (i32.const 77))) + (drop (call $g (i32.const 77))) + (drop (call $g (i32.const 77))) + (drop (call $g (i32.const 255))) + (drop (call $g (i32.const 255))) + (drop (call $g (i32.const 255))) + (drop (call $g (i32.const 255))) + )) diff --git a/test/monitors/path_profile_many_paths.wasm b/test/monitors/path_profile_many_paths.wasm new file mode 100644 index 000000000..0ba21fca1 Binary files /dev/null and b/test/monitors/path_profile_many_paths.wasm differ diff --git a/test/monitors/path_profile_many_paths.wat b/test/monitors/path_profile_many_paths.wat new file mode 100644 index 000000000..28f1a9e7c --- /dev/null +++ b/test/monitors/path_profile_many_paths.wat @@ -0,0 +1,55 @@ +;; 32 paths from five if/else in a row, each taken once, so the numbering must be compact and unique. +(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 000000000..cfa32f4df Binary files /dev/null and b/test/monitors/path_profile_mixed_nest.wasm differ diff --git a/test/monitors/path_profile_mixed_nest.wat b/test/monitors/path_profile_mixed_nest.wat new file mode 100644 index 000000000..bf2c093a2 --- /dev/null +++ b/test/monitors/path_profile_mixed_nest.wat @@ -0,0 +1,43 @@ +;; A br_table arm containing an if/else, and an if arm containing a br_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 000000000..334cc7a21 Binary files /dev/null and b/test/monitors/path_profile_one_armed.wasm differ 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 000000000..0f7817662 Binary files /dev/null and b/test/monitors/path_profile_recur.wasm differ 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 000000000..59fddbe95 Binary files /dev/null and b/test/monitors/path_profile_returns.wasm differ diff --git a/test/monitors/path_profile_returns.wat b/test/monitors/path_profile_returns.wat new file mode 100644 index 000000000..87c31ddb4 --- /dev/null +++ b/test/monitors/path_profile_returns.wat @@ -0,0 +1,22 @@ +;; `return` on several paths, and an if/else where only one arm returns. +(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 000000000..ff27c9040 Binary files /dev/null and b/test/monitors/path_profile_select_call.wasm differ diff --git a/test/monitors/path_profile_select_call.wat b/test/monitors/path_profile_select_call.wat new file mode 100644 index 000000000..ccf38c7f2 --- /dev/null +++ b/test/monitors/path_profile_select_call.wat @@ -0,0 +1,24 @@ +;; `select`, which is not a branch and so stays one path, and a br_if on a call's result. +(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_shapes.wasm b/test/monitors/path_profile_shapes.wasm new file mode 100644 index 000000000..d215d6b4e Binary files /dev/null and b/test/monitors/path_profile_shapes.wasm differ 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 000000000..f5f8dc3ad Binary files /dev/null and b/test/monitors/path_profile_straight.wasm differ diff --git a/test/monitors/path_profile_straight.wat b/test/monitors/path_profile_straight.wat new file mode 100644 index 000000000..e4a67936b --- /dev/null +++ b/test/monitors/path_profile_straight.wat @@ -0,0 +1,18 @@ +;; Straight-line functions, some making calls, each with exactly one path. +(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 000000000..86acb7952 Binary files /dev/null and b/test/monitors/path_profile_table1.wasm differ diff --git a/test/monitors/path_profile_table1.wat b/test/monitors/path_profile_table1.wat new file mode 100644 index 000000000..ca27acf0f --- /dev/null +++ b/test/monitors/path_profile_table1.wat @@ -0,0 +1,12 @@ +;; A br_table with only a default label, which is an unconditional branch. +(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 000000000..353a1f091 Binary files /dev/null and b/test/monitors/path_profile_table2.wasm differ diff --git a/test/monitors/path_profile_table2.wat b/test/monitors/path_profile_table2.wat new file mode 100644 index 000000000..6364c1803 --- /dev/null +++ b/test/monitors/path_profile_table2.wat @@ -0,0 +1,18 @@ +;; A br_table with one label plus the default, which selects by index rather than like a br_if. +(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 000000000..2584e6e65 Binary files /dev/null and b/test/monitors/path_profile_table_default.wasm differ diff --git a/test/monitors/path_profile_table_default.wat b/test/monitors/path_profile_table_default.wat new file mode 100644 index 000000000..e88e72720 --- /dev/null +++ b/test/monitors/path_profile_table_default.wat @@ -0,0 +1,19 @@ +;; A br_table where every index taken goes to the default, including out-of-range and negative ones. +(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 000000000..f3558c222 Binary files /dev/null and b/test/monitors/path_profile_trap.wasm differ 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 000000000..0cfa31d0e Binary files /dev/null and b/test/monitors/path_profile_wide_merge.wasm differ diff --git a/test/monitors/path_profile_wide_merge.wat b/test/monitors/path_profile_wide_merge.wat new file mode 100644 index 000000000..95650428b --- /dev/null +++ b/test/monitors/path_profile_wide_merge.wat @@ -0,0 +1,40 @@ +;; A br_table whose five arms all branch to one join block, which then continues. +(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 000000000..6dee70ca1 Binary files /dev/null and b/test/monitors/path_profile_wide_only.wasm differ diff --git a/test/monitors/path_profile_wide_only.wat b/test/monitors/path_profile_wide_only.wat new file mode 100644 index 000000000..38f1bc3e9 --- /dev/null +++ b/test/monitors/path_profile_wide_only.wat @@ -0,0 +1,32 @@ +(module + (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) + ) + (func (export "main") + (call $wide_switch (i32.const 0)) + (call $wide_switch (i32.const 1)) + (call $wide_switch (i32.const 2)) + (call $wide_switch (i32.const 3)) + (call $wide_switch (i32.const 4)) + (call $wide_switch (i32.const 5)) + (call $wide_switch (i32.const 6)) + (call $wide_switch (i32.const 7))) +) 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 diff --git a/test/unittest/ControlStackTest.v3 b/test/unittest/ControlStackTest.v3 index 64ceb21e1..9bd410a71 100644 --- a/test/unittest/ControlStackTest.v3 +++ b/test/unittest/ControlStackTest.v3 @@ -23,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; @@ -75,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) { @@ -120,6 +169,126 @@ def test_br_if0(t: ControlStackTester) { 3); } +def test_range_entry(t: ControlStackTester) { + t.addLocal(ValueType.I32); + 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) { t.code([ u8.!(Opcode.BLOCK.code), EMPTY,