summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2019-03-14 07:59:35 +0100
committerAndreas Rumpf <rumpf_a@web.de>2019-03-14 07:59:44 +0100
commit2ab6b2c657451c26100645446958671e195a5fb6 (patch)
treef2fb8ca60ecdca22acbb525327b0d8488b52582a /lib
parent79b1eafa59983509a2eb61072974de8dfc05f196 (diff)
downloadNim-2ab6b2c657451c26100645446958671e195a5fb6.tar.gz
--newruntime: work in progress
Diffstat (limited to 'lib')
-rw-r--r--lib/core/runtime_v2.nim78
-rw-r--r--lib/core/seqs.nim69
-rw-r--r--lib/core/strs.nim74
-rw-r--r--lib/system/chcks.nim88
-rw-r--r--lib/system/mmdisp.nim2
5 files changed, 198 insertions, 113 deletions
diff --git a/lib/core/runtime_v2.nim b/lib/core/runtime_v2.nim
new file mode 100644
index 000000000..8a9068c37
--- /dev/null
+++ b/lib/core/runtime_v2.nim
@@ -0,0 +1,78 @@
+#[
+In this new runtime we simply the object layouts a bit: The runtime type
+information is only accessed for the objects that have it and it's always
+at offset 0 then. The ``ref`` object header is independent from the
+runtime type and only contains a reference count.
+
+Object subtyping is checked via the generated 'name'. This should have
+comparable overhead to the old pointer chasing approach but has the benefit
+that it works across DLL boundaries.
+
+The generated name is a concatenation of the object names in the hierarchy
+so that a subtype check becomes a substring check. For example::
+
+  type
+    ObjectA = object of RootObj
+    ObjectB = object of ObjectA
+
+ObjectA's ``name`` is "|ObjectA|RootObj|".
+ObjectB's ``name`` is "|ObjectB|ObjectA|RootObj|".
+
+Now to check for ``x of ObjectB`` we need to check
+for ``x.typ.name.hasSubstring("|ObjectB|")``. In the actual implementation,
+however, we could also use a
+hash of ``package & "." & module & "." & name`` to save space.
+
+]#
+
+type
+  TNimNode {.compilerProc.} = object # to keep the code generator simple
+  TNimType {.compilerProc.} = object
+    destructor: proc (p: pointer) {.nimcall, benign.}
+    size: int
+    name: cstring
+  PNimType = ptr TNimType
+
+  ObjHeader = object
+    rc: int # the object header is now a single RC field.
+            # we could remove it in non-debug builds but this seems
+            # unwise.
+
+proc isObj(obj: PNimType, subclass: cstring): bool {.compilerproc.} =
+  proc strstr(s, sub: cstring): cstring {.header: "<string.h>", importc.}
+
+  result = strstr(obj.name, subclass) != nil
+
+proc chckObj(obj: PNimType, subclass: cstring) {.compilerproc.} =
+  # checks if obj is of type subclass:
+  if not isObj(obj, subclass): sysFatal(ObjectConversionError, "invalid object conversion")
+
+template `+!`(p: pointer, s: int): pointer =
+  cast[pointer](cast[int](p) +% s)
+
+template `-!`(p: pointer, s: int): pointer =
+  cast[pointer](cast[int](p) -% s)
+
+template head(p: pointer): ptr ObjHeader =
+  cast[ptr ObjHeader](cast[int](p) -% sizeof(ObjHeader))
+
+proc nimNewObj(size: int): pointer {.compilerRtl.} =
+  result = alloc0(size + sizeof(ObjHeader)) +! sizeof(ObjHeader)
+  # XXX Respect   defined(useMalloc)  here!
+
+proc nimDecWeakRef(p: pointer) {.compilerRtl.} =
+  dec head(p).rc
+
+proc nimIncWeakRef(p: pointer) {.compilerRtl.} =
+  inc head(p).rc
+
+proc nimRawDispose(p: pointer) {.compilerRtl.} =
+  if head(p).rc != 0:
+    cstderr.rawWrite "[FATAL] dangling references exist\n"
+    quit 1
+  dealloc(p -! sizeof(ObjHeader))
+
+proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
+  let d = cast[ptr PNimType](p)[].destructor
+  if d != nil: d(p)
+  nimRawDispose(p)
diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim
index fd46b96eb..08c10962b 100644
--- a/lib/core/seqs.nim
+++ b/lib/core/seqs.nim
@@ -44,43 +44,44 @@ This means the check for whether ``s.p`` needs to be freed should
 be ``s.len == 0`` even though that feels slightly more awkward.
 ]#
 
-proc `=destroy`[T](s: var seq[T]) =
-  var x = cast[ptr NimSeqV2[T]](addr s)
-  var p = x.p
-  if p != nil:
+when not defined(nimV2):
+  proc `=destroy`[T](s: var seq[T]) =
+    var x = cast[ptr NimSeqV2[T]](addr s)
+    var p = x.p
+    if p != nil:
+      mixin `=destroy`
+      when not supportsCopyMem(T):
+        for i in 0..<x.len: `=destroy`(p.data[i])
+      if p.region != nil:
+        p.region.dealloc(p.region, p, payloadSize(p.cap))
+      x.p = nil
+      x.len = 0
+
+  proc `=`[T](x: var seq[T]; y: seq[T]) =
     mixin `=destroy`
-    when not supportsCopyMem(T):
-      for i in 0..<x.len: `=destroy`(p.data[i])
-    if p.region != nil:
-      p.region.dealloc(p.region, p, payloadSize(p.cap))
-    x.p = nil
-    x.len = 0
-
-proc `=`[T](x: var seq[T]; y: seq[T]) =
-  mixin `=destroy`
-  var a = cast[ptr NimSeqV2[T]](addr x)
-  var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
-
-  if a.p == b.p: return
-  `=destroy`(x)
-  a.len = b.len
-  if b.p != nil:
-    a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
-    when supportsCopyMem(T):
-      if a.len > 0:
-        copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
-    else:
-      for i in 0..<a.len:
-        a.p.data[i] = b.p.data[i]
+    var a = cast[ptr NimSeqV2[T]](addr x)
+    var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
 
-proc `=sink`[T](x: var seq[T]; y: seq[T]) =
-  mixin `=destroy`
-  var a = cast[ptr NimSeqV2[T]](addr x)
-  var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
-  if a.p != nil and a.p != b.p:
+    if a.p == b.p: return
     `=destroy`(x)
-  a.len = b.len
-  a.p = b.p
+    a.len = b.len
+    if b.p != nil:
+      a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
+      when supportsCopyMem(T):
+        if a.len > 0:
+          copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
+      else:
+        for i in 0..<a.len:
+          a.p.data[i] = b.p.data[i]
+
+  proc `=sink`[T](x: var seq[T]; y: seq[T]) =
+    mixin `=destroy`
+    var a = cast[ptr NimSeqV2[T]](addr x)
+    var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
+    if a.p != nil and a.p != b.p:
+      `=destroy`(x)
+    a.len = b.len
+    a.p = b.p
 
 
 type
diff --git a/lib/core/strs.nim b/lib/core/strs.nim
index 406efe5a1..540765de3 100644
--- a/lib/core/strs.nim
+++ b/lib/core/strs.nim
@@ -41,43 +41,45 @@ template isLiteral(s): bool = s.p == nil or s.p.region == nil
 
 template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator)
 
-template frees(s) =
-  if not isLiteral(s):
-    s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
-
-proc `=destroy`(s: var string) =
-  var a = cast[ptr NimStringV2](addr s)
-  frees(a)
-  a.len = 0
-  a.p = nil
-
-proc `=sink`(x: var string, y: string) =
-  var a = cast[ptr NimStringV2](addr x)
-  var b = cast[ptr NimStringV2](unsafeAddr y)
-  # we hope this is optimized away for not yet alive objects:
-  if unlikely(a.p == b.p): return
-  frees(a)
-  a.len = b.len
-  a.p = b.p
-
-proc `=`(x: var string, y: string) =
-  var a = cast[ptr NimStringV2](addr x)
-  var b = cast[ptr NimStringV2](unsafeAddr y)
-  if unlikely(a.p == b.p): return
-  frees(a)
-  a.len = b.len
-  if isLiteral(b):
-    # we can shallow copy literals:
+when not defined(nimV2):
+
+  template frees(s) =
+    if not isLiteral(s):
+      s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
+
+  proc `=destroy`(s: var string) =
+    var a = cast[ptr NimStringV2](addr s)
+    frees(a)
+    a.len = 0
+    a.p = nil
+
+  proc `=sink`(x: var string, y: string) =
+    var a = cast[ptr NimStringV2](addr x)
+    var b = cast[ptr NimStringV2](unsafeAddr y)
+    # we hope this is optimized away for not yet alive objects:
+    if unlikely(a.p == b.p): return
+    frees(a)
+    a.len = b.len
     a.p = b.p
-  else:
-    let region = if a.p != nil and a.p.region != nil: a.p.region else: getLocalAllocator()
-    # we have to allocate the 'cap' here, consider
-    # 'let y = newStringOfCap(); var x = y'
-    # on the other hand... These get turned into moves now.
-    a.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(b.len)))
-    a.p.region = region
-    a.p.cap = b.len
-    copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
+
+  proc `=`(x: var string, y: string) =
+    var a = cast[ptr NimStringV2](addr x)
+    var b = cast[ptr NimStringV2](unsafeAddr y)
+    if unlikely(a.p == b.p): return
+    frees(a)
+    a.len = b.len
+    if isLiteral(b):
+      # we can shallow copy literals:
+      a.p = b.p
+    else:
+      let region = if a.p != nil and a.p.region != nil: a.p.region else: getLocalAllocator()
+      # we have to allocate the 'cap' here, consider
+      # 'let y = newStringOfCap(); var x = y'
+      # on the other hand... These get turned into moves now.
+      a.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(b.len)))
+      a.p.region = region
+      a.p.cap = b.len
+      copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
 
 proc resize(old: int): int {.inline.} =
   if old <= 0: result = 4
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index 8821e32f8..d108e09f1 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -63,46 +63,48 @@ proc chckNilDisp(p: pointer) {.compilerproc.} =
   if p == nil:
     sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
 
-proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
-  # checks if obj is of type subclass:
-  var x = obj
-  if x == subclass: return # optimized fast path
-  while x != subclass:
-    if x == nil:
-      sysFatal(ObjectConversionError, "invalid object conversion")
-    x = x.base
-
-proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
-  if a != b:
-    sysFatal(ObjectAssignmentError, "invalid object assignment")
-
-type ObjCheckCache = array[0..1, PNimType]
-
-proc isObjSlowPath(obj, subclass: PNimType;
-                   cache: var ObjCheckCache): bool {.noinline.} =
-  # checks if obj is of type subclass:
-  var x = obj.base
-  while x != subclass:
-    if x == nil:
-      cache[0] = obj
-      return false
-    x = x.base
-  cache[1] = obj
-  return true
-
-proc isObjWithCache(obj, subclass: PNimType;
-                    cache: var ObjCheckCache): bool {.compilerProc, inline.} =
-  if obj == subclass: return true
-  if obj.base == subclass: return true
-  if cache[0] == obj: return false
-  if cache[1] == obj: return true
-  return isObjSlowPath(obj, subclass, cache)
-
-proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
-  # checks if obj is of type subclass:
-  var x = obj
-  if x == subclass: return true # optimized fast path
-  while x != subclass:
-    if x == nil: return false
-    x = x.base
-  return true
+when not defined(nimV2):
+
+  proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
+    # checks if obj is of type subclass:
+    var x = obj
+    if x == subclass: return # optimized fast path
+    while x != subclass:
+      if x == nil:
+        sysFatal(ObjectConversionError, "invalid object conversion")
+      x = x.base
+
+  proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
+    if a != b:
+      sysFatal(ObjectAssignmentError, "invalid object assignment")
+
+  type ObjCheckCache = array[0..1, PNimType]
+
+  proc isObjSlowPath(obj, subclass: PNimType;
+                    cache: var ObjCheckCache): bool {.noinline.} =
+    # checks if obj is of type subclass:
+    var x = obj.base
+    while x != subclass:
+      if x == nil:
+        cache[0] = obj
+        return false
+      x = x.base
+    cache[1] = obj
+    return true
+
+  proc isObjWithCache(obj, subclass: PNimType;
+                      cache: var ObjCheckCache): bool {.compilerProc, inline.} =
+    if obj == subclass: return true
+    if obj.base == subclass: return true
+    if cache[0] == obj: return false
+    if cache[1] == obj: return true
+    return isObjSlowPath(obj, subclass, cache)
+
+  proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
+    # checks if obj is of type subclass:
+    var x = obj
+    if x == subclass: return true # optimized fast path
+    while x != subclass:
+      if x == nil: return false
+      x = x.base
+    return true
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 9284f07d2..a2cddc472 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -496,6 +496,8 @@ else:
   elif defined(gcRegions):
     # XXX due to bootstrapping reasons, we cannot use  compileOption("gc", "stack") here
     include "system/gc_regions"
+  elif defined(nimV2):
+    include "core/runtime_v2"
   elif defined(gcMarkAndSweep) or defined(gcDestructors):
     # XXX use 'compileOption' here
     include "system/gc_ms"