diff --git a/cl/compile.go b/cl/compile.go index 4239cd76..60c2ceba 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -201,7 +201,7 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { } g := pkg.NewVar(name, typ, llssa.Background(vtype)) if vtype == goVar { - g.Init(p.prog.Null(g.Type)) + g.Init(p.prog.Nil(g.Type)) } } @@ -338,6 +338,8 @@ func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyObj ftype = llgoSigsetjmp case "siglongjmp": ftype = llgoSiglongjmp + case "deferData": + ftype = llgoDeferData case "unreachable": ftype = llgoUnreachable default: @@ -378,8 +380,8 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do fn := p.fn argc := pkg.NewVar("__llgo_argc", types.NewPointer(types.Typ[types.Int32]), llssa.InC) argv := pkg.NewVar("__llgo_argv", types.NewPointer(argvTy), llssa.InC) - argc.Init(prog.Null(argc.Type)) - argv.Init(prog.Null(argv.Type)) + argc.Init(prog.Nil(argc.Type)) + argv.Init(prog.Nil(argv.Type)) b.Store(argc.Expr, fn.Param(0)) b.Store(argv.Expr, fn.Param(1)) callRuntimeInit(b, pkg) @@ -395,7 +397,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do modName := pysymPrefix + modPath modPtr := pkg.PyNewModVar(modName, true).Expr mod := b.Load(modPtr) - cond := b.BinOp(token.NEQ, mod, prog.Null(mod.Type)) + cond := b.BinOp(token.NEQ, mod, prog.Nil(mod.Type)) newBlk := p.fn.MakeBlock() b.If(cond, jumpTo, newBlk) b.SetBlockEx(newBlk, llssa.AtEnd, false) @@ -431,8 +433,11 @@ func intVal(v ssa.Value) int64 { } 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 + switch vx := vx.(type) { + case *ssa.Alloc: + ret, ok = p.vargs[vx] // varargs: this is a varargs index + case *ssa.Const: + ok = vx.Value == nil } return } @@ -642,6 +647,8 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon p.siglongjmp(b, args) case llgoSigjmpbuf: // func sigjmpbuf() ret = b.AllocaSigjmpBuf() + case llgoDeferData: // func deferData() *Defer + ret = b.DeferData() case llgoUnreachable: // func unreachable() b.Unreachable() default: diff --git a/cl/import.go b/cl/import.go index 7cd23813..473226c0 100644 --- a/cl/import.go +++ b/cl/import.go @@ -312,6 +312,7 @@ const ( llgoSigjmpbuf = llgoInstrBase + 10 llgoSigsetjmp = llgoInstrBase + 11 llgoSiglongjmp = llgoInstrBase + 12 + llgoDeferData = llgoInstrBase + 13 ) func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, string, int) { diff --git a/internal/runtime/c/c.go b/internal/runtime/c/c.go index 63ace390..4183c832 100644 --- a/internal/runtime/c/c.go +++ b/internal/runtime/c/c.go @@ -42,15 +42,30 @@ func Alloca(size uintptr) Pointer //go:linkname AllocaCStr llgo.allocaCStr func AllocaCStr(s string) *Char +//go:linkname GoDeferData llgo.deferData +func GoDeferData() Pointer + //go:linkname Unreachable llgo.unreachable func Unreachable() +//go:linkname AllocaSigjmpBuf llgo.sigjmpbuf +func AllocaSigjmpBuf() Pointer + +//go:linkname Sigsetjmp llgo.sigsetjmp +func Sigsetjmp(jb Pointer, savemask Int) Int + +//go:linkname Siglongjmp llgo.siglongjmp +func Siglongjmp(jb Pointer, retval Int) + //go:linkname Rand C.rand func Rand() Int //go:linkname Malloc C.malloc func Malloc(size uintptr) Pointer +//go:linkname Free C.free +func Free(ptr Pointer) + //go:linkname Memcpy C.memcpy func Memcpy(dst, src Pointer, n uintptr) Pointer diff --git a/internal/runtime/z_rt.go b/internal/runtime/z_rt.go index 3b5b29b7..69fa4584 100644 --- a/internal/runtime/z_rt.go +++ b/internal/runtime/z_rt.go @@ -19,6 +19,7 @@ package runtime import ( "unsafe" + "github.com/goplus/llgo/c/pthread" "github.com/goplus/llgo/internal/abi" "github.com/goplus/llgo/internal/runtime/c" ) @@ -27,19 +28,43 @@ import ( // Defer presents defer statements in a function. type Defer struct { - Addr unsafe.Pointer + Addr unsafe.Pointer // sigjmpbuf Bits uintptr Link *Defer Rund int // index of RunDefers } -// ----------------------------------------------------------------------------- +// Panic panics with a value. +func Panic(v Eface) { + ptr := c.Malloc(unsafe.Sizeof(v)) + *(*Eface)(ptr) = v + excepKey.Set(ptr) -// Zeroinit initializes memory to zero. -func Zeroinit(p unsafe.Pointer, size uintptr) unsafe.Pointer { - return c.Memset(p, 0, size) + Rethrow((*Defer)(c.GoDeferData())) } +// Rethrow rethrows a panic. +func Rethrow(link *Defer) { + if link == nil { + ptr := excepKey.Get() + TracePanic(*(*Eface)(ptr)) + c.Free(ptr) + c.Unreachable() + } else { + c.Siglongjmp(link.Addr, 1) + } +} + +var ( + excepKey pthread.Key +) + +func init() { + excepKey.Create(nil) +} + +// ----------------------------------------------------------------------------- + // TracePanic prints panic message. func TracePanic(v Eface) { kind := v._type.Kind() @@ -56,3 +81,10 @@ func stringTracef(fp c.FilePtr, format *c.Char, s String) { } // ----------------------------------------------------------------------------- + +// Zeroinit initializes memory to zero. +func Zeroinit(p unsafe.Pointer, size uintptr) unsafe.Pointer { + return c.Memset(p, 0, size) +} + +// ----------------------------------------------------------------------------- diff --git a/ssa/abitype.go b/ssa/abitype.go index e41fda58..2fa2ec71 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -303,7 +303,7 @@ func (p Package) abiTypeInit(g Global, t types.Type, pub bool) { var eq Expr var blks []BasicBlock if pub { - eq = b.BinOp(token.EQL, b.Load(expr), b.Prog.Null(expr.Type)) + eq = b.BinOp(token.EQL, b.Load(expr), b.Prog.Nil(expr.Type)) blks = b.Func.MakeBlocks(2) b.If(eq, blks[0], blks[1]) b.SetBlockEx(blks[0], AtEnd, false) @@ -343,7 +343,7 @@ func (b Builder) abiType(t types.Type) Expr { if g == nil { prog := b.Prog g = pkg.doNewVar(name, prog.AbiTypePtrPtr()) - g.Init(prog.Null(g.Type)) + g.Init(prog.Nil(g.Type)) if pub { g.impl.SetLinkage(llvm.LinkOnceAnyLinkage) } diff --git a/ssa/eh.go b/ssa/eh.go index 0dac60e8..d883fdf2 100644 --- a/ssa/eh.go +++ b/ssa/eh.go @@ -22,7 +22,6 @@ import "C" import ( "go/token" "go/types" - "log" "unsafe" "github.com/goplus/llvm" @@ -77,6 +76,7 @@ func (b Builder) Siglongjmp(jb, retval Expr) { const ( deferKey = "__llgo_defer" + excepKey = "__llgo_ex" ) func (p Function) deferInitBuilder() (b Builder, next BasicBlock) { @@ -96,29 +96,33 @@ type aDefer struct { stmts []func(bits Expr) } -func (p Package) deferInit() { - keyVar := p.VarOf(deferKey) +func (p Package) keyInit(name string) { + keyVar := p.VarOf(name) if keyVar == nil { return } prog := p.Prog - keyNil := prog.Null(prog.DeferPtrPtr()) + keyNil := prog.Nil(prog.CIntPtr()) keyVar.Init(keyNil) keyVar.impl.SetLinkage(llvm.LinkOnceAnyLinkage) b := p.afterBuilder() - eq := b.BinOp(token.EQL, b.Load(keyVar.Expr), keyNil) + eq := b.BinOp(token.EQL, b.Load(keyVar.Expr), prog.IntVal(0, prog.CInt())) b.IfThen(eq, func() { - b.pthreadKeyCreate(keyVar.Expr, prog.Null(prog.VoidPtr())) + b.pthreadKeyCreate(keyVar.Expr, prog.Nil(prog.VoidPtr())) }) } -func (p Package) newDeferKey() Global { - return p.NewVarEx(deferKey, p.Prog.DeferPtrPtr()) +func (p Package) newKey(name string) Global { + return p.NewVarEx(name, p.Prog.CIntPtr()) } func (b Builder) deferKey() Expr { - return b.Load(b.Pkg.newDeferKey().Expr) + return b.Load(b.Pkg.newKey(deferKey).Expr) +} + +func (b Builder) excepKey() Expr { + return b.Load(b.Pkg.newKey(excepKey).Expr) } func (b Builder) getDefer(kind DoAction) *aDefer { @@ -148,7 +152,7 @@ func (b Builder) getDefer(kind DoAction) *aDefer { deferData := Expr{ptr, prog.DeferPtr()} b.pthreadSetspecific(key, deferData) blks := self.MakeBlocks(2) - procBlk, throwBlk := blks[0], blks[1] + procBlk, rethrowBlk := blks[0], blks[1] bitsPtr := b.FieldAddr(deferData, deferBits) rundPtr := b.FieldAddr(deferData, deferRund) self.defer_ = &aDefer{ @@ -157,7 +161,7 @@ func (b Builder) getDefer(kind DoAction) *aDefer { bitsPtr: bitsPtr, rundPtr: rundPtr, procBlk: procBlk, - runsNext: []BasicBlock{throwBlk}, + runsNext: []BasicBlock{rethrowBlk}, } czero := prog.IntVal(0, prog.CInt()) retval := b.Sigsetjmp(jb, czero) @@ -168,12 +172,14 @@ func (b Builder) getDefer(kind DoAction) *aDefer { next, rundBlk = blks[0], blks[1] } b.If(b.BinOp(token.EQL, retval, czero), next, rundBlk) - b.SetBlockEx(rundBlk, AtEnd, false) // exec runDefers and throw + b.SetBlockEx(rundBlk, AtEnd, false) // exec runDefers and rethrow b.Store(rundPtr, prog.Val(0)) b.Jump(procBlk) - b.SetBlockEx(throwBlk, AtEnd, false) // throw - linkJBPtr := b.FieldAddr(link, deferSigjmpbuf) - b.Siglongjmp(b.Load(linkJBPtr), prog.IntVal(1, prog.CInt())) + + b.SetBlockEx(rethrowBlk, AtEnd, false) // rethrow + b.Call(b.Pkg.rtFunc("Rethrow"), b.Load(link)) + b.Unreachable() // TODO: func supports noreturn attribute + if kind == DeferAlways { b.SetBlockEx(next, AtEnd, false) b.blk.last = next.last @@ -182,6 +188,12 @@ func (b Builder) getDefer(kind DoAction) *aDefer { return self.defer_ } +// DeferData returns the defer data (*runtime.Defer). +func (b Builder) DeferData() Expr { + key := b.deferKey() + return Expr{b.pthreadGetspecific(key).impl, b.Prog.DeferPtr()} +} + // Defer emits a defer instruction. func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) { if debugInstr { @@ -249,13 +261,16 @@ func (p Function) endDefer(b Builder) { link := b.getField(b.Load(self.data), 2) b.pthreadSetspecific(self.key, link) - - // rund := b.Load(self.rundPtr) b.IndirectJump(self.rundPtr, nexts) } // ----------------------------------------------------------------------------- +// Unreachable emits an unreachable instruction. +func (b Builder) Unreachable() { + b.impl.CreateUnreachable() +} + /* // Recover emits a recover instruction. func (b Builder) Recover() (v Expr) { @@ -263,22 +278,34 @@ func (b Builder) Recover() (v Expr) { log.Println("Recover") } prog := b.Prog - return prog.Zero(prog.Eface()) + return prog.Zero(prog.Any()) } */ // Panic emits a panic instruction. func (b Builder) Panic(v Expr) { + b.Call(b.Pkg.rtFunc("Panic"), v) + b.Unreachable() // TODO: func supports noreturn attribute +} + +/* +// Panic emits a panic instruction. +func (b Builder) Panic(v Expr) { + vimpl := v.impl if debugInstr { - log.Printf("Panic %v\n", v.impl) + log.Printf("Panic %v\n", vimpl) } + if v.kind != vkEface { + panic("Panic only accepts an any expression") + } + ptr := b.dupMalloc(v) + b.pthreadSetspecific(b.excepKey(), ptr) +} + +func (b Builder) doPanic(v Expr) { b.Call(b.Pkg.rtFunc("TracePanic"), v) b.impl.CreateUnreachable() } - -// Unreachable emits an unreachable instruction. -func (b Builder) Unreachable() { - b.impl.CreateUnreachable() -} +*/ // ----------------------------------------------------------------------------- diff --git a/ssa/expr.go b/ssa/expr.go index 960f1cde..ac7733a6 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -83,6 +83,7 @@ func pyVarExpr(mod Expr, name string) Expr { // ----------------------------------------------------------------------------- +// Zero returns a zero constant expression. func (p Program) Zero(t Type) Expr { var ret llvm.Value switch u := t.raw.Type.Underlying().(type) { @@ -127,16 +128,11 @@ func (p Program) Zero(t Type) Expr { return Expr{ret, t} } -// Null returns a null constant expression. -func (p Program) Null(t Type) Expr { +// Nil returns a null constant expression. t should be a pointer type. +func (p Program) Nil(t Type) Expr { return Expr{llvm.ConstNull(t.ll), t} } -// PyNull returns a null *PyObject constant expression. -func (p Program) PyNull() Expr { - return p.Null(p.PyObjectPtr()) -} - // BoolVal returns a boolean constant expression. func (p Program) BoolVal(v bool) Expr { t := p.Bool() @@ -180,7 +176,7 @@ func (p Program) Val(v interface{}) Expr { func (b Builder) Const(v constant.Value, typ Type) Expr { prog := b.Prog if v == nil { - return prog.Null(typ) + return prog.Nil(typ) } raw := typ.raw.Type switch t := raw.Underlying().(type) { diff --git a/ssa/goroutine.go b/ssa/goroutine.go index 389ae8df..d2bf3787 100644 --- a/ssa/goroutine.go +++ b/ssa/goroutine.go @@ -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.pthreadCreate(pthd, prog.Null(voidPtr), pkg.routine(t, len(args)), data) + b.pthreadCreate(pthd, prog.Nil(voidPtr), pkg.routine(t, len(args)), data) } func (p Package) routineName() string { @@ -107,7 +107,7 @@ func (p Package) routine(t Type, n int) Expr { } b.Call(fn, args...) b.free(param) - b.Return(prog.Null(prog.VoidPtr())) + b.Return(prog.Nil(prog.VoidPtr())) return routine.Expr } diff --git a/ssa/memory.go b/ssa/memory.go index 94cfc7f3..d63df457 100644 --- a/ssa/memory.go +++ b/ssa/memory.go @@ -107,6 +107,17 @@ func aggregateInit(b llvm.Builder, ptr llvm.Value, tll llvm.Type, flds ...llvm.V } } +/* +func (b Builder) dupMalloc(v Expr) Expr { + prog := b.Prog + n := prog.SizeOf(v.Type) + tptr := prog.Pointer(v.Type) + ptr := b.malloc(prog.Val(uintptr(n))).impl + b.Store(Expr{ptr, tptr}, v) + return Expr{ptr, tptr} +} +*/ + // ----------------------------------------------------------------------------- // The Alloc instruction reserves space for a variable of the given type, diff --git a/ssa/package.go b/ssa/package.go index 24171164..2d0f3e09 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -131,14 +131,16 @@ type aProgram struct { rtSliceTy llvm.Type rtMapTy llvm.Type - anyTy Type - voidTy Type - voidPtr Type - voidPPtr Type - boolTy Type - cstrTy Type - cintTy Type - //cintPtr Type + anyTy Type + //anyPtr Type + //anyPPtr Type + voidTy Type + voidPtr Type + voidPPtr Type + boolTy Type + cstrTy Type + cintTy Type + cintPtr Type stringTy Type uintptrTy Type intTy Type @@ -158,7 +160,6 @@ type aProgram struct { abiTyPPtr Type deferTy Type deferPtr Type - deferPPtr Type pyImpTy *types.Signature pyNewList *types.Signature @@ -328,14 +329,6 @@ func (p Program) DeferPtr() Type { 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 { @@ -360,6 +353,7 @@ func (p Program) Void() Type { return p.voidTy } +// VoidPtr returns *void type. func (p Program) VoidPtr() Type { if p.voidPtr == nil { p.voidPtr = p.rawType(types.Typ[types.UnsafePointer]) @@ -367,6 +361,7 @@ func (p Program) VoidPtr() Type { return p.voidPtr } +// VoidPtrPtr returns **void type. func (p Program) VoidPtrPtr() Type { if p.voidPPtr == nil { p.voidPPtr = p.rawType(types.NewPointer(types.Typ[types.UnsafePointer])) @@ -382,6 +377,7 @@ func (p Program) Bool() Type { return p.boolTy } +// CStr returns *int8 type. func (p Program) CStr() Type { if p.cstrTy == nil { // *int8 p.cstrTy = p.rawType(types.NewPointer(types.Typ[types.Int8])) @@ -389,6 +385,7 @@ func (p Program) CStr() Type { return p.cstrTy } +// String returns string type. func (p Program) String() Type { if p.stringTy == nil { p.stringTy = p.rawType(types.Typ[types.String]) @@ -396,6 +393,24 @@ func (p Program) String() Type { return p.stringTy } +/* +// AnyPtrPtr returns **any type. +func (p Program) AnyPtrPtr() Type { + if p.anyPPtr == nil { + p.anyPPtr = p.Pointer(p.AnyPtr()) + } + return p.anyPPtr +} + +// AnyPtr returns *any type. +func (p Program) AnyPtr() Type { + if p.anyPtr == nil { + p.anyPtr = p.Pointer(p.Any()) + } + return p.anyPtr +} +*/ + // Any returns the any (empty interface) type. func (p Program) Any() Type { if p.anyTy == nil { @@ -410,6 +425,7 @@ func (p Program) Any() Type { func (p Program) Eface() Type { return p.Any() } +*/ // CIntPtr returns *c.Int type. func (p Program) CIntPtr() Type { @@ -418,7 +434,6 @@ func (p Program) CIntPtr() Type { } return p.cintPtr } -*/ // CInt returns c.Int type. func (p Program) CInt() Type { @@ -550,7 +565,7 @@ func (p Package) cFunc(fullName string, sig *types.Signature) Expr { func (p Package) closureStub(b Builder, t *types.Struct, v Expr) Expr { name := v.impl.Name() prog := b.Prog - nilVal := prog.Null(prog.VoidPtr()).impl + nilVal := prog.Nil(prog.VoidPtr()).impl if fn, ok := p.stubs[name]; ok { v = fn.Expr } else { @@ -605,7 +620,8 @@ func (p Package) afterBuilder() Builder { // AfterInit is called after the package is initialized (init all packages that depends on). func (p Package) AfterInit(b Builder, ret BasicBlock) { - p.deferInit() + p.keyInit(deferKey) + p.keyInit(excepKey) doAfterb := p.afterb != nil doPyLoadModSyms := p.pyHasModSyms() if doAfterb || doPyLoadModSyms { diff --git a/ssa/python.go b/ssa/python.go index 2ff344a4..b9520bee 100644 --- a/ssa/python.go +++ b/ssa/python.go @@ -217,7 +217,7 @@ func (p Package) PyNewModVar(name string, doInit bool) Global { objPtr := prog.PyObjectPtrPtr().raw.Type g := p.NewVar(name, objPtr, InC) if doInit { - g.Init(prog.Null(g.Type)) + g.Init(prog.Nil(g.Type)) g.impl.SetLinkage(llvm.LinkOnceAnyLinkage) } p.pymods[name] = g @@ -246,7 +246,7 @@ func (b Builder) PyLoadModSyms(modName string, objs ...PyObjRef) Expr { args = append(args, o.Expr) } prog := b.Prog - args = append(args, prog.Null(prog.CStr())) + args = append(args, prog.Nil(prog.CStr())) return b.Call(fnLoad, args...) } @@ -273,7 +273,7 @@ func (b Builder) pyCall(fn Expr, args []Expr) (ret Expr) { callargs := make([]Expr, n+2) callargs[0] = fn copy(callargs[1:], args) - callargs[n+1] = prog.PyNull() + callargs[n+1] = prog.Nil(prog.PyObjectPtr()) ret = b.Call(call, callargs...) } return @@ -372,7 +372,7 @@ func (p Package) PyNewFunc(name string, sig *types.Signature, doInit bool) PyObj obj := p.NewVar(name, prog.PyObjectPtrPtr().RawType(), InC) if doInit { prog.NeedPyInit = true - obj.Init(prog.Null(obj.Type)) + obj.Init(prog.Nil(obj.Type)) obj.impl.SetLinkage(llvm.LinkOnceAnyLinkage) } ty := &aType{obj.ll, rawType{types.NewPointer(sig)}, vkPyFuncRef}