diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/core/runtime_v2.nim | 78 | ||||
-rw-r--r-- | lib/core/seqs.nim | 69 | ||||
-rw-r--r-- | lib/core/strs.nim | 74 | ||||
-rw-r--r-- | lib/system/chcks.nim | 88 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 2 |
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" |