From 5e5d149ca5b7137c133be50be168251bed3a9ef3 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 11 Jun 2024 10:35:27 +0800 Subject: [PATCH] cl: isVargs support defer/go --- README.md | 4 +- cl/_testlibc/defer/in.go | 16 +++++ cl/_testlibc/defer/out.ll | 148 ++++++++++++++++++++++++++++++++++++++ cl/builtin_test.go | 31 ++++++++ cl/compile.go | 20 ++++-- internal/llgen/llgenf.go | 2 +- 6 files changed, 213 insertions(+), 8 deletions(-) create mode 100644 cl/_testlibc/defer/in.go create mode 100644 cl/_testlibc/defer/out.ll diff --git a/README.md b/README.md index d4ed6535..69d14a6a 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ Here are some examples related to Go syntax: ### Garbage Collection (GC) -By default, LLGo implements `gc` based on [bdwgc](https://www.hboehm.info/gc/). +By default, LLGo implements `gc` based on [bdwgc](https://www.hboehm.info/gc/) (also known as [libgc](https://www.hboehm.info/gc/)). However, you can disable gc by specifying the `nogc` tag. For example: @@ -214,7 +214,7 @@ Follow these steps to generate the `llgo` command (its usage is the same as the ```sh brew update # execute if needed -brew install bdw-gc +brew install libgc brew install llvm@17 go install -v ./... ``` diff --git a/cl/_testlibc/defer/in.go b/cl/_testlibc/defer/in.go new file mode 100644 index 00000000..8395a818 --- /dev/null +++ b/cl/_testlibc/defer/in.go @@ -0,0 +1,16 @@ +package main + +import "github.com/goplus/llgo/c" + +func f(s string) bool { + return len(s) > 2 +} + +func main() { + if s := "hello"; f(s) { + defer c.Printf(c.Str("%s\n"), c.AllocaCStr(s)) + } else { + defer c.Printf(c.Str("world\n")) + } + defer c.Printf(c.Str("bye\n")) +} diff --git a/cl/_testlibc/defer/out.ll b/cl/_testlibc/defer/out.ll new file mode 100644 index 00000000..07de7a11 --- /dev/null +++ b/cl/_testlibc/defer/out.ll @@ -0,0 +1,148 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } +%"github.com/goplus/llgo/internal/runtime.Defer" = type { i64, ptr, i64 } + +@"main.init$guard" = global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = global ptr null +@0 = private unnamed_addr constant [6 x i8] c"hello\00", align 1 +@1 = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1 +@2 = private unnamed_addr constant [6 x i8] c"hello\00", align 1 +@__llgo_defer = linkonce global ptr null +@3 = private unnamed_addr constant [7 x i8] c"world\0A\00", align 1 +@4 = private unnamed_addr constant [5 x i8] c"bye\0A\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.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 + call void @"main.init$after"() + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +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 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0 + store ptr @0, ptr %3, align 8 + %4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1 + store i64 5, ptr %4, align 4 + %5 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %2, align 8 + %6 = call i1 @main.f(%"github.com/goplus/llgo/internal/runtime.String" %5) + %7 = load ptr, ptr @__llgo_defer, align 8 + %8 = call ptr @pthread_getspecific(ptr %7) + %9 = alloca i8, i64 24, align 1 + %10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %9, i32 0, i32 0 + store i64 0, ptr %10, align 4 + %11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %9, i32 0, i32 1 + store ptr %8, ptr %11, align 8 + %12 = call i32 @pthread_setspecific(ptr %7, ptr %9) + %13 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %9, i32 0, i32 0 + %14 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %9, i32 0, i32 2 + br i1 %6, label %_llgo_1, label %_llgo_3 + +_llgo_1: ; preds = %_llgo_0 + %15 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %16 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %15, i32 0, i32 0 + store ptr @2, ptr %16, align 8 + %17 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %15, i32 0, i32 1 + store i64 5, ptr %17, align 4 + %18 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %15, align 8 + %19 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %18, 1 + %20 = add i64 %19, 1 + %21 = alloca i8, i64 %20, align 1 + %22 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %21, %"github.com/goplus/llgo/internal/runtime.String" %18) + %23 = load i64, ptr %13, align 4 + %24 = or i64 %23, 1 + store i64 %24, ptr %13, align 4 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_3, %_llgo_1 + store i64 0, ptr %14, align 4 + br label %_llgo_5 + +_llgo_3: ; preds = %_llgo_0 + %25 = load i64, ptr %13, align 4 + %26 = or i64 %25, 2 + store i64 %26, ptr %13, align 4 + br label %_llgo_2 + +_llgo_4: ; No predecessors! + ret i32 0 + +_llgo_5: ; preds = %_llgo_2 + %27 = load i64, ptr %13, align 4 + %28 = call i32 (ptr, ...) @printf(ptr @4) + %29 = and i64 %27, 2 + %30 = icmp ne i64 %29, 0 + br i1 %30, label %_llgo_7, label %_llgo_8 + +_llgo_6: ; preds = %_llgo_10 + ret i32 0 + +_llgo_7: ; preds = %_llgo_5 + %31 = call i32 (ptr, ...) @printf(ptr @3) + br label %_llgo_8 + +_llgo_8: ; preds = %_llgo_7, %_llgo_5 + %32 = and i64 %27, 1 + %33 = icmp ne i64 %32, 0 + br i1 %33, label %_llgo_9, label %_llgo_10 + +_llgo_9: ; preds = %_llgo_8 + %34 = call i32 (ptr, ...) @printf(ptr @1, ptr %22) + br label %_llgo_10 + +_llgo_10: ; preds = %_llgo_9, %_llgo_8 + %35 = load %"github.com/goplus/llgo/internal/runtime.Defer", ptr %9, align 8 + %36 = extractvalue %"github.com/goplus/llgo/internal/runtime.Defer" %35, 2 + %37 = call i32 @pthread_setspecific(ptr %7, i64 %36) + %38 = load i64, ptr %14, align 4 + switch i64 %38, label %_llgo_6 [ + ] +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String") + +declare i32 @printf(ptr, ...) + +declare ptr @pthread_getspecific(i32) + +declare i32 @pthread_setspecific(i32, ptr) + +define void @"main.init$after"() { +_llgo_0: + %0 = load ptr, ptr @__llgo_defer, align 8 + %1 = icmp eq ptr %0, null + br i1 %1, label %_llgo_1, label %_llgo_2 + +_llgo_1: ; preds = %_llgo_0 + %2 = call i32 @pthread_key_create(ptr @__llgo_defer, ptr null) + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +declare i32 @pthread_key_create(ptr, ptr) diff --git a/cl/builtin_test.go b/cl/builtin_test.go index 8ced2dc1..13debf7c 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -21,11 +21,42 @@ import ( "go/constant" "go/types" "testing" + "unsafe" llssa "github.com/goplus/llgo/ssa" "golang.org/x/tools/go/ssa" ) +func TestIsVargs(t *testing.T) { + if isVargs(nil, ssaAlloc(&ssa.Return{})) { + t.Fatal("isVargs?") + } + if isVargs(nil, ssaAlloc(ssaSlice(&ssa.Go{}))) { + t.Fatal("isVargs?") + } + if isVargs(nil, ssaAlloc(ssaSlice(&ssa.Return{}))) { + t.Fatal("isVargs?") + } +} + +func ssaSlice(refs ...ssa.Instruction) *ssa.Slice { + a := &ssa.Slice{} + setRefs(unsafe.Pointer(a), refs...) + return a +} + +func ssaAlloc(refs ...ssa.Instruction) *ssa.Alloc { + a := &ssa.Alloc{} + setRefs(unsafe.Pointer(a), refs...) + return a +} + +func setRefs(v unsafe.Pointer, refs ...ssa.Instruction) { + off := unsafe.Offsetof(ssa.Alloc{}.Comment) - unsafe.Sizeof([]int(nil)) + ptr := uintptr(v) + off + *(*[]ssa.Instruction)(unsafe.Pointer(ptr)) = refs +} + func TestRecvTypeName(t *testing.T) { if ret := recvTypeName(&ast.IndexExpr{ X: &ast.Ident{Name: "Pointer"}, diff --git a/cl/compile.go b/cl/compile.go index 4239cd76..b8d1613e 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -430,9 +430,10 @@ func intVal(v ssa.Value) int64 { panic("intVal: ssa.Value is not a const int") } -func (p *context) isVArgs(vx ssa.Value) (ret []llssa.Expr, ok bool) { - if va, vok := vx.(*ssa.Alloc); vok { - ret, ok = p.vargs[va] // varargs: this is a varargs index +func (p *context) isVArgs(v ssa.Value) (ret []llssa.Expr, ok bool) { + switch v := v.(type) { + case *ssa.Alloc: + ret, ok = p.vargs[v] // varargs: this is a varargs index } return } @@ -455,9 +456,18 @@ func isVargs(ctx *context, v *ssa.Alloc) bool { lastref := refs[n-1] if i, ok := lastref.(*ssa.Slice); ok { if refs = *i.Referrers(); len(refs) == 1 { - if call, ok := refs[0].(*ssa.Call); ok { - return ctx.funcKind(call.Call.Value) == fnHasVArg + var call *ssa.CallCommon + switch ref := refs[0].(type) { + case *ssa.Call: + call = &ref.Call + case *ssa.Defer: + call = &ref.Call + case *ssa.Go: + call = &ref.Call + default: + return false } + return ctx.funcKind(call.Value) == fnHasVArg } } return false diff --git a/internal/llgen/llgenf.go b/internal/llgen/llgenf.go index 369eabd5..843c245a 100644 --- a/internal/llgen/llgenf.go +++ b/internal/llgen/llgenf.go @@ -131,5 +131,5 @@ func genZip(dir string, outFile, inFile string) { } func inCompilerDir(dir string) bool { - return strings.Contains(dir, "/llgo/cl/") + return strings.Contains(dir, "/cl/_test") }