Merge pull request #1130 from visualfc/globals
link globals importpath.name=value
This commit is contained in:
@@ -19,6 +19,7 @@ package build
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"debug/macho"
|
"debug/macho"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
@@ -67,16 +68,18 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Goos string
|
Goos string
|
||||||
Goarch string
|
Goarch string
|
||||||
BinPath string
|
BinPath string
|
||||||
AppExt string // ".exe" on Windows, empty on Unix
|
AppExt string // ".exe" on Windows, empty on Unix
|
||||||
OutFile string // only valid for ModeBuild when len(pkgs) == 1
|
OutFile string // only valid for ModeBuild when len(pkgs) == 1
|
||||||
RunArgs []string // only valid for ModeRun
|
RunArgs []string // only valid for ModeRun
|
||||||
Mode Mode
|
Mode Mode
|
||||||
GenExpect bool // only valid for ModeCmpTest
|
GenExpect bool // only valid for ModeCmpTest
|
||||||
Verbose bool
|
Verbose bool
|
||||||
Tags string
|
Tags string
|
||||||
|
GlobalNames map[string][]string // pkg => names
|
||||||
|
GlobalDatas map[string]string // pkg.name => data
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultConf(mode Mode) *Config {
|
func NewDefaultConf(mode Mode) *Config {
|
||||||
@@ -266,9 +269,16 @@ func Do(args []string, conf *Config) ([]Package, error) {
|
|||||||
allPkgs := append([]*aPackage{}, pkgs...)
|
allPkgs := append([]*aPackage{}, pkgs...)
|
||||||
allPkgs = append(allPkgs, dpkg...)
|
allPkgs = append(allPkgs, dpkg...)
|
||||||
|
|
||||||
|
// update globals importpath.name=value
|
||||||
|
addGlobalString(conf, "runtime.defaultGOROOT="+runtime.GOROOT(), nil)
|
||||||
|
addGlobalString(conf, "runtime.buildVersion="+runtime.Version(), nil)
|
||||||
|
|
||||||
|
global, err := createGlobals(conf, ctx.prog, pkgs)
|
||||||
|
check(err)
|
||||||
|
|
||||||
for _, pkg := range initial {
|
for _, pkg := range initial {
|
||||||
if needLink(pkg, mode) {
|
if needLink(pkg, mode) {
|
||||||
linkMainPkg(ctx, pkg, allPkgs, conf, mode, verbose)
|
linkMainPkg(ctx, pkg, allPkgs, global, conf, mode, verbose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +439,62 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, conf *Config, mode Mode, verbose bool) {
|
var (
|
||||||
|
errXflags = errors.New("-X flag requires argument of the form importpath.name=value")
|
||||||
|
)
|
||||||
|
|
||||||
|
func addGlobalString(conf *Config, arg string, mainPkgs []string) {
|
||||||
|
eq := strings.Index(arg, "=")
|
||||||
|
dot := strings.LastIndex(arg[:eq+1], ".")
|
||||||
|
if eq < 0 || dot < 0 {
|
||||||
|
panic(errXflags)
|
||||||
|
}
|
||||||
|
pkg := arg[:dot]
|
||||||
|
pkgs := []string{pkg}
|
||||||
|
if pkg == "main" {
|
||||||
|
pkgs = mainPkgs
|
||||||
|
}
|
||||||
|
if conf.GlobalNames == nil {
|
||||||
|
conf.GlobalNames = make(map[string][]string)
|
||||||
|
}
|
||||||
|
if conf.GlobalDatas == nil {
|
||||||
|
conf.GlobalDatas = make(map[string]string)
|
||||||
|
}
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
name := pkg + arg[dot:eq]
|
||||||
|
value := arg[eq+1:]
|
||||||
|
if _, ok := conf.GlobalDatas[name]; !ok {
|
||||||
|
conf.GlobalNames[pkg] = append(conf.GlobalNames[pkg], name)
|
||||||
|
}
|
||||||
|
conf.GlobalDatas[name] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createGlobals(conf *Config, prog llssa.Program, pkgs []*aPackage) (llssa.Package, error) {
|
||||||
|
if len(conf.GlobalDatas) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
if pkg.ExportFile == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if names, ok := conf.GlobalNames[pkg.PkgPath]; ok {
|
||||||
|
err := pkg.LPkg.Undefined(names...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pkg.ExportFile = pkg.ExportFile + "-global.ll"
|
||||||
|
os.WriteFile(pkg.ExportFile, []byte(pkg.LPkg.String()), 0644)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global := prog.NewPackage("", "global")
|
||||||
|
for name, value := range conf.GlobalDatas {
|
||||||
|
global.AddGlobalString(name, value)
|
||||||
|
}
|
||||||
|
return global, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global llssa.Package, conf *Config, mode Mode, verbose bool) {
|
||||||
pkgPath := pkg.PkgPath
|
pkgPath := pkg.PkgPath
|
||||||
name := path.Base(pkgPath)
|
name := path.Base(pkgPath)
|
||||||
app := conf.OutFile
|
app := conf.OutFile
|
||||||
@@ -462,6 +527,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, conf *Co
|
|||||||
if p.ExportFile != "" && aPkg != nil { // skip packages that only contain declarations
|
if p.ExportFile != "" && aPkg != nil { // skip packages that only contain declarations
|
||||||
linkArgs = append(linkArgs, aPkg.LinkArgs...)
|
linkArgs = append(linkArgs, aPkg.LinkArgs...)
|
||||||
llFiles = append(llFiles, aPkg.LLFiles...)
|
llFiles = append(llFiles, aPkg.LLFiles...)
|
||||||
|
llFiles = append(llFiles, aPkg.ExportFile)
|
||||||
need1, need2 := isNeedRuntimeOrPyInit(ctx, p)
|
need1, need2 := isNeedRuntimeOrPyInit(ctx, p)
|
||||||
if !needRuntime {
|
if !needRuntime {
|
||||||
needRuntime = need1
|
needRuntime = need1
|
||||||
@@ -476,6 +542,12 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, conf *Co
|
|||||||
// defer os.Remove(entryLLFile)
|
// defer os.Remove(entryLLFile)
|
||||||
llFiles = append(llFiles, entryLLFile)
|
llFiles = append(llFiles, entryLLFile)
|
||||||
|
|
||||||
|
if global != nil {
|
||||||
|
export := pkg.ExportFile + "-global.ll"
|
||||||
|
os.WriteFile(export, []byte(global.String()), 0666)
|
||||||
|
llFiles = append(llFiles, export)
|
||||||
|
}
|
||||||
|
|
||||||
// add rpath and find libs
|
// add rpath and find libs
|
||||||
exargs := make([]string, 0, ctx.nLibdir<<1)
|
exargs := make([]string, 0, ctx.nLibdir<<1)
|
||||||
libs := make([]string, 0, ctx.nLibdir*3)
|
libs := make([]string, 0, ctx.nLibdir*3)
|
||||||
@@ -726,7 +798,6 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error {
|
|||||||
if pkg.ExportFile != "" {
|
if pkg.ExportFile != "" {
|
||||||
pkg.ExportFile += ".ll"
|
pkg.ExportFile += ".ll"
|
||||||
os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644)
|
os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644)
|
||||||
aPkg.LLFiles = append(aPkg.LLFiles, pkg.ExportFile)
|
|
||||||
if debugBuild || verbose {
|
if debugBuild || verbose {
|
||||||
fmt.Fprintf(os.Stderr, "==> Export %s: %s\n", aPkg.PkgPath, pkg.ExportFile)
|
fmt.Fprintf(os.Stderr, "==> Export %s: %s\n", aPkg.PkgPath, pkg.ExportFile)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,11 +31,16 @@ const (
|
|||||||
LLGoFiles = "_wrap/runtime.c"
|
LLGoFiles = "_wrap/runtime.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GOROOT returns the root of the Go tree. It uses the
|
var defaultGOROOT string // set by cmd/link
|
||||||
// GOROOT environment variable, if set at process start,
|
|
||||||
// or else the root used during the Go build.
|
|
||||||
func GOROOT() string {
|
func GOROOT() string {
|
||||||
return ""
|
return defaultGOROOT
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildVersion string
|
||||||
|
|
||||||
|
func Version() string {
|
||||||
|
return buildVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname c_maxprocs C.llgo_maxprocs
|
//go:linkname c_maxprocs C.llgo_maxprocs
|
||||||
|
|||||||
23
ssa/expr.go
23
ssa/expr.go
@@ -301,32 +301,11 @@ func (b Builder) CMalloc(n Expr) Expr {
|
|||||||
// Str returns a Go string constant expression.
|
// Str returns a Go string constant expression.
|
||||||
func (b Builder) Str(v string) Expr {
|
func (b Builder) Str(v string) Expr {
|
||||||
prog := b.Prog
|
prog := b.Prog
|
||||||
data := b.createGlobalStr(v)
|
data := b.Pkg.createGlobalStr(v)
|
||||||
size := llvm.ConstInt(prog.tyInt(), uint64(len(v)), false)
|
size := llvm.ConstInt(prog.tyInt(), uint64(len(v)), false)
|
||||||
return Expr{aggregateValue(b.impl, prog.rtString(), data, size), prog.String()}
|
return Expr{aggregateValue(b.impl, prog.rtString(), data, size), prog.String()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Builder) createGlobalStr(v string) (ret llvm.Value) {
|
|
||||||
if ret, ok := b.Pkg.strs[v]; ok {
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
prog := b.Prog
|
|
||||||
if v != "" {
|
|
||||||
typ := llvm.ArrayType(prog.tyInt8(), len(v))
|
|
||||||
global := llvm.AddGlobal(b.Pkg.mod, typ, "")
|
|
||||||
global.SetInitializer(b.Prog.ctx.ConstString(v, false))
|
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
|
||||||
global.SetGlobalConstant(true)
|
|
||||||
global.SetUnnamedAddr(true)
|
|
||||||
global.SetAlignment(1)
|
|
||||||
ret = llvm.ConstInBoundsGEP(typ, global, []llvm.Value{prog.Val(0).impl})
|
|
||||||
} else {
|
|
||||||
ret = llvm.ConstNull(prog.CStr().ll)
|
|
||||||
}
|
|
||||||
b.Pkg.strs[v] = ret
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsafeString(data *byte, size int) string
|
// unsafeString(data *byte, size int) string
|
||||||
func (b Builder) unsafeString(data, size llvm.Value) Expr {
|
func (b Builder) unsafeString(data, size llvm.Value) Expr {
|
||||||
prog := b.Prog
|
prog := b.Prog
|
||||||
|
|||||||
53
ssa/globals.go
Normal file
53
ssa/globals.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 The GoPlus Authors (goplus.org). All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/goplus/llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (pkg Package) AddGlobalString(name string, value string) {
|
||||||
|
prog := pkg.Prog
|
||||||
|
styp := prog.String()
|
||||||
|
data := pkg.createGlobalStr(value)
|
||||||
|
length := prog.IntVal(uint64(len(value)), prog.Uintptr())
|
||||||
|
cv := llvm.ConstNamedStruct(styp.ll, []llvm.Value{data, length.impl})
|
||||||
|
pkg.NewVarEx(name, prog.Pointer(styp)).Init(Expr{cv, styp})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undefined global string var by names
|
||||||
|
func (pkg Package) Undefined(names ...string) error {
|
||||||
|
prog := pkg.Prog
|
||||||
|
styp := prog.rtString()
|
||||||
|
for _, name := range names {
|
||||||
|
global := pkg.VarOf(name)
|
||||||
|
if global == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
typ := prog.Elem(global.Type)
|
||||||
|
if typ.ll != styp {
|
||||||
|
return fmt.Errorf("%s: not a var of type string (type:%v)", name, typ.RawType())
|
||||||
|
}
|
||||||
|
newGlobal := llvm.AddGlobal(pkg.mod, styp, "")
|
||||||
|
global.impl.ReplaceAllUsesWith(newGlobal)
|
||||||
|
global.impl.EraseFromParentAsGlobal()
|
||||||
|
newGlobal.SetName(name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -788,6 +788,27 @@ func (p Package) InitDebug(name, pkgPath string, positioner Positioner) {
|
|||||||
p.cu = p.di.createCompileUnit(name, pkgPath)
|
p.cu = p.di.createCompileUnit(name, pkgPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p Package) createGlobalStr(v string) (ret llvm.Value) {
|
||||||
|
if ret, ok := p.strs[v]; ok {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
prog := p.Prog
|
||||||
|
if v != "" {
|
||||||
|
typ := llvm.ArrayType(prog.tyInt8(), len(v))
|
||||||
|
global := llvm.AddGlobal(p.mod, typ, "")
|
||||||
|
global.SetInitializer(prog.ctx.ConstString(v, false))
|
||||||
|
global.SetLinkage(llvm.PrivateLinkage)
|
||||||
|
global.SetGlobalConstant(true)
|
||||||
|
global.SetUnnamedAddr(true)
|
||||||
|
global.SetAlignment(1)
|
||||||
|
ret = llvm.ConstInBoundsGEP(typ, global, []llvm.Value{prog.Val(0).impl})
|
||||||
|
} else {
|
||||||
|
ret = llvm.ConstNull(prog.CStr().ll)
|
||||||
|
}
|
||||||
|
p.strs[v] = ret
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -557,3 +557,65 @@ _llgo_0:
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGlobalStrings(t *testing.T) {
|
||||||
|
prog := NewProgram(nil)
|
||||||
|
prog.SetRuntime(func() *types.Package {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
imp := packages.NewImporter(fset)
|
||||||
|
pkg, _ := imp.Import(PkgRuntime)
|
||||||
|
return pkg
|
||||||
|
})
|
||||||
|
pkg := prog.NewPackage("bar", "foo/bar")
|
||||||
|
typ := types.NewPointer(types.Typ[types.String])
|
||||||
|
a := pkg.NewVar("foo/bar.a", typ, InGo)
|
||||||
|
if pkg.NewVar("foo/bar.a", typ, InGo) != a {
|
||||||
|
t.Fatal("NewVar(a) failed")
|
||||||
|
}
|
||||||
|
a.InitNil()
|
||||||
|
pkg.NewVarEx("foo/bar.a", prog.Type(typ, InGo))
|
||||||
|
b := pkg.NewVar("foo/bar.b", typ, InGo)
|
||||||
|
b.InitNil()
|
||||||
|
c := pkg.NewVar("foo/bar.c", types.NewPointer(types.Typ[types.Int]), InGo)
|
||||||
|
c.Init(prog.Val(100))
|
||||||
|
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||||
|
source_filename = "foo/bar"
|
||||||
|
|
||||||
|
%"github.com/goplus/llgo/runtime/internal/runtime.String" = type { ptr, i64 }
|
||||||
|
|
||||||
|
@"foo/bar.a" = global %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, align 8
|
||||||
|
@"foo/bar.b" = global %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, align 8
|
||||||
|
@"foo/bar.c" = global i64 100, align 8
|
||||||
|
`)
|
||||||
|
err := pkg.Undefined("foo/bar.a", "foo/bar.b")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pkg.Undefined("foo.bar.d")
|
||||||
|
err = pkg.Undefined("foo/bar.c")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("must err")
|
||||||
|
}
|
||||||
|
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||||
|
source_filename = "foo/bar"
|
||||||
|
|
||||||
|
%"github.com/goplus/llgo/runtime/internal/runtime.String" = type { ptr, i64 }
|
||||||
|
|
||||||
|
@"foo/bar.c" = global i64 100, align 8
|
||||||
|
@"foo/bar.a" = external global %"github.com/goplus/llgo/runtime/internal/runtime.String"
|
||||||
|
@"foo/bar.b" = external global %"github.com/goplus/llgo/runtime/internal/runtime.String"
|
||||||
|
`)
|
||||||
|
global := prog.NewPackage("", "global")
|
||||||
|
global.AddGlobalString("foo/bar.a", "1.0")
|
||||||
|
global.AddGlobalString("foo/bar.b", "info")
|
||||||
|
assertPkg(t, global, `; ModuleID = 'global'
|
||||||
|
source_filename = "global"
|
||||||
|
|
||||||
|
%"github.com/goplus/llgo/runtime/internal/runtime.String" = type { ptr, i64 }
|
||||||
|
|
||||||
|
@0 = private unnamed_addr constant [3 x i8] c"1.0", align 1
|
||||||
|
@"foo/bar.a" = global %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 3 }, align 8
|
||||||
|
@1 = private unnamed_addr constant [4 x i8] c"info", align 1
|
||||||
|
@"foo/bar.b" = global %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 4 }, align 8
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user