diff --git a/go.mod b/go.mod index f8b4cbf2..8768b130 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/aykevl/go-wasm v0.0.1 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/goplus/gop v1.2.6 - github.com/goplus/llvm v0.7.1-0.20240417171659-4fb15c5dc82a + github.com/goplus/llvm v0.7.1-0.20240418070213-5013f6a4299b github.com/qiniu/x v1.13.10 golang.org/x/tools v0.19.0 ) diff --git a/go.sum b/go.sum index dda1662c..b557f82b 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/goplus/llvm v0.7.0 h1:b8XzmRA97U0V0BPSaYuZ2vw+lLO2JSpRLMtR6dAenIo= github.com/goplus/llvm v0.7.0/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= github.com/goplus/llvm v0.7.1-0.20240417171659-4fb15c5dc82a h1:pKOqI/f4lyPAlto2MT0JJsDCNMvRyF8jBT2saegwIK8= github.com/goplus/llvm v0.7.1-0.20240417171659-4fb15c5dc82a/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= +github.com/goplus/llvm v0.7.1-0.20240418070213-5013f6a4299b h1:sRVmYXGgKjqgaoVQ1bshnw9Ar77stDtjHC4A/PDJ0fk= +github.com/goplus/llvm v0.7.1-0.20240418070213-5013f6a4299b/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= github.com/goplus/mod v0.13.10 h1:5Om6KOvo31daN7N30kWU1vC5zhsJPM+uPbcEN/FnlzE= github.com/goplus/mod v0.13.10/go.mod h1:HDuPZgpWiaTp3PUolFgsiX+Q77cbUWB/mikVHfYND3c= github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE= diff --git a/ssa/builder.go b/ssa/builder.go index 3b6ec4ea..18ec7b96 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -20,13 +20,35 @@ import ( "github.com/goplus/llvm" ) -type Builder struct { - impl llvm.Builder -} - +// ----------------------------------------------------------------------------- +/* type BasicBlock struct { impl llvm.BasicBlock } +*/ +// ----------------------------------------------------------------------------- -func (p BasicBlock) End() { +type aBuilder struct { + impl llvm.Builder + prog Program } + +type Builder = *aBuilder + +func (b Builder) Return(results ...Expr) Builder { + switch n := len(results); n { + case 0: + b.impl.CreateRetVoid() + 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) + } + return b +} + +// ----------------------------------------------------------------------------- diff --git a/ssa/decl.go b/ssa/decl.go index 7d0843ae..a42f9cb6 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -20,6 +20,8 @@ import ( "github.com/goplus/llvm" ) +// ----------------------------------------------------------------------------- + // A NamedConst is a Member of a Package representing a package-level // named constant. // @@ -33,6 +35,8 @@ type aNamedConst struct { type NamedConst = *aNamedConst +// ----------------------------------------------------------------------------- + // A Global is a named Value holding the address of a package-level // variable. // @@ -40,10 +44,13 @@ type NamedConst = *aNamedConst // identifier. type aGlobal struct { impl llvm.Value + Type } type Global = *aGlobal +// ----------------------------------------------------------------------------- + // Function represents the parameters, results, and code of a function // or method. // @@ -95,12 +102,20 @@ type Global = *aGlobal // respectively, and is nil in the generic method. type aFunction struct { impl llvm.Value + Type + prog Program } type Function = *aFunction -func (p *aFunction) BodyStart() *BasicBlock { - body := llvm.AddBasicBlock(p.impl, "entry") - return &BasicBlock{body} +func (p *aFunction) MakeBody(label string) Builder { + body := llvm.AddBasicBlock(p.impl, label) + prog := p.prog + b := prog.ctx.NewBuilder() + b.Finalize() + b.SetInsertPointAtEnd(body) + return &aBuilder{b, prog} } + +// ----------------------------------------------------------------------------- diff --git a/ssa/expr.go b/ssa/expr.go index 4c151a79..5de23502 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -15,3 +15,21 @@ */ package ssa + +import ( + "github.com/goplus/llvm" +) + +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") +} diff --git a/ssa/operator.go b/ssa/operator.go index 31232763..7a318386 100644 --- a/ssa/operator.go +++ b/ssa/operator.go @@ -24,9 +24,9 @@ import ( type valueKind = int -type Value struct { +type Expr struct { impl llvm.Value - kind valueKind + Type } const ( @@ -42,6 +42,7 @@ const ( vkComplex vkString vkBool + vkFunc ) var mathOpToLLVM = []llvm.Opcode{ @@ -134,45 +135,44 @@ func isPredOp(op token.Token) bool { // ADD SUB MUL QUO REM + - * / % // AND OR XOR SHL SHR AND_NOT & | ^ << >> &^ // EQL NEQ LSS LEQ GTR GEQ == != < <= < >= -func (b Builder) BinOp(op token.Token, x, y Value) (v Value) { +func (b Builder) BinOp(op token.Token, x, y Expr) Expr { switch { case isMathOp(op): // op: + - * / % - switch x.kind { + kind := x.kind + switch kind { case vkString, vkComplex: panic("todo") } - idx := mathOpIdx(op, x.kind) + idx := mathOpIdx(op, kind) if llop := mathOpToLLVM[idx]; llop != 0 { - v.impl = llvm.CreateBinOp(b.impl, llop, x.impl, y.impl) - return + return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type} } case isLogicOp(op): // op: & | ^ << >> &^ if op == token.AND_NOT { panic("todo") } + kind := x.kind llop := logicOpToLLVM[op-logicOpBase] - if op == token.SHR && x.kind == vkUnsigned { + if op == token.SHR && kind == vkUnsigned { llop = llvm.AShr } - v.impl = llvm.CreateBinOp(b.impl, llop, x.impl, y.impl) - return + return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type} case isPredOp(op): // op: == != < <= < >= - switch x.kind { + tret := b.prog.Bool() + kind := x.kind + switch kind { case vkSigned: pred := intPredOpToLLVM[op-predOpBase] - v.impl = llvm.CreateICmp(b.impl, pred, x.impl, y.impl) - return + return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret} case vkUnsigned: pred := uintPredOpToLLVM[op-predOpBase] - v.impl = llvm.CreateICmp(b.impl, pred, x.impl, y.impl) - return + return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret} case vkFloat: pred := floatPredOpToLLVM[op-predOpBase] - v.impl = llvm.ConstFCmp(pred, x.impl, y.impl) - return + return Expr{llvm.ConstFCmp(pred, x.impl, y.impl), tret} case vkString, vkComplex, vkBool: panic("todo") } } - return + panic("todo") } diff --git a/ssa/package.go b/ssa/package.go index b0934708..f38e4565 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -27,7 +27,6 @@ import ( // A Program is a partial or complete Go program converted to SSA form. type aProgram struct { ctx llvm.Context - b Builder typs typeutil.Map target *Target @@ -35,12 +34,17 @@ type aProgram struct { // tm llvm.TargetMachine intType llvm.Type + int1Type llvm.Type int8Type llvm.Type int16Type llvm.Type int32Type llvm.Type int64Type llvm.Type voidType llvm.Type voidPtrTy llvm.Type + + boolTy Type + intTy Type + f64Ty Type } type Program = *aProgram @@ -51,20 +55,35 @@ func NewProgram(target *Target) Program { } ctx := llvm.NewContext() ctx.Finalize() - b := ctx.NewBuilder() - b.Finalize() td := llvm.NewTargetData("") // TODO(xsw): target config - return &aProgram{ctx: ctx, b: Builder{b}, target: target, td: td} + return &aProgram{ctx: ctx, target: target, td: td} } -func (p *aProgram) NewPackage(name, pkgPath string) Package { +func (p Program) NewPackage(name, pkgPath string) Package { mod := p.ctx.NewModule(pkgPath) mod.Finalize() return &aPackage{mod, p} } -func (p *aProgram) Builder() Builder { - return p.b +func (p Program) Bool() Type { + if p.boolTy == nil { + p.boolTy = p.llvmType(types.Typ[types.Bool]) + } + return p.boolTy +} + +func (p Program) Int() Type { + if p.intTy == nil { + p.intTy = p.llvmType(types.Typ[types.Int]) + } + return p.intTy +} + +func (p Program) Float64() Type { + if p.f64Ty == nil { + p.f64Ty = p.llvmType(types.Typ[types.Float64]) + } + return p.f64Ty } // A Package is a single analyzed Go package containing Members for @@ -82,21 +101,23 @@ type aPackage struct { type Package = *aPackage -func (p *aPackage) NewConst(name string, val constant.Value) NamedConst { +func (p Package) NewConst(name string, val constant.Value) NamedConst { return &aNamedConst{} } -func (p *aPackage) NewVar(name string, typ types.Type) Global { - gbl := llvm.AddGlobal(p.mod, p.prog.llvmType(typ), name) - return &aGlobal{gbl} +func (p Package) NewVar(name string, typ types.Type) Global { + t := p.prog.llvmType(typ) + gbl := llvm.AddGlobal(p.mod, t.ll, name) + return &aGlobal{gbl, t} } -func (p *aPackage) NewFunc(name string, sig *types.Signature) Function { - fn := llvm.AddFunction(p.mod, name, p.prog.llvmSignature(sig)) - return &aFunction{fn, p.prog} +func (p Package) NewFunc(name string, sig *types.Signature) Function { + t := p.prog.llvmSignature(sig) + fn := llvm.AddFunction(p.mod, name, t.ll) + return &aFunction{fn, t, p.prog} } -func (p *aPackage) String() string { +func (p Package) String() string { return p.mod.String() } diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 8bd2aabe..49adf334 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -79,7 +79,7 @@ source_filename = "foo/bar" `) } -func TestFunc(t *testing.T) { +func TestDeclFunc(t *testing.T) { prog := NewProgram(nil) pkg := prog.NewPackage("bar", "foo/bar") params := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int])) @@ -91,3 +91,19 @@ source_filename = "foo/bar" declare void @fn(i64) `) } + +func TestBasicFunc(t *testing.T) { + prog := NewProgram(nil) + pkg := prog.NewPackage("bar", "foo/bar") + rets := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int])) + sig := types.NewSignatureType(nil, nil, nil, nil, rets, false) + b := pkg.NewFunc("fn", sig).MakeBody("") + b.Return(prog.Val(1)) + assertPkg(t, pkg, `; ModuleID = 'foo/bar' +source_filename = "foo/bar" + +define i64 @fn() { + ret i64 1 +} +`) +} diff --git a/ssa/type.go b/ssa/type.go index 39132d64..c0ae36c5 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -22,107 +22,136 @@ import ( "github.com/goplus/llvm" ) -/* -// A Type is a Member of a Package representing a package-level named type. -type Type struct { - impl llvm.Type -} -*/ +// ----------------------------------------------------------------------------- -func (p *aProgram) llvmType(typ types.Type) llvm.Type { +type aType struct { + ll llvm.Type + t types.Type + kind valueKind +} + +type Type = *aType + +func (p Program) llvmType(typ types.Type) Type { if v := p.typs.At(typ); v != nil { - return v.(llvm.Type) + return v.(Type) } ret := p.toLLVMType(typ) p.typs.Set(typ, ret) return ret } -func (p *aProgram) llvmSignature(sig *types.Signature) llvm.Type { +func (p Program) llvmSignature(sig *types.Signature) Type { if v := p.typs.At(sig); v != nil { - return v.(llvm.Type) + return v.(Type) } ret := p.toLLVMFunc(sig) p.typs.Set(sig, ret) return ret } -func (p *aProgram) tyVoidPtr() llvm.Type { +func (p Program) tyVoidPtr() llvm.Type { if p.voidPtrTy.IsNil() { p.voidPtrTy = llvm.PointerType(p.tyVoid(), 0) } return p.voidPtrTy } -func (p *aProgram) tyVoid() llvm.Type { +func (p Program) tyVoid() llvm.Type { if p.voidType.IsNil() { p.voidType = p.ctx.VoidType() } return p.voidType } -func (p *aProgram) tyInt() llvm.Type { +func (p Program) tyInt1() llvm.Type { + if p.int1Type.IsNil() { + p.int1Type = p.ctx.Int1Type() + } + return p.int1Type +} + +func (p Program) tyInt() llvm.Type { if p.intType.IsNil() { p.intType = llvmIntType(p.ctx, p.td.PointerSize()) } return p.intType } -func (p *aProgram) tyInt8() llvm.Type { +func llvmIntType(ctx llvm.Context, size int) llvm.Type { + if size <= 4 { + return ctx.Int32Type() + } + return ctx.Int64Type() +} + +func (p Program) tyInt8() llvm.Type { if p.int8Type.IsNil() { p.int8Type = p.ctx.Int8Type() } return p.int8Type } -func (p *aProgram) tyInt16() llvm.Type { +func (p Program) tyInt16() llvm.Type { if p.int16Type.IsNil() { p.int16Type = p.ctx.Int16Type() } return p.int16Type } -func (p *aProgram) tyInt32() llvm.Type { +func (p Program) tyInt32() llvm.Type { if p.int32Type.IsNil() { p.int32Type = p.ctx.Int32Type() } return p.int32Type } -func (p *aProgram) tyInt64() llvm.Type { +func (p Program) tyInt64() llvm.Type { if p.int64Type.IsNil() { p.int64Type = p.ctx.Int64Type() } return p.int64Type } -func (p *aProgram) toLLVMType(typ types.Type) llvm.Type { +func (p Program) toLLVMType(typ types.Type) Type { switch t := typ.(type) { case *types.Basic: switch t.Kind() { - case types.Int, types.Uint, types.Uintptr: - return p.tyInt() - case types.Bool, types.Uint8, types.Int8: - return p.tyInt8() - case types.Int16, types.Uint16: - return p.tyInt16() - case types.Int32, types.Uint32: - return p.tyInt32() - case types.Int64, types.Uint64: - return p.tyInt64() + case types.Int: + return &aType{p.tyInt(), typ, vkSigned} + case types.Uint, types.Uintptr: + return &aType{p.tyInt(), typ, vkUnsigned} + case types.Bool: + return &aType{p.tyInt1(), typ, vkBool} + case types.Uint8: + return &aType{p.tyInt8(), typ, vkUnsigned} + case types.Int8: + return &aType{p.tyInt8(), typ, vkSigned} + case types.Int16: + return &aType{p.tyInt16(), typ, vkSigned} + case types.Uint16: + return &aType{p.tyInt16(), typ, vkUnsigned} + case types.Int32: + return &aType{p.tyInt32(), typ, vkSigned} + case types.Uint32: + return &aType{p.tyInt32(), typ, vkUnsigned} + case types.Int64: + return &aType{p.tyInt64(), typ, vkSigned} + case types.Uint64: + return &aType{p.tyInt64(), typ, vkUnsigned} case types.Float32: - return p.ctx.FloatType() + return &aType{p.ctx.FloatType(), typ, vkFloat} case types.Float64: - return p.ctx.DoubleType() + return &aType{p.ctx.DoubleType(), typ, vkFloat} case types.Complex64: case types.Complex128: case types.String: case types.UnsafePointer: - return p.tyVoidPtr() + return &aType{p.tyVoidPtr(), typ, vkInvalid} } case *types.Pointer: elem := p.llvmType(t.Elem()) - return llvm.PointerType(elem, 0) + return &aType{llvm.PointerType(elem.ll, 0), typ, vkInvalid} case *types.Slice: case *types.Map: case *types.Struct: @@ -133,54 +162,47 @@ func (p *aProgram) toLLVMType(typ types.Type) llvm.Type { return p.toLLVMFunc(t) case *types.Array: elem := p.llvmType(t.Elem()) - return llvm.ArrayType(elem, int(t.Len())) + return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid} case *types.Chan: } panic("todo") } -func llvmIntType(ctx llvm.Context, size int) llvm.Type { - if size <= 4 { - return ctx.Int32Type() - } - return ctx.Int64Type() -} - -func (p *aProgram) toLLVMNamedStruct(name string, typ *types.Struct) llvm.Type { +func (p Program) toLLVMNamedStruct(name string, typ *types.Struct) llvm.Type { t := p.ctx.StructCreateNamed(name) fields := p.toLLVMFields(typ) t.StructSetBody(fields, false) return t } -func (p *aProgram) toLLVMStruct(typ *types.Struct) llvm.Type { +func (p Program) toLLVMStruct(typ *types.Struct) Type { fields := p.toLLVMFields(typ) - return p.ctx.StructType(fields, false) + return &aType{p.ctx.StructType(fields, false), typ, vkInvalid} } -func (p *aProgram) toLLVMFields(typ *types.Struct) []llvm.Type { +func (p Program) toLLVMFields(typ *types.Struct) []llvm.Type { n := typ.NumFields() fields := make([]llvm.Type, n) for i := 0; i < n; i++ { - fields[i] = p.llvmType(typ.Field(i).Type()) + fields[i] = p.llvmType(typ.Field(i).Type()).ll } return fields } -func (p *aProgram) toLLVMTuple(t *types.Tuple) llvm.Type { +func (p Program) toLLVMTuple(t *types.Tuple) llvm.Type { return p.ctx.StructType(p.toLLVMTypes(t), false) } -func (p *aProgram) toLLVMTypes(t *types.Tuple) []llvm.Type { +func (p Program) toLLVMTypes(t *types.Tuple) []llvm.Type { n := t.Len() ret := make([]llvm.Type, n) for i := 0; i < n; i++ { - ret[i] = p.llvmType(t.At(i).Type()) + ret[i] = p.llvmType(t.At(i).Type()).ll } return ret } -func (p *aProgram) toLLVMFunc(sig *types.Signature) llvm.Type { +func (p Program) toLLVMFunc(sig *types.Signature) Type { params := p.toLLVMTypes(sig.Params()) results := sig.Results() var ret llvm.Type @@ -188,18 +210,21 @@ func (p *aProgram) toLLVMFunc(sig *types.Signature) llvm.Type { case 0: ret = p.tyVoid() case 1: - ret = p.llvmType(results.At(0).Type()) + ret = p.llvmType(results.At(0).Type()).ll default: ret = p.toLLVMTuple(results) } - return llvm.FunctionType(ret, params, sig.Variadic()) + ft := llvm.FunctionType(ret, params, sig.Variadic()) + return &aType{ft, sig, vkFunc} } -func (p *aProgram) toLLVMNamed(typ *types.Named) llvm.Type { +func (p Program) toLLVMNamed(typ *types.Named) Type { name := typ.Obj().Name() switch typ := typ.Underlying().(type) { case *types.Struct: - return p.toLLVMNamedStruct(name, typ) + return &aType{p.toLLVMNamedStruct(name, typ), typ, vkInvalid} } panic("todo") } + +// -----------------------------------------------------------------------------