Merge pull request #31 from xushiwei/q
cl: _testdata/printf: almost done
This commit is contained in:
12
cl/_testdata/printf/in.go
Normal file
12
cl/_testdata/printf/in.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname printf _printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
var hello = [...]int8{'H', 'e', 'l', 'l', 'o', '\n', 0}
|
||||
|
||||
func main() {
|
||||
printf(&hello[0])
|
||||
}
|
||||
32
cl/_testdata/printf/out.ll
Normal file
32
cl/_testdata/printf/out.ll
Normal file
@@ -0,0 +1,32 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"init$guard" = global ptr null
|
||||
@hello = global ptr null
|
||||
|
||||
define void @init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"init$guard", align 1
|
||||
store i8 72, ptr @hello, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @hello, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @hello, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @hello, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @hello, i64 4), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @hello, i64 5), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @hello, i64 6), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
ret void
|
||||
}
|
||||
@@ -38,9 +38,8 @@ type context struct {
|
||||
prog llssa.Program
|
||||
pkg llssa.Package
|
||||
fn llssa.Function
|
||||
fns map[*ssa.Function]llssa.Function
|
||||
glbs map[*ssa.Global]llssa.Global
|
||||
bvals map[ssa.Value]llssa.Expr // block values
|
||||
inits []func()
|
||||
}
|
||||
|
||||
func (p *context) compileType(pkg llssa.Package, member *ssa.Type) {
|
||||
@@ -48,41 +47,28 @@ func (p *context) compileType(pkg llssa.Package, member *ssa.Type) {
|
||||
}
|
||||
|
||||
// Global variable.
|
||||
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) llssa.Global {
|
||||
if g, ok := p.glbs[gbl]; ok {
|
||||
return g
|
||||
}
|
||||
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
||||
g := pkg.NewVar(gbl.Name(), gbl.Type())
|
||||
g.Init(p.prog.Null(g.Type))
|
||||
p.glbs[gbl] = g
|
||||
return g
|
||||
}
|
||||
|
||||
func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) llssa.Function {
|
||||
if fn, ok := p.fns[f]; ok {
|
||||
return fn
|
||||
}
|
||||
fn := p.doCompileFunc(pkg, f)
|
||||
p.fns[f] = fn
|
||||
return fn
|
||||
}
|
||||
|
||||
func (p *context) doCompileFunc(pkg llssa.Package, f *ssa.Function) (fn llssa.Function) {
|
||||
fn = pkg.NewFunc(f.Name(), f.Signature)
|
||||
p.fn = fn
|
||||
defer func() {
|
||||
p.fn = nil
|
||||
}()
|
||||
nblk := len(f.Blocks)
|
||||
if nblk == 0 { // external function
|
||||
return
|
||||
}
|
||||
fn.MakeBlocks(nblk)
|
||||
b := fn.NewBuilder()
|
||||
for _, block := range f.Blocks {
|
||||
p.compileBlock(b, block)
|
||||
}
|
||||
return
|
||||
func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) {
|
||||
fn := pkg.NewFunc(f.Name(), f.Signature)
|
||||
p.inits = append(p.inits, func() {
|
||||
p.fn = fn
|
||||
defer func() {
|
||||
p.fn = nil
|
||||
}()
|
||||
nblk := len(f.Blocks)
|
||||
if nblk == 0 { // external function
|
||||
return
|
||||
}
|
||||
fn.MakeBlocks(nblk)
|
||||
b := fn.NewBuilder()
|
||||
for _, block := range f.Blocks {
|
||||
p.compileBlock(b, block)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock) llssa.BasicBlock {
|
||||
@@ -101,10 +87,12 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l
|
||||
}
|
||||
switch v := iv.(type) {
|
||||
case *ssa.Call:
|
||||
call := v.Call
|
||||
fn := p.compileValue(b, call.Value)
|
||||
args := p.compileValues(b, call.Args)
|
||||
ret = b.Call(fn, args...)
|
||||
if false {
|
||||
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)
|
||||
@@ -112,6 +100,13 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l
|
||||
case *ssa.UnOp:
|
||||
x := p.compileValue(b, v.X)
|
||||
ret = b.UnOp(v.Op, x)
|
||||
case *ssa.IndexAddr:
|
||||
x := p.compileValue(b, v.X)
|
||||
idx := p.compileValue(b, v.Index)
|
||||
ret = b.IndexAddr(x, idx)
|
||||
case *ssa.Alloc:
|
||||
t := v.Type()
|
||||
ret = b.Alloc(p.prog.Type(t), v.Heap)
|
||||
default:
|
||||
panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv))
|
||||
}
|
||||
@@ -168,13 +163,14 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
|
||||
}
|
||||
}
|
||||
case *ssa.Function:
|
||||
fn := p.compileFunc(p.pkg, v)
|
||||
fn := p.pkg.FuncOf(v.Name())
|
||||
return fn.Expr
|
||||
case *ssa.Global:
|
||||
g := p.compileGlobal(p.pkg, v)
|
||||
g := p.pkg.VarOf(v.Name())
|
||||
return g.Expr
|
||||
case *ssa.Const:
|
||||
return b.Const(v.Value, v.Type())
|
||||
t := v.Type()
|
||||
return b.Const(v.Value, p.prog.Type(t))
|
||||
}
|
||||
panic(fmt.Sprintf("compileValue: unknown value - %T\n", v))
|
||||
}
|
||||
@@ -215,8 +211,6 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.P
|
||||
ctx := &context{
|
||||
prog: prog,
|
||||
pkg: ret,
|
||||
fns: make(map[*ssa.Function]llssa.Function),
|
||||
glbs: make(map[*ssa.Global]llssa.Global),
|
||||
}
|
||||
for _, m := range members {
|
||||
member := m.val
|
||||
@@ -233,6 +227,9 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.P
|
||||
ctx.compileGlobal(ret, member)
|
||||
}
|
||||
}
|
||||
for _, ini := range ctx.inits {
|
||||
ini()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,12 @@ import (
|
||||
)
|
||||
|
||||
func TestFromTestdata(t *testing.T) {
|
||||
testFromDir(t, "", "./_testdata")
|
||||
testFromDir(t, "printf", "./_testdata")
|
||||
}
|
||||
|
||||
func init() {
|
||||
llssa.Initialize(llssa.InitAll)
|
||||
llssa.SetDebug(llssa.DbgFlagAll)
|
||||
}
|
||||
|
||||
func testFromDir(t *testing.T, sel, relDir string) {
|
||||
|
||||
2
go.mod
2
go.mod
@@ -5,7 +5,7 @@ go 1.18
|
||||
require (
|
||||
github.com/aykevl/go-wasm v0.0.1
|
||||
github.com/goplus/gop v1.2.6
|
||||
github.com/goplus/llvm v0.7.1-0.20240418160956-6233231cbcc9
|
||||
github.com/goplus/llvm v0.7.1-0.20240420180312-6230a4ea7a47
|
||||
github.com/qiniu/x v1.13.10
|
||||
golang.org/x/tools v0.19.0
|
||||
)
|
||||
|
||||
2
go.sum
2
go.sum
@@ -6,6 +6,8 @@ github.com/goplus/gop v1.2.6 h1:kog3c5Js+8EopqmI4+CwueXsqibnBwYVt5q5N7juRVY=
|
||||
github.com/goplus/gop v1.2.6/go.mod h1:uREWbR1MrFaviZ4Mbx4ZCcAYDoqzO0iv1Qo6Np0Xx4E=
|
||||
github.com/goplus/llvm v0.7.1-0.20240418160956-6233231cbcc9 h1:E/NBN5tDh6COcJmygdBb9RAJhE4uIHfT51VBlP3tglU=
|
||||
github.com/goplus/llvm v0.7.1-0.20240418160956-6233231cbcc9/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
|
||||
github.com/goplus/llvm v0.7.1-0.20240420180312-6230a4ea7a47 h1:B3nWTLOQh4+Yqt6NryE/cVQdo/+NLiT8AtD4YaeKScg=
|
||||
github.com/goplus/llvm v0.7.1-0.20240420180312-6230a4ea7a47/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=
|
||||
|
||||
@@ -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
|
||||
|
||||
116
ssa/expr.go
116
ssa/expr.go
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
42
ssa/type.go
42
ssa/type.go
@@ -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}
|
||||
}
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 loader
|
||||
|
||||
/*
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"github.com/goplus/llgo/build"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
type Package struct {
|
||||
SSA *ssa.Package
|
||||
EmbedGlobals map[string][]*EmbedFile
|
||||
}
|
||||
|
||||
type EmbedFile struct {
|
||||
/*
|
||||
Name string
|
||||
Size uint64
|
||||
Hash string // hash of the file (as a hex string)
|
||||
NeedsData bool // true if this file is embedded as a byte slice
|
||||
Data []byte // contents of this file (only if NeedsData is set)
|
||||
*/
|
||||
/*
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
*ssa.Program
|
||||
}
|
||||
|
||||
func (ctx *Context) Load(pkg build.Package) (ret Package, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// BuildPackage builds an SSA program with IR for a single package.
|
||||
//
|
||||
// It populates pkg by type-checking the specified file ASTs. All
|
||||
// dependencies are loaded using the importer specified by tc, which
|
||||
// typically loads compiler export data; SSA code cannot be built for
|
||||
// those packages. BuildPackage then constructs an ssa.Program with all
|
||||
// dependency packages created, and builds and returns the SSA package
|
||||
// corresponding to pkg.
|
||||
//
|
||||
// The caller must have set pkg.Path() to the import path.
|
||||
//
|
||||
// The operation fails if there were any type-checking or import errors.
|
||||
//
|
||||
// See ../example_test.go for an example.
|
||||
func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
|
||||
if fset == nil {
|
||||
panic("no token.FileSet")
|
||||
}
|
||||
if pkg.Path() == "" {
|
||||
panic("package has no import path")
|
||||
}
|
||||
|
||||
info := &types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
Uses: make(map[*ast.Ident]types.Object),
|
||||
Implicits: make(map[ast.Node]types.Object),
|
||||
Scopes: make(map[ast.Node]*types.Scope),
|
||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||
}
|
||||
typeparams.InitInstanceInfo(info)
|
||||
versions.InitFileVersions(info)
|
||||
if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
prog := ssa.NewProgram(fset, mode)
|
||||
|
||||
// Create SSA packages for all imports.
|
||||
// Order is not significant.
|
||||
created := make(map[*types.Package]bool)
|
||||
var createAll func(pkgs []*types.Package)
|
||||
createAll = func(pkgs []*types.Package) {
|
||||
for _, p := range pkgs {
|
||||
if !created[p] {
|
||||
created[p] = true
|
||||
prog.CreatePackage(p, nil, nil, true)
|
||||
createAll(p.Imports())
|
||||
}
|
||||
}
|
||||
}
|
||||
createAll(pkg.Imports())
|
||||
|
||||
// TODO(adonovan): we could replace createAll with just:
|
||||
//
|
||||
// // Create SSA packages for all imports.
|
||||
// for _, p := range pkg.Imports() {
|
||||
// prog.CreatePackage(p, nil, nil, true)
|
||||
// }
|
||||
//
|
||||
// (with minor changes to changes to ../builder_test.go as
|
||||
// shown in CL 511715 PS 10.) But this would strictly violate
|
||||
// the letter of the doc comment above, which says "all
|
||||
// dependencies created".
|
||||
//
|
||||
// Tim makes the good point with some extra work we could
|
||||
// remove the need for any CreatePackage calls except the
|
||||
// ones with syntax (i.e. primary packages). Of course
|
||||
// You wouldn't have ssa.Packages and Members for as
|
||||
// many things but no-one really uses that anyway.
|
||||
// I wish I had done this from the outset.
|
||||
|
||||
// Create and build the primary package.
|
||||
ssapkg := prog.CreatePackage(pkg, files, info, false)
|
||||
ssapkg.Build()
|
||||
return ssapkg, info, nil
|
||||
}
|
||||
*/
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 parser
|
||||
|
||||
/*
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
|
||||
"github.com/goplus/llgo/build"
|
||||
)
|
||||
|
||||
// A Mode value is a set of flags (or 0).
|
||||
// They control the amount of source code parsed and other optional
|
||||
// parser functionality.
|
||||
type Mode = parser.Mode
|
||||
|
||||
const (
|
||||
PackageClauseOnly = parser.PackageClauseOnly // stop parsing after package clause
|
||||
ImportsOnly = parser.ImportsOnly // stop parsing after import declarations
|
||||
ParseComments = parser.ParseComments // parse comments and add them to AST
|
||||
Trace = parser.Trace // print a trace of parsed productions
|
||||
DeclarationErrors = parser.DeclarationErrors // report declaration errors
|
||||
SpuriousErrors = parser.SpuriousErrors // same as AllErrors, for backward-compatibility
|
||||
SkipObjectResolution = parser.SkipObjectResolution // don't resolve identifiers to objects - see ParseFile
|
||||
AllErrors = parser.AllErrors // report all errors (not just the first 10 on different lines)
|
||||
)
|
||||
|
||||
// A Package node represents a set of source files
|
||||
// collectively building a Go package.
|
||||
type Package struct {
|
||||
*ast.Package
|
||||
}
|
||||
|
||||
func Parse(fset *token.FileSet, pkg build.Package, mode Mode) (_ Package, first error) {
|
||||
ret := map[string]*ast.Package{}
|
||||
for _, file := range pkg.GoFiles {
|
||||
f, err := parser.ParseFile(fset, file, nil, mode)
|
||||
if err != nil {
|
||||
if first == nil {
|
||||
first = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
ret[file] = f
|
||||
}
|
||||
}
|
||||
*/
|
||||
Reference in New Issue
Block a user