From ef054ebd4edc3fece92379b6b7ba4b3f2eb4f66f Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Tue, 26 May 2026 16:49:12 +0200 Subject: [PATCH] =?UTF-8?q?perf(lsp):=20C/C++=20Tier=202=20=E2=80=94=20pre?= =?UTF-8?q?-built=20per-language=20cross-LSP=20registry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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%. --- internal/cbm/lsp/c_lsp.c | 115 +++++++++++++++++++++++++++------- internal/cbm/lsp/c_lsp.h | 17 +++++ src/pipeline/pass_lsp_cross.h | 1 + src/pipeline/pass_parallel.c | 15 ++++- 4 files changed, 123 insertions(+), 25 deletions(-) diff --git a/internal/cbm/lsp/c_lsp.c b/internal/cbm/lsp/c_lsp.c index aa691582a..cd54fcad8 100644 --- a/internal/cbm/lsp/c_lsp.c +++ b/internal/cbm/lsp/c_lsp.c @@ -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(®, arena); - - // Register stdlib - cbm_c_stdlib_register(®, arena); - if (cpp_mode) cbm_cpp_stdlib_register(®, 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(®, 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(®, 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(®, arena); + + // Register stdlib + cbm_c_stdlib_register(®, arena); + if (cpp_mode) cbm_cpp_stdlib_register(®, arena); + + // Register all defs (shared helper — def-driven) + c_register_lsp_defs(arena, ®, module_qn, defs, def_count); // Use cached tree if available, otherwise parse fresh TSParser* parser = NULL; diff --git a/internal/cbm/lsp/c_lsp.h b/internal/cbm/lsp/c_lsp.h index 51f10b3e5..bba883d9a 100644 --- a/internal/cbm/lsp/c_lsp.h +++ b/internal/cbm/lsp/c_lsp.h @@ -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); diff --git a/src/pipeline/pass_lsp_cross.h b/src/pipeline/pass_lsp_cross.h index 06433fc07..e2fb5bccb 100644 --- a/src/pipeline/pass_lsp_cross.h +++ b/src/pipeline/pass_lsp_cross.h @@ -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 diff --git a/src/pipeline/pass_parallel.c b/src/pipeline/pass_parallel.c index 136fc30a3..3408ff60f 100644 --- a/src/pipeline/pass_parallel.c +++ b/src/pipeline/pass_parallel.c @@ -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; }