From 25b104cf132eb04a24a464753553f783098bfc65 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 2 May 2024 07:37:31 +0800 Subject: [PATCH 1/5] llgo/ssa: PhisExpr --- cl/compile.go | 2 +- cl/compile_test.go | 2 +- ssa/expr.go | 65 +++++++++++++++++++++++++++++++++++++++++----- ssa/type.go | 1 + 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/cl/compile.go b/cl/compile.go index 1d09b2fc..c8b4ded9 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -400,7 +400,7 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue for i, pred := range preds { bblks[i] = p.fn.Block(pred.Index) } - phi.AddIncoming(vals, bblks) + phi.AddIncoming(b, vals, bblks) }) case *ssa.ChangeType: t := v.Type() diff --git a/cl/compile_test.go b/cl/compile_test.go index b9c3443f..6efd7574 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -29,7 +29,7 @@ func testCompile(t *testing.T, src, expected string) { } func TestFromTestrt(t *testing.T) { - cltest.FromDir(t, "", "./_testrt", true) + cltest.FromDir(t, "concat", "./_testrt", true) } func TestFromTestdata(t *testing.T) { diff --git a/ssa/expr.go b/ssa/expr.go index e19c7a12..ef607af1 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -50,12 +50,17 @@ func (v Expr) TypeOf() types.Type { // Do evaluates the delay expression and returns the result. func (v Expr) Do() Expr { - if vt := v.Type; vt.kind == vkDelayExpr { + switch vt := v.Type; vt.kind { + case vkDelayExpr: return vt.t.(delayExprTy)() + case vkPhisExpr: + panic("unreachable") } return v } +// ----------------------------------------------------------------------------- + // DelayExpr returns a delay expression. func DelayExpr(f func() Expr) Expr { return Expr{Type: &aType{t: delayExprTy(f), kind: vkDelayExpr}} @@ -73,6 +78,24 @@ func (p delayExprTy) String() string { // ----------------------------------------------------------------------------- +type phisExprTy struct { + phis []llvm.Value +} + +func (p phisExprTy) Underlying() types.Type { + panic("don't call") +} + +func (p phisExprTy) String() string { + return "phisExpr" +} + +func phisExpr(phis []llvm.Value) Expr { + return Expr{Type: &aType{t: &phisExprTy{phis}, kind: vkPhisExpr}} +} + +// ----------------------------------------------------------------------------- + // Null returns a null constant expression. func (p Program) Null(t Type) Expr { return Expr{llvm.ConstNull(t.ll), t} @@ -337,6 +360,14 @@ func llvmValues(vals []Expr) []llvm.Value { return ret } +func fieldValues(b llvm.Builder, vals []Expr, fldIdx int) []llvm.Value { + ret := make([]llvm.Value, len(vals)) + for i, v := range vals { + ret[i] = llvm.CreateExtractValue(b, v.impl, fldIdx) + } + return ret +} + func llvmBlocks(bblks []BasicBlock) []llvm.BasicBlock { ret := make([]llvm.BasicBlock, len(bblks)) for i, v := range bblks { @@ -351,15 +382,37 @@ type Phi struct { } // AddIncoming adds incoming values to a phi node. -func (p Phi) AddIncoming(vals []Expr, bblks []BasicBlock) { - v := llvmValues(vals) - b := llvmBlocks(bblks) - p.impl.AddIncoming(v, b) +func (p Phi) AddIncoming(b Builder, vals []Expr, bblks []BasicBlock) { + bs := llvmBlocks(bblks) + if p.kind != vkPhisExpr { // normal phi node + vs := llvmValues(vals) + p.impl.AddIncoming(vs, bs) + return + } + phis := p.t.(*phisExprTy).phis + for i, phi := range phis { + flds := fieldValues(b.impl, vals, i) + phi.AddIncoming(flds, bs) + } } // Phi returns a phi node. func (b Builder) Phi(t Type) Phi { - return Phi{Expr{llvm.CreatePHI(b.impl, t.ll), t}} + impl := b.impl + switch tund := t.t.Underlying().(type) { + case *types.Basic: + kind := tund.Kind() + switch kind { + case types.String: + prog := b.Prog + phis := make([]llvm.Value, 2) + phis[0] = llvm.CreatePHI(impl, prog.tyVoidPtr()) + phis[1] = llvm.CreatePHI(impl, prog.tyInt()) + return Phi{phisExpr(phis)} + } + } + phi := llvm.CreatePHI(impl, t.ll) + return Phi{Expr{phi, t}} } // ----------------------------------------------------------------------------- diff --git a/ssa/type.go b/ssa/type.go index 8dfcd58b..c33b1e5d 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -43,6 +43,7 @@ const ( vkFunc vkTuple vkDelayExpr = -1 + vkPhisExpr = -2 ) // ----------------------------------------------------------------------------- From fbb1f89ab33d8e9b903d6f0b5bfee24d5f7243cf Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 2 May 2024 07:56:52 +0800 Subject: [PATCH 2/5] phisExpr as an AggregateVal --- cl/compile.go | 4 ++-- ssa/expr.go | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cl/compile.go b/cl/compile.go index c8b4ded9..8f67a0cb 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -517,7 +517,7 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { val := p.compileValue(b, v.Value) b.MapUpdate(m, key, val) case *ssa.Panic: - arg := p.compileValue(b, v.X).Do() + arg := p.compileValue(b, v.X).Do(b) b.Panic(arg) default: panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr)) @@ -574,7 +574,7 @@ func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int) n := len(vals) - hasVArg ret := make([]llssa.Expr, n) for i := 0; i < n; i++ { - ret[i] = p.compileValue(b, vals[i]).Do() + ret[i] = p.compileValue(b, vals[i]).Do(b) } if hasVArg > 0 { ret = p.compileVArg(ret, b, vals[n]) diff --git a/ssa/expr.go b/ssa/expr.go index ef607af1..495259fc 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -49,12 +49,13 @@ func (v Expr) TypeOf() types.Type { */ // Do evaluates the delay expression and returns the result. -func (v Expr) Do() Expr { +func (v Expr) Do(b Builder) Expr { switch vt := v.Type; vt.kind { case vkDelayExpr: return vt.t.(delayExprTy)() case vkPhisExpr: - panic("unreachable") + e := vt.t.(*phisExprTy) + return Expr{b.impl.CreateAggregateRet(e.phis), e.Type} } return v } @@ -80,6 +81,7 @@ func (p delayExprTy) String() string { type phisExprTy struct { phis []llvm.Value + Type } func (p phisExprTy) Underlying() types.Type { @@ -90,8 +92,8 @@ func (p phisExprTy) String() string { return "phisExpr" } -func phisExpr(phis []llvm.Value) Expr { - return Expr{Type: &aType{t: &phisExprTy{phis}, kind: vkPhisExpr}} +func phisExpr(t Type, phis []llvm.Value) Expr { + return Expr{Type: &aType{t: &phisExprTy{phis, t}, kind: vkPhisExpr}} } // ----------------------------------------------------------------------------- @@ -389,8 +391,8 @@ func (p Phi) AddIncoming(b Builder, vals []Expr, bblks []BasicBlock) { p.impl.AddIncoming(vs, bs) return } - phis := p.t.(*phisExprTy).phis - for i, phi := range phis { + e := p.t.(*phisExprTy) + for i, phi := range e.phis { flds := fieldValues(b.impl, vals, i) phi.AddIncoming(flds, bs) } @@ -408,7 +410,7 @@ func (b Builder) Phi(t Type) Phi { phis := make([]llvm.Value, 2) phis[0] = llvm.CreatePHI(impl, prog.tyVoidPtr()) phis[1] = llvm.CreatePHI(impl, prog.tyInt()) - return Phi{phisExpr(phis)} + return Phi{phisExpr(t, phis)} } } phi := llvm.CreatePHI(impl, t.ll) From 40855c2d2ab2b4dd64ffd1e020a1ea2909769149 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 2 May 2024 11:27:02 +0800 Subject: [PATCH 3/5] cl: compilePhis; llgo/ssa: phi.AddIncoming --- cl/_testrt/concat/in.go | 2 +- cl/_testrt/concat/out.ll | 32 ++++++++++++++-------- cl/compile.go | 59 +++++++++++++++++++++++++++++++--------- cl/compile_test.go | 2 +- ssa/expr.go | 32 ++++++++++++++++------ 5 files changed, 91 insertions(+), 36 deletions(-) diff --git a/cl/_testrt/concat/in.go b/cl/_testrt/concat/in.go index 811a0c8d..149bfbae 100644 --- a/cl/_testrt/concat/in.go +++ b/cl/_testrt/concat/in.go @@ -13,5 +13,5 @@ func concat(args ...string) (ret string) { func main() { result := concat("Hello", " ", "World") - c.Fprintf(c.Stderr, c.Str("Hello %s\n"), c.AllocaCStr(result)) + c.Fprintf(c.Stderr, c.Str("Hi, %s\n"), c.AllocaCStr(result)) } diff --git a/cl/_testrt/concat/out.ll b/cl/_testrt/concat/out.ll index 06e49967..764decd6 100644 --- a/cl/_testrt/concat/out.ll +++ b/cl/_testrt/concat/out.ll @@ -10,30 +10,38 @@ source_filename = "main" @2 = private unnamed_addr constant [2 x i8] c" \00", align 1 @3 = private unnamed_addr constant [6 x i8] c"World\00", align 1 @__stderrp = external global ptr -@4 = private unnamed_addr constant [10 x i8] c"Hello %s\0A\00", align 1 +@4 = private unnamed_addr constant [8 x i8] c"Hi, %s\0A\00", align 1 define %"github.com/goplus/llgo/internal/runtime.String" @main.concat(%"github.com/goplus/llgo/internal/runtime.Slice" %0) { _llgo_0: %1 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) + %2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 0) + %3 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %2, 0 + %4 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %2, 1 br label %_llgo_1 _llgo_1: ; preds = %_llgo_2, %_llgo_0 - %2 = phi %"github.com/goplus/llgo/internal/runtime.String" [ %10, %_llgo_0 ], [ %9, %_llgo_2 ] - %3 = phi i64 [ -1, %_llgo_0 ], [ %4, %_llgo_2 ] - %4 = add i64 %3, 1 - %5 = icmp slt i64 %4, %1 - br i1 %5, label %_llgo_2, label %_llgo_3 + %5 = phi ptr [ %3, %_llgo_0 ], [ %14, %_llgo_2 ] + %6 = phi i64 [ %4, %_llgo_0 ], [ %15, %_llgo_2 ] + %7 = phi i64 [ -1, %_llgo_0 ], [ %8, %_llgo_2 ] + %mrv = insertvalue %"github.com/goplus/llgo/internal/runtime.String" poison, ptr %5, 0 + %mrv1 = insertvalue %"github.com/goplus/llgo/internal/runtime.String" %mrv, i64 %6, 1 + ret %"github.com/goplus/llgo/internal/runtime.String" %mrv1 + %8 = add i64 %7, 1 + %9 = icmp slt i64 %8, %1 + br i1 %9, label %_llgo_2, label %_llgo_3 _llgo_2: ; preds = %_llgo_1 - %6 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) - %7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %6, i64 %4 - %8 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %7, align 8 - %9 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.String" %2, %"github.com/goplus/llgo/internal/runtime.String" %8) + %10 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) + %11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %10, i64 %8 + %12 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %11, align 8 + %13 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(void , %"github.com/goplus/llgo/internal/runtime.String" %12) + %14 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %13, 0 + %15 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %13, 1 br label %_llgo_1 _llgo_3: ; preds = %_llgo_1 - ret %"github.com/goplus/llgo/internal/runtime.String" %2 - %10 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 0) + ret void } define void @main.init() { diff --git a/cl/compile.go b/cl/compile.go index 8f67a0cb..3c7e2fe4 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -237,7 +237,8 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bo callRuntimeInit(b, pkg) b.Call(pkg.FuncOf("main.init").Expr) } - for _, instr := range block.Instrs { + instrs := p.compilePhis(b, block.Instrs) + for _, instr := range instrs { p.compileInstr(b, instr) } return ret @@ -327,6 +328,50 @@ func (p *context) allocaCStr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) panic("allocaCStr(s string): invalid arguments") } +func isPhi(i ssa.Instruction) bool { + _, ok := i.(*ssa.Phi) + return ok +} + +func (p *context) compilePhis(b llssa.Builder, instrs []ssa.Instruction) []ssa.Instruction { + if ninstr := len(instrs); ninstr > 0 { + if isPhi(instrs[0]) { + n := 1 + for n < ninstr && isPhi(instrs[n]) { + n++ + } + rets := make([]llssa.Expr, n) + for i := 0; i < n; i++ { + iv := instrs[i].(*ssa.Phi) + rets[i] = p.compilePhi(b, iv) + } + for i := 0; i < n; i++ { + iv := instrs[i].(*ssa.Phi) + p.bvals[iv] = rets[i].Do(b) + } + return instrs[n:] + } + } + return instrs +} + +func (p *context) compilePhi(b llssa.Builder, v *ssa.Phi) (ret llssa.Expr) { + phi := b.Phi(p.prog.Type(v.Type())) + ret = phi.Expr + p.phis = append(p.phis, func() { + preds := v.Block().Preds + bblks := make([]llssa.BasicBlock, len(preds)) + for i, pred := range preds { + bblks[i] = p.fn.Block(pred.Index) + } + edges := v.Edges + phi.AddIncoming(b, bblks, func(i int) llssa.Expr { + return p.compileValue(b, edges[i]) + }) + }) + return +} + func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) { if asValue { if v, ok := p.bvals[iv]; ok { @@ -390,18 +435,6 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue case *ssa.UnOp: x := p.compileValue(b, v.X) ret = b.UnOp(v.Op, x) - case *ssa.Phi: - phi := b.Phi(p.prog.Type(v.Type())) - ret = phi.Expr - p.phis = append(p.phis, func() { - vals := p.compileValues(b, v.Edges, 0) - preds := v.Block().Preds - bblks := make([]llssa.BasicBlock, len(preds)) - for i, pred := range preds { - bblks[i] = p.fn.Block(pred.Index) - } - phi.AddIncoming(b, vals, bblks) - }) case *ssa.ChangeType: t := v.Type() x := p.compileValue(b, v.X) diff --git a/cl/compile_test.go b/cl/compile_test.go index 6efd7574..b9c3443f 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -29,7 +29,7 @@ func testCompile(t *testing.T, src, expected string) { } func TestFromTestrt(t *testing.T) { - cltest.FromDir(t, "concat", "./_testrt", true) + cltest.FromDir(t, "", "./_testrt", true) } func TestFromTestdata(t *testing.T) { diff --git a/ssa/expr.go b/ssa/expr.go index 495259fc..b69cb231 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -55,6 +55,7 @@ func (v Expr) Do(b Builder) Expr { return vt.t.(delayExprTy)() case vkPhisExpr: e := vt.t.(*phisExprTy) + // TODO(xsw): to check CreateAggregateRet is correct or not return Expr{b.impl.CreateAggregateRet(e.phis), e.Type} } return v @@ -362,10 +363,10 @@ func llvmValues(vals []Expr) []llvm.Value { return ret } -func fieldValues(b llvm.Builder, vals []Expr, fldIdx int) []llvm.Value { - ret := make([]llvm.Value, len(vals)) - for i, v := range vals { - ret[i] = llvm.CreateExtractValue(b, v.impl, fldIdx) +func llvmDelayValues(f func(i int) Expr, n int) []llvm.Value { + ret := make([]llvm.Value, n) + for i := 0; i < n; i++ { + ret[i] = f(i).impl } return ret } @@ -384,17 +385,30 @@ type Phi struct { } // AddIncoming adds incoming values to a phi node. -func (p Phi) AddIncoming(b Builder, vals []Expr, bblks []BasicBlock) { +func (p Phi) AddIncoming(b Builder, bblks []BasicBlock, f func(i int) Expr) { bs := llvmBlocks(bblks) if p.kind != vkPhisExpr { // normal phi node - vs := llvmValues(vals) + vs := llvmDelayValues(f, len(bblks)) p.impl.AddIncoming(vs, bs) return } e := p.t.(*phisExprTy) - for i, phi := range e.phis { - flds := fieldValues(b.impl, vals, i) - phi.AddIncoming(flds, bs) + phis := e.phis + vals := make([][]llvm.Value, len(phis)) + for iblk, blk := range bblks { + last := blk.impl.LastInstruction() + b.impl.SetInsertPointBefore(last) + impl := b.impl + val := f(iblk).impl + for i := range phis { + if iblk == 0 { + vals[i] = make([]llvm.Value, len(bblks)) + } + vals[i][iblk] = llvm.CreateExtractValue(impl, val, i) + } + } + for i, phi := range phis { + phi.AddIncoming(vals[i], bs) } } From 3e47a977e49e8f6129e7c7aef9d3c763b385588b Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 2 May 2024 11:31:33 +0800 Subject: [PATCH 4/5] update llvm --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index f432506b..c0b356e5 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/aykevl/go-wasm v0.0.1 github.com/goplus/gogen v1.15.2 - github.com/goplus/llvm v0.7.3 + github.com/goplus/llvm v0.7.4-0.20240502033044-f17514e2af8a github.com/goplus/mod v0.13.10 github.com/qiniu/x v1.13.10 golang.org/x/tools v0.20.0 diff --git a/go.sum b/go.sum index 0aef5b16..c6b1c09a 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/goplus/gogen v1.15.2 h1:Q6XaSx/Zi5tWnjfAziYsQI6Jv6MgODRpFtOYqNkiiqM= github.com/goplus/gogen v1.15.2/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk= github.com/goplus/llvm v0.7.3 h1:I7UkAO4kzn0Es2iHKRpGU1LjYQ452XwYfsSs1OAAXk8= github.com/goplus/llvm v0.7.3/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= +github.com/goplus/llvm v0.7.4-0.20240502033044-f17514e2af8a h1:FLRrz/S4mCuVTB+M1lSOiCU7bTbcbl5A5XhI3ntKfmI= +github.com/goplus/llvm v0.7.4-0.20240502033044-f17514e2af8a/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= From 3cbe4aac8714625f83938060143b754f1d2d1a24 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 2 May 2024 11:38:05 +0800 Subject: [PATCH 5/5] TestUserdefExpr --- ssa/ssa_test.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index f15992bd..ceaea2e4 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -30,15 +30,21 @@ func TestMakeInterface(t *testing.T) { } */ -func TestDelayExpr(t *testing.T) { +func TestUserdefExpr(t *testing.T) { a := delayExprTy(nil) + b := &phisExprTy{} _ = a.String() - defer func() { - if r := recover(); r == nil { - t.Log("TestDelayExpr: no error?") - } - }() - a.Underlying() + _ = b.String() + test := func(a types.Type) { + defer func() { + if r := recover(); r == nil { + t.Log("TestUserdefExpr: no error?") + } + }() + a.Underlying() + } + test(a) + test(b) } func TestAny(t *testing.T) {