cl: _testdata/printf: almost done

This commit is contained in:
xushiwei
2024-04-21 15:12:57 +08:00
parent 8f31e4a6d3
commit 4641cb11eb
12 changed files with 305 additions and 265 deletions

View File

@@ -130,7 +130,7 @@ func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
}
params = make([]Type, n)
for i := 0; i < n; i++ {
params[i] = prog.llvmType(in.At(i).Type())
params[i] = prog.Type(in.At(i).Type())
}
}
return

View File

@@ -17,9 +17,12 @@
package ssa
import (
"bytes"
"fmt"
"go/constant"
"go/token"
"go/types"
"log"
"github.com/goplus/llvm"
)
@@ -57,16 +60,15 @@ func (p Program) BoolVal(v bool) Expr {
return Expr{ret, t}
}
func (p Program) IntVal(v int) Expr {
t := p.Int()
ret := llvm.ConstInt(t.ll, uint64(v), false)
func (p Program) IntVal(v uint64, t Type) Expr {
ret := llvm.ConstInt(t.ll, v, false)
return Expr{ret, t}
}
func (p Program) Val(v interface{}) Expr {
switch v := v.(type) {
case int:
return p.IntVal(v)
return p.IntVal(uint64(v), p.Int())
case bool:
return p.BoolVal(v)
case float64:
@@ -77,15 +79,16 @@ func (p Program) Val(v interface{}) Expr {
panic("todo")
}
func (b Builder) Const(v constant.Value, t types.Type) Expr {
switch t := t.(type) {
func (b Builder) Const(v constant.Value, typ Type) Expr {
switch t := typ.t.(type) {
case *types.Basic:
switch t.Kind() {
case types.Bool:
kind := t.Kind()
switch {
case kind == types.Bool:
return b.prog.BoolVal(constant.BoolVal(v))
case types.Int:
if v, exact := constant.Int64Val(v); exact {
return b.prog.IntVal(int(v))
case kind >= types.Int && kind <= types.Uintptr:
if v, exact := constant.Uint64Val(v); exact {
return b.prog.IntVal(v, typ)
}
}
}
@@ -191,6 +194,9 @@ func isPredOp(op token.Token) bool {
// AND OR XOR SHL SHR AND_NOT & | ^ << >> &^
// EQL NEQ LSS LEQ GTR GEQ == != < <= < >=
func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
if debugInstr {
log.Printf("BinOp %d, %v, %v\n", op, x.impl, y.impl)
}
switch {
case isMathOp(op): // op: + - * / %
kind := x.kind
@@ -243,25 +249,109 @@ func (b Builder) UnOp(op token.Token, x Expr) Expr {
case token.MUL:
return b.Load(x)
}
if debugInstr {
log.Printf("UnOp %v, %v\n", op, x.impl)
}
panic("todo")
}
// Load returns the value at the pointer ptr.
func (b Builder) Load(ptr Expr) Expr {
elem := ptr.t.(*types.Pointer).Elem()
telem := b.prog.llvmType(elem)
if debugInstr {
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}
}
// 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)
}
b.impl.CreateStore(val.impl, ptr.impl)
return b
}
// The IndexAddr instruction yields the address of the element at
// index `idx` of collection `x`. `idx` is an integer expression.
//
// The elements of maps and strings are not addressable; use Lookup (map),
// Index (string), or MapUpdate instead.
//
// Dynamically, this instruction panics if `x` evaluates to a nil *array
// pointer.
//
// Example printed form:
//
// t2 = &t0[t1]
func (b Builder) IndexAddr(x, idx Expr) Expr {
if debugInstr {
log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl)
}
prog := b.prog
telem := prog.Index(x.Type)
pt := prog.Pointer(telem)
indices := []llvm.Value{idx.impl}
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, x.impl, indices), pt}
}
// The Alloc instruction reserves space for a variable of the given type,
// zero-initializes it, and yields its address.
//
// If heap is false, Alloc zero-initializes the same local variable in
// the call frame and returns its address; in this case the Alloc must
// be present in Function.Locals. We call this a "local" alloc.
//
// If heap is true, Alloc allocates a new zero-initialized variable
// each time the instruction is executed. We call this a "new" alloc.
//
// When Alloc is applied to a channel, map or slice type, it returns
// the address of an uninitialized (nil) reference of that kind; store
// the result of MakeSlice, MakeMap or MakeChan in that location to
// instantiate these types.
//
// Example printed form:
//
// t0 = local int
// t1 = new int
func (b Builder) Alloc(t Type, heap bool) (ret Expr) {
if debugInstr {
log.Printf("Alloc %v, %v\n", t.ll, heap)
}
telem := b.prog.Elem(t)
if heap {
ret.impl = llvm.CreateAlloca(b.impl, telem.ll)
} else {
panic("todo")
}
// TODO: zero-initialize
ret.Type = t
return
}
// -----------------------------------------------------------------------------
// The Call instruction represents a function or method call.
//
// The Call instruction yields the function result if there is exactly
// one. Otherwise it returns a tuple, the components of which are
// accessed via Extract.
//
// Example printed form:
//
// t2 = println(t0, t1)
// t4 = t3()
// t7 = invoke t5.Println(...t6)
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
if debugInstr {
var b bytes.Buffer
fmt.Fprint(&b, "Call @", fn.impl.Name())
for _, arg := range args {
fmt.Fprint(&b, ", ", arg.impl)
}
log.Println(b.String())
}
switch t := fn.t.(type) {
case *types.Signature:
ret.Type = b.prog.retType(t)

View File

@@ -19,11 +19,29 @@ package ssa
import (
"go/constant"
"go/types"
"log"
"github.com/goplus/llvm"
"golang.org/x/tools/go/types/typeutil"
)
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
}
// -----------------------------------------------------------------------------
// InitFlags is a set of flags for initializing the LLVM library.
@@ -112,7 +130,9 @@ func NewProgram(target *Target) Program {
func (p Program) NewPackage(name, pkgPath string) Package {
mod := p.ctx.NewModule(pkgPath)
mod.Finalize()
return &aPackage{mod, p}
fns := make(map[string]Function)
gbls := make(map[string]Global)
return &aPackage{mod, fns, gbls, p}
}
// Void returns void type.
@@ -126,7 +146,7 @@ func (p Program) Void() Type {
// Bool returns bool type.
func (p Program) Bool() Type {
if p.boolTy == nil {
p.boolTy = p.llvmType(types.Typ[types.Bool])
p.boolTy = p.Type(types.Typ[types.Bool])
}
return p.boolTy
}
@@ -134,7 +154,7 @@ func (p Program) Bool() Type {
// Int returns int type.
func (p Program) Int() Type {
if p.intTy == nil {
p.intTy = p.llvmType(types.Typ[types.Int])
p.intTy = p.Type(types.Typ[types.Int])
}
return p.intTy
}
@@ -142,7 +162,7 @@ func (p Program) Int() Type {
// Float64 returns float64 type.
func (p Program) Float64() Type {
if p.f64Ty == nil {
p.f64Ty = p.llvmType(types.Typ[types.Float64])
p.f64Ty = p.Type(types.Typ[types.Float64])
}
return p.f64Ty
}
@@ -159,6 +179,8 @@ func (p Program) Float64() Type {
// and unspecified other things too.
type aPackage struct {
mod llvm.Module
fns map[string]Function
vars map[string]Global
prog Program
}
@@ -170,18 +192,40 @@ 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 {
t := p.prog.llvmType(typ)
if debugInstr {
log.Println("==> NewVar", name, typ)
}
t := p.prog.Type(typ)
gbl := llvm.AddGlobal(p.mod, t.ll, name)
return &aGlobal{Expr{gbl, t}}
ret := &aGlobal{Expr{gbl, t}}
p.vars[name] = ret
return ret
}
// 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)
return newFunction(fn, t, p.prog)
ret := newFunction(fn, t, p.prog)
p.fns[name] = ret
return ret
}
// FuncOf returns a function by name.
func (p Package) FuncOf(name string) Function {
return p.fns[name]
}
// VarOf returns a global variable by name.
func (p Package) VarOf(name string) Global {
return p.vars[name]
}
// -----------------------------------------------------------------------------
// String returns a string representation of the package.
func (p Package) String() string {
return p.mod.String()

View File

@@ -25,6 +25,7 @@ import (
func init() {
Initialize(InitAll)
SetDebug(DbgFlagAll)
}
func assertPkg(t *testing.T, p Package, expected string) {
@@ -55,7 +56,7 @@ func TestConst(t *testing.T) {
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Bool]))
sig := types.NewSignatureType(nil, nil, nil, nil, rets, false)
b := pkg.NewFunc("fn", sig).MakeBody(1)
b.Return(b.Const(constant.MakeBool(true), types.Typ[types.Bool]))
b.Return(b.Const(constant.MakeBool(true), prog.Bool()))
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
source_filename = "foo/bar"

View File

@@ -17,6 +17,10 @@
package ssa
import (
"bytes"
"fmt"
"log"
"github.com/goplus/llvm"
)
@@ -57,12 +61,26 @@ func (b Builder) SetBlock(blk BasicBlock) Builder {
if b.fn != blk.fn {
panic("mismatched function")
}
if debugInstr {
log.Printf("Block _llgo_%v:\n", blk.idx)
}
b.impl.SetInsertPointAtEnd(blk.impl)
return b
}
// Return emits a return instruction.
func (b Builder) Return(results ...Expr) {
if debugInstr {
var b bytes.Buffer
fmt.Fprint(&b, "Return ")
for i, arg := range results {
if i > 0 {
fmt.Fprint(&b, ", ")
}
fmt.Fprint(&b, arg.impl)
}
log.Println(b.String())
}
switch n := len(results); n {
case 0:
b.impl.CreateRetVoid()
@@ -78,6 +96,9 @@ func (b Builder) Jump(jmpb BasicBlock) {
if b.fn != jmpb.fn {
panic("mismatched function")
}
if debugInstr {
log.Printf("Jump _llgo_%v\n", jmpb.idx)
}
b.impl.CreateBr(jmpb.impl)
}
@@ -86,6 +107,9 @@ func (b Builder) If(cond Expr, thenb, elseb BasicBlock) {
if b.fn != thenb.fn || b.fn != elseb.fn {
panic("mismatched function")
}
if debugInstr {
log.Printf("If %v, _llgo_%v, _llgo_%v\n", cond.impl, thenb.idx, elseb.idx)
}
b.impl.CreateCondBr(cond.impl, thenb.impl, elseb.impl)
}

View File

@@ -57,6 +57,21 @@ func HasVArg(t *types.Tuple, n int) bool {
return n > 0 && IsVArg(t.At(n-1))
}
func indexType(t types.Type) types.Type {
switch t := t.(type) {
case *types.Slice:
return t.Elem()
case *types.Pointer:
switch t := t.Elem().(type) {
case *types.Array:
return t.Elem()
}
case *types.Array:
return t.Elem()
}
panic("index: type doesn't support index - " + t.String())
}
// -----------------------------------------------------------------------------
type aType struct {
@@ -67,7 +82,20 @@ type aType struct {
type Type = *aType
func (p Program) llvmType(typ types.Type) Type {
func (p Program) Pointer(typ Type) Type {
return p.Type(types.NewPointer(typ.t))
}
func (p Program) Elem(typ Type) Type {
elem := typ.t.(*types.Pointer).Elem()
return p.Type(elem)
}
func (p Program) Index(typ Type) Type {
return p.Type(indexType(typ.t))
}
func (p Program) Type(typ types.Type) Type {
if v := p.typs.At(typ); v != nil {
return v.(Type)
}
@@ -185,7 +213,7 @@ func (p Program) toLLVMType(typ types.Type) Type {
return &aType{p.tyVoidPtr(), typ, vkInvalid}
}
case *types.Pointer:
elem := p.llvmType(t.Elem())
elem := p.Type(t.Elem())
return &aType{llvm.PointerType(elem.ll, 0), typ, vkInvalid}
case *types.Slice:
case *types.Map:
@@ -196,7 +224,7 @@ func (p Program) toLLVMType(typ types.Type) Type {
case *types.Signature:
return p.toLLVMFunc(t)
case *types.Array:
elem := p.llvmType(t.Elem())
elem := p.Type(t.Elem())
return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid}
case *types.Chan:
}
@@ -221,7 +249,7 @@ func (p Program) toLLVMFields(typ *types.Struct) (fields []llvm.Type) {
if n > 0 {
fields = make([]llvm.Type, n)
for i := 0; i < n; i++ {
fields[i] = p.llvmType(typ.Field(i).Type()).ll
fields[i] = p.Type(typ.Field(i).Type()).ll
}
}
return
@@ -235,7 +263,7 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) {
if n > 0 {
ret = make([]llvm.Type, n)
for i := 0; i < n; i++ {
ret[i] = p.llvmType(t.At(i).Type()).ll
ret[i] = p.Type(t.At(i).Type()).ll
}
}
return
@@ -255,7 +283,7 @@ func (p Program) toLLVMFunc(sig *types.Signature) Type {
case 0:
ret = p.tyVoid()
case 1:
ret = p.llvmType(out.At(0).Type()).ll
ret = p.Type(out.At(0).Type()).ll
default:
ret = p.toLLVMTuple(out)
}
@@ -269,7 +297,7 @@ func (p Program) retType(sig *types.Signature) Type {
case 0:
return p.Void()
case 1:
return p.llvmType(out.At(0).Type())
return p.Type(out.At(0).Type())
default:
return &aType{p.toLLVMTuple(out), out, vkTuple}
}