From ba4521775639304a671b1099f66dc9a9375a2b19 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 2 Jun 2024 15:24:42 +0800 Subject: [PATCH] llgo/ssa: RunDefers --- cl/_testgo/defer/in.go | 10 +- cl/_testgo/defer/out.ll | 238 ++++++++++++++++++++++++++++++++++++++++ cl/compile.go | 2 + ssa/decl.go | 5 - ssa/expr.go | 26 +++-- ssa/package.go | 18 ++- ssa/stmt_builder.go | 33 +++++- 7 files changed, 303 insertions(+), 29 deletions(-) diff --git a/cl/_testgo/defer/in.go b/cl/_testgo/defer/in.go index 344527b3..b66b5444 100644 --- a/cl/_testgo/defer/in.go +++ b/cl/_testgo/defer/in.go @@ -1,7 +1,7 @@ package main func f(s string) bool { - return len(s) > 5 + return len(s) > 2 } func fail() { @@ -11,9 +11,11 @@ func fail() { func main() { defer func() { println("hi") - if e := recover(); e != nil { - println(e.(string)) - } + /* + if e := recover(); e != nil { + println(e.(string)) + } + */ }() if s := "hello"; f(s) { defer println(s) diff --git a/cl/_testgo/defer/out.ll b/cl/_testgo/defer/out.ll index e69de29b..e2500b92 100644 --- a/cl/_testgo/defer/out.ll +++ b/cl/_testgo/defer/out.ll @@ -0,0 +1,238 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } +%"github.com/goplus/llgo/internal/runtime.eface" = type { ptr, ptr } +%"github.com/goplus/llgo/internal/runtime.Defer" = type { { ptr, ptr }, i64, ptr } + +@"main.init$guard" = global ptr null +@0 = private unnamed_addr constant [6 x i8] c"error\00", align 1 +@_llgo_string = linkonce global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = global ptr null +@__llgo_defer = linkonce global ptr null +@1 = private unnamed_addr constant [6 x i8] c"hello\00", align 1 +@2 = private unnamed_addr constant [6 x i8] c"hello\00", align 1 +@3 = private unnamed_addr constant [4 x i8] c"bye\00", align 1 +@4 = private unnamed_addr constant [6 x i8] c"world\00", align 1 +@5 = private unnamed_addr constant [3 x i8] c"hi\00", align 1 + +define i1 @main.f(%"github.com/goplus/llgo/internal/runtime.String" %0) { +_llgo_0: + %1 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %0, 1 + %2 = icmp sgt i64 %1, 2 + ret i1 %2 +} + +define void @main.fail() { +_llgo_0: + %0 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 0 + store ptr @0, ptr %1, align 8 + %2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 1 + store i64 5, ptr %2, align 4 + %3 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %0, align 8 + %4 = load ptr, ptr @_llgo_string, align 8 + %5 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" %3, ptr %5, align 8 + %6 = alloca %"github.com/goplus/llgo/internal/runtime.eface", align 8 + %7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.eface", ptr %6, i32 0, i32 0 + store ptr %4, ptr %7, align 8 + %8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.eface", ptr %6, i32 0, i32 1 + store ptr %5, ptr %8, align 8 + %9 = load %"github.com/goplus/llgo/internal/runtime.eface", ptr %6, align 8 + call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.eface" %9) + unreachable +} + +define void @main.init() { +_llgo_0: + %0 = load i1, ptr @"main.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"main.init$guard", align 1 + %1 = load i32, ptr @__llgo_defer, align 4 + %2 = icmp eq i32 %1, ptr null + br i1 %2, label %_llgo_3, label %_llgo_4 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void + +_llgo_3: ; preds = %_llgo_1 + %3 = call i32 @pthread_key_create(ptr @__llgo_defer, ptr null) + br label %_llgo_4 + +_llgo_4: ; preds = %_llgo_3, %_llgo_1 + call void @"main.init$abi"() +} + +define i32 @main(i32 %0, ptr %1) { +_llgo_0: + store i32 %0, ptr @__llgo_argc, align 4 + store ptr %1, ptr @__llgo_argv, align 8 + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %2 = load i32, ptr @__llgo_defer, align 4 + %3 = call ptr @pthread_getspecific(i32 %2) + %4 = alloca i8, i64 32, align 1 + %5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 0 + store ptr @main._llgo_defer, ptr %5, align 8 + %6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 1 + store i64 0, ptr %6, align 4 + %7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 2 + store ptr %3, ptr %7, align 8 + %8 = call i32 @pthread_setspecific(i32 %2, ptr %4) + %9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 1 + %10 = load i64, ptr %9, align 4 + %11 = or i64 %10, 1 + store i64 %11, ptr %9, align 4 + %12 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %13 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %12, i32 0, i32 0 + store ptr @1, ptr %13, align 8 + %14 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %12, i32 0, i32 1 + store i64 5, ptr %14, align 4 + %15 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %12, align 8 + %16 = call i1 @main.f(%"github.com/goplus/llgo/internal/runtime.String" %15) + br i1 %16, label %_llgo_2, label %_llgo_4 + +_llgo_1: ; No predecessors! + ret i32 0 + +_llgo_2: ; preds = %_llgo_0 + %17 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %18 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %17, i32 0, i32 0 + store ptr @2, ptr %18, align 8 + %19 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %17, i32 0, i32 1 + store i64 5, ptr %19, align 4 + %20 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %17, align 8 + %21 = load i32, ptr @__llgo_defer, align 4 + %22 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 1 + %23 = load i64, ptr %22, align 4 + %24 = or i64 %23, 2 + store i64 %24, ptr %22, align 4 + br label %_llgo_3 + +_llgo_3: ; preds = %_llgo_4, %_llgo_2 + %25 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %26 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %25, i32 0, i32 0 + store ptr @3, ptr %26, align 8 + %27 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %25, i32 0, i32 1 + store i64 3, ptr %27, align 4 + %28 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %25, align 8 + %29 = load i32, ptr @__llgo_defer, align 4 + %30 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 1 + %31 = load i64, ptr %30, align 4 + %32 = or i64 %31, 4 + store i64 %32, ptr %30, align 4 + %33 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 1 + %34 = load i64, ptr %33, align 4 + call void @main._llgo_defer(i64 %34) + ret i32 0 + +_llgo_4: ; preds = %_llgo_0 + %35 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %36 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %35, i32 0, i32 0 + store ptr @4, ptr %36, align 8 + %37 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %35, i32 0, i32 1 + store i64 5, ptr %37, align 4 + %38 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %35, align 8 + %39 = load i32, ptr @__llgo_defer, align 4 + %40 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 1 + %41 = load i64, ptr %40, align 4 + %42 = or i64 %41, 8 + store i64 %42, ptr %40, align 4 + call void @main.fail() + br label %_llgo_3 +} + +define void @"main.init$abi"() { +_llgo_0: + %0 = load ptr, ptr @_llgo_string, align 8 + %1 = icmp eq ptr %0, null + br i1 %1, label %_llgo_1, label %_llgo_2 + +_llgo_1: ; preds = %_llgo_0 + %2 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 24) + store ptr %2, ptr @_llgo_string, align 8 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64) + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64) + +declare void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.eface") + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +define void @"main.main$1"() { +_llgo_0: + %0 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 0 + store ptr @5, ptr %1, align 8 + %2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 1 + store i64 2, ptr %2, align 4 + %3 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %0, align 8 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %3) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + ret void +} + +define void @main._llgo_defer(i64 %0) { +_llgo_0: + %1 = and i64 %0, 1 + %2 = icmp ne i64 %1, 0 + br i1 %2, label %_llgo_1, label %_llgo_2 + +_llgo_1: ; preds = %_llgo_0 + call void @"main.main$1"() + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + %3 = and i64 %0, 2 + %4 = icmp ne i64 %3, 0 + br i1 %4, label %_llgo_3, label %_llgo_4 + +_llgo_3: ; preds = %_llgo_2 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %20) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_4 + +_llgo_4: ; preds = %_llgo_3, %_llgo_2 + %5 = and i64 %0, 4 + %6 = icmp ne i64 %5, 0 + br i1 %6, label %_llgo_5, label %_llgo_6 + +_llgo_5: ; preds = %_llgo_4 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %28) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_6 + +_llgo_6: ; preds = %_llgo_5, %_llgo_4 + %7 = and i64 %0, 8 + %8 = icmp ne i64 %7, 0 + br i1 %8, label %_llgo_7, label %_llgo_8 + +_llgo_7: ; preds = %_llgo_6 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %38) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_8 + +_llgo_8: ; preds = %_llgo_7, %_llgo_6 + ret void +} + +declare ptr @pthread_getspecific(i32) + +declare i32 @pthread_setspecific(i32, ptr) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String") + +declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) + +declare i32 @pthread_key_create(ptr, ptr) diff --git a/cl/compile.go b/cl/compile.go index 4b3ed739..3f34ef2a 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -800,6 +800,8 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { p.call(b, llssa.Defer, &v.Call) case *ssa.Go: p.call(b, llssa.Go, &v.Call) + case *ssa.RunDefers: + b.RunDefers() case *ssa.Panic: arg := p.compileValue(b, v.X) b.Panic(arg) diff --git a/ssa/decl.go b/ssa/decl.go index d91d4d1f..2cda172f 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -254,11 +254,6 @@ func (p Function) Name() string { return p.impl.Name() } -// DeferFuncName returns the name of the defer procedure. -func (p Function) DeferFuncName() string { - return p.Name() + "._llgo_defer" -} - // Params returns the function's ith parameter. func (p Function) Param(i int) Expr { i += p.base // skip if hasFreeVars diff --git a/ssa/expr.go b/ssa/expr.go index fa04059a..0c81e2a3 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -113,6 +113,16 @@ func (p Program) Zero(t Type) Expr { ret = llvm.ConstStruct(flds, false) case *types.Slice: ret = p.Zero(p.rtType("Slice")).impl + /* TODO(xsw): + case *types.Interface: + var name string + if u.Empty() { + name = "Eface" + } else { + name = "Iface" + } + ret = p.Zero(p.rtType(name)).impl + */ default: log.Panicln("todo:", u) } @@ -818,6 +828,11 @@ func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) { // Go spec (excluding "make" and "new"). func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { switch fn { + case "String": // unsafe.String + return b.unsafeString(args[0].impl, args[1].impl) + case "Slice": // unsafe.Slice + size := args[1].impl + return b.unsafeSlice(args[0], size, size) case "len": if len(args) == 1 { arg := args[0] @@ -857,8 +872,6 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { } } } - case "print", "println": - return b.PrintEx(fn == "println", args...) case "copy": if len(args) == 2 { dst := args[0] @@ -874,11 +887,10 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { } } } - case "String": // unsafe.String - return b.unsafeString(args[0].impl, args[1].impl) - case "Slice": // unsafe.Slice - size := args[1].impl - return b.unsafeSlice(args[0], size, size) + //case "recover": + // return b.Recover() + case "print", "println": + return b.PrintEx(fn == "println", args...) } panic("todo: " + fn) } diff --git a/ssa/package.go b/ssa/package.go index 94a5a18e..aa7ab492 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -333,16 +333,6 @@ func (p Program) Struct(typs ...Type) Type { return p.rawType(types.NewStruct(els, nil)) } -/* -// Eface returns the empty interface type. -func (p Program) Eface() Type { - if p.efaceTy == nil { - p.efaceTy = p.rawType(tyAny) - } - return p.efaceTy -} -*/ - // DeferPtr returns *runtime.Defer. func (p Program) DeferPtr() Type { if p.deferPtr == nil { @@ -436,7 +426,7 @@ func (p Program) String() Type { return p.stringTy } -// Any returns any type. +// Any returns the any (empty interface) type. func (p Program) Any() Type { if p.anyTy == nil { p.anyTy = p.rawType(tyAny) @@ -444,6 +434,12 @@ func (p Program) Any() Type { return p.anyTy } +// Eface returns the empty interface type. +// It is equivalent to Any. +func (p Program) Eface() Type { + return p.Any() +} + // CIntPtr returns *c.Int type. func (p Program) CIntPtr() Type { if p.cintPtr == nil { diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index 43523753..4cbb2ca0 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -175,6 +175,17 @@ func (p Package) newDeferKey() Global { return p.NewVarEx(deferKey, p.Prog.CIntPtr()) } +// DeferFuncName returns the name of the defer procedure. +func (p Function) DeferFuncName() string { + return p.Name() + "._llgo_defer" +} + +// DeferFunc returns the defer procedure of this function. +func (p Function) DeferFunc() Function { + name := p.DeferFuncName() + return p.Pkg.NewFunc(name, p.Prog.tyDeferFunc(), InC) +} + func (b Builder) deferKey() Expr { return b.Load(b.Pkg.newDeferKey().Expr) } @@ -192,8 +203,7 @@ func (b Builder) Defer(fn Expr, args ...Expr) { zero := prog.Val(uintptr(0)) key := b.deferKey() if next == 0 { - name := self.DeferFuncName() - deferfn := pkg.NewFunc(name, b.Prog.tyDeferFunc(), InC) + deferfn := self.DeferFunc() deferb := deferfn.MakeBody(1) pkg.deferb = unsafe.Pointer(deferb) pkg.deferparam = deferfn.Param(0) @@ -218,8 +228,27 @@ func (b Builder) Defer(fn Expr, args ...Expr) { }) } +// RunDefers emits instructions to run deferred instructions. +func (b Builder) RunDefers() { + self := b.Func + deferfn := self.DeferFunc() + bitsPtr := b.FieldAddr(self.deferData, 1) + b.Call(deferfn.Expr, b.Load(bitsPtr)) +} + // ----------------------------------------------------------------------------- +/* +// Recover emits a recover instruction. +func (b Builder) Recover() (v Expr) { + if debugInstr { + log.Println("Recover") + } + prog := b.Prog + return prog.Zero(prog.Eface()) +} +*/ + // Panic emits a panic instruction. func (b Builder) Panic(v Expr) { if debugInstr {