diff options
Diffstat (limited to 'compiler/transf.nim')
-rw-r--r-- | compiler/transf.nim | 195 |
1 files changed, 106 insertions, 89 deletions
diff --git a/compiler/transf.nim b/compiler/transf.nim index 83e66a069..7b2979dea 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -21,9 +21,14 @@ 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; + noDestructors = false): PNode + +import closureiters, lambdalifting + type PTransNode* = distinct PNode @@ -38,13 +43,13 @@ type # if we encounter the 2nd yield statement next: PTransCon # for stacking - TTransfContext = object of passes.TPassContext + TTransfContext = object of TPassContext module: PSym transCon: PTransCon # top of a TransCon stack inlining: int # > 0 if we are in inlining context (copy vars) nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' - deferDetected, tooEarly, needsDestroyPass: bool + deferDetected, tooEarly, needsDestroyPass, noDestructors: bool graph: ModuleGraph PTransf = ref TTransfContext @@ -62,18 +67,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 @@ -110,14 +122,16 @@ proc transformSons(c: PTransf, n: PNode): PTransNode = for i in countup(0, sonsLen(n)-1): result[i] = transform(c, n.sons[i]) -proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode = - result = newTransNode(nkFastAsgn, PNode(ri).info, 2) +proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PTransNode): PTransNode = + result = newTransNode(kind, PNode(ri).info, 2) result[0] = PTransNode(le) result[1] = ri 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, true, c.noDestructors) if s.kind == skIterator: if c.tooEarly: return n else: return liftIterSym(c.graph, n, getCurrOwner(c)) @@ -190,26 +204,32 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode = var L = sonsLen(it) var defs = newTransNode(it.kind, it.info, L) for j in countup(0, L-3): - let x = freshVar(c, it.sons[j].sym) - idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x) - defs[j] = x.PTransNode + if it[j].kind == nkSym: + let x = freshVar(c, it.sons[j].sym) + idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x) + defs[j] = x.PTransNode + else: + defs[j] = transform(c, it[j]) assert(it.sons[L-2].kind == nkEmpty) defs[L-2] = newNodeI(nkEmpty, it.info).PTransNode defs[L-1] = transform(c, it.sons[L-1]) result[i] = defs proc transformConstSection(c: PTransf, v: PNode): PTransNode = - result = newTransNode(v) - for i in countup(0, sonsLen(v)-1): - var it = v.sons[i] - if it.kind == nkCommentStmt: - result[i] = PTransNode(it) - else: - if it.kind != nkConstDef: internalError(c.graph.config, it.info, "transformConstSection") - if it.sons[0].kind != nkSym: - internalError(c.graph.config, it.info, "transformConstSection") + result = PTransNode(v) + when false: + result = newTransNode(v) + for i in countup(0, sonsLen(v)-1): + var it = v.sons[i] + if it.kind == nkCommentStmt: + result[i] = PTransNode(it) + else: + if it.kind != nkConstDef: internalError(c.graph.config, it.info, "transformConstSection") + if it.sons[0].kind != nkSym: + debug it.sons[0] + internalError(c.graph.config, it.info, "transformConstSection") - result[i] = PTransNode(it) + result[i] = PTransNode(it) proc hasContinue(n: PNode): bool = case n.kind @@ -223,25 +243,17 @@ proc newLabel(c: PTransf, n: PNode): PSym = result = newSym(skLabel, nil, getCurrOwner(c), n.info) result.name = getIdent(c.graph.cache, genPrefix & $result.id) -proc freshLabels(c: PTransf, n: PNode; symMap: var TIdTable) = - if n.kind in {nkBlockStmt, nkBlockExpr}: - if n.sons[0].kind == nkSym: - let x = newLabel(c, n[0]) - idTablePut(symMap, n[0].sym, x) - n.sons[0].sym = x - if n.kind == nkSym and n.sym.kind == skLabel: - let x = PSym(idTableGet(symMap, n.sym)) - if x != nil: n.sym = x - else: - for i in 0 ..< safeLen(n): freshLabels(c, n.sons[i], symMap) - proc transformBlock(c: PTransf, n: PNode): PTransNode = var labl: PSym - if n.sons[0].kind != nkEmpty: - # already named block? -> Push symbol on the stack: - labl = n.sons[0].sym + if c.inlining > 0: + labl = newLabel(c, n[0]) + idNodeTablePut(c.transCon.mapping, n[0].sym, newSymNode(labl)) else: - labl = newLabel(c, n) + labl = + if n.sons[0].kind != nkEmpty: + n.sons[0].sym # already named block? -> Push symbol on the stack + else: + newLabel(c, n) c.breakSyms.add(labl) result = transformSons(c, n) discard c.breakSyms.pop @@ -282,28 +294,10 @@ proc transformWhile(c: PTransf; n: PNode): PTransNode = discard c.breakSyms.pop proc transformBreak(c: PTransf, n: PNode): PTransNode = - if n.sons[0].kind != nkEmpty or c.inlining > 0: - result = n.PTransNode - when false: - let lablCopy = idNodeTableGet(c.transCon.mapping, n.sons[0].sym) - if lablCopy.isNil: - result = n.PTransNode - else: - result = newTransNode(n.kind, n.info, 1) - result[0] = lablCopy.PTransNode - elif c.breakSyms.len > 0: - # this check can fail for 'nim check' + result = transformSons(c, n) + if n.sons[0].kind == nkEmpty and c.breakSyms.len > 0: let labl = c.breakSyms[c.breakSyms.high] - result = transformSons(c, n) result[0] = newSymNode(labl).PTransNode - else: - result = n.PTransNode - -proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) = - # XXX: BUG: what if `n` is an expression with side-effects? - for i in countup(0, sonsLen(c.transCon.forStmt) - 3): - add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i], - transform(c, newTupleAccess(c.graph, n, i)))) proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode = case n.kind @@ -328,6 +322,18 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode = result[i] = introduceNewLocalVars(c, n.sons[i]) proc transformYield(c: PTransf, n: PNode): PTransNode = + proc asgnTo(lhs: PNode, rhs: PTransNode): PTransNode = + # Choose the right assignment instruction according to the given ``lhs`` + # node since it may not be a nkSym (a stack-allocated skForVar) but a + # nkDotExpr (a heap-allocated slot into the envP block) + case lhs.kind: + of nkSym: + internalAssert c.graph.config, lhs.sym.kind == skForVar + result = newAsgnStmt(c, nkFastAsgn, lhs, rhs) + of nkDotExpr: + result = newAsgnStmt(c, nkAsgn, lhs, rhs) + else: + internalAssert c.graph.config, false result = newTransNode(nkStmtList, n.info, 0) var e = n.sons[0] # c.transCon.forStmt.len == 3 means that there is one for loop variable @@ -340,13 +346,20 @@ proc transformYield(c: PTransf, n: PNode): PTransNode = for i in countup(0, sonsLen(e) - 1): var v = e.sons[i] if v.kind == nkExprColonExpr: v = v.sons[1] - add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i], - transform(c, v))) + let lhs = c.transCon.forStmt.sons[i] + let rhs = transform(c, v) + add(result, asgnTo(lhs, rhs)) else: - unpackTuple(c, e, result) + # Unpack the tuple into the loop variables + # XXX: BUG: what if `n` is an expression with side-effects? + for i in countup(0, sonsLen(c.transCon.forStmt) - 3): + let lhs = c.transCon.forStmt.sons[i] + let rhs = transform(c, newTupleAccess(c.graph, e, i)) + add(result, asgnTo(lhs, rhs)) else: - var x = transform(c, e) - add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x)) + let lhs = c.transCon.forStmt.sons[0] + let rhs = transform(c, e) + add(result, asgnTo(lhs, rhs)) inc(c.transCon.yieldStmts) if c.transCon.yieldStmts <= 1: @@ -542,9 +555,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 @@ -584,7 +598,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = # generate a temporary and produce an assignment statement: var temp = newTemp(c, formal.typ, formal.info) addVar(v, temp) - add(stmtList, newAsgnStmt(c, temp, arg.PTransNode)) + add(stmtList, newAsgnStmt(c, nkFastAsgn, temp, arg.PTransNode)) idNodeTablePut(newC.mapping, formal, temp) of paVarAsgn: assert(skipTypes(formal.typ, abstractInst).kind == tyVar) @@ -595,16 +609,11 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = addSonSkipIntLit(typ, formal.typ.sons[0]) var temp = newTemp(c, typ, formal.info) addVar(v, temp) - add(stmtList, newAsgnStmt(c, temp, arg.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, true, c.noDestructors) pushInfoContext(c.graph.config, n.info) - # XXX optimize this somehow. But the check "c.inlining" is not correct: - var symMap: TIdTable - initIdTable symMap - freshLabels(c, body, symMap) - inc(c.inlining) add(stmtList, transform(c, body)) #findWrongOwners(c, stmtList.pnode) @@ -726,7 +735,7 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode = let actions = newTransNode(nkStmtListExpr, n[1], 2) # Generating `let exc = (excType)(getCurrentException())` # -> getCurrentException() - let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", newNodeI(nkEmpty, n.info))) + let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException")) # -> (excType) let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2) convNode[0] = PTransNode(newNodeI(nkEmpty, n.info)) @@ -1021,27 +1030,37 @@ 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; + noDestructors = false): 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, "") + c.noDestructors = noDestructors + 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: + if c.needsDestroyPass and not noDestructors: 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: @@ -1051,14 +1070,12 @@ 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) -proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = +proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode; + noDestructors = false): PNode = if nfTransf in n.flags: result = n else: @@ -1067,6 +1084,6 @@ proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = liftDefer(c, result) # expressions are not to be injected with destructor calls as that # the list of top level statements needs to be collected before. - if c.needsDestroyPass: + if c.needsDestroyPass and not noDestructors: result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) |