diff --git a/chore/llgen/llgen.go b/chore/llgen/llgen.go index 91fc0688..594076da 100644 --- a/chore/llgen/llgen.go +++ b/chore/llgen/llgen.go @@ -25,8 +25,8 @@ import ( ) func main() { - if len(os.Args) != 2 { - fmt.Fprintln(os.Stderr, "Usage: llgen xxx.go") + if len(os.Args) < 2 { + fmt.Fprintln(os.Stderr, "Usage: llgen xxx.go [pkgPath]") return } @@ -35,6 +35,13 @@ func main() { dir, _ := filepath.Split(inFile) outFile := dir + "out.ll" + pkgPath := "" + if len(os.Args) == 3 { + pkgPath = os.Args[2] + } else { + pkgPath = llgen.PkgPath(dir) + } + llgen.Init() - llgen.Do(llgen.PkgPath(dir), inFile, outFile) + llgen.Do(pkgPath, inFile, outFile) } diff --git a/cl/_testdata/apkg/out.ll b/cl/_testdata/apkg/out.ll index 75094713..7959439a 100644 --- a/cl/_testdata/apkg/out.ll +++ b/cl/_testdata/apkg/out.ll @@ -1,15 +1,15 @@ ; ModuleID = 'apkg' source_filename = "apkg" -@"init$guard" = global ptr null +@"apkg.init$guard" = global ptr null define void @apkg.init() { _llgo_0: - %0 = load i1, ptr @"init$guard", align 1 + %0 = load i1, ptr @"apkg.init$guard", align 1 br i1 %0, label %_llgo_2, label %_llgo_1 _llgo_1: ; preds = %_llgo_0 - store i1 true, ptr @"init$guard", align 1 + store i1 true, ptr @"apkg.init$guard", align 1 br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 diff --git a/cl/_testdata/fncall/out.ll b/cl/_testdata/fncall/out.ll index 223503e0..b0325800 100644 --- a/cl/_testdata/fncall/out.ll +++ b/cl/_testdata/fncall/out.ll @@ -1,15 +1,15 @@ ; ModuleID = 'main' source_filename = "main" -@"init$guard" = global ptr null +@"main.init$guard" = global ptr null define void @main.init() { _llgo_0: - %0 = load i1, ptr @"init$guard", align 1 + %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 @"init$guard", align 1 + store i1 true, ptr @"main.init$guard", align 1 br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 diff --git a/cl/_testdata/importpkg/out.ll b/cl/_testdata/importpkg/out.ll index ddd41201..c07ae22b 100644 --- a/cl/_testdata/importpkg/out.ll +++ b/cl/_testdata/importpkg/out.ll @@ -1,24 +1,24 @@ ; ModuleID = 'main' source_filename = "main" -@"init$guard" = global ptr null -@hello = global ptr null +@"main.init$guard" = global ptr null +@main.hello = global ptr null define void @main.init() { _llgo_0: - %0 = load i1, ptr @"init$guard", align 1 + %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 @"init$guard", align 1 + store i1 true, ptr @"main.init$guard", align 1 call void @"github.com/goplus/llgo/cl/internal/stdio.init"() - store i8 72, ptr @hello, align 1 - store i8 101, ptr getelementptr inbounds (i8, ptr @hello, i64 1), align 1 - store i8 108, ptr getelementptr inbounds (i8, ptr @hello, i64 2), align 1 - store i8 108, ptr getelementptr inbounds (i8, ptr @hello, i64 3), align 1 - store i8 111, ptr getelementptr inbounds (i8, ptr @hello, i64 4), align 1 - store i8 10, ptr getelementptr inbounds (i8, ptr @hello, i64 5), align 1 - store i8 0, ptr getelementptr inbounds (i8, ptr @hello, i64 6), align 1 + store i8 72, ptr @main.hello, align 1 + store i8 101, ptr getelementptr inbounds (i8, ptr @main.hello, i64 1), align 1 + store i8 108, ptr getelementptr inbounds (i8, ptr @main.hello, i64 2), align 1 + store i8 108, ptr getelementptr inbounds (i8, ptr @main.hello, i64 3), align 1 + store i8 111, ptr getelementptr inbounds (i8, ptr @main.hello, i64 4), align 1 + store i8 10, ptr getelementptr inbounds (i8, ptr @main.hello, i64 5), align 1 + store i8 0, ptr getelementptr inbounds (i8, ptr @main.hello, i64 6), align 1 br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -28,7 +28,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 define void @main() { _llgo_0: call void @main.init() - call void (ptr, ...) @printf(ptr @hello) + call void (ptr, ...) @printf(ptr @main.hello) ret void } diff --git a/cl/_testdata/printf/out.ll b/cl/_testdata/printf/out.ll index 6f6cc558..a5532ec5 100644 --- a/cl/_testdata/printf/out.ll +++ b/cl/_testdata/printf/out.ll @@ -1,23 +1,23 @@ ; ModuleID = 'main' source_filename = "main" -@"init$guard" = global ptr null -@hello = global ptr null +@"main.init$guard" = global ptr null +@main.hello = global ptr null define void @main.init() { _llgo_0: - %0 = load i1, ptr @"init$guard", align 1 + %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 @"init$guard", align 1 - store i8 72, ptr @hello, align 1 - store i8 101, ptr getelementptr inbounds (i8, ptr @hello, i64 1), align 1 - store i8 108, ptr getelementptr inbounds (i8, ptr @hello, i64 2), align 1 - store i8 108, ptr getelementptr inbounds (i8, ptr @hello, i64 3), align 1 - store i8 111, ptr getelementptr inbounds (i8, ptr @hello, i64 4), align 1 - store i8 10, ptr getelementptr inbounds (i8, ptr @hello, i64 5), align 1 - store i8 0, ptr getelementptr inbounds (i8, ptr @hello, i64 6), align 1 + store i1 true, ptr @"main.init$guard", align 1 + store i8 72, ptr @main.hello, align 1 + store i8 101, ptr getelementptr inbounds (i8, ptr @main.hello, i64 1), align 1 + store i8 108, ptr getelementptr inbounds (i8, ptr @main.hello, i64 2), align 1 + store i8 108, ptr getelementptr inbounds (i8, ptr @main.hello, i64 3), align 1 + store i8 111, ptr getelementptr inbounds (i8, ptr @main.hello, i64 4), align 1 + store i8 10, ptr getelementptr inbounds (i8, ptr @main.hello, i64 5), align 1 + store i8 0, ptr getelementptr inbounds (i8, ptr @main.hello, i64 6), align 1 br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -29,6 +29,6 @@ declare void @printf(ptr, ...) define void @main() { _llgo_0: call void @main.init() - call void (ptr, ...) @printf(ptr @hello) + call void (ptr, ...) @printf(ptr @main.hello) ret void } diff --git a/cl/_testdata/varinit/out.ll b/cl/_testdata/varinit/out.ll index a8a4848b..3052516c 100644 --- a/cl/_testdata/varinit/out.ll +++ b/cl/_testdata/varinit/out.ll @@ -1,17 +1,17 @@ ; ModuleID = 'main' source_filename = "main" -@"init$guard" = global ptr null -@a = global ptr null +@"main.init$guard" = global ptr null +@main.a = global ptr null define void @main.init() { _llgo_0: - %0 = load i1, ptr @"init$guard", align 1 + %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 @"init$guard", align 1 - store i64 100, ptr @a, align 4 + store i1 true, ptr @"main.init$guard", align 1 + store i64 100, ptr @main.a, align 4 br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -21,9 +21,9 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 define void @main() { _llgo_0: call void @main.init() - %0 = load i64, ptr @a, align 4 + %0 = load i64, ptr @main.a, align 4 %1 = add i64 %0, 1 - store i64 %1, ptr @a, align 4 - %2 = load i64, ptr @a, align 4 + store i64 %1, ptr @main.a, align 4 + %2 = load i64, ptr @main.a, align 4 ret void } diff --git a/cl/compile.go b/cl/compile.go index 38a20fcc..0a448401 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -91,6 +91,7 @@ type context struct { pkg llssa.Package fn llssa.Function fset *token.FileSet + goTyps *types.Package goPkg *ssa.Package link map[string]string // pkgPath.nameInPkg => linkname loaded map[*types.Package]none // loaded packages @@ -104,7 +105,8 @@ func (p *context) compileType(pkg llssa.Package, member *ssa.Type) { // Global variable. func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { - name, typ := gbl.Name(), gbl.Type() + typ := gbl.Type() + name := fullName(gbl.Pkg.Pkg, gbl.Name()) if debugInstr { log.Println("==> NewVar", name, typ) } @@ -245,10 +247,7 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { fn := p.funcOf(v) return fn.Expr case *ssa.Global: - if v.Pkg != p.goPkg { - panic("todo") - } - g := p.pkg.VarOf(v.Name()) + g := p.varOf(v) return g.Expr case *ssa.Const: t := v.Type() @@ -309,6 +308,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll prog: prog, pkg: ret, fset: pkg.Prog.Fset, + goTyps: pkgTypes, goPkg: pkg, link: make(map[string]string), loaded: make(map[*types.Package]none), diff --git a/cl/compile_test.go b/cl/compile_test.go index 9b72311f..2deacb58 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -118,16 +118,16 @@ var a int `, `; ModuleID = 'foo' source_filename = "foo" -@"init$guard" = global ptr null -@a = global ptr null +@"foo.init$guard" = global ptr null +@foo.a = global ptr null define void @foo.init() { _llgo_0: - %0 = load i1, ptr @"init$guard", align 1 + %0 = load i1, ptr @"foo.init$guard", align 1 br i1 %0, label %_llgo_2, label %_llgo_1 _llgo_1: ; preds = %_llgo_0 - store i1 true, ptr @"init$guard", align 1 + store i1 true, ptr @"foo.init$guard", align 1 br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -145,15 +145,15 @@ func fn(a int, b float64) int { `, `; ModuleID = 'foo' source_filename = "foo" -@"init$guard" = global ptr null +@"foo.init$guard" = global ptr null define void @foo.init() { _llgo_0: - %0 = load i1, ptr @"init$guard", align 1 + %0 = load i1, ptr @"foo.init$guard", align 1 br i1 %0, label %_llgo_2, label %_llgo_1 _llgo_1: ; preds = %_llgo_0 - store i1 true, ptr @"init$guard", align 1 + store i1 true, ptr @"foo.init$guard", align 1 br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 diff --git a/cl/import.go b/cl/import.go index 1b2008f8..30a203a7 100644 --- a/cl/import.go +++ b/cl/import.go @@ -126,11 +126,7 @@ func (p *context) funcName(pkg *types.Package, fn *ssa.Function) string { } func (p *context) funcOf(fn *ssa.Function) llssa.Function { - pkgTypes := fn.Pkg.Pkg - if _, ok := p.loaded[pkgTypes]; !ok { - p.loaded[pkgTypes] = none{} - p.importPkg(pkgTypes) - } + pkgTypes := p.ensureLoaded(fn.Pkg.Pkg) pkg := p.pkg name := p.funcName(pkgTypes, fn) if ret := pkg.FuncOf(name); ret != nil { @@ -138,3 +134,23 @@ func (p *context) funcOf(fn *ssa.Function) llssa.Function { } return pkg.NewFunc(name, fn.Signature) } + +func (p *context) varOf(v *ssa.Global) llssa.Global { + pkgTypes := p.ensureLoaded(v.Pkg.Pkg) + pkg := p.pkg + name := fullName(pkgTypes, v.Name()) + if ret := pkg.VarOf(name); ret != nil { + return ret + } + return pkg.NewVar(name, v.Type()) +} + +func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package { + if p.goTyps != pkgTypes { + if _, ok := p.loaded[pkgTypes]; !ok { + p.loaded[pkgTypes] = none{} + p.importPkg(pkgTypes) + } + } + return pkgTypes +} diff --git a/cmd/internal/build/build.go b/cmd/internal/build/build.go index 910c75fe..fddf7ce2 100644 --- a/cmd/internal/build/build.go +++ b/cmd/internal/build/build.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved. + * 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. diff --git a/cmd/internal/gen/gen.go b/cmd/internal/gen/gen.go new file mode 100644 index 00000000..a5c63883 --- /dev/null +++ b/cmd/internal/gen/gen.go @@ -0,0 +1,95 @@ +/* + * 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 build implements the “llgo gen command. +package gen + +import ( + "fmt" + "log" + "os" + "reflect" + + "github.com/goplus/llgo" + "github.com/goplus/llgo/cl" + "github.com/goplus/llgo/cmd/internal/base" + "github.com/goplus/llgo/internal/projs" + "github.com/goplus/llgo/ssa" + "github.com/qiniu/x/errors" +) + +// llgo gen +var Cmd = &base.Command{ + UsageLine: "llgo gen [-v] [packages|files]", + Short: "Convert Go code into LLVM ir (*.ll) code", +} + +var ( + flag = &Cmd.Flag + flagVerbose = flag.Bool("v", false, "print verbose information") +) + +func init() { + Cmd.Run = runCmd +} + +func runCmd(cmd *base.Command, args []string) { + err := flag.Parse(args) + if err != nil { + log.Panicln("parse input arguments failed:", err) + } + pattern := flag.Args() + if len(pattern) == 0 { + pattern = []string{"."} + } + + projects, err := projs.ParseAll(pattern...) + if err != nil { + log.Panicln("gopprojs.ParseAll:", err) + } + + ssa.Initialize(ssa.InitAll) + if *flagVerbose { + ssa.SetDebug(ssa.DbgFlagAll) + cl.SetDebug(cl.DbgFlagAll) + } + + for _, proj := range projects { + switch v := proj.(type) { + case *projs.DirProj: + _, _, err = llgo.Gen(v.Dir, nil, true, 0) + case *projs.PkgPathProj: + _, _, err = llgo.GenPkgPath("", v.Path, nil, true, 0) + case *projs.FilesProj: + _, err = llgo.GenFiles("", v.Files, nil) + default: + log.Panicln("`llgo gen` doesn't support", reflect.TypeOf(v)) + } + if err != nil { + fmt.Fprintf(os.Stderr, "llgo gen failed: %d errors.\n", errorNum(err)) + os.Exit(1) + } + } +} + +func errorNum(err error) int { + if e, ok := err.(errors.List); ok { + return len(e) + } + return 1 +} + +// ----------------------------------------------------------------------------- diff --git a/cmd/llgo/llgo.go b/cmd/llgo/llgo.go index 7e22a168..14049b6d 100644 --- a/cmd/llgo/llgo.go +++ b/cmd/llgo/llgo.go @@ -26,6 +26,7 @@ import ( "github.com/goplus/llgo/cmd/internal/base" "github.com/goplus/llgo/cmd/internal/build" + "github.com/goplus/llgo/cmd/internal/gen" "github.com/goplus/llgo/cmd/internal/help" ) @@ -38,6 +39,7 @@ func init() { flag.Usage = mainUsage base.Llgo.Commands = []*base.Command{ build.Cmd, + gen.Cmd, } } diff --git a/gen.go b/gen.go new file mode 100644 index 00000000..b033c9d2 --- /dev/null +++ b/gen.go @@ -0,0 +1,34 @@ +/* + * 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 llgo + +type GenFlags int + +// Gen generates llgo_autogen.ll for a Go package directory. +func Gen(dir string, conf *Config, genTestPkg bool, flags GenFlags) (string, bool, error) { + panic("todo") +} + +// GenPkgPath generates llgo_autogen.ll for a Go package. +func GenPkgPath(workDir, pkgPath string, conf *Config, allowExtern bool, flags GenFlags) (localDir string, recursively bool, err error) { + panic("todo") +} + +// GenFiles generates llgo_autogen.ll for specified Go files. +func GenFiles(autogen string, files []string, conf *Config) (outFiles []string, err error) { + panic("todo") +} diff --git a/load.go b/load.go index 06d4851a..7966059e 100644 --- a/load.go +++ b/load.go @@ -16,9 +16,18 @@ package llgo +import ( + "github.com/goplus/llgo/ssa" +) + // ----------------------------------------------------------------------------- type Config struct { } +// LoadDir loads Go packages from a specified directory. +func LoadDir(dir string, conf *Config, genTestPkg, promptGen bool) (out, test *ssa.Package, err error) { + panic("todo") +} + // -----------------------------------------------------------------------------