summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2011-11-24 23:28:28 +0100
committerAraq <rumpf_a@web.de>2011-11-24 23:28:28 +0100
commit703430787d746ca805949a13c28452aca90af49d (patch)
treeb967ebfb6852770de9997815865a278759346b61
parentca5d2916dd4b80bac65fde4e8c6a31dca4e7f3b4 (diff)
downloadNim-703430787d746ca805949a13c28452aca90af49d.tar.gz
C codegen: generate nimKeepAlive calls at strategic places to keep the C compiler from optimizing away all stack roots
-rw-r--r--compiler/ccgcalls.nim232
-rwxr-xr-xcompiler/ccgexprs.nim164
-rwxr-xr-xcompiler/msgs.nim6
-rwxr-xr-xlib/system/gc.nim21
-rwxr-xr-xtodo.txt5
-rwxr-xr-xweb/news.txt2
6 files changed, 256 insertions, 174 deletions
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
new file mode 100644
index 000000000..dbb9190d2
--- /dev/null
+++ b/compiler/ccgcalls.nim
@@ -0,0 +1,232 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2011 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+type
+  TAfterCallActions = tuple[p: BProc, actions: PRope]
+
+proc fixupCall(p: BProc, t: PNode, d: var TLoc, pl: PRope) =
+  var pl = pl
+  var typ = t.sons[0].typ # getUniqueType() is too expensive here!
+  if typ.sons[0] != nil:
+    if isInvalidReturnType(typ.sons[0]):
+      if sonsLen(t) > 1: app(pl, ", ")
+      # beware of 'result = p(result)'. We always allocate a temporary:
+      if d.k in {locTemp, locNone}:
+        # We already got a temp. Great, special case it:
+        if d.k == locNone: getTemp(p, typ.sons[0], d)
+        app(pl, addrLoc(d))
+        app(pl, ")")
+        app(p.s[cpsStmts], pl)
+        appf(p.s[cpsStmts], ";$n")
+      else:
+        var tmp: TLoc
+        getTemp(p, typ.sons[0], tmp)
+        app(pl, addrLoc(tmp))
+        app(pl, ")")
+        app(p.s[cpsStmts], pl)
+        appf(p.s[cpsStmts], ";$n")
+        genAssignment(p, d, tmp, {}) # no need for deep copying
+    else:
+      app(pl, ")")
+      if d.k == locNone: getTemp(p, typ.sons[0], d)
+      assert(d.t != nil)        # generate an assignment to d:
+      var list: TLoc
+      initLoc(list, locCall, nil, OnUnknown)
+      list.r = pl
+      genAssignment(p, d, list, {}) # no need for deep copying
+  else:
+    app(pl, ")")
+    app(p.s[cpsStmts], pl)
+    appf(p.s[cpsStmts], ";$n")
+
+proc emitAfterCallActions(aca: TAfterCallActions) {.inline.} =
+  app(aca.p.s[cpsStmts], aca.actions)
+
+proc isInCurrentFrame(p: BProc, n: PNode): bool =
+  # checks if `n` is an expression that refers to the current frame;
+  # this does not work reliably because of forwarding + inlining can break it
+  case n.kind
+  of nkSym:
+    if n.sym.kind in {skVar, skResult, skTemp} and p.prc != nil:
+      result = p.prc.id == n.sym.owner.id
+  of nkDotExpr, nkBracketExpr:
+    if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyPtr,tyRef}:
+      result = isInCurrentFrame(p, n.sons[0])
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    result = isInCurrentFrame(p, n.sons[1])
+  of nkHiddenDeref, nkDerefExpr:
+    # what about: var x = addr(y); callAsOpenArray(x[])?
+    # *shrug* ``addr`` is unsafe anyway.
+    result = false
+  of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
+    result = isInCurrentFrame(p, n.sons[0])
+  else: nil
+
+proc genKeepAlive(aca: var TAfterCallActions, n: PNode, a: TLoc) {.inline.} =
+  if a.s == onStack and optRefcGC in gGlobalOptions:
+    aca.p.module.appcg(aca.actions,
+                       "#nimKeepAlive((#TGenericSeq*)$1);$n", [a.rdLoc])
+
+proc openArrayLoc(aca: var TAfterCallActions, n: PNode): PRope =
+  var a: TLoc
+  initLocExpr(aca.p, n, a)
+  case skipTypes(a.t, abstractVar).kind
+  of tyOpenArray:
+    result = ropef("$1, $1Len0", [rdLoc(a)])
+  of tyString, tySequence:
+    result = ropef("$1->data, $1->$2", [a.rdLoc, lenField()])
+    genKeepAlive(aca, n, a)
+  of tyArray, tyArrayConstr:
+    result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])
+  else: InternalError("openArrayLoc: " & typeToString(a.t))
+
+proc genArgStringToCString(aca: var TAfterCallActions, 
+                           n: PNode): PRope {.inline.} =
+  var a: TLoc
+  initLocExpr(aca.p, n.sons[0], a)
+  result = ropef("$1->data", [a.rdLoc])
+  # we don't guarantee save string->cstring conversions anyway, so we use
+  # an additional check to improve performance:
+  if isInCurrentFrame(aca.p, n): genKeepAlive(aca, n, a)
+  
+proc genArg(aca: var TAfterCallActions, n: PNode, param: PSym): PRope =
+  var a: TLoc
+  if n.kind == nkStringToCString:
+    result = genArgStringToCString(aca, n)
+  elif skipTypes(param.typ, abstractVar).kind == tyOpenArray:
+    var n = if n.kind != nkHiddenAddr: n else: n.sons[0]
+    result = openArrayLoc(aca, n)
+  elif ccgIntroducedPtr(param):
+    initLocExpr(aca.p, n, a)
+    result = addrLoc(a)
+  else:
+    initLocExpr(aca.p, n, a)
+    result = rdLoc(a)
+
+proc genArgNoParam(aca: var TAfterCallActions, n: PNode): PRope =
+  var a: TLoc
+  if n.kind == nkStringToCString:
+    result = genArgStringToCString(aca, n)
+  else:
+    initLocExpr(aca.p, n, a)
+    result = rdLoc(a)
+
+proc genCall(p: BProc, t: PNode, d: var TLoc) =
+  var op: TLoc
+  var aca: TAfterCallActions
+  aca.p = p
+  # this is a hotspot in the compiler
+  initLocExpr(p, t.sons[0], op)
+  var pl = con(op.r, "(")
+  var typ = t.sons[0].typ # getUniqueType() is too expensive here!
+  assert(typ.kind == tyProc)
+  var length = sonsLen(t)
+  for i in countup(1, length - 1):
+    assert(sonsLen(typ) == sonsLen(typ.n))
+    if i < sonsLen(typ):
+      assert(typ.n.sons[i].kind == nkSym)
+      app(pl, genArg(aca, t.sons[i], typ.n.sons[i].sym))
+    else:
+      app(pl, genArgNoParam(aca, t.sons[i]))
+    if i < length - 1: app(pl, ", ")
+  fixupCall(p, t, d, pl)
+  emitAfterCallActions(aca)
+
+proc genInfixCall(p: BProc, t: PNode, d: var TLoc) =
+  var op, a: TLoc
+  var aca: TAfterCallActions
+  aca.p = p
+  initLocExpr(p, t.sons[0], op)
+  var pl: PRope = nil
+  var typ = t.sons[0].typ # getUniqueType() is too expensive here!
+  assert(typ.kind == tyProc)
+  var length = sonsLen(t)
+  assert(sonsLen(typ) == sonsLen(typ.n))
+  
+  var param = typ.n.sons[1].sym
+  app(pl, genArg(aca, t.sons[1], param))
+  
+  if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, "->")
+  else: app(pl, ".")
+  app(pl, op.r)
+  app(pl, "(")
+  for i in countup(2, length - 1):
+    assert(sonsLen(typ) == sonsLen(typ.n))
+    if i < sonsLen(typ):
+      assert(typ.n.sons[i].kind == nkSym)
+      app(pl, genArg(aca, t.sons[i], typ.n.sons[i].sym))
+    else:
+      app(pl, genArgNoParam(aca, t.sons[i]))
+    if i < length - 1: app(pl, ", ")
+  fixupCall(p, t, d, pl)
+  emitAfterCallActions(aca)
+
+proc genNamedParamCall(p: BProc, t: PNode, d: var TLoc) =
+  # generates a crappy ObjC call
+  var op, a: TLoc
+  var aca: TAfterCallActions
+  aca.p = p
+  initLocExpr(p, t.sons[0], op)
+  var pl = toRope"["
+  var typ = t.sons[0].typ # getUniqueType() is too expensive here!
+  assert(typ.kind == tyProc)
+  var length = sonsLen(t)
+  assert(sonsLen(typ) == sonsLen(typ.n))
+  
+  if length > 1:
+    app(pl, genArg(aca, t.sons[1], typ.n.sons[1].sym))
+    app(pl, " ")
+  app(pl, op.r)
+  if length > 2:
+    app(pl, ": ")
+    app(pl, genArg(aca, t.sons[2], typ.n.sons[2].sym))
+  for i in countup(3, length-1):
+    assert(sonsLen(typ) == sonsLen(typ.n))
+    if i >= sonsLen(typ):
+      InternalError(t.info, "varargs for objective C method?")
+    assert(typ.n.sons[i].kind == nkSym)
+    var param = typ.n.sons[i].sym
+    app(pl, " ")
+    app(pl, param.name.s)
+    app(pl, ": ")
+    app(pl, genArg(aca, t.sons[i], param))
+  if typ.sons[0] != nil:
+    if isInvalidReturnType(typ.sons[0]):
+      if sonsLen(t) > 1: app(pl, " ")
+      # beware of 'result = p(result)'. We always allocate a temporary:
+      if d.k in {locTemp, locNone}:
+        # We already got a temp. Great, special case it:
+        if d.k == locNone: getTemp(p, typ.sons[0], d)
+        app(pl, "Result: ")
+        app(pl, addrLoc(d))
+        app(pl, "]")
+        app(p.s[cpsStmts], pl)
+        appf(p.s[cpsStmts], ";$n")
+      else:
+        var tmp: TLoc
+        getTemp(p, typ.sons[0], tmp)
+        app(pl, addrLoc(tmp))
+        app(pl, "]")
+        app(p.s[cpsStmts], pl)
+        appf(p.s[cpsStmts], ";$n")
+        genAssignment(p, d, tmp, {}) # no need for deep copying
+    else:
+      app(pl, "]")
+      if d.k == locNone: getTemp(p, typ.sons[0], d)
+      assert(d.t != nil)        # generate an assignment to d:
+      var list: TLoc
+      initLoc(list, locCall, nil, OnUnknown)
+      list.r = pl
+      genAssignment(p, d, list, {}) # no need for deep copying
+  else:
+    app(pl, "]")
+    app(p.s[cpsStmts], pl)
+    appf(p.s[cpsStmts], ";$n")
+  emitAfterCallActions(aca)
+
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 899343e4c..8a2b60aba 100755
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -784,169 +784,7 @@ proc genEcho(p: BProc, n: PNode) =
   appcg(p, cpsStmts, "printf($1$2);$n", [
     makeCString(repeatStr(n.len-1, "%s") & tnl), args])
 
-proc fixupCall(p: BProc, t: PNode, d: var TLoc, pl: PRope) =
-  var pl = pl
-  var typ = t.sons[0].typ # getUniqueType() is too expensive here!
-  if typ.sons[0] != nil:
-    if isInvalidReturnType(typ.sons[0]):
-      if sonsLen(t) > 1: app(pl, ", ")
-      # beware of 'result = p(result)'. We always allocate a temporary:
-      if d.k in {locTemp, locNone}:
-        # We already got a temp. Great, special case it:
-        if d.k == locNone: getTemp(p, typ.sons[0], d)
-        app(pl, addrLoc(d))
-        app(pl, ")")
-        app(p.s[cpsStmts], pl)
-        appf(p.s[cpsStmts], ";$n")
-      else:
-        var tmp: TLoc
-        getTemp(p, typ.sons[0], tmp)
-        app(pl, addrLoc(tmp))
-        app(pl, ")")
-        app(p.s[cpsStmts], pl)
-        appf(p.s[cpsStmts], ";$n")
-        genAssignment(p, d, tmp, {}) # no need for deep copying
-    else:
-      app(pl, ")")
-      if d.k == locNone: getTemp(p, typ.sons[0], d)
-      assert(d.t != nil)        # generate an assignment to d:
-      var list: TLoc
-      initLoc(list, locCall, nil, OnUnknown)
-      list.r = pl
-      genAssignment(p, d, list, {}) # no need for deep copying
-  else:
-    app(pl, ")")
-    app(p.s[cpsStmts], pl)
-    appf(p.s[cpsStmts], ";$n")
-
-proc openArrayLoc(a: TLoc): PRope =
-  case skipTypes(a.t, abstractVar).kind
-  of tyOpenArray:
-    result = ropef("$1, $1Len0", [rdLoc(a)])
-  of tyString, tySequence:
-    result = ropef("$1->data, $1->$2", [rdLoc(a), lenField()])
-  of tyArray, tyArrayConstr:
-    result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])
-  else: InternalError("openArrayLoc: " & typeToString(a.t))
-
-proc genArg(p: BProc, n: PNode, param: PSym): PRope =
-  var a: TLoc
-  if skipTypes(param.typ, abstractVar).kind == tyOpenArray:
-    var n = if n.kind != nkHiddenAddr: n else: n.sons[0]
-    initLocExpr(p, n, a)
-    result = openArrayLoc(a)
-  elif ccgIntroducedPtr(param): 
-    initLocExpr(p, n, a)
-    result = addrLoc(a)
-  else:
-    initLocExpr(p, n, a)
-    result = rdLoc(a)
-
-proc genCall(p: BProc, t: PNode, d: var TLoc) =
-  var op, a: TLoc
-  # this is a hotspot in the compiler
-  initLocExpr(p, t.sons[0], op)
-  var pl = con(op.r, "(")
-  var typ = t.sons[0].typ # getUniqueType() is too expensive here!
-  assert(typ.kind == tyProc)
-  var length = sonsLen(t)
-  for i in countup(1, length - 1):
-    assert(sonsLen(typ) == sonsLen(typ.n))
-    if i < sonsLen(typ):
-      assert(typ.n.sons[i].kind == nkSym)
-      app(pl, genArg(p, t.sons[i], typ.n.sons[i].sym))
-    else:
-      initLocExpr(p, t.sons[i], a) # generate expression for param
-      app(pl, rdLoc(a))
-    if i < length - 1: app(pl, ", ")
-  fixupCall(p, t, d, pl)
-
-proc genInfixCall(p: BProc, t: PNode, d: var TLoc) =
-  var op, a: TLoc
-  initLocExpr(p, t.sons[0], op)
-  var pl: PRope = nil
-  var typ = t.sons[0].typ # getUniqueType() is too expensive here!
-  assert(typ.kind == tyProc)
-  var length = sonsLen(t)
-  assert(sonsLen(typ) == sonsLen(typ.n))
-  
-  var param = typ.n.sons[1].sym
-  app(pl, genArg(p, t.sons[1], param))
-  
-  if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, "->")
-  else: app(pl, ".")
-  app(pl, op.r)
-  app(pl, "(")
-  for i in countup(2, length - 1):
-    assert(sonsLen(typ) == sonsLen(typ.n))
-    if i < sonsLen(typ):
-      assert(typ.n.sons[i].kind == nkSym)
-      app(pl, genArg(p, t.sons[i], typ.n.sons[i].sym))
-    else:
-      initLocExpr(p, t.sons[i], a) # generate expression for param      
-      app(pl, rdLoc(a))
-    if i < length - 1: app(pl, ", ")
-  fixupCall(p, t, d, pl)
-
-proc genNamedParamCall(p: BProc, t: PNode, d: var TLoc) =
-  # generates a crappy ObjC call
-  var op, a: TLoc
-  initLocExpr(p, t.sons[0], op)
-  var pl = toRope"["
-  var typ = t.sons[0].typ # getUniqueType() is too expensive here!
-  assert(typ.kind == tyProc)
-  var length = sonsLen(t)
-  assert(sonsLen(typ) == sonsLen(typ.n))
-  
-  if length > 1:
-    app(pl, genArg(p, t.sons[1], typ.n.sons[1].sym))
-    app(pl, " ")
-  app(pl, op.r)
-  if length > 2:
-    app(pl, ": ")
-    app(pl, genArg(p, t.sons[2], typ.n.sons[2].sym))
-  for i in countup(3, length-1):
-    assert(sonsLen(typ) == sonsLen(typ.n))
-    if i >= sonsLen(typ):
-      InternalError(t.info, "varargs for objective C method?")
-    assert(typ.n.sons[i].kind == nkSym)
-    var param = typ.n.sons[i].sym
-    app(pl, " ")
-    app(pl, param.name.s)
-    app(pl, ": ")
-    app(pl, genArg(p, t.sons[i], param))
-  if typ.sons[0] != nil:
-    if isInvalidReturnType(typ.sons[0]):
-      if sonsLen(t) > 1: app(pl, " ")
-      # beware of 'result = p(result)'. We always allocate a temporary:
-      if d.k in {locTemp, locNone}:
-        # We already got a temp. Great, special case it:
-        if d.k == locNone: getTemp(p, typ.sons[0], d)
-        app(pl, "Result: ")
-        app(pl, addrLoc(d))
-        app(pl, "]")
-        app(p.s[cpsStmts], pl)
-        appf(p.s[cpsStmts], ";$n")
-      else:
-        var tmp: TLoc
-        getTemp(p, typ.sons[0], tmp)
-        app(pl, addrLoc(tmp))
-        app(pl, "]")
-        app(p.s[cpsStmts], pl)
-        appf(p.s[cpsStmts], ";$n")
-        genAssignment(p, d, tmp, {}) # no need for deep copying
-    else:
-      app(pl, "]")
-      if d.k == locNone: getTemp(p, typ.sons[0], d)
-      assert(d.t != nil)        # generate an assignment to d:
-      var list: TLoc
-      initLoc(list, locCall, nil, OnUnknown)
-      list.r = pl
-      genAssignment(p, d, list, {}) # no need for deep copying
-  else:
-    app(pl, "]")
-    app(p.s[cpsStmts], pl)
-    appf(p.s[cpsStmts], ";$n")
+include ccgcalls
 
 proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
   #   <Nimrod code>
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 8106a6862..63204459a 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -564,18 +564,18 @@ var
   lastError = UnknownLineInfo()
 
 proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, 
-               eh: TErrorHandling) = 
+               eh: TErrorHandling) =
   var frmt: string
   var ignoreMsg = false
   case msg
-  of errMin..errMax: 
+  of errMin..errMax:
     writeContext(info)
     frmt = posErrorFormat
     # we try to filter error messages so that not two error message
     # in the same file and line are produced:
     ignoreMsg = lastError == info and eh != doAbort
     lastError = info
-  of warnMin..warnMax: 
+  of warnMin..warnMax:
     ignoreMsg = optWarns notin gOptions or msg notin gNotes
     if not ignoreMsg: writeContext(info)
     frmt = posWarningFormat
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index f56821180..51ff9c90c 100755
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -31,6 +31,8 @@ const
   rcWhite = 0b010  # member of a garbage cycle
   rcPurple = 0b011 # possible root of a cycle
   rcZct = 0b100    # in ZCT
+  rcMarked = 0b101 # dummy write to keep C code generator from
+                   # eliminating the root
   rcRed = 0b101    # Candidate cycle undergoing sigma-computation
   rcOrange = 0b110 # Candidate cycle awaiting epoch boundary
   rcShift = 3      # shift by rcShift to get the reference counter
@@ -517,12 +519,19 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
       # mark the cell:
       cell.refcount = cell.refcount +% rcIncrement
       add(gch.decStack, cell)
-    # care for string->cstring and seq->openArray conversions:
-    var b = cast[PCell](c -% sizeof(TGenericSeq))
-    if isAllocatedPtr(gch.region, b):
-      # mark the cell:
-      b.refcount = b.refcount +% rcIncrement
-      add(gch.decStack, b)
+    when false:
+      # Care for string->cstring and seq->openArray conversions.
+      # Now the codegen deals with it, it generated ``nimKeepAlive`` calls.
+      var b = cast[PCell](c -% sizeof(TGenericSeq))
+      if isAllocatedPtr(gch.region, b):
+        # mark the cell:
+        b.refcount = b.refcount +% rcIncrement
+        add(gch.decStack, b)
+
+proc nimKeepAlive(p: PGenericSeq) {.compilerRtl, noinline.} =
+  var c = usrToCell(p)
+  if isAllocatedPtr(gch.region, c):
+    c.refcount = c.refcount or rcMarked
 
 proc markThreadStacks(gch: var TGcHeap) = 
   when hasThreadSupport and hasSharedHeap:
diff --git a/todo.txt b/todo.txt
index 3f960f115..d831dbbe1 100755
--- a/todo.txt
+++ b/todo.txt
@@ -1,4 +1,4 @@
-Version 0.8.14
+version 0.8.14
 ==============
 
 - fix thread tests
@@ -20,7 +20,7 @@ version 0.9.0
 - change overloading resolution
 - implement closures; implement proper coroutines
 - implement ``partial`` pragma for partial evaluation
-- implicit invokation of `items` seems nice
+- implicit invokation of `items`/`pairs` seems nice
 - we need to support iteration of 2 different data structures in parallel
 - make exceptions compatible with C++ exceptions
 - ``=`` should be overloadable; requires specialization for ``=``
@@ -68,6 +68,7 @@ version 0.9.XX
   per default?
 - fix implicit generic routines
 - improve docgen to use the semantic pass
+- 'export' feature (requires improved docgen)
 - think about ``{:}.toTable[int, string]()``
 - mocking support with ``tyProxy`` that does:
   o.p(x) --> p(o, x) -->  myMacro(o, p, x)
diff --git a/web/news.txt b/web/news.txt
index 3ad135ec8..d2bfe54af 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -18,6 +18,8 @@ Bugfixes
 - Bugfix c2nim, c2pas: the ``--out`` option has never worked properly.
 - Bugfix: forwarding of generic procs never worked.
 - Some more bugfixes for macros and compile-time evaluation.
+- The compiler now generates code that tries to keep the C compiler from
+  optimizing stack allocated GC roots away.
 
 
 Changes affecting backwards compatibility