perf(lsp): C/C++ Tier 2 — pre-built per-language cross-LSP registry

Brings C/C++/CUDA to Go's optimization level. Extracts the cross-LSP
registration loop into c_register_lsp_defs (shared by per-file and
Tier 2 paths), adds:
  cbm_c_build_cross_registry  — builds ONE project-wide registry from
    all C-family defs (filters by lang) + both C and C++ stdlibs,
    finalized, shared READ-ONLY across resolve workers.
  cbm_run_c_lsp_cross_with_registry — per-file resolve using the
    shared registry; no per-file registry rebuild.

C cross-LSP is DEF-DRIVEN (fields/returns/embeds come from the def
strings, no per-file AST field collection), so the shared registry
produces identical resolution to the per-file build — zero quality
loss. resolve_worker dispatches CBM_LANG_C/CPP/CUDA to the new path;
pipeline.c builds cross_registries.c; pass_lsp_cross.h includes c_lsp.h.

postgres bench (4,967 files, ADVANCED mode): 628 C files share one
pre-built registry (lsp_cross_prepare=17ms, zero slow files), 44 LSP
overrides, +9 edges vs FULL. All 200 test_c_lsp tests pass. No
SEGV/ASan. Total pipeline overhead vs FULL: +5.2%.
This commit is contained in:
Martin Vogel
2026-05-26 16:49:12 +02:00
parent b61861eb5a
commit ef054ebd4e
4 changed files with 123 additions and 25 deletions
+93 -22
View File
@@ -4554,26 +4554,13 @@ void cbm_run_c_lsp(CBMArena* arena, CBMFileResult* result,
// Entry point: cross-file LSP
// ============================================================================
void cbm_run_c_lsp_cross(
CBMArena* arena,
const char* source, int source_len,
const char* module_qn,
bool cpp_mode,
CBMLSPDef* defs, int def_count,
const char** include_paths, const char** include_ns_qns, int include_count,
TSTree* cached_tree,
CBMResolvedCallArray* out) {
if (!source || source_len == 0 || !out) return;
CBMTypeRegistry reg;
cbm_registry_init(&reg, arena);
// Register stdlib
cbm_c_stdlib_register(&reg, arena);
if (cpp_mode) cbm_cpp_stdlib_register(&reg, arena);
// Register all defs
/* Register one batch of CBMLSPDef[] into a registry. Shared by the
* per-file cross-LSP path and the Tier 2 pre-built registry builder.
* Reads field/return/embedded info from the def strings (def-driven —
* no per-file AST mutation), so the same defs always yield the same
* registry entries regardless of which file is being processed. */
static void c_register_lsp_defs(CBMArena* arena, CBMTypeRegistry* reg,
const char* module_qn, CBMLSPDef* defs, int def_count) {
for (int i = 0; i < def_count; i++) {
CBMLSPDef* d = &defs[i];
if (!d->qualified_name || !d->short_name) continue;
@@ -4642,7 +4629,7 @@ void cbm_run_c_lsp_cross(
}
}
cbm_registry_add_type(&reg, rt);
cbm_registry_add_type(reg, rt);
}
if (d->label && (strcmp(d->label, "Function") == 0 || strcmp(d->label, "Method") == 0)) {
@@ -4681,9 +4668,93 @@ void cbm_run_c_lsp_cross(
rf.receiver_type = d->receiver_type; /* borrowed */
}
cbm_registry_add_func(&reg, rf);
cbm_registry_add_func(reg, rf);
}
}
}
/* Tier 2: build a project-wide C/C++/CUDA registry ONCE from all defs.
* Registers both C and C++ stdlibs (C is a subset; harmless overlap)
* and all C-family defs. Shared READ-ONLY across resolve workers.
* Def-driven (no AST field collection) so produces identical entries
* to the per-file build — zero quality loss. */
CBMTypeRegistry* cbm_c_build_cross_registry(CBMArena* arena, CBMLSPDef* defs, int def_count) {
if (!arena) return NULL;
CBMTypeRegistry* reg = (CBMTypeRegistry*)cbm_arena_alloc(arena, sizeof(*reg));
if (!reg) return NULL;
cbm_registry_init(reg, arena);
cbm_c_stdlib_register(reg, arena);
cbm_cpp_stdlib_register(reg, arena);
for (int i = 0; i < def_count; i++) {
CBMLSPDef* d = &defs[i];
if (d->lang != CBM_LANG_C && d->lang != CBM_LANG_CPP && d->lang != CBM_LANG_CUDA) {
continue;
}
c_register_lsp_defs(arena, reg, "", d, 1);
}
cbm_registry_finalize(reg);
return reg;
}
void cbm_run_c_lsp_cross_with_registry(
CBMArena* arena,
const char* source, int source_len,
const char* module_qn,
bool cpp_mode,
CBMTypeRegistry* reg,
const char** include_paths, const char** include_ns_qns, int include_count,
TSTree* cached_tree,
CBMResolvedCallArray* out) {
if (!source || source_len == 0 || !out || !reg) return;
TSParser* parser = NULL;
TSTree* tree = cached_tree;
bool owns_tree = false;
if (!tree) {
parser = ts_parser_new();
if (!parser) return;
const TSLanguage* ts_lang = cpp_mode ? tree_sitter_cpp() : tree_sitter_c();
ts_parser_set_language(parser, ts_lang);
tree = ts_parser_parse_string(parser, NULL, source, source_len);
ts_parser_delete(parser);
owns_tree = true;
if (!tree) return;
}
TSNode root = ts_tree_root_node(tree);
CLSPContext ctx;
c_lsp_init(&ctx, arena, source, source_len, reg, module_qn, cpp_mode, out);
for (int i = 0; i < include_count; i++) {
c_lsp_add_include(&ctx, include_paths[i], include_ns_qns[i]);
}
c_lsp_process_file(&ctx, root);
if (owns_tree) {
ts_tree_delete(tree);
}
}
void cbm_run_c_lsp_cross(
CBMArena* arena,
const char* source, int source_len,
const char* module_qn,
bool cpp_mode,
CBMLSPDef* defs, int def_count,
const char** include_paths, const char** include_ns_qns, int include_count,
TSTree* cached_tree,
CBMResolvedCallArray* out) {
if (!source || source_len == 0 || !out) return;
CBMTypeRegistry reg;
cbm_registry_init(&reg, arena);
// Register stdlib
cbm_c_stdlib_register(&reg, arena);
if (cpp_mode) cbm_cpp_stdlib_register(&reg, arena);
// Register all defs (shared helper — def-driven)
c_register_lsp_defs(arena, &reg, module_qn, defs, def_count);
// Use cached tree if available, otherwise parse fresh
TSParser* parser = NULL;
+17
View File
@@ -114,6 +114,23 @@ void cbm_run_c_lsp_cross(
TSTree* cached_tree, // NULL = parse internally
CBMResolvedCallArray* out);
// Tier 2: build a project-wide C/C++/CUDA registry ONCE from all defs
// (filters by lang). Shared READ-ONLY across resolve workers. Def-driven
// (no AST field collection) → identical entries to the per-file build.
CBMTypeRegistry* cbm_c_build_cross_registry(CBMArena* arena, CBMLSPDef* defs, int def_count);
// Cross-file LSP using a pre-built shared registry (Tier 2). Skips the
// per-file registry build; just parse + resolve.
void cbm_run_c_lsp_cross_with_registry(
CBMArena* arena,
const char* source, int source_len,
const char* module_qn,
bool cpp_mode,
CBMTypeRegistry* reg, // pre-built, finalized, READ-ONLY
const char** include_paths, const char** include_ns_qns, int include_count,
TSTree* cached_tree, // NULL = parse internally
CBMResolvedCallArray* out);
// Register C stdlib types and functions into a registry.
void cbm_c_stdlib_register(CBMTypeRegistry* reg, CBMArena* arena);
+1
View File
@@ -31,6 +31,7 @@
* go_lsp.h was where the project-wide def descriptor landed first. */
#include "lsp/go_lsp.h"
#include "lsp/py_lsp.h" /* cbm_py_build_cross_registry / cbm_run_py_lsp_cross_with_registry */
#include "lsp/c_lsp.h" /* cbm_c_build_cross_registry / cbm_run_c_lsp_cross_with_registry */
#include "pipeline/pipeline_internal.h"
#include <stdbool.h>
+12 -3
View File
@@ -2052,9 +2052,18 @@ static void resolve_worker(int worker_id, void *ctx_ptr) {
result->cached_tree, &result->resolved_calls);
used_prebuilt = true;
break;
/* C/C++/CUDA, TS/JS/TSX, PHP, C# fall through to the
* per-file build path below until their _with_registry
* variants are added in follow-up commits. */
case CBM_LANG_C:
case CBM_LANG_CPP:
case CBM_LANG_CUDA:
cbm_run_c_lsp_cross_with_registry(
&result->arena, result->source, result->source_len,
def_module, (lang != CBM_LANG_C), prebuilt,
imp_keys, imp_vals, imp_count,
result->cached_tree, &result->resolved_calls);
used_prebuilt = true;
break;
/* TS/JS/TSX, PHP fall through to the per-file build
* path below until their _with_registry variants land. */
default:
break;
}