fixes #15753 [backport:1.4] (#15971)

(cherry picked from commit 7eb34d170a)
This commit is contained in:
Andreas Rumpf
2020-11-15 15:47:42 +01:00
committed by narimiran
parent f02b359968
commit fffb0dbea5
2 changed files with 109 additions and 19 deletions

View File

@@ -390,6 +390,16 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
v.addVar(result, lowerings.newIntLit(c.g, body.info, first))
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) =
let incCall = genBuiltin(c.g, mInc, "inc", i)
incCall.add lowerings.newIntLit(c.g, c.info, 1)
@@ -506,43 +516,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"
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)
let elemType = t.lastSon
createTypeBoundOps(c.g, c.c, elemType, c.info)
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):
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))
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:
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType)
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr))
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp)
var cond: PNode
if isCyclic:
if isFinal(elemType):
let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
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:
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, x)
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
else:
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
cond.typ = getSysType(c.g, x.info, tyBool)
case c.kind
of attachedSink:
body.add genIf(c, cond, actions)
body.add newAsgnStmt(x, y)
if isCyclic:
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:
body.add genIf(c, y, callCodegenProc(c.g,
if isCyclic: "nimIncRefCyclic" else: "nimIncRef", c.info, y))
body.add genIf(c, cond, actions)
body.add newAsgnStmt(x, y)
if isCyclic:
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:
body.add genIf(c, cond, actions)
of attachedDeepCopy: assert(false, "cannot happen")
@@ -569,19 +611,30 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let xenv = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
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)
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xenv)
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp)
let decRefProc =
if c.g.config.selectedGC == gcOrc: "nimDecRefIsLastCyclicDyn"
if isCyclic: "nimDecRefIsLastCyclicDyn"
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)
case c.kind
of attachedSink:
body.add genIf(c, cond, actions)
body.add newAsgnStmt(x, y)
if isCyclic:
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:
let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
yenv.typ = getSysType(c.g, c.info, tyPointer)
@@ -589,8 +642,12 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if c.g.config.selectedGC == gcOrc: "nimIncRefCyclic"
else: "nimIncRef"
body.add genIf(c, yenv, callCodegenProc(c.g, incRefProc, c.info, yenv))
body.add genIf(c, cond, actions)
body.add newAsgnStmt(x, y)
if isCyclic:
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:
body.add genIf(c, cond, actions)
of attachedDeepCopy: assert(false, "cannot happen")

View 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()