如何用clang -emit-llvm编译并保留未使用的C声明
I've run into a similar issue when building LLVM-based compilers that rely on external runtime type info, so here are several practical workarounds that solve your problem without manual LLVM IR writing or duplicate code:
1. Force Type Retention with __attribute__((used)) Placeholders
Clang discards unused type declarations because they're considered dead code. To trick it into keeping them, create a small companion file that references all your runtime types/functions with the __attribute__((used)) attribute (this tells Clang not to eliminate the symbol even if unused).
Example:
Create a runtime-placeholder.c file:
#include "runtime.h" // For struct types: reference via sizeof to force retention __attribute__((used)) const size_t __keep_MyStruct = sizeof(MyStruct); __attribute__((used)) const size_t __keep_AnotherStruct = sizeof(AnotherStruct); // For function declarations: reference via function pointer __attribute__((used)) void (*__keep_my_runtime_func)(int) = my_runtime_func; __attribute__((used)) char* (*__keep_string_alloc)(size_t) = string_alloc;
Compile this to bitcode with:
clang -c -emit-llvm runtime-placeholder.c -o runtime-decls.bc
The resulting bitcode will include all your struct and function declarations, since the placeholder variables force Clang to retain them. You can automate the generation of this placeholder file with a simple script (e.g., using clang -Xclang -ast-dump to parse your header and generate references automatically).
2. Generate Declarations Only with llvm-extract
If you don't mind writing minimal empty implementations for your runtime functions, this approach lets you extract just the declarations from a fully compiled bitcode file:
Step 1: Create a minimal runtime stub file
Create runtime-stubs.c with empty bodies for all runtime functions:
#include "runtime.h" // Empty stubs for all runtime functions (only to retain declarations) void my_runtime_func(int arg) {} char* string_alloc(size_t size) { return NULL; } MyStruct create_my_struct(int val) { return (MyStruct){.value = val}; }
Step 2: Compile to full bitcode
clang -c -emit-llvm runtime-stubs.c -o runtime-full.bc
Step 3: Extract only declarations
Use llvm-extract to strip out function bodies and keep just types/declarations:
llvm-extract --glob='*' --only-declarations runtime-full.bc -o runtime-decls.bc
The output runtime-decls.bc will have exactly what you need: all struct definitions and function declarations, no executable code. This is perfect if you want to avoid linking runtime function bodies into your compiler's output.
3. Direct Header Compilation with Clang CC1 Options
You can compile the header directly using Clang's low-level cc1 frontend, which has more granular control over declaration retention. Run:
clang -cc1 -emit-llvm -femit-all-decls -x c-header runtime.h -o runtime-decls.bc
Note that -femit-all-decls works differently in cc1 mode (it preserves more declarations than when using the regular clang driver). However, this might still miss some types in edge cases, so the placeholder or stub approaches are more reliable for complex runtimes.
Why Your Precompiled Header (.gch) Approach Failed
Precompiled headers are Clang's internal cached format, not standard LLVM bitcode. They contain additional metadata for fast compilation and can't be parsed directly with LLVMParseBitcodeInContext2—this is expected behavior, so you should abandon this route.
Bonus: Automated Maintenance for Large Runtimes
For large-scale runtimes, consider writing a script that parses your runtime.h (using tools like libclang or even simple regex for simpler cases) to auto-generate the placeholder or stub files. This eliminates the need to manually sync declarations between your C header and LLVM IR.
内容的提问来源于stack exchange,提问作者jayphelps

