Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/goallc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ concurrency:
cancel-in-progress: true

env:
PINNED_LLVM_RELEASE: goallc-llvm23.1.0-20260813T171727Z
PINNED_LLVM_REVISION: 1d142acb4dab7262347b57ac341235913b210f40
PINNED_LLVM_RELEASE: goallc-llvm23.1.0-20260814T092516Z
PINNED_LLVM_REVISION: 3710742685729c697378ab13a456484e5693aba9

jobs:
llvm-payload:
Expand Down
14 changes: 10 additions & 4 deletions src/cmd/compile/internal/gc/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"cmd/compile/internal/liveness"
"cmd/compile/internal/objw"
"cmd/compile/internal/pgoir"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/staticinit"
"cmd/compile/internal/types"
Expand Down Expand Up @@ -66,10 +67,15 @@ func enqueueFunc(fn *ir.Func, symABIs *ssagen.SymABIs) {
// is ABI0, and only ABI0 assembly function can have a FUNCDATA
// reference to args_stackmap (see cmd/internal/obj/plist.go:Flushplist).
// So avoid introducing an args_stackmap if the func is not ABI0.
liveness.WriteFuncMap(fn, abiInfo)

x := ssagen.EmitArgInfo(fn, abiInfo)
objw.Global(x, int32(len(x.P)), obj.RODATA|obj.LOCAL)
argMap := liveness.WriteFuncMap(fn, abiInfo)

argInfo := ssagen.EmitArgInfo(fn, abiInfo)
objw.Global(argInfo, int32(len(argInfo.P)), obj.RODATA|obj.LOCAL)
if base.Flag.EnableLLVM && symABIs.HasDef(fn.Sym()) {
// Assembly FUNCDATA directives refer to these definitions from
// another archive member, so LLVM cannot discover their uses.
ssa.MarkGoObjDataReferencedOutsideLLVM(argMap, argInfo)
}
}
return
}
Expand Down
9 changes: 5 additions & 4 deletions src/cmd/compile/internal/liveness/plive.go
Original file line number Diff line number Diff line change
Expand Up @@ -1555,11 +1555,11 @@ func isfat(t *types.Type) bool {
}

// WriteFuncMap writes the pointer bitmaps for bodyless function fn's
// inputs and outputs as the value of symbol <fn>.args_stackmap.
// If fn has outputs, two bitmaps are written, otherwise just one.
func WriteFuncMap(fn *ir.Func, abiInfo *abi.ABIParamResultInfo) {
// inputs and outputs as the value of symbol <fn>.args_stackmap and returns
// that symbol. If fn has outputs, two bitmaps are written, otherwise just one.
func WriteFuncMap(fn *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
if ir.FuncName(fn) == "_" {
return
return nil
}
nptr := int(abiInfo.ArgWidth() / int64(types.PtrSize))
bv := bitvec.New(int32(nptr))
Expand Down Expand Up @@ -1592,6 +1592,7 @@ func WriteFuncMap(fn *ir.Func, abiInfo *abi.ABIParamResultInfo) {
}

objw.Global(lsym, int32(off), obj.RODATA|obj.LOCAL)
return lsym
}

// checkStackmapOverflow checks for potential overflow in runtime stackmap reading.
Expand Down
165 changes: 145 additions & 20 deletions src/cmd/compile/internal/ssa/llvmdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,35 @@ import (
"fmt"
"sort"
"strconv"
"strings"

"github.com/goallc/go-llvm"
)

// llvmGoObjReferenceName is the single naming boundary for undefined Go
// symbols in LLVM IR. Go's symbol model remains unchanged; the LLVM-only
// suffix tells the GoObj writer to serialize a surviving relocation through
// the predefined builtin index table.
func llvmGoObjReferenceName(s *obj.LSym) string {
if s == nil {
base.Fatalf("nil GoObj symbol reference")
}
if strings.Contains(s.Name, goobj.BuiltinSymbolSuffixPrefix) {
base.Fatalf("Go symbol name %q uses reserved LLVM builtin suffix", s.Name)
}
if base.Ctxt.Flag_linkshared {
return s.Name
}
if name, ok := goobj.BuiltinSymbolName(s.Name, int(s.ABI())); ok {
return name
}
// Linkname references currently retain their ordinary linker name. A
// runtime implementation may itself be linknamed while compiler-generated
// references to the same logical symbol still use the builtin table, so the
// builtin lookup above deliberately takes precedence over this attribute.
return s.Name
}

// emitGoObjImportMetadata carries the exact package path and linker
// fingerprint already decoded by the Go importer. LLVM must not rediscover
// either value from an importcfg file or a symbol-name prefix.
Expand All @@ -43,23 +68,16 @@ func emitGoObjCgoModuleAsm() {
CurrentModule.SetInlineAsm(".goobj.cgo " + strconv.Quote(string(data)) + "\n")
}

// attachGoObjSymbolRef attaches the part of an undefined Go symbol's identity
// that cannot be recovered from an LLVM relocation. The optimized relocation
// stream still decides which declarations become GoObj references; the
// attachment only supplies the imported package's symbol index or the builtin
// index when such a relocation survives.
// attachGoObjSymbolRef attaches the part of an undefined imported Go symbol's
// identity that cannot be recovered from an LLVM relocation. Builtin identity
// is carried by the declaration name instead.
func attachGoObjSymbolRef(value llvm.Value, s *obj.LSym) {
if value.IsNil() || s == nil {
base.Fatalf("invalid LLVM value in GoObj symbol reference")
}

if !base.Ctxt.Flag_linkshared && !s.IsLinkname() {
if idx := goobj.BuiltinIdx(s.Name, int(s.ABI())); idx >= 0 {
value.SetGlobalMetadata(GlobalCtxt.MDKindID("goobj.builtin"), GlobalCtxt.MDNode([]llvm.Metadata{
llvm.ConstInt(GlobalCtxt.Int32Type(), uint64(idx), false).ConstantAsMetadata(),
}))
return
}
if strings.Contains(value.Name(), goobj.BuiltinSymbolSuffixPrefix) {
return
}
// Linknamed symbols live in GoObj's non-package namespace even when the
// compiler learned about them through an imported package. Their export
Expand All @@ -86,15 +104,51 @@ func attachGoObjSymbolRef(value llvm.Value, s *obj.LSym) {
}))
}

// attachGoObjABISymbolRef is for LLVM-generated runtime calls that do not
func getOrInsertLLVMFunctionRef(s *obj.LSym, sig llvmFuncSignature, cc llvm.CallConv) llvm.Value {
if s == nil || llvmCallConv(s.ABI()) != cc {
base.Fatalf("invalid LLVM GoObj function reference")
}
value := getOrInsertLLVMFunction(llvmGoObjReferenceName(s), sig, cc)
attachGoObjSymbolRef(value, s)
return value
}

// getOrInsertLLVMABISymbolRef is for LLVM-generated runtime calls that do not
// carry an SSA AuxCall. Use the compiler's ABI-aware symbol table so their
// builtin/non-package classification stays identical to the native writer.
func attachGoObjABISymbolRef(value llvm.Value, name string, abi obj.ABI) {
func getOrInsertLLVMABISymbolRef(name string, abi obj.ABI, sig llvmFuncSignature, cc llvm.CallConv) llvm.Value {
s := base.Ctxt.LookupABI(name, abi)
if s == nil || s.Name != name || s.ABI() != abi {
base.Fatalf("invalid LLVM GoObj symbol model for %s", name)
}
attachGoObjSymbolRef(value, s)
return getOrInsertLLVMFunctionRef(s, sig, cc)
}

// emitLateGoObjBuiltinDeclarations emits only the declarations consumed by
// LLVM machine passes. Ordinary builtin declarations are created lazily from
// their exact SSA AuxCall signatures.
func emitLateGoObjBuiltinDeclarations() {
if base.Ctxt.Flag_linkshared {
return
}
voidSig := llvmFuncSignature{
Type: llvm.FunctionType(GlobalCtxt.VoidType(), nil, false),
ReturnType: GlobalCtxt.VoidType(),
ClosureContextIndex: -1,
}
for i := 0; i < goobj.NBuiltin(); i++ {
if !goobj.BuiltinIsLate(i) {
continue
}
name, abiValue := goobj.BuiltinName(i)
storageName, ok := goobj.BuiltinSymbolName(name, abiValue)
if !ok {
base.Fatalf("late LLVM runtime helper %s is absent from GoObj builtin table", name)
}
abi := obj.ABI(abiValue)
fn := getOrInsertLLVMFunction(storageName, voidSig, llvmCallConv(abi))
preserveGoObjMetadataValues(fn)
}
}

// LowerGoObjData lowers compiler-emitted linker data into LLVM globals. The
Expand Down Expand Up @@ -193,10 +247,38 @@ func LowerGoObjData() {
setGoObjKeepMetadata(g, s)
setGoObjGotypeMetadata(g, s)
setGoObjMarkerRelocMetadata(g, s)
if lowerer.externalRoots[s] {
// This definition is referenced from a different archive member, so
// its use is invisible to LLVM. Keep it present and distinct through
// GlobalDCE and ConstantMerge; Go linker reachability still decides
// whether the GoObj symbol survives in the final binary.
preserveGoObjMetadataValues(g)
}
}
for s := range lowerer.externalRoots {
if !lowerer.lowered[s] {
base.Fatalf("GoObj data referenced outside LLVM was not lowered: %s", s.Name)
}
}
emitGoObjCompilerUsed()
}

// MarkGoObjDataReferencedOutsideLLVM marks compiler data definitions whose
// references live in another archive member and therefore cannot participate
// in LLVM IR reachability. The definitions are kept only through object
// emission; the Go linker remains responsible for final reachability.
func MarkGoObjDataReferencedOutsideLLVM(syms ...*obj.LSym) {
if currentLLVMDataLowerer == nil {
base.Fatalf("marking external GoObj data before LLVM module initialization")
}
for _, s := range syms {
if s == nil {
base.Fatalf("marking nil GoObj data referenced outside LLVM")
}
currentLLVMDataLowerer.externalRoots[s] = true
}
}

// FinalizeGoObjSymbolMetadata carries native GoObj definition classes and
// package-local indices after NumberSyms has assigned them. LowerGoObjData runs
// first so imported-reference metadata retains the same pre-numbering
Expand Down Expand Up @@ -243,6 +325,10 @@ func setGoObjPackageSymbolIndexMetadata(value llvm.Value, s *obj.LSym) {
if value.IsNil() || s == nil || s.PkgIdx != goobj.PkgIdxSelf || !s.Indexed() || s.SymIdx < 0 {
base.Fatalf("invalid LLVM GoObj package symbol index")
}
// An early imported declaration can later become a local definition through
// compiler-generated data. Definitions use their package symbol index, so
// discard the stale imported-reference attachment.
value.EraseGlobalMetadata(GlobalCtxt.MDKindID("goobj.import"))
value.SetGlobalMetadata(GlobalCtxt.MDKindID(goObjSymbolIndexMD), GlobalCtxt.MDNode([]llvm.Metadata{
llvm.ConstInt(GlobalCtxt.Int32Type(), uint64(s.SymIdx), false).ConstantAsMetadata(),
}))
Expand Down Expand Up @@ -275,7 +361,14 @@ func llvmGoDataRef(s *obj.LSym) llvm.Value {
if s == nil {
base.Fatalf("nil Go data symbol in LLVM lowering")
}
if s.Type == objabi.STEXT || s.Type == objabi.STEXTFIPS || s.ABI() == obj.ABIInternal {
if llvmGoObjReferenceName(s) != s.Name {
return llvmExternalDataRef(s, nil)
}
// FuncPCABI0 carries an ABI0 LSym through OpAddr, but bodyless assembly
// functions still have the unresolved Sxxx kind here. Recover the semantic
// function identity from the front end before choosing an LLVM GlobalValue;
// ABI alone is insufficient because ordinary data symbols also use ABI0.
if llvmGoFunctionSymbol(s) {
data := map[*obj.LSym]bool(nil)
if currentLLVMDataLowerer != nil {
data = currentLLVMDataLowerer.data
Expand Down Expand Up @@ -325,6 +418,28 @@ func llvmGoDataRef(s *obj.LSym) llvm.Value {
return g
}

func llvmGoFunctionSymbol(s *obj.LSym) bool {
if s.Type == objabi.STEXT || s.Type == objabi.STEXTFIPS || s.ABI() == obj.ABIInternal {
return true
}
// Bodyless assembly declarations are initialized without setupTextLSym, so
// their LSym remains Sxxx. typecheck.Target.Funcs is the authoritative list
// of current-package function declarations and includes generated ABI
// wrappers before LLVM module initialization.
if typecheck.Target == nil {
return false
}
for _, fn := range typecheck.Target.Funcs {
if fn == nil || fn.Nname == nil || fn.Sym() == nil || fn.Sym().Name == "_" {
continue
}
if fn.LinksymABI(fn.ABI) == s {
return true
}
}
return false
}

func (l *llvmDataLowerer) globalName(s *obj.LSym) string {
if s.Name != "" {
return s.Name
Expand Down Expand Up @@ -459,8 +574,8 @@ func llvmExternalDataRef(s *obj.LSym, data map[*obj.LSym]bool) llvm.Value {
// at this point. Their ABI nevertheless identifies them as functions (for
// example runtime.memequal64 in an equality closure), so do not rely on
// STEXT alone here.
if s.Type == objabi.STEXT || s.Type == objabi.STEXTFIPS || s.ABI() == obj.ABIInternal {
storageName := llvmFunctionStorageName(s.Name, llvmCallConv(s.ABI()))
if llvmGoFunctionSymbol(s) {
storageName := llvmFunctionStorageName(llvmGoObjReferenceName(s), llvmCallConv(s.ABI()))
if f := CurrentModule.NamedFunction(storageName); !f.IsNil() {
attachGoObjSymbolRef(f, s)
return f
Expand All @@ -470,11 +585,12 @@ func llvmExternalDataRef(s *obj.LSym, data map[*obj.LSym]bool) llvm.Value {
attachGoObjSymbolRef(f, s)
return f
}
if g := CurrentModule.NamedGlobal(s.Name); !g.IsNil() {
storageName := llvmGoObjReferenceName(s)
if g := CurrentModule.NamedGlobal(storageName); !g.IsNil() {
attachGoObjSymbolRef(g, s)
return g
}
g := llvm.AddGlobal(CurrentModule, GlobalCtxt.Int8Type(), s.Name)
g := llvm.AddGlobal(CurrentModule, GlobalCtxt.Int8Type(), storageName)
attachGoObjSymbolRef(g, s)
return g
}
Expand Down Expand Up @@ -564,9 +680,18 @@ func setGoObjFunctionFlags(fn llvm.Value, s *obj.LSym) {
if s.ReflectMethod() {
flag |= goobj.SymFlagReflectMethod
}
if s.NoSplit() {
flag |= goobj.SymFlagNoSplit
}
if s.IsPkgInit() {
flag2 |= goobj.SymFlagPkgInit
}
if s.IsLinkname() || s.Name == "main.main" {
flag2 |= goobj.SymFlagLinkname
}
if s.IsLinknameStd() {
flag2 |= goobj.SymFlagLinknameStd
}
if s.ABIWrapper() {
flag2 |= goobj.SymFlagABIWrapper
}
Expand Down
2 changes: 2 additions & 0 deletions src/cmd/compile/internal/ssa/llvmtypeddata.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
type llvmDataLowerer struct {
data map[*obj.LSym]bool
roots map[*obj.LSym]bool
externalRoots map[*obj.LSym]bool
lowered map[*obj.LSym]bool
values map[*obj.LSym]llvm.Value
anonymousCount int
Expand All @@ -34,6 +35,7 @@ func newLLVMDataLowerer(data map[*obj.LSym]bool) *llvmDataLowerer {
return &llvmDataLowerer{
data: data,
roots: make(map[*obj.LSym]bool),
externalRoots: make(map[*obj.LSym]bool),
lowered: make(map[*obj.LSym]bool),
values: make(map[*obj.LSym]llvm.Value),
runtimeTypes: make(map[*types.Type]llvm.Type),
Expand Down
Loading
Loading