TestBasicFunc

This commit is contained in:
xushiwei
2024-04-18 15:03:10 +08:00
parent 4fcffa46e1
commit 3a8ce26c6f
9 changed files with 215 additions and 96 deletions

2
go.mod
View File

@@ -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
)

2
go.sum
View File

@@ -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=

View File

@@ -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
}
// -----------------------------------------------------------------------------

View File

@@ -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}
}
// -----------------------------------------------------------------------------

View File

@@ -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")
}

View File

@@ -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")
}

View File

@@ -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()
}

View File

@@ -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
}
`)
}

View File

@@ -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")
}
// -----------------------------------------------------------------------------