From 7cab76f44417e2e4446be15558c90dd17bd00384 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 21 Apr 2024 16:04:05 +0800 Subject: [PATCH 1/2] llgen --- chore/llgen/llgen.go | 79 ++++++++++++++++++++++++++++++++++++++++++++ cl/compile.go | 47 +++++++++++++++++++++----- cl/compile_test.go | 1 + ssa/package.go | 9 ++--- 4 files changed, 120 insertions(+), 16 deletions(-) create mode 100644 chore/llgen/llgen.go diff --git a/chore/llgen/llgen.go b/chore/llgen/llgen.go new file mode 100644 index 00000000..850a70da --- /dev/null +++ b/chore/llgen/llgen.go @@ -0,0 +1,79 @@ +/* + * 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 main + +import ( + "fmt" + "go/ast" + "go/importer" + "go/parser" + "go/token" + "go/types" + "os" + "path/filepath" + + "github.com/goplus/llgo/cl" + "golang.org/x/tools/go/ssa" + "golang.org/x/tools/go/ssa/ssautil" + + llssa "github.com/goplus/llgo/ssa" +) + +func main() { + if len(os.Args) != 2 { + fmt.Fprintln(os.Stderr, "Usage: llgen xxx.go") + return + } + + inFile := os.Args[1] + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, inFile, nil, parser.ParseComments) + check(err) + + files := []*ast.File{f} + name := f.Name.Name + pkg := types.NewPackage(name, name) + foo, _, err := ssautil.BuildPackage( + &types.Config{Importer: importer.Default()}, fset, pkg, files, ssa.SanityCheckFunctions) + check(err) + + foo.WriteTo(os.Stderr) + for _, m := range foo.Members { + if f, ok := m.(*ssa.Function); ok { + f.WriteTo(os.Stderr) + } + } + + llssa.Initialize(llssa.InitAll) + llssa.SetDebug(llssa.DbgFlagAll) + cl.SetDebug(cl.DbgFlagAll) + + prog := llssa.NewProgram(nil) + ret, err := cl.NewPackage(prog, foo, nil) + check(err) + + dir, _ := filepath.Split(inFile) + outFile := dir + "out.ll" + err = os.WriteFile(outFile, []byte(ret.String()), 0644) + check(err) +} + +func check(err error) { + if err != nil { + panic(err) + } +} diff --git a/cl/compile.go b/cl/compile.go index c1934f5f..7d795e9e 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -18,13 +18,30 @@ package cl import ( "fmt" + "log" "sort" llssa "github.com/goplus/llgo/ssa" "golang.org/x/tools/go/ssa" ) -type Config struct { +// ----------------------------------------------------------------------------- + +type dbgFlags = int + +const ( + DbgFlagInstruction dbgFlags = 1 << iota + + DbgFlagAll = DbgFlagInstruction +) + +var ( + debugInstr bool +) + +// SetDebug sets debug flags. +func SetDebug(dbgFlags dbgFlags) { + debugInstr = (dbgFlags & DbgFlagInstruction) != 0 } // ----------------------------------------------------------------------------- @@ -48,12 +65,20 @@ func (p *context) compileType(pkg llssa.Package, member *ssa.Type) { // Global variable. func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { - g := pkg.NewVar(gbl.Name(), gbl.Type()) + name, typ := gbl.Name(), gbl.Type() + if debugInstr { + log.Println("==> NewVar", name, typ) + } + g := pkg.NewVar(name, typ) g.Init(p.prog.Null(g.Type)) } func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) { - fn := pkg.NewFunc(f.Name(), f.Signature) + name := f.Name() + if debugInstr { + log.Println("==> NewFunc", name) + } + fn := pkg.NewFunc(name, f.Signature) p.inits = append(p.inits, func() { p.fn = fn defer func() { @@ -63,6 +88,9 @@ func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) { if nblk == 0 { // external function return } + if debugInstr { + log.Println("==> FuncBody", name) + } fn.MakeBlocks(nblk) b := fn.NewBuilder() for _, block := range f.Blocks { @@ -87,12 +115,10 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l } switch v := iv.(type) { case *ssa.Call: - if false { - call := v.Call - fn := p.compileValue(b, call.Value) - args := p.compileValues(b, call.Args) - ret = b.Call(fn, args...) - } + call := v.Call + fn := p.compileValue(b, call.Value) + args := p.compileValues(b, call.Args) + ret = b.Call(fn, args...) case *ssa.BinOp: x := p.compileValue(b, v.X) y := p.compileValue(b, v.Y) @@ -185,6 +211,9 @@ func (p *context) compileValues(b llssa.Builder, vals []ssa.Value) []llssa.Expr // ----------------------------------------------------------------------------- +type Config struct { +} + // NewPackage compiles a Go package to LLVM IR package. func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.Package, err error) { type namedMember struct { diff --git a/cl/compile_test.go b/cl/compile_test.go index 303e0cbe..150ddc6e 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -38,6 +38,7 @@ func TestFromTestdata(t *testing.T) { } func init() { + SetDebug(DbgFlagAll) llssa.Initialize(llssa.InitAll) llssa.SetDebug(llssa.DbgFlagAll) } diff --git a/ssa/package.go b/ssa/package.go index ce3969e0..7d040420 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -19,12 +19,13 @@ package ssa import ( "go/constant" "go/types" - "log" "github.com/goplus/llvm" "golang.org/x/tools/go/types/typeutil" ) +// ----------------------------------------------------------------------------- + type dbgFlags = int const ( @@ -192,9 +193,6 @@ func (p Package) NewConst(name string, val constant.Value) NamedConst { // NewVar creates a new global variable. func (p Package) NewVar(name string, typ types.Type) Global { - if debugInstr { - log.Println("==> NewVar", name, typ) - } t := p.prog.Type(typ) gbl := llvm.AddGlobal(p.mod, t.ll, name) ret := &aGlobal{Expr{gbl, t}} @@ -204,9 +202,6 @@ func (p Package) NewVar(name string, typ types.Type) Global { // NewFunc creates a new function. func (p Package) NewFunc(name string, sig *types.Signature) Function { - if debugInstr { - log.Println("==> NewFunc", name) - } t := p.prog.llvmSignature(sig) fn := llvm.AddFunction(p.mod, name, t.ll) ret := newFunction(fn, t, p.prog) From 7ffedc2da71a748ee5be7342fdc6cb01d56e5b87 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 21 Apr 2024 17:54:51 +0800 Subject: [PATCH 2/2] cl: _testdata/printf --- cl/_testdata/printf/out.ll | 1 + cl/compile.go | 78 ++++++++++++++++++++++++++++++++++---- cl/compile_test.go | 7 +--- ssa/expr.go | 4 +- ssa/type.go | 6 +-- 5 files changed, 77 insertions(+), 19 deletions(-) diff --git a/cl/_testdata/printf/out.ll b/cl/_testdata/printf/out.ll index 693d1261..fbe0e69e 100644 --- a/cl/_testdata/printf/out.ll +++ b/cl/_testdata/printf/out.ll @@ -28,5 +28,6 @@ declare void @printf(ptr, ...) define void @main() { _llgo_0: + call void (ptr, ...) @printf(ptr @hello) ret void } diff --git a/cl/compile.go b/cl/compile.go index 7d795e9e..f4648cf0 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -19,6 +19,7 @@ package cl import ( "fmt" "log" + "os" "sort" llssa "github.com/goplus/llgo/ssa" @@ -31,17 +32,45 @@ type dbgFlags = int const ( DbgFlagInstruction dbgFlags = 1 << iota + DbgFlagGoSSA - DbgFlagAll = DbgFlagInstruction + DbgFlagAll = DbgFlagInstruction | DbgFlagGoSSA ) var ( debugInstr bool + debugGoSSA bool ) // SetDebug sets debug flags. func SetDebug(dbgFlags dbgFlags) { debugInstr = (dbgFlags & DbgFlagInstruction) != 0 + debugGoSSA = (dbgFlags & DbgFlagGoSSA) != 0 +} + +// ----------------------------------------------------------------------------- + +const ( + fnNormal = iota + fnHasVArg + fnUnsafeInit +) + +func funcKind(vfn ssa.Value) int { + if fn, ok := vfn.(*ssa.Function); ok { + n := len(fn.Params) + if n == 0 { + if fn.Name() == "init" && fn.Pkg.Pkg.Path() == "unsafe" { + return fnUnsafeInit + } + } else { + last := fn.Params[n-1] + if last.Name() == llssa.NameValist { + return fnHasVArg + } + } + } + return fnNormal } // ----------------------------------------------------------------------------- @@ -55,6 +84,7 @@ type context struct { prog llssa.Program pkg llssa.Package fn llssa.Function + goPkg *ssa.Package bvals map[ssa.Value]llssa.Expr // block values inits []func() } @@ -88,6 +118,9 @@ func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) { if nblk == 0 { // external function return } + if debugGoSSA { + f.WriteTo(os.Stderr) + } if debugInstr { log.Println("==> FuncBody", name) } @@ -116,8 +149,15 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l switch v := iv.(type) { case *ssa.Call: call := v.Call + kind := funcKind(call.Value) + if kind == fnUnsafeInit { + return + } + if debugGoSSA { + log.Println(">>> Call", call.Value, call.Args) + } fn := p.compileValue(b, call.Value) - args := p.compileValues(b, call.Args) + args := p.compileValues(b, call.Args, kind) ret = b.Call(fn, args...) case *ssa.BinOp: x := p.compileValue(b, v.X) @@ -189,9 +229,15 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { } } case *ssa.Function: + if v.Pkg != p.goPkg { + panic("todo") + } fn := p.pkg.FuncOf(v.Name()) return fn.Expr case *ssa.Global: + if v.Pkg != p.goPkg { + panic("todo") + } g := p.pkg.VarOf(v.Name()) return g.Expr case *ssa.Const: @@ -201,10 +247,25 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { panic(fmt.Sprintf("compileValue: unknown value - %T\n", v)) } -func (p *context) compileValues(b llssa.Builder, vals []ssa.Value) []llssa.Expr { - ret := make([]llssa.Expr, len(vals)) - for i, v := range vals { - ret[i] = p.compileValue(b, v) +func (p *context) compileVArg(ret []llssa.Expr, b llssa.Builder, v ssa.Value) []llssa.Expr { + _ = b + switch v := v.(type) { + case *ssa.Const: + if v.Value == nil { + return ret + } + } + panic("todo") +} + +func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int) []llssa.Expr { + n := len(vals) - hasVArg + ret := make([]llssa.Expr, n) + for i := 0; i < n; i++ { + ret[i] = p.compileValue(b, vals[i]) + } + if hasVArg > 0 { + ret = p.compileVArg(ret, b, vals[n]) } return ret } @@ -238,8 +299,9 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.P ret = prog.NewPackage(pkgTypes.Name(), pkgTypes.Path()) ctx := &context{ - prog: prog, - pkg: ret, + prog: prog, + pkg: ret, + goPkg: pkg, } for _, m := range members { member := m.val diff --git a/cl/compile_test.go b/cl/compile_test.go index 150ddc6e..aac37468 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -34,7 +34,7 @@ import ( ) func TestFromTestdata(t *testing.T) { - testFromDir(t, "printf", "./_testdata") + testFromDir(t, "", "./_testdata") } func init() { @@ -94,11 +94,6 @@ func testCompileEx(t *testing.T, src any, fname, expected string) { t.Fatal("BuildPackage failed:", err) } foo.WriteTo(os.Stderr) - for _, m := range foo.Members { - if f, ok := m.(*ssa.Function); ok { - f.WriteTo(os.Stderr) - } - } prog := llssa.NewProgram(nil) ret, err := NewPackage(prog, foo, nil) if err != nil { diff --git a/ssa/expr.go b/ssa/expr.go index 8c6ce9ae..4787e404 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -258,7 +258,7 @@ func (b Builder) UnOp(op token.Token, x Expr) Expr { // Load returns the value at the pointer ptr. func (b Builder) Load(ptr Expr) Expr { if debugInstr { - log.Printf("Load @%v\n", ptr.impl.Name()) + log.Printf("Load %v\n", ptr.impl.Name()) } telem := b.prog.Elem(ptr.Type) return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem} @@ -267,7 +267,7 @@ func (b Builder) Load(ptr Expr) Expr { // Store stores val at the pointer ptr. func (b Builder) Store(ptr, val Expr) Builder { if debugInstr { - log.Printf("Store @%v, %v\n", ptr.impl.Name(), val.impl) + log.Printf("Store %v, %v\n", ptr.impl.Name(), val.impl) } b.impl.CreateStore(val.impl, ptr.impl) return b diff --git a/ssa/type.go b/ssa/type.go index c6cc8c28..cb35a885 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -42,15 +42,15 @@ const ( // ----------------------------------------------------------------------------- const ( - nameValist = "__llgo_va_list" + NameValist = "__llgo_va_list" ) func VArg() *types.Var { - return types.NewParam(0, nil, nameValist, types.Typ[types.Invalid]) + return types.NewParam(0, nil, NameValist, types.Typ[types.Invalid]) } func IsVArg(arg *types.Var) bool { - return arg.Name() == nameValist + return arg.Name() == NameValist } func HasVArg(t *types.Tuple, n int) bool {