diff --git a/compiler/aliasanalysis.nim b/compiler/aliasanalysis.nim index e24c6d8e26..26b300527f 100644 --- a/compiler/aliasanalysis.nim +++ b/compiler/aliasanalysis.nim @@ -21,6 +21,24 @@ proc skipConvDfa*(n: PNode): PNode = else: break proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = + let stripped = skipConvDfa(orig) + if stripped.typ != nil and isSinkType(stripped.typ): + var base = stripped + while true: + case base.kind + of nkDotExpr, nkCheckedFieldExpr: + base = skipConvDfa(base[0]) + of nkHiddenDeref, nkDerefExpr: + base = skipConvDfa(base[0]) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + base = skipConvDfa(base[1]) + of nkObjUpConv, nkObjDownConv: + base = skipConvDfa(base[0]) + else: + break + if base.kind == nkSym and base.sym.owner == owner and + {sfGlobal, sfThread, sfCursor} * base.sym.flags == {}: + return true var n = orig while true: case n.kind diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index ddf9c2704c..922d78b091 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -1260,9 +1260,24 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = # block :stateLoop: # local vars decl (if needed) # body # Might get wrapped in try-except + var needsLoop = false + proc detectNonYieldGoto(n: PNode) = + if needsLoop: return + for i, c in n: + if c.kind == nkGotoState and c[0].kind == nkIntLit and (i > 0 and n[i - 1].kind != nkYieldStmt): + needsLoop = true + return + else: + detectNonYieldGoto(c) + for s in ctx.states: + detectNonYieldGoto(s.body) + if needsLoop: break + + result = newNodeI(nkStmtList, n.info) let loopBody = newNodeI(nkStmtList, n.info) - result = newTree(nkWhileStmt, ctx.g.boolLit(n.info, true), loopBody) - result.info = n.info + if needsLoop: + result = newTree(nkWhileStmt, ctx.g.boolLit(n.info, true), loopBody) + result.info = n.info let localVars = newNodeI(nkStmtList, n.info) @@ -1274,7 +1289,10 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = blockBody = ctx.wrapIntoTryExcept(blockBody) blockStmt.add(blockBody) - loopBody.add(blockStmt) + if needsLoop: + loopBody.add(blockStmt) + else: + result = blockStmt if ctx.hasExceptions: # Since we have yields in tries, we must switch current exception diff --git a/compiler/dfa.nim b/compiler/dfa.nim index ef6a767f07..32e12677d6 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -347,23 +347,43 @@ proc skipTrivials(c: var Con, n: PNode): PNode = result = result[1] else: break +proc matchesFieldAccess(orig: PNode; field: PSym): bool = + result = false + var n = skipConvDfa(orig) + while true: + case n.kind + of nkDotExpr, nkCheckedFieldExpr: + if n[1].kind == nkSym and n[1].sym == field: + result = true + break + n = skipConvDfa(n[0]) + else: + break + proc genUse(c: var Con; orig: PNode) = let n = c.skipTrivials(orig) - if n.kind == nkSym: + if c.root.kind == skField and matchesFieldAccess(orig, c.root): + c.code.add Instr(kind: use, n: orig) + inc c.interestingInstructions + elif n.kind == nkSym: if n.sym.kind in InterestingSyms and n.sym == c.root: c.code.add Instr(kind: use, n: orig) inc c.interestingInstructions + else: + discard else: gen(c, n) proc genDef(c: var Con; orig: PNode) = let n = c.skipTrivials(orig) - if n.kind == nkSym and n.sym.kind in InterestingSyms: - if n.sym == c.root: - c.code.add Instr(kind: def, n: orig) - inc c.interestingInstructions + if c.root.kind == skField and matchesFieldAccess(orig, c.root): + c.code.add Instr(kind: def, n: orig) + inc c.interestingInstructions + elif n.kind == nkSym and n.sym.kind in InterestingSyms and n.sym == c.root: + c.code.add Instr(kind: def, n: orig) + inc c.interestingInstructions proc genCall(c: var Con; n: PNode) = gen(c, n[0]) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index e6ddf79a8a..ed8299bbe1 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -97,8 +97,22 @@ when false: for i in low(InstrKind)..high(InstrKind): echo "INSTR ", i, " ", perfCounters[i] -proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool = - let root = parampatterns.exprRoot(n, allowCalls=false) +proc fieldRootFromAccess(n: PNode): PSym = + result = nil + var it = n + while true: + case it.kind + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + it = it[1] + of nkObjUpConv, nkObjDownConv, nkHiddenDeref, nkDerefExpr, nkHiddenAddr, nkBracketExpr: + it = it[0] + of nkDotExpr, nkCheckedFieldExpr: + if it[1].kind == nkSym: return it[1].sym + return nil + else: + return nil + +proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope; root: PSym): bool = if root == nil: return false elif sfSingleUsedTemp in root.flags: return true @@ -172,7 +186,12 @@ proc isLastRead(n: PNode; c: var Con; s: var Scope): bool = if not hasDestructorOrAsgn(c, n.typ): return true let m = skipConvDfa(n) - result = isLastReadImpl(n, c, s) + let root = parampatterns.exprRoot(n, allowCalls=false) + if n.typ != nil and isSinkType(n.typ) and isAnalysableFieldAccess(n, c.owner): + let fieldRoot = fieldRootFromAccess(n) + if fieldRoot != nil: + return isLastReadImpl(n, c, s, fieldRoot) + result = isLastReadImpl(n, c, s, root) proc isFirstWrite(n: PNode; c: var Con): bool = let m = skipConvDfa(n) @@ -814,34 +833,35 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} + nkCallKinds + nkLiterals: result = p(n, c, s, consumed) - elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and - isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)): - # Sinked params can be consumed only once. We need to reset the memory - # to disable the destructor which we have not elided - result = destructiveMoveVar(n, c, s) - elif n.kind in {nkHiddenSubConv, nkHiddenStdConv, nkConv}: - result = copyTree(n) - if n.typ.skipTypes(abstractInst-{tyOwned}).kind != tyOwned and - n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned: - # allow conversions from owned to unowned via this little hack: - let nTyp = n[1].typ - n[1].typ = n.typ - result[1] = p(n[1], c, s, sinkArg) - result[1].typ = nTyp - else: - result[1] = p(n[1], c, s, sinkArg) - elif n.kind in {nkObjDownConv, nkObjUpConv}: - result = copyTree(n) - result[0] = p(n[0], c, s, sinkArg) - elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}: - result = copyTree(n) - result[1] = p(n[1], c, s, sinkArg) - elif n.typ == nil: - # 'raise X' can be part of a 'case' expression. Deal with it here: - result = p(n, c, s, normal) else: - # copy objects that are not temporary but passed to a 'sink' parameter - result = passCopyToSink(n, c, s) + if ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and + isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)): + # Sinked params can be consumed only once. We need to reset the memory + # to disable the destructor which we have not elided + result = destructiveMoveVar(n, c, s) + elif n.kind in {nkHiddenSubConv, nkHiddenStdConv, nkConv}: + result = copyTree(n) + if n.typ.skipTypes(abstractInst-{tyOwned}).kind != tyOwned and + n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned: + # allow conversions from owned to unowned via this little hack: + let nTyp = n[1].typ + n[1].typ = n.typ + result[1] = p(n[1], c, s, sinkArg) + result[1].typ = nTyp + else: + result[1] = p(n[1], c, s, sinkArg) + elif n.kind in {nkObjDownConv, nkObjUpConv}: + result = copyTree(n) + result[0] = p(n[0], c, s, sinkArg) + elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}: + result = copyTree(n) + result[1] = p(n[1], c, s, sinkArg) + elif n.typ == nil: + # 'raise X' can be part of a 'case' expression. Deal with it here: + result = p(n, c, s, normal) + else: + # copy objects that are not temporary but passed to a 'sink' parameter + result = passCopyToSink(n, c, s) else: case n.kind of nkBracket, nkTupleConstr, nkClosure, nkCurly: diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 66b54a74ae..825e178e2d 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -187,6 +187,20 @@ type proc exprRoot*(n: PNode; allowCalls = true): PSym = result = nil + if n.typ != nil and isSinkType(n.typ): + var probe = n + while true: + case probe.kind + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + probe = probe[1] + of nkObjUpConv, nkObjDownConv, nkHiddenDeref, nkDerefExpr, nkHiddenAddr, nkBracketExpr: + probe = probe[0] + of nkDotExpr, nkCheckedFieldExpr: + if probe[1].kind == nkSym: + return probe[1].sym + break + else: + break var it = n while true: case it.kind