From 0585d684dce1c33f240aa9aebc76700ad36cbd30 Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Sat, 23 Sep 2023 13:48:21 -0500 Subject: [PATCH] Add SsaCoverage --- aeneas/src/jvm/SsaJvmGen.v3 | 3 ++ aeneas/src/mach/MachBackend.v3 | 5 +- aeneas/src/main/Aeneas.v3 | 3 +- aeneas/src/main/Compiler.v3 | 2 + aeneas/src/main/Version.v3 | 2 +- aeneas/src/ssa/Ssa.v3 | 9 ++++ aeneas/src/ssa/SsaCoverage.v3 | 80 ++++++++++++++++++++++++++++++++ aeneas/src/ssa/SsaInterpreter.v3 | 4 ++ aeneas/src/ssa/SsaPrinter.v3 | 3 ++ aeneas/src/ssa/SsaRebuilder.v3 | 1 + aeneas/src/x86/X86CodeGen.v3 | 3 ++ 11 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 aeneas/src/ssa/SsaCoverage.v3 diff --git a/aeneas/src/jvm/SsaJvmGen.v3 b/aeneas/src/jvm/SsaJvmGen.v3 index 38f976192..22c0620ed 100644 --- a/aeneas/src/jvm/SsaJvmGen.v3 +++ b/aeneas/src/jvm/SsaJvmGen.v3 @@ -303,6 +303,9 @@ class SsaJvmGen(jprog: JvmProgram, context: SsaContext, jsig: JvmSig, code: JvmC x: SsaGoto => emitGotoOrFallThru(x.succs[0].dest); x: SsaSwitch => emitSwitch(block, x); x: SsaThrow => emitThrow(x.exception); + x: SsaNewVar => ; // do nothing + x: SsaDeleteVar => ; // do nothing + x: SsaProbe => ; // do nothing _ => context.fail("unexpected SSA end type"); } } diff --git a/aeneas/src/mach/MachBackend.v3 b/aeneas/src/mach/MachBackend.v3 index a930b2711..6ebfeaa99 100644 --- a/aeneas/src/mach/MachBackend.v3 +++ b/aeneas/src/mach/MachBackend.v3 @@ -424,8 +424,9 @@ class SsaMachGen(context: SsaContext, mach: MachProgram, regSet: MachRegSet, w: x: SsaSwitch => visitSwitch(block, x); x: SsaThrow => visitThrow(block, x); x: SsaCheckpoint => visitCheckpoint(x); - x: SsaNewVar => ; - x: SsaDeleteVar => ; + x: SsaNewVar => ; // do nothing + x: SsaDeleteVar => ; // do nothing + x: SsaProbe => ; // do nothing _ => context.fail("invalid instruction type"); } } diff --git a/aeneas/src/main/Aeneas.v3 b/aeneas/src/main/Aeneas.v3 index 225c0b657..6c0ee256c 100644 --- a/aeneas/src/main/Aeneas.v3 +++ b/aeneas/src/main/Aeneas.v3 @@ -87,7 +87,8 @@ component Aeneas { var compilation = Compilation.new(compiler, prog); var after: void -> void; if (CLOptions.COVERAGE.val != VstMatcher.None) { - after = IcCoverage.new(compiler, prog, CLOptions.COVERAGE.val, CLOptions.INSTR_COVERAGE.val).report; + if (CLOptions.SSA_INT.val) after = SsaCoverage.new(compiler, prog, CLOptions.COVERAGE.val, CLOptions.INSTR_COVERAGE.val).report; + else after = IcCoverage.new(compiler, prog, CLOptions.COVERAGE.val, CLOptions.INSTR_COVERAGE.val).report; } else if (CLOptions.INSTR_PROFILE.val != VstMatcher.None) { after = IcInstrProfiler.new(compiler, prog, CLOptions.INSTR_PROFILE.val).report; } else if (CLOptions.PROFILE.val != VstMatcher.None) { diff --git a/aeneas/src/main/Compiler.v3 b/aeneas/src/main/Compiler.v3 index bbe310e3a..267202752 100644 --- a/aeneas/src/main/Compiler.v3 +++ b/aeneas/src/main/Compiler.v3 @@ -69,6 +69,7 @@ class Compiler(target: Target) { def printOptMatcher = CLOptions.PRINT_OPT.get(); def useGlobalRegAllocMatcher = if(level >= 2, VstMatcher.All, CLOptions.USE_GLOBALREGALLOC.get()); var icMon: (IrSpec, IcMethod) -> void; // monitor for IC generation + var ssaMon: IrSpec -> void; // major phases of compilation var Trace = CLOptions.TRACE.get(); var TraceCalls = CLOptions.TRACE_CALLS.get(); @@ -322,6 +323,7 @@ class Compilation(compiler: Compiler, prog: Program) { opt.optGraph(); context.printSsa("Postpass Optimized"); } + if (compiler.ssaMon != null) compiler.ssaMon(memberRef); } return meth.ssa; } diff --git a/aeneas/src/main/Version.v3 b/aeneas/src/main/Version.v3 index 712376a2f..782856f63 100644 --- a/aeneas/src/main/Version.v3 +++ b/aeneas/src/main/Version.v3 @@ -3,6 +3,6 @@ // Updated by VCS scripts. DO NOT EDIT. component Version { - def version: string = "III-7.1653"; + def version: string = "III-7.1654"; var buildData: string; } diff --git a/aeneas/src/ssa/Ssa.v3 b/aeneas/src/ssa/Ssa.v3 index f915f0163..0a495d9f5 100644 --- a/aeneas/src/ssa/Ssa.v3 +++ b/aeneas/src/ssa/Ssa.v3 @@ -138,6 +138,15 @@ class SsaCheckpoint extends SsaInstr { new(source, a: Array) super(a) { } def getType() -> Type { return Void.TYPE; } } +// Support for instrumentation +class SsaProbe extends SsaInstr(Ssa.NO_INSTRS) { + def getType() -> Type { return Void.TYPE; } + def fire(i: SsaInterpreter) { } + def dup() -> SsaProbe { return SsaProbe.new(); } + def render(buf: StringBuilder) -> StringBuilder { + return buf.puts("probe"); + } +} // A new local variable class SsaNewVar extends SsaInstr { def name: string; diff --git a/aeneas/src/ssa/SsaCoverage.v3 b/aeneas/src/ssa/SsaCoverage.v3 new file mode 100644 index 000000000..2200d1dca --- /dev/null +++ b/aeneas/src/ssa/SsaCoverage.v3 @@ -0,0 +1,80 @@ +// Copyright 2023 Virgil authors. All rights reserved. +// See LICENSE for details of Apache 2.0 license. + +class SsaCoverage(compiler: Compiler, prog: Program, matcher: VstMatcher, instr: bool) { + var methList: List; + + new() { + compiler.ssaMon = onGen; + } + def onGen(memberRef: IrSpec) { + if (!matcher.matches(memberRef.source())) return; + var m = memberRef.asMethod(); + methList = List.new(m, methList); + for (b in m.ssa.bfBlocks(null)) { + var p = SsaCoverageProbe.new(); + var i = b.next; + while (i != b && SsaPhi.?(i)) i = i.next; + p.insertBefore(i); + } + } + def report() { + var results = Arrays.map(Lists.toArray(methList), count); + results = Arrays.sort(results, 0, results.length, cmp); + for (e in results) print(e); + } + def count(m: IrMethod) -> (IrMethod, int, int) { + var count = 0, executed = 0; + for (b in m.ssa.bfBlocks(null)) { + for (i = b.next; i != b; i = i.next) match (i) { + x: SsaCoverageProbe => { + count++; + if (x.executed) executed++; + } + } + } + return (m, executed, count); + } + def cmp(a: (IrMethod, int, int), b: (IrMethod, int, int)) -> bool { + var ap = double.!(a.1) / double.!(a.2); + var bp = double.!(b.1) / double.!(b.2); + return ap > bp; + } + def print(m: IrMethod, executed: int, count: int) { + var percent = (executed * 100) / count; + if (percent < 100) Terminal.sp(); + if (percent < 10) Terminal.sp(); + Terminal.cyan("%d", percent); + Terminal.put("% "); + if (percent < 100) Terminal.yellow("%q", m.renderLong); + else Terminal.green("%q", m.renderLong); + Terminal.ln(); +/*TODO if (percent < 100 && instr) { + var printer = IcPrinter.new(null); + printer.indent = 2; + for (i < m.iccode.length) { + var buf = TerminalBuffer.new(); + if (!wasExecuted(m, i)) buf.red(); + printer.render(buf, i, m.iccode[i]); + buf.end().outt(); + } + } +*/ + } +} + +class SsaCoverageProbe extends SsaProbe { + var executed = false; + + def fire(i: SsaInterpreter) { + executed = true; + } + def dup() -> SsaProbe { + var x = SsaCoverageProbe.new(); + x.executed = this.executed; + return x; + } + def render(buf: StringBuilder) -> StringBuilder { + return buf.put1("coverage-probe[executed=%z]", executed); + } +} diff --git a/aeneas/src/ssa/SsaInterpreter.v3 b/aeneas/src/ssa/SsaInterpreter.v3 index 6235e9e08..4c08cbb3c 100644 --- a/aeneas/src/ssa/SsaInterpreter.v3 +++ b/aeneas/src/ssa/SsaInterpreter.v3 @@ -94,6 +94,10 @@ class SsaInterpreter(prog: Program, genSsa: (IrSpec, int) -> SsaGraph) { x: SsaParam, x: SsaCheckpoint, x: SsaNewVar, x: SsaDeleteVar => { return nextInstr(i); } + x: SsaProbe => { + x.fire(this); + return nextInstr(i); + } } return fail("unexpected SsaInstr type"); } diff --git a/aeneas/src/ssa/SsaPrinter.v3 b/aeneas/src/ssa/SsaPrinter.v3 index fed407177..a391347d5 100644 --- a/aeneas/src/ssa/SsaPrinter.v3 +++ b/aeneas/src/ssa/SsaPrinter.v3 @@ -139,6 +139,9 @@ class SsaPrinter { x: SsaDeleteVar => { buf.put1("deleteVar %d", x.n); } + x: SsaProbe => { + x.render(buf); + } } if (args) { var args = i.inputs; diff --git a/aeneas/src/ssa/SsaRebuilder.v3 b/aeneas/src/ssa/SsaRebuilder.v3 index 61c274a44..4a5059116 100644 --- a/aeneas/src/ssa/SsaRebuilder.v3 +++ b/aeneas/src/ssa/SsaRebuilder.v3 @@ -47,6 +47,7 @@ class SsaRebuilder(context: SsaContext) { x: SsaCheckpoint => genCheckpoint(x); x: SsaNewVar => genNewVar(x); x: SsaDeleteVar => genDeleteVar(x); + x: SsaProbe => curBlock.append(x.dup()); _ => context.fail1("unexpected block end @%d", i.uid); } } diff --git a/aeneas/src/x86/X86CodeGen.v3 b/aeneas/src/x86/X86CodeGen.v3 index 070703476..f074a73d3 100644 --- a/aeneas/src/x86/X86CodeGen.v3 +++ b/aeneas/src/x86/X86CodeGen.v3 @@ -99,6 +99,9 @@ class X86CodeGen extends OldCodeGen { x: SsaGoto => genGoto(x.target()); x: SsaSwitch => genSwitch(x); x: SsaThrow => genThrow(x); + x: SsaNewVar => ; // do nothing + x: SsaDeleteVar => ; // do nothing + x: SsaProbe => ; // do nothing } v.end = code.length; if (v.varSize > 0) {