defer support; llgo/ssa: IfThen

This commit is contained in:
xushiwei
2024-06-02 14:29:35 +08:00
parent e151bd4cd1
commit 5a5929048d
11 changed files with 328 additions and 37 deletions

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

@@ -0,0 +1,25 @@
package main
func f(s string) bool {
return len(s) > 5
}
func fail() {
panic("error")
}
func main() {
defer func() {
println("hi")
if e := recover(); e != nil {
println(e.(string))
}
}()
if s := "hello"; f(s) {
defer println(s)
} else {
defer println("world")
fail()
}
defer println("bye")
}

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

View File

@@ -796,6 +796,8 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
key := p.compileValue(b, v.Key) key := p.compileValue(b, v.Key)
val := p.compileValue(b, v.Value) val := p.compileValue(b, v.Value)
b.MapUpdate(m, key, val) b.MapUpdate(m, key, val)
case *ssa.Defer:
p.call(b, llssa.Defer, &v.Call)
case *ssa.Go: case *ssa.Go:
p.call(b, llssa.Go, &v.Call) p.call(b, llssa.Go, &v.Call)
case *ssa.Panic: case *ssa.Panic:

View File

@@ -35,7 +35,7 @@ func AllocZ(size uintptr) unsafe.Pointer {
} }
// Zeroinit initializes memory to zero. // 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) return c.Memset(p, 0, size)
} }

View File

@@ -0,0 +1,32 @@
/*
* 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
}
// DeferProc calls deferred statements.
func DeferProc(d *Defer) {
for d != nil {
d.proc(d.bits)
d = d.link
}
}

View File

@@ -85,15 +85,13 @@ func (p Package) NewVar(name string, typ types.Type, bg Background) Global {
return p.doNewVar(name, t) return p.doNewVar(name, t)
} }
/* // NewVarEx creates a new global variable.
// NewVarFrom creates a new global variable. func (p Package) NewVarEx(name string, t Type) Global {
func (p Package) NewVarFrom(name string, t Type) Global {
if v, ok := p.vars[name]; ok { if v, ok := p.vars[name]; ok {
return v return v
} }
return p.doNewVar(name, t) return p.doNewVar(name, t)
} }
*/
func (p Package) doNewVar(name string, t Type) Global { func (p Package) doNewVar(name string, t Type) Global {
var gbl llvm.Value var gbl llvm.Value
@@ -185,6 +183,10 @@ type aFunction struct {
params []Type params []Type
freeVars Expr freeVars Expr
base int // base = 1 if hasFreeVars; base = 0 otherwise base int // base = 1 if hasFreeVars; base = 0 otherwise
deferNextBit int // next defer bit
deferData Expr
hasVArg bool hasVArg bool
} }
@@ -222,7 +224,14 @@ func newFunction(fn llvm.Value, t Type, pkg Package, prog Program, hasFreeVars b
if hasFreeVars { if hasFreeVars {
base = 1 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) { func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
@@ -240,12 +249,15 @@ func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
return return
} }
/*
// Name returns the function's name. // Name returns the function's name.
func (p Function) Name() string { func (p Function) Name() string {
return p.impl.Name() 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. // Params returns the function's ith parameter.
func (p Function) Param(i int) Expr { func (p Function) Param(i int) Expr {

View File

@@ -493,7 +493,6 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
typ := t.raw.Type typ := t.raw.Type
switch typ.(type) { switch typ.(type) {
default: default:
// TODO(xsw): remove instr name
ret.impl = llvm.CreateBitCast(b.impl, x.impl, t.ll) ret.impl = llvm.CreateBitCast(b.impl, x.impl, t.ll)
ret.Type = b.Prog.rawType(typ) ret.Type = b.Prog.rawType(typ)
return return
@@ -748,7 +747,6 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
var ll llvm.Type var ll llvm.Type
var data Expr var data Expr
var sig *types.Signature var sig *types.Signature
var prog = b.Prog
var raw = fn.raw.Type var raw = fn.raw.Type
switch kind { switch kind {
case vkClosure: case vkClosure:
@@ -758,7 +756,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
fallthrough fallthrough
case vkFuncPtr: case vkFuncPtr:
sig = raw.(*types.Signature) sig = raw.(*types.Signature)
ll = prog.FuncDecl(sig, InC).ll ll = b.Prog.FuncDecl(sig, InC).ll
case vkFuncDecl: case vkFuncDecl:
sig = raw.(*types.Signature) sig = raw.(*types.Signature)
ll = fn.ll ll = fn.ll
@@ -768,7 +766,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
default: default:
log.Panicf("unreachable: %d(%T)\n", kind, raw) 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)) ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b))
return return
} }
@@ -795,8 +793,8 @@ type DoAction int
const ( const (
Call DoAction = iota Call DoAction = iota
Go
Defer Defer
Go
) )
// Do call a function with an action. // Do call a function with an action.
@@ -804,6 +802,8 @@ func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) {
switch da { switch da {
case Call: case Call:
return b.Call(fn, args...) return b.Call(fn, args...)
case Defer:
b.Defer(fn, args...)
case Go: case Go:
b.Go(fn, args...) b.Go(fn, args...)
} }

View File

@@ -50,7 +50,7 @@ func (p Program) tyPthreadCreate() *types.Signature {
return p.createThdTy 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()) fn := b.Pkg.cFunc("pthread_create", b.Prog.tyPthreadCreate())
return b.Call(fn, pp, attr, routine, arg) 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} data := Expr{b.aggregateMalloc(t, flds...), voidPtr}
size := prog.SizeOf(voidPtr) size := prog.SizeOf(voidPtr)
pthd := b.Alloca(prog.IntVal(uint64(size), prog.Uintptr())) 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 { 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 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 { func (b Builder) aggregateMalloc(t Type, flds ...llvm.Value) llvm.Value {
prog := b.Prog prog := b.Prog
size := prog.SizeOf(t) size := prog.SizeOf(t)

View File

@@ -137,6 +137,7 @@ type aProgram struct {
boolTy Type boolTy Type
cstrTy Type cstrTy Type
cintTy Type cintTy Type
cintPtr Type
stringTy Type stringTy Type
uintptrTy Type uintptrTy Type
intTy Type intTy Type
@@ -148,15 +149,19 @@ type aProgram struct {
u32Ty Type u32Ty Type
i64Ty Type i64Ty Type
u64Ty Type u64Ty Type
pyObjPtr Type pyObjPtr Type
pyObjPPtr Type pyObjPPtr Type
abiTyptr Type
abiTypptr Type abiTyPtr Type
abiTyPPtr Type
deferTy Type
deferPtr Type
pyImpTy *types.Signature pyImpTy *types.Signature
pyNewList *types.Signature pyNewList *types.Signature
pyListSetI *types.Signature pyListSetI *types.Signature
callArgs *types.Signature floatFromDbl *types.Signature
callNoArgs *types.Signature callNoArgs *types.Signature
callOneArg *types.Signature callOneArg *types.Signature
callFOArgs *types.Signature callFOArgs *types.Signature
@@ -166,8 +171,13 @@ type aProgram struct {
mallocTy *types.Signature mallocTy *types.Signature
freeTy *types.Signature freeTy *types.Signature
createKeyTy *types.Signature
createThdTy *types.Signature createThdTy *types.Signature
getSpecTy *types.Signature
setSpecTy *types.Signature
routineTy *types.Signature routineTy *types.Signature
destructTy *types.Signature
deferFnTy *types.Signature
paramObjPtr_ *types.Var paramObjPtr_ *types.Var
@@ -333,20 +343,36 @@ func (p Program) Eface() Type {
} }
*/ */
// DeferPtr returns *runtime.Defer.
func (p Program) DeferPtr() Type {
if p.deferPtr == nil {
p.deferPtr = p.Pointer(p.Defer())
}
return p.deferPtr
}
// Defer returns runtime.Defer type.
func (p Program) Defer() Type {
if p.deferTy == nil {
p.deferTy = p.rtType("Defer")
}
return p.deferTy
}
// AbiTypePtr returns *abi.Type. // AbiTypePtr returns *abi.Type.
func (p Program) AbiTypePtr() Type { func (p Program) AbiTypePtr() Type {
if p.abiTyptr == nil { if p.abiTyPtr == nil {
p.abiTyptr = p.rawType(types.NewPointer(p.rtNamed("Type"))) p.abiTyPtr = p.rawType(types.NewPointer(p.rtNamed("Type")))
} }
return p.abiTyptr return p.abiTyPtr
} }
// AbiTypePtrPtr returns **abi.Type. // AbiTypePtrPtr returns **abi.Type.
func (p Program) AbiTypePtrPtr() Type { func (p Program) AbiTypePtrPtr() Type {
if p.abiTypptr == nil { if p.abiTyPPtr == nil {
p.abiTypptr = p.Pointer(p.AbiTypePtr()) p.abiTyPPtr = p.Pointer(p.AbiTypePtr())
} }
return p.abiTypptr return p.abiTyPPtr
} }
// PyObjectPtrPtr returns the **py.Object type. // PyObjectPtrPtr returns the **py.Object type.
@@ -418,6 +444,15 @@ func (p Program) Any() Type {
return p.anyTy return p.anyTy
} }
// 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 { func (p Program) CInt() Type {
if p.cintTy == nil { // C.int if p.cintTy == nil { // C.int
p.cintTy = p.rawType(types.Typ[types.Int32]) // TODO(xsw): support 64-bit p.cintTy = p.rawType(types.Typ[types.Int32]) // TODO(xsw): support 64-bit
@@ -528,6 +563,8 @@ type aPackage struct {
Prog Program Prog Program
iRoutine int iRoutine int
deferMgr
} }
type Package = *aPackage type Package = *aPackage
@@ -595,10 +632,14 @@ func (p Package) String() string {
// AfterInit is called after the package is initialized (init all packages that depends on). // AfterInit is called after the package is initialized (init all packages that depends on).
func (p Package) AfterInit(b Builder, ret BasicBlock) { func (p Package) AfterInit(b Builder, ret BasicBlock) {
doDeferInit := p.hasDefer()
doAbiInit := p.hasAbiInit() doAbiInit := p.hasAbiInit()
doPyLoadModSyms := p.pyHasModSyms() doPyLoadModSyms := p.pyHasModSyms()
if doAbiInit || doPyLoadModSyms { if doDeferInit || doAbiInit || doPyLoadModSyms {
b.SetBlockEx(ret, afterInit, false) b.SetBlockEx(ret, afterInit, false)
if doDeferInit {
p.deferInit(b)
}
if doAbiInit { if doAbiInit {
p.abiInit(b) p.abiInit(b)
} }
@@ -739,14 +780,14 @@ func (p Program) tyNewList() *types.Signature {
// func(float64) *Object // func(float64) *Object
func (p Program) tyFloatFromDouble() *types.Signature { func (p Program) tyFloatFromDouble() *types.Signature {
if p.callArgs == nil { if p.floatFromDbl == nil {
paramObjPtr := p.paramObjPtr() paramObjPtr := p.paramObjPtr()
paramFloat := types.NewParam(token.NoPos, nil, "", p.Float64().raw.Type) paramFloat := types.NewParam(token.NoPos, nil, "", p.Float64().raw.Type)
params := types.NewTuple(paramFloat) params := types.NewTuple(paramFloat)
results := types.NewTuple(paramObjPtr) 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, ...) // func(*Object, ...)

View File

@@ -19,9 +19,11 @@ package ssa
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"go/token"
"go/types" "go/types"
"log" "log"
"strings" "strings"
"unsafe"
"github.com/goplus/llvm" "github.com/goplus/llvm"
) )
@@ -128,6 +130,96 @@ func notInit(instr llvm.Value) bool {
return true return true
} }
// -----------------------------------------------------------------------------
const (
deferKey = "__llgo_defer"
)
type deferMgr struct {
deferb unsafe.Pointer
deferparam Expr
}
// 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) hasDefer() bool {
return p.deferb != nil
}
func (p Package) deferInit(b Builder) {
prog := p.Prog
keyVar := p.newDeferKey()
keyNil := prog.Null(prog.CIntPtr())
keyVar.Init(keyNil)
keyVar.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
eq := b.BinOp(token.EQL, b.Load(keyVar.Expr), keyNil)
b.IfThen(eq, func() {
b.pthreadKeyCreate(keyVar.Expr, prog.Null(prog.VoidPtr()))
})
b = Builder(p.deferb)
b.Return()
}
func (p Package) newDeferKey() Global {
return p.NewVarEx(deferKey, p.Prog.CIntPtr())
}
func (b Builder) deferKey() Expr {
return b.Load(b.Pkg.newDeferKey().Expr)
}
// Defer emits a defer instruction.
func (b Builder) Defer(fn Expr, args ...Expr) {
if debugInstr {
logCall("Defer", fn, args)
}
prog := b.Prog
pkg := b.Pkg
self := b.Func
next := self.deferNextBit
self.deferNextBit++
zero := prog.Val(uintptr(0))
key := b.deferKey()
if next == 0 {
name := self.DeferFuncName()
deferfn := pkg.NewFunc(name, b.Prog.tyDeferFunc(), InC)
deferb := deferfn.MakeBody(1)
pkg.deferb = unsafe.Pointer(deferb)
pkg.deferparam = deferfn.Param(0)
// TODO(xsw): move to funtion start
// proc func(uintptr)
// bits uintptr
// link *Defer
link := b.pthreadGetspecific(key)
ptr := b.aggregateAlloca(prog.Defer(), deferfn.impl, zero.impl, link.impl)
self.deferData = Expr{ptr, prog.DeferPtr()}
b.pthreadSetspecific(key, self.deferData)
}
bitsPtr := b.FieldAddr(self.deferData, 1)
nextbit := prog.Val(uintptr(1 << next))
b.Store(bitsPtr, b.BinOp(token.OR, b.Load(bitsPtr), nextbit))
b = Builder(pkg.deferb)
has := b.BinOp(token.NEQ, b.BinOp(token.AND, pkg.deferparam, nextbit), zero)
b.IfThen(has, func() {
b.Call(fn, args...)
})
}
// -----------------------------------------------------------------------------
// Panic emits a panic instruction. // Panic emits a panic instruction.
func (b Builder) Panic(v Expr) { func (b Builder) Panic(v Expr) {
if debugInstr { if debugInstr {
@@ -206,6 +298,17 @@ func (b Builder) If(cond Expr, thenb, elseb BasicBlock) {
b.impl.CreateCondBr(cond.impl, thenb.first, elseb.first) 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. // Phi represents a phi node.