diff --git a/cl/compile.go b/cl/compile.go index 37d2e672..e16d3cb9 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -61,17 +61,15 @@ func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) { defer func() { p.fn = nil }() - if f.Blocks == nil { // external function + nblk := len(f.Blocks) + if nblk == 0 { // external function return } - b := fn.MakeBody("") - for _, block := range f.Blocks { - p.compileBlock(b, block) - } + b := fn.MakeBody(nblk) + p.compileBlock(b, f.Blocks[0]) } func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock) { - _ = block.Index for _, instr := range block.Instrs { p.compileInstr(b, instr) } diff --git a/ssa/decl.go b/ssa/decl.go index c04ec0f2..8a015486 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -24,6 +24,9 @@ import ( // ----------------------------------------------------------------------------- +type aNamedConst struct { +} + // A NamedConst is a Member of a Package representing a package-level // named constant. // @@ -32,22 +35,19 @@ import ( // // NB: a NamedConst is not a Value; it contains a constant Value, which // it augments with the name and position of its 'const' declaration. -type aNamedConst struct { -} - type NamedConst = *aNamedConst // ----------------------------------------------------------------------------- +type aGlobal struct { + Expr +} + // A Global is a named Value holding the address of a package-level // variable. // // Pos() returns the position of the ast.ValueSpec.Names[*] // identifier. -type aGlobal struct { - Expr -} - type Global = *aGlobal // ----------------------------------------------------------------------------- @@ -104,16 +104,18 @@ type Global = *aGlobal type aFunction struct { Expr prog Program + blks []BasicBlock params []Type hasVArg bool } +// Function represents a function or method. type Function = *aFunction func newFunction(fn llvm.Value, t Type, prog Program) Function { 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) { @@ -131,17 +133,40 @@ func newParams(fn Type, prog Program) (params []Type, hasVArg bool) { return } +// Params returns the function's ith parameter. func (p Function) Param(i int) Expr { return Expr{p.impl.Param(i), p.params[i]} } -func (p Function) MakeBody(label string) Builder { - body := llvm.AddBasicBlock(p.impl, label) +// MakeBody creates nblk basic blocks for the function, and creates +// a new Builder associated to #0 block. +func (p Function) MakeBody(nblk int) Builder { + p.MakeBlocks(nblk) prog := p.prog b := prog.ctx.NewBuilder() b.Finalize() - b.SetInsertPointAtEnd(body) - return &aBuilder{b, prog} + b.SetInsertPointAtEnd(p.blks[0].impl) + 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] } // ----------------------------------------------------------------------------- diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 688d02ba..75b769c6 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -26,18 +26,6 @@ func init() { 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) { if v := p.String(); 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])) rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) sig := types.NewSignatureType(nil, nil, nil, params, rets, false) - pkg.NewFunc("fn", sig).MakeBody(""). + pkg.NewFunc("fn", sig).MakeBody(1). Return(prog.Val(1)) assertPkg(t, pkg, `; ModuleID = '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])) sig := types.NewSignatureType(nil, nil, nil, params, rets, false) fn := pkg.NewFunc("fn", sig) - fn.MakeBody("").Return(fn.Param(0)) + fn.MakeBody(1).Return(fn.Param(0)) assertPkg(t, pkg, `; ModuleID = '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])) sig := types.NewSignatureType(nil, nil, nil, params, rets, false) fn := pkg.NewFunc("fn", sig) - fn.MakeBody(""). + fn.MakeBody(1). Return(prog.Val(1)) 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.Return() @@ -178,7 +166,7 @@ func TestFuncMultiRet(t *testing.T) { sig := types.NewSignatureType(nil, nil, nil, params, rets, false) a := pkg.NewVar("a", types.Typ[types.Int]) fn := pkg.NewFunc("fn", sig) - b := fn.MakeBody("") + b := fn.MakeBody(1) b.Return(a.Expr, fn.Param(0)) assertPkg(t, pkg, `; ModuleID = '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) { prog := NewProgram(nil) 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])) sig := types.NewSignatureType(nil, nil, nil, params, rets, false) fn := pkg.NewFunc("fn", sig) - b := fn.MakeBody("") + b := fn.MakeBody(1) ret := b.BinOp(token.ADD, fn.Param(0), prog.Val(1)) b.Return(ret) 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])) sig := types.NewSignatureType(nil, nil, nil, params, rets, false) fn := pkg.NewFunc("fn", sig) - b := fn.MakeBody("") + b := fn.MakeBody(1) ret := b.UnOp(token.MUL, fn.Param(0)) b.Return(ret) assertPkg(t, pkg, `; ModuleID = 'foo/bar' diff --git a/ssa/builder.go b/ssa/stmt_builder.go similarity index 55% rename from ssa/builder.go rename to ssa/stmt_builder.go index a27fd4e2..4cf15fe8 100644 --- a/ssa/builder.go +++ b/ssa/stmt_builder.go @@ -21,21 +21,48 @@ import ( ) // ----------------------------------------------------------------------------- -/* -type BasicBlock struct { + +type aBasicBlock struct { 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 { impl llvm.Builder + fn Function prog Program } +// Builder represents a builder for creating instructions in a function. 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 { case 0: b.impl.CreateRetVoid() @@ -44,7 +71,14 @@ func (b Builder) Return(results ...Expr) Builder { default: 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) } // -----------------------------------------------------------------------------