| name | c4cll-debug-flags |
| description | Use when working on c4cll frontend or backend debugging and you need to choose the right command-line flags for parser, sema, HIR, BIR, or codegen investigation. Covers parse-only, parser-debug channels, canonical dumps, HIR dumps, backend-route BIR observation, and practical command recipes for narrowing failures. |
c4cll Debug Flags
Use this skill when the task is to investigate frontend or backend behavior
through c4cll command-line flags instead of immediately patching the code.
Pipeline Map
C/C++ -> HIR -> LIR -> BIR -> MIR -> assembly
HIR: frontend semantic stage; owns template instantiation and Sema-like work
LIR: LLVM IR route; inspect with --codegen llvm
BIR: backend IR for CFG, phi, legalization, register allocation, and preparation
MIR: machine-lowering route after BIR
assembly: final backend-native text from --codegen asm
Current limitation:
- there is no standalone
--dump-lir
- use
--codegen llvm for LLVM IR
--dump-bir, --dump-prepared-bir, --dump-mir, and --trace-mir are
backend-native evidence, not LLVM-path evidence
Frontend Debug
Quick selection
- Parser shape / syntax issue:
- Parser control-flow / backtracking issue:
- add
--parser-debug
- add
--parser-debug-tentative for speculative-parse visibility
- add
--parser-debug-injected when token injection/swap might be involved
- Sema / canonical type issue:
- HIR lowering / compile-time behavior:
- use
--dump-hir-summary
- escalate to
--dump-hir when full detail is needed
- LLVM path output is wrong:
Command recipes
Basic parser check:
./build/c4cll --parse-only <file>
Parser debug:
./build/c4cll --parser-debug --parse-only <file>
Parser debug with speculative branches:
./build/c4cll --parser-debug --parser-debug-tentative --parse-only <file>
Parser debug with injected-token events:
./build/c4cll --parser-debug --parser-debug-injected --parse-only <file>
Full parser debug for hard parser cases:
./build/c4cll --parser-debug --parser-debug-tentative --parser-debug-injected --parse-only <file>
Canonical semantic view:
./build/c4cll --dump-canonical <file>
Compact HIR view:
./build/c4cll --dump-hir-summary <file>
Full HIR plus compile-time/materialization stats:
./build/c4cll --dump-hir <file>
Investigation workflow
- Start with the narrowest mode that answers the current question.
- Prefer
--parse-only before --dump-hir when the failure may still be in parser land.
- If parser debug output is noisy, rerun with a reduced testcase before adding more channels.
- If parse succeeds but later stages fail, switch to
--dump-canonical or --dump-hir-summary.
- Use full
--dump-hir only when summary output is insufficient.
Practical heuristics
- If the testcase hangs or runs very slowly during parse:
- use
--parser-debug first to see progress heartbeats
- If a failure involves nested templates or dependent names:
- include
--parser-debug-tentative
- If the code path is known to inject synthetic tokens:
- include
--parser-debug-injected
- If a bug report is about "wrong type" rather than "won't parse":
- If a bug report is about template lowering, deferred consteval, or NTTP materialization:
- prefer
--dump-hir-summary, then --dump-hir
Constraints
--pp-only, --lex-only, --parse-only, --dump-canonical, --dump-hir,
and --dump-hir-summary are mutually exclusive frontend inspection modes.
- Parser debug flags are most useful with
--parse-only, but can also help when
earlier frontend stages need observation before later failures.
Examples for current repo
STL parser bring-up:
./build/c4cll --parser-debug --parser-debug-tentative --parse-only tests/cpp/std/std_vector_simple.cpp
EASTL parser bring-up:
./build/c4cll --parser-debug --parser-debug-tentative --parser-debug-injected --parse-only tests/cpp/eastl/eastl_vector_simple.cpp
Known parser-debug negative testcase:
./build/c4cll --parser-debug --parser-debug-tentative --parse-only tests/cpp/internal/negative_case/parser_debug_qualified_type_template_arg_stack.cpp
Backend Debug
Quick selection
- Backend route / semantic BIR investigation:
- Backend CFG / phi / legalization / regalloc prep is wrong:
- Machine lowering route is wrong:
- use
--dump-mir, then --trace-mir
- Final native output is wrong:
- use
--codegen asm
- for the landed AArch64
.s route, use
c4cll --codegen asm --target aarch64-linux-gnu input.c -o out.s
- compare with
--codegen llvm only to separate LLVM-path output from
backend-native output
Command recipes
Semantic BIR:
./build/c4cll --dump-bir --target <triple> <file>
Prepared BIR:
./build/c4cll --dump-prepared-bir --target <triple> <file>
./build/c4cll --dump-prepared-bir --target <triple> --mir-focus-function <function> <file>
MIR route:
./build/c4cll --dump-mir --target <triple> <file>
./build/c4cll --trace-mir --target <triple> <file>
Backend route versus LLVM route:
./build/c4cll --codegen asm --target <triple> <file> -o /tmp/backend.s
./build/c4cll --codegen llvm --target <triple> <file> -o /tmp/llvm.ll
AArch64 backend-native assembly output:
c4cll --codegen asm --target aarch64-linux-gnu input.c -o out.s
Repo-local build form:
./build/c4cll --codegen asm --target aarch64-linux-gnu tests/backend/case/aarch64_return_zero_smoke.c -o /tmp/return_zero.s
External AArch64 assembler/link smoke, matching the CMake helper boundary:
./build/c4cll --codegen asm --target aarch64-linux-gnu tests/backend/case/aarch64_return_zero_smoke.c -o /tmp/return_zero.s
aarch64-linux-gnu-as /tmp/return_zero.s -o /tmp/return_zero.o
clang --target=aarch64-linux-gnu /tmp/return_zero.o -o /tmp/return_zero
The CMake smoke helper performs the same split: c4cll emits .s, then the
external assembler and clang link path prove object/link validity. It only
runs the linked binary when the host architecture can execute AArch64 output.
Force semantic BIR through the asm route:
./build/c4cll --codegen asm --backend-bir-stage semantic --target <triple> <file>
Investigation workflow
- First ask whether the question is about semantic BIR, prepared BIR, MIR
route selection, or final machine asm.
- Use
--dump-bir to verify semantic lowering first.
- Use
--dump-prepared-bir to verify backend-facing authority next.
- Use
--dump-mir or --trace-mir only after prepared BIR no longer answers
the ownership or route question.
- Use
--codegen asm when the question is final backend-native .s text from
the machine-node printer, not internal IR ownership.
- Use
--codegen llvm only as the comparison surface, not as proof that the
backend route is healthy.
Prepared BIR contract sections
Current --dump-prepared-bir output is organized into ownership-oriented
sections:
--- prepared-control-flow ---
--- prepared-value-locations ---
--- prepared-stack-layout ---
--- prepared-frame-plan ---
--- prepared-dynamic-stack-plan ---
--- prepared-call-plans ---
--- prepared-storage-plans ---
--- prepared-addressing ---
Reading rule:
- use
--dump-bir to verify semantic lowering first
- use
--dump-prepared-bir to verify backend-facing authority next
- do not treat
--dump-mir or --trace-mir as the source of truth for
stack, frame, or call ownership if the prepared dump already answers it
Practical heuristics
- If the question is "did this testcase reach BIR yet?":
- If the question is "did this route stay on backend lowering or bounce back to
LLVM-ish text?":
- compare
--codegen asm and --codegen llvm
- If the question is target-specific:
- rerun the same testcase with the exact target triple you care about
- If the question is route drift:
- keep one known-good semantic BIR testcase nearby as a sentinel
Constraints
--pp-only, --lex-only, --parse-only, --dump-canonical, --dump-hir,
--dump-hir-summary, --dump-bir, --dump-prepared-bir, --dump-mir,
and --trace-mir are mutually exclusive inspection modes.
--backend-bir-stage is for --codegen asm only; do not combine it with
--dump-bir, --dump-prepared-bir, --dump-mir, or --trace-mir.
--dump-mir and --trace-mir are route-level MIR visibility, not a full
printed target-local MIR graph.
--dump-bir shows semantic backend IR; --dump-prepared-bir shows
backend-facing ownership and preparation; --dump-mir and --trace-mir
show machine-lowering route visibility. None of those are the emitted .s
text contract.
--codegen asm --target aarch64-linux-gnu input.c -o out.s emits
backend-native assembly text from selected machine nodes. It is printer
output, not an internal assembler, object encoder, linker, or semantic bridge
that parses .s back into BIR, prepared BIR, MIR, or backend semantics.
--codegen llvm is LLVM-route IR output and should not be described as the
source of AArch64 backend-native .s output.
Examples for current repo
Minimal BIR route check:
./build/c4cll --dump-bir --target x86_64-unknown-linux-gnu tests/c/internal/backend_case/branch_if_eq.c
Prepared BIR route check:
./build/c4cll --dump-prepared-bir --target x86_64-unknown-linux-gnu tests/c/internal/backend_case/branch_if_eq.c
Backend route compare on the same testcase:
./build/c4cll --codegen asm --target riscv64-unknown-linux-gnu tests/c/internal/backend_case/call_helper.c -o /tmp/call_helper_backend.s
./build/c4cll --codegen llvm --target riscv64-unknown-linux-gnu tests/c/internal/backend_case/call_helper.c -o /tmp/call_helper_llvm.ll
AArch64 .s emission and external smoke:
./build/c4cll --codegen asm --target aarch64-linux-gnu tests/backend/case/aarch64_return_zero_smoke.c -o /tmp/return_zero.s
aarch64-linux-gnu-as /tmp/return_zero.s -o /tmp/return_zero.o
clang --target=aarch64-linux-gnu /tmp/return_zero.o -o /tmp/return_zero