mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:50:13 +01:00
`CARBON_CHECK` and `CARBON_FATAL` messages are formatted with `llvm::formatv`. Previously each check site that had a message instantiated its own copy of the formatv machinery -- a `formatv_object` over a tuple of per-argument format adapters, plus that tuple -- in every translation unit, keyed on the site's file, line, condition, and format strings. A translation unit with many checks paid for that machinery over and over. This restructures check failure so that the formatting machinery is compiled exactly once, and only a single small adapter is instantiated per distinct value type per translation unit: - `CheckFailImpl` (out-of-line) now takes the message's format string and an array of already-type-erased `format_adapter`s, and renders the whole failure message -- prefix plus the extra message -- directly into one stream. The extra message is rendered in place, so no separate string is ever materialized for it. - `FormatvInto` (in the `.cpp`) renders a format string over that adapter array. Rather than instantiate `llvm::formatv`, it drives the formatv replacement loop over the public `formatv_object_base::parseFormatString`, so this rendering code exists exactly once. (A TODO notes that we should add a type-erased entry point upstream in LLVM rather than reimplement the loop here.) - The lowering from the macro down to that out-of-line call is split so that the only per-check-site instantiation is trivial: - `CheckFail<...>` is instantiated once per site, since its file, line, condition, and format template-string parameters are unique to the site. It just lowers those compile-time strings to ordinary arguments and forwards to `CheckFailFormat`. - `CheckFailFormat<Ts...>` is instantiated once per distinct value-type sequence and shared across sites; it builds one type-erased adapter per value. - `CheckFailWithAdapters<Adapters...>` collects pointers to those adapters into an array and calls `CheckFailImpl`. It is a distinct function so the adapter temporaries stay alive while pointers to their base class are in flight. Format semantics, including runtime format-string validation, are unchanged, and the rendered message is byte-for-byte identical. For `DCHECK` in optimized builds the check is dead code; its arguments are now routed through a trivial `IgnoreDeadCheckArgs` no-op rather than `CheckFail`. This still type-checks the arguments so they cannot bitrot, without instantiating any formatting machinery for them and without provoking unused-variable warnings. Measured full-rebuild impact (353 first-party translation units, fastbuild): -112.6s CPU, -6.9% relative to trunk. Assisted-by: Claude