Merge pull request #333 from xushiwei/q

patch sync/atomic; typepatch fix (don't change types)
This commit is contained in:
xushiwei
2024-06-17 04:16:33 +08:00
committed by GitHub
11 changed files with 451 additions and 136 deletions

View File

@@ -208,6 +208,7 @@ Here are the Go packages that can be imported correctly:
* [unicode/utf16](https://pkg.go.dev/unicode/utf16) * [unicode/utf16](https://pkg.go.dev/unicode/utf16)
* [math/bits](https://pkg.go.dev/math/bits) * [math/bits](https://pkg.go.dev/math/bits)
* [math](https://pkg.go.dev/math) * [math](https://pkg.go.dev/math)
* [sync/atomic](https://pkg.go.dev/sync/atomic) (partially)
## How to install ## How to install

View File

@@ -17,6 +17,7 @@
package atomic package atomic
import ( import (
"unsafe"
_ "unsafe" _ "unsafe"
) )
@@ -24,48 +25,48 @@ const (
LLGoPackage = "decl" LLGoPackage = "decl"
) )
type integer interface { type valtype interface {
~int | ~uint | ~uintptr | ~int32 | ~uint32 | ~int64 | ~uint64 ~int | ~uint | ~uintptr | ~int32 | ~uint32 | ~int64 | ~uint64 | ~unsafe.Pointer
} }
// llgo:link Add llgo.atomicAdd // llgo:link Add llgo.atomicAdd
func Add[T integer](ptr *T, v T) T { return v } func Add[T valtype](ptr *T, v T) T { return v }
// llgo:link Sub llgo.atomicSub // llgo:link Sub llgo.atomicSub
func Sub[T integer](ptr *T, v T) T { return v } func Sub[T valtype](ptr *T, v T) T { return v }
// llgo:link And llgo.atomicAnd // llgo:link And llgo.atomicAnd
func And[T integer](ptr *T, v T) T { return v } func And[T valtype](ptr *T, v T) T { return v }
// llgo:link NotAnd llgo.atomicNand // llgo:link NotAnd llgo.atomicNand
func NotAnd[T integer](ptr *T, v T) T { return v } func NotAnd[T valtype](ptr *T, v T) T { return v }
// llgo:link Or llgo.atomicOr // llgo:link Or llgo.atomicOr
func Or[T integer](ptr *T, v T) T { return v } func Or[T valtype](ptr *T, v T) T { return v }
// llgo:link Xor llgo.atomicXor // llgo:link Xor llgo.atomicXor
func Xor[T integer](ptr *T, v T) T { return v } func Xor[T valtype](ptr *T, v T) T { return v }
// llgo:link Max llgo.atomicMax // llgo:link Max llgo.atomicMax
func Max[T integer](ptr *T, v T) T { return v } func Max[T valtype](ptr *T, v T) T { return v }
// llgo:link Min llgo.atomicMin // llgo:link Min llgo.atomicMin
func Min[T integer](ptr *T, v T) T { return v } func Min[T valtype](ptr *T, v T) T { return v }
// llgo:link UMax llgo.atomicUMax // llgo:link UMax llgo.atomicUMax
func UMax[T integer](ptr *T, v T) T { return v } func UMax[T valtype](ptr *T, v T) T { return v }
// llgo:link UMin llgo.atomicUMin // llgo:link UMin llgo.atomicUMin
func UMin[T integer](ptr *T, v T) T { return v } func UMin[T valtype](ptr *T, v T) T { return v }
// llgo:link Load llgo.atomicLoad // llgo:link Load llgo.atomicLoad
func Load[T integer](ptr *T) T { return *ptr } func Load[T valtype](ptr *T) T { return *ptr }
// llgo:link Store llgo.atomicStore // llgo:link Store llgo.atomicStore
func Store[T integer](ptr *T, v T) {} func Store[T valtype](ptr *T, v T) {}
// llgo:link Exchange llgo.atomicXchg // llgo:link Exchange llgo.atomicXchg
func Exchange[T integer](ptr *T, v T) T { return v } func Exchange[T valtype](ptr *T, v T) T { return v }
// llgo:link CompareAndExchange llgo.atomicCmpXchg // llgo:link CompareAndExchange llgo.atomicCmpXchg
func CompareAndExchange[T integer](ptr *T, old, new T) (T, bool) { return old, false } func CompareAndExchange[T valtype](ptr *T, old, new T) (T, bool) { return old, false }

View File

@@ -5,6 +5,20 @@ import (
) )
func main() { func main() {
var v int64 = 100 var v int64
println(atomic.AddInt64(&v, 1))
atomic.StoreInt64(&v, 100)
println("store:", atomic.LoadInt64(&v))
ret := atomic.AddInt64(&v, 1)
println("ret:", ret, "v:", v)
swp := atomic.CompareAndSwapInt64(&v, 100, 102)
println("swp:", swp, "v:", v)
swp = atomic.CompareAndSwapInt64(&v, 101, 102)
println("swp:", swp, "v:", v)
ret = atomic.AddInt64(&v, -1)
println("ret:", ret, "v:", v)
} }

View File

@@ -1,9 +1,15 @@
; ModuleID = 'main' ; ModuleID = 'main'
source_filename = "main" source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
@"main.init$guard" = global i1 false, align 1 @"main.init$guard" = global i1 false, align 1
@__llgo_argc = global i32 0, align 4 @__llgo_argc = global i32 0, align 4
@__llgo_argv = global ptr null, align 8 @__llgo_argv = global ptr null, align 8
@0 = private unnamed_addr constant [6 x i8] c"store:", align 1
@1 = private unnamed_addr constant [4 x i8] c"ret:", align 1
@2 = private unnamed_addr constant [2 x i8] c"v:", align 1
@3 = private unnamed_addr constant [4 x i8] c"swp:", align 1
define void @main.init() { define void @main.init() {
_llgo_0: _llgo_0:
@@ -26,10 +32,106 @@ _llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"() call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init() call void @main.init()
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) %2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8)
store i64 100, ptr %2, align 4 call void @"sync/atomic.StoreInt64"(ptr %2, i64 100)
%3 = call i64 @"sync/atomic.AddInt64"(ptr %2, i64 1) %3 = call i64 @"sync/atomic.LoadInt64"(ptr %2)
%4 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %4, i32 0, i32 0
store ptr @0, ptr %5, align 8
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %4, i32 0, i32 1
store i64 6, ptr %6, align 4
%7 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %4, align 8
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %7)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %3) call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %3)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10)
%8 = call i64 @"sync/atomic.AddInt64"(ptr %2, i64 1)
%9 = load i64, ptr %2, align 4
%10 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %10, i32 0, i32 0
store ptr @1, ptr %11, align 8
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %10, i32 0, i32 1
store i64 4, ptr %12, align 4
%13 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %10, align 8
%14 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%15 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %14, i32 0, i32 0
store ptr @2, ptr %15, align 8
%16 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %14, i32 0, i32 1
store i64 2, ptr %16, align 4
%17 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %14, align 8
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %13)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %8)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %17)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %9)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10)
%18 = call i1 @"sync/atomic.CompareAndSwapInt64"(ptr %2, i64 100, i64 102)
%19 = load i64, ptr %2, align 4
%20 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%21 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %20, i32 0, i32 0
store ptr @3, ptr %21, align 8
%22 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %20, i32 0, i32 1
store i64 4, ptr %22, align 4
%23 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %20, align 8
%24 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%25 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %24, i32 0, i32 0
store ptr @2, ptr %25, align 8
%26 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %24, i32 0, i32 1
store i64 2, ptr %26, align 4
%27 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %24, align 8
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %23)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %18)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %27)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %19)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10)
%28 = call i1 @"sync/atomic.CompareAndSwapInt64"(ptr %2, i64 101, i64 102)
%29 = load i64, ptr %2, align 4
%30 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%31 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %30, i32 0, i32 0
store ptr @3, ptr %31, align 8
%32 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %30, i32 0, i32 1
store i64 4, ptr %32, align 4
%33 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %30, align 8
%34 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%35 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %34, i32 0, i32 0
store ptr @2, ptr %35, align 8
%36 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %34, i32 0, i32 1
store i64 2, ptr %36, align 4
%37 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %34, align 8
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %33)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %28)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %37)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %29)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10)
%38 = call i64 @"sync/atomic.AddInt64"(ptr %2, i64 -1)
%39 = load i64, ptr %2, align 4
%40 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%41 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %40, i32 0, i32 0
store ptr @1, ptr %41, align 8
%42 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %40, i32 0, i32 1
store i64 4, ptr %42, align 4
%43 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %40, align 8
%44 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%45 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %44, i32 0, i32 0
store ptr @2, ptr %45, align 8
%46 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %44, i32 0, i32 1
store i64 2, ptr %46, align 4
%47 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %44, align 8
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %43)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %38)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %47)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %39)
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10)
ret i32 0 ret i32 0
} }
@@ -39,8 +141,18 @@ declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
declare i64 @"sync/atomic.AddInt64"(ptr, i64) declare void @"sync/atomic.StoreInt64"(ptr, i64)
declare i64 @"sync/atomic.LoadInt64"(ptr)
declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String")
declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8)
declare void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64) declare void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64)
declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) declare i64 @"sync/atomic.AddInt64"(ptr, i64)
declare i1 @"sync/atomic.CompareAndSwapInt64"(ptr, i64, i64)
declare void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1)

View File

@@ -29,6 +29,8 @@ import (
func TestCollectSkipNames(t *testing.T) { func TestCollectSkipNames(t *testing.T) {
ctx := &context{skips: make(map[string]none)} ctx := &context{skips: make(map[string]none)}
ctx.collectSkipNames("//llgo:skipall")
ctx.collectSkipNames("//llgo:skip")
ctx.collectSkipNames("//llgo:skip abs") ctx.collectSkipNames("//llgo:skip abs")
} }
@@ -218,6 +220,13 @@ func TestErrImport(t *testing.T) {
var ctx context var ctx context
pkg := types.NewPackage("foo", "foo") pkg := types.NewPackage("foo", "foo")
ctx.importPkg(pkg, nil) ctx.importPkg(pkg, nil)
alt := types.NewPackage("bar", "bar")
alt.Scope().Insert(
types.NewConst(0, alt, "LLGoPackage", types.Typ[types.String], constant.MakeString("noinit")),
)
ctx.patches = Patches{"foo": &ssa.Package{Pkg: alt}}
ctx.importPkg(pkg, &pkgInfo{})
} }
func TestErrInitLinkname(t *testing.T) { func TestErrInitLinkname(t *testing.T) {

View File

@@ -28,6 +28,7 @@ import (
"strings" "strings"
"github.com/goplus/llgo/cl/blocks" "github.com/goplus/llgo/cl/blocks"
"github.com/goplus/llgo/internal/typepatch"
llssa "github.com/goplus/llgo/ssa" llssa "github.com/goplus/llgo/ssa"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
) )
@@ -143,6 +144,7 @@ type context struct {
bvals map[ssa.Value]llssa.Expr // block values bvals map[ssa.Value]llssa.Expr // block values
vargs map[*ssa.Alloc][]llssa.Expr // varargs vargs map[*ssa.Alloc][]llssa.Expr // varargs
patches Patches
blkInfos []blocks.Info blkInfos []blocks.Info
inits []func() inits []func()
@@ -1000,33 +1002,41 @@ func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Patches is patches of some packages.
type Patches = map[string]*ssa.Package
// NewPackage compiles a Go package to LLVM IR package. // NewPackage compiles a Go package to LLVM IR package.
func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, err error) { func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, err error) {
return NewPackageEx(prog, pkg, nil, files) return NewPackageEx(prog, nil, pkg, files)
} }
// NewPackageEx compiles a Go package (pkg) to LLVM IR package. // NewPackageEx compiles a Go package to LLVM IR package.
// The Go package may have an alternative package (alt). func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, err error) {
// The pkg and alt have the same (Pkg *types.Package).
func NewPackageEx(prog llssa.Program, pkg, alt *ssa.Package, files []*ast.File) (ret llssa.Package, err error) {
pkgProg := pkg.Prog pkgProg := pkg.Prog
pkgTypes := pkg.Pkg pkgTypes := pkg.Pkg
pkgName, pkgPath := pkgTypes.Name(), llssa.PathOf(pkgTypes) pkgName, pkgPath := pkgTypes.Name(), llssa.PathOf(pkgTypes)
alt, hasPatch := patches[pkgPath]
if hasPatch {
pkgTypes = typepatch.Pkg(pkgTypes, alt.Pkg)
pkg.Pkg = pkgTypes
alt.Pkg = pkgTypes
}
if pkgPath == llssa.PkgRuntime { if pkgPath == llssa.PkgRuntime {
prog.SetRuntime(pkgTypes) prog.SetRuntime(pkgTypes)
} }
ret = prog.NewPackage(pkgName, pkgPath) ret = prog.NewPackage(pkgName, pkgPath)
ctx := &context{ ctx := &context{
prog: prog, prog: prog,
pkg: ret, pkg: ret,
fset: pkgProg.Fset, fset: pkgProg.Fset,
goProg: pkgProg, goProg: pkgProg,
goTyps: pkgTypes, goTyps: pkgTypes,
goPkg: pkg, goPkg: pkg,
link: make(map[string]string), patches: patches,
skips: make(map[string]none), link: make(map[string]string),
vargs: make(map[*ssa.Alloc][]llssa.Expr), skips: make(map[string]none),
vargs: make(map[*ssa.Alloc][]llssa.Expr),
loaded: map[*types.Package]*pkgInfo{ loaded: map[*types.Package]*pkgInfo{
types.Unsafe: {kind: PkgDeclOnly}, // TODO(xsw): PkgNoInit or PkgDeclOnly? types.Unsafe: {kind: PkgDeclOnly}, // TODO(xsw): PkgNoInit or PkgDeclOnly?
}, },
@@ -1034,7 +1044,7 @@ func NewPackageEx(prog llssa.Program, pkg, alt *ssa.Package, files []*ast.File)
ctx.initPyModule() ctx.initPyModule()
ctx.initFiles(pkgPath, files) ctx.initFiles(pkgPath, files)
if alt != nil { if hasPatch {
skips := ctx.skips skips := ctx.skips
ctx.skips = nil ctx.skips = nil
processPkg(ctx, ret, alt) processPkg(ctx, ret, alt)

View File

@@ -127,14 +127,22 @@ func pkgKindByScope(scope *types.Scope) (int, string) {
} }
func (p *context) importPkg(pkg *types.Package, i *pkgInfo) { func (p *context) importPkg(pkg *types.Package, i *pkgInfo) {
pkgPath := llssa.PathOf(pkg)
scope := pkg.Scope() scope := pkg.Scope()
kind, _ := pkgKindByScope(scope) kind, _ := pkgKindByScope(scope)
if kind == PkgNormal { if kind == PkgNormal {
if alt, ok := p.patches[pkgPath]; ok {
pkg = alt.Pkg
scope = pkg.Scope()
if kind, _ = pkgKindByScope(scope); kind != PkgNormal {
goto start
}
}
return return
} }
start:
i.kind = kind i.kind = kind
fset := p.fset fset := p.fset
pkgPath := llssa.PathOf(pkg)
names := scope.Names() names := scope.Names()
syms := newPkgSymInfo() syms := newPkgSymInfo()
for _, name := range names { for _, name := range names {

View File

@@ -34,7 +34,6 @@ import (
"github.com/goplus/llgo/cl" "github.com/goplus/llgo/cl"
"github.com/goplus/llgo/internal/packages" "github.com/goplus/llgo/internal/packages"
"github.com/goplus/llgo/internal/typepatch"
"github.com/goplus/llgo/xtool/clang" "github.com/goplus/llgo/xtool/clang"
"github.com/goplus/llgo/xtool/env" "github.com/goplus/llgo/xtool/env"
@@ -50,6 +49,10 @@ const (
ModeRun ModeRun
) )
const (
debugBuild = packages.DebugPackagesLoad
)
func needLLFile(mode Mode) bool { func needLLFile(mode Mode) bool {
return mode != ModeBuild return mode != ModeBuild
} }
@@ -129,35 +132,25 @@ func Do(args []string, conf *Config) {
return return
} }
altPkgPaths := altPkgs(initial, llssa.PkgRuntime)
altPkgs, err := packages.LoadEx(dedup, sizes, cfg, altPkgPaths...)
check(err)
var needRt bool var needRt bool
var rt []*packages.Package
load := func() []*packages.Package {
if rt == nil {
var err error
rt, err = packages.LoadEx(dedup, sizes, cfg, llssa.PkgRuntime, llssa.PkgPython)
check(err)
}
return rt
}
prog.SetRuntime(func() *types.Package { prog.SetRuntime(func() *types.Package {
needRt = true needRt = true
rt := load() return altPkgs[0].Types
return rt[0].Types
}) })
prog.SetPython(func() *types.Package { prog.SetPython(func() *types.Package {
rt := load() return dedup.Check(llssa.PkgPython).Types
return rt[1].Types
}) })
imp := func(pkgPath string) *packages.Package {
if ret, e := packages.LoadEx(dedup, sizes, cfg, pkgPath); e == nil {
return ret[0]
}
return nil
}
progSSA := ssa.NewProgram(initial[0].Fset, ssaBuildMode) progSSA := ssa.NewProgram(initial[0].Fset, ssaBuildMode)
pkgs := buildAllPkgs(prog, progSSA, imp, initial, nil, mode, verbose) patches := make(cl.Patches, len(altPkgPaths))
altSSAPkgs(progSSA, patches, altPkgs[1:])
ctx := &context{progSSA, prog, dedup, patches, make(map[string]none), mode, verbose}
pkgs := buildAllPkgs(ctx, initial)
var runtimeFiles []string var runtimeFiles []string
if needRt { if needRt {
@@ -165,11 +158,7 @@ func Do(args []string, conf *Config) {
llssa.SetDebug(0) llssa.SetDebug(0)
cl.SetDebug(0) cl.SetDebug(0)
skip := make(map[string]bool) dpkg := buildAllPkgs(ctx, altPkgs[:1])
for _, v := range pkgs {
skip[v.PkgPath] = true
}
dpkg := buildAllPkgs(prog, progSSA, imp, rt[:1], skip, mode, verbose)
for _, pkg := range dpkg { for _, pkg := range dpkg {
if !strings.HasSuffix(pkg.ExportFile, ".ll") { if !strings.HasSuffix(pkg.ExportFile, ".ll") {
continue continue
@@ -212,21 +201,33 @@ const (
ssaBuildMode = ssa.SanityCheckFunctions ssaBuildMode = ssa.SanityCheckFunctions
) )
func buildAllPkgs(prog llssa.Program, progSSA *ssa.Program, imp importer, initial []*packages.Package, skip map[string]bool, mode Mode, verbose bool) (pkgs []*aPackage) { type context struct {
// Create SSA-form program representation. progSSA *ssa.Program
pkgs, errPkgs := allPkgs(progSSA, imp, initial, verbose) prog llssa.Program
dedup packages.Deduper
patches cl.Patches
built map[string]none
mode Mode
verbose bool
}
func buildAllPkgs(ctx *context, initial []*packages.Package) (pkgs []*aPackage) {
prog := ctx.prog
pkgs, errPkgs := allPkgs(ctx, initial)
for _, errPkg := range errPkgs { for _, errPkg := range errPkgs {
for _, err := range errPkg.Errors { for _, err := range errPkg.Errors {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
} }
fmt.Fprintln(os.Stderr, "cannot build SSA for package", errPkg) fmt.Fprintln(os.Stderr, "cannot build SSA for package", errPkg)
} }
built := ctx.built
for _, aPkg := range pkgs { for _, aPkg := range pkgs {
pkg := aPkg.Package pkg := aPkg.Package
if skip[pkg.PkgPath] { if _, ok := built[pkg.PkgPath]; ok {
pkg.ExportFile = "" pkg.ExportFile = ""
continue continue
} }
built[pkg.PkgPath] = none{}
switch kind, param := cl.PkgKindOf(pkg.Types); kind { switch kind, param := cl.PkgKindOf(pkg.Types); kind {
case cl.PkgDeclOnly: case cl.PkgDeclOnly:
// skip packages that only contain declarations // skip packages that only contain declarations
@@ -277,7 +278,7 @@ func buildAllPkgs(prog llssa.Program, progSSA *ssa.Program, imp importer, initia
} }
} }
default: default:
buildPkg(prog, aPkg, mode, verbose) buildPkg(ctx, aPkg)
setNeedRuntimeOrPyInit(pkg, prog.NeedRuntime, prog.NeedPyInit) setNeedRuntimeOrPyInit(pkg, prog.NeedRuntime, prog.NeedPyInit)
} }
} }
@@ -375,27 +376,23 @@ func linkMainPkg(pkg *packages.Package, pkgs []*aPackage, runtimeFiles []string,
return return
} }
func buildPkg(prog llssa.Program, aPkg *aPackage, mode Mode, verbose bool) { func buildPkg(ctx *context, aPkg *aPackage) {
pkg := aPkg.Package pkg := aPkg.Package
pkgPath := pkg.PkgPath pkgPath := pkg.PkgPath
if verbose { if debugBuild || ctx.verbose {
fmt.Fprintln(os.Stderr, pkgPath) fmt.Fprintln(os.Stderr, pkgPath)
} }
if canSkipToBuild(pkgPath) { if canSkipToBuild(pkgPath) {
pkg.ExportFile = "" pkg.ExportFile = ""
return return
} }
altSSA := aPkg.AltSSA var syntax = pkg.Syntax
syntax := pkg.Syntax
if altPkg := aPkg.AltPkg; altPkg != nil { if altPkg := aPkg.AltPkg; altPkg != nil {
syntax = append(syntax, altPkg.Syntax...) syntax = append(syntax, altPkg.Syntax...)
if altSSA != nil {
altSSA.Pkg = typepatch.Pkg(pkg.Types, altPkg.Types)
}
} }
ret, err := cl.NewPackageEx(prog, aPkg.SSA, altSSA, syntax) ret, err := cl.NewPackageEx(ctx.prog, ctx.patches, aPkg.SSA, syntax)
check(err) check(err)
if needLLFile(mode) { if needLLFile(ctx.mode) {
pkg.ExportFile += ".ll" pkg.ExportFile += ".ll"
os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644) os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644)
} }
@@ -404,7 +401,7 @@ func buildPkg(prog llssa.Program, aPkg *aPackage, mode Mode, verbose bool) {
func canSkipToBuild(pkgPath string) bool { func canSkipToBuild(pkgPath string) bool {
switch pkgPath { switch pkgPath {
case "unsafe", "errors": case "unsafe", "errors", "runtime", "sync": // TODO(xsw): remove it
return true return true
default: default:
return strings.HasPrefix(pkgPath, "internal/") || return strings.HasPrefix(pkgPath, "internal/") ||
@@ -412,14 +409,6 @@ func canSkipToBuild(pkgPath string) bool {
} }
} }
type aPackage struct {
*packages.Package
SSA *ssa.Package
AltPkg *packages.Package
AltSSA *ssa.Package
LPkg llssa.Package
}
type none struct{} type none struct{}
var hasAltPkg = map[string]none{ var hasAltPkg = map[string]none{
@@ -429,26 +418,59 @@ var hasAltPkg = map[string]none{
"runtime": {}, "runtime": {},
} }
type importer = func(pkgPath string) *packages.Package const (
altPkgPathPrefix = "github.com/goplus/llgo/internal/lib/"
)
func allPkgs(prog *ssa.Program, imp importer, initial []*packages.Package, verbose bool) (all []*aPackage, errs []*packages.Package) { func altPkgs(initial []*packages.Package, alts ...string) []string {
packages.Visit(initial, nil, func(p *packages.Package) { packages.Visit(initial, nil, func(p *packages.Package) {
if p.Types != nil && !p.IllTyped { if p.Types != nil && !p.IllTyped {
var altPkg *packages.Package if _, ok := hasAltPkg[p.PkgPath]; ok {
var altSSA *ssa.Package alts = append(alts, altPkgPathPrefix+p.PkgPath)
var ssaPkg = createSSAPkg(prog, p) }
if imp != nil { }
if _, ok := hasAltPkg[p.PkgPath]; ok { })
if verbose { return alts
log.Println("==> Patching", p.PkgPath) }
}
altPkgPath := "github.com/goplus/llgo/internal/lib/" + p.PkgPath func altSSAPkgs(prog *ssa.Program, patches cl.Patches, alts []*packages.Package) {
if altPkg = imp(altPkgPath); altPkg != nil { // TODO(xsw): how to minimize import times packages.Visit(alts, nil, func(p *packages.Package) {
altSSA = createAltSSAPkg(prog, altPkg) if p.Types != nil && !p.IllTyped {
} pkgSSA := prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true)
if strings.HasPrefix(p.PkgPath, altPkgPathPrefix) {
path := p.PkgPath[len(altPkgPathPrefix):]
patches[path] = pkgSSA
if debugBuild {
log.Println("==> Patching", path)
} }
} }
all = append(all, &aPackage{p, ssaPkg, altPkg, altSSA, nil}) }
})
prog.Build()
}
type aPackage struct {
*packages.Package
SSA *ssa.Package
AltPkg *packages.Cached
LPkg llssa.Package
}
func allPkgs(ctx *context, initial []*packages.Package) (all []*aPackage, errs []*packages.Package) {
prog := ctx.progSSA
verbose := ctx.verbose
built := ctx.built
packages.Visit(initial, nil, func(p *packages.Package) {
if p.Types != nil && !p.IllTyped {
if _, ok := built[p.PkgPath]; ok {
return
}
var altPkg *packages.Cached
var ssaPkg = createSSAPkg(prog, p, verbose)
if _, ok := hasAltPkg[p.PkgPath]; ok {
altPkg = ctx.dedup.Check(altPkgPathPrefix + p.PkgPath)
}
all = append(all, &aPackage{p, ssaPkg, altPkg, nil})
} else { } else {
errs = append(errs, p) errs = append(errs, p)
} }
@@ -456,22 +478,12 @@ func allPkgs(prog *ssa.Program, imp importer, initial []*packages.Package, verbo
return return
} }
func createAltSSAPkg(prog *ssa.Program, alt *packages.Package) *ssa.Package { func createSSAPkg(prog *ssa.Program, p *packages.Package, verbose bool) *ssa.Package {
altSSA := prog.ImportedPackage(alt.PkgPath)
if altSSA == nil {
packages.Visit([]*packages.Package{alt}, nil, func(p *packages.Package) {
if p.Types != nil && !p.IllTyped {
createSSAPkg(prog, p)
}
})
altSSA = prog.ImportedPackage(alt.PkgPath)
}
return altSSA
}
func createSSAPkg(prog *ssa.Program, p *packages.Package) *ssa.Package {
pkgSSA := prog.ImportedPackage(p.PkgPath) pkgSSA := prog.ImportedPackage(p.PkgPath)
if pkgSSA == nil { if pkgSSA == nil {
if debugBuild || verbose {
log.Println("==> BuildSSA", p.PkgPath)
}
pkgSSA = prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true) pkgSSA = prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true)
pkgSSA.Build() // TODO(xsw): build concurrently pkgSSA.Build() // TODO(xsw): build concurrently
} }

View File

@@ -16,17 +16,125 @@
package atomic package atomic
// llgo:skipall
import ( import (
_ "unsafe" "unsafe"
) )
const ( const (
LLGoPackage = true LLGoPackage = true
) )
//go:linkname cAddInt64 llgo.atomicAdd type valtype interface {
func cAddInt64(addr *int64, delta int64) (old int64) ~int | ~uint | ~uintptr | ~int32 | ~uint32 | ~int64 | ~uint64 | ~unsafe.Pointer
}
//go:linkname SwapInt32 llgo.atomicXchg
func SwapInt32(addr *int32, new int32) (old int32)
//go:linkname SwapInt64 llgo.atomicXchg
func SwapInt64(addr *int64, new int64) (old int64)
//go:linkname SwapUint32 llgo.atomicXchg
func SwapUint32(addr *uint32, new uint32) (old uint32)
//go:linkname SwapUint64 llgo.atomicXchg
func SwapUint64(addr *uint64, new uint64) (old uint64)
//go:linkname SwapUintptr llgo.atomicXchg
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
//go:linkname SwapPointer llgo.atomicXchg
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
// llgo:link atomicCmpXchg llgo.atomicCmpXchg
func atomicCmpXchg[T valtype](ptr *T, old, new T) (T, bool) { return old, false }
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool) {
_, swapped = atomicCmpXchg(addr, old, new)
return
}
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool) {
_, swapped = atomicCmpXchg(addr, old, new)
return
}
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool) {
_, swapped = atomicCmpXchg(addr, old, new)
return
}
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool) {
_, swapped = atomicCmpXchg(addr, old, new)
return
}
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool) {
_, swapped = atomicCmpXchg(addr, old, new)
return
}
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) {
_, swapped = atomicCmpXchg(addr, old, new)
return
}
// llgo:link atomicAdd llgo.atomicAdd
func atomicAdd[T valtype](ptr *T, v T) T { return v }
func AddInt32(addr *int32, delta int32) (new int32) {
return atomicAdd(addr, delta) + delta
}
func AddUint32(addr *uint32, delta uint32) (new uint32) {
return atomicAdd(addr, delta) + delta
}
func AddInt64(addr *int64, delta int64) (new int64) { func AddInt64(addr *int64, delta int64) (new int64) {
return cAddInt64(addr, delta) + delta return atomicAdd(addr, delta) + delta
} }
func AddUint64(addr *uint64, delta uint64) (new uint64) {
return atomicAdd(addr, delta) + delta
}
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr) {
return atomicAdd(addr, delta) + delta
}
//go:linkname LoadInt32 llgo.atomicLoad
func LoadInt32(addr *int32) (val int32)
//go:linkname LoadInt64 llgo.atomicLoad
func LoadInt64(addr *int64) (val int64)
//go:linkname LoadUint32 llgo.atomicLoad
func LoadUint32(addr *uint32) (val uint32)
//go:linkname LoadUint64 llgo.atomicLoad
func LoadUint64(addr *uint64) (val uint64)
//go:linkname LoadUintptr llgo.atomicLoad
func LoadUintptr(addr *uintptr) (val uintptr)
//go:linkname LoadPointer llgo.atomicLoad
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
//go:linkname StoreInt32 llgo.atomicStore
func StoreInt32(addr *int32, val int32)
//go:linkname StoreInt64 llgo.atomicStore
func StoreInt64(addr *int64, val int64)
//go:linkname StoreUint32 llgo.atomicStore
func StoreUint32(addr *uint32, val uint32)
//go:linkname StoreUint64 llgo.atomicStore
func StoreUint64(addr *uint64, val uint64)
//go:linkname StoreUintptr llgo.atomicStore
func StoreUintptr(addr *uintptr, val uintptr)
//go:linkname StorePointer llgo.atomicStore
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)

View File

@@ -61,6 +61,10 @@ const (
typecheckCgo = NeedModule - 1 // TODO(xsw): how to check typecheckCgo = NeedModule - 1 // TODO(xsw): how to check
) )
const (
DebugPackagesLoad = false
)
// A Config specifies details about how packages should be loaded. // A Config specifies details about how packages should be loaded.
// The zero value is a valid configuration. // The zero value is a valid configuration.
// Calls to Load do not modify this struct. // Calls to Load do not modify this struct.
@@ -99,7 +103,7 @@ type loader struct {
requestedMode LoadMode requestedMode LoadMode
} }
type cachedPackage struct { type Cached struct {
Types *types.Package Types *types.Package
TypesInfo *types.Info TypesInfo *types.Info
Syntax []*ast.File Syntax []*ast.File
@@ -115,14 +119,20 @@ func NewDeduper() Deduper {
return &aDeduper{} return &aDeduper{}
} }
func (p Deduper) check(pkgPath string) *cachedPackage { func (p Deduper) Check(pkgPath string) *Cached {
if v, ok := p.cache.Load(pkgPath); ok { if v, ok := p.cache.Load(pkgPath); ok {
return v.(*cachedPackage) if DebugPackagesLoad {
log.Println("==> dedup.check:", pkgPath)
}
return v.(*Cached)
} }
return nil return nil
} }
func (p Deduper) set(pkgPath string, cp *cachedPackage) { func (p Deduper) set(pkgPath string, cp *Cached) {
if DebugPackagesLoad {
log.Println("==> dedup.set:", pkgPath)
}
p.cache.Store(pkgPath, cp) p.cache.Store(pkgPath, cp)
} }
@@ -162,7 +172,7 @@ func loadPackageEx(dedup Deduper, ld *loader, lpkg *loaderPackage) {
} }
if dedup != nil { if dedup != nil {
if cp := dedup.check(lpkg.PkgPath); cp != nil { if cp := dedup.Check(lpkg.PkgPath); cp != nil {
lpkg.Types = cp.Types lpkg.Types = cp.Types
lpkg.Fset = ld.Fset lpkg.Fset = ld.Fset
lpkg.TypesInfo = cp.TypesInfo lpkg.TypesInfo = cp.TypesInfo
@@ -172,7 +182,7 @@ func loadPackageEx(dedup Deduper, ld *loader, lpkg *loaderPackage) {
} }
defer func() { defer func() {
if !lpkg.IllTyped && lpkg.needtypes && lpkg.needsrc { if !lpkg.IllTyped && lpkg.needtypes && lpkg.needsrc {
dedup.set(lpkg.PkgPath, &cachedPackage{ dedup.set(lpkg.PkgPath, &Cached{
Types: lpkg.Types, Types: lpkg.Types,
TypesInfo: lpkg.TypesInfo, TypesInfo: lpkg.TypesInfo,
Syntax: lpkg.Syntax, Syntax: lpkg.Syntax,

View File

@@ -22,6 +22,17 @@ import (
"unsafe" "unsafe"
) )
type typesPackage struct {
path string
name string
scope *types.Scope
imports []*types.Package
complete bool
fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go
goVersion string // minimum Go version required for package (by Config.GoVersion, typically from go.mod)
}
type typesScope struct { type typesScope struct {
parent *types.Scope parent *types.Scope
children []*types.Scope children []*types.Scope
@@ -44,23 +55,42 @@ type iface struct {
data unsafe.Pointer data unsafe.Pointer
} }
func setScope(pkg *types.Package, scope *types.Scope) {
p := (*typesPackage)(unsafe.Pointer(pkg))
p.scope = scope
}
func setPkg(o types.Object, pkg *types.Package) { func setPkg(o types.Object, pkg *types.Package) {
data := (*iface)(unsafe.Pointer(&o)).data data := (*iface)(unsafe.Pointer(&o)).data
(*object)(data).pkg = pkg (*object)(data).pkg = pkg
} }
func setObject(scope *types.Scope, name string, o types.Object) { func getElems(scope *types.Scope) map[string]types.Object {
s := (*typesScope)(unsafe.Pointer(scope)) s := (*typesScope)(unsafe.Pointer(scope))
s.elems[name] = o return s.elems
}
func setElems(scope *types.Scope, elems map[string]types.Object) {
s := (*typesScope)(unsafe.Pointer(scope))
s.elems = elems
} }
func Pkg(pkg, alt *types.Package) *types.Package { func Pkg(pkg, alt *types.Package) *types.Package {
scope := pkg.Scope() ret := *pkg
altScope := alt.Scope() scope := *pkg.Scope()
for _, name := range altScope.Names() {
o := altScope.Lookup(name) old := getElems(&scope)
setPkg(o, pkg) elems := make(map[string]types.Object, len(old))
setObject(scope, name, o) for name, o := range old {
elems[name] = o
} }
return pkg
altScope := alt.Scope()
for name, o := range getElems(altScope) {
setPkg(o, pkg)
elems[name] = o
}
setElems(&scope, elems)
setScope(&ret, &scope)
return &ret
} }