Skip to content

Commit 0dd9608

Browse files
committed
Continue nim-lang#13002
1 parent fb1b51f commit 0dd9608

File tree

2 files changed

+82
-38
lines changed

2 files changed

+82
-38
lines changed

compiler/injectdestructors.nim

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ type
3434
uninit: IntSet # set of uninit'ed vars
3535
uninitComputed: bool
3636

37-
const toDebug {.strdefine.} = "" # "server" # "serverNimAsyncContinue"
37+
const toDebug {.strdefine.} = ""
3838

3939
template dbg(body) =
4040
when toDebug.len > 0:
@@ -47,7 +47,7 @@ proc isLastRead(location: PNode; c: var Con; pc, comesFrom: int): int =
4747
case c.g[pc].kind
4848
of def:
4949
if defInstrTargets(c.g[pc], location):
50-
# the path lead to a redefinition of 's' --> abandon it.
50+
# the path leads to a redefinition of 's' --> abandon it.
5151
return high(int)
5252
inc pc
5353
of use:
@@ -85,17 +85,63 @@ proc isLastRead(n: PNode; c: var Con): bool =
8585
instr = i
8686
break
8787

88-
dbg:
89-
echo "starting point for ", n, " is ", instr, " ", n.kind
88+
dbg: echo "starting point for ", n, " is ", instr, " ", n.kind
9089

9190
if instr < 0: return false
9291
# we go through all paths beginning from 'instr+1' and need to
9392
# ensure that we don't find another 'use X' instruction.
9493
if instr+1 >= c.g.len: return true
9594

9695
result = isLastRead(n, c, instr+1, -1) >= 0
97-
dbg:
98-
echo "ugh ", c.otherRead.isNil, " ", result
96+
dbg: echo "ugh ", c.otherRead.isNil, " ", result
97+
98+
proc isFirstWrite(location: PNode; c: var Con; pc, comesFrom: int; instr: int): int =
99+
var pc = pc
100+
while pc < instr:
101+
case c.g[pc].kind
102+
of def:
103+
if defInstrTargets(c.g[pc], location):
104+
# a definition of 's' before ours makes ours not the first write
105+
return -1
106+
inc pc
107+
of use:
108+
if useInstrTargets(c.g[pc], location):
109+
return -1
110+
inc pc
111+
of goto:
112+
pc = pc + c.g[pc].dest
113+
of fork:
114+
# every branch must not contain a def/use of our location:
115+
let variantA = isFirstWrite(location, c, pc+1, pc, instr)
116+
if variantA < 0: return -1
117+
var variantB = isFirstWrite(location, c, pc + c.g[pc].dest, pc, instr + c.g[pc].dest)
118+
if variantB < 0: return -1
119+
elif variantB == high(int):
120+
variantB = variantA
121+
pc = variantB
122+
of InstrKind.join:
123+
let dest = pc + c.g[pc].dest
124+
if dest == comesFrom: return pc + 1
125+
inc pc
126+
return pc
127+
128+
proc isFirstWrite(n: PNode; c: var Con): bool =
129+
# first we need to search for the instruction that belongs to 'n':
130+
var instr = -1
131+
let m = dfa.skipConvDfa(n)
132+
133+
for i in countdown(c.g.len-1, 0): # We search backwards here to treat loops correctly
134+
if c.g[i].kind == def and c.g[i].n == m:
135+
if instr < 0:
136+
instr = i
137+
break
138+
139+
if instr < 0: return false
140+
# we go through all paths going to 'instr' and need to
141+
# ensure that we don't find another 'def/use X' instruction.
142+
if instr == 0: return true
143+
144+
result = isFirstWrite(n, c, 0, -1, instr) >= 0
99145

100146
proc initialized(code: ControlFlowGraph; pc: int,
101147
init, uninit: var IntSet; comesFrom: int): int =
@@ -200,22 +246,20 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} =
200246
else:
201247
result = t.attachedOps[attachedSink] != nil
202248

203-
proc genSink(c: Con; dest, ri: PNode): PNode =
204-
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
205-
let k = if t.attachedOps[attachedSink] != nil: attachedSink
206-
else: attachedAsgn
207-
if t.attachedOps[k] != nil:
208-
result = genOp(c, t, k, dest, ri)
209-
else:
210-
# in rare cases only =destroy exists but no sink or assignment
211-
# (see Pony object in tmove_objconstr.nim)
212-
# we generate a fast assignment in this case:
249+
proc genSink(c: var Con; dest, ri: PNode): PNode =
250+
if isFirstWrite(dest, c): # optimize sink call into a bitwise memcopy
213251
result = newTree(nkFastAsgn, dest)
214-
215-
proc genSinkOrMemMove(c: Con; dest, ri: PNode, isFirstWrite: bool): PNode =
216-
# optimize sink call into a bitwise memcopy
217-
if isFirstWrite: newTree(nkFastAsgn, dest)
218-
else: genSink(c, dest, ri)
252+
else:
253+
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
254+
let k = if t.attachedOps[attachedSink] != nil: attachedSink
255+
else: attachedAsgn
256+
if t.attachedOps[k] != nil:
257+
result = genOp(c, t, k, dest, ri)
258+
else:
259+
# in rare cases only =destroy exists but no sink or assignment
260+
# (see Pony object in tmove_objconstr.nim)
261+
# we generate a fast assignment in this case:
262+
result = newTree(nkFastAsgn, dest)
219263

220264
proc genCopyNoCheck(c: Con; dest, ri: PNode): PNode =
221265
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
@@ -289,7 +333,7 @@ type
289333
sinkArg
290334

291335
proc p(n: PNode; c: var Con; mode: ProcessMode): PNode
292-
proc moveOrCopy(dest, ri: PNode; c: var Con, isFirstWrite: bool): PNode
336+
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode
293337

294338
proc isClosureEnv(n: PNode): bool = n.kind == nkSym and n.sym.name.s[0] == ':'
295339

@@ -552,7 +596,7 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
552596
if ri.kind == nkEmpty and c.inLoop > 0:
553597
ri = genDefaultCall(v.typ, c, v.info)
554598
if ri.kind != nkEmpty:
555-
let r = moveOrCopy(v, ri, c, isFirstWrite = (c.inLoop == 0))
599+
let r = moveOrCopy(v, ri, c)
556600
result.add r
557601
else: # keep the var but transform 'ri':
558602
var v = copyNode(n)
@@ -572,7 +616,7 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
572616
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
573617
cycleCheck(n, c)
574618
assert n[1].kind notin {nkAsgn, nkFastAsgn}
575-
result = moveOrCopy(n[0], n[1], c, isFirstWrite = false)
619+
result = moveOrCopy(n[0], n[1], c)
576620
else:
577621
result = copyNode(n)
578622
result.add copyTree(n[0])
@@ -615,21 +659,21 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
615659
for i in 0..<n.len:
616660
result[i] = p(n[i], c, mode)
617661

618-
proc moveOrCopy(dest, ri: PNode; c: var Con, isFirstWrite: bool): PNode =
662+
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
619663
case ri.kind
620664
of nkCallKinds:
621665
if isUnpackedTuple(dest):
622666
result = newTree(nkFastAsgn, dest, p(ri, c, consumed))
623667
else:
624-
result = genSinkOrMemMove(c, dest, ri, isFirstWrite)
668+
result = genSink(c, dest, ri)
625669
result.add p(ri, c, consumed)
626670
of nkBracketExpr:
627671
if isUnpackedTuple(ri[0]):
628672
# unpacking of tuple: take over elements
629673
result = newTree(nkFastAsgn, dest, p(ri, c, consumed))
630674
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
631675
# Rule 3: `=sink`(x, z); wasMoved(z)
632-
var snk = genSinkOrMemMove(c, dest, ri, isFirstWrite)
676+
var snk = genSink(c, dest, ri)
633677
snk.add ri
634678
result = newTree(nkStmtList, snk, genWasMoved(ri, c))
635679
else:
@@ -640,53 +684,53 @@ proc moveOrCopy(dest, ri: PNode; c: var Con, isFirstWrite: bool): PNode =
640684
if ri.len > 0 and isDangerousSeq(ri.typ):
641685
result = genCopy(c, dest, ri)
642686
else:
643-
result = genSinkOrMemMove(c, dest, ri, isFirstWrite)
687+
result = genSink(c, dest, ri)
644688
result.add p(ri, c, consumed)
645689
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
646-
result = genSinkOrMemMove(c, dest, ri, isFirstWrite)
690+
result = genSink(c, dest, ri)
647691
result.add p(ri, c, consumed)
648692
of nkSym:
649693
if isSinkParam(ri.sym):
650694
# Rule 3: `=sink`(x, z); wasMoved(z)
651695
sinkParamIsLastReadCheck(c, ri)
652-
var snk = genSinkOrMemMove(c, dest, ri, isFirstWrite)
696+
var snk = genSink(c, dest, ri)
653697
snk.add ri
654698
result = newTree(nkStmtList, snk, genWasMoved(ri, c))
655699
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
656700
isLastRead(ri, c) and canBeMoved(c, dest.typ):
657701
# Rule 3: `=sink`(x, z); wasMoved(z)
658-
var snk = genSinkOrMemMove(c, dest, ri, isFirstWrite)
702+
var snk = genSink(c, dest, ri)
659703
snk.add ri
660704
result = newTree(nkStmtList, snk, genWasMoved(ri, c))
661705
else:
662706
result = genCopy(c, dest, ri)
663707
result.add p(ri, c, consumed)
664708
of nkHiddenSubConv, nkHiddenStdConv, nkConv:
665709
when false:
666-
result = moveOrCopy(dest, ri[1], c, isFirstWrite)
710+
result = moveOrCopy(dest, ri[1], c)
667711
if not sameType(ri.typ, ri[1].typ):
668712
let copyRi = copyTree(ri)
669713
copyRi[1] = result[^1]
670714
result[^1] = copyRi
671715
else:
672-
result = genSinkOrMemMove(c, dest, ri, isFirstWrite)
716+
result = genSink(c, dest, ri)
673717
result.add p(ri, c, sinkArg)
674718
of nkObjDownConv, nkObjUpConv:
675719
when false:
676-
result = moveOrCopy(dest, ri[0], c, isFirstWrite)
720+
result = moveOrCopy(dest, ri[0], c)
677721
let copyRi = copyTree(ri)
678722
copyRi[0] = result[^1]
679723
result[^1] = copyRi
680724
else:
681-
result = genSinkOrMemMove(c, dest, ri, isFirstWrite)
725+
result = genSink(c, dest, ri)
682726
result.add p(ri, c, sinkArg)
683727
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt:
684-
handleNested(ri): moveOrCopy(dest, node, c, isFirstWrite)
728+
handleNested(ri): moveOrCopy(dest, node, c)
685729
else:
686730
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
687731
canBeMoved(c, dest.typ):
688732
# Rule 3: `=sink`(x, z); wasMoved(z)
689-
var snk = genSinkOrMemMove(c, dest, ri, isFirstWrite)
733+
var snk = genSink(c, dest, ri)
690734
snk.add ri
691735
result = newTree(nkStmtList, snk, genWasMoved(ri, c))
692736
else:

tests/destructor/tmisc_destructors.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ proc test(): auto =
2828
var (a, b, _) = test()
2929

3030
doAssert assign_counter == 0
31-
doAssert sink_counter == 3
31+
doAssert sink_counter == 0
3232

3333
# bug #11510
3434
proc main =

0 commit comments

Comments
 (0)