Rough outline: LLVM source-based code coverage for ponyc #4848
Closed
SeanTAllen
started this conversation in
pony-cov
Replies: 1 comment
|
Replaced by #5536. That plan corrects a load-bearing error in this outline: wiring up |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Rough outline for adding code coverage support to ponyc via LLVM's source-based coverage instrumentation.
Approach
ponyc already uses LLVM 21.1.8 as its backend. LLVM has built-in source-based code coverage that inserts edge/block counters at the IR level and embeds source mapping into the binary. The infrastructure is already there — ponyc just needs to wire it up.
The alternative approaches (ptrace-based like kcov, hardware trace via Intel PT, source-level instrumentation) are all significantly more work and less capable. LLVM's coverage gives per-expression granularity, branch coverage, and ~5-15% runtime overhead.
What ponyc already has
Debug info generation (
src/libponyc/codegen/gendebug.cc,codegen.c): Full DWARF emission is enabled. DIBuilder is properly initialized, compile units are created, method/function debug info is generated, debug locations are set on instructions. This is the foundation LLVM's coverage mapping needs.Modern pass pipeline (
src/libponyc/codegen/genopt.cc): Uses LLVM'sPassBuilderwith extension point callbacks (registerOptimizerEarlyEPCallback,registerOptimizerLastEPCallback, etc.), all already using the LLVM 21ThinOrFullLTOPhaseparameter. Adding theInstrProfilingLoweringPassslots in naturally.Extensible compiler flags (
src/libponyc/pass/pass.h): Thepass_opt_tstruct already hasrelease,strip_debug,verify,lint_llvm— addingcoveragefits the existing pattern.Vendored compiler-rt source (
lib/llvm/src/compiler-rt/lib/profile/): The profiling runtime source is already in the tree. ponyc already builds CRT objects (crtbeginS.o, etc.) from the vendoredcompiler-rt/lib/builtins/viasrc/crt/CMakeLists.txt— the same approach can build the profiling runtime library.Embedded LLD linker (
src/libponyc/codegen/genexe.cc): On Linux and macOS, ponyc drives LLD directly as a library. Adding a-lclang_rt.profileor linking the profiling.ais straightforward — the link step already handles platform-specific CRT objects and library flags.What needs to change in ponyc
~6-7 files, estimated 50-150 lines of changes:
src/libponyc/pass/pass.h— Addbool coveragetopass_opt_tsrc/libponyc/options/options.c) — Add--coverageflag, following the pattern ofOPT_STRIP/OPT_LINT_LLVMsrc/libponyc/codegen/codegen.c— When coverage is enabled, ensure debug info module flags are set and potentially add coverage-specific module flagssrc/libponyc/codegen/genopt.cc— AddInstrProfilingLoweringPassvia the existingregisterOptimizerLastEPCallbackhook (see LLVM 21 API details below)src/libponyc/codegen/genopt.cc— When--coverageis active, preventstrip_debugfrom being force-set in release builds (coverage requires debug info to function)src/libponyc/codegen/genexe.cc— Link thecompiler-rtprofiling runtime. On Linux and macOS (embedded LLD paths), add the profiling library to the args. On Windows (COFF/MSVC path) and the external-linker fallback paths, add the equivalent flagssrc/crt/CMakeLists.txtor a newsrc/profile_rt/CMakeLists.txt) — Build the profiling runtime from the vendoredlib/llvm/src/compiler-rt/lib/profile/source, following the pattern already used for CRT objectsLLVM 21 API specifics
The key LLVM 21 types for the pass integration:
Alternatively, ponyc could pass
PGOOptions(fromllvm/Support/PGOOptions.h) to thePassBuilderconstructor withPGOAction::IRInstr, which is how clang drives it. The manualregisterOptimizerLastEPCallbackapproach gives more control and keeps the change minimal.Intended workflow
A standalone Pony tool could later wrap the reporting step to produce Pony-specific output, but
llvm-covhandles the heavy lifting out of the box.Open question: debug info under optimization
Lines 1039-1044 in
genopt.cccurrently force-strip debug info in release builds:This comment has been there for a while. Before proceeding, we should revalidate whether optimized builds with debug info are in fact still problematic. If the underlying issues have been fixed (or were fixed incidentally over time), coverage could work with optimized builds too. If not, coverage would be limited to debug builds — which is likely fine for test binaries anyway.
When
--coverageis active, the implementation must either skip thestrip_debug = trueoverride or error out if--coverageand--releaseare combined. Stripping debug info would destroy the coverage mapping data.What this gives us
What this doesn't cover (potential follow-up)
InstrProfOptions::Sampling, but it's not needed for the initial implementation)All reactions