diff --git a/cl/compile.go b/cl/compile.go index e16d3cb9..c76baa8b 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -39,6 +39,7 @@ type context struct { pkg llssa.Package fn llssa.Function glbs map[*ssa.Global]llssa.Global + vals map[ssa.Value]llssa.Expr } // Global variable. @@ -65,14 +66,21 @@ func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) { if nblk == 0 { // external function return } - b := fn.MakeBody(nblk) - p.compileBlock(b, f.Blocks[0]) + 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) { +func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock) llssa.BasicBlock { + ret := p.fn.Block(block.Index) + b.SetBlock(ret) + p.vals = make(map[ssa.Value]llssa.Expr) for _, instr := range block.Instrs { p.compileInstr(b, instr) } + return ret } func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) llssa.Expr { @@ -90,23 +98,55 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { return } switch v := instr.(type) { + case *ssa.Store: + ptr := p.compileValue(b, v.Addr) + val := p.compileValue(b, v.Val) + b.Store(ptr, val) + case *ssa.Jump: + fn := p.fn + succs := v.Block().Succs + jmpb := fn.Block(succs[0].Index) + b.Jump(jmpb) + case *ssa.Return: + var results []llssa.Expr + if n := len(v.Results); n > 0 { + results = make([]llssa.Expr, n) + for i, r := range v.Results { + results[i] = p.compileValue(b, r) + } + } + b.Return(results...) case *ssa.If: - p.compileValue(b, v.Cond) - return + fn := p.fn + cond := p.compileValue(b, v.Cond) + succs := v.Block().Succs + thenb := fn.Block(succs[0].Index) + elseb := fn.Block(succs[1].Index) + b.If(cond, thenb, elseb) + default: + panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr)) } - panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr)) } -func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { +func (p *context) compileValue(b llssa.Builder, v ssa.Value) (ret llssa.Expr) { + if e, ok := p.vals[v]; ok { + return e + } if iv, ok := v.(instrAndValue); ok { - return p.compileInstrAndValue(b, iv) + ret = p.compileInstrAndValue(b, iv) + } else { + switch v := v.(type) { + case *ssa.Global: + g := p.compileGlobal(p.pkg, v) + ret = g.Expr + case *ssa.Const: + ret = b.Const(v.Value, v.Type()) + default: + panic(fmt.Sprintf("compileValue: unknown value - %T\n", v)) + } } - switch v := v.(type) { - case *ssa.Global: - g := p.compileGlobal(p.pkg, v) - return g.Expr - } - panic(fmt.Sprintf("compileValue: unknown value - %T\n", v)) + p.vals[v] = ret + return ret } // ----------------------------------------------------------------------------- @@ -147,9 +187,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.P // Do not try to build generic (non-instantiated) functions. continue } - if false { - ctx.compileFunc(ret, member) - } + ctx.compileFunc(ret, member) case *ssa.Type: ctx.compileType(ret, member) case *ssa.Global: diff --git a/cl/compile_test.go b/cl/compile_test.go index 022362d3..b8a45a7f 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -68,5 +68,19 @@ source_filename = "foo" @"init$guard" = external global ptr @a = external global ptr + +define void @init() { +_llgo_0: + %0 = load i1, ptr @"init$guard", align 1 + %1 = load i1, ptr @"init$guard", align 1 + br i1 %1, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"init$guard", align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} `) } diff --git a/ssa/decl.go b/ssa/decl.go index 8a015486..477afc30 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -18,6 +18,7 @@ package ssa import ( "go/types" + "strconv" "github.com/goplus/llvm" ) @@ -138,15 +139,21 @@ func (p Function) Param(i int) Expr { return Expr{p.impl.Param(i), p.params[i]} } +// NewBuilder creates a new Builder for the function. +func (p Function) NewBuilder() Builder { + prog := p.prog + b := prog.ctx.NewBuilder() + b.Finalize() + return &aBuilder{b, p, prog} +} + // 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(p.blks[0].impl) - return &aBuilder{b, p, prog} + b := p.NewBuilder() + b.impl.SetInsertPointAtEnd(p.blks[0].impl) + return b } // MakeBlocks creates nblk basic blocks for the function. @@ -157,7 +164,7 @@ func (p Function) MakeBlocks(nblk int) []BasicBlock { n := len(p.blks) f := p.impl for i := 0; i < nblk; i++ { - label := "" + label := "_llgo_" + strconv.Itoa(i) blk := llvm.AddBasicBlock(f, label) p.blks = append(p.blks, &aBasicBlock{blk, p, n + i}) } diff --git a/ssa/expr.go b/ssa/expr.go index e449df7c..7de92aa4 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -17,6 +17,7 @@ package ssa import ( + "go/constant" "go/token" "go/types" @@ -42,12 +43,24 @@ func llvmValues(vals []Expr) []llvm.Value { // ----------------------------------------------------------------------------- +func (p Program) BoolVal(v bool) Expr { + t := p.Bool() + var bv uint64 + if v { + bv = 1 + } + ret := llvm.ConstInt(t.ll, bv, v) + return Expr{ret, t} +} + 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 bool: + return p.BoolVal(v) case float64: t := p.Float64() ret := llvm.ConstFloat(t.ll, v) @@ -56,6 +69,17 @@ func (p Program) Val(v interface{}) Expr { panic("todo") } +func (b Builder) Const(v constant.Value, t types.Type) Expr { + switch t := t.(type) { + case *types.Basic: + switch t.Kind() { + case types.Bool: + return b.prog.BoolVal(constant.BoolVal(v)) + } + } + panic("todo") +} + // ----------------------------------------------------------------------------- const ( @@ -104,7 +128,7 @@ var logicOpToLLVM = []llvm.Opcode{ token.OR - logicOpBase: llvm.Or, token.XOR - logicOpBase: llvm.Xor, token.SHL - logicOpBase: llvm.Shl, - token.SHR - logicOpBase: llvm.LShr, + token.SHR - logicOpBase: llvm.AShr, // Arithmetic Shift Right } // AND OR XOR SHL SHR AND_NOT & | ^ << >> &^ @@ -173,7 +197,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr { kind := x.kind llop := logicOpToLLVM[op-logicOpBase] if op == token.SHR && kind == vkUnsigned { - llop = llvm.AShr + llop = llvm.LShr // Logical Shift Right } return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type} case isPredOp(op): // op: == != < <= < >= @@ -217,6 +241,12 @@ func (b Builder) Load(ptr Expr) Expr { 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 { + b.impl.CreateStore(val.impl, ptr.impl) + return b +} + // ----------------------------------------------------------------------------- func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 75b769c6..98f01bf0 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -17,6 +17,7 @@ package ssa import ( + "go/constant" "go/token" "go/types" "testing" @@ -27,6 +28,7 @@ func init() { } func assertPkg(t *testing.T, p Package, expected string) { + t.Helper() if v := p.String(); v != expected { t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected) } @@ -43,6 +45,23 @@ source_filename = "foo/bar" `) } +func TestConst(t *testing.T) { + prog := NewProgram(nil) + pkg := prog.NewPackage("bar", "foo/bar") + 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])) + assertPkg(t, pkg, `; ModuleID = 'foo/bar' +source_filename = "foo/bar" + +define i1 @fn() { +_llgo_0: + ret i1 true +} +`) +} + func TestStruct(t *testing.T) { empty := types.NewStruct(nil, nil) @@ -99,6 +118,7 @@ func TestBasicFunc(t *testing.T) { source_filename = "foo/bar" define i64 @fn(i64 %0, double %1) { +_llgo_0: ret i64 1 } `) @@ -118,6 +138,7 @@ func TestFuncParam(t *testing.T) { source_filename = "foo/bar" define i64 @fn(i64 %0, double %1) { +_llgo_0: ret i64 %0 } `) @@ -145,11 +166,13 @@ func TestFuncCall(t *testing.T) { source_filename = "foo/bar" define i64 @fn(i64 %0, double %1) { +_llgo_0: ret i64 1 } define void @main() { - %1 = call i64 @fn(i64 1, double 1.200000e+00) +_llgo_0: + %0 = call i64 @fn(i64 1, double 1.200000e+00) ret void } `) @@ -174,12 +197,30 @@ source_filename = "foo/bar" @a = external global i64 define { i64, double } @fn(double %0) { +_llgo_0: %mrv = insertvalue { i64, double } { ptr @a, double poison }, double %0, 1 ret { i64, double } %mrv } `) } +func TestJump(t *testing.T) { + prog := NewProgram(nil) + pkg := prog.NewPackage("bar", "foo/bar") + sig := types.NewSignatureType(nil, nil, nil, nil, nil, false) + fn := pkg.NewFunc("loop", sig) + b := fn.MakeBody(1) + b.Jump(fn.Block(0)) + assertPkg(t, pkg, `; ModuleID = 'foo/bar' +source_filename = "foo/bar" + +define void @loop() { +_llgo_0: + br label %_llgo_0 +} +`) +} + func TestIf(t *testing.T) { prog := NewProgram(nil) pkg := prog.NewPackage("bar", "foo/bar") @@ -201,13 +242,14 @@ func TestIf(t *testing.T) { source_filename = "foo/bar" define i64 @fn(i64 %0) { - %2 = icmp sgt i64 %0, 0 - br i1 %2, label %3, label %4 +_llgo_0: + %1 = icmp sgt i64 %0, 0 + br i1 %1, label %_llgo_1, label %_llgo_2 -3: ; preds = %1 +_llgo_1: ; preds = %_llgo_0 ret i64 1 -4: ; preds = %1 +_llgo_2: ; preds = %_llgo_0 ret i64 0 } `) @@ -244,8 +286,9 @@ func TestBinOp(t *testing.T) { source_filename = "foo/bar" define i64 @fn(i64 %0, double %1) { - %3 = add i64 %0, 1 - ret i64 %3 +_llgo_0: + %2 = add i64 %0, 1 + ret i64 %2 } `) } @@ -260,13 +303,19 @@ func TestUnOp(t *testing.T) { sig := types.NewSignatureType(nil, nil, nil, params, rets, false) fn := pkg.NewFunc("fn", sig) b := fn.MakeBody(1) - ret := b.UnOp(token.MUL, fn.Param(0)) - b.Return(ret) + ptr := fn.Param(0) + val := b.UnOp(token.MUL, ptr) + val2 := b.BinOp(token.SHR, val, prog.Val(1)) + b.Store(ptr, val2) + b.Return(val2) assertPkg(t, pkg, `; ModuleID = 'foo/bar' source_filename = "foo/bar" define i64 @fn(ptr %0) { - %2 = load i64, ptr %0, align 4 +_llgo_0: + %1 = load i64, ptr %0, align 4 + %2 = ashr i64 %1, 1 + store i64 %2, ptr %0, align 4 ret i64 %2 } `) diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index 4cf15fe8..63f8bedd 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -73,6 +73,14 @@ func (b Builder) Return(results ...Expr) { } } +// Jump emits a jump instruction. +func (b Builder) Jump(jmpb BasicBlock) { + if b.fn != jmpb.fn { + panic("mismatched function") + } + b.impl.CreateBr(jmpb.impl) +} + // If emits an if instruction. func (b Builder) If(cond Expr, thenb, elseb BasicBlock) { if b.fn != thenb.fn || b.fn != elseb.fn {