Change the order of compilation passes, transformation is made lazy at code gen (#8489)

* Ast no transformation
* Add getImplNoTransform to the macros module
* progress on delaying transf
* Fix methods tranformation
* Fix lazy lambdalifting
* fix create thread wrapper
* transform for lambda lifting
* improve getImplTransformed
* Fix destructor tests
* try to fix nimprof for linux
This commit is contained in:
cooldome 2018-10-18 19:21:25 +01:00 • committed by Andreas Rumpf
commit eaca5be9d6
22 changed files with 188 additions and 85 deletions

View file

@ -660,7 +660,7 @@ type
mNHint, mNWarning, mNError, mNHint, mNWarning, mNError,
mInstantiationInfo, mGetTypeInfo, mInstantiationInfo, mGetTypeInfo,
mNimvm, mIntDefine, mStrDefine, mRunnableExamples, mNimvm, mIntDefine, mStrDefine, mRunnableExamples,
mException, mBuiltinType, mSymOwner, mUncheckedArray mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf
# things that we can evaluate safely at compile time, even if not asked for it: # things that we can evaluate safely at compile time, even if not asked for it:
const const
@ -811,7 +811,7 @@ type
of routineKinds: of routineKinds:
procInstCache*: seq[PInstantiation] procInstCache*: seq[PInstantiation]
gcUnsafetyReason*: PSym # for better error messages wrt gcsafe gcUnsafetyReason*: PSym # for better error messages wrt gcsafe
#scope*: PScope # the scope where the proc was defined transformedBody*: PNode # cached body after transf pass
of skModule, skPackage: of skModule, skPackage:
# modules keep track of the generic symbols they use from other modules. # modules keep track of the generic symbols they use from other modules.
# this is because in incremental compilation, when a module is about to # this is because in incremental compilation, when a module is about to

View file

@ -14,7 +14,7 @@ import
nversion, nimsets, msgs, std / sha1, bitsets, idents, types, nversion, nimsets, msgs, std / sha1, bitsets, idents, types,
ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
lowerings, semparallel, tables, sets, ndi, lineinfos, pathutils lowerings, semparallel, tables, sets, ndi, lineinfos, pathutils, transf
import strutils except `%` # collides with ropes.`%` import strutils except `%` # collides with ropes.`%`
@ -845,6 +845,8 @@ proc genProcAux(m: BModule, prc: PSym) =
var header = genProcHeader(m, prc) var header = genProcHeader(m, prc)
var returnStmt: Rope = nil var returnStmt: Rope = nil
assert(prc.ast != nil) assert(prc.ast != nil)
let procBody = transformBody(m.g.graph, prc, cache = false)
if sfPure notin prc.flags and prc.typ.sons[0] != nil: if sfPure notin prc.flags and prc.typ.sons[0] != nil:
if resultPos >= prc.ast.len: if resultPos >= prc.ast.len:
internalError(m.config, prc.info, "proc has no result symbol") internalError(m.config, prc.info, "proc has no result symbol")
@ -852,7 +854,7 @@ proc genProcAux(m: BModule, prc: PSym) =
let res = resNode.sym # get result symbol let res = resNode.sym # get result symbol
if not isInvalidReturnType(m.config, prc.typ.sons[0]): if not isInvalidReturnType(m.config, prc.typ.sons[0]):
if sfNoInit in prc.flags: incl(res.flags, sfNoInit) if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(prc.getBody); val != nil): if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil):
var decl = localVarDecl(p, resNode) var decl = localVarDecl(p, resNode)
var a: TLoc var a: TLoc
initLocExprSingleUse(p, val, a) initLocExprSingleUse(p, val, a)
@ -873,7 +875,7 @@ proc genProcAux(m: BModule, prc: PSym) =
# global is either 'nil' or points to valid memory and so the RC operation # global is either 'nil' or points to valid memory and so the RC operation
# succeeds without touching not-initialized memory. # succeeds without touching not-initialized memory.
if sfNoInit in prc.flags: discard if sfNoInit in prc.flags: discard
elif allPathsAsgnResult(prc.getBody) == InitSkippable: discard elif allPathsAsgnResult(procBody) == InitSkippable: discard
else: else:
resetLoc(p, res.loc) resetLoc(p, res.loc)
if skipTypes(res.typ, abstractInst).kind == tyArray: if skipTypes(res.typ, abstractInst).kind == tyArray:
@ -885,7 +887,7 @@ proc genProcAux(m: BModule, prc: PSym) =
if param.typ.isCompileTimeOnly: continue if param.typ.isCompileTimeOnly: continue
assignParam(p, param) assignParam(p, param)
closureSetup(p, prc) closureSetup(p, prc)
genStmts(p, prc.getBody) # modifies p.locals, p.init, etc. genStmts(p, procBody) # modifies p.locals, p.init, etc.
var generatedProc: Rope var generatedProc: Rope
if sfNoReturn in prc.flags: if sfNoReturn in prc.flags:
if hasDeclspec in extccomp.CC[p.config.cCompiler].props: if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
@ -1522,7 +1524,8 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
m.initProc.options = initProcOptions(m) m.initProc.options = initProcOptions(m)
#softRnl = if optLineDir in m.config.options: noRnl else: rnl #softRnl = if optLineDir in m.config.options: noRnl else: rnl
# XXX replicate this logic! # XXX replicate this logic!
genStmts(m.initProc, n) let tranformed_n = transformStmt(m.g.graph, m.module, n)
genStmts(m.initProc, tranformed_n)
proc finishModule(m: BModule) = proc finishModule(m: BModule) =
var i = 0 var i = 0

View file

@ -281,6 +281,7 @@ proc genDispatcher(g: ModuleGraph; methods: TSymSeq, relevantCols: IntSet): PSym
else: else:
disp = ret disp = ret
nilchecks.add disp nilchecks.add disp
nilchecks.flags.incl nfTransf # should not be further transformed
result.ast.sons[bodyPos] = nilchecks result.ast.sons[bodyPos] = nilchecks
proc generateMethodDispatchers*(g: ModuleGraph): PNode = proc generateMethodDispatchers*(g: ModuleGraph): PNode =

View file

@ -70,7 +70,7 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
n = n.sons[0] n = n.sons[0]
inc derefs inc derefs
of nkBracketExpr: of nkBracketExpr:
if isConstExpr(n.sons[1]) or isLet(n.sons[1]): if isConstExpr(n.sons[1]) or isLet(n.sons[1]) or isConstExpr(n.sons[1].skipConv):
n = n.sons[0] n = n.sons[0]
else: return else: return
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:

View file

@ -32,7 +32,7 @@ import
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables, nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
times, ropes, math, passes, ccgutils, wordrecg, renderer, times, ropes, math, passes, ccgutils, wordrecg, renderer,
intsets, cgmeth, lowerings, sighashes, lineinfos, rodutils, pathutils intsets, cgmeth, lowerings, sighashes, lineinfos, rodutils, pathutils, transf
from modulegraphs import ModuleGraph, PPassContext from modulegraphs import ModuleGraph, PPassContext
@ -2021,7 +2021,8 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
else: else:
returnStmt = "return $#;$n" % [a.res] returnStmt = "return $#;$n" % [a.res]
p.nested: genStmt(p, prc.getBody) let transformed_body = transformBody(oldProc.module.graph, prc, cache = false)
p.nested: genStmt(p, transformed_body)
var def: Rope var def: Rope
if not prc.constraint.isNil: if not prc.constraint.isNil:
@ -2271,7 +2272,8 @@ proc genModule(p: PProc, n: PNode) =
add(p.body, frameCreate(p, add(p.body, frameCreate(p,
makeJSString("module " & p.module.module.name.s), makeJSString("module " & p.module.module.name.s),
makeJSString(toFilename(p.config, p.module.module.info)))) makeJSString(toFilename(p.config, p.module.module.info))))
genStmt(p, n) let n_transformed = transformStmt(p.module.graph, p.module.module, n)
genStmt(p, n_transformed)
if optStackTrace in p.options: if optStackTrace in p.options:
add(p.body, frameDestroy(p)) add(p.body, frameDestroy(p))

View file

@ -11,7 +11,8 @@
import import
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, intsets, strutils, options, ast, astalgo, trees, treetab, msgs,
idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
transf
discard """ discard """
The basic approach is that captured vars need to be put on the heap and The basic approach is that captured vars need to be put on the heap and
@ -390,7 +391,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
if innerProc: if innerProc:
if s.isIterator: c.somethingToDo = true if s.isIterator: c.somethingToDo = true
if not c.processed.containsOrIncl(s.id): if not c.processed.containsOrIncl(s.id):
detectCapturedVars(s.getBody, s, c) let body = transformBody(c.graph, s)
detectCapturedVars(body, s, c)
let ow = s.skipGenericOwner let ow = s.skipGenericOwner
if ow == owner: if ow == owner:
if owner.isIterator: if owner.isIterator:
@ -651,14 +653,17 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
# echo renderTree(s.getBody, {renderIds}) # echo renderTree(s.getBody, {renderIds})
let oldInContainer = c.inContainer let oldInContainer = c.inContainer
c.inContainer = 0 c.inContainer = 0
var body = liftCapturedVars(s.getBody, s, d, c) var body = transformBody(d.graph, s)
body = liftCapturedVars(body, s, d, c)
if c.envvars.getOrDefault(s.id).isNil: if c.envvars.getOrDefault(s.id).isNil:
s.ast.sons[bodyPos] = body s.transformedBody = body
else: else:
s.ast.sons[bodyPos] = newTree(nkStmtList, rawClosureCreation(s, d, c), body) s.transformedBody = newTree(nkStmtList, rawClosureCreation(s, d, c), body)
c.inContainer = oldInContainer c.inContainer = oldInContainer
if s.typ.callConv == ccClosure: if s.typ.callConv == ccClosure:
result = symToClosure(n, owner, d, c) result = symToClosure(n, owner, d, c)
elif s.id in d.capturedVars: elif s.id in d.capturedVars:
if s.owner != owner: if s.owner != owner:
result = accessViaEnvParam(d.graph, n, owner) result = accessViaEnvParam(d.graph, n, owner)

View file

@ -405,6 +405,8 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
varSection, varInit, call, barrier, fv: PNode; varSection, varInit, call, barrier, fv: PNode;
spawnKind: TSpawnResult): PSym = spawnKind: TSpawnResult): PSym =
var body = newNodeI(nkStmtList, f.info) var body = newNodeI(nkStmtList, f.info)
body.flags.incl nfTransf # do not transform further
var threadLocalBarrier: PSym var threadLocalBarrier: PSym
if barrier != nil: if barrier != nil:
var varSection2 = newNodeI(nkVarSection, barrier.info) var varSection2 = newNodeI(nkVarSection, barrier.info)

View file

@ -583,7 +583,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
result = buildEchoStmt(c, result) result = buildEchoStmt(c, result)
if c.config.cmd == cmdIdeTools: if c.config.cmd == cmdIdeTools:
appendToModule(c.module, result) appendToModule(c.module, result)
result = transformStmt(c.graph, c.module, result) trackTopLevelStmt(c.graph, c.module, result)
proc recoverContext(c: PContext) = proc recoverContext(c: PContext) =
# clean up in case of a semantic error: We clean up the stacks, etc. This is # clean up in case of a semantic error: We clean up the stacks, etc. This is

View file

@ -413,14 +413,15 @@ proc getAppType(n: PNode; g: ModuleGraph): PNode =
result = newStrNodeT("console", n, g) result = newStrNodeT("console", n, g)
proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) = proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) =
var err = false if tfUncheckedArray notin n.typ.flags:
if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}: var err = false
err = value <% firstOrd(g.config, n.typ) or value >% lastOrd(g.config, n.typ, fixedUnsigned=true) if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}:
else: err = value <% firstOrd(g.config, n.typ) or value >% lastOrd(g.config, n.typ, fixedUnsigned=true)
err = value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ) else:
if err: err = value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ)
localError(g.config, n.info, "cannot convert " & $value & if err:
" to " & typeToString(n.typ)) localError(g.config, n.info, "cannot convert " & $value &
" to " & typeToString(n.typ))
proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode = proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
let dstTyp = skipTypes(n.typ, abstractRange) let dstTyp = skipTypes(n.typ, abstractRange)

View file

@ -146,9 +146,8 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym) idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
freshGenSyms(b, result, orig, symMap) freshGenSyms(b, result, orig, symMap)
b = semProcBody(c, b) b = semProcBody(c, b)
b = hloBody(c, b) result.ast[bodyPos] = hloBody(c, b)
n.sons[bodyPos] = transformBody(c.graph, c.module, b, result) trackProc(c.graph, result, result.ast[bodyPos])
#echo "code instantiated ", result.name.s
excl(result.flags, sfForward) excl(result.flags, sfForward)
dec c.inGenericInst dec c.inGenericInst

View file

@ -9,7 +9,7 @@
import import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg, strutils, options, guards, writetracking, lineinfos, wordrecg, strutils, options, guards, writetracking, lineinfos, semfold,
modulegraphs modulegraphs
when defined(useDfa): when defined(useDfa):
@ -48,6 +48,7 @@ type
tags: PNode # list of tags tags: PNode # list of tags
bottom, inTryStmt: int bottom, inTryStmt: int
owner: PSym owner: PSym
owner_module: PSym
init: seq[int] # list of initialized variables init: seq[int] # list of initialized variables
guards: TModel # nested guards guards: TModel # nested guards
locked: seq[PNode] # locked locations locked: seq[PNode] # locked locations
@ -562,8 +563,10 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit: if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit:
internalAssert tracked.config, op.n.sons[0].kind == nkEffectList internalAssert tracked.config, op.n.sons[0].kind == nkEffectList
var effectList = op.n.sons[0] var effectList = op.n.sons[0]
let s = n.skipConv var s = n.skipConv
if s.kind == nkSym and s.sym.kind in routineKinds: if s.kind == nkCast and s[1].typ.kind == tyProc:
s = s[1]
if s.kind == nkSym and s.sym.kind in routineKinds and isNoEffectList(effectList):
propagateEffects(tracked, n, s.sym) propagateEffects(tracked, n, s.sym)
elif isNoEffectList(effectList): elif isNoEffectList(effectList):
if isForwardedProc(n): if isForwardedProc(n):
@ -709,9 +712,13 @@ proc track(tracked: PEffects, n: PNode) =
for i in 0 ..< safeLen(n): for i in 0 ..< safeLen(n):
track(tracked, n.sons[i]) track(tracked, n.sons[i])
of nkCallKinds: of nkCallKinds:
if getConstExpr(tracked.owner_module, n, tracked.graph) != nil:
return
# p's effects are ours too: # p's effects are ours too:
let a = n.sons[0] var a = n.sons[0]
let op = a.typ let op = a.typ
if a.kind == nkCast and a[1].typ.kind == tyProc:
a = a[1]
# XXX: in rare situations, templates and macros will reach here after # XXX: in rare situations, templates and macros will reach here after
# calling getAst(templateOrMacro()). Currently, templates and macros # calling getAst(templateOrMacro()). Currently, templates and macros
# are indistinguishable from normal procs (both have tyProc type) and # are indistinguishable from normal procs (both have tyProc type) and
@ -934,6 +941,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
t.exc = effects.sons[exceptionEffects] t.exc = effects.sons[exceptionEffects]
t.tags = effects.sons[tagEffects] t.tags = effects.sons[tagEffects]
t.owner = s t.owner = s
t.owner_module = s.getModule
t.init = @[] t.init = @[]
t.guards.s = @[] t.guards.s = @[]
t.guards.o = initOperators(g) t.guards.o = initOperators(g)

View file

@ -1328,8 +1328,8 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
pushProcCon(c, s) pushProcCon(c, s)
addResult(c, s.typ.sons[0], n.info, skProc) addResult(c, s.typ.sons[0], n.info, skProc)
addResultNode(c, n) addResultNode(c, n)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) trackProc(c.graph, s, s.ast[bodyPos])
popProcCon(c) popProcCon(c)
elif efOperand notin flags: elif efOperand notin flags:
localError(c.config, n.info, errGenericLambdaNotAllowed) localError(c.config, n.info, errGenericLambdaNotAllowed)
@ -1369,8 +1369,8 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
pushProcCon(c, s) pushProcCon(c, s)
addResult(c, n.typ.sons[0], n.info, skProc) addResult(c, n.typ.sons[0], n.info, skProc)
addResultNode(c, n) addResultNode(c, n)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) trackProc(c.graph, s, s.ast[bodyPos])
popProcCon(c) popProcCon(c)
popOwner(c) popOwner(c)
closeScope(c) closeScope(c)
@ -1683,10 +1683,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if lfDynamicLib notin s.loc.flags: if lfDynamicLib notin s.loc.flags:
# no semantic checking for importc: # no semantic checking for importc:
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
# unfortunately we cannot skip this step when in 'system.compiles' # unfortunately we cannot skip this step when in 'system.compiles'
# context as it may even be evaluated in 'system.compiles': # context as it may even be evaluated in 'system.compiles':
n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) trackProc(c.graph, s, s.ast[bodyPos])
else: else:
if s.typ.sons[0] != nil and kind != skIterator: if s.typ.sons[0] != nil and kind != skIterator:
addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info)) addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))

View file

@ -30,6 +30,11 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
localError(conf, info, "type 'var var' is not allowed") localError(conf, info, "type 'var var' is not allowed")
elif computeSize(conf, t) == szIllegalRecursion: elif computeSize(conf, t) == szIllegalRecursion:
localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'") localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'")
t = typ.skipTypes({tyGenericInst})
if t.kind == tyArray and tfUncheckedArray in t.flags:
t[0].flags.incl tfUncheckedArray # mark range of unchecked array also unchecked
when false: when false:
if t.kind == tyObject and t.sons[0] != nil: if t.kind == tyObject and t.sons[0] != nil:
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:

View file

@ -21,9 +21,13 @@
import import
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups, intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups,
idents, renderer, types, passes, semfold, magicsys, cgmeth, idents, renderer, types, passes, semfold, magicsys, cgmeth,
lambdalifting, sempass2, lowerings, destroyer, liftlocals, closureiters, sempass2, lowerings, destroyer, liftlocals,
modulegraphs, lineinfos modulegraphs, lineinfos
proc transformBody*(g: ModuleGraph, prc: PSym, cache = true): PNode
import closureiters, lambdalifting
type type
PTransNode* = distinct PNode PTransNode* = distinct PNode
@ -62,18 +66,25 @@ proc newTransNode(kind: TNodeKind, n: PNode,
var x = newNodeIT(kind, n.info, n.typ) var x = newNodeIT(kind, n.info, n.typ)
newSeq(x.sons, sons) newSeq(x.sons, sons)
x.typ = n.typ x.typ = n.typ
# x.flags = n.flags
result = x.PTransNode result = x.PTransNode
proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b))
proc len(a: PTransNode): int {.inline.} = sonsLen(a.PNode)
proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} = proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
var n = PNode(a) var n = PNode(a)
n.sons[i] = PNode(x) n.sons[i] = PNode(x)
proc `[]=`(a: PTransNode, i: BackwardsIndex, x: PTransNode) {.inline.} =
`[]=`(a, a.len - i.int, x)
proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} = proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
var n = PNode(a) var n = PNode(a)
result = n.sons[i].PTransNode result = n.sons[i].PTransNode
proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b)) proc `[]`(a: PTransNode, i: BackwardsIndex): PTransNode {.inline.} =
proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode) `[]`(a, a.len - i.int)
proc newTransCon(owner: PSym): PTransCon = proc newTransCon(owner: PSym): PTransCon =
assert owner != nil assert owner != nil
@ -118,6 +129,8 @@ proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PTransNode): PTrans
proc transformSymAux(c: PTransf, n: PNode): PNode = proc transformSymAux(c: PTransf, n: PNode): PNode =
let s = n.sym let s = n.sym
if s.typ != nil and s.typ.callConv == ccClosure: if s.typ != nil and s.typ.callConv == ccClosure:
if s.kind in routineKinds:
discard transformBody(c.graph, s)
if s.kind == skIterator: if s.kind == skIterator:
if c.tooEarly: return n if c.tooEarly: return n
else: return liftIterSym(c.graph, n, getCurrOwner(c)) else: return liftIterSym(c.graph, n, getCurrOwner(c))
@ -561,9 +574,10 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
c.breakSyms.add(labl) c.breakSyms.add(labl)
if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
call.sons[0].typ.callConv == ccClosure: call.sons[0].typ.callConv == ccClosure:
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode result[1] = n.PTransNode
n.sons[length-2] = transform(c, n.sons[length-2]).PNode result[1][^1] = transformLoopBody(c, n[^1])
result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode result[1][^2] = transform(c, n[^2])
result[1] = lambdalifting.liftForLoop(c.graph, result[1].PNode, getCurrOwner(c)).PTransNode
discard c.breakSyms.pop discard c.breakSyms.pop
return result return result
@ -617,7 +631,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
add(stmtList, newAsgnStmt(c, nkFastAsgn, temp, arg.PTransNode)) add(stmtList, newAsgnStmt(c, nkFastAsgn, temp, arg.PTransNode))
idNodeTablePut(newC.mapping, formal, temp) idNodeTablePut(newC.mapping, formal, temp)
var body = iter.getBody.copyTree let body = transformBody(c.graph, iter).copyTree
pushInfoContext(c.graph.config, n.info) pushInfoContext(c.graph.config, n.info)
# XXX optimize this somehow. But the check "c.inlining" is not correct: # XXX optimize this somehow. But the check "c.inlining" is not correct:
var symMap: TIdTable var symMap: TIdTable
@ -1040,17 +1054,20 @@ template liftDefer(c, root) =
if c.deferDetected: if c.deferDetected:
liftDeferAux(root) liftDeferAux(root)
proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode = proc transformBody*(g: ModuleGraph, prc: PSym, cache = true): PNode =
if nfTransf in n.flags or prc.kind in {skTemplate}: assert prc.kind in routineKinds
result = n
if prc.transformedBody != nil:
result = prc.transformedBody
elif nfTransf in prc.ast[bodyPos].flags or prc.kind in {skTemplate}:
result = prc.ast[bodyPos]
else: else:
var c = openTransf(g, module, "")
result = liftLambdas(g, prc, n, c.tooEarly) prc.transformedBody = newNode(nkEmpty) # protects from recursion
#result = n var c = openTransf(g, prc.getModule, "")
result = liftLambdas(g, prc, prc.ast[bodyPos], c.tooEarly)
result = processTransf(c, result, prc) result = processTransf(c, result, prc)
liftDefer(c, result) liftDefer(c, result)
#result = liftLambdas(prc, result)
when useEffectSystem: trackProc(g, prc, result)
result = liftLocalsIfRequested(prc, result, g.cache, g.config) result = liftLocalsIfRequested(prc, result, g.cache, g.config)
if c.needsDestroyPass: #and newDestructors: if c.needsDestroyPass: #and newDestructors:
result = injectDestructorCalls(g, prc, result) result = injectDestructorCalls(g, prc, result)
@ -1059,8 +1076,14 @@ proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode =
result = g.transformClosureIterator(prc, result) result = g.transformClosureIterator(prc, result)
incl(result.flags, nfTransf) incl(result.flags, nfTransf)
#if prc.name.s == "testbody":
# echo renderTree(result) let cache = cache or prc.typ.callConv == ccInline
if cache:
# genProc for inline procs will be called multiple times from diffrent modules,
# it is important to transform exactly once to get sym ids and locations right
prc.transformedBody = result
else:
prc.transformedBody = nil
proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode = proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode =
if nfTransf in n.flags: if nfTransf in n.flags:
@ -1070,9 +1093,6 @@ proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode =
result = processTransf(c, n, module) result = processTransf(c, n, module)
liftDefer(c, result) liftDefer(c, result)
#result = liftLambdasForTopLevel(module, result) #result = liftLambdasForTopLevel(module, result)
when useEffectSystem: trackTopLevelStmt(g, module, result)
#if n.info ?? "temp.nim":
# echo renderTree(result, {renderIds})
if c.needsDestroyPass: if c.needsDestroyPass:
result = injectDestructorCalls(g, module, result) result = injectDestructorCalls(g, module, result)
incl(result.flags, nfTransf) incl(result.flags, nfTransf)

View file

@ -924,6 +924,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
regs[ra].node.flags.incl nfIsRef regs[ra].node.flags.incl nfIsRef
else: else:
stackTrace(c, tos, pc, "node is not a symbol") stackTrace(c, tos, pc, "node is not a symbol")
of opcGetImplTransf:
decodeB(rkNode)
let a = regs[rb].node
if a.kind == nkSym:
regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit)
else:
let ast = a.sym.ast.shallowCopy
for i in 0..<a.sym.ast.len:
ast[i] = a.sym.ast[i]
ast[bodyPos] = transformBody(c.graph, a.sym)
ast.copyTree()
of opcSymOwner: of opcSymOwner:
decodeB(rkNode) decodeB(rkNode)
let a = regs[rb].node let a = regs[rb].node

View file

@ -107,6 +107,7 @@ type
opcEqIdent, opcEqIdent,
opcStrToIdent, opcStrToIdent,
opcGetImpl, opcGetImpl,
opcGetImplTransf
opcEcho, opcEcho,
opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ... opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...

View file

@ -29,7 +29,7 @@
import import
strutils, ast, astalgo, types, msgs, renderer, vmdef, strutils, ast, astalgo, types, msgs, renderer, vmdef,
trees, intsets, magicsys, options, lowerings, lineinfos trees, intsets, magicsys, options, lowerings, lineinfos, transf
import platform import platform
from os import splitFile from os import splitFile
@ -1122,6 +1122,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
of mStaticExec: genBinaryABCD(c, n, dest, opcGorge) of mStaticExec: genBinaryABCD(c, n, dest, opcGorge)
of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag) of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag)
of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl)
of mGetImplTransf: genUnaryABC(c, n, dest, opcGetImplTransf)
of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner) of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner)
of mNChild: genBinaryABC(c, n, dest, opcNChild) of mNChild: genBinaryABC(c, n, dest, opcNChild)
of mNSetChild: genVoidABC(c, n, dest, opcNSetChild) of mNSetChild: genVoidABC(c, n, dest, opcNSetChild)
@ -2129,7 +2130,7 @@ proc genProc(c: PCtx; s: PSym): int =
s.ast.sons[miscPos] = x s.ast.sons[miscPos] = x
# thanks to the jmp we can add top level statements easily and also nest # thanks to the jmp we can add top level statements easily and also nest
# procs easily: # procs easily:
let body = s.getBody let body = transformBody(c.graph, s, cache = not isCompileTimeProc(s))
let procStart = c.xjmp(body, opcJmp, 0) let procStart = c.xjmp(body, opcJmp, 0)
var p = PProc(blocks: @[], sym: s) var p = PProc(blocks: @[], sym: s)
let oldPrc = c.prc let oldPrc = c.prc

View file

@ -252,6 +252,10 @@ else: # bootstrapping substitute
else: else:
n.strValOld n.strValOld
when defined(nimSymImplTransform):
proc getImplTransformed*(symbol: NimNode): NimNode {.magic: "GetImplTransf", noSideEffect.}
## for a typed proc returns the AST after transformation pass
when defined(nimHasSymOwnerInMacro): when defined(nimHasSymOwnerInMacro):
proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.} proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.}
## accepts node of kind nnkSym and returns its owner's symbol. ## accepts node of kind nnkSym and returns its owner's symbol.

View file

@ -118,13 +118,13 @@ when defined(memProfiler):
var var
gTicker {.threadvar.}: int gTicker {.threadvar.}: int
proc requestedHook(): bool {.nimcall.} = proc requestedHook(): bool {.nimcall, locks: 0.} =
if gTicker == 0: if gTicker == 0:
gTicker = SamplingInterval gTicker = SamplingInterval
result = true result = true
dec gTicker dec gTicker
proc hook(st: StackTrace, size: int) {.nimcall.} = proc hook(st: StackTrace, size: int) {.nimcall, locks: 0.} =
when defined(ignoreAllocationSize): when defined(ignoreAllocationSize):
hookAux(st, 1) hookAux(st, 1)
else: else:
@ -136,7 +136,7 @@ else:
gTicker: int # we use an additional counter to gTicker: int # we use an additional counter to
# avoid calling 'getTicks' too frequently # avoid calling 'getTicks' too frequently
proc requestedHook(): bool {.nimcall.} = proc requestedHook(): bool {.nimcall, locks: 0.} =
if interval == 0: result = true if interval == 0: result = true
elif gTicker == 0: elif gTicker == 0:
gTicker = 500 gTicker = 500
@ -145,7 +145,7 @@ else:
else: else:
dec gTicker dec gTicker
proc hook(st: StackTrace) {.nimcall.} = proc hook(st: StackTrace) {.nimcall locks: 0.} =
#echo "profiling! ", interval #echo "profiling! ", interval
if interval == 0: if interval == 0:
hookAux(st, 1) hookAux(st, 1)

View file

@ -1,12 +1,7 @@
discard """ discard """
errormsg: "'=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'" errormsg: "'=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
file: "tprevent_assign2.nim"
cmd: "nim check --hint[Performance]:off $file" line: 48
nimout: '''
tprevent_assign2.nim(53, 31) Error: '=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'; another read is done here: tprevent_assign2.nim(52, 13)
tprevent_assign2.nim(55, 31) Error: '=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'; another read is done here: tprevent_assign2.nim(52, 13)
tprevent_assign2.nim(66, 29) Error: '=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'; another read is done here: tprevent_assign2.nim(68, 9)
'''
""" """
type type
@ -56,13 +51,5 @@ proc preventThis() =
else: else:
discard discard
proc preventThis2() = allowThis()
var otherTree: Foo preventThis()
try:
try:
otherTree = createTree(44)
echo otherTree
finally:
take2(createTree(34), otherTree)
finally:
echo otherTree

View file

@ -0,0 +1,53 @@
discard """
errormsg: "'=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
file: "tprevent_assign3.nim"
line: 46
"""
type
Foo = object
x: int
proc `=destroy`(f: var Foo) = f.x = 0
proc `=`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
proc `=sink`(a: var Foo; b: Foo) = a.x = b.x
proc createTree(x: int): Foo =
Foo(x: x)
proc take2(a, b: sink Foo) =
echo a.x, " ", b.x
proc allowThis() =
var otherTree: Foo
try:
for i in 0..3:
while true:
#if i == 0:
otherTree = createTree(44)
case i
of 0:
echo otherTree
take2(createTree(34), otherTree)
of 1:
take2(createTree(34), otherTree)
else:
discard
finally:
discard
proc preventThis2() =
var otherTree: Foo
try:
try:
otherTree = createTree(44)
echo otherTree
finally:
take2(createTree(34), otherTree)
finally:
echo otherTree
allowThis()
preventThis2()

View file

@ -94,7 +94,7 @@ proc fn3(x, y: int): bool =
static: static:
let fn1s = "proc fn1(x, y: int): int =\n result = 2 * (x + y)\n" let fn1s = "proc fn1(x, y: int): int =\n result = 2 * (x + y)\n"
let fn2s = "proc fn2(x, y: float): float =\n result = (y + 2.0 * x) / (x - y)\n" let fn2s = "proc fn2(x, y: float): float =\n result = (y + 2 * x) / (x - y)\n"
let fn3s = "proc fn3(x, y: int): bool =\n result = ((x and 3) div 4 or x mod (y xor -1)) == 0 or not contains([1, 2], y)\n" let fn3s = "proc fn3(x, y: int): bool =\n result = ((x and 3) div 4 or x mod (y xor -1)) == 0 or not contains([1, 2], y)\n"
doAssert fn1.repr_to_string == fn1s doAssert fn1.repr_to_string == fn1s
doAssert fn2.repr_to_string == fn2s doAssert fn2.repr_to_string == fn2s