lowerings.nim 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements common simple lowerings.
  10. const
  11. genPrefix* = ":tmp" # prefix for generated names
  12. import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
  13. lineinfos
  14. from trees import getMagic
  15. proc newDeref*(n: PNode): PNode {.inline.} =
  16. result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
  17. addSon(result, n)
  18. proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
  19. if tup.kind == nkHiddenAddr:
  20. result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar}))
  21. result.addSon(newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar}).sons[i]))
  22. addSon(result[0], tup[0])
  23. var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
  24. lit.intVal = i
  25. addSon(result[0], lit)
  26. else:
  27. result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
  28. abstractInst).sons[i])
  29. addSon(result, copyTree(tup))
  30. var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
  31. lit.intVal = i
  32. addSon(result, lit)
  33. proc addVar*(father, v: PNode) =
  34. var vpart = newNodeI(nkIdentDefs, v.info, 3)
  35. vpart.sons[0] = v
  36. vpart.sons[1] = newNodeI(nkEmpty, v.info)
  37. vpart.sons[2] = vpart[1]
  38. addSon(father, vpart)
  39. proc newAsgnStmt(le, ri: PNode): PNode =
  40. result = newNodeI(nkAsgn, le.info, 2)
  41. result.sons[0] = le
  42. result.sons[1] = ri
  43. proc newFastAsgnStmt(le, ri: PNode): PNode =
  44. result = newNodeI(nkFastAsgn, le.info, 2)
  45. result.sons[0] = le
  46. result.sons[1] = ri
  47. proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
  48. assert n.kind == nkVarTuple
  49. let value = n.lastSon
  50. result = newNodeI(nkStmtList, n.info)
  51. var temp = newSym(skTemp, getIdent(g.cache, genPrefix), owner, value.info, g.config.options)
  52. temp.typ = skipTypes(value.typ, abstractInst)
  53. incl(temp.flags, sfFromGeneric)
  54. var v = newNodeI(nkVarSection, value.info)
  55. let tempAsNode = newSymNode(temp)
  56. v.addVar(tempAsNode)
  57. result.add(v)
  58. result.add newAsgnStmt(tempAsNode, value)
  59. for i in 0 .. n.len-3:
  60. if n.sons[i].kind == nkSym: v.addVar(n.sons[i])
  61. result.add newAsgnStmt(n.sons[i], newTupleAccess(g, tempAsNode, i))
  62. proc evalOnce*(g: ModuleGraph; value: PNode; owner: PSym): PNode =
  63. ## Turns (value) into (let tmp = value; tmp) so that 'value' can be re-used
  64. ## freely, multiple times. This is frequently required and such a builtin would also be
  65. ## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
  66. result = newNodeIT(nkStmtListExpr, value.info, value.typ)
  67. var temp = newSym(skTemp, getIdent(g.cache, genPrefix), owner, value.info, g.config.options)
  68. temp.typ = skipTypes(value.typ, abstractInst)
  69. incl(temp.flags, sfFromGeneric)
  70. var v = newNodeI(nkLetSection, value.info)
  71. let tempAsNode = newSymNode(temp)
  72. v.addVar(tempAsNode)
  73. result.add(v)
  74. result.add newAsgnStmt(tempAsNode, value)
  75. result.add tempAsNode
  76. proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
  77. result = newNodeI(nkBracketExpr, tup.info)
  78. addSon(result, copyTree(tup))
  79. var lit = newNodeI(nkIntLit, tup.info)
  80. lit.intVal = i
  81. addSon(result, lit)
  82. proc newTryFinally*(body, final: PNode): PNode =
  83. result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
  84. proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
  85. let value = n.lastSon
  86. result = newNodeI(nkStmtList, n.info)
  87. var temp = newSym(skTemp, getIdent(g.cache, "_"), owner, value.info, owner.options)
  88. var v = newNodeI(nkLetSection, value.info)
  89. let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
  90. var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
  91. vpart.sons[0] = tempAsNode
  92. vpart.sons[1] = newNodeI(nkEmpty, value.info)
  93. vpart.sons[2] = value
  94. addSon(v, vpart)
  95. result.add(v)
  96. let lhs = n.sons[0]
  97. for i in 0 .. lhs.len-1:
  98. result.add newAsgnStmt(lhs.sons[i], newTupleAccessRaw(tempAsNode, i))
  99. proc lowerSwap*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
  100. result = newNodeI(nkStmtList, n.info)
  101. # note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
  102. var temp = newSym(skVar, getIdent(g.cache, genPrefix), owner, n.info, owner.options)
  103. temp.typ = n.sons[1].typ
  104. incl(temp.flags, sfFromGeneric)
  105. var v = newNodeI(nkVarSection, n.info)
  106. let tempAsNode = newSymNode(temp)
  107. var vpart = newNodeI(nkIdentDefs, v.info, 3)
  108. vpart.sons[0] = tempAsNode
  109. vpart.sons[1] = newNodeI(nkEmpty, v.info)
  110. vpart.sons[2] = n[1]
  111. addSon(v, vpart)
  112. result.add(v)
  113. result.add newFastAsgnStmt(n[1], n[2])
  114. result.add newFastAsgnStmt(n[2], tempAsNode)
  115. proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType =
  116. result = newType(tyObject, owner)
  117. if final:
  118. rawAddSon(result, nil)
  119. incl result.flags, tfFinal
  120. else:
  121. rawAddSon(result, getCompilerProc(g, "RootObj").typ)
  122. result.n = newNodeI(nkRecList, info)
  123. let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info)),
  124. owner, info, owner.options)
  125. incl s.flags, sfAnon
  126. s.typ = result
  127. result.sym = s
  128. proc rawAddField*(obj: PType; field: PSym) =
  129. assert field.kind == skField
  130. field.position = sonsLen(obj.n)
  131. addSon(obj.n, newSymNode(field))
  132. propagateToOwner(obj, field.typ)
  133. proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
  134. # returns a[].field as a node
  135. assert field.kind == skField
  136. var deref = newNodeI(nkHiddenDeref, info)
  137. deref.typ = a.typ.skipTypes(abstractInst).sons[0]
  138. addSon(deref, a)
  139. result = newNodeI(nkDotExpr, info)
  140. addSon(result, deref)
  141. addSon(result, newSymNode(field))
  142. result.typ = field.typ
  143. proc rawDirectAccess*(obj, field: PSym): PNode =
  144. # returns a.field as a node
  145. assert field.kind == skField
  146. result = newNodeI(nkDotExpr, field.info)
  147. addSon(result, newSymNode obj)
  148. addSon(result, newSymNode field)
  149. result.typ = field.typ
  150. proc lookupInRecord(n: PNode, id: int): PSym =
  151. result = nil
  152. case n.kind
  153. of nkRecList:
  154. for i in 0 ..< sonsLen(n):
  155. result = lookupInRecord(n.sons[i], id)
  156. if result != nil: return
  157. of nkRecCase:
  158. if n.sons[0].kind != nkSym: return
  159. result = lookupInRecord(n.sons[0], id)
  160. if result != nil: return
  161. for i in 1 ..< sonsLen(n):
  162. case n.sons[i].kind
  163. of nkOfBranch, nkElse:
  164. result = lookupInRecord(lastSon(n.sons[i]), id)
  165. if result != nil: return
  166. else: discard
  167. of nkSym:
  168. if n.sym.id == -abs(id): result = n.sym
  169. else: discard
  170. proc addField*(obj: PType; s: PSym; cache: IdentCache) =
  171. # because of 'gensym' support, we have to mangle the name with its ID.
  172. # This is hacky but the clean solution is much more complex than it looks.
  173. var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info,
  174. s.options)
  175. field.id = -s.id
  176. let t = skipIntLit(s.typ)
  177. field.typ = t
  178. assert t.kind != tyTyped
  179. propagateToOwner(obj, t)
  180. field.position = sonsLen(obj.n)
  181. addSon(obj.n, newSymNode(field))
  182. proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache): PSym {.discardable.} =
  183. result = lookupInRecord(obj.n, s.id)
  184. if result == nil:
  185. var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info,
  186. s.options)
  187. field.id = -s.id
  188. let t = skipIntLit(s.typ)
  189. field.typ = t
  190. assert t.kind != tyTyped
  191. propagateToOwner(obj, t)
  192. field.position = sonsLen(obj.n)
  193. addSon(obj.n, newSymNode(field))
  194. result = field
  195. proc newDotExpr(obj, b: PSym): PNode =
  196. result = newNodeI(nkDotExpr, obj.info)
  197. let field = lookupInRecord(obj.typ.n, b.id)
  198. assert field != nil, b.name.s
  199. addSon(result, newSymNode(obj))
  200. addSon(result, newSymNode(field))
  201. result.typ = field.typ
  202. proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
  203. # returns a[].b as a node
  204. var deref = newNodeI(nkHiddenDeref, info)
  205. deref.typ = a.typ.skipTypes(abstractInst).sons[0]
  206. var t = deref.typ.skipTypes(abstractInst)
  207. var field: PSym
  208. while true:
  209. assert t.kind == tyObject
  210. field = lookupInRecord(t.n, b)
  211. if field != nil: break
  212. t = t.sons[0]
  213. if t == nil: break
  214. t = t.skipTypes(skipPtrs)
  215. #if field == nil:
  216. # echo "FIELD ", b
  217. # debug deref.typ
  218. assert field != nil
  219. addSon(deref, a)
  220. result = newNodeI(nkDotExpr, info)
  221. addSon(result, deref)
  222. addSon(result, newSymNode(field))
  223. result.typ = field.typ
  224. proc indirectAccess(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
  225. # returns a[].b as a node
  226. var deref = newNodeI(nkHiddenDeref, info)
  227. deref.typ = a.typ.skipTypes(abstractInst).sons[0]
  228. var t = deref.typ.skipTypes(abstractInst)
  229. var field: PSym
  230. let bb = getIdent(cache, b)
  231. while true:
  232. assert t.kind == tyObject
  233. field = getSymFromList(t.n, bb)
  234. if field != nil: break
  235. t = t.sons[0]
  236. if t == nil: break
  237. t = t.skipTypes(skipPtrs)
  238. #if field == nil:
  239. # echo "FIELD ", b
  240. # debug deref.typ
  241. assert field != nil
  242. addSon(deref, a)
  243. result = newNodeI(nkDotExpr, info)
  244. addSon(result, deref)
  245. addSon(result, newSymNode(field))
  246. result.typ = field.typ
  247. proc getFieldFromObj*(t: PType; v: PSym): PSym =
  248. assert v.kind != skField
  249. var t = t
  250. while true:
  251. assert t.kind == tyObject
  252. result = lookupInRecord(t.n, v.id)
  253. if result != nil: break
  254. t = t.sons[0]
  255. if t == nil: break
  256. t = t.skipTypes(skipPtrs)
  257. proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
  258. # returns a[].b as a node
  259. result = indirectAccess(a, b.id, info)
  260. proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
  261. result = indirectAccess(newSymNode(a), b, info)
  262. proc genAddrOf*(n: PNode): PNode =
  263. result = newNodeI(nkAddr, n.info, 1)
  264. result.sons[0] = n
  265. result.typ = newType(tyPtr, n.typ.owner)
  266. result.typ.rawAddSon(n.typ)
  267. proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
  268. result = newNodeIT(k, n.info,
  269. n.typ.skipTypes(abstractInst).sons[0])
  270. result.add n
  271. proc callCodegenProc*(g: ModuleGraph; name: string;
  272. info: TLineInfo = unknownLineInfo();
  273. arg1, arg2, arg3, optionalArgs: PNode = nil): PNode =
  274. result = newNodeI(nkCall, info)
  275. let sym = magicsys.getCompilerProc(g, name)
  276. if sym == nil:
  277. localError(g.config, info, "system module needs: " & name)
  278. else:
  279. result.add newSymNode(sym)
  280. if arg1 != nil: result.add arg1
  281. if arg2 != nil: result.add arg2
  282. if arg3 != nil: result.add arg3
  283. if optionalArgs != nil:
  284. for i in 1..optionalArgs.len-3:
  285. result.add optionalArgs[i]
  286. result.typ = sym.typ.sons[0]
  287. proc callProc(a: PNode): PNode =
  288. result = newNodeI(nkCall, a.info)
  289. result.add a
  290. result.typ = a.typ.sons[0]
  291. # we have 4 cases to consider:
  292. # - a void proc --> nothing to do
  293. # - a proc returning GC'ed memory --> requires a flowVar
  294. # - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
  295. # - not in a parallel environment --> requires a flowVar for memory safety
  296. type
  297. TSpawnResult* = enum
  298. srVoid, srFlowVar, srByVar
  299. TFlowVarKind = enum
  300. fvInvalid # invalid type T for 'FlowVar[T]'
  301. fvGC # FlowVar of a GC'ed type
  302. fvBlob # FlowVar of a blob type
  303. proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
  304. if t.isEmptyType: srVoid
  305. elif inParallel and not containsGarbageCollectedRef(t): srByVar
  306. else: srFlowVar
  307. proc flowVarKind(t: PType): TFlowVarKind =
  308. if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
  309. elif containsGarbageCollectedRef(t): fvInvalid
  310. else: fvBlob
  311. proc typeNeedsNoDeepCopy(t: PType): bool =
  312. var t = t.skipTypes(abstractInst)
  313. # for the tconvexhull example (and others) we're a bit lax here and pretend
  314. # seqs and strings are *by value* only and 'shallow' doesn't exist!
  315. if t.kind == tyString: return true
  316. # note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
  317. # for the stricter check and likewise we can skip 'seq' for a less
  318. # strict check:
  319. if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
  320. result = not containsGarbageCollectedRef(t)
  321. proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym =
  322. result = newSym(skLet, name, owner, varSection.info, owner.options)
  323. result.flags.incl sfHoisted
  324. result.typ = expr.typ
  325. var varDef = newNodeI(nkIdentDefs, varSection.info, 3)
  326. varDef.sons[0] = newSymNode(result)
  327. varDef.sons[1] = newNodeI(nkEmpty, varSection.info)
  328. varDef.sons[2] = expr
  329. varSection.add varDef
  330. proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
  331. v: PNode; useShallowCopy=false): PSym =
  332. result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
  333. owner.options)
  334. result.typ = typ
  335. incl(result.flags, sfFromGeneric)
  336. var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
  337. vpart.sons[0] = newSymNode(result)
  338. vpart.sons[1] = newNodeI(nkEmpty, varSection.info)
  339. vpart.sons[2] = if varInit.isNil: v else: vpart[1]
  340. varSection.add vpart
  341. if varInit != nil:
  342. if useShallowCopy and typeNeedsNoDeepCopy(typ):
  343. varInit.add newFastAsgnStmt(newSymNode(result), v)
  344. else:
  345. let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
  346. deepCopyCall.sons[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))
  347. deepCopyCall.sons[1] = newSymNode(result)
  348. deepCopyCall.sons[2] = v
  349. varInit.add deepCopyCall
  350. discard """
  351. We generate roughly this:
  352. proc f_wrapper(thread, args) =
  353. barrierEnter(args.barrier) # for parallel statement
  354. var a = args.a # thread transfer; deepCopy or shallowCopy or no copy
  355. # depending on whether we're in a 'parallel' statement
  356. var b = args.b
  357. var fv = args.fv
  358. fv.owner = thread # optional
  359. nimArgsPassingDone() # signal parent that the work is done
  360. #
  361. args.fv.blob = f(a, b, ...)
  362. nimFlowVarSignal(args.fv)
  363. # - or -
  364. f(a, b, ...)
  365. barrierLeave(args.barrier) # for parallel statement
  366. stmtList:
  367. var scratchObj
  368. scratchObj.a = a
  369. scratchObj.b = b
  370. nimSpawn(f_wrapper, addr scratchObj)
  371. scratchObj.fv # optional
  372. """
  373. proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
  374. varSection, varInit, call, barrier, fv: PNode;
  375. spawnKind: TSpawnResult): PSym =
  376. var body = newNodeI(nkStmtList, f.info)
  377. body.flags.incl nfTransf # do not transform further
  378. var threadLocalBarrier: PSym
  379. if barrier != nil:
  380. var varSection2 = newNodeI(nkVarSection, barrier.info)
  381. threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,
  382. barrier.typ, barrier)
  383. body.add varSection2
  384. body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info,
  385. threadLocalBarrier.newSymNode)
  386. var threadLocalProm: PSym
  387. if spawnKind == srByVar:
  388. threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
  389. elif fv != nil:
  390. internalAssert g.config, fv.typ.kind == tyGenericInst
  391. threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
  392. body.add varSection
  393. body.add varInit
  394. if fv != nil and spawnKind != srByVar:
  395. # generate:
  396. # fv.owner = threadParam
  397. body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
  398. "owner", fv.info, g.cache), threadParam.newSymNode)
  399. body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.info,
  400. threadParam.newSymNode)
  401. if spawnKind == srByVar:
  402. body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
  403. elif fv != nil:
  404. let fk = fv.typ.sons[1].flowVarKind
  405. if fk == fvInvalid:
  406. localError(g.config, f.info, "cannot create a flowVar of type: " &
  407. typeToString(fv.typ.sons[1]))
  408. body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
  409. if fk == fvGC: "data" else: "blob", fv.info, g.cache), call)
  410. if fk == fvGC:
  411. let incRefCall = newNodeI(nkCall, fv.info, 2)
  412. incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
  413. incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
  414. "data", fv.info, g.cache)
  415. body.add incRefCall
  416. if barrier == nil:
  417. # by now 'fv' is shared and thus might have beeen overwritten! we need
  418. # to use the thread-local view instead:
  419. body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info,
  420. threadLocalProm.newSymNode)
  421. else:
  422. body.add call
  423. if barrier != nil:
  424. body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.info,
  425. threadLocalBarrier.newSymNode)
  426. var params = newNodeI(nkFormalParams, f.info)
  427. params.add newNodeI(nkEmpty, f.info)
  428. params.add threadParam.newSymNode
  429. params.add argsParam.newSymNode
  430. var t = newType(tyProc, threadParam.owner)
  431. t.rawAddSon nil
  432. t.rawAddSon threadParam.typ
  433. t.rawAddSon argsParam.typ
  434. t.n = newNodeI(nkFormalParams, f.info)
  435. t.n.add newNodeI(nkEffectList, f.info)
  436. t.n.add threadParam.newSymNode
  437. t.n.add argsParam.newSymNode
  438. let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
  439. result = newSym(skProc, getIdent(g.cache, name), argsParam.owner, f.info,
  440. argsParam.options)
  441. let emptyNode = newNodeI(nkEmpty, f.info)
  442. result.ast = newProcNode(nkProcDef, f.info, body = body,
  443. params = params, name = newSymNode(result), pattern = emptyNode,
  444. genericParams = emptyNode, pragmas = emptyNode,
  445. exceptions = emptyNode)
  446. result.typ = t
  447. proc createCastExpr(argsParam: PSym; objType: PType): PNode =
  448. result = newNodeI(nkCast, argsParam.info)
  449. result.add newNodeI(nkEmpty, argsParam.info)
  450. result.add newSymNode(argsParam)
  451. result.typ = newType(tyPtr, objType.owner)
  452. result.typ.rawAddSon(objType)
  453. proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym,
  454. castExpr, call,
  455. varSection, varInit, result: PNode) =
  456. let formals = n[0].typ.n
  457. let tmpName = getIdent(g.cache, genPrefix)
  458. for i in 1 ..< n.len:
  459. # we pick n's type here, which hopefully is 'tyArray' and not
  460. # 'tyOpenArray':
  461. var argType = n[i].typ.skipTypes(abstractInst)
  462. if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}:
  463. localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
  464. #elif containsTyRef(argType):
  465. # localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
  466. let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
  467. var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
  468. field.typ = argType
  469. objType.addField(field, g.cache)
  470. result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
  471. let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,
  472. indirectAccess(castExpr, field, n.info))
  473. call.add(newSymNode(temp))
  474. proc getRoot*(n: PNode): PSym =
  475. ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
  476. ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
  477. ## determined as the owner; ``obj`` in the example.
  478. case n.kind
  479. of nkSym:
  480. if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:
  481. result = n.sym
  482. of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
  483. nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
  484. result = getRoot(n.sons[0])
  485. of nkHiddenStdConv, nkHiddenSubConv, nkConv:
  486. result = getRoot(n.sons[1])
  487. of nkCallKinds:
  488. if getMagic(n) == mSlice: result = getRoot(n.sons[1])
  489. else: discard
  490. proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
  491. result = nkIntLit.newIntNode(value)
  492. result.typ = getSysType(g, info, tyInt)
  493. proc genHigh*(g: ModuleGraph; n: PNode): PNode =
  494. if skipTypes(n.typ, abstractVar).kind == tyArray:
  495. result = newIntLit(g, n.info, lastOrd(g.config, skipTypes(n.typ, abstractVar)))
  496. else:
  497. result = newNodeI(nkCall, n.info, 2)
  498. result.typ = getSysType(g, n.info, tyInt)
  499. result.sons[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
  500. result.sons[1] = n
  501. proc genLen*(g: ModuleGraph; n: PNode): PNode =
  502. if skipTypes(n.typ, abstractVar).kind == tyArray:
  503. result = newIntLit(g, n.info, lastOrd(g.config, skipTypes(n.typ, abstractVar)) + 1)
  504. else:
  505. result = newNodeI(nkCall, n.info, 2)
  506. result.typ = getSysType(g, n.info, tyInt)
  507. result.sons[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
  508. result.sons[1] = n
  509. proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym;
  510. castExpr, call,
  511. varSection, varInit, result: PNode) =
  512. let formals = n[0].typ.n
  513. let tmpName = getIdent(g.cache, genPrefix)
  514. # we need to copy the foreign scratch object fields into local variables
  515. # for correctness: These are called 'threadLocal' here.
  516. for i in 1 ..< n.len:
  517. let n = n[i]
  518. let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
  519. abstractInst)
  520. #if containsTyRef(argType):
  521. # localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
  522. let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
  523. var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
  524. if argType.kind in {tyVarargs, tyOpenArray}:
  525. # important special case: we always create a zero-copy slice:
  526. let slice = newNodeI(nkCall, n.info, 4)
  527. slice.typ = n.typ
  528. slice.sons[0] = newSymNode(createMagic(g, "slice", mSlice))
  529. slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type
  530. var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
  531. fieldB.typ = getSysType(g, n.info, tyInt)
  532. objType.addField(fieldB, g.cache)
  533. if getMagic(n) == mSlice:
  534. let a = genAddrOf(n[1])
  535. field.typ = a.typ
  536. objType.addField(field, g.cache)
  537. result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
  538. var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
  539. fieldA.typ = getSysType(g, n.info, tyInt)
  540. objType.addField(fieldA, g.cache)
  541. result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
  542. result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
  543. let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ,
  544. indirectAccess(castExpr, fieldA, n.info),
  545. useShallowCopy=true)
  546. slice.sons[2] = threadLocal.newSymNode
  547. else:
  548. let a = genAddrOf(n)
  549. field.typ = a.typ
  550. objType.addField(field, g.cache)
  551. result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
  552. result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
  553. slice.sons[2] = newIntLit(g, n.info, 0)
  554. # the array itself does not need to go through a thread local variable:
  555. slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
  556. let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ,
  557. indirectAccess(castExpr, fieldB, n.info),
  558. useShallowCopy=true)
  559. slice.sons[3] = threadLocal.newSymNode
  560. call.add slice
  561. elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and
  562. n.getRoot != nil:
  563. # it is more efficient to pass a pointer instead:
  564. let a = genAddrOf(n)
  565. field.typ = a.typ
  566. objType.addField(field, g.cache)
  567. result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
  568. let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,
  569. indirectAccess(castExpr, field, n.info),
  570. useShallowCopy=true)
  571. call.add(genDeref(threadLocal.newSymNode))
  572. else:
  573. # boring case
  574. field.typ = argType
  575. objType.addField(field, g.cache)
  576. result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
  577. let threadLocal = addLocalVar(g, varSection, varInit,
  578. objType.owner, field.typ,
  579. indirectAccess(castExpr, field, n.info),
  580. useShallowCopy=true)
  581. call.add(threadLocal.newSymNode)
  582. proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType;
  583. barrier, dest: PNode = nil): PNode =
  584. # if 'barrier' != nil, then it is in a 'parallel' section and we
  585. # generate quite different code
  586. let n = spawnExpr[^2]
  587. let spawnKind = spawnResult(retType, barrier!=nil)
  588. case spawnKind
  589. of srVoid:
  590. internalAssert g.config, dest == nil
  591. result = newNodeI(nkStmtList, n.info)
  592. of srFlowVar:
  593. internalAssert g.config, dest == nil
  594. result = newNodeIT(nkStmtListExpr, n.info, retType)
  595. of srByVar:
  596. if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded")
  597. result = newNodeI(nkStmtList, n.info)
  598. if n.kind notin nkCallKinds:
  599. localError(g.config, n.info, "'spawn' takes a call expression")
  600. return
  601. if optThreadAnalysis in g.config.globalOptions:
  602. if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
  603. localError(g.config, n.info, "'spawn' takes a GC safe call expression")
  604. var
  605. threadParam = newSym(skParam, getIdent(g.cache, "thread"), owner, n.info, g.config.options)
  606. argsParam = newSym(skParam, getIdent(g.cache, "args"), owner, n.info, g.config.options)
  607. block:
  608. let ptrType = getSysType(g, n.info, tyPointer)
  609. threadParam.typ = ptrType
  610. argsParam.typ = ptrType
  611. argsParam.position = 1
  612. var objType = createObj(g, owner, n.info)
  613. incl(objType.flags, tfFinal)
  614. let castExpr = createCastExpr(argsParam, objType)
  615. var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options)
  616. block:
  617. scratchObj.typ = objType
  618. incl(scratchObj.flags, sfFromGeneric)
  619. var varSectionB = newNodeI(nkVarSection, n.info)
  620. varSectionB.addVar(scratchObj.newSymNode)
  621. result.add varSectionB
  622. var call = newNodeIT(nkCall, n.info, n.typ)
  623. var fn = n.sons[0]
  624. # templates and macros are in fact valid here due to the nature of
  625. # the transformation:
  626. if fn.kind == nkClosure:
  627. localError(g.config, n.info, "closure in spawn environment is not allowed")
  628. if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
  629. skFunc, skMethod, skConverter}):
  630. # for indirect calls we pass the function pointer in the scratchObj
  631. var argType = n[0].typ.skipTypes(abstractInst)
  632. var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options)
  633. field.typ = argType
  634. objType.addField(field, g.cache)
  635. result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
  636. fn = indirectAccess(castExpr, field, n.info)
  637. elif fn.kind == nkSym and fn.sym.kind == skIterator:
  638. localError(g.config, n.info, "iterator in spawn environment is not allowed")
  639. elif fn.typ.callConv == ccClosure:
  640. localError(g.config, n.info, "closure in spawn environment is not allowed")
  641. call.add(fn)
  642. var varSection = newNodeI(nkVarSection, n.info)
  643. var varInit = newNodeI(nkStmtList, n.info)
  644. if barrier.isNil:
  645. setupArgsForConcurrency(g, n, objType, scratchObj, castExpr, call,
  646. varSection, varInit, result)
  647. else:
  648. setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call,
  649. varSection, varInit, result)
  650. var barrierAsExpr: PNode = nil
  651. if barrier != nil:
  652. let typ = newType(tyPtr, owner)
  653. typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
  654. var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options)
  655. field.typ = typ
  656. objType.addField(field, g.cache)
  657. result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
  658. barrierAsExpr = indirectAccess(castExpr, field, n.info)
  659. var fvField, fvAsExpr: PNode = nil
  660. if spawnKind == srFlowVar:
  661. var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
  662. field.typ = retType
  663. objType.addField(field, g.cache)
  664. fvField = newDotExpr(scratchObj, field)
  665. fvAsExpr = indirectAccess(castExpr, field, n.info)
  666. # create flowVar:
  667. result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
  668. if barrier == nil:
  669. result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info,
  670. fvField)
  671. elif spawnKind == srByVar:
  672. var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
  673. field.typ = newType(tyPtr, objType.owner)
  674. field.typ.rawAddSon(retType)
  675. objType.addField(field, g.cache)
  676. fvAsExpr = indirectAccess(castExpr, field, n.info)
  677. result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
  678. let wrapper = createWrapperProc(g, fn, threadParam, argsParam,
  679. varSection, varInit, call,
  680. barrierAsExpr, fvAsExpr, spawnKind)
  681. result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.info,
  682. wrapper.newSymNode, genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
  683. if spawnKind == srFlowVar: result.add fvField