Repository navigation
Expose function cloning and local IR transformation utilities - #56
Conversation
There was a problem hiding this comment.
Review summary
This adds a clean, well-tested set of Go bindings for IR cloning and local transformation utilities missing from llvm-c. The wrappers correctly reuse existing helpers (boolToLLVMBool), follow the (receiver) (named return) style of ir.go, pair C.CString with defer C.free, and use length-carrying string accessors (StringRef(Name, Len) / C.GoStringN) rather than relying on NUL termination. Test coverage is strong: it verifies the original is unmodified, debug-info linkage names stay independent, external refs/attributes survive cloning, declaration clones keep attributes, round-trips tail-call kinds and inline-asm properties, and re-verifies the module.
The findings below are mostly minor. The most substantive is the unchecked unwrap<T>() casts at the cgo boundary; the rest are robustness/clarity notes.
Verified: the repo targets LLVM 14–22 (LLVM 22 is the default via build tags), so the InlineAsm::getAsmString() return-type change (LLVM 21+ returns StringRef by value) is relevant. I confirmed with the referenced upstream headers that the current const auto & binding is not a dangling-pointer bug — lifetime extension keeps it alive and .data() points into the InlineAsm's persistent storage in both regimes — but see the inline note on clarity.
Inline comments below.
Go clients cannot currently clone a function, retain a mandatory tail-call boundary, or clean up a specialized CFG through the bindings. Expose LLVM's existing cloning and local IR utilities so clients can implement transformations in Go without a custom pass.
The API copies functions with their attributes and remapped debug scopes, folds local instructions and terminators, removes unreachable blocks, reads/rebuilds inline assembly properties, converts metadata values, changes a subprogram's linker name, and looks up symbols of any global kind. Tail-call kinds distinguish
musttailfrom optional hints. The C++ layer uses generic LLVM APIs and contains no client transformation policy.Narrowing conversions validate null/wrong-kind inputs at the C boundary and return failures that Go reports as descriptive, recoverable panics. Unreachable-block removal requires a function definition; cloning still accepts declarations. Invalid tail-call enum values are rejected without mutation, and compile-time assertions keep the Go constants aligned with LLVM. Inline assembly strings explicitly borrow
InlineAsmstorage throughStringRefacross both the olderstd::string&and newerStringRefAPIs.AsMetadatadocuments and tests constant, argument, instruction, and MetadataAsValue inputs.Tests verify independent function/debug cloning, declaration attributes, PHI cleanup, external references, symbol lookup including aliases, metadata identity, inline-assembly flags and length-carrying strings, all four tail-call IR spellings, and recoverable invalid-input handling. The new invalid-input regression aborts in C++ on the prior commit and passes with the checked boundary. Full binding tests pass locally with LLVM 19 and 22 on macOS/arm64 and Linux/amd64; the remote matrix covers LLVM 14–22.
Needed for the Go implementation of xgo-dev/llgo#2748.