Merge pull request #261 from xushiwei/q

defer support; llgo/ssa: IfThen/RunDefers/EndBuild
This commit is contained in:
xushiwei
2024-06-03 01:35:33 +08:00
committed by GitHub
25 changed files with 682 additions and 109 deletions

View File

@@ -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

View File

@@ -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

18
cl/_testgo/defer/in.go Normal file
View File

@@ -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")
}

199
cl/_testgo/defer/out.ll Normal file
View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

Binary file not shown.

Binary file not shown.

View File

@@ -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)
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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)
}
// -----------------------------------------------------------------------------

View File

@@ -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)

View File

@@ -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, ...)

View File

@@ -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)

View File

@@ -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.