parent
92da06e64e
commit
7eb34d170a
2 changed files with 109 additions and 19 deletions
|
|
@ -392,6 +392,16 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||||
v.addVar(result, lowerings.newIntLit(c.g, body.info, first))
|
v.addVar(result, lowerings.newIntLit(c.g, body.info, first))
|
||||||
body.add v
|
body.add v
|
||||||
|
|
||||||
|
proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
|
||||||
|
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId(c.idgen), c.fn, c.info)
|
||||||
|
temp.typ = value.typ
|
||||||
|
incl(temp.flags, sfFromGeneric)
|
||||||
|
|
||||||
|
var v = newNodeI(nkVarSection, c.info)
|
||||||
|
result = newSymNode(temp)
|
||||||
|
v.addVar(result, value)
|
||||||
|
body.add v
|
||||||
|
|
||||||
proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
|
proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
|
||||||
let incCall = genBuiltin(c.g, mInc, "inc", i)
|
let incCall = genBuiltin(c.g, mInc, "inc", i)
|
||||||
incCall.add lowerings.newIntLit(c.g, c.info, 1)
|
incCall.add lowerings.newIntLit(c.g, c.info, 1)
|
||||||
|
|
@ -508,43 +518,75 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
discard "strings are atomic and have no inner elements that are to trace"
|
discard "strings are atomic and have no inner elements that are to trace"
|
||||||
|
|
||||||
proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
|
#[ bug #15753 is really subtle. Usually the classical write barrier for reference
|
||||||
|
counting looks like this::
|
||||||
|
|
||||||
|
incRef source # increment first; this takes care of self-assignments1
|
||||||
|
decRef dest
|
||||||
|
dest[] = source
|
||||||
|
|
||||||
|
However, 'decRef dest' might trigger a cycle collection and then the collector
|
||||||
|
traverses the graph. It is crucial that when it follows the pointers the assignment
|
||||||
|
'dest[] = source' already happened so that we don't do trial deletion on a wrong
|
||||||
|
graph -- this causes premature freeing of objects! The correct barrier looks like
|
||||||
|
this::
|
||||||
|
|
||||||
|
let tmp = dest
|
||||||
|
incRef source
|
||||||
|
dest[] = source
|
||||||
|
decRef tmp
|
||||||
|
|
||||||
|
]#
|
||||||
var actions = newNodeI(nkStmtList, c.info)
|
var actions = newNodeI(nkStmtList, c.info)
|
||||||
let elemType = t.lastSon
|
let elemType = t.lastSon
|
||||||
|
|
||||||
createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
|
createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
|
||||||
|
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType)
|
||||||
|
|
||||||
|
let tmp =
|
||||||
|
if isCyclic and c.kind in {attachedAsgn, attachedSink}:
|
||||||
|
declareTempOf(c, body, x)
|
||||||
|
else:
|
||||||
|
x
|
||||||
|
|
||||||
if isFinal(elemType):
|
if isFinal(elemType):
|
||||||
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
|
addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr))
|
||||||
var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||||
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
||||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf)
|
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf)
|
||||||
else:
|
else:
|
||||||
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
|
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr))
|
||||||
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)
|
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp)
|
||||||
|
|
||||||
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType)
|
|
||||||
|
|
||||||
var cond: PNode
|
var cond: PNode
|
||||||
if isCyclic:
|
if isCyclic:
|
||||||
if isFinal(elemType):
|
if isFinal(elemType):
|
||||||
let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||||
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, x, typInfo)
|
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
|
||||||
else:
|
else:
|
||||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, x)
|
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
|
||||||
else:
|
else:
|
||||||
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
|
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
|
||||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||||
|
|
||||||
case c.kind
|
case c.kind
|
||||||
of attachedSink:
|
of attachedSink:
|
||||||
body.add genIf(c, cond, actions)
|
if isCyclic:
|
||||||
body.add newAsgnStmt(x, y)
|
body.add newAsgnStmt(x, y)
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
else:
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
body.add newAsgnStmt(x, y)
|
||||||
of attachedAsgn:
|
of attachedAsgn:
|
||||||
body.add genIf(c, y, callCodegenProc(c.g,
|
body.add genIf(c, y, callCodegenProc(c.g,
|
||||||
if isCyclic: "nimIncRefCyclic" else: "nimIncRef", c.info, y))
|
if isCyclic: "nimIncRefCyclic" else: "nimIncRef", c.info, y))
|
||||||
body.add genIf(c, cond, actions)
|
if isCyclic:
|
||||||
body.add newAsgnStmt(x, y)
|
body.add newAsgnStmt(x, y)
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
else:
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
body.add newAsgnStmt(x, y)
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
|
@ -571,19 +613,30 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
let xenv = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
let xenv = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
||||||
xenv.typ = getSysType(c.g, c.info, tyPointer)
|
xenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||||
|
|
||||||
|
let isCyclic = c.g.config.selectedGC == gcOrc
|
||||||
|
let tmp =
|
||||||
|
if isCyclic and c.kind in {attachedAsgn, attachedSink}:
|
||||||
|
declareTempOf(c, body, xenv)
|
||||||
|
else:
|
||||||
|
xenv
|
||||||
|
|
||||||
var actions = newNodeI(nkStmtList, c.info)
|
var actions = newNodeI(nkStmtList, c.info)
|
||||||
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xenv)
|
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp)
|
||||||
|
|
||||||
let decRefProc =
|
let decRefProc =
|
||||||
if c.g.config.selectedGC == gcOrc: "nimDecRefIsLastCyclicDyn"
|
if isCyclic: "nimDecRefIsLastCyclicDyn"
|
||||||
else: "nimDecRefIsLast"
|
else: "nimDecRefIsLast"
|
||||||
let cond = callCodegenProc(c.g, decRefProc, c.info, xenv)
|
let cond = callCodegenProc(c.g, decRefProc, c.info, tmp)
|
||||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||||
|
|
||||||
case c.kind
|
case c.kind
|
||||||
of attachedSink:
|
of attachedSink:
|
||||||
body.add genIf(c, cond, actions)
|
if isCyclic:
|
||||||
body.add newAsgnStmt(x, y)
|
body.add newAsgnStmt(x, y)
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
else:
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
body.add newAsgnStmt(x, y)
|
||||||
of attachedAsgn:
|
of attachedAsgn:
|
||||||
let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
|
let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
|
||||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||||
|
|
@ -591,8 +644,12 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
if c.g.config.selectedGC == gcOrc: "nimIncRefCyclic"
|
if c.g.config.selectedGC == gcOrc: "nimIncRefCyclic"
|
||||||
else: "nimIncRef"
|
else: "nimIncRef"
|
||||||
body.add genIf(c, yenv, callCodegenProc(c.g, incRefProc, c.info, yenv))
|
body.add genIf(c, yenv, callCodegenProc(c.g, incRefProc, c.info, yenv))
|
||||||
body.add genIf(c, cond, actions)
|
if isCyclic:
|
||||||
body.add newAsgnStmt(x, y)
|
body.add newAsgnStmt(x, y)
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
else:
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
body.add newAsgnStmt(x, y)
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
|
|
||||||
33
tests/arc/torc_selfcycles.nim
Normal file
33
tests/arc/torc_selfcycles.nim
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
discard """
|
||||||
|
output: '''ok'''
|
||||||
|
cmd: '''nim c --gc:orc -d:useMalloc -d:nimStressOrc $file'''
|
||||||
|
valgrind: "leaks"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bug #15753
|
||||||
|
|
||||||
|
type
|
||||||
|
NodeKind = enum
|
||||||
|
nkDancing,
|
||||||
|
nkColumn
|
||||||
|
|
||||||
|
DancingNode = ref object
|
||||||
|
right: DancingNode
|
||||||
|
column: DancingNode
|
||||||
|
kind: NodeKind
|
||||||
|
|
||||||
|
proc newColumnNode(): DancingNode =
|
||||||
|
result = DancingNode(kind: nkColumn)
|
||||||
|
result.right = result
|
||||||
|
result.column = result
|
||||||
|
|
||||||
|
proc createDLXList(): DancingNode =
|
||||||
|
result = newColumnNode()
|
||||||
|
|
||||||
|
for i in 0 .. 15:
|
||||||
|
let n = newColumnNode()
|
||||||
|
n.right = result.right
|
||||||
|
result = n
|
||||||
|
echo "ok"
|
||||||
|
|
||||||
|
var dlxlist = createDLXList()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue