summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2014-04-16 08:44:57 +0200
committerAraq <rumpf_a@web.de>2014-04-16 08:44:57 +0200
commit8e08ff559f4c03587c683b1bf2ef71f256af3824 (patch)
tree58b3814cc079243ab759ba80e1d7a7802fa0caab
parentb961e47bfe519bf456a3e8a0dba3025a3c047b04 (diff)
downloadNim-8e08ff559f4c03587c683b1bf2ef71f256af3824.tar.gz
first version of 'spawn'
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/ccgexprs.nim3
-rw-r--r--compiler/cgen.nim2
-rw-r--r--compiler/lambdalifting.nim31
-rw-r--r--compiler/lowerings.nim174
-rw-r--r--compiler/semexprs.nim2
-rw-r--r--compiler/sigmatch.nim1
-rw-r--r--compiler/wordrecg.nim3
-rw-r--r--lib/system/alloc.nim7
9 files changed, 188 insertions, 37 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 791b73685..e720d2bfa 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -559,7 +559,7 @@ type
     mFloat, mFloat32, mFloat64, mFloat128,
     mBool, mChar, mString, mCstring,
     mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc,
-    mVoidType, mPNimrodNode, mShared, mGuarded,
+    mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn,
     mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor,
     mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mAppType,
     mNaN, mInf, mNegInf,
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index e346e1b53..9702fb25c 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1632,6 +1632,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     localError(e.info, errCannotGenerateCodeForX, e.sons[0].sym.name.s)
   of mSlurp..mQuoteAst:
     localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
+  of mSpawn:
+    let n = lowerings.wrapProcForSpawn(p.module.module, e.sons[1])
+    expr(p, n, d)
   else: internalError(e.info, "genMagicExpr: " & $op)
 
 proc genConstExpr(p: BProc, n: PNode): PRope
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 683aed069..f64ebacfb 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -14,7 +14,7 @@ import
   options, intsets,
   nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
   times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
-  rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases
+  rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings
 
 when options.hasTinyCBackend:
   import tccgen
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 440468ac4..c5b9d0f00 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -141,12 +141,6 @@ type
     closureParam, state, resultSym: PSym # only if isIter
     obj: PType # only if isIter
 
-proc createObj(owner: PSym, info: TLineInfo): PType =
-  result = newType(tyObject, owner)
-  rawAddSon(result, nil)
-  incl result.flags, tfFinal
-  result.n = newNodeI(nkRecList, info)
-
 proc getStateType(iter: PSym): PType =
   var n = newNodeI(nkRange, iter.info)
   addSon(n, newIntNode(nkIntLit, -1))
@@ -189,15 +183,6 @@ proc getEnvParam(routine: PSym): PSym =
   let hidden = lastSon(params)
   if hidden.kind == nkSym and hidden.sym.name.s == paramName:
     result = hidden.sym
-    
-proc addField(obj: PType; s: PSym) =
-  # because of 'gensym' support, we have to mangle the name with its ID.
-  # This is hacky but the clean solution is much more complex than it looks.
-  var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info)
-  let t = skipIntLit(s.typ)
-  field.typ = t
-  field.position = sonsLen(obj.n)
-  addSon(obj.n, newSymNode(field))
 
 proc initIterContext(c: POuterContext, iter: PSym) =
   c.fn = iter
@@ -273,22 +258,6 @@ proc addDep(e, d: PEnv, owner: PSym): PSym =
   rawAddSon(result.typ, d.obj)
   addField(e.obj, result)
   e.deps.add((d, result))
-  
-proc indirectAccess(a: PNode, b: PSym, info: TLineInfo): PNode = 
-  # returns a[].b as a node
-  var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.sons[0]
-  assert deref.typ.kind == tyObject
-  let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id))
-  assert field != nil, b.name.s
-  addSon(deref, a)
-  result = newNodeI(nkDotExpr, info)
-  addSon(result, deref)
-  addSon(result, newSymNode(field))
-  result.typ = field.typ
-
-proc indirectAccess(a, b: PSym, info: TLineInfo): PNode =
-  result = indirectAccess(newSymNode(a), b, info)
 
 proc newCall(a, b: PSym): PNode =
   result = newNodeI(nkCall, a.info)
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 2cf641d93..bee3427f4 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -12,7 +12,7 @@
 const
   genPrefix* = ":tmp"         # prefix for generated names
 
-import ast, types, idents, magicsys
+import ast, astalgo, types, idents, magicsys, msgs
 
 proc newTupleAccess*(tup: PNode, i: int): PNode =
   result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
@@ -34,6 +34,11 @@ proc newAsgnStmt(le, ri: PNode): PNode =
   result.sons[0] = le
   result.sons[1] = ri
 
+proc newFastAsgnStmt(le, ri: PNode): PNode =
+  result = newNodeI(nkFastAsgn, le.info, 2)
+  result.sons[0] = le
+  result.sons[1] = ri
+
 proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
   assert n.kind == nkVarTuple
   let value = n.lastSon
@@ -50,3 +55,170 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
   result.add newAsgnStmt(newSymNode(temp), value)
   for i in 0 .. n.len-3:
     result.add newAsgnStmt(n.sons[i], newTupleAccess(value, i))
+
+proc createObj*(owner: PSym, info: TLineInfo): PType =
+  result = newType(tyObject, owner)
+  rawAddSon(result, nil)
+  incl result.flags, tfFinal
+  result.n = newNodeI(nkRecList, info)
+
+proc addField*(obj: PType; s: PSym) =
+  # because of 'gensym' support, we have to mangle the name with its ID.
+  # This is hacky but the clean solution is much more complex than it looks.
+  var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info)
+  let t = skipIntLit(s.typ)
+  field.typ = t
+  field.position = sonsLen(obj.n)
+  addSon(obj.n, newSymNode(field))
+
+proc newDotExpr(obj, b: PSym): PNode =
+  result = newNodeI(nkDotExpr, obj.info)
+  let field = getSymFromList(obj.typ.n, getIdent(b.name.s & $b.id))
+  assert field != nil, b.name.s
+  addSon(result, newSymNode(obj))
+  addSon(result, newSymNode(field))
+  result.typ = field.typ
+
+proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = 
+  # returns a[].b as a node
+  var deref = newNodeI(nkHiddenDeref, info)
+  deref.typ = a.typ.sons[0]
+  assert deref.typ.kind == tyObject
+  let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id))
+  assert field != nil, b.name.s
+  addSon(deref, a)
+  result = newNodeI(nkDotExpr, info)
+  addSon(result, deref)
+  addSon(result, newSymNode(field))
+  result.typ = field.typ
+
+proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
+  result = indirectAccess(newSymNode(a), b, info)
+
+proc genAddrOf*(n: PNode): PNode =
+  result = newNodeI(nkAddr, n.info, 1)
+  result.sons[0] = n
+  result.typ = newType(tyPtr, n.typ.owner)
+  result.typ.rawAddSon(n.typ)
+
+proc callCodegenProc*(name: string, arg1: PNode; 
+                      arg2, arg3: PNode = nil): PNode =
+  result = newNodeI(nkCall, arg1.info)
+  let sym = magicsys.getCompilerProc(name)
+  if sym == nil:
+    localError(arg1.info, errSystemNeeds, name)
+  else:
+    result.add newSymNode(sym)
+    result.add arg1
+    if arg2 != nil: result.add arg2
+    if arg3 != nil: result.add arg3
+
+proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
+                       varSection, call: PNode): PSym =
+  var body = newNodeI(nkStmtList, f.info)
+  body.add varSection
+  body.add callCodeGenProc("nimArgsPassingDone", newSymNode(threadParam))
+  body.add call
+
+  var params = newNodeI(nkFormalParams, f.info)
+  params.add emptyNode
+  params.add threadParam.newSymNode
+  params.add argsParam.newSymNode
+
+  var t = newType(tyProc, threadParam.owner)
+  t.rawAddSon nil
+  t.rawAddSon threadParam.typ
+  t.rawAddSon argsParam.typ
+  t.n = newNodeI(nkFormalParams, f.info)
+  t.n.add newNodeI(nkEffectList, f.info)
+  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(name), argsParam.owner, f.info)
+  result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result))
+  result.typ = t
+
+proc createCastExpr(argsParam: PSym; objType: PType): PNode =
+  result = newNodeI(nkCast, argsParam.info)
+  result.add emptyNode
+  result.add newSymNode(argsParam)
+  result.typ = newType(tyPtr, objType.owner)
+  result.typ.rawAddSon(objType)
+
+proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode =
+  result = newNodeI(nkStmtList, n.info)
+  if n.kind notin nkCallKinds or not n.typ.isEmptyType:
+    localError(n.info, "'spawn' takes a call expression of type void")
+    return
+  var
+    threadParam = newSym(skParam, getIdent"thread", owner, n.info)
+    argsParam = newSym(skParam, getIdent"args", owner, n.info)
+  block:
+    let ptrType = getSysType(tyPointer)
+    threadParam.typ = ptrType
+    argsParam.typ = ptrType
+    argsParam.position = 1
+  var objType = createObj(owner, n.info)
+  let castExpr = createCastExpr(argsParam, objType)
+
+  var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info)
+  block:
+    scratchObj.typ = objType
+    incl(scratchObj.flags, sfFromGeneric)
+    var varSectionB = newNodeI(nkVarSection, n.info)
+    varSectionB.addVar(scratchObj.newSymNode)
+    result.add varSectionB
+
+  var call = newNodeI(nkCall, n.info)
+  var fn = n.sons[0]
+  # templates and macros are in fact valid here due to the nature of
+  # the transformation:
+  if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
+                                               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"fn", owner, n.info)
+    field.typ = argType
+    objType.addField(field)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
+    fn = indirectAccess(castExpr, field, n.info)
+  elif fn.kind == nkSym and fn.sym.kind in {skClosureIterator, skIterator}:
+    localError(n.info, "iterator in spawn environment is not allowed")
+  elif fn.typ.callConv == ccClosure:
+    localError(n.info, "closure in spawn environment is not allowed")
+
+  call.add(fn)
+  var varSection = newNodeI(nkVarSection, n.info)
+  let formals = n[0].typ.n
+  let tmpName = getIdent(genPrefix)
+  for i in 1 .. <n.len:
+    # we pick n's type here, which hopefully is 'tyArray' and not
+    # 'tyOpenArray':
+    var argType = n[i].typ.skipTypes(abstractInst)
+    if argType.kind == tyVar:
+      localError(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")
+
+    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
+    var field = newSym(skField, fieldname, owner, n.info)
+    field.typ = argType
+    objType.addField(field)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
+
+    var temp = newSym(skTemp, tmpName, owner, n.info)
+    temp.typ = argType
+    incl(temp.flags, sfFromGeneric)
+
+    var vpart = newNodeI(nkIdentDefs, n.info, 3)
+    vpart.sons[0] = newSymNode(temp)
+    vpart.sons[1] = ast.emptyNode
+    vpart.sons[2] = indirectAccess(castExpr, field, n.info)
+    varSection.add vpart
+
+    call.add(newSymNode(temp))
+
+  let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, call)
+  result.add callCodeGenProc("nimSpawn", wrapper.newSymNode,
+                             genAddrOf(scratchObj.newSymNode))
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 6c1721bdd..652e1dd4b 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   if result.typ != nil:
     # XXX tyGenericInst here?
     if result.typ.kind == tyVar: result = newDeref(result)
+  elif efWantStmt in flags:
+    result.typ = newTypeS(tyEmpty, c)
   else:
     localError(n.info, errExprXHasNoType, 
                renderTree(result, {renderNoComments}))
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 9d1585c56..6b85542d3 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -1284,6 +1284,7 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
     result = a
   elif a.typ.isNil:
     let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator}
+                elif formal.kind == tyStmt: {efDetermineType, efWantStmt}
                 else: {efDetermineType}
     result = c.semOperand(c, a, flags)
   else:
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 47ce89789..9a74f53fc 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -63,7 +63,7 @@ type
     wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
     wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, 
     wAsmNoStackFrame,
-    wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked,
+    wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard,
 
     wAuto, wBool, wCatch, wChar, wClass,
     wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@@ -146,6 +146,7 @@ const
     "computedgoto", "injectstmt",
     "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
     "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
+    "guard",
     
     "auto", "bool", "catch", "char", "class",
     "const_cast", "default", "delete", "double",
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 954485eb4..eaef6cd95 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -722,10 +722,13 @@ proc alloc0(allocator: var TMemRegion, size: int): pointer =
   zeroMem(result, size)
 
 proc dealloc(allocator: var TMemRegion, p: pointer) =
+  sysAssert(p != nil, "dealloc 0")
   var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
-  sysAssert(cast[ptr TFreeCell](x).zeroField == 1, "dealloc 1")
+  sysAssert(x != nil, "dealloc 1")
+  sysAssert(isAccessible(allocator, x), "is not accessible")
+  sysAssert(cast[ptr TFreeCell](x).zeroField == 1, "dealloc 2")
   rawDealloc(allocator, x)
-  sysAssert(not isAllocatedPtr(allocator, x), "dealloc 2")
+  sysAssert(not isAllocatedPtr(allocator, x), "dealloc 3")
 
 proc realloc(allocator: var TMemRegion, p: pointer, newsize: int): pointer =
   if newsize > 0: