ssa: TestIf
This commit is contained in:
@@ -61,17 +61,15 @@ func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) {
|
|||||||
defer func() {
|
defer func() {
|
||||||
p.fn = nil
|
p.fn = nil
|
||||||
}()
|
}()
|
||||||
if f.Blocks == nil { // external function
|
nblk := len(f.Blocks)
|
||||||
|
if nblk == 0 { // external function
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b := fn.MakeBody("")
|
b := fn.MakeBody(nblk)
|
||||||
for _, block := range f.Blocks {
|
p.compileBlock(b, f.Blocks[0])
|
||||||
p.compileBlock(b, block)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock) {
|
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock) {
|
||||||
_ = block.Index
|
|
||||||
for _, instr := range block.Instrs {
|
for _, instr := range block.Instrs {
|
||||||
p.compileInstr(b, instr)
|
p.compileInstr(b, instr)
|
||||||
}
|
}
|
||||||
|
|||||||
49
ssa/decl.go
49
ssa/decl.go
@@ -24,6 +24,9 @@ import (
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type aNamedConst struct {
|
||||||
|
}
|
||||||
|
|
||||||
// A NamedConst is a Member of a Package representing a package-level
|
// A NamedConst is a Member of a Package representing a package-level
|
||||||
// named constant.
|
// named constant.
|
||||||
//
|
//
|
||||||
@@ -32,22 +35,19 @@ import (
|
|||||||
//
|
//
|
||||||
// NB: a NamedConst is not a Value; it contains a constant Value, which
|
// NB: a NamedConst is not a Value; it contains a constant Value, which
|
||||||
// it augments with the name and position of its 'const' declaration.
|
// it augments with the name and position of its 'const' declaration.
|
||||||
type aNamedConst struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
type NamedConst = *aNamedConst
|
type NamedConst = *aNamedConst
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type aGlobal struct {
|
||||||
|
Expr
|
||||||
|
}
|
||||||
|
|
||||||
// A Global is a named Value holding the address of a package-level
|
// A Global is a named Value holding the address of a package-level
|
||||||
// variable.
|
// variable.
|
||||||
//
|
//
|
||||||
// Pos() returns the position of the ast.ValueSpec.Names[*]
|
// Pos() returns the position of the ast.ValueSpec.Names[*]
|
||||||
// identifier.
|
// identifier.
|
||||||
type aGlobal struct {
|
|
||||||
Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
type Global = *aGlobal
|
type Global = *aGlobal
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -104,16 +104,18 @@ type Global = *aGlobal
|
|||||||
type aFunction struct {
|
type aFunction struct {
|
||||||
Expr
|
Expr
|
||||||
prog Program
|
prog Program
|
||||||
|
blks []BasicBlock
|
||||||
|
|
||||||
params []Type
|
params []Type
|
||||||
hasVArg bool
|
hasVArg bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function represents a function or method.
|
||||||
type Function = *aFunction
|
type Function = *aFunction
|
||||||
|
|
||||||
func newFunction(fn llvm.Value, t Type, prog Program) Function {
|
func newFunction(fn llvm.Value, t Type, prog Program) Function {
|
||||||
params, hasVArg := newParams(t, prog)
|
params, hasVArg := newParams(t, prog)
|
||||||
return &aFunction{Expr{fn, t}, prog, params, hasVArg}
|
return &aFunction{Expr{fn, t}, prog, nil, params, hasVArg}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
|
func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
|
||||||
@@ -131,17 +133,40 @@ func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Params returns the function's ith parameter.
|
||||||
func (p Function) Param(i int) Expr {
|
func (p Function) Param(i int) Expr {
|
||||||
return Expr{p.impl.Param(i), p.params[i]}
|
return Expr{p.impl.Param(i), p.params[i]}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Function) MakeBody(label string) Builder {
|
// MakeBody creates nblk basic blocks for the function, and creates
|
||||||
body := llvm.AddBasicBlock(p.impl, label)
|
// a new Builder associated to #0 block.
|
||||||
|
func (p Function) MakeBody(nblk int) Builder {
|
||||||
|
p.MakeBlocks(nblk)
|
||||||
prog := p.prog
|
prog := p.prog
|
||||||
b := prog.ctx.NewBuilder()
|
b := prog.ctx.NewBuilder()
|
||||||
b.Finalize()
|
b.Finalize()
|
||||||
b.SetInsertPointAtEnd(body)
|
b.SetInsertPointAtEnd(p.blks[0].impl)
|
||||||
return &aBuilder{b, prog}
|
return &aBuilder{b, p, prog}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeBlocks creates nblk basic blocks for the function.
|
||||||
|
func (p Function) MakeBlocks(nblk int) []BasicBlock {
|
||||||
|
if p.blks == nil {
|
||||||
|
p.blks = make([]BasicBlock, 0, nblk)
|
||||||
|
}
|
||||||
|
n := len(p.blks)
|
||||||
|
f := p.impl
|
||||||
|
for i := 0; i < nblk; i++ {
|
||||||
|
label := ""
|
||||||
|
blk := llvm.AddBasicBlock(f, label)
|
||||||
|
p.blks = append(p.blks, &aBasicBlock{blk, p, n + i})
|
||||||
|
}
|
||||||
|
return p.blks[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block returns the ith basic block of the function.
|
||||||
|
func (p Function) Block(idx int) BasicBlock {
|
||||||
|
return p.blks[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -26,18 +26,6 @@ func init() {
|
|||||||
Initialize(InitAll)
|
Initialize(InitAll)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func asmPkg(t *testing.T, p *Package) {
|
|
||||||
b, err := p.CodeGen(AssemblyFile)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("ctx.ParseIR:", err)
|
|
||||||
}
|
|
||||||
if v := string(b); v != "" {
|
|
||||||
t.Log(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func assertPkg(t *testing.T, p Package, expected string) {
|
func assertPkg(t *testing.T, p Package, expected string) {
|
||||||
if v := p.String(); v != expected {
|
if v := p.String(); v != expected {
|
||||||
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
|
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
|
||||||
@@ -105,7 +93,7 @@ func TestBasicFunc(t *testing.T) {
|
|||||||
types.NewVar(0, nil, "b", types.Typ[types.Float64]))
|
types.NewVar(0, nil, "b", types.Typ[types.Float64]))
|
||||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||||
pkg.NewFunc("fn", sig).MakeBody("").
|
pkg.NewFunc("fn", sig).MakeBody(1).
|
||||||
Return(prog.Val(1))
|
Return(prog.Val(1))
|
||||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||||
source_filename = "foo/bar"
|
source_filename = "foo/bar"
|
||||||
@@ -125,7 +113,7 @@ func TestFuncParam(t *testing.T) {
|
|||||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||||
fn := pkg.NewFunc("fn", sig)
|
fn := pkg.NewFunc("fn", sig)
|
||||||
fn.MakeBody("").Return(fn.Param(0))
|
fn.MakeBody(1).Return(fn.Param(0))
|
||||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||||
source_filename = "foo/bar"
|
source_filename = "foo/bar"
|
||||||
|
|
||||||
@@ -145,11 +133,11 @@ func TestFuncCall(t *testing.T) {
|
|||||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||||
fn := pkg.NewFunc("fn", sig)
|
fn := pkg.NewFunc("fn", sig)
|
||||||
fn.MakeBody("").
|
fn.MakeBody(1).
|
||||||
Return(prog.Val(1))
|
Return(prog.Val(1))
|
||||||
|
|
||||||
sigMain := types.NewSignatureType(nil, nil, nil, nil, nil, false)
|
sigMain := types.NewSignatureType(nil, nil, nil, nil, nil, false)
|
||||||
b := pkg.NewFunc("main", sigMain).MakeBody("")
|
b := pkg.NewFunc("main", sigMain).MakeBody(1)
|
||||||
b.Call(fn.Expr, prog.Val(1), prog.Val(1.2))
|
b.Call(fn.Expr, prog.Val(1), prog.Val(1.2))
|
||||||
b.Return()
|
b.Return()
|
||||||
|
|
||||||
@@ -178,7 +166,7 @@ func TestFuncMultiRet(t *testing.T) {
|
|||||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||||
a := pkg.NewVar("a", types.Typ[types.Int])
|
a := pkg.NewVar("a", types.Typ[types.Int])
|
||||||
fn := pkg.NewFunc("fn", sig)
|
fn := pkg.NewFunc("fn", sig)
|
||||||
b := fn.MakeBody("")
|
b := fn.MakeBody(1)
|
||||||
b.Return(a.Expr, fn.Param(0))
|
b.Return(a.Expr, fn.Param(0))
|
||||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||||
source_filename = "foo/bar"
|
source_filename = "foo/bar"
|
||||||
@@ -192,6 +180,39 @@ define { i64, double } @fn(double %0) {
|
|||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIf(t *testing.T) {
|
||||||
|
prog := NewProgram(nil)
|
||||||
|
pkg := prog.NewPackage("bar", "foo/bar")
|
||||||
|
params := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int]))
|
||||||
|
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)
|
||||||
|
b := fn.MakeBody(3)
|
||||||
|
iftrue := fn.Block(1)
|
||||||
|
iffalse := fn.Block(2)
|
||||||
|
if iftrue.Index() != 1 || iftrue.Parent() != fn {
|
||||||
|
t.Fatal("iftrue")
|
||||||
|
}
|
||||||
|
cond := b.BinOp(token.GTR, fn.Param(0), prog.Val(0))
|
||||||
|
b.If(cond, iftrue, iffalse)
|
||||||
|
b.SetBlock(iftrue).Return(prog.Val(1))
|
||||||
|
b.SetBlock(iffalse).Return(prog.Val(0))
|
||||||
|
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||||
|
source_filename = "foo/bar"
|
||||||
|
|
||||||
|
define i64 @fn(i64 %0) {
|
||||||
|
%2 = icmp sgt i64 %0, 0
|
||||||
|
br i1 %2, label %3, label %4
|
||||||
|
|
||||||
|
3: ; preds = %1
|
||||||
|
ret i64 1
|
||||||
|
|
||||||
|
4: ; preds = %1
|
||||||
|
ret i64 0
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPrintf(t *testing.T) {
|
func TestPrintf(t *testing.T) {
|
||||||
prog := NewProgram(nil)
|
prog := NewProgram(nil)
|
||||||
pkg := prog.NewPackage("bar", "foo/bar")
|
pkg := prog.NewPackage("bar", "foo/bar")
|
||||||
@@ -216,7 +237,7 @@ func TestBinOp(t *testing.T) {
|
|||||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||||
fn := pkg.NewFunc("fn", sig)
|
fn := pkg.NewFunc("fn", sig)
|
||||||
b := fn.MakeBody("")
|
b := fn.MakeBody(1)
|
||||||
ret := b.BinOp(token.ADD, fn.Param(0), prog.Val(1))
|
ret := b.BinOp(token.ADD, fn.Param(0), prog.Val(1))
|
||||||
b.Return(ret)
|
b.Return(ret)
|
||||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||||
@@ -238,7 +259,7 @@ func TestUnOp(t *testing.T) {
|
|||||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||||
fn := pkg.NewFunc("fn", sig)
|
fn := pkg.NewFunc("fn", sig)
|
||||||
b := fn.MakeBody("")
|
b := fn.MakeBody(1)
|
||||||
ret := b.UnOp(token.MUL, fn.Param(0))
|
ret := b.UnOp(token.MUL, fn.Param(0))
|
||||||
b.Return(ret)
|
b.Return(ret)
|
||||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||||
|
|||||||
@@ -21,21 +21,48 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
/*
|
|
||||||
type BasicBlock struct {
|
type aBasicBlock struct {
|
||||||
impl llvm.BasicBlock
|
impl llvm.BasicBlock
|
||||||
|
fn Function
|
||||||
|
idx int
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
// BasicBlock represents a basic block in a function.
|
||||||
|
type BasicBlock = *aBasicBlock
|
||||||
|
|
||||||
|
// Parent returns the function to which the basic block belongs.
|
||||||
|
func (p BasicBlock) Parent() Function {
|
||||||
|
return p.fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index returns the index of the basic block in the parent function.
|
||||||
|
func (p BasicBlock) Index() int {
|
||||||
|
return p.idx
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
type aBuilder struct {
|
type aBuilder struct {
|
||||||
impl llvm.Builder
|
impl llvm.Builder
|
||||||
|
fn Function
|
||||||
prog Program
|
prog Program
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Builder represents a builder for creating instructions in a function.
|
||||||
type Builder = *aBuilder
|
type Builder = *aBuilder
|
||||||
|
|
||||||
func (b Builder) Return(results ...Expr) Builder {
|
// SetBlock sets the current block to the specified basic block.
|
||||||
|
func (b Builder) SetBlock(blk BasicBlock) Builder {
|
||||||
|
if b.fn != blk.fn {
|
||||||
|
panic("mismatched function")
|
||||||
|
}
|
||||||
|
b.impl.SetInsertPointAtEnd(blk.impl)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return emits a return instruction.
|
||||||
|
func (b Builder) Return(results ...Expr) {
|
||||||
switch n := len(results); n {
|
switch n := len(results); n {
|
||||||
case 0:
|
case 0:
|
||||||
b.impl.CreateRetVoid()
|
b.impl.CreateRetVoid()
|
||||||
@@ -44,7 +71,14 @@ func (b Builder) Return(results ...Expr) Builder {
|
|||||||
default:
|
default:
|
||||||
b.impl.CreateAggregateRet(llvmValues(results))
|
b.impl.CreateAggregateRet(llvmValues(results))
|
||||||
}
|
}
|
||||||
return b
|
}
|
||||||
|
|
||||||
|
// If emits an if instruction.
|
||||||
|
func (b Builder) If(cond Expr, thenb, elseb BasicBlock) {
|
||||||
|
if b.fn != thenb.fn || b.fn != elseb.fn {
|
||||||
|
panic("mismatched function")
|
||||||
|
}
|
||||||
|
b.impl.CreateCondBr(cond.impl, thenb.impl, elseb.impl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
Reference in New Issue
Block a user