diff --git a/compiler/ast.nim b/compiler/ast.nim index 97ff4b5930..a6f774790d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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': diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index e7ff00bb96..db26c5366b 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -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.. 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.. 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 diff --git a/compiler/dfa.nim b/compiler/dfa.nim index dd9dd4c798..ca1849a3cf 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -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..