mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-12 06:18:51 +00:00
fixes #25333; WIP
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user