summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorcooldome <cdome@bk.ru>2018-10-18 19:21:25 +0100
committerAndreas Rumpf <rumpf_a@web.de>2018-10-18 20:21:25 +0200
commiteaca5be9d6e205e8aa7055306122a6c053ef35a6 (patch)
tree784c5f2726a94b656e3f85ab231c4dfc377cc167 /compiler
parent15dbd973dec848f1c429fe7043e53254a501812b (diff)
downloadNim-eaca5be9d6e205e8aa7055306122a6c053ef35a6.tar.gz
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
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim4
-rw-r--r--compiler/cgen.nim13
-rw-r--r--compiler/cgmeth.nim1
-rw-r--r--compiler/guards.nim2
-rw-r--r--compiler/jsgen.nim8
-rw-r--r--compiler/lambdalifting.nim17
-rw-r--r--compiler/lowerings.nim2
-rw-r--r--compiler/sem.nim2
-rw-r--r--compiler/semfold.nim17
-rw-r--r--compiler/seminst.nim5
-rw-r--r--compiler/sempass2.nim16
-rw-r--r--compiler/semstmts.nim12
-rw-r--r--compiler/semtypinst.nim5
-rw-r--r--compiler/transf.nim64
-rw-r--r--compiler/vm.nim11
-rw-r--r--compiler/vmdef.nim1
-rw-r--r--compiler/vmgen.nim5
17 files changed, 122 insertions, 63 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index eaf79a190..68c8e7dd2 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -660,7 +660,7 @@ type
     mNHint, mNWarning, mNError,
     mInstantiationInfo, mGetTypeInfo,
     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:
 const
@@ -811,7 +811,7 @@ type
     of routineKinds:
       procInstCache*: seq[PInstantiation]
       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:
       # modules keep track of the generic symbols they use from other modules.
       # this is because in incremental compilation, when a module is about to
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 86aa7fb45..26d66a0f5 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -14,7 +14,7 @@ import
   nversion, nimsets, msgs, std / sha1, bitsets, idents, types,
   ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
   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.`%`
 
@@ -845,6 +845,8 @@ proc genProcAux(m: BModule, prc: PSym) =
   var header = genProcHeader(m, prc)
   var returnStmt: Rope = 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 resultPos >= prc.ast.len:
       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
     if not isInvalidReturnType(m.config, prc.typ.sons[0]):
       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 a: TLoc
         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
       # succeeds without touching not-initialized memory.
       if sfNoInit in prc.flags: discard
-      elif allPathsAsgnResult(prc.getBody) == InitSkippable: discard
+      elif allPathsAsgnResult(procBody) == InitSkippable: discard
       else:
         resetLoc(p, res.loc)
       if skipTypes(res.typ, abstractInst).kind == tyArray:
@@ -885,7 +887,7 @@ proc genProcAux(m: BModule, prc: PSym) =
     if param.typ.isCompileTimeOnly: continue
     assignParam(p, param)
   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
   if sfNoReturn in prc.flags:
     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)
   #softRnl = if optLineDir in m.config.options: noRnl else: rnl
   # 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) =
   var i = 0
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index f2ad3dca0..cba07446f 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -281,6 +281,7 @@ proc genDispatcher(g: ModuleGraph; methods: TSymSeq, relevantCols: IntSet): PSym
     else:
       disp = ret
   nilchecks.add disp
+  nilchecks.flags.incl nfTransf # should not be further transformed
   result.ast.sons[bodyPos] = nilchecks
 
 proc generateMethodDispatchers*(g: ModuleGraph): PNode =
diff --git a/compiler/guards.nim b/compiler/guards.nim
index 99bb51fce..a01c023e4 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -70,7 +70,7 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
       n = n.sons[0]
       inc derefs
     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]
       else: return
     of nkHiddenStdConv, nkHiddenSubConv, nkConv:
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 7ab4a0472..19bbde777 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -32,7 +32,7 @@ import
   ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
   nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
   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
 
@@ -2021,7 +2021,8 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
     else:
       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
   if not prc.constraint.isNil:
@@ -2271,7 +2272,8 @@ proc genModule(p: PProc, n: PNode) =
     add(p.body, frameCreate(p,
         makeJSString("module " & p.module.module.name.s),
         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:
     add(p.body, frameDestroy(p))
 
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 79b44319f..c318421fa 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -11,7 +11,8 @@
 
 import
   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 """
   The basic approach is that captured vars need to be put on the heap and
@@ -257,7 +258,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   # add 'new' statement:
   result.add newCall(getSysSym(g, n.info, "internalNew"), env)
   result.add makeClosure(g, iter, env, n.info)
-
+  
 proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
   let envParam = getHiddenParam(g, owner)
   let obj = envParam.typ.lastSon
@@ -390,7 +391,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
     if innerProc:
       if s.isIterator: c.somethingToDo = true
       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
     if ow == owner:
       if owner.isIterator:
@@ -651,14 +653,17 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
         #  echo renderTree(s.getBody, {renderIds})
         let oldInContainer = c.inContainer
         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:
-          s.ast.sons[bodyPos] = body
+          s.transformedBody = body
         else:
-          s.ast.sons[bodyPos] = newTree(nkStmtList, rawClosureCreation(s, d, c), body)
+          s.transformedBody = newTree(nkStmtList, rawClosureCreation(s, d, c), body)
         c.inContainer = oldInContainer
+
       if s.typ.callConv == ccClosure:
         result = symToClosure(n, owner, d, c)
+
     elif s.id in d.capturedVars:
       if s.owner != owner:
         result = accessViaEnvParam(d.graph, n, owner)
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index dc12e25b7..603def326 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -405,6 +405,8 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
                        varSection, varInit, call, barrier, fv: PNode;
                        spawnKind: TSpawnResult): PSym =
   var body = newNodeI(nkStmtList, f.info)
+  body.flags.incl nfTransf # do not transform further
+
   var threadLocalBarrier: PSym
   if barrier != nil:
     var varSection2 = newNodeI(nkVarSection, barrier.info)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 0d336724c..97a47ceca 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -583,7 +583,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
     result = buildEchoStmt(c, result)
   if c.config.cmd == cmdIdeTools:
     appendToModule(c.module, result)
-  result = transformStmt(c.graph, c.module, result)
+  trackTopLevelStmt(c.graph, c.module, result)
 
 proc recoverContext(c: PContext) =
   # clean up in case of a semantic error: We clean up the stacks, etc. This is
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 53c9af07a..5565e8ed9 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -413,14 +413,15 @@ proc getAppType(n: PNode; g: ModuleGraph): PNode =
     result = newStrNodeT("console", n, g)
 
 proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) =
-  var err = false
-  if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}:
-    err = value <% firstOrd(g.config, n.typ) or value >% lastOrd(g.config, n.typ, fixedUnsigned=true)
-  else:
-    err = value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ)
-  if err:
-    localError(g.config, n.info, "cannot convert " & $value &
-                                     " to " & typeToString(n.typ))
+  if tfUncheckedArray notin n.typ.flags:
+    var err = false
+    if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}:
+      err = value <% firstOrd(g.config, n.typ) or value >% lastOrd(g.config, n.typ, fixedUnsigned=true)
+    else:
+      err = value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ)
+    if err:
+      localError(g.config, n.info, "cannot convert " & $value &
+                                      " to " & typeToString(n.typ))
 
 proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
   let dstTyp = skipTypes(n.typ, abstractRange)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index b6936d1be..de2e10a9b 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -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)
     freshGenSyms(b, result, orig, symMap)
     b = semProcBody(c, b)
-    b = hloBody(c, b)
-    n.sons[bodyPos] = transformBody(c.graph, c.module, b, result)
-    #echo "code instantiated ", result.name.s
+    result.ast[bodyPos] = hloBody(c, b)
+    trackProc(c.graph, result, result.ast[bodyPos])
     excl(result.flags, sfForward)
     dec c.inGenericInst
 
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 0a9de674b..c1bdb08a8 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -9,7 +9,7 @@
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  wordrecg, strutils, options, guards, writetracking, lineinfos,
+  wordrecg, strutils, options, guards, writetracking, lineinfos, semfold,
   modulegraphs
 
 when defined(useDfa):
@@ -48,6 +48,7 @@ type
     tags: PNode # list of tags
     bottom, inTryStmt: int
     owner: PSym
+    owner_module: PSym
     init: seq[int] # list of initialized variables
     guards: TModel # nested guards
     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:
     internalAssert tracked.config, op.n.sons[0].kind == nkEffectList
     var effectList = op.n.sons[0]
-    let s = n.skipConv
-    if s.kind == nkSym and s.sym.kind in routineKinds:
+    var s = n.skipConv
+    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)
     elif isNoEffectList(effectList):
       if isForwardedProc(n):
@@ -709,9 +712,13 @@ proc track(tracked: PEffects, n: PNode) =
     for i in 0 ..< safeLen(n):
       track(tracked, n.sons[i])
   of nkCallKinds:
+    if getConstExpr(tracked.owner_module, n, tracked.graph) != nil:
+      return 
     # p's effects are ours too:
-    let a = n.sons[0]
+    var a = n.sons[0]
     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
     # calling getAst(templateOrMacro()). Currently, templates and macros
     # 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.tags = effects.sons[tagEffects]
   t.owner = s
+  t.owner_module = s.getModule
   t.init = @[]
   t.guards.s = @[]
   t.guards.o = initOperators(g)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index a286cdc85..b97a56178 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1328,8 +1328,8 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
       pushProcCon(c, s)
       addResult(c, s.typ.sons[0], n.info, skProc)
       addResultNode(c, n)
-      let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-      n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s)
+      s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
+      trackProc(c.graph, s, s.ast[bodyPos])
       popProcCon(c)
     elif efOperand notin flags:
       localError(c.config, n.info, errGenericLambdaNotAllowed)
@@ -1369,8 +1369,8 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   pushProcCon(c, s)
   addResult(c, n.typ.sons[0], n.info, skProc)
   addResultNode(c, n)
-  let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-  n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s)
+  s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
+  trackProc(c.graph, s, s.ast[bodyPos])
   popProcCon(c)
   popOwner(c)
   closeScope(c)
@@ -1683,10 +1683,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
 
         if lfDynamicLib notin s.loc.flags:
           # 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'
           # 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:
         if s.typ.sons[0] != nil and kind != skIterator:
           addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index b05fb37ae..be6ffc586 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -30,6 +30,11 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
     localError(conf, info, "type 'var var' is not allowed")
   elif computeSize(conf, t) == szIllegalRecursion:
     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:
     if t.kind == tyObject and t.sons[0] != nil:
       if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
diff --git a/compiler/transf.nim b/compiler/transf.nim
index c2f6c799a..8721661df 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -21,9 +21,13 @@
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups,
   idents, renderer, types, passes, semfold, magicsys, cgmeth,
-  lambdalifting, sempass2, lowerings, destroyer, liftlocals, closureiters,
+  sempass2, lowerings, destroyer, liftlocals,
   modulegraphs, lineinfos
 
+proc transformBody*(g: ModuleGraph, prc: PSym, cache = true): PNode
+
+import closureiters, lambdalifting
+
 type
   PTransNode* = distinct PNode
 
@@ -62,18 +66,25 @@ proc newTransNode(kind: TNodeKind, n: PNode,
   var x = newNodeIT(kind, n.info, n.typ)
   newSeq(x.sons, sons)
   x.typ = n.typ
+#  x.flags = n.flags
   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.} =
   var n = PNode(a)
   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.} =
   var n = PNode(a)
   result = n.sons[i].PTransNode
 
-proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b))
-proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode)
+proc `[]`(a: PTransNode, i: BackwardsIndex): PTransNode {.inline.} =
+  `[]`(a, a.len - i.int)
 
 proc newTransCon(owner: PSym): PTransCon =
   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 =
   let s = n.sym
   if s.typ != nil and s.typ.callConv == ccClosure:
+    if s.kind in routineKinds:
+      discard transformBody(c.graph, s)
     if s.kind == skIterator:
       if c.tooEarly: return n
       else: return liftIterSym(c.graph, n, getCurrOwner(c))
@@ -146,7 +159,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
       return
     tc = tc.next
   result = b
-
+  
 proc transformSym(c: PTransf, n: PNode): PTransNode =
   result = PTransNode(transformSymAux(c, n))
 
@@ -561,9 +574,10 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
   c.breakSyms.add(labl)
   if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
       call.sons[0].typ.callConv == ccClosure:
-    n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
-    n.sons[length-2] = transform(c, n.sons[length-2]).PNode
-    result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode
+    result[1] = n.PTransNode
+    result[1][^1] = transformLoopBody(c, n[^1])
+    result[1][^2] = transform(c, n[^2])
+    result[1] = lambdalifting.liftForLoop(c.graph, result[1].PNode, getCurrOwner(c)).PTransNode
     discard c.breakSyms.pop
     return result
 
@@ -617,7 +631,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
       add(stmtList, newAsgnStmt(c, nkFastAsgn, temp, arg.PTransNode))
       idNodeTablePut(newC.mapping, formal, temp)
 
-  var body = iter.getBody.copyTree
+  let body = transformBody(c.graph, iter).copyTree
   pushInfoContext(c.graph.config, n.info)
   # XXX optimize this somehow. But the check "c.inlining" is not correct:
   var symMap: TIdTable
@@ -1040,27 +1054,36 @@ template liftDefer(c, root) =
   if c.deferDetected:
     liftDeferAux(root)
 
-proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode =
-  if nfTransf in n.flags or prc.kind in {skTemplate}:
-    result = n
+proc transformBody*(g: ModuleGraph, prc: PSym, cache = true): PNode =
+  assert prc.kind in routineKinds
+
+  if prc.transformedBody != nil:
+    result = prc.transformedBody
+  elif nfTransf in prc.ast[bodyPos].flags or prc.kind in {skTemplate}:
+    result = prc.ast[bodyPos]
   else:
-    var c = openTransf(g, module, "")
-    result = liftLambdas(g, prc, n, c.tooEarly)
-    #result = n
+
+    prc.transformedBody = newNode(nkEmpty) # protects from recursion
+    var c = openTransf(g, prc.getModule, "")
+    result = liftLambdas(g, prc, prc.ast[bodyPos], c.tooEarly)
     result = processTransf(c, result, prc)
     liftDefer(c, result)
-    #result = liftLambdas(prc, result)
-    when useEffectSystem: trackProc(g, prc, result)
     result = liftLocalsIfRequested(prc, result, g.cache, g.config)
     if c.needsDestroyPass: #and newDestructors:
       result = injectDestructorCalls(g, prc, result)
 
     if prc.isIterator:
       result = g.transformClosureIterator(prc, result)
-
+  
     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 =
   if nfTransf in n.flags:
@@ -1070,9 +1093,6 @@ proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode =
     result = processTransf(c, n, module)
     liftDefer(c, result)
     #result = liftLambdasForTopLevel(module, result)
-    when useEffectSystem: trackTopLevelStmt(g, module, result)
-    #if n.info ?? "temp.nim":
-    #  echo renderTree(result, {renderIds})
     if c.needsDestroyPass:
       result = injectDestructorCalls(g, module, result)
     incl(result.flags, nfTransf)
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 4255ba3fc..420934470 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -924,6 +924,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         regs[ra].node.flags.incl nfIsRef
       else:
         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:
       decodeB(rkNode)
       let a = regs[rb].node
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 17f48da07..875ae5a52 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -107,6 +107,7 @@ type
     opcEqIdent,
     opcStrToIdent,
     opcGetImpl,
+    opcGetImplTransf
 
     opcEcho,
     opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 14d411f9b..c59581aca 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -29,7 +29,7 @@
 
 import
   strutils, ast, astalgo, types, msgs, renderer, vmdef,
-  trees, intsets, magicsys, options, lowerings, lineinfos
+  trees, intsets, magicsys, options, lowerings, lineinfos, transf
 import platform
 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 mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag)
   of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl)
+  of mGetImplTransf: genUnaryABC(c, n, dest, opcGetImplTransf)
   of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner)
   of mNChild: genBinaryABC(c, n, dest, opcNChild)
   of mNSetChild: genVoidABC(c, n, dest, opcNSetChild)
@@ -2129,7 +2130,7 @@ proc genProc(c: PCtx; s: PSym): int =
     s.ast.sons[miscPos] = x
     # thanks to the jmp we can add top level statements easily and also nest
     # procs easily:
-    let body = s.getBody
+    let body = transformBody(c.graph, s, cache = not isCompileTimeProc(s))
     let procStart = c.xjmp(body, opcJmp, 0)
     var p = PProc(blocks: @[], sym: s)
     let oldPrc = c.prc