better write barrier, now works as the documenation says it needs to work

This commit is contained in:
araq
2026-02-09 12:31:49 +01:00
parent cd41586b99
commit 6aad72b257
2 changed files with 52 additions and 1 deletions

View File

@@ -730,11 +730,33 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
dest[] = source
decRef tmp
For YRC the write barrier is more complicated still and must be:
let tmp = dest
# assignment must come first so that the collector sees the most-recent graph:
atomic: dest[] = source
# Then teach the cycle collector about the changes edge (these use locks, see yrc.nim):
incRef source
decRef tmp
This is implemented as a single runtime call (nimAsgnYrc / nimSinkYrc).
]#
var actions = newNodeI(nkStmtList, c.info)
let elemType = t.elementType
createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
# YRC uses dedicated runtime procs for the entire write barrier:
if c.g.config.selectedGC == gcYrc:
case c.kind
of attachedAsgn, attachedDup:
body.add callCodegenProc(c.g, "nimAsgnYrc", c.info, genAddr(c, x), y)
return
of attachedSink:
body.add callCodegenProc(c.g, "nimSinkYrc", c.info, genAddr(c, x), y)
return
else: discard # fall through for destructor, trace, wasMoved
let isCyclic = c.g.config.selectedGC in {gcOrc, gcYrc} and types.canFormAcycle(c.g, elemType)
let isInheritableAcyclicRef = c.g.config.selectedGC in {gcOrc, gcYrc} and
@@ -819,6 +841,17 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
xenv.typ = getSysType(c.g, c.info, tyPointer)
# YRC uses dedicated runtime procs for the write barrier on the env pointer:
if c.g.config.selectedGC == gcYrc:
case c.kind
of attachedAsgn, attachedDup:
body.add callCodegenProc(c.g, "nimAsgnYrc", c.info, genAddr(c, xenv), y)
return
of attachedSink:
body.add callCodegenProc(c.g, "nimSinkYrc", c.info, genAddr(c, xenv), y)
return
else: discard # fall through for destructor, trace, wasMoved
let isCyclic = c.g.config.selectedGC in {gcOrc, gcYrc}
let tmp =
if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:
@@ -852,7 +885,6 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genIf(c, cond, actions)
else:
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
body.add genIf(c, cond, actions)
body.add newAsgnStmt(x, y)
of attachedDup:

View File

@@ -448,6 +448,25 @@ proc unsureAsgnRef(dest: ptr pointer, src: pointer) {.inline.} =
dest[] = src
if src != nil: nimIncRefCyclic(src, true)
proc nimAsgnYrc(dest: ptr pointer; src: pointer) {.compilerRtl.} =
## YRC write barrier for ref copy assignment.
## Atomically stores src into dest, then buffers RC adjustments.
## Freeing is always done by the cycle collector, never inline.
let tmp = dest[]
atomicStoreN(dest, src, ATOMIC_RELEASE)
if src != nil:
nimIncRefCyclic(src, true)
if tmp != nil:
discard nimDecRefIsLastCyclicDyn(tmp)
proc nimSinkYrc(dest: ptr pointer; src: pointer) {.compilerRtl.} =
## YRC write barrier for ref sink (move). No incRef on source.
## Freeing is always done by the cycle collector, never inline.
let tmp = dest[]
atomicStoreN(dest, src, ATOMIC_RELEASE)
if tmp != nil:
discard nimDecRefIsLastCyclicDyn(tmp)
proc nimMarkCyclic(p: pointer) {.compilerRtl, inl.} =
when optimizedOrc:
if p != nil: