mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-03 11:42:33 +00:00
initial version of the new untested destructor pass
This commit is contained in:
@@ -871,7 +871,8 @@ type
|
||||
# mean that there is no destructor.
|
||||
# see instantiateDestructor in semdestruct.nim
|
||||
deepCopy*: PSym # overriden 'deepCopy' operation
|
||||
assignment*: PSym # overriden '=' operator
|
||||
assignment*: PSym # overriden '=' operation
|
||||
sink*: PSym # overriden '=sink' operation
|
||||
methods*: seq[(int,PSym)] # attached methods
|
||||
size*: BiggestInt # the size of the type in bytes
|
||||
# -1 means that the size is unkwown
|
||||
@@ -1047,6 +1048,8 @@ proc newNode*(kind: TNodeKind): PNode =
|
||||
|
||||
proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
|
||||
result = newNode(kind)
|
||||
if children.len > 0:
|
||||
result.info = children[0].info
|
||||
result.sons = @children
|
||||
|
||||
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
||||
@@ -1290,6 +1293,7 @@ proc assignType*(dest, src: PType) =
|
||||
dest.align = src.align
|
||||
dest.destructor = src.destructor
|
||||
dest.deepCopy = src.deepCopy
|
||||
dest.sink = src.sink
|
||||
dest.assignment = src.assignment
|
||||
dest.lockLevel = src.lockLevel
|
||||
# this fixes 'type TLock = TSysLock':
|
||||
|
||||
@@ -76,7 +76,10 @@
|
||||
## inefficiencies. A better strategy is to collect all the temporaries
|
||||
## in a single object that we put into a single try-finally that
|
||||
## surrounds the proc body. This means the code stays quite efficient
|
||||
## when compiled to C.
|
||||
## when compiled to C. In fact, we do the same for variables, so
|
||||
## destructors are called when the proc returns, not at scope exit!
|
||||
## This makes certains idioms easier to support. (Taking the slice
|
||||
## of a temporary object.)
|
||||
##
|
||||
## foo(bar(X(), Y()))
|
||||
## X and Y get destroyed after bar completes:
|
||||
@@ -94,103 +97,17 @@ import
|
||||
|
||||
template hasDestructor(t: PType): bool = tfHasAsgn in t.flags
|
||||
|
||||
when false:
|
||||
type
|
||||
VarInfo = object
|
||||
hasInitValue: bool
|
||||
addrTaken: bool
|
||||
assigned: int # we don't care about the 'var' vs 'let'
|
||||
# distinction; it's an optimization pass
|
||||
read: int
|
||||
scope: int # the scope the variable is declared in
|
||||
|
||||
Con = object
|
||||
t: Table[int, VarInfo]
|
||||
owner: PSym
|
||||
scope: int
|
||||
|
||||
const
|
||||
InterestingSyms = {skVar, skResult}
|
||||
|
||||
proc collectData(c: var Con; n: PNode)
|
||||
|
||||
proc collectDef(c: var Con; n: PNode; hasInitValue: bool) =
|
||||
if n.kind == nkSym:
|
||||
c.t[n.sym.id] = VarInfo(hasInitValue: hasInitValue,
|
||||
addrTaken: false, assigned: 0, read: 0,
|
||||
scope: scope)
|
||||
|
||||
proc collectVarSection(c: var Con; n: PNode) =
|
||||
for a in n:
|
||||
if a.kind == nkCommentStmt: continue
|
||||
if a.kind == nkVarTuple:
|
||||
collectData(c, a.lastSon)
|
||||
for i in 0 .. a.len-3: collectDef(c, a[i], a.lastSon != nil)
|
||||
else:
|
||||
collectData(c, a.lastSon)
|
||||
if a.lastSon.kind != nkEmpty:
|
||||
collectDef(c, a.sons[0], a.lastSon != nil)
|
||||
|
||||
proc collectData(c: var Con; n: PNode) =
|
||||
case n.kind
|
||||
of nkAsgn, nkFastAsgn:
|
||||
if n[0].kind == nkSym and (let s = n[0].sym; s.owner == c.owner and
|
||||
s.kind in InterestingSyms):
|
||||
inc c.t[s.id].assigned
|
||||
collectData(c, n[1])
|
||||
of nkSym:
|
||||
if (let s = n[0].sym; s.owner == c.owner and
|
||||
s.kind in InterestingSyms):
|
||||
inc c.t[s.id].read
|
||||
of nkAddr, nkHiddenAddr:
|
||||
var n = n[0]
|
||||
while n.kind == nkBracketExpr: n = n[0]
|
||||
if (let s = n[0].sym; s.owner == c.owner and
|
||||
s.kind in InterestingSyms):
|
||||
c.t[s.id].addrTaken = true
|
||||
|
||||
of nkCallKinds:
|
||||
if n.sons[0].kind == nkSym:
|
||||
let s = n.sons[0].sym
|
||||
if s.magic != mNone:
|
||||
genMagic(c, n, s.magic)
|
||||
else:
|
||||
genCall(c, n)
|
||||
else:
|
||||
genCall(c, n)
|
||||
of nkCharLit..nkNilLit, nkIdent: discard
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr,
|
||||
nkDerefExpr, nkHiddenDeref:
|
||||
collectData(c, n[0])
|
||||
of nkIfStmt, nkIfExpr: genIf(c, n)
|
||||
of nkWhenStmt:
|
||||
# This is "when nimvm" node. Chose the first branch.
|
||||
collectData(c, n.sons[0].sons[1])
|
||||
of nkCaseStmt: genCase(c, n)
|
||||
of nkWhileStmt: genWhile(c, n)
|
||||
of nkBlockExpr, nkBlockStmt: genBlock(c, n)
|
||||
of nkReturnStmt: genReturn(c, n)
|
||||
of nkRaiseStmt: genRaise(c, n)
|
||||
of nkBreakStmt: genBreak(c, n)
|
||||
of nkTryStmt: genTry(c, n)
|
||||
of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
|
||||
nkBracket, nkCurly, nkPar, nkClosure, nkObjConstr:
|
||||
for x in n: collectData(c, x)
|
||||
of nkPragmaBlock: collectData(c, n.lastSon)
|
||||
of nkDiscardStmt: collectData(c, n.sons[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr,
|
||||
nkCast:
|
||||
collectData(c, n.sons[1])
|
||||
of nkObjDownConv, nkStringToCString, nkCStringToString:
|
||||
collectData(c, n.sons[0])
|
||||
of nkVarSection, nkLetSection: collectVarSection(c, n)
|
||||
else: discard
|
||||
const
|
||||
InterestingSyms = {skVar, skResult, skLet}
|
||||
|
||||
type
|
||||
Con = object
|
||||
owner: PSym
|
||||
g: ControlFlowGraph
|
||||
tmps: PType
|
||||
jumpTargets: IntSet
|
||||
tmpObj: PType
|
||||
tmp: PSym
|
||||
destroys, topLevelVars: PNode
|
||||
|
||||
proc isHarmlessVar*(s: PSym; c: Con): bool =
|
||||
# 's' is harmless if it used only once and its
|
||||
@@ -224,29 +141,151 @@ proc isHarmlessVar*(s: PSym; c: Con): bool =
|
||||
# L3
|
||||
#
|
||||
# So this analysis is for now overly conservative, but correct.
|
||||
discard
|
||||
var defsite = -1
|
||||
var usages = 0
|
||||
for i in 0..<c.g.len:
|
||||
case c.g[i].kind
|
||||
of def:
|
||||
if c.g[i].sym == s:
|
||||
if defsite < 0: defsite = i
|
||||
else: return false
|
||||
of use:
|
||||
if c.g[i].sym == s:
|
||||
if defsite < 0: return false
|
||||
for j in defsite .. i:
|
||||
# not within the same basic block?
|
||||
if j in c.jumpTargets: return false
|
||||
# if we want to die after the first 'use':
|
||||
if usages > 1: return false
|
||||
inc usages
|
||||
of useWithinCall:
|
||||
if c.g[i].sym == s: return false
|
||||
of goto, fork:
|
||||
discard "we do not perform an abstract interpretation yet"
|
||||
|
||||
template interestingSym(s: PSym): bool =
|
||||
s.owner == owner and s.kind in InterestingSyms and hasDestructor(s.typ)
|
||||
s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ)
|
||||
|
||||
proc genSink(t: PType; dest: PNode): PNode =
|
||||
let op = if t.sink != nil: t.sink else: t.assignment
|
||||
assert op != nil
|
||||
result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
|
||||
|
||||
proc genCopy(t: PType; dest: PNode): PNode =
|
||||
assert t.assignment != nil
|
||||
result = newTree(nkCall, newSymNode(t.assignment), newTree(nkHiddenAddr, dest))
|
||||
|
||||
proc genDestroy(t: PType; dest: PNode): PNode =
|
||||
assert t.destructor != nil
|
||||
result = newTree(nkCall, newSymNode(t.destructor), newTree(nkHiddenAddr, dest))
|
||||
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
if ri.kind in nkCallKinds:
|
||||
result = genSink(ri.typ, dest)
|
||||
elif ri.kind == nkSym and isHarmlessVar(ri.sym, c):
|
||||
result = genSink(ri.typ, dest)
|
||||
else:
|
||||
result = genCopy(ri.typ, dest)
|
||||
|
||||
proc addTopVar(c: var Con; v: PNode) =
|
||||
c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode)
|
||||
|
||||
proc p(n, parent: PNode; c: var Con) =
|
||||
template recurse(n, dest) =
|
||||
let x = dest
|
||||
for i in 0..<n.safeLen:
|
||||
p(n[i], x, c)
|
||||
parent.add x
|
||||
|
||||
case n.kind
|
||||
of nkVarSection, nkLetSection:
|
||||
discard "transform; var x = y to var x; x op y where op is a move or copy"
|
||||
var stmtList = newNodeI(nkStmtList, n.info)
|
||||
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
let L = it.len-1
|
||||
let ri = it[L]
|
||||
if it.kind == nkVarTuple and hasDestructor(ri.typ):
|
||||
let x = lowerTupleUnpacking(it, c.owner)
|
||||
p(x, stmtList, c)
|
||||
elif it.kind == nkIdentDefs and hasDestructor(it[0].typ):
|
||||
it.sons[L] = emptyNode
|
||||
for j in 0..L-1:
|
||||
let v = it[j]
|
||||
doAssert v.kind == nkSym
|
||||
# move the variable declaration to the top of the frame:
|
||||
c.addTopVar v
|
||||
# make sure it's destroyed at the end of the proc:
|
||||
c.destroys.add genDestroy(v.typ, v)
|
||||
if ri.kind != nkEmpty:
|
||||
let r = moveOrCopy(v, ri, c)
|
||||
recurse(ri, r)
|
||||
stmtList.add r
|
||||
else:
|
||||
# keep it, but transform 'ri':
|
||||
var varSection = copyNode(n)
|
||||
var itCopy = copyNode(it)
|
||||
for j in 0..L-1:
|
||||
itCopy.add it[j]
|
||||
p(ri, itCopy, c)
|
||||
varSection.add itCopy
|
||||
stmtList.add varSection
|
||||
parent.add stmtList
|
||||
of nkCallKinds:
|
||||
if n.typ != nil and hasDestructor(n.typ):
|
||||
discard "produce temp creation"
|
||||
let stmtList = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, n.info)
|
||||
rawAddField c.tmpObj, f
|
||||
var m = genSink(n.typ, rawDirectAccess(c.tmp, f))
|
||||
recurse(n, m)
|
||||
stmtList.add m
|
||||
stmtList.add rawDirectAccess(c.tmp, f)
|
||||
parent.add stmtList
|
||||
c.destroys.add genDestroy(n.typ, rawDirectAccess(c.tmp, f))
|
||||
else:
|
||||
recurse(n, copyNode(n))
|
||||
of nkAsgn, nkFastAsgn:
|
||||
if n[0].kind == nkSym and interestingSym(n[0].sym):
|
||||
discard "use move or assignment"
|
||||
let ri = n[1]
|
||||
let r = moveOrCopy(n[0], ri, c)
|
||||
# fortunately this skips the nkCall which we do not want to transform
|
||||
# to a temp here!
|
||||
recurse(ri, r)
|
||||
parent.add r
|
||||
else:
|
||||
recurse(n, copyNode(n))
|
||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||
nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
|
||||
parent.add n
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
p(n[i], n, c)
|
||||
recurse(n, copyNode(n))
|
||||
|
||||
proc injectDestructorCalls*(owner: PSym; n: PNode;
|
||||
disableExceptions = false): PNode =
|
||||
when false:
|
||||
var c = Con(t: initTable[int, VarInfo](), owner: owner)
|
||||
collectData(c, n)
|
||||
var allTemps = createObj(owner, n.info)
|
||||
proc injectDestructorCalls*(owner: PSym; n: PNode): PNode =
|
||||
var c: Con
|
||||
c.owner = owner
|
||||
c.tmp = newSym(skTemp, getIdent":d", owner, n.info)
|
||||
c.tmpObj = createObj(owner, n.info)
|
||||
c.tmp.typ = c.tmpObj
|
||||
c.destroys = newNodeI(nkStmtList, n.info)
|
||||
c.topLevelVars = newNodeI(nkVarSection, n.info)
|
||||
let cfg = constructCfg(owner, n)
|
||||
shallowCopy(c.g, cfg)
|
||||
c.jumpTargets = initIntSet()
|
||||
for i in 0..<c.g.len:
|
||||
if c.g[i].kind in {goto, fork}:
|
||||
c.jumpTargets.incl(i+c.g[i].dest)
|
||||
var stmtList = newNodeI(nkStmtList, n.info)
|
||||
for i in 0..<n.len:
|
||||
p(n[i], stmtList, c)
|
||||
if c.tmp.typ.n.len > 0:
|
||||
c.addTopVar(newSymNode c.tmp)
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
if c.topLevelVars.len > 0:
|
||||
result.add c.topLevelVars
|
||||
if c.destroys.len > 0:
|
||||
result.add newTryFinally(stmtList, c.destroys)
|
||||
else:
|
||||
result.add stmtList
|
||||
|
||||
@@ -235,14 +235,14 @@ proc genTry(c: var Con; n: PNode) =
|
||||
|
||||
proc genRaise(c: var Con; n: PNode) =
|
||||
gen(c, n.sons[0])
|
||||
c.code.add Instr(n: n, kind: goto, dest: high(int))
|
||||
c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
|
||||
|
||||
proc genReturn(c: var Con; n: PNode) =
|
||||
if n.sons[0].kind != nkEmpty: gen(c, n.sons[0])
|
||||
c.code.add Instr(n: n, kind: goto, dest: high(int))
|
||||
c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
|
||||
|
||||
const
|
||||
InterestingSyms = {skVar, skResult}
|
||||
InterestingSyms = {skVar, skResult, skLet}
|
||||
|
||||
proc genUse(c: var Con; n: PNode) =
|
||||
var n = n
|
||||
@@ -279,7 +279,7 @@ proc genMagic(c: var Con; n: PNode; m: TMagic) =
|
||||
for i in 2..<n.len: gen(c, n[i])
|
||||
of mExit:
|
||||
genCall(c, n)
|
||||
c.code.add Instr(n: n, kind: goto, dest: high(int))
|
||||
c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
|
||||
else:
|
||||
genCall(c, n)
|
||||
|
||||
|
||||
@@ -70,6 +70,9 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
|
||||
lit.intVal = i
|
||||
addSon(result, lit)
|
||||
|
||||
proc newTryFinally*(body, final: PNode): PNode =
|
||||
result = newTree(nkTryStmt, body, newTree(nkFinally, final))
|
||||
|
||||
proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
|
||||
let value = n.lastSon
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
@@ -139,6 +142,14 @@ proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
|
||||
addSon(result, newSymNode(field))
|
||||
result.typ = field.typ
|
||||
|
||||
proc rawDirectAccess*(obj, field: PSym): PNode =
|
||||
# returns a.field as a node
|
||||
assert field.kind == skField
|
||||
result = newNodeI(nkDotExpr, field.info)
|
||||
addSon(result, newSymNode obj)
|
||||
addSon(result, newSymNode field)
|
||||
result.typ = field.typ
|
||||
|
||||
proc lookupInRecord(n: PNode, id: int): PSym =
|
||||
result = nil
|
||||
case n.kind
|
||||
|
||||
@@ -14,6 +14,7 @@ const
|
||||
hasTinyCBackend* = defined(tinyc)
|
||||
useEffectSystem* = true
|
||||
useWriteTracking* = false
|
||||
newDestructors* = true
|
||||
hasFFI* = defined(useFFI)
|
||||
newScopeForIf* = true
|
||||
useCaas* = not defined(noCaas)
|
||||
|
||||
@@ -982,9 +982,10 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||
message(s.info, warnLockLevel,
|
||||
"declared lock level is $1, but real lock level is $2" %
|
||||
[$s.typ.lockLevel, $t.maxLockLevel])
|
||||
if s.kind == skFunc:
|
||||
when defined(dfa): dataflowAnalysis(s, body)
|
||||
trackWrites(s, body)
|
||||
when false:
|
||||
if s.kind == skFunc:
|
||||
when defined(dfa): dataflowAnalysis(s, body)
|
||||
trackWrites(s, body)
|
||||
|
||||
proc trackTopLevelStmt*(module: PSym; n: PNode) =
|
||||
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
|
||||
|
||||
@@ -399,7 +399,7 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
|
||||
# in order for this transformation to be correct.
|
||||
let L = identDefs.len
|
||||
let value = identDefs[L-1]
|
||||
if value.typ != nil and tfHasAsgn in value.typ.flags:
|
||||
if value.typ != nil and tfHasAsgn in value.typ.flags and c.p.owner.kind != skFunc:
|
||||
# the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()':
|
||||
identDefs.sons[L-1] = emptyNode
|
||||
if result.kind != nkStmtList:
|
||||
@@ -1303,7 +1303,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
localError(n.info, errGenerated,
|
||||
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
|
||||
incl(s.flags, sfUsed)
|
||||
of "=":
|
||||
of "=", "=sink":
|
||||
if s.magic == mAsgn: return
|
||||
incl(s.flags, sfUsed)
|
||||
let t = s.typ
|
||||
@@ -1321,14 +1321,15 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
objB = objB.sons[0]
|
||||
else: break
|
||||
if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
|
||||
if obj.assignment.isNil:
|
||||
obj.assignment = s
|
||||
let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
|
||||
if opr[].isNil:
|
||||
opr[] = s
|
||||
else:
|
||||
localError(n.info, errGenerated,
|
||||
"cannot bind another '=' to: " & typeToString(obj))
|
||||
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
|
||||
return
|
||||
localError(n.info, errGenerated,
|
||||
"signature for '=' must be proc[T: object](x: var T; y: T)")
|
||||
"signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)")
|
||||
else:
|
||||
if sfOverriden in s.flags:
|
||||
localError(n.info, errGenerated,
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
import
|
||||
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
|
||||
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
|
||||
lambdalifting, sempass2, lowerings, lookups
|
||||
|
||||
# implementation
|
||||
lambdalifting, sempass2, lowerings, lookups, destroyer
|
||||
|
||||
type
|
||||
PTransNode* = distinct PNode
|
||||
@@ -974,6 +972,8 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
|
||||
#result = liftLambdas(prc, result)
|
||||
incl(result.flags, nfTransf)
|
||||
when useEffectSystem: trackProc(prc, result)
|
||||
if prc.kind == skFunc:
|
||||
result = injectDestructorCalls(prc, n)
|
||||
#if prc.name.s == "testbody":
|
||||
# echo renderTree(result)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user