Analyzer Architecture
A technically substantive overview of how Precedes analyzes C/C++ source code. Every capability described here is confirmed in the source repository and its tests.
Analysis Pipeline
Precedes is a local command-line static performance analyzer. It analyzes source code locally and never executes user binaries. No code is uploaded.
Implementation Status
precedes optimize
Version 0.3.2 candidate. Toolchain: Clang/LLVM 21.1, GCC 15.2, clang-tidy. Pinned compiler versions.
Clang AST-Based Recognition
The analyzer operates over full Clang JSON AST dumps. Over 130 Python modules implement 49 rule files, each producing diagnostics in the PCPP-* namespace.
What the AST layer recognizes
- Loop constructs:
ForStmt,CXXForRangeStmt,WhileStmt,DoStmt - Function declarations:
FunctionDecl,CXXMethodDecl,CXXConstructorDecl,CXXDestructorDecl - Implicit casts, parenthesized expressions, cleanup objects, materialized temporaries
- Allocation patterns, vector reserve opportunities, STL antipatterns
Example: vector reserve detection
// Input
void fill(std::vector<int>& v, int n) {
for (int i = 0; i < n; ++i)
v.push_back(i);
}
// Precedes reports: PCPP-RESERVE-001
// "push_back in counted loop without prior reserve"
The diagnostic identifies a counted loop calling push_back on a vector that has no preceding reserve call with a matching bound. This is a static observation, not an automatic rewrite.
CFG and Control-Flow Reasoning
The semantic_flow module implements bounded abstract interpretation over Clang 21 CFGs. A native C++ LibTooling helper (semantic_flow.inc) extracts intraprocedural control-flow graphs; the Python layer runs reaching-value analysis and loop-region invariance proofs.
FunctionFlow — bounded abstract interpretation
Analysis budgets
| Parameter | Limit | Purpose |
|---|---|---|
MAX_STEPS | 20,000 | Abstract-interpretation steps before termination |
MAX_CELLS | 250,000 | Tracked abstract-value cells |
MAX_REGIONS | 32 | Concurrent loop-region analyses |
MAX_DEPTH | 12 | Nesting depth for region invariance |
Budget exhaustion is an explicit terminal state — it never silently proves invariance.
Source Call Graph and Function Indexing
The source_call_graph module builds a bounded, source-visible resolved call graph shared by effect-propagation and recursion rules.
Indexed declarations
FunctionDecl— free functionsCXXMethodDecl— member functionsCXXConversionDecl— conversion operatorsCXXConstructorDecl— constructors, indexed by type and signatureCXXDestructorDecl— destructors
Key mechanisms
previousDecl chains to join in-class and out-of-line definitions
VarDecl bindings for indirect-call resolution
None on overflow
Interprocedural Effect Propagation
The call_effects module computes may-effects of source-visible calls. "Unknown" is tracked separately from known effects — an unknown call never implies a pure body.
Effects model
Computation
A reverse worklist algorithm computes a finite may-effect fixed point. The analysis is bounded by a 4,096 global-object budget; exceeding it adds an explicit "global-object budget exhausted" unknown entry rather than silently dropping objects.
Access modes tracked per global object: read, write, and address-taken.
Source-Provable Indirect-Call Resolution
The call graph resolves indirect calls only when the source provably determines the target. This is a conservative, bounded analysis — no speculative devirtualization.
Resolution paths
DeclRefExpr— direct reference to a named function declaration- Const pointer/reference bindings — traced from
VarDeclinitializers - Member expressions — resolved to the referenced member declaration
- Function-to-pointer decay and LValue-to-RValue conversions
- Dereference operators on resolved pointers
- Bounded recursion through stable (const-qualified, non-addressable) bindings
Virtual dispatch through base-class pointers is explicitly tracked as uncertainty, not resolved optimistically. Unresolved calls propagate unknown effects through the entire transitive call chain.
C++ Object Lifetime Semantics
A native Clang LibTooling helper (call_associations.inc) extracts source identities and reachable automatic destruction from the existing CFG. The Python layer (proven_call_associations.py) validates these source facts for use by effect and rule analyses.
Tracked lifetime categories
| Category | Description |
|---|---|
dispatch | Resolved virtual/non-virtual call dispatch |
open_dispatch | Unresolved virtual dispatch (explicit uncertainty) |
destructor | Automatic variable destruction with in-loop tracking |
structural | Structural destruction with operation/block/ordinal |
temporary | Temporary object destruction |
construction_failure | C++ construction failure cleanup paths |
allocation | Allocator identity tracking (function/method target) |
deallocation | Deallocator identity tracking |
failure_cleanup | Subobject, array element, base, virtual base, delegated cleanup |
handler_region | Exception handler regions with owner/site/end |
destruction_unknown | Unavailable cleanup: structural, temporary, reference, array, automatic, delete |
Implicit allocation and deallocation calls (ImplicitAllocationCall, ImplicitDeallocationCall, ImplicitFailureDeallocationCall) are also tracked in the call graph. Failure-cleanup paths distinguish deallocation, array_element, member, base, virtual_base, and delegated_object conditions.
Loop Analysis
49 rule implementations cover a broad range of loop optimization opportunities. These are diagnostic rules — they identify optimization candidates, not apply transformations.
Rule categories
Induction variable analysis
Two specialized modules provide induction variable proofs:
- Direct loop induction — zero-start, strict-bound proof for pure direct-loop models. Produces
InductionProofwith declaration, start, bound, mutation escape, and post-loop observability. - Affine recurrence — bounded affine counter proof over the CFG backedge structure. Extends
FunctionFlowfor recurrence detection.
Native CLI
The standalone executable needs no Python installation or web server. It embeds trusted native helpers built with Clang 21 LibTooling.
precedes doctor # environment check
precedes check foo.cpp # single-file analysis
precedes screening -p build/compile_commands.json # project mode
precedes optimize --help # opt-in trusted optimization (experimental)
Commands
check
Single-file analysis. Accepts one UTF-8 .cpp or .c file up to 256 KiB. Outputs PCPP-* diagnostics.
screening
Project mode. Reads a local compilation database. Precedes never runs the project build or database compiler commands.
doctor
Checks installed compiler versions and environment.
optimize
Experimental Opt-in trusted optimization. Requires --allow-execution and --allow-model. Separate from static analysis.
Output
Use --format json for the versioned result envelope or --verbose for underlying evidence. Exit codes: 0 (no actionable findings), 1 (actionable findings), 2 (input/configuration errors), 3 (complete analyzer failure).
Known Limitations
Precedes is a bounded static analyzer, not a whole-program optimizing compiler.
Precedes