llvm: expose target machine section options#38
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces LLVMGoCreateTargetMachineWithOptions to the Go LLVM bindings, enabling configuration of TargetOptions like function and data sections. The review identifies critical compilation issues where parameter names shadow LLVM namespaces and suggests using idiomatic wrap and unwrap helpers instead of reinterpret_cast for safer and more consistent C-binding management.
| static std::optional<Reloc::Model> mapRelocMode(LLVMRelocMode Reloc) { | ||
| switch (Reloc) { |
There was a problem hiding this comment.
The parameter name Reloc shadows the llvm::Reloc namespace, which will cause a compilation error when trying to access Reloc::Model or Reloc::Static. Rename the parameter to RelocMode to avoid this conflict.
| static std::optional<Reloc::Model> mapRelocMode(LLVMRelocMode Reloc) { | |
| switch (Reloc) { | |
| static std::optional<Reloc::Model> mapRelocMode(LLVMRelocMode RelocMode) { | |
| switch (RelocMode) { |
| const Target *TheTarget = reinterpret_cast<const Target *>(T); | ||
| if (TheTarget == nullptr) | ||
| return nullptr; |
There was a problem hiding this comment.
Use the idiomatic unwrap function provided by llvm/Support/CBindingWrapping.h instead of reinterpret_cast. It is also safer to check the input pointer T for null before unwrapping.
| const Target *TheTarget = reinterpret_cast<const Target *>(T); | |
| if (TheTarget == nullptr) | |
| return nullptr; | |
| if (!T) | |
| return nullptr; | |
| const Target *TheTarget = unwrap(T); |
| TargetOptions Options; | ||
| Options.FunctionSections = FunctionSections; | ||
| Options.DataSections = DataSections; | ||
| Options.UniqueSectionNames = UniqueSectionNames; |
There was a problem hiding this comment.
Null-check ordering / wasted work on error path
TargetOptions is fully constructed and its three fields written (lines 72–75) before the null-check on T (lines 77–79). TargetOptions default-constructs a non-trivial object (initialises ~30 fields in LLVM 18+). Move the null-check — and the reinterpret_cast — above the TargetOptions construction so the object is never allocated on the error path:
const Target *TheTarget = reinterpret_cast<const Target *>(T);
if (TheTarget == nullptr)
return nullptr;
TargetOptions Options;
Options.FunctionSections = FunctionSections;
Options.DataSections = DataSections;
Options.UniqueSectionNames = UniqueSectionNames;| LLVMBool FunctionSections, LLVMBool DataSections, | ||
| LLVMBool UniqueSectionNames) { | ||
| TargetOptions Options; | ||
| Options.FunctionSections = FunctionSections; |
There was a problem hiding this comment.
LLVMBool → bit-field assignment: consider normalising to 0/1 in C
LLVMBool is typedef int. TargetOptions::FunctionSections (and the other two) are bool / 1-bit unsigned fields depending on LLVM version. The Go caller always goes through boolToLLVMBool, which guarantees 0 or 1, so this is safe today. However, the C function itself accepts any int; a direct C/C++ caller passing 2 would silently produce 0 for a 1-bit field.
Consider normalising at the C layer to make the function defensively correct on its own:
Options.FunctionSections = !!FunctionSections;
Options.DataSections = !!DataSections;
Options.UniqueSectionNames = !!UniqueSectionNames;| return CodeModel::Large; | ||
| case LLVMCodeModelDefault: | ||
| case LLVMCodeModelJITDefault: | ||
| default: |
There was a problem hiding this comment.
LLVMCodeModelJITDefault silently treated as "no code model"
Both LLVMCodeModelDefault and LLVMCodeModelJITDefault return std::nullopt, giving LLVM its default code model. That is the same behaviour as the existing CreateTargetMachine, so it is not a regression — but the silent equivalence of a JIT-specific enum value is non-obvious. A brief inline comment here (and ideally a note in the Go doc comment) would prevent confusion:
case LLVMCodeModelJITDefault:
// JIT-specific model; let LLVM pick the target default, same as LLVMCodeModelDefault
return std::nullopt;| return | ||
| } | ||
|
|
||
| // CreateTargetMachineWithOptions creates a new TargetMachine with explicit |
There was a problem hiding this comment.
Doc comment slightly misleading
"Use CreateTargetMachine if you want LLVM's built-in default TargetOptions" implies the two functions differ in who supplies the defaults. The real distinction is that the options exposed here (FunctionSections, DataSections, UniqueSectionNames) are absent from the stable LLVM C API. A clearer phrasing:
Use
CreateTargetMachineif you do not need to overrideTargetOptionsfields that are absent from the LLVM C API.
143274b to
a111b86
Compare
Summary
TargetMachineOptionsTargetOptionsCreateTargetMachinebehavior unchanged and addCreateTargetMachineWithOptionsfor explicit section configurationTesting