lambda lifting compiles again

This commit is contained in:
Andreas Rumpf
2018-05-12 08:30:55 +02:00
parent bb8c47b496
commit 1310279691
3 changed files with 154 additions and 149 deletions

View File

@@ -102,14 +102,14 @@ proc genLabel(c: Con): TPosition =
proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
let dist = p.int - c.code.len
internalAssert(-0x7fff < dist and dist < 0x7fff)
doAssert(-0x7fff < dist and dist < 0x7fff)
c.code.add Instr(n: n, kind: goto, dest: dist)
proc patch(c: var Con, p: TPosition) =
# patch with current index
let p = p.int
let diff = c.code.len - p
internalAssert(-0x7fff < diff and diff < 0x7fff)
doAssert(-0x7fff < diff and diff < 0x7fff)
c.code[p].dest = diff
proc popBlock(c: var Con; oldLen: int) =

View File

@@ -11,7 +11,8 @@
import
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, magicsys, rodread, lowerings, tables
idents, renderer, types, magicsys, rodread, lowerings, tables,
modulegraphs
discard """
The basic approach is that captured vars need to be put on the heap and
@@ -125,25 +126,25 @@ proc newCall(a: PSym, b: PNode): PNode =
result.add newSymNode(a)
result.add b
proc createStateType(iter: PSym): PType =
proc createStateType(g: ModuleGraph; iter: PSym): PType =
var n = newNodeI(nkRange, iter.info)
addSon(n, newIntNode(nkIntLit, -1))
addSon(n, newIntNode(nkIntLit, 0))
result = newType(tyRange, iter)
result.n = n
var intType = nilOrSysInt()
var intType = nilOrSysInt(g)
if intType.isNil: intType = newType(tyInt, iter)
rawAddSon(result, intType)
proc createStateField(iter: PSym): PSym =
proc createStateField(g: ModuleGraph; iter: PSym): PSym =
result = newSym(skField, getIdent(":state"), iter, iter.info)
result.typ = createStateType(iter)
result.typ = createStateType(g, iter)
proc createEnvObj(owner: PSym; info: TLineInfo): PType =
proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
# YYY meh, just add the state field for every closure for now, it's too
# hard to figure out if it comes from a closure iterator:
result = createObj(owner, info, final=false)
rawAddField(result, createStateField(owner))
result = createObj(g, owner, info, final=false)
rawAddField(result, createStateField(g, owner))
proc getIterResult(iter: PSym): PSym =
if resultPos < iter.ast.len:
@@ -166,7 +167,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
assert sfFromGeneric in param.flags
#echo "produced environment: ", param.id, " for ", routine.id
proc getHiddenParam(routine: PSym): PSym =
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
let params = routine.ast.sons[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
@@ -174,7 +175,7 @@ proc getHiddenParam(routine: PSym): PSym =
assert sfFromGeneric in result.flags
else:
# writeStackTrace()
localError(routine.info, "internal error: could not find env param for " & routine.name.s)
localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
result = routine
proc getEnvParam*(routine: PSym): PSym =
@@ -208,14 +209,14 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
result.sons[0] = le
result.sons[1] = ri
proc makeClosure*(prc: PSym; env: PNode; info: TLineInfo): PNode =
proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode =
result = newNodeIT(nkClosure, info, prc.typ)
result.add(newSymNode(prc))
if env == nil:
result.add(newNodeIT(nkNilLit, info, getSysType(tyNil)))
result.add(newNodeIT(nkNilLit, info, getSysType(g, info, tyNil)))
else:
if env.skipConv.kind == nkClosure:
localError(info, "internal error: taking closure of closure")
localError(g.config, info, "internal error: taking closure of closure")
result.add(env)
proc interestingIterVar(s: PSym): bool {.inline.} =
@@ -232,7 +233,7 @@ proc liftingHarmful(owner: PSym): bool {.inline.} =
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
result = gCmd == cmdCompileToJS and not isCompileTime
proc liftIterSym*(n: PNode; owner: PSym): PNode =
proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
if liftingHarmful(owner): return n
let iter = n.sym
@@ -240,10 +241,10 @@ proc liftIterSym*(n: PNode; owner: PSym): PNode =
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let hp = getHiddenParam(iter)
let hp = getHiddenParam(g, iter)
var env: PNode
if owner.isIterator:
let it = getHiddenParam(owner)
let it = getHiddenParam(g, owner)
addUniqueField(it.typ.sons[0], hp)
env = indirectAccess(newSymNode(it), hp, hp.info)
else:
@@ -255,11 +256,11 @@ proc liftIterSym*(n: PNode; owner: PSym): PNode =
addVar(v, env)
result.add(v)
# add 'new' statement:
result.add newCall(getSysSym"internalNew", env)
result.add makeClosure(iter, env, n.info)
result.add newCall(getSysSym(g, n.info, "internalNew"), env)
result.add makeClosure(g, iter, env, n.info)
proc freshVarForClosureIter*(s, owner: PSym): PNode =
let envParam = getHiddenParam(owner)
proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
let envParam = getHiddenParam(g, owner)
let obj = envParam.typ.lastSon
addField(obj, s)
@@ -269,18 +270,18 @@ proc freshVarForClosureIter*(s, owner: PSym): PNode =
if field != nil:
result = rawIndirectAccess(access, field, s.info)
else:
localError(s.info, "internal error: cannot generate fresh variable")
localError(g.config, s.info, "internal error: cannot generate fresh variable")
result = access
# ------------------ new stuff -------------------------------------------
proc markAsClosure(owner: PSym; n: PNode) =
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
let s = n.sym
if illegalCapture(s):
localError(n.info, "illegal capture '$1' of type <$2> which is declared here: $3" %
localError(g.config, n.info, "illegal capture '$1' of type <$2> which is declared here: $3" %
[s.name.s, typeToString(s.typ), $s.info])
elif owner.typ.callConv notin {ccClosure, ccDefault}:
localError(n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
[s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]])
incl(owner.typ.flags, tfCapturesEnv)
owner.typ.callConv = ccClosure
@@ -290,12 +291,14 @@ type
processed, capturedVars: IntSet
ownerToType: Table[int, PType]
somethingToDo: bool
graph: ModuleGraph
proc initDetectionPass(fn: PSym): DetectionPass =
proc initDetectionPass(g: ModuleGraph; fn: PSym): DetectionPass =
result.processed = initIntSet()
result.capturedVars = initIntSet()
result.ownerToType = initTable[int, PType]()
result.processed.incl(fn.id)
result.graph = g
discard """
proc outer =
@@ -312,7 +315,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
result = c.ownerToType.getOrDefault(owner.id)
if result.isNil:
result = newType(tyRef, owner)
let obj = createEnvObj(owner, info)
let obj = createEnvObj(c.graph, owner, info)
rawAddSon(result, obj)
c.ownerToType[owner.id] = result
@@ -321,13 +324,13 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
let obj = refObj.lastSon
let fieldType = c.getEnvTypeForOwner(dep, info) #getHiddenParam(dep).typ
if refObj == fieldType:
localError(dep.info, "internal error: invalid up reference computed")
localError(c.graph.config, dep.info, "internal error: invalid up reference computed")
let upIdent = getIdent(upName)
let upField = lookupInRecord(obj.n, upIdent)
if upField != nil:
if upField.typ != fieldType:
localError(dep.info, "internal error: up references do not agree")
localError(c.graph.config, dep.info, "internal error: up references do not agree")
else:
let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
result.typ = fieldType
@@ -369,7 +372,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
cp.typ = t
addHiddenParam(fn, cp)
elif cp.typ != t and fn.kind != skIterator:
localError(fn.info, "internal error: inconsistent environment type")
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
#echo "adding closure to ", fn.name.s
proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
@@ -395,7 +398,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
addClosureParam(c, owner, n.info)
if interestingIterVar(s):
if not c.capturedVars.containsOrIncl(s.id):
let obj = getHiddenParam(owner).typ.lastSon
let obj = getHiddenParam(c.graph, owner).typ.lastSon
#let obj = c.getEnvTypeForOwner(s.owner).lastSon
addField(obj, s)
# but always return because the rest of the proc is only relevant when
@@ -415,7 +418,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
"""
# mark 'owner' as taking a closure:
c.somethingToDo = true
markAsClosure(owner, n)
markAsClosure(c.graph, owner, n)
addClosureParam(c, owner, n.info)
#echo "capturing ", n.info
# variable 's' is actually captured:
@@ -440,7 +443,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
"""
let up = w.skipGenericOwner
#echo "up for ", w.name.s, " up ", up.name.s
markAsClosure(w, n)
markAsClosure(c.graph, w, n)
addClosureParam(c, w, n.info) # , ow
createUpField(c, w, up, n.info)
w = up
@@ -467,10 +470,10 @@ proc initLiftingPass(fn: PSym): LiftingPass =
result.processed.incl(fn.id)
result.envVars = initTable[int, PNode]()
proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
let s = n.sym
# Type based expression construction for simplicity:
let envParam = getHiddenParam(owner)
let envParam = getHiddenParam(g, owner)
if not envParam.isNil:
var access = newSymNode(envParam)
while true:
@@ -482,7 +485,7 @@ proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
let upField = lookupInRecord(obj.n, getIdent(upName))
if upField == nil: break
access = rawIndirectAccess(access, upField, n.info)
localError(n.info, "internal error: environment misses: " & s.name.s)
localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
result = n
proc newEnvVar(owner: PSym; typ: PType): PNode =
@@ -501,22 +504,22 @@ proc newEnvVar(owner: PSym; typ: PType): PNode =
proc setupEnvVar(owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
if owner.isIterator:
return getHiddenParam(owner).newSymNode
return getHiddenParam(d.graph, owner).newSymNode
result = c.envvars.getOrDefault(owner.id)
if result.isNil:
let envVarType = d.ownerToType.getOrDefault(owner.id)
if envVarType.isNil:
localError owner.info, "internal error: could not determine closure type"
localError d.graph.config, owner.info, "internal error: could not determine closure type"
result = newEnvVar(owner, envVarType)
c.envVars[owner.id] = result
proc getUpViaParam(owner: PSym): PNode =
let p = getHiddenParam(owner)
proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
let p = getHiddenParam(g, owner)
result = p.newSymNode
if owner.isIterator:
let upField = lookupInRecord(p.typ.lastSon.n, getIdent(upName))
if upField == nil:
localError(owner.info, "could not find up reference for closure iter")
localError(g.config, owner.info, "could not find up reference for closure iter")
else:
result = rawIndirectAccess(result, upField, p.info)
@@ -526,7 +529,7 @@ proc rawClosureCreation(owner: PSym;
var env: PNode
if owner.isIterator:
env = getHiddenParam(owner).newSymNode
env = getHiddenParam(d.graph, owner).newSymNode
else:
env = setupEnvVar(owner, d, c)
if env.kind == nkSym:
@@ -534,7 +537,7 @@ proc rawClosureCreation(owner: PSym;
addVar(v, env)
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym"internalNew", env))
result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
# add assignment statements for captured parameters:
for i in 1..<owner.typ.n.len:
let local = owner.typ.n[i].sym
@@ -545,7 +548,7 @@ proc rawClosureCreation(owner: PSym;
let upField = lookupInRecord(env.typ.lastSon.n, getIdent(upName))
if upField != nil:
let up = getUpViaParam(owner)
let up = getUpViaParam(d.graph, owner)
if up != nil and upField.typ == up.typ:
result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
up, env.info))
@@ -553,7 +556,7 @@ proc rawClosureCreation(owner: PSym;
# result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
# oldenv, env.info))
else:
localError(env.info, "internal error: cannot create up reference")
localError(d.graph.config, env.info, "internal error: cannot create up reference")
proc closureCreationForIter(iter: PNode;
d: DetectionPass; c: var LiftingPass): PNode =
@@ -561,10 +564,10 @@ proc closureCreationForIter(iter: PNode;
let owner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(envName), owner, iter.info)
incl(v.flags, sfShadowed)
v.typ = getHiddenParam(iter.sym).typ
v.typ = getHiddenParam(d.graph, iter.sym).typ
var vnode: PNode
if owner.isIterator:
let it = getHiddenParam(owner)
let it = getHiddenParam(d.graph, owner)
addUniqueField(it.typ.sons[0], v)
vnode = indirectAccess(newSymNode(it), v, v.info)
else:
@@ -572,7 +575,7 @@ proc closureCreationForIter(iter: PNode;
var vs = newNodeI(nkVarSection, iter.info)
addVar(vs, vnode)
result.add(vs)
result.add(newCall(getSysSym"internalNew", vnode))
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName))
if upField != nil:
@@ -581,8 +584,8 @@ proc closureCreationForIter(iter: PNode;
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
u, iter.info))
else:
localError(iter.info, "internal error: cannot create up reference for iter")
result.add makeClosure(iter.sym, vnode, iter.info)
localError(d.graph.config, iter.info, "internal error: cannot create up reference for iter")
result.add makeClosure(d.graph, iter.sym, vnode, iter.info)
proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
@@ -592,11 +595,11 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
if field != nil:
result = rawIndirectAccess(access, field, n.info)
else:
localError(n.info, "internal error: not part of closure object type")
localError(d.graph.config, n.info, "internal error: not part of closure object type")
result = n
proc getStateField(owner: PSym): PSym =
getHiddenParam(owner).typ.sons[0].n.sons[0].sym
proc getStateField(g: ModuleGraph; owner: PSym): PSym =
getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode
@@ -604,8 +607,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
if c.inContainer > 0:
localError(n.info, "invalid control flow: 'yield' within a constructor")
let state = getStateField(owner)
localError(d.graph.config, n.info, "invalid control flow: 'yield' within a constructor")
let state = getStateField(d.graph, owner)
assert state != nil
assert state.typ != nil
assert state.typ.n != nil
@@ -615,7 +618,8 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
var stateAsgnStmt = newNodeI(nkAsgn, n.info)
stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
state, n.info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo,
getSysType(d.graph, n.info, tyInt)))
var retStmt = newNodeI(nkReturnStmt, n.info)
if n.sons[0].kind != nkEmpty:
@@ -628,7 +632,8 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
retStmt.add(emptyNode)
var stateLabelStmt = newNodeI(nkState, n.info)
stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo,
getSysType(d.graph, n.info, tyInt)))
result = newNodeI(nkStmtList, n.info)
result.add(stateAsgnStmt)
@@ -637,16 +642,16 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
proc transformReturn(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
let state = getStateField(owner)
let state = getStateField(d.graph, owner)
result = newNodeI(nkStmtList, n.info)
var stateAsgnStmt = newNodeI(nkAsgn, n.info)
stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
state, n.info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(d.graph, n.info, tyInt)))
result.add(stateAsgnStmt)
result.add(n)
proc wrapIterBody(n: PNode; owner: PSym): PNode =
proc wrapIterBody(g: ModuleGraph; n: PNode; owner: PSym): PNode =
if not owner.isIterator: return n
when false:
# unfortunately control flow is still convoluted and we can end up
@@ -660,7 +665,7 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode =
let info = n.info
result = newNodeI(nkStmtList, info)
var gs = newNodeI(nkGotoState, info)
gs.add(rawIndirectAccess(newSymNode(owner.getHiddenParam), getStateField(owner), info))
gs.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)), getStateField(g, owner), info))
result.add(gs)
var state0 = newNodeI(nkState, info)
state0.add(newIntNode(nkIntLit, 0))
@@ -669,9 +674,9 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode =
result.add(n)
var stateAsgnStmt = newNodeI(nkAsgn, info)
stateAsgnStmt.add(rawIndirectAccess(newSymNode(owner.getHiddenParam),
getStateField(owner), info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
stateAsgnStmt.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)),
getStateField(g, owner), info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(g, info, tyInt)))
result.add(stateAsgnStmt)
proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
@@ -679,25 +684,25 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
let s = n.sym
if s == owner:
# recursive calls go through (lambda, hiddenParam):
let available = getHiddenParam(owner)
result = makeClosure(s, available.newSymNode, n.info)
let available = getHiddenParam(d.graph, owner)
result = makeClosure(d.graph, s, available.newSymNode, n.info)
elif s.isIterator:
result = closureCreationForIter(n, d, c)
elif s.skipGenericOwner == owner:
# direct dependency, so use the outer's env variable:
result = makeClosure(s, setupEnvVar(owner, d, c), n.info)
result = makeClosure(d.graph, s, setupEnvVar(owner, d, c), n.info)
else:
let available = getHiddenParam(owner)
let wanted = getHiddenParam(s).typ
let available = getHiddenParam(d.graph, owner)
let wanted = getHiddenParam(d.graph, s).typ
# ugh: call through some other inner proc;
var access = newSymNode(available)
while true:
if access.typ == wanted:
return makeClosure(s, access, n.info)
return makeClosure(d.graph, s, access, n.info)
let obj = access.typ.sons[0]
let upField = lookupInRecord(obj.n, getIdent(upName))
if upField == nil:
localError(n.info, "internal error: no environment found")
localError(d.graph.config, n.info, "internal error: no environment found")
return n
access = rawIndirectAccess(access, upField, n.info)
@@ -713,7 +718,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
# echo renderTree(s.getBody, {renderIds})
let oldInContainer = c.inContainer
c.inContainer = 0
let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s)
let body = wrapIterBody(d.graph, liftCapturedVars(s.getBody, s, d, c), s)
if c.envvars.getOrDefault(s.id).isNil:
s.ast.sons[bodyPos] = body
else:
@@ -723,9 +728,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
result = symToClosure(n, owner, d, c)
elif s.id in d.capturedVars:
if s.owner != owner:
result = accessViaEnvParam(n, owner)
result = accessViaEnvParam(d.graph, n, owner)
elif owner.isIterator and interestingIterVar(s):
result = accessViaEnvParam(n, owner)
result = accessViaEnvParam(d.graph, n, owner)
else:
result = accessViaEnvVar(n, owner, d, c)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
@@ -791,8 +796,8 @@ proc semCaptureSym*(s, owner: PSym) =
# since the analysis is not entirely correct, we don't set 'tfCapturesEnv'
# here
proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
var d = initDetectionPass(fn)
proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNode =
var d = initDetectionPass(g, fn)
var c = initLiftingPass(fn)
# pretend 'fn' is a closure iterator for the analysis:
let oldKind = fn.kind
@@ -801,11 +806,11 @@ proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
fn.typ.callConv = ccClosure
d.ownerToType[fn.id] = ptrType
detectCapturedVars(body, fn, d)
result = wrapIterBody(liftCapturedVars(body, fn, d, c), fn)
result = wrapIterBody(g, liftCapturedVars(body, fn, d, c), fn)
fn.kind = oldKind
fn.typ.callConv = oldCC
proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode =
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ...
@@ -819,7 +824,7 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
result = body
tooEarly = true
else:
var d = initDetectionPass(fn)
var d = initDetectionPass(g, fn)
detectCapturedVars(body, fn, d)
if not d.somethingToDo and fn.isIterator:
addClosureParam(d, fn, body.info)
@@ -829,7 +834,7 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
var newBody = liftCapturedVars(body, fn, d, c)
if c.envvars.getOrDefault(fn.id) != nil:
newBody = newTree(nkStmtList, rawClosureCreation(fn, d, c), newBody)
result = wrapIterBody(newBody, fn)
result = wrapIterBody(g, newBody, fn)
else:
result = body
#if fn.name.s == "get2":
@@ -845,7 +850,7 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
# ------------------- iterator transformation --------------------------------
proc liftForLoop*(body: PNode; owner: PSym): PNode =
proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
# problem ahead: the iterator could be invoked indirectly, but then
# we don't know what environment to create here:
#
@@ -876,7 +881,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
if liftingHarmful(owner): return body
var L = body.len
if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds):
localError(body.info, "ignored invalid for loop")
localError(g.config, body.info, "ignored invalid for loop")
return body
var call = body[L-2]
@@ -889,7 +894,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
# createClosure()
let iter = op.sym
let hp = getHiddenParam(iter)
let hp = getHiddenParam(g, iter)
env = newSym(skLet, iter.name, owner, body.info)
env.typ = hp.typ
env.flags = hp.flags
@@ -898,7 +903,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
addVar(v, newSymNode(env))
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym"internalNew", env.newSymNode))
result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
elif op.kind == nkStmtListExpr:
let closure = op.lastSon
if closure.kind == nkClosure:
@@ -908,7 +913,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
var loopBody = newNodeI(nkStmtList, body.info, 3)
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool))
whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(g, body.info, tyBool))
whileLoop.sons[1] = loopBody
result.add whileLoop
@@ -923,7 +928,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
addSon(vpart, ast.emptyNode) # no explicit type
if not env.isNil:
call.sons[0] = makeClosure(call.sons[0].sym, env.newSymNode, body.info)
call.sons[0] = makeClosure(g, call.sons[0].sym, env.newSymNode, body.info)
addSon(vpart, call)
addSon(v2, vpart)

View File

@@ -12,18 +12,18 @@
const
genPrefix* = ":tmp" # prefix for generated names
import ast, astalgo, types, idents, magicsys, msgs, options
import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs
from trees import getMagic
proc newDeref*(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc newTupleAccess*(tup: PNode, i: int): PNode =
proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
abstractInst).sons[i])
addSon(result, copyTree(tup))
var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt))
var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
lit.intVal = i
addSon(result, lit)
@@ -44,7 +44,7 @@ proc newFastAsgnStmt(le, ri: PNode): PNode =
result.sons[0] = le
result.sons[1] = ri
proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
assert n.kind == nkVarTuple
let value = n.lastSon
result = newNodeI(nkStmtList, n.info)
@@ -61,7 +61,7 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
result.add newAsgnStmt(tempAsNode, value)
for i in 0 .. n.len-3:
if n.sons[i].kind == nkSym: v.addVar(n.sons[i])
result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i))
result.add newAsgnStmt(n.sons[i], newTupleAccess(g, tempAsNode, i))
proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
result = newNodeI(nkBracketExpr, tup.info)
@@ -112,13 +112,13 @@ proc lowerSwap*(n: PNode; owner: PSym): PNode =
result.add newFastAsgnStmt(n[1], n[2])
result.add newFastAsgnStmt(n[2], tempAsNode)
proc createObj*(owner: PSym, info: TLineInfo; final=true): PType =
proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType =
result = newType(tyObject, owner)
if final:
rawAddSon(result, nil)
incl result.flags, tfFinal
else:
rawAddSon(result, getCompilerProc("RootObj").typ)
rawAddSon(result, getCompilerProc(g, "RootObj").typ)
result.n = newNodeI(nkRecList, info)
let s = newSym(skType, getIdent("Env_" & info.toFilename),
owner, info)
@@ -218,7 +218,7 @@ proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
#if field == nil:
# echo "FIELD ", b
# debug deref.typ
internalAssert field != nil
assert field != nil
addSon(deref, a)
result = newNodeI(nkDotExpr, info)
addSon(result, deref)
@@ -242,7 +242,7 @@ proc indirectAccess(a: PNode, b: string, info: TLineInfo): PNode =
#if field == nil:
# echo "FIELD ", b
# debug deref.typ
internalAssert field != nil
assert field != nil
addSon(deref, a)
result = newNodeI(nkDotExpr, info)
addSon(result, deref)
@@ -278,12 +278,12 @@ proc genDeref*(n: PNode): PNode =
n.typ.skipTypes(abstractInst).sons[0])
result.add n
proc callCodegenProc*(name: string, arg1: PNode;
proc callCodegenProc*(g: ModuleGraph; name: string, arg1: PNode;
arg2, arg3, optionalArgs: PNode = nil): PNode =
result = newNodeI(nkCall, arg1.info)
let sym = magicsys.getCompilerProc(name)
let sym = magicsys.getCompilerProc(g, name)
if sym == nil:
localError(arg1.info, errSystemNeeds, name)
localError(g.config, arg1.info, "system module needs: " & name)
else:
result.add newSymNode(sym)
result.add arg1
@@ -333,7 +333,7 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
result = not containsGarbageCollectedRef(t)
proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
v: PNode; useShallowCopy=false): PSym =
result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info)
result.typ = typ
@@ -349,7 +349,7 @@ proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
varInit.add newFastAsgnStmt(newSymNode(result), v)
else:
let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
deepCopyCall.sons[0] = newSymNode(getSysMagic("deepCopy", mDeepCopy))
deepCopyCall.sons[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))
deepCopyCall.sons[1] = newSymNode(result)
deepCopyCall.sons[2] = v
varInit.add deepCopyCall
@@ -384,23 +384,23 @@ stmtList:
"""
proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
varSection, varInit, call, barrier, fv: PNode;
spawnKind: TSpawnResult): PSym =
var body = newNodeI(nkStmtList, f.info)
var threadLocalBarrier: PSym
if barrier != nil:
var varSection2 = newNodeI(nkVarSection, barrier.info)
threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner,
threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,
barrier.typ, barrier)
body.add varSection2
body.add callCodegenProc("barrierEnter", threadLocalBarrier.newSymNode)
body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.newSymNode)
var threadLocalProm: PSym
if spawnKind == srByVar:
threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
elif fv != nil:
internalAssert fv.typ.kind == tyGenericInst
threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
internalAssert g.config, fv.typ.kind == tyGenericInst
threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
body.add varSection
body.add varInit
if fv != nil and spawnKind != srByVar:
@@ -409,30 +409,30 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
"owner", fv.info), threadParam.newSymNode)
body.add callCodegenProc("nimArgsPassingDone", threadParam.newSymNode)
body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.newSymNode)
if spawnKind == srByVar:
body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
elif fv != nil:
let fk = fv.typ.sons[1].flowVarKind
if fk == fvInvalid:
localError(f.info, "cannot create a flowVar of type: " &
localError(g.config, f.info, "cannot create a flowVar of type: " &
typeToString(fv.typ.sons[1]))
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
if fk == fvGC: "data" else: "blob", fv.info), call)
if fk == fvGC:
let incRefCall = newNodeI(nkCall, fv.info, 2)
incRefCall.sons[0] = newSymNode(getSysMagic("GCref", mGCref))
incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
"data", fv.info)
body.add incRefCall
if barrier == nil:
# by now 'fv' is shared and thus might have beeen overwritten! we need
# to use the thread-local view instead:
body.add callCodegenProc("nimFlowVarSignal", threadLocalProm.newSymNode)
body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.newSymNode)
else:
body.add call
if barrier != nil:
body.add callCodegenProc("barrierLeave", threadLocalBarrier.newSymNode)
body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.newSymNode)
var params = newNodeI(nkFormalParams, f.info)
params.add emptyNode
@@ -460,7 +460,7 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode =
result.typ = newType(tyPtr, objType.owner)
result.typ.rawAddSon(objType)
proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym,
castExpr, call,
varSection, varInit, result: PNode) =
let formals = n[0].typ.n
@@ -470,7 +470,7 @@ proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
# 'tyOpenArray':
var argType = n[i].typ.skipTypes(abstractInst)
if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}:
localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
#elif containsTyRef(argType):
# localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
@@ -480,7 +480,7 @@ proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
let temp = addLocalVar(varSection, varInit, objType.owner, argType,
let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,
indirectAccess(castExpr, field, n.info))
call.add(newSymNode(temp))
@@ -501,20 +501,20 @@ proc getRoot*(n: PNode): PSym =
if getMagic(n) == mSlice: result = getRoot(n.sons[1])
else: discard
proc newIntLit*(value: BiggestInt): PNode =
proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
result = nkIntLit.newIntNode(value)
result.typ = getSysType(tyInt)
result.typ = getSysType(g, info, tyInt)
proc genHigh*(n: PNode): PNode =
proc genHigh*(g: ModuleGraph; n: PNode): PNode =
if skipTypes(n.typ, abstractVar).kind == tyArray:
result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
result = newIntLit(g, n.info, lastOrd(skipTypes(n.typ, abstractVar)))
else:
result = newNodeI(nkCall, n.info, 2)
result.typ = getSysType(tyInt)
result.sons[0] = newSymNode(getSysMagic("high", mHigh))
result.typ = getSysType(g, n.info, tyInt)
result.sons[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
result.sons[1] = n
proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym;
castExpr, call,
varSection, varInit, result: PNode) =
let formals = n[0].typ.n
@@ -535,10 +535,10 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
# important special case: we always create a zero-copy slice:
let slice = newNodeI(nkCall, n.info, 4)
slice.typ = n.typ
slice.sons[0] = newSymNode(createMagic("slice", mSlice))
slice.sons[0].typ = getSysType(tyInt) # fake type
slice.sons[0] = newSymNode(createMagic(g, "slice", mSlice))
slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type
var fieldB = newSym(skField, tmpName, objType.owner, n.info)
fieldB.typ = getSysType(tyInt)
fieldB.typ = getSysType(g, n.info, tyInt)
objType.addField(fieldB)
if getMagic(n) == mSlice:
@@ -548,12 +548,12 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
var fieldA = newSym(skField, tmpName, objType.owner, n.info)
fieldA.typ = getSysType(tyInt)
fieldA.typ = getSysType(g, n.info, tyInt)
objType.addField(fieldA)
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldA.typ,
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ,
indirectAccess(castExpr, fieldA, n.info),
useShallowCopy=true)
slice.sons[2] = threadLocal.newSymNode
@@ -562,13 +562,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
field.typ = a.typ
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(n))
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
slice.sons[2] = newIntLit(0)
slice.sons[2] = newIntLit(g, n.info, 0)
# the array itself does not need to go through a thread local variable:
slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldB.typ,
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ,
indirectAccess(castExpr, fieldB, n.info),
useShallowCopy=true)
slice.sons[3] = threadLocal.newSymNode
@@ -580,7 +580,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
field.typ = a.typ
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
let threadLocal = addLocalVar(varSection,nil, objType.owner, field.typ,
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,
indirectAccess(castExpr, field, n.info),
useShallowCopy=true)
call.add(genDeref(threadLocal.newSymNode))
@@ -589,13 +589,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
field.typ = argType
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
let threadLocal = addLocalVar(varSection, varInit,
let threadLocal = addLocalVar(g, varSection, varInit,
objType.owner, field.typ,
indirectAccess(castExpr, field, n.info),
useShallowCopy=true)
call.add(threadLocal.newSymNode)
proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType;
barrier, dest: PNode = nil): PNode =
# if 'barrier' != nil, then it is in a 'parallel' section and we
# generate quite different code
@@ -603,31 +603,31 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
let spawnKind = spawnResult(retType, barrier!=nil)
case spawnKind
of srVoid:
internalAssert dest == nil
internalAssert g.config, dest == nil
result = newNodeI(nkStmtList, n.info)
of srFlowVar:
internalAssert dest == nil
internalAssert g.config, dest == nil
result = newNodeIT(nkStmtListExpr, n.info, retType)
of srByVar:
if dest == nil: localError(n.info, "'spawn' must not be discarded")
if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded")
result = newNodeI(nkStmtList, n.info)
if n.kind notin nkCallKinds:
localError(n.info, "'spawn' takes a call expression")
localError(g.config, n.info, "'spawn' takes a call expression")
return
if optThreadAnalysis in gGlobalOptions:
if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
localError(n.info, "'spawn' takes a GC safe call expression")
localError(g.config, n.info, "'spawn' takes a GC safe call expression")
var
threadParam = newSym(skParam, getIdent"thread", owner, n.info)
argsParam = newSym(skParam, getIdent"args", owner, n.info)
block:
let ptrType = getSysType(tyPointer)
let ptrType = getSysType(g, n.info, tyPointer)
threadParam.typ = ptrType
argsParam.typ = ptrType
argsParam.position = 1
var objType = createObj(owner, n.info)
var objType = createObj(g, owner, n.info)
incl(objType.flags, tfFinal)
let castExpr = createCastExpr(argsParam, objType)
@@ -644,7 +644,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
# templates and macros are in fact valid here due to the nature of
# the transformation:
if fn.kind == nkClosure:
localError(n.info, "closure in spawn environment is not allowed")
localError(g.config, n.info, "closure in spawn environment is not allowed")
if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
skFunc, skMethod, skConverter}):
# for indirect calls we pass the function pointer in the scratchObj
@@ -655,24 +655,24 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
fn = indirectAccess(castExpr, field, n.info)
elif fn.kind == nkSym and fn.sym.kind == skIterator:
localError(n.info, "iterator in spawn environment is not allowed")
localError(g.config, n.info, "iterator in spawn environment is not allowed")
elif fn.typ.callConv == ccClosure:
localError(n.info, "closure in spawn environment is not allowed")
localError(g.config, n.info, "closure in spawn environment is not allowed")
call.add(fn)
var varSection = newNodeI(nkVarSection, n.info)
var varInit = newNodeI(nkStmtList, n.info)
if barrier.isNil:
setupArgsForConcurrency(n, objType, scratchObj, castExpr, call,
setupArgsForConcurrency(g, n, objType, scratchObj, castExpr, call,
varSection, varInit, result)
else:
setupArgsForParallelism(n, objType, scratchObj, castExpr, call,
setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call,
varSection, varInit, result)
var barrierAsExpr: PNode = nil
if barrier != nil:
let typ = newType(tyPtr, owner)
typ.rawAddSon(magicsys.getCompilerProc("Barrier").typ)
typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
var field = newSym(skField, getIdent"barrier", owner, n.info)
field.typ = typ
objType.addField(field)
@@ -689,7 +689,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
# create flowVar:
result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
if barrier == nil:
result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField)
result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField)
elif spawnKind == srByVar:
var field = newSym(skField, getIdent"fv", owner, n.info)
@@ -699,10 +699,10 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
fvAsExpr = indirectAccess(castExpr, field, n.info)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
let wrapper = createWrapperProc(fn, threadParam, argsParam,
let wrapper = createWrapperProc(g, fn, threadParam, argsParam,
varSection, varInit, call,
barrierAsExpr, fvAsExpr, spawnKind)
result.add callCodegenProc("nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
if spawnKind == srFlowVar: result.add fvField