diff --git a/cl/_testlibgo/_atomic/in.go b/cl/_testlibgo/atomic/in.go similarity index 67% rename from cl/_testlibgo/_atomic/in.go rename to cl/_testlibgo/atomic/in.go index be7974ac..c6879d7e 100644 --- a/cl/_testlibgo/_atomic/in.go +++ b/cl/_testlibgo/atomic/in.go @@ -6,6 +6,5 @@ import ( func main() { var v int64 = 100 - atomic.AddInt64(&v, 1) - println(v) + println(atomic.AddInt64(&v, 1)) } diff --git a/cl/_testlibgo/atomic/out.ll b/cl/_testlibgo/atomic/out.ll new file mode 100644 index 00000000..2155a116 --- /dev/null +++ b/cl/_testlibgo/atomic/out.ll @@ -0,0 +1,46 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global i1 false, align 1 +@__llgo_argc = global i32 0, align 4 +@__llgo_argv = global ptr null, align 8 + +define void @main.init() { +_llgo_0: + %0 = load i1, ptr @"main.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"main.init$guard", align 1 + call void @"sync/atomic.init"() + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define i32 @main(i32 %0, ptr %1) { +_llgo_0: + store i32 %0, ptr @__llgo_argc, align 4 + store ptr %1, ptr @__llgo_argv, align 8 + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) + store i64 100, ptr %2, align 4 + %3 = call i64 @"sync/atomic.AddInt64"(ptr %2, i64 1) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %3) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + ret i32 0 +} + +declare void @"sync/atomic.init"() + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) + +declare i64 @"sync/atomic.AddInt64"(ptr, i64) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) diff --git a/cl/_testrt/_gotypes/in.go b/cl/_testrt/gotypes/in.go similarity index 100% rename from cl/_testrt/_gotypes/in.go rename to cl/_testrt/gotypes/in.go diff --git a/cl/_testrt/_gotypes/out.ll b/cl/_testrt/gotypes/out.ll similarity index 88% rename from cl/_testrt/_gotypes/out.ll rename to cl/_testrt/gotypes/out.ll index ae8de993..571235f3 100644 --- a/cl/_testrt/_gotypes/out.ll +++ b/cl/_testrt/gotypes/out.ll @@ -3,9 +3,9 @@ source_filename = "main" %"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr } -@"main.init$guard" = global ptr null -@__llgo_argc = global ptr null -@__llgo_argv = global ptr null +@"main.init$guard" = global i1 false, align 1 +@__llgo_argc = global i32 0, align 4 +@__llgo_argv = global ptr null, align 8 define void @main.foo(%"github.com/goplus/llgo/internal/runtime.iface" %0) { _llgo_0: diff --git a/cl/builtin_test.go b/cl/builtin_test.go index 9d3ee83d..bf5e12df 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -209,7 +209,7 @@ func TestIntVal(t *testing.T) { } func TestIgnoreName(t *testing.T) { - if !ignoreName("runtime.foo") || !ignoreName("runtime/foo") || !ignoreName("internal/abi") { + if !ignoreName("runtime/foo") || !ignoreName("internal/abi") { t.Fatal("ignoreName failed") } } diff --git a/cl/compile.go b/cl/compile.go index 3b631e69..b73e96ce 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -96,22 +96,12 @@ func ignoreName(name string) bool { return true } */ - if strings.HasPrefix(name, "internal/") || strings.HasPrefix(name, "crypto/") || + return strings.HasPrefix(name, "internal/") || strings.HasPrefix(name, "crypto/") || strings.HasPrefix(name, "arena.") || strings.HasPrefix(name, "maps.") || strings.HasPrefix(name, "time.") || strings.HasPrefix(name, "syscall.") || strings.HasPrefix(name, "os.") || strings.HasPrefix(name, "plugin.") || - strings.HasPrefix(name, "reflect.") || strings.HasPrefix(name, "errors.") { - return true // TODO(xsw) - } - return inPkg(name, "runtime") || inPkg(name, "sync") -} - -func inPkg(name, pkg string) bool { - if len(name) > len(pkg) && strings.HasPrefix(name, pkg) { - c := name[len(pkg)] - return c == '.' || c == '/' - } - return false + strings.HasPrefix(name, "reflect.") || strings.HasPrefix(name, "errors.") || + strings.HasPrefix(name, "runtime/") } // ----------------------------------------------------------------------------- @@ -157,6 +147,8 @@ type context struct { inits []func() phis []func() + + skipall bool } func (p *context) inMain(instr ssa.Instruction) bool { @@ -1048,7 +1040,9 @@ func NewPackageEx(prog llssa.Program, pkg, alt *ssa.Package, files []*ast.File) processPkg(ctx, ret, alt) ctx.skips = skips } - processPkg(ctx, ret, pkg) + if !ctx.skipall { + processPkg(ctx, ret, pkg) + } for len(ctx.inits) > 0 { inits := ctx.inits ctx.inits = nil diff --git a/cl/import.go b/cl/import.go index c1217778..72addadd 100644 --- a/cl/import.go +++ b/cl/import.go @@ -193,10 +193,12 @@ func (p *context) initFiles(pkgPath string, files []*ast.File) { } } +// llgo:skip symbol1 symbol2 ... +// llgo:skipall func (p *context) collectSkipNames(line string) { const ( - skip = "//llgo:skip " - skip2 = "// llgo:skip " + skip = "//llgo:skip" + skip2 = "// llgo:skip" ) if strings.HasPrefix(line, skip2) { p.collectSkip(line, len(skip2)) @@ -206,7 +208,15 @@ func (p *context) collectSkipNames(line string) { } func (p *context) collectSkip(line string, prefix int) { - names := strings.Split(line[prefix:], " ") + line = line[prefix:] + if line == "all" { + p.skipall = true + return + } + if len(line) == 0 || line[0] != ' ' { + return + } + names := strings.Split(line[1:], " ") for _, name := range names { if name != "" { p.skips[name] = none{} diff --git a/internal/build/build.go b/internal/build/build.go index 2fba905e..4dc353c5 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -22,6 +22,7 @@ import ( "go/token" "go/types" "io" + "log" "os" "os/exec" "path" @@ -155,15 +156,20 @@ func Do(args []string, conf *Config) { return nil } - pkgs := buildAllPkgs(prog, imp, initial, nil, mode, verbose) + progSSA := ssa.NewProgram(initial[0].Fset, ssaBuildMode) + pkgs := buildAllPkgs(prog, progSSA, imp, initial, nil, mode, verbose) var runtimeFiles []string if needRt { + // TODO(xsw): maybe we need trace runtime sometimes + llssa.SetDebug(0) + cl.SetDebug(0) + skip := make(map[string]bool) for _, v := range pkgs { skip[v.PkgPath] = true } - dpkg := buildAllPkgs(prog, imp, rt[:1], skip, mode, verbose) + dpkg := buildAllPkgs(prog, progSSA, imp, rt[:1], skip, mode, verbose) for _, pkg := range dpkg { if !strings.HasSuffix(pkg.ExportFile, ".ll") { continue @@ -206,10 +212,9 @@ const ( ssaBuildMode = ssa.SanityCheckFunctions ) -func buildAllPkgs(prog llssa.Program, imp importer, initial []*packages.Package, skip map[string]bool, mode Mode, verbose bool) (pkgs []*aPackage) { +func buildAllPkgs(prog llssa.Program, progSSA *ssa.Program, imp importer, initial []*packages.Package, skip map[string]bool, mode Mode, verbose bool) (pkgs []*aPackage) { // Create SSA-form program representation. - ssaProg, pkgs, errPkgs := allPkgs(imp, initial, ssaBuildMode) - ssaProg.Build() + pkgs, errPkgs := allPkgs(progSSA, imp, initial, verbose) for _, errPkg := range errPkgs { for _, err := range errPkg.Errors { fmt.Fprintln(os.Stderr, err) @@ -399,7 +404,7 @@ func buildPkg(prog llssa.Program, aPkg *aPackage, mode Mode, verbose bool) { func canSkipToBuild(pkgPath string) bool { switch pkgPath { - case "unsafe", "runtime", "errors", "sync", "sync/atomic": + case "unsafe", "errors": return true default: return strings.HasPrefix(pkgPath, "internal/") || @@ -418,25 +423,25 @@ type aPackage struct { type none struct{} var hasAltPkg = map[string]none{ - "math": {}, + "math": {}, + "sync": {}, + "sync/atomic": {}, + "runtime": {}, } type importer = func(pkgPath string) *packages.Package -func allPkgs(imp importer, initial []*packages.Package, mode ssa.BuilderMode) (prog *ssa.Program, all []*aPackage, errs []*packages.Package) { - var fset *token.FileSet - if len(initial) > 0 { - fset = initial[0].Fset - } - - prog = ssa.NewProgram(fset, mode) +func allPkgs(prog *ssa.Program, imp importer, initial []*packages.Package, verbose bool) (all []*aPackage, errs []*packages.Package) { packages.Visit(initial, nil, func(p *packages.Package) { if p.Types != nil && !p.IllTyped { var altPkg *packages.Package var altSSA *ssa.Package - var ssaPkg = prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true) + var ssaPkg = createSSAPkg(prog, p) if imp != nil { if _, ok := hasAltPkg[p.PkgPath]; ok { + if verbose { + log.Println("==> Patching", p.PkgPath) + } altPkgPath := "github.com/goplus/llgo/internal/lib/" + p.PkgPath if altPkg = imp(altPkgPath); altPkg != nil { // TODO(xsw): how to minimize import times altSSA = createAltSSAPkg(prog, altPkg) @@ -452,22 +457,27 @@ func allPkgs(imp importer, initial []*packages.Package, mode ssa.BuilderMode) (p } func createAltSSAPkg(prog *ssa.Program, alt *packages.Package) *ssa.Package { - altPath := alt.Types.Path() - altSSA := prog.ImportedPackage(altPath) + altSSA := prog.ImportedPackage(alt.PkgPath) if altSSA == nil { packages.Visit([]*packages.Package{alt}, nil, func(p *packages.Package) { - pkgTypes := p.Types - if pkgTypes != nil && !p.IllTyped { - if prog.ImportedPackage(pkgTypes.Path()) == nil { - prog.CreatePackage(pkgTypes, p.Syntax, p.TypesInfo, true) - } + if p.Types != nil && !p.IllTyped { + createSSAPkg(prog, p) } }) - altSSA = prog.ImportedPackage(altPath) + altSSA = prog.ImportedPackage(alt.PkgPath) } return altSSA } +func createSSAPkg(prog *ssa.Program, p *packages.Package) *ssa.Package { + pkgSSA := prog.ImportedPackage(p.PkgPath) + if pkgSSA == nil { + pkgSSA = prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true) + pkgSSA.Build() // TODO(xsw): build concurrently + } + return pkgSSA +} + var ( // TODO(xsw): complete build flags buildFlags = map[string]bool{ diff --git a/internal/lib/runtime/runtime.go b/internal/lib/runtime/runtime.go new file mode 100644 index 00000000..e8492442 --- /dev/null +++ b/internal/lib/runtime/runtime.go @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 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 runtime + +// llgo:skipall +import ( + _ "unsafe" +) diff --git a/internal/lib/sync/atomic/atomic.go b/internal/lib/sync/atomic/atomic.go new file mode 100644 index 00000000..24c3ecb1 --- /dev/null +++ b/internal/lib/sync/atomic/atomic.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 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 atomic + +import ( + _ "unsafe" +) + +const ( + LLGoPackage = true +) + +//go:linkname cAddInt64 llgo.atomicAdd +func cAddInt64(addr *int64, delta int64) (old int64) + +func AddInt64(addr *int64, delta int64) (new int64) { + return cAddInt64(addr, delta) + delta +} diff --git a/internal/lib/sync/sync.go b/internal/lib/sync/sync.go new file mode 100644 index 00000000..56822964 --- /dev/null +++ b/internal/lib/sync/sync.go @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 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 sync + +// llgo:skipall +import ( + _ "unsafe" +)