diff --git a/cl/_testdata/print/out.ll b/cl/_testdata/print/out.ll index 9f89186a..8fbc290b 100644 --- a/cl/_testdata/print/out.ll +++ b/cl/_testdata/print/out.ll @@ -85,7 +85,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 - call void @"main.init$abi"() + call void @"main.init$after"() store i64 0, ptr @main.minhexdigits, align 4 br label %_llgo_2 @@ -1412,7 +1412,7 @@ declare i32 @printf(ptr, ...) declare void @"github.com/goplus/llgo/internal/runtime.init"() -define void @"main.init$abi"() { +define void @"main.init$after"() { _llgo_0: %0 = load ptr, ptr @_llgo_float32, align 8 %1 = icmp eq ptr %0, null diff --git a/cl/_testdata/vargs/out.ll b/cl/_testdata/vargs/out.ll index a687457b..2dcb1327 100644 --- a/cl/_testdata/vargs/out.ll +++ b/cl/_testdata/vargs/out.ll @@ -19,7 +19,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 - call void @"main.init$abi"() + call void @"main.init$after"() br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -118,7 +118,7 @@ declare void @"github.com/goplus/llgo/internal/runtime.init"() declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) -define void @"main.init$abi"() { +define void @"main.init$after"() { _llgo_0: %0 = load ptr, ptr @_llgo_int, align 8 %1 = icmp eq ptr %0, null diff --git a/cl/_testgo/defer/in.go b/cl/_testgo/defer/in.go new file mode 100644 index 00000000..82d34e31 --- /dev/null +++ b/cl/_testgo/defer/in.go @@ -0,0 +1,18 @@ +package main + +func f(s string) bool { + return len(s) > 2 +} + +func main() { + defer func() { + println("hi") + }() + if s := "hello"; f(s) { + defer println(s) + } else { + defer println("world") + return + } + defer println("bye") +} diff --git a/cl/_testgo/defer/out.ll b/cl/_testgo/defer/out.ll new file mode 100644 index 00000000..d7649276 --- /dev/null +++ b/cl/_testgo/defer/out.ll @@ -0,0 +1,199 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } +%"github.com/goplus/llgo/internal/runtime.Defer" = type { { ptr, ptr }, i64, ptr, i64 } + +@"main.init$guard" = global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = global ptr null +@__llgo_defer = linkonce global ptr null +@0 = private unnamed_addr constant [6 x i8] c"hello\00", align 1 +@1 = private unnamed_addr constant [6 x i8] c"hello\00", align 1 +@2 = private unnamed_addr constant [4 x i8] c"bye\00", align 1 +@3 = private unnamed_addr constant [6 x i8] c"world\00", align 1 +@4 = 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.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 = load ptr, ptr @__llgo_defer, align 8 + %3 = call ptr @pthread_getspecific(ptr %2) + %4 = alloca i8, i64 40, align 1 + %5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 0 + store ptr null, 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(ptr %2, ptr %4) + %9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 1 + %10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, i32 0, i32 3 + %11 = load i64, ptr %9, align 4 + %12 = or i64 %11, 1 + store i64 %12, ptr %9, align 4 + %13 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %14 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %13, i32 0, i32 0 + store ptr @0, ptr %14, align 8 + %15 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %13, i32 0, i32 1 + store i64 5, ptr %15, align 4 + %16 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %13, align 8 + %17 = call i1 @main.f(%"github.com/goplus/llgo/internal/runtime.String" %16) + br i1 %17, label %_llgo_2, label %_llgo_3 + +_llgo_1: ; No predecessors! + ret i32 0 + +_llgo_2: ; preds = %_llgo_0 + %18 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %19 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %18, i32 0, i32 0 + store ptr @1, ptr %19, align 8 + %20 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %18, i32 0, i32 1 + store i64 5, ptr %20, align 4 + %21 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %18, align 8 + %22 = load i64, ptr %9, align 4 + %23 = or i64 %22, 2 + store i64 %23, ptr %9, align 4 + %24 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %25 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %24, i32 0, i32 0 + store ptr @2, ptr %25, align 8 + %26 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %24, i32 0, i32 1 + store i64 3, ptr %26, align 4 + %27 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %24, align 8 + %28 = load i64, ptr %9, align 4 + %29 = or i64 %28, 4 + store i64 %29, ptr %9, align 4 + store i64 0, ptr %10, align 4 + br label %_llgo_4 + +_llgo_3: ; preds = %_llgo_0 + %30 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %31 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %30, i32 0, i32 0 + store ptr @3, ptr %31, align 8 + %32 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %30, i32 0, i32 1 + store i64 5, ptr %32, align 4 + %33 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %30, align 8 + %34 = load i64, ptr %9, align 4 + %35 = or i64 %34, 8 + store i64 %35, ptr %9, align 4 + store i64 1, ptr %10, align 4 + br label %_llgo_4 + +_llgo_4: ; preds = %_llgo_3, %_llgo_2 + %36 = load i64, ptr %9, align 4 + %37 = and i64 %36, 8 + %38 = icmp ne i64 %37, 0 + br i1 %38, label %_llgo_7, label %_llgo_8 + +_llgo_5: ; preds = %_llgo_14 + ret i32 0 + +_llgo_6: ; preds = %_llgo_14 + ret i32 0 + +_llgo_7: ; preds = %_llgo_4 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %33) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_8 + +_llgo_8: ; preds = %_llgo_7, %_llgo_4 + %39 = and i64 %36, 4 + %40 = icmp ne i64 %39, 0 + br i1 %40, label %_llgo_9, label %_llgo_10 + +_llgo_9: ; preds = %_llgo_8 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %27) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_10 + +_llgo_10: ; preds = %_llgo_9, %_llgo_8 + %41 = and i64 %36, 2 + %42 = icmp ne i64 %41, 0 + br i1 %42, label %_llgo_11, label %_llgo_12 + +_llgo_11: ; preds = %_llgo_10 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %21) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_12 + +_llgo_12: ; preds = %_llgo_11, %_llgo_10 + %43 = and i64 %36, 1 + %44 = icmp ne i64 %43, 0 + br i1 %44, label %_llgo_13, label %_llgo_14 + +_llgo_13: ; preds = %_llgo_12 + call void @"main.main$1"() + br label %_llgo_14 + +_llgo_14: ; preds = %_llgo_13, %_llgo_12 + %45 = load %"github.com/goplus/llgo/internal/runtime.Defer", ptr %4, align 8 + %46 = extractvalue %"github.com/goplus/llgo/internal/runtime.Defer" %45, 2 + %47 = call i32 @pthread_setspecific(ptr %2, ptr %46) + %48 = load i64, ptr %10, align 4 + switch i64 %48, label %_llgo_5 [ + i64 1, label %_llgo_6 + ] +} + +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 @4, 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 +} + +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) + +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/_testgo/errors/out.ll b/cl/_testgo/errors/out.ll index 599cc745..56719002 100644 --- a/cl/_testgo/errors/out.ll +++ b/cl/_testgo/errors/out.ll @@ -60,7 +60,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 - call void @"main.init$abi"() + call void @"main.init$after"() br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -102,7 +102,7 @@ _llgo_0: declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) -define void @"main.init$abi"() { +define void @"main.init$after"() { _llgo_0: %0 = call ptr @"github.com/goplus/llgo/internal/runtime.NewNamed"(i64 25, i64 0, i64 1) store ptr %0, ptr @_llgo_main.errorString, align 8 diff --git a/cl/_testgo/strucintf/out.ll b/cl/_testgo/strucintf/out.ll index f23f046d..57632d34 100644 --- a/cl/_testgo/strucintf/out.ll +++ b/cl/_testgo/strucintf/out.ll @@ -49,7 +49,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 call void @"github.com/goplus/llgo/cl/internal/foo.init"() - call void @"main.init$abi"() + call void @"main.init$after"() br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -241,7 +241,7 @@ _llgo_18: ; preds = %_llgo_17, %_llgo_16 declare ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr, i64) -define void @"main.init$abi"() { +define void @"main.init$after"() { _llgo_0: %0 = load ptr, ptr @_llgo_int, align 8 %1 = icmp eq ptr %0, null diff --git a/cl/_testgo/struczero/out.ll b/cl/_testgo/struczero/out.ll index 2bd65e65..48894dd4 100644 --- a/cl/_testgo/struczero/out.ll +++ b/cl/_testgo/struczero/out.ll @@ -117,7 +117,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 call void @"github.com/goplus/llgo/cl/internal/foo.init"() - call void @"main.init$abi"() + call void @"main.init$after"() br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -185,7 +185,7 @@ _llgo_0: ret i32 0 } -define void @"main.init$abi"() { +define void @"main.init$after"() { _llgo_0: %0 = load ptr, ptr @"_llgo_github.com/goplus/llgo/cl/internal/foo.Foo", align 8 %1 = icmp eq ptr %0, null diff --git a/cl/_testrt/any/out.ll b/cl/_testrt/any/out.ll index d2f59f3f..83e277c1 100644 --- a/cl/_testrt/any/out.ll +++ b/cl/_testrt/any/out.ll @@ -68,7 +68,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 - call void @"main.init$abi"() + call void @"main.init$after"() br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -101,7 +101,7 @@ _llgo_0: ret i32 0 } -define void @"main.init$abi"() { +define void @"main.init$after"() { _llgo_0: %0 = load ptr, ptr @_llgo_int8, align 8 %1 = icmp eq ptr %0, null diff --git a/cl/_testrt/builtin/out.ll b/cl/_testrt/builtin/out.ll index 8045b41c..ffd5bc49 100644 --- a/cl/_testrt/builtin/out.ll +++ b/cl/_testrt/builtin/out.ll @@ -86,7 +86,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 - call void @"main.init$abi"() + call void @"main.init$after"() store i64 9223372036854775807, ptr @main.a, align 4 store i64 -9223372036854775808, ptr @main.b, align 4 store i64 -1, ptr @main.n, align 4 @@ -643,7 +643,7 @@ declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.SliceAppend"(%"github.com/goplus/llgo/internal/runtime.Slice", ptr, i64, i64) -define void @"main.init$abi"() { +define void @"main.init$after"() { _llgo_0: %0 = load ptr, ptr @_llgo_int, align 8 %1 = icmp eq ptr %0, null diff --git a/cl/_testrt/cast/out.ll b/cl/_testrt/cast/out.ll index 1f6b0f30..5225b11c 100644 --- a/cl/_testrt/cast/out.ll +++ b/cl/_testrt/cast/out.ll @@ -401,7 +401,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 - call void @"main.init$abi"() + call void @"main.init$after"() br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -472,7 +472,7 @@ _llgo_0: ret i32 0 } -define void @"main.init$abi"() { +define void @"main.init$after"() { _llgo_0: %0 = load ptr, ptr @_llgo_string, align 8 %1 = icmp eq ptr %0, null diff --git a/cl/_testrt/eface/out.ll b/cl/_testrt/eface/out.ll index af51095f..c809135b 100644 --- a/cl/_testrt/eface/out.ll +++ b/cl/_testrt/eface/out.ll @@ -199,7 +199,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 call void @"github.com/goplus/llgo/internal/abi.init"() - call void @"main.init$abi"() + call void @"main.init$after"() br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -436,7 +436,7 @@ declare void @"github.com/goplus/llgo/internal/abi.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"() -define void @"main.init$abi"() { +define void @"main.init$after"() { _llgo_0: %0 = load ptr, ptr @_llgo_bool, align 8 %1 = icmp eq ptr %0, null diff --git a/cl/_testrt/panic/out.ll b/cl/_testrt/panic/out.ll index 4bac3016..ce254403 100644 --- a/cl/_testrt/panic/out.ll +++ b/cl/_testrt/panic/out.ll @@ -17,7 +17,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 - call void @"main.init$abi"() + call void @"main.init$after"() br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -51,7 +51,7 @@ _llgo_0: declare void @"github.com/goplus/llgo/internal/runtime.init"() -define void @"main.init$abi"() { +define void @"main.init$after"() { _llgo_0: %0 = load ptr, ptr @_llgo_string, align 8 %1 = icmp eq ptr %0, null diff --git a/cl/compile.go b/cl/compile.go index 19b7e265..7ace3f5e 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -285,6 +285,7 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun for _, phi := range p.phis { phi() } + b.EndBuild() }) } return fn, nil, goFunc @@ -796,8 +797,12 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { key := p.compileValue(b, v.Key) val := p.compileValue(b, v.Value) b.MapUpdate(m, key, val) + case *ssa.Defer: + 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/internal/abi/llgo_autogen.lla b/internal/abi/llgo_autogen.lla index 747da4e5..eb0833a5 100644 Binary files a/internal/abi/llgo_autogen.lla and b/internal/abi/llgo_autogen.lla differ diff --git a/internal/runtime/llgo_autogen.lla b/internal/runtime/llgo_autogen.lla index 6574534d..cfd6dabb 100644 Binary files a/internal/runtime/llgo_autogen.lla and b/internal/runtime/llgo_autogen.lla differ diff --git a/internal/runtime/z_c.go b/internal/runtime/z_c.go index 2689b8cc..e7a1bef4 100644 --- a/internal/runtime/z_c.go +++ b/internal/runtime/z_c.go @@ -35,7 +35,7 @@ func AllocZ(size uintptr) unsafe.Pointer { } // Zeroinit initializes memory to zero. -func Zeroinit(p c.Pointer, size uintptr) c.Pointer { +func Zeroinit(p unsafe.Pointer, size uintptr) unsafe.Pointer { return c.Memset(p, 0, size) } diff --git a/internal/runtime/z_defer.go b/internal/runtime/z_defer.go new file mode 100644 index 00000000..007d4888 --- /dev/null +++ b/internal/runtime/z_defer.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package runtime + +// Defer presents defer statements in a function. +type Defer struct { + proc func(uintptr) + bits uintptr + link *Defer + rund int // index of RunDefers +} + +// DeferProc calls deferred statements. +func DeferProc(d *Defer) { + for d != nil { + d.proc(d.bits) + d = d.link + _ = d.rund + } +} diff --git a/ssa/abitype.go b/ssa/abitype.go index a0deac81..309f58da 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -19,7 +19,6 @@ package ssa import ( "go/token" "go/types" - "unsafe" "github.com/goplus/llgo/ssa/abi" "github.com/goplus/llvm" @@ -285,32 +284,8 @@ func lastParamType(prog Program, fn Expr) Type { // ----------------------------------------------------------------------------- -type abiTypes struct { - iniabi unsafe.Pointer -} - -func (p Package) hasAbiInit() bool { - return p.iniabi != nil -} - -func (p Package) abiInit(b Builder) { - inib := Builder(p.iniabi) - inib.Return() - b.Call(inib.Func.Expr) -} - -func (p Package) abiBuilder() Builder { - if p.iniabi == nil { - sigAbiInit := types.NewSignatureType(nil, nil, nil, nil, nil, false) - fn := p.NewFunc(p.Path()+".init$abi", sigAbiInit, InC) - fnb := fn.MakeBody(1) - p.iniabi = unsafe.Pointer(fnb) - } - return Builder(p.iniabi) -} - func (p Package) abiTypeInit(g Global, t types.Type, pub bool) { - b := p.abiBuilder() + b := p.afterBuilder() tabi := b.abiTypeOf(t) expr := g.Expr var eq Expr diff --git a/ssa/decl.go b/ssa/decl.go index 255bc1fc..889f5aa1 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -85,15 +85,13 @@ func (p Package) NewVar(name string, typ types.Type, bg Background) Global { return p.doNewVar(name, t) } -/* -// NewVarFrom creates a new global variable. -func (p Package) NewVarFrom(name string, t Type) Global { +// NewVarEx creates a new global variable. +func (p Package) NewVarEx(name string, t Type) Global { if v, ok := p.vars[name]; ok { return v } return p.doNewVar(name, t) } -*/ func (p Package) doNewVar(name string, t Type) Global { var gbl llvm.Value @@ -182,6 +180,8 @@ type aFunction struct { blks []BasicBlock + defer_ *aDefer + params []Type freeVars Expr base int // base = 1 if hasFreeVars; base = 0 otherwise @@ -222,7 +222,14 @@ func newFunction(fn llvm.Value, t Type, pkg Package, prog Program, hasFreeVars b if hasFreeVars { base = 1 } - return &aFunction{Expr{fn, t}, pkg, prog, nil, params, Expr{}, base, hasVArg} + return &aFunction{ + Expr: Expr{fn, t}, + Pkg: pkg, + Prog: prog, + params: params, + base: base, + hasVArg: hasVArg, + } } func newParams(fn Type, prog Program) (params []Type, hasVArg bool) { diff --git a/ssa/expr.go b/ssa/expr.go index c829d3aa..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) } @@ -493,7 +503,6 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { typ := t.raw.Type switch typ.(type) { default: - // TODO(xsw): remove instr name ret.impl = llvm.CreateBitCast(b.impl, x.impl, t.ll) ret.Type = b.Prog.rawType(typ) return @@ -748,7 +757,6 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { var ll llvm.Type var data Expr var sig *types.Signature - var prog = b.Prog var raw = fn.raw.Type switch kind { case vkClosure: @@ -758,7 +766,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { fallthrough case vkFuncPtr: sig = raw.(*types.Signature) - ll = prog.FuncDecl(sig, InC).ll + ll = b.Prog.FuncDecl(sig, InC).ll case vkFuncDecl: sig = raw.(*types.Signature) ll = fn.ll @@ -768,7 +776,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { default: log.Panicf("unreachable: %d(%T)\n", kind, raw) } - ret.Type = prog.retType(sig) + ret.Type = b.Prog.retType(sig) ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b)) return } @@ -795,8 +803,8 @@ type DoAction int const ( Call DoAction = iota - Go Defer + Go ) // Do call a function with an action. @@ -804,6 +812,8 @@ func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) { switch da { case Call: return b.Call(fn, args...) + case Defer: + b.Defer(fn, args...) case Go: b.Go(fn, args...) } @@ -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/goroutine.go b/ssa/goroutine.go index e050632a..389ae8df 100644 --- a/ssa/goroutine.go +++ b/ssa/goroutine.go @@ -50,7 +50,7 @@ func (p Program) tyPthreadCreate() *types.Signature { return p.createThdTy } -func (b Builder) createThread(pp, attr, routine, arg Expr) Expr { +func (b Builder) pthreadCreate(pp, attr, routine, arg Expr) Expr { fn := b.Pkg.cFunc("pthread_create", b.Prog.tyPthreadCreate()) return b.Call(fn, pp, attr, routine, arg) } @@ -86,7 +86,7 @@ func (b Builder) Go(fn Expr, args ...Expr) { data := Expr{b.aggregateMalloc(t, flds...), voidPtr} size := prog.SizeOf(voidPtr) pthd := b.Alloca(prog.IntVal(uint64(size), prog.Uintptr())) - b.createThread(pthd, prog.Null(voidPtr), pkg.routine(t, len(args)), data) + b.pthreadCreate(pthd, prog.Null(voidPtr), pkg.routine(t, len(args)), data) } func (p Package) routineName() string { @@ -112,3 +112,71 @@ func (p Package) routine(t Type, n int) Expr { } // ----------------------------------------------------------------------------- + +// func(c.Pointer) +func (p Program) tyDestruct() *types.Signature { + if p.destructTy == nil { + paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type) + params := types.NewTuple(paramPtr) + p.destructTy = types.NewSignatureType(nil, nil, nil, params, nil, false) + } + return p.destructTy +} + +// func(*c.Int, func(c.Pointer)) c.Int +func (p Program) tyPthreadKeyCreate() *types.Signature { + if p.createKeyTy == nil { + cint := p.CInt() + cintPtr := p.Pointer(cint) + paramCintPtr := types.NewParam(token.NoPos, nil, "", cintPtr.raw.Type) + paramDestruct := types.NewParam(token.NoPos, nil, "", p.tyDestruct()) + paramCInt := types.NewParam(token.NoPos, nil, "", cint.raw.Type) + params := types.NewTuple(paramCintPtr, paramDestruct) + results := types.NewTuple(paramCInt) + p.createKeyTy = types.NewSignatureType(nil, nil, nil, params, results, false) + } + return p.createKeyTy +} + +func (b Builder) pthreadKeyCreate(key, destruct Expr) Expr { + fn := b.Pkg.cFunc("pthread_key_create", b.Prog.tyPthreadKeyCreate()) + return b.Call(fn, key, destruct) +} + +// ----------------------------------------------------------------------------- + +// func(c.Int) c.Pointer +func (p Program) tyPthreadGetspecific() *types.Signature { + if p.getSpecTy == nil { + paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type) + paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type) + params := types.NewTuple(paramCInt) + results := types.NewTuple(paramPtr) + p.getSpecTy = types.NewSignatureType(nil, nil, nil, params, results, false) + } + return p.getSpecTy +} + +// func(c.Int, c.Pointer) c.Int +func (p Program) tyPthreadSetspecific() *types.Signature { + if p.setSpecTy == nil { + paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type) + paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type) + params := types.NewTuple(paramCInt, paramPtr) + results := types.NewTuple(paramCInt) + p.setSpecTy = types.NewSignatureType(nil, nil, nil, params, results, false) + } + return p.setSpecTy +} + +func (b Builder) pthreadGetspecific(key Expr) Expr { + fn := b.Pkg.cFunc("pthread_getspecific", b.Prog.tyPthreadGetspecific()) + return b.Call(fn, key) +} + +func (b Builder) pthreadSetspecific(key, val Expr) Expr { + fn := b.Pkg.cFunc("pthread_setspecific", b.Prog.tyPthreadSetspecific()) + return b.Call(fn, key, val) +} + +// ----------------------------------------------------------------------------- diff --git a/ssa/memory.go b/ssa/memory.go index df7b4908..94cfc7f3 100644 --- a/ssa/memory.go +++ b/ssa/memory.go @@ -74,6 +74,14 @@ func (b Builder) aggregateAllocU(t Type, flds ...llvm.Value) llvm.Value { return ptr } +func (b Builder) aggregateAlloca(t Type, flds ...llvm.Value) llvm.Value { + prog := b.Prog + size := prog.SizeOf(t) + ptr := b.Alloca(prog.IntVal(size, prog.Uintptr())).impl + aggregateInit(b.impl, ptr, t.ll, flds...) + return ptr +} + func (b Builder) aggregateMalloc(t Type, flds ...llvm.Value) llvm.Value { prog := b.Prog size := prog.SizeOf(t) diff --git a/ssa/package.go b/ssa/package.go index a81deef7..e96a595d 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -20,6 +20,7 @@ import ( "go/token" "go/types" "strconv" + "unsafe" "github.com/goplus/llgo/ssa/abi" "github.com/goplus/llvm" @@ -130,13 +131,14 @@ type aProgram struct { rtSliceTy llvm.Type rtMapTy llvm.Type - anyTy Type - voidTy Type - voidPtr Type - voidPPtr Type - boolTy Type - cstrTy Type - cintTy Type + anyTy Type + voidTy Type + voidPtr Type + voidPPtr Type + boolTy Type + cstrTy Type + cintTy Type + //cintPtr Type stringTy Type uintptrTy Type intTy Type @@ -148,26 +150,36 @@ type aProgram struct { u32Ty Type i64Ty Type u64Ty Type + pyObjPtr Type pyObjPPtr Type - abiTyptr Type - abiTypptr Type - pyImpTy *types.Signature - pyNewList *types.Signature - pyListSetI *types.Signature - callArgs *types.Signature - callNoArgs *types.Signature - callOneArg *types.Signature - callFOArgs *types.Signature - loadPyModS *types.Signature - getAttrStr *types.Signature + abiTyPtr Type + abiTyPPtr Type + deferTy Type + deferPtr Type + deferPPtr Type + + pyImpTy *types.Signature + pyNewList *types.Signature + pyListSetI *types.Signature + floatFromDbl *types.Signature + callNoArgs *types.Signature + callOneArg *types.Signature + callFOArgs *types.Signature + loadPyModS *types.Signature + getAttrStr *types.Signature mallocTy *types.Signature freeTy *types.Signature + createKeyTy *types.Signature createThdTy *types.Signature + getSpecTy *types.Signature + setSpecTy *types.Signature routineTy *types.Signature + destructTy *types.Signature + //deferFnTy *types.Signature paramObjPtr_ *types.Var @@ -323,30 +335,44 @@ 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) +// Defer returns runtime.Defer type. +func (p Program) Defer() Type { + if p.deferTy == nil { + p.deferTy = p.rtType("Defer") } - return p.efaceTy + return p.deferTy } -*/ -// AbiTypePtr returns *abi.Type. +// DeferPtr returns *runtime.Defer type. +func (p Program) DeferPtr() Type { + if p.deferPtr == nil { + p.deferPtr = p.Pointer(p.Defer()) + } + return p.deferPtr +} + +// DeferPtrPtr returns **runtime.Defer type. +func (p Program) DeferPtrPtr() Type { + if p.deferPPtr == nil { + p.deferPPtr = p.Pointer(p.DeferPtr()) + } + return p.deferPPtr +} + +// AbiTypePtr returns *abi.Type type. func (p Program) AbiTypePtr() Type { - if p.abiTyptr == nil { - p.abiTyptr = p.rawType(types.NewPointer(p.rtNamed("Type"))) + if p.abiTyPtr == nil { + p.abiTyPtr = p.rawType(types.NewPointer(p.rtNamed("Type"))) } - return p.abiTyptr + return p.abiTyPtr } -// AbiTypePtrPtr returns **abi.Type. +// AbiTypePtrPtr returns **abi.Type type. func (p Program) AbiTypePtrPtr() Type { - if p.abiTypptr == nil { - p.abiTypptr = p.Pointer(p.AbiTypePtr()) + if p.abiTyPPtr == nil { + p.abiTyPPtr = p.Pointer(p.AbiTypePtr()) } - return p.abiTypptr + return p.abiTyPPtr } // PyObjectPtrPtr returns the **py.Object type. @@ -410,7 +436,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) @@ -418,6 +444,23 @@ 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 { + p.cintPtr = p.Pointer(p.CInt()) + } + return p.cintPtr +} +*/ + +// CInt returns c.Int type. func (p Program) CInt() Type { if p.cintTy == nil { // C.int p.cintTy = p.rawType(types.Typ[types.Int32]) // TODO(xsw): support 64-bit @@ -518,14 +561,15 @@ func (p Program) Uint64() Type { type aPackage struct { mod llvm.Module abi abi.Builder - abiTypes + + Prog Program vars map[string]Global fns map[string]Function stubs map[string]Function pyobjs map[string]PyObjRef pymods map[string]Global - Prog Program + afterb unsafe.Pointer iRoutine int } @@ -593,14 +637,29 @@ func (p Package) String() string { return p.mod.String() } +// ----------------------------------------------------------------------------- + +func (p Package) afterBuilder() Builder { + if p.afterb == nil { + sigAfterInit := types.NewSignatureType(nil, nil, nil, nil, nil, false) + fn := p.NewFunc(p.Path()+".init$after", sigAfterInit, InC) + fnb := fn.MakeBody(1) + p.afterb = unsafe.Pointer(fnb) + } + return Builder(p.afterb) +} + // AfterInit is called after the package is initialized (init all packages that depends on). func (p Package) AfterInit(b Builder, ret BasicBlock) { - doAbiInit := p.hasAbiInit() + p.deferInit() + doAfterb := p.afterb != nil doPyLoadModSyms := p.pyHasModSyms() - if doAbiInit || doPyLoadModSyms { + if doAfterb || doPyLoadModSyms { b.SetBlockEx(ret, afterInit, false) - if doAbiInit { - p.abiInit(b) + if doAfterb { + afterb := Builder(p.afterb) + afterb.Return() + b.Call(afterb.Func.Expr) } if doPyLoadModSyms { p.pyLoadModSyms(b) @@ -608,6 +667,8 @@ func (p Package) AfterInit(b Builder, ret BasicBlock) { } } +// ----------------------------------------------------------------------------- + /* type CodeGenFileType = llvm.CodeGenFileType @@ -739,14 +800,14 @@ func (p Program) tyNewList() *types.Signature { // func(float64) *Object func (p Program) tyFloatFromDouble() *types.Signature { - if p.callArgs == nil { + if p.floatFromDbl == nil { paramObjPtr := p.paramObjPtr() paramFloat := types.NewParam(token.NoPos, nil, "", p.Float64().raw.Type) params := types.NewTuple(paramFloat) results := types.NewTuple(paramObjPtr) - p.callArgs = types.NewSignatureType(nil, nil, nil, params, results, false) + p.floatFromDbl = types.NewSignatureType(nil, nil, nil, params, results, false) } - return p.callArgs + return p.floatFromDbl } // func(*Object, ...) diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index f861fb97..b9ef006d 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -27,6 +27,16 @@ import ( "github.com/goplus/llvm" ) +func TestEndDefer(t *testing.T) { + prog := NewProgram(nil) + pkg := prog.NewPackage("foo", "foo") + sigMain := types.NewSignatureType(nil, nil, nil, nil, nil, false) + fn := pkg.NewFunc("main", sigMain, InC) + b := fn.MakeBody(1) + fn.defer_ = &aDefer{} + fn.endDefer(b) +} + func TestUnsafeString(t *testing.T) { prog := NewProgram(nil) prog.SetRuntime(func() *types.Package { @@ -187,6 +197,7 @@ func TestVar(t *testing.T) { if pkg.NewVar("a", types.Typ[types.Int], InGo) != a { t.Fatal("NewVar(a) failed") } + pkg.NewVarEx("a", prog.Type(types.Typ[types.Int], InGo)) a.Init(prog.Val(100)) b := pkg.NewVar("b", types.Typ[types.Int], InGo) b.Init(a.Expr) diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index 92fcd37f..36da0a9d 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -19,6 +19,7 @@ package ssa import ( "bytes" "fmt" + "go/token" "go/types" "log" "strings" @@ -61,6 +62,11 @@ type aBuilder struct { // Builder represents a builder for creating instructions in a function. type Builder = *aBuilder +// EndBuild ends the build process of a function. +func (b Builder) EndBuild() { + b.Func.endDefer(b) +} + // Dispose disposes of the builder. func (b Builder) Dispose() { b.impl.Dispose() @@ -128,6 +134,164 @@ func notInit(instr llvm.Value) bool { return true } +// ----------------------------------------------------------------------------- + +const ( + deferKey = "__llgo_defer" +) + +type aDefer struct { + nextBit int // next defer bit + key Expr // pthread TLS key + data Expr // pointer to runtime.Defer + bitsPtr Expr // pointer to defer bits + rundPtr Expr // pointer to RunDefers index + procBlk BasicBlock // deferProc block + stmts []func(bits Expr) + runsNext []BasicBlock // next blocks of RunDefers +} + +/* +// func(uintptr) +func (p Program) tyDeferFunc() *types.Signature { + if p.deferFnTy == nil { + paramUintptr := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type) + params := types.NewTuple(paramUintptr) + p.deferFnTy = types.NewSignatureType(nil, nil, nil, params, nil, false) + } + return p.deferFnTy +} +*/ + +func (p Package) deferInit() { + keyVar := p.VarOf(deferKey) + if keyVar == nil { + return + } + prog := p.Prog + keyNil := prog.Null(prog.DeferPtrPtr()) + keyVar.Init(keyNil) + keyVar.impl.SetLinkage(llvm.LinkOnceAnyLinkage) + + b := p.afterBuilder() + eq := b.BinOp(token.EQL, b.Load(keyVar.Expr), keyNil) + b.IfThen(eq, func() { + b.pthreadKeyCreate(keyVar.Expr, prog.Null(prog.VoidPtr())) + }) +} + +func (p Package) newDeferKey() Global { + return p.NewVarEx(deferKey, p.Prog.DeferPtrPtr()) +} + +func (b Builder) deferKey() Expr { + return b.Load(b.Pkg.newDeferKey().Expr) +} + +func (b Builder) getDefer() *aDefer { + self := b.Func + if self.defer_ == nil { + // TODO(xsw): move to funtion start + // 0: proc func(uintptr) + // 1: bits uintptr + // 2: link *Defer + // 3: rund int + prog := b.Prog + key := b.deferKey() + deferfn := prog.Null(prog.VoidPtr()) + zero := prog.Val(uintptr(0)) + link := b.pthreadGetspecific(key) + ptr := b.aggregateAlloca(prog.Defer(), deferfn.impl, zero.impl, link.impl) + deferData := Expr{ptr, prog.DeferPtr()} + b.pthreadSetspecific(key, deferData) + self.defer_ = &aDefer{ + key: key, + data: deferData, + bitsPtr: b.FieldAddr(deferData, 1), + rundPtr: b.FieldAddr(deferData, 3), + procBlk: self.MakeBlock(), + } + } + return self.defer_ +} + +// Defer emits a defer instruction. +func (b Builder) Defer(fn Expr, args ...Expr) { + if debugInstr { + logCall("Defer", fn, args) + } + prog := b.Prog + self := b.getDefer() + next := self.nextBit + self.nextBit++ + bits := b.Load(self.bitsPtr) + nextbit := prog.Val(uintptr(1 << next)) + b.Store(self.bitsPtr, b.BinOp(token.OR, bits, nextbit)) + + self.stmts = append(self.stmts, func(bits Expr) { + zero := prog.Val(uintptr(0)) + has := b.BinOp(token.NEQ, b.BinOp(token.AND, bits, nextbit), zero) + b.IfThen(has, func() { + b.Call(fn, args...) + }) + }) +} + +// RunDefers emits instructions to run deferred instructions. +func (b Builder) RunDefers() { + prog := b.Prog + self := b.getDefer() + b.Store(self.rundPtr, prog.Val(len(self.runsNext))) + b.Jump(self.procBlk) + + blk := b.Func.MakeBlock() + self.runsNext = append(self.runsNext, blk) + + b.SetBlockEx(blk, AtEnd, false) + b.blk.last = blk.last +} + +func (p Function) endDefer(b Builder) { + self := p.defer_ + if self == nil { + return + } + nexts := self.runsNext + n := len(nexts) + if n == 0 { + return + } + b.SetBlockEx(self.procBlk, AtEnd, true) + bits := b.Load(self.bitsPtr) + stmts := self.stmts + for i := len(stmts) - 1; i >= 0; i-- { + stmts[i](bits) + } + + link := b.getField(b.Load(self.data), 2) + b.pthreadSetspecific(self.key, link) + + prog := b.Prog + rund := b.Load(self.rundPtr) + sw := b.impl.CreateSwitch(rund.impl, nexts[0].first, n-1) + for i := 1; i < n; i++ { + sw.AddCase(prog.Val(i).impl, nexts[i].first) + } +} + +// ----------------------------------------------------------------------------- + +/* +// 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 { @@ -206,6 +370,17 @@ func (b Builder) If(cond Expr, thenb, elseb BasicBlock) { b.impl.CreateCondBr(cond.impl, thenb.first, elseb.first) } +// IfThen emits an if-then instruction. +func (b Builder) IfThen(cond Expr, then func()) { + blks := b.Func.MakeBlocks(2) + b.If(cond, blks[0], blks[1]) + b.SetBlockEx(blks[0], AtEnd, false) + then() + b.Jump(blks[1]) + b.SetBlockEx(blks[1], AtEnd, false) + b.blk.last = blks[1].last +} + // ----------------------------------------------------------------------------- // Phi represents a phi node.