diff options
Diffstat (limited to 'compiler/spawn.nim')
-rw-r--r-- | compiler/spawn.nim | 229 |
1 files changed, 123 insertions, 106 deletions
diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 382237e70..58d5a4928 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -10,13 +10,13 @@ ## This module implements threadpool's ``spawn``. import ast, types, idents, magicsys, msgs, options, modulegraphs, - lowerings -from trees import getMagic + lowerings, liftdestructors, renderer +from trees import getMagic, getRoot proc callProc(a: PNode): PNode = result = newNodeI(nkCall, a.info) result.add a - result.typ = a.typ[0] + result.typ = a.typ.returnType # we have 4 cases to consider: # - a void proc --> nothing to do @@ -36,8 +36,9 @@ proc spawnResult*(t: PType; inParallel: bool): TSpawnResult = elif inParallel and not containsGarbageCollectedRef(t): srByVar else: srFlowVar -proc flowVarKind(t: PType): TFlowVarKind = - if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC +proc flowVarKind(c: ConfigRef, t: PType): TFlowVarKind = + if c.selectedGC in {gcArc, gcOrc, gcAtomicArc}: fvBlob + elif t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC elif containsGarbageCollectedRef(t): fvInvalid else: fvBlob @@ -49,12 +50,12 @@ proc typeNeedsNoDeepCopy(t: PType): bool = # note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var' # for the stricter check and likewise we can skip 'seq' for a less # strict check: - if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon + if t.kind in {tyVar, tyLent, tySequence}: t = t.elementType result = not containsGarbageCollectedRef(t) -proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType; +proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator; owner: PSym; typ: PType; v: PNode; useShallowCopy=false): PSym = - result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info, + result = newSym(skTemp, getIdent(g.cache, genPrefix), idgen, owner, varSection.info, owner.options) result.typ = typ incl(result.flags, sfFromGeneric) @@ -65,14 +66,18 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: P vpart[2] = if varInit.isNil: v else: vpart[1] varSection.add vpart if varInit != nil: - if useShallowCopy and typeNeedsNoDeepCopy(typ) or optTinyRtti in g.config.globalOptions: - varInit.add newFastAsgnStmt(newSymNode(result), v) + if g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: + # inject destructors pass will do its own analysis + varInit.add newFastMoveStmt(g, newSymNode(result), v) else: - let deepCopyCall = newNodeI(nkCall, varInit.info, 3) - deepCopyCall[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy)) - deepCopyCall[1] = newSymNode(result) - deepCopyCall[2] = v - varInit.add deepCopyCall + if useShallowCopy and typeNeedsNoDeepCopy(typ) or optTinyRtti in g.config.globalOptions: + varInit.add newFastMoveStmt(g, newSymNode(result), v) + else: + let deepCopyCall = newNodeI(nkCall, varInit.info, 3) + deepCopyCall[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy)) + deepCopyCall[1] = newSymNode(result) + deepCopyCall[2] = v + varInit.add deepCopyCall discard """ We generate roughly this: @@ -104,26 +109,35 @@ stmtList: """ +proc castToVoidPointer(g: ModuleGraph, n: PNode, fvField: PNode): PNode = + if g.config.backend == backendCpp: + result = fvField + else: + let ptrType = getSysType(g, n.info, tyPointer) + result = newNodeI(nkCast, fvField.info) + result.add newNodeI(nkEmpty, fvField.info) + result.add fvField + result.typ = ptrType + proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; varSection, varInit, call, barrier, fv: PNode; - spawnKind: TSpawnResult): PSym = + idgen: IdGenerator; + spawnKind: TSpawnResult, result: PSym) = var body = newNodeI(nkStmtList, f.info) - body.flags.incl nfTransf # do not transform further - - var threadLocalBarrier: PSym + var threadLocalBarrier: PSym = nil if barrier != nil: var varSection2 = newNodeI(nkVarSection, barrier.info) - threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner, + threadLocalBarrier = addLocalVar(g, varSection2, nil, idgen, result, barrier.typ, barrier) body.add varSection2 body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info, threadLocalBarrier.newSymNode) - var threadLocalProm: PSym + var threadLocalProm: PSym = nil if spawnKind == srByVar: - threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv) + threadLocalProm = addLocalVar(g, varSection, nil, idgen, result, fv.typ, fv) elif fv != nil: internalAssert g.config, fv.typ.kind == tyGenericInst - threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv) + threadLocalProm = addLocalVar(g, varSection, nil, idgen, result, fv.typ, fv) body.add varSection body.add varInit if fv != nil and spawnKind != srByVar: @@ -137,10 +151,10 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; if spawnKind == srByVar: body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call) elif fv != nil: - let fk = fv.typ[1].flowVarKind + let fk = flowVarKind(g.config, fv.typ.firstGenericParam) if fk == fvInvalid: localError(g.config, f.info, "cannot create a flowVar of type: " & - typeToString(fv.typ[1])) + typeToString(fv.typ.firstGenericParam)) body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, if fk == fvGC: "data" else: "blob", fv.info, g.cache), call) if fk == fvGC: @@ -152,8 +166,9 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; if barrier == nil: # by now 'fv' is shared and thus might have beeen overwritten! we need # to use the thread-local view instead: + let castExpr = castToVoidPointer(g, f, threadLocalProm.newSymNode) body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info, - threadLocalProm.newSymNode) + castExpr) else: body.add call if barrier != nil: @@ -165,7 +180,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; params.add threadParam.newSymNode params.add argsParam.newSymNode - var t = newType(tyProc, threadParam.owner) + var t = newType(tyProc, idgen, threadParam.owner) t.rawAddSon nil t.rawAddSon threadParam.typ t.rawAddSon argsParam.typ @@ -174,9 +189,6 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; t.n.add threadParam.newSymNode t.n.add argsParam.newSymNode - let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper" - result = newSym(skProc, getIdent(g.cache, name), argsParam.owner, f.info, - argsParam.options) let emptyNode = newNodeI(nkEmpty, f.info) result.ast = newProcNode(nkProcDef, f.info, body = body, params = params, name = newSymNode(result), pattern = emptyNode, @@ -184,14 +196,20 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; exceptions = emptyNode) result.typ = t -proc createCastExpr(argsParam: PSym; objType: PType): PNode = +proc createCastExpr(argsParam: PSym; objType: PType; idgen: IdGenerator): PNode = result = newNodeI(nkCast, argsParam.info) result.add newNodeI(nkEmpty, argsParam.info) result.add newSymNode(argsParam) - result.typ = newType(tyPtr, objType.owner) + result.typ = newType(tyPtr, idgen, objType.owner) result.typ.rawAddSon(objType) -proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym, +template checkMagicProcs(g: ModuleGraph, n: PNode, formal: PNode) = + if (formal.typ.kind == tyVarargs and formal.typ.elementType.kind in {tyTyped, tyUntyped}) or + formal.typ.kind in {tyTyped, tyUntyped}: + localError(g.config, n.info, "'spawn'ed function cannot have a 'typed' or 'untyped' parameter") + +proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; + idgen: IdGenerator; owner: PSym; scratchObj: PSym, castExpr, call, varSection, varInit, result: PNode) = let formals = n[0].typ.n @@ -200,39 +218,30 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchOb # we pick n's type here, which hopefully is 'tyArray' and not # 'tyOpenArray': var argType = n[i].typ.skipTypes(abstractInst) - if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}: - localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter") + if i < formals.len: + if formals[i].typ.kind in {tyVar, tyLent}: + localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter") + + checkMagicProcs(g, n[i], formals[i]) + + if formals[i].typ.kind in {tyTypeDesc, tyStatic}: + continue #elif containsTyRef(argType): # localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure") let fieldname = if i < formals.len: formals[i].sym.name else: tmpName - var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options) + var field = newSym(skField, fieldname, idgen, objType.owner, n.info, g.config.options) field.typ = argType - objType.addField(field, g.cache) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i]) - let temp = addLocalVar(g, varSection, varInit, objType.owner, argType, + let temp = addLocalVar(g, varSection, varInit, idgen, owner, argType, indirectAccess(castExpr, field, n.info)) call.add(newSymNode(temp)) -proc getRoot*(n: PNode): PSym = - ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression - ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be - ## determined as the owner; ``obj`` in the example. - case n.kind - of nkSym: - if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}: - result = n.sym - of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr, - nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: - result = getRoot(n[0]) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: - result = getRoot(n[1]) - of nkCallKinds: - if getMagic(n) == mSlice: result = getRoot(n[1]) - else: discard - -proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym; +proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; + idgen: IdGenerator; + owner: PSym; scratchObj: PSym; castExpr, call, varSection, varInit, result: PNode) = let formals = n[0].typ.n @@ -241,44 +250,49 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb # for correctness: These are called 'threadLocal' here. for i in 1..<n.len: let n = n[i] + if i < formals.len and formals[i].typ.kind in {tyStatic, tyTypeDesc}: + continue + + checkMagicProcs(g, n, formals[i]) + let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ, abstractInst) #if containsTyRef(argType): # localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure") let fieldname = if i < formals.len: formals[i].sym.name else: tmpName - var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options) + var field = newSym(skField, fieldname, idgen, objType.owner, n.info, g.config.options) if argType.kind in {tyVarargs, tyOpenArray}: # important special case: we always create a zero-copy slice: let slice = newNodeI(nkCall, n.info, 4) slice.typ = n.typ - slice[0] = newSymNode(createMagic(g, "slice", mSlice)) + slice[0] = newSymNode(createMagic(g, idgen, "slice", mSlice)) slice[0].typ = getSysType(g, n.info, tyInt) # fake type - var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options) + var fieldB = newSym(skField, tmpName, idgen, objType.owner, n.info, g.config.options) fieldB.typ = getSysType(g, n.info, tyInt) - objType.addField(fieldB, g.cache) + discard objType.addField(fieldB, g.cache, idgen) if getMagic(n) == mSlice: - let a = genAddrOf(n[1]) + let a = genAddrOf(n[1], idgen) field.typ = a.typ - objType.addField(field, g.cache) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) - var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options) + var fieldA = newSym(skField, tmpName, idgen, objType.owner, n.info, g.config.options) fieldA.typ = getSysType(g, n.info, tyInt) - objType.addField(fieldA, g.cache) + discard objType.addField(fieldA, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2]) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3]) - let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ, + let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, fieldA.typ, indirectAccess(castExpr, fieldA, n.info), useShallowCopy=true) slice[2] = threadLocal.newSymNode else: - let a = genAddrOf(n) + let a = genAddrOf(n, idgen) field.typ = a.typ - objType.addField(field, g.cache) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n)) @@ -286,7 +300,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb # the array itself does not need to go through a thread local variable: slice[1] = genDeref(indirectAccess(castExpr, field, n.info)) - let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ, + let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, fieldB.typ, indirectAccess(castExpr, fieldB, n.info), useShallowCopy=true) slice[3] = threadLocal.newSymNode @@ -294,27 +308,27 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and n.getRoot != nil: # it is more efficient to pass a pointer instead: - let a = genAddrOf(n) + let a = genAddrOf(n, idgen) field.typ = a.typ - objType.addField(field, g.cache) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) - let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ, + let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, field.typ, indirectAccess(castExpr, field, n.info), useShallowCopy=true) call.add(genDeref(threadLocal.newSymNode)) else: # boring case field.typ = argType - objType.addField(field, g.cache) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n) let threadLocal = addLocalVar(g, varSection, varInit, - objType.owner, field.typ, + idgen, owner, field.typ, indirectAccess(castExpr, field, n.info), useShallowCopy=true) call.add(threadLocal.newSymNode) -proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType; - barrier, dest: PNode = nil): PNode = +proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExpr: PNode; retType: PType; + barrier: PNode = nil, dest: PNode = nil): PNode = # if 'barrier' != nil, then it is in a 'parallel' section and we # generate quite different code let n = spawnExpr[^2] @@ -331,25 +345,31 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P result = newNodeI(nkStmtList, n.info) if n.kind notin nkCallKinds: - localError(g.config, n.info, "'spawn' takes a call expression") + localError(g.config, n.info, "'spawn' takes a call expression; got: " & $n) return if optThreadAnalysis in g.config.globalOptions: if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}: localError(g.config, n.info, "'spawn' takes a GC safe call expression") - var - threadParam = newSym(skParam, getIdent(g.cache, "thread"), owner, n.info, g.config.options) - argsParam = newSym(skParam, getIdent(g.cache, "args"), owner, n.info, g.config.options) + + var fn = n[0] + let + name = (if fn.kind == nkSym: fn.sym.name.s else: genPrefix) & "Wrapper" + wrapperProc = newSym(skProc, getIdent(g.cache, name), idgen, owner, fn.info, g.config.options) + threadParam = newSym(skParam, getIdent(g.cache, "thread"), idgen, wrapperProc, n.info, g.config.options) + argsParam = newSym(skParam, getIdent(g.cache, "args"), idgen, wrapperProc, n.info, g.config.options) + + wrapperProc.flags.incl sfInjectDestructors block: let ptrType = getSysType(g, n.info, tyPointer) threadParam.typ = ptrType argsParam.typ = ptrType argsParam.position = 1 - var objType = createObj(g, owner, n.info) + var objType = createObj(g, idgen, owner, n.info) incl(objType.flags, tfFinal) - let castExpr = createCastExpr(argsParam, objType) + let castExpr = createCastExpr(argsParam, objType, idgen) - var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options) + var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), idgen, owner, n.info, g.config.options) block: scratchObj.typ = objType incl(scratchObj.flags, sfFromGeneric) @@ -358,7 +378,6 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P result.add varSectionB var call = newNodeIT(nkCall, n.info, n.typ) - var fn = n[0] # templates and macros are in fact valid here due to the nature of # the transformation: if fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure): @@ -367,62 +386,60 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P skFunc, skMethod, skConverter}): # for indirect calls we pass the function pointer in the scratchObj var argType = n[0].typ.skipTypes(abstractInst) - var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options) + var field = newSym(skField, getIdent(g.cache, "fn"), idgen, owner, n.info, g.config.options) field.typ = argType - objType.addField(field, g.cache) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0]) fn = indirectAccess(castExpr, field, n.info) elif fn.kind == nkSym and fn.sym.kind == skIterator: localError(g.config, n.info, "iterator in spawn environment is not allowed") - elif fn.typ.callConv == ccClosure: - 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(g, n, objType, scratchObj, castExpr, call, + setupArgsForConcurrency(g, n, objType, idgen, wrapperProc, scratchObj, castExpr, call, varSection, varInit, result) else: - setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call, + setupArgsForParallelism(g, n, objType, idgen, wrapperProc, scratchObj, castExpr, call, varSection, varInit, result) var barrierAsExpr: PNode = nil if barrier != nil: - let typ = newType(tyPtr, owner) + let typ = newType(tyPtr, idgen, owner) typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ) - var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options) + var field = newSym(skField, getIdent(g.cache, "barrier"), idgen, owner, n.info, g.config.options) field.typ = typ - objType.addField(field, g.cache) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier) barrierAsExpr = indirectAccess(castExpr, field, n.info) var fvField, fvAsExpr: PNode = nil if spawnKind == srFlowVar: - var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options) + var field = newSym(skField, getIdent(g.cache, "fv"), idgen, owner, n.info, g.config.options) field.typ = retType - objType.addField(field, g.cache) + discard objType.addField(field, g.cache, idgen) fvField = newDotExpr(scratchObj, field) fvAsExpr = indirectAccess(castExpr, field, n.info) # create flowVar: result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1])) if barrier == nil: - result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, - fvField) + let castExpr = castToVoidPointer(g, n, fvField) + result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, castExpr) elif spawnKind == srByVar: - var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options) - field.typ = newType(tyPtr, objType.owner) + var field = newSym(skField, getIdent(g.cache, "fv"), idgen, owner, n.info, g.config.options) + field.typ = newType(tyPtr, idgen, objType.owner) field.typ.rawAddSon(retType) - objType.addField(field, g.cache) + discard objType.addField(field, g.cache, idgen) fvAsExpr = indirectAccess(castExpr, field, n.info) - result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest)) + result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest, idgen)) - let wrapper = createWrapperProc(g, fn, threadParam, argsParam, - varSection, varInit, call, - barrierAsExpr, fvAsExpr, spawnKind) - result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.info, - wrapper.newSymNode, genAddrOf(scratchObj.newSymNode), nil, spawnExpr) + createTypeBoundOps(g, nil, objType, n.info, idgen) + createWrapperProc(g, fn, threadParam, argsParam, + varSection, varInit, call, + barrierAsExpr, fvAsExpr, idgen, spawnKind, wrapperProc) + result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapperProc.info, + wrapperProc.newSymNode, genAddrOf(scratchObj.newSymNode, idgen), nil, spawnExpr) if spawnKind == srFlowVar: result.add fvField - |