From c784a2e63bd626273257897de64871d5acaa8a29 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 19 Apr 2024 00:05:57 +0800 Subject: [PATCH] TestFuncCall --- ssa/builder.go | 6 +---- ssa/decl.go | 34 ++++++++++++++++---------- ssa/expr.go | 63 +++++++++++++++++++++++++++++-------------------- ssa/package.go | 57 ++++++++++++++++++++++++++++++++++++++++++++ ssa/ssa_test.go | 36 ++++++++++++++++++++++++++++ ssa/target.go | 14 +++-------- ssa/type.go | 36 ++++++++++++++++++++++++---- 7 files changed, 188 insertions(+), 58 deletions(-) diff --git a/ssa/builder.go b/ssa/builder.go index 18ec7b96..a27fd4e2 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -42,11 +42,7 @@ func (b Builder) Return(results ...Expr) Builder { case 1: b.impl.CreateRet(results[0].impl) default: - rets := make([]llvm.Value, n) - for i, v := range results { - rets[i] = v.impl - } - b.impl.CreateAggregateRet(rets) + b.impl.CreateAggregateRet(llvmValues(results)) } return b } diff --git a/ssa/decl.go b/ssa/decl.go index 83b3bbb2..8871f615 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -103,29 +103,39 @@ type Global = *aGlobal // the generic method. TypeArgs() refers to [string,U] or [string,int], // respectively, and is nil in the generic method. type aFunction struct { - impl llvm.Value - Type - + Expr prog Program params []Type + ret Type } type Function = *aFunction func newFunction(fn llvm.Value, t Type, prog Program) Function { - ret := &aFunction{fn, t, prog, newParams(t, prog)} - return ret + params, ret := newParamsAndRet(t, prog) + return &aFunction{Expr{fn, t}, prog, params, ret} } -func newParams(fn Type, prog Program) []Type { - in := fn.t.(*types.Signature).Params() - n := in.Len() - ret := make([]Type, n) - for i := 0; i < n; i++ { - ret[i] = prog.llvmType(in.At(i).Type()) +func newParamsAndRet(fn Type, prog Program) (params []Type, ret Type) { + sig := fn.t.(*types.Signature) + in := sig.Params() + if n := in.Len(); n > 0 { + params = make([]Type, n) + for i := 0; i < n; i++ { + params[i] = prog.llvmType(in.At(i).Type()) + } } - return ret + out := sig.Results() + switch n := out.Len(); n { + case 0: + ret = prog.Void() + case 1: + ret = prog.llvmType(out.At(0).Type()) + default: + ret = &aType{prog.toLLVMTuple(out), out, vkTuple} + } + return } func (p Function) Param(i int) Expr { diff --git a/ssa/expr.go b/ssa/expr.go index b0062682..872a52b0 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -18,37 +18,51 @@ package ssa import ( "go/token" + "go/types" "github.com/goplus/llvm" ) // ----------------------------------------------------------------------------- -type valueKind = int - type Expr struct { impl llvm.Value Type } +// ----------------------------------------------------------------------------- + +func llvmValues(vals []Expr) []llvm.Value { + ret := make([]llvm.Value, len(vals)) + for i, v := range vals { + ret[i] = v.impl + } + return ret +} + +// ----------------------------------------------------------------------------- + +func (p Program) Val(v interface{}) Expr { + switch v := v.(type) { + case int: + t := p.Int() + ret := llvm.ConstInt(t.ll, uint64(v), false) + return Expr{ret, t} + case float64: + t := p.Float64() + ret := llvm.ConstFloat(t.ll, v) + return Expr{ret, t} + } + panic("todo") +} + +// ----------------------------------------------------------------------------- + const ( mathOpBase = token.ADD mathOpLast = token.REM ) -const ( - vkInvalid valueKind = iota - vkSigned - vkUnsigned - vkFloat - vkComplex - vkString - vkBool - vkFunc -) - -// ----------------------------------------------------------------------------- - var mathOpToLLVM = []llvm.Opcode{ int(token.ADD-mathOpBase)<<2 | vkSigned: llvm.Add, int(token.ADD-mathOpBase)<<2 | vkUnsigned: llvm.Add, @@ -183,18 +197,15 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr { // ----------------------------------------------------------------------------- -func (p Program) Val(v interface{}) Expr { - switch v := v.(type) { - case int: - t := p.Int() - ret := llvm.ConstInt(t.ll, uint64(v), false) - return Expr{ret, t} - case float64: - t := p.Float64() - ret := llvm.ConstFloat(t.ll, v) - return Expr{ret, t} +func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { + switch t := fn.t.(type) { + case *types.Signature: + ret.Type = b.prog.retType(t) + default: + panic("todo") } - panic("todo") + ret.impl = llvm.CreateCall(b.impl, fn.ll, fn.impl, llvmValues(args)) + return } // ----------------------------------------------------------------------------- diff --git a/ssa/package.go b/ssa/package.go index 2ebb9015..ae04a014 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -24,6 +24,51 @@ import ( "golang.org/x/tools/go/types/typeutil" ) +// ----------------------------------------------------------------------------- + +type InitFlags int + +const ( + InitNativeTarget InitFlags = 1 << iota + InitAllTargets + InitAllTargetInfos + InitAllTargetMCs + + InitNativeAsmPrinter + InitAllAsmPrinters + + InitAllAsmParsers + + InitNative = InitNativeTarget | InitNativeAsmPrinter + InitAll = InitAllTargets | InitAllAsmParsers | InitAllAsmPrinters | InitAllTargetInfos | InitAllTargetMCs +) + +func Initialize(flags InitFlags) { + if flags&InitAllTargetInfos != 0 { + llvm.InitializeAllTargetInfos() + } + if flags&InitAllTargets != 0 { + llvm.InitializeAllTargets() + } + if flags&InitAllTargetMCs != 0 { + llvm.InitializeAllTargetMCs() + } + if flags&InitAllAsmParsers != 0 { + llvm.InitializeAllAsmParsers() + } + if flags&InitAllAsmPrinters != 0 { + llvm.InitializeAllAsmPrinters() + } + if flags&InitNativeTarget != 0 { + llvm.InitializeNativeTarget() + } + if flags&InitNativeAsmPrinter != 0 { + llvm.InitializeNativeAsmPrinter() + } +} + +// ----------------------------------------------------------------------------- + // A Program is a partial or complete Go program converted to SSA form. type aProgram struct { ctx llvm.Context @@ -42,6 +87,7 @@ type aProgram struct { voidType llvm.Type voidPtrTy llvm.Type + voidTy Type boolTy Type intTy Type f64Ty Type @@ -65,6 +111,13 @@ func (p Program) NewPackage(name, pkgPath string) Package { return &aPackage{mod, p} } +func (p Program) Void() Type { + if p.voidTy == nil { + p.voidTy = &aType{p.tyVoid(), types.Typ[types.Invalid], vkInvalid} + } + return p.voidTy +} + func (p Program) Bool() Type { if p.boolTy == nil { p.boolTy = p.llvmType(types.Typ[types.Bool]) @@ -86,6 +139,8 @@ func (p Program) Float64() Type { return p.f64Ty } +// ----------------------------------------------------------------------------- + // A Package is a single analyzed Go package containing Members for // all package-level functions, variables, constants and types it // declares. These may be accessed directly via Members, or via the @@ -160,3 +215,5 @@ func (p *Package) WriteFile(file string) (err error) { return llvm.WriteBitcodeToFile(p.mod, f) } */ + +// ----------------------------------------------------------------------------- diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 9900d171..208f6b62 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -22,6 +22,10 @@ import ( "testing" ) +func init() { + Initialize(InitAll) +} + /* func asmPkg(t *testing.T, p *Package) { b, err := p.CodeGen(AssemblyFile) @@ -131,6 +135,38 @@ define i64 @fn(i64 %0, double %1) { `) } +func TestFuncCall(t *testing.T) { + prog := NewProgram(nil) + pkg := prog.NewPackage("bar", "foo/bar") + + params := types.NewTuple( + types.NewVar(0, nil, "a", types.Typ[types.Int]), + types.NewVar(0, nil, "b", types.Typ[types.Float64])) + rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) + sig := types.NewSignatureType(nil, nil, nil, params, rets, false) + fn := pkg.NewFunc("fn", sig) + fn.MakeBody(""). + Return(prog.Val(1)) + + sigMain := types.NewSignatureType(nil, nil, nil, nil, nil, false) + b := pkg.NewFunc("main", sigMain).MakeBody("") + b.Call(fn.Expr, prog.Val(1), prog.Val(1.2)) + b.Return() + + assertPkg(t, pkg, `; ModuleID = 'foo/bar' +source_filename = "foo/bar" + +define i64 @fn(i64 %0, double %1) { + ret i64 1 +} + +define void @main() { + %1 = call i64 @fn(i64 1, double 1.200000e+00) + ret void +} +`) +} + func TestBinOp(t *testing.T) { prog := NewProgram(nil) pkg := prog.NewPackage("bar", "foo/bar") diff --git a/ssa/target.go b/ssa/target.go index a9d95735..d570a360 100644 --- a/ssa/target.go +++ b/ssa/target.go @@ -16,17 +16,7 @@ package ssa -import ( - "github.com/goplus/llvm" -) - -func init() { - llvm.InitializeAllTargetInfos() - llvm.InitializeAllTargets() - llvm.InitializeAllTargetMCs() - llvm.InitializeAllAsmParsers() - llvm.InitializeAllAsmPrinters() -} +// ----------------------------------------------------------------------------- type Target struct { GOOS string @@ -150,3 +140,5 @@ func (p *Target) toSpec() (spec targetSpec) { return } */ + +// ----------------------------------------------------------------------------- diff --git a/ssa/type.go b/ssa/type.go index c0ae36c5..ad089ae9 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -24,6 +24,22 @@ import ( // ----------------------------------------------------------------------------- +type valueKind = int + +const ( + vkInvalid valueKind = iota + vkSigned + vkUnsigned + vkFloat + vkComplex + vkString + vkBool + vkFunc + vkTuple +) + +// ----------------------------------------------------------------------------- + type aType struct { ll llvm.Type t types.Type @@ -204,20 +220,32 @@ func (p Program) toLLVMTypes(t *types.Tuple) []llvm.Type { func (p Program) toLLVMFunc(sig *types.Signature) Type { params := p.toLLVMTypes(sig.Params()) - results := sig.Results() + out := sig.Results() var ret llvm.Type - switch nret := results.Len(); nret { + switch nret := out.Len(); nret { case 0: ret = p.tyVoid() case 1: - ret = p.llvmType(results.At(0).Type()).ll + ret = p.llvmType(out.At(0).Type()).ll default: - ret = p.toLLVMTuple(results) + ret = p.toLLVMTuple(out) } ft := llvm.FunctionType(ret, params, sig.Variadic()) return &aType{ft, sig, vkFunc} } +func (p Program) retType(sig *types.Signature) Type { + out := sig.Results() + switch n := out.Len(); n { + case 0: + return p.Void() + case 1: + return p.llvmType(out.At(0).Type()) + default: + return &aType{p.toLLVMTuple(out), out, vkTuple} + } +} + func (p Program) toLLVMNamed(typ *types.Named) Type { name := typ.Obj().Name() switch typ := typ.Underlying().(type) {