Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
695 changes: 678 additions & 17 deletions cl/compile.go

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions cl/instr.go
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,12 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
ret = p.emitDo(b, act, ds, llssa.Builtin(fn), llssa.Builder.Call, args...)
case *ssa.Function:
aFn, pyFn, ftype := p.compileFunction(cv)
if p.isRuntimeSetFinalizerCall(call) && len(args) == 2 && act == llssa.Call && ds == nil {
finalizer := p.compileLateValue(b, args[1])
obj := p.compileLateValue(b, args[0])
ret = p.emitDo(b, act, nil, aFn.Expr, llssa.Builder.Call, obj, finalizer)
return
}
// TODO(xsw): check ca != llssa.Call
switch ftype {
case cFunc:
Expand Down
3 changes: 3 additions & 0 deletions runtime/internal/clite/bdwgc/bdwgc.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ func IsDisabled() c.Int
//go:linkname Gcollect C.GC_gcollect
func Gcollect()

//go:linkname ClearStack C.GC_clear_stack
func ClearStack(arg c.Pointer) c.Pointer

//go:linkname GetMemoryUse C.GC_get_memory_use
func GetMemoryUse() uintptr

Expand Down
68 changes: 68 additions & 0 deletions runtime/internal/lib/runtime/_wrap/runtime.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#if defined(__linux__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif

#include <stdint.h>
#include <pthread.h>
#include <unistd.h>

int llgo_maxprocs()
Expand All @@ -8,3 +14,65 @@ int llgo_maxprocs()
return 1;
#endif
}

void llgo_clobber_pointer_regs(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3,
uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7)
{
volatile uintptr_t sink = a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7;
(void)sink;
}

void llgo_clear_stack_ptr(uintptr_t target)
{
if (target == 0) {
return;
}

volatile uintptr_t marker = 0;
uintptr_t *cur = 0;
uintptr_t *end = 0;

#if defined(__APPLE__)
void *stackaddr = pthread_get_stackaddr_np(pthread_self());
size_t stacksize = pthread_get_stacksize_np(pthread_self());
if (stackaddr != 0 && stacksize != 0) {
uintptr_t *mark = (uintptr_t *)&marker;
uintptr_t *lo = (uintptr_t *)((char *)stackaddr - stacksize);
uintptr_t *hi = (uintptr_t *)stackaddr;
if (mark >= lo && mark < hi) {
cur = lo;
end = hi;
} else {
lo = (uintptr_t *)stackaddr;
hi = (uintptr_t *)((char *)stackaddr + stacksize);
if (mark >= lo && mark < hi) {
cur = lo;
end = hi;
}
}
}
#elif defined(__linux__)
pthread_attr_t attr;
void *stackaddr = 0;
size_t stacksize = 0;
if (pthread_getattr_np(pthread_self(), &attr) == 0) {
if (pthread_attr_getstack(&attr, &stackaddr, &stacksize) == 0) {
cur = (uintptr_t *)stackaddr;
end = (uintptr_t *)((char *)stackaddr + stacksize);
}
pthread_attr_destroy(&attr);
}
#endif

if (cur == 0 || end == 0 || end <= cur) {
return;
}
if ((uintptr_t *)target >= cur && (uintptr_t *)target < end) {
return;
}
for (; cur < end; cur++) {
if (*cur == target) {
*cur = 0;
}
}
}
168 changes: 166 additions & 2 deletions runtime/internal/lib/runtime/mfinal.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,171 @@

package runtime

import (
"unsafe"

"github.com/goplus/llgo/runtime/abi"
"github.com/goplus/llgo/runtime/internal/clite/bdwgc"
psync "github.com/goplus/llgo/runtime/internal/clite/pthread/sync"
"github.com/goplus/llgo/runtime/internal/clite/sync/atomic"
)

type finalizerClosure struct {
fn unsafe.Pointer
env unsafe.Pointer
}

type finalizerEntry struct {
fn any
obj unsafe.Pointer
key uintptr
next *finalizerEntry
prevFn bdwgc.FinalizerFunc
prevCb unsafe.Pointer
stop int32
}

var finalizerState struct {
once psync.Once
mu psync.Mutex
m map[uintptr]*finalizerEntry
head *finalizerEntry
tail *finalizerEntry
}

func initFinalizerState() {
finalizerState.mu.Init(nil)
finalizerState.m = make(map[uintptr]*finalizerEntry)
}

func SetFinalizer(obj any, finalizer any) {
// TODO(xsw):
// panic("todo: runtime.SetFinalizer")
objFace := *(*eface)(unsafe.Pointer(&obj))
if objFace._type == nil {
throw("runtime.SetFinalizer: first argument is nil")
}
if objFace._type.Kind() != abi.Pointer {
throw("runtime.SetFinalizer: first argument is " + objFace._type.String() + ", not pointer")
}
objPtr := ifacePointerData(&objFace)
if objPtr == nil {
throw("runtime.SetFinalizer: first argument is nil")
}

finalizerState.once.Do(initFinalizerState)
key := hideFinalizerPtr(objPtr)

finalizerState.mu.Lock()
if old := finalizerState.m[key]; old != nil {
atomic.Store(&old.stop, 1)
delete(finalizerState.m, key)
restoreFinalizer(objPtr, old)
}
finalizerState.mu.Unlock()

finalizerFace := *(*eface)(unsafe.Pointer(&finalizer))
if finalizerFace._type == nil {
return
}
ft := finalizerFuncType(finalizerFace._type)
if ft == nil {
throw("runtime.SetFinalizer: second argument is " + finalizerFace._type.String() + ", not a function")
}
if len(ft.In) != 1 || ft.In[0] != objFace._type {
throw("runtime.SetFinalizer: cannot pass " + objFace._type.String() + " to finalizer " + finalizerFace._type.String())
}
entry := &finalizerEntry{fn: finalizer, key: key}
var oldFn bdwgc.FinalizerFunc
var oldCb unsafe.Pointer
bdwgc.RegisterFinalizer(objPtr, setFinalizerCallback, unsafe.Pointer(entry), &oldFn, &oldCb)
entry.prevFn = oldFn
entry.prevCb = oldCb

finalizerState.mu.Lock()
finalizerState.m[key] = entry
finalizerState.mu.Unlock()
}

func ifacePointerData(e *eface) unsafe.Pointer {
if e._type.IsDirectIface() {
return e.data
}
return *(*unsafe.Pointer)(e.data)
}

func finalizerFuncType(t *abi.Type) *abi.FuncType {
if t.IsClosure() {
st := t.StructType()
if st == nil || len(st.Fields) == 0 {
return nil
}
return st.Fields[0].Typ.FuncType()
}
return t.FuncType()
}

func callFinalizer(fn any, ptr unsafe.Pointer) {
c := (*finalizerClosure)((*eface)(unsafe.Pointer(&fn)).data)
f := *(*func(unsafe.Pointer))(unsafe.Pointer(c))
f(ptr)
}

func setFinalizerCallback(ptr unsafe.Pointer, cb unsafe.Pointer) {
entry := (*finalizerEntry)(cb)
if entry.prevFn != nil {
entry.prevFn(ptr, entry.prevCb)
}
if atomic.Load(&entry.stop) == 1 {
return
}

// Keep the object alive until runFinalizers invokes the Go finalizer.
// Do not allocate or lock here; BDWGC calls this while collecting.
entry.obj = ptr
entry.next = nil
if finalizerState.tail == nil {
finalizerState.head = entry
finalizerState.tail = entry
} else {
finalizerState.tail.next = entry
finalizerState.tail = entry
}
}

func restoreFinalizer(ptr unsafe.Pointer, entry *finalizerEntry) {
var oldFn bdwgc.FinalizerFunc
var oldCb unsafe.Pointer
if entry.prevFn != nil {
bdwgc.RegisterFinalizer(ptr, entry.prevFn, entry.prevCb, &oldFn, &oldCb)
return
}
bdwgc.RegisterFinalizer(ptr, nil, nil, &oldFn, &oldCb)
}

func runFinalizers() {
finalizerState.once.Do(initFinalizerState)
for {
entry := finalizerState.head
if entry == nil {
return
}
finalizerState.head = entry.next
if finalizerState.head == nil {
finalizerState.tail = nil
}
entry.next = nil
finalizerState.mu.Lock()
if finalizerState.m[entry.key] == entry {
delete(finalizerState.m, entry.key)
}
finalizerState.mu.Unlock()

if atomic.Load(&entry.stop) != 1 {
callFinalizer(entry.fn, entry.obj)
}
entry.obj = nil
}
}

func hideFinalizerPtr(ptr unsafe.Pointer) uintptr {
return ^uintptr(ptr)
}
4 changes: 4 additions & 0 deletions runtime/internal/lib/runtime/runtime_gc.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ func ReadMemStats(m *runtime.MemStats) {
}

func GC() {
bdwgc.ClearStack(nil)
bdwgc.Gcollect()
runFinalizers()
// BDW finalizers are observed on a subsequent collection cycle.
// Run one extra cycle so weak-pointer cleanup hooks (unique/weak) see
// finalized state before we trigger map cleanup callbacks.
bdwgc.ClearStack(nil)
bdwgc.Gcollect()
runFinalizers()
unique_runtime_notifyMapCleanup()
if poolCleanup != nil {
poolCleanup()
Expand Down
28 changes: 28 additions & 0 deletions ssa/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,34 @@ func (b Builder) aggregateValue(t Type, flds ...llvm.Value) Expr {
return Expr{aggregateValue(b.impl, t.ll, flds...), t}
}

// LoadAndClearSinglePointer atomically copies a pointer-sized value out of ptr
// and clears the source slot. It handles either *P or *struct{P}.
func (b Builder) LoadAndClearSinglePointer(ptr Expr) (Expr, bool) {
elem := b.Prog.Elem(ptr.Type)
if elem.ll.TypeKind() == llvm.PointerTypeKind {
old := b.loadAndClearPointerWord(ptr.impl, elem.ll)
return Expr{old, elem}, true
}

st, ok := types.Unalias(elem.RawType()).Underlying().(*types.Struct)
if !ok || st.NumFields() != 1 {
return Nil, false
}
field := b.Prog.rawType(st.Field(0).Type())
if field.ll.TypeKind() != llvm.PointerTypeKind {
return Nil, false
}
fieldPtr := llvm.CreateStructGEP(b.impl, elem.ll, ptr.impl, 0)
old := b.loadAndClearPointerWord(fieldPtr, field.ll)
return Expr{old, elem}, true
}

func (b Builder) loadAndClearPointerWord(ptr llvm.Value, typ llvm.Type) llvm.Value {
old := llvm.CreateLoad(b.impl, typ, ptr)
b.impl.CreateStore(llvm.ConstNull(typ), ptr)
return old
}

func aggregateValue(b llvm.Builder, tll llvm.Type, flds ...llvm.Value) llvm.Value {
agg := llvm.Undef(tll)
for i, fld := range flds {
Expand Down
Loading
Loading