diff options
author | Ico Doornekamp <ico@pruts.nl> | 2020-01-23 14:25:22 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2020-01-23 14:25:22 +0100 |
commit | b68eb1cad0dc13d497ae0e620dd2178a6367644c (patch) | |
tree | 4456c09a2cf5a964cc1d6cb2cae16dabfa0ff2a8 /lib/system | |
parent | f500895efe84525c131beadd2e83b30caa6674a3 (diff) | |
download | Nim-b68eb1cad0dc13d497ae0e620dd2178a6367644c.tar.gz |
Removed lib/system/allocators.nim. seqs_v2 and strs_v2 now uses allocShared0. (#13190)
* Cleanup, remove lib/system/allocators.nim. seqs_v2 and strs_v2 now use allocShared0 by default. * Fixed -d:useMalloc allocShared / reallocShared / deallocShared. These now use the alloc/dealloc/realloc implementation that also takes care of zeroing memory at realloc. * Removed debug printfs * Removed unpairedEnvAllocs() from tests/destructor/tnewruntime_misc * More mmdisp cleanups. The shared allocators do not need to zero memory or throw since the regular ones already do that * Introduced realloc0 and reallocShared0, these procs are now used by strs_v2 and seqs_v2. This also allowed the -d:useMalloc allocator to drop the extra header with allocation length. * Moved strs_v2/seqs_v2 'allocated' flag into 'cap' field * Added 'getAllocStats()' to get low level alloc/dealloc counters. Enable with -d:allocStats * *allocShared implementations for boehm and go allocators now depend on the proper *allocImpl procs
Diffstat (limited to 'lib/system')
-rw-r--r-- | lib/system/alloc.nim | 44 | ||||
-rw-r--r-- | lib/system/allocators.nim | 86 | ||||
-rw-r--r-- | lib/system/ansi_c.nim | 2 | ||||
-rw-r--r-- | lib/system/gc_regions.nim | 26 | ||||
-rw-r--r-- | lib/system/memalloc.nim | 115 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 103 | ||||
-rw-r--r-- | lib/system/seqs_v2.nim | 44 | ||||
-rw-r--r-- | lib/system/strs_v2.nim | 52 | ||||
-rw-r--r-- | lib/system/widestrs.nim | 8 |
9 files changed, 234 insertions, 246 deletions
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index c65abe5c2..59b7fcfd9 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -948,13 +948,18 @@ proc dealloc(allocator: var MemRegion, p: pointer) = proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer = if newsize > 0: - result = alloc0(allocator, newsize) + result = alloc(allocator, newsize) if p != nil: copyMem(result, p, min(ptrSize(p), newsize)) dealloc(allocator, p) elif p != nil: dealloc(allocator, p) +proc realloc0(allocator: var MemRegion, p: pointer, oldsize, newsize: Natural): pointer = + result = realloc(allocator, p, newsize) + if newsize > oldsize: + zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize) + proc deallocOsPages(a: var MemRegion) = # we free every 'ordinarily' allocated page by iterating over the page bits: var it = addr(a.heapLinks) @@ -997,17 +1002,22 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = proc deallocOsPages = deallocOsPages(allocator) - proc alloc(size: Natural): pointer = + proc allocImpl(size: Natural): pointer = result = alloc(allocator, size) - proc alloc0(size: Natural): pointer = + proc alloc0Impl(size: Natural): pointer = result = alloc0(allocator, size) - proc dealloc(p: pointer) = + proc deallocImpl(p: pointer) = dealloc(allocator, p) - proc realloc(p: pointer, newSize: Natural): pointer = + proc reallocImpl(p: pointer, newSize: Natural): pointer = + result = realloc(allocator, p, newSize) + + proc realloc0Impl(p: pointer, oldSize, newSize: Natural): pointer = result = realloc(allocator, p, newSize) + if newSize > oldSize: + zeroMem(cast[pointer](cast[int](result) + oldSize), newSize - oldSize) when false: proc countFreeMem(): int = @@ -1034,33 +1044,41 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = var heapLock: SysLock initSysLock(heapLock) - proc allocShared(size: Natural): pointer = + proc allocSharedImpl(size: Natural): pointer = when hasThreadSupport: acquireSys(heapLock) result = alloc(sharedHeap, size) releaseSys(heapLock) else: - result = alloc(size) + result = allocImpl(size) - proc allocShared0(size: Natural): pointer = - result = allocShared(size) + proc allocShared0Impl(size: Natural): pointer = + result = allocSharedImpl(size) zeroMem(result, size) - proc deallocShared(p: pointer) = + proc deallocSharedImpl(p: pointer) = when hasThreadSupport: acquireSys(heapLock) dealloc(sharedHeap, p) releaseSys(heapLock) else: - dealloc(p) + deallocImpl(p) - proc reallocShared(p: pointer, newSize: Natural): pointer = + proc reallocSharedImpl(p: pointer, newSize: Natural): pointer = when hasThreadSupport: acquireSys(heapLock) result = realloc(sharedHeap, p, newSize) releaseSys(heapLock) else: - result = realloc(p, newSize) + result = reallocImpl(p, newSize) + + proc reallocShared0Impl(p: pointer, oldSize, newSize: Natural): pointer = + when hasThreadSupport: + acquireSys(heapLock) + result = realloc0(sharedHeap, p, oldSize, newSize) + releaseSys(heapLock) + else: + result = realloc0Impl(p, oldSize, newSize) when hasThreadSupport: template sharedMemStatsShared(v: int) = diff --git a/lib/system/allocators.nim b/lib/system/allocators.nim deleted file mode 100644 index e642999a8..000000000 --- a/lib/system/allocators.nim +++ /dev/null @@ -1,86 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2017 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Unstable API. - -type - AllocatorFlag* {.pure.} = enum ## flags describing the properties of the allocator - ThreadLocal ## the allocator is thread local only. - ZerosMem ## the allocator always zeros the memory on an allocation - Allocator* = ptr AllocatorObj - AllocatorObj* {.inheritable, compilerproc.} = object - alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall, raises: [], tags: [], gcsafe.} - dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall, raises: [], tags: [], gcsafe.} - realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall, raises: [], tags: [], gcsafe.} - deallocAll*: proc (a: Allocator) {.nimcall, raises: [], tags: [], gcsafe.} - flags*: set[AllocatorFlag] - name*: cstring - allocCount: int - deallocCount: int - -var - localAllocator {.threadvar.}: Allocator - sharedAllocator: Allocator - allocatorStorage {.threadvar.}: AllocatorObj - -when defined(useMalloc) and not defined(nimscript): - import "system/ansi_c" - -import "system/memory" - -template `+!`(p: pointer, s: int): pointer = - cast[pointer](cast[int](p) +% s) - -proc getLocalAllocator*(): Allocator = - result = localAllocator - if result == nil: - result = addr allocatorStorage - result.alloc = proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall, raises: [].} = - when defined(useMalloc) and not defined(nimscript): - result = c_malloc(cuint size) - # XXX do we need this? - nimZeroMem(result, size) - elif compileOption("threads"): - result = system.allocShared0(size) - else: - result = system.alloc0(size) - inc a.allocCount - result.dealloc = proc (a: Allocator; p: pointer; size: int) {.nimcall, raises: [].} = - when defined(useMalloc) and not defined(nimscript): - c_free(p) - elif compileOption("threads"): - system.deallocShared(p) - else: - system.dealloc(p) - inc a.deallocCount - result.realloc = proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall, raises: [].} = - when defined(useMalloc) and not defined(nimscript): - result = c_realloc(p, cuint newSize) - elif compileOption("threads"): - result = system.reallocShared(p, newSize) - else: - result = system.realloc(p, newSize) - nimZeroMem(result +! oldSize, newSize - oldSize) - result.deallocAll = nil - result.flags = {ThreadLocal, ZerosMem} - result.name = "nim_local" - localAllocator = result - -proc setLocalAllocator*(a: Allocator) = - localAllocator = a - -proc getSharedAllocator*(): Allocator = - result = sharedAllocator - -proc setSharedAllocator*(a: Allocator) = - sharedAllocator = a - -proc allocCounters*(): (int, int) = - let a = getLocalAllocator() - result = (a.allocCount, a.deallocCount) diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 55e004f6e..6649e9517 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -140,6 +140,8 @@ proc c_sprintf*(buf, frmt: cstring): cint {. proc c_malloc*(size: csize_t): pointer {. importc: "malloc", header: "<stdlib.h>".} +proc c_calloc*(nmemb, size: csize_t): pointer {. + importc: "calloc", header: "<stdlib.h>".} proc c_free*(p: pointer) {. importc: "free", header: "<stdlib.h>".} proc c_realloc*(p: pointer, newsize: csize_t): pointer {. diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim index 963fc7b78..6b1640ab1 100644 --- a/lib/system/gc_regions.nim +++ b/lib/system/gc_regions.nim @@ -377,16 +377,21 @@ proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, deprecated: "old compiler compat".} = asgnRef(dest, src) -proc alloc(size: Natural): pointer = +proc allocImpl(size: Natural): pointer = result = c_malloc(cast[csize_t](size)) if result == nil: raiseOutOfMem() -proc alloc0(size: Natural): pointer = +proc alloc0Impl(size: Natural): pointer = result = alloc(size) zeroMem(result, size) -proc realloc(p: pointer, newsize: Natural): pointer = +proc reallocImpl(p: pointer, newsize: Natural): pointer = result = c_realloc(p, cast[csize_t](newsize)) if result == nil: raiseOutOfMem() -proc dealloc(p: pointer) = c_free(p) +proc realloc0Impl(p: pointer, oldsize, newsize: Natural): pointer = + result = c_realloc(p, cast[csize_t](newsize)) + if result == nil: raiseOutOfMem() + if newsize > oldsize: + zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize) +proc deallocImpl(p: pointer) = c_free(p) proc alloc0(r: var MemRegion; size: Natural): pointer = # ignore the region. That is correct for the channels module @@ -400,16 +405,21 @@ proc alloc(r: var MemRegion; size: Natural): pointer = proc dealloc(r: var MemRegion; p: pointer) = dealloc(p) -proc allocShared(size: Natural): pointer = +proc allocSharedImpl(size: Natural): pointer = result = c_malloc(cast[csize_t](size)) if result == nil: raiseOutOfMem() -proc allocShared0(size: Natural): pointer = +proc allocShared0Impl(size: Natural): pointer = result = alloc(size) zeroMem(result, size) -proc reallocShared(p: pointer, newsize: Natural): pointer = +proc reallocSharedImpl(p: pointer, newsize: Natural): pointer = + result = c_realloc(p, cast[csize_t](newsize)) + if result == nil: raiseOutOfMem() +proc reallocShared0Impl(p: pointer, oldsize, newsize: Natural): pointer = result = c_realloc(p, cast[csize_t](newsize)) if result == nil: raiseOutOfMem() -proc deallocShared(p: pointer) = c_free(p) + if newsize > oldsize: + zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize) +proc deallocSharedImpl(p: pointer) = c_free(p) when hasThreadSupport: proc getFreeSharedMem(): int = 0 diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim index b48a6d70a..201e92e98 100644 --- a/lib/system/memalloc.nim +++ b/lib/system/memalloc.nim @@ -33,8 +33,48 @@ when notJSnotNims: ## otherwise. Like any procedure dealing with raw memory this is ## **unsafe**. -when hasAlloc: - proc alloc*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} +when hasAlloc and not defined(js): + + proc allocImpl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} + proc alloc0Impl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} + proc deallocImpl*(p: pointer) {.noconv, rtl, tags: [], benign, raises: [].} + proc reallocImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} + proc realloc0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} + + proc allocSharedImpl*(size: Natural): pointer {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} + proc allocShared0Impl*(size: Natural): pointer {.noconv, rtl, benign, raises: [], tags: [].} + proc deallocSharedImpl*(p: pointer) {.noconv, rtl, benign, raises: [], tags: [].} + proc reallocSharedImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} + proc reallocShared0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} + + # Allocator statistics for memory leak tests + + {.push stackTrace: off.} + + type AllocStats* = object + allocCount: int + deallocCount: int + + proc `-`*(a, b: AllocStats): AllocStats = + result.allocCount = a.allocCount - b.allocCount + result.deallocCount = a.deallocCount - b.deallocCount + + template dumpAllocstats*(code: untyped) = + let stats1 = getAllocStats() + code + let stats2 = getAllocStats() + echo $(stats2 - stats1) + + when defined(allocStats): + var stats: AllocStats + template incStat(what: untyped) = inc stats.what + proc getAllocStats*(): AllocStats = stats + + else: + template incStat(what: untyped) = discard + proc getAllocStats*(): AllocStats = discard + + template alloc*(size: Natural): pointer = ## Allocates a new memory block with at least ``size`` bytes. ## ## The block has to be freed with `realloc(block, 0) <#realloc,pointer,Natural>`_ @@ -47,6 +87,9 @@ when hasAlloc: ## ## See also: ## * `alloc0 <#alloc0,Natural>`_ + incStat(allocCount) + allocImpl(size) + proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} = ## Allocates a new memory block with at least ``T.sizeof * size`` bytes. ## @@ -62,7 +105,7 @@ when hasAlloc: ## * `create <#create,typedesc>`_ cast[ptr T](alloc(T.sizeof * size)) - proc alloc0*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} + template alloc0*(size: Natural): pointer = ## Allocates a new memory block with at least ``size`` bytes. ## ## The block has to be freed with `realloc(block, 0) <#realloc,pointer,Natural>`_ @@ -72,6 +115,9 @@ when hasAlloc: ## ## The allocated memory belongs to its allocating thread! ## Use `allocShared0 <#allocShared0,Natural>`_ to allocate from a shared heap. + incStat(allocCount) + alloc0Impl(size) + proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} = ## Allocates a new memory block with at least ``T.sizeof * size`` bytes. ## @@ -84,8 +130,21 @@ when hasAlloc: ## Use `createShared <#createShared,typedesc>`_ to allocate from a shared heap. cast[ptr T](alloc0(sizeof(T) * size)) - proc realloc*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], - benign, raises: [].} + template realloc*(p: pointer, newSize: Natural): pointer = + ## Grows or shrinks a given memory block. + ## + ## If `p` is **nil** then a new memory block is returned. + ## In either way the block has at least ``newSize`` bytes. + ## If ``newSize == 0`` and `p` is not **nil** ``realloc`` calls ``dealloc(p)``. + ## In other cases the block has to be freed with + ## `dealloc(block) <#dealloc,pointer>`_. + ## + ## The allocated memory belongs to its allocating thread! + ## Use `reallocShared <#reallocShared,pointer,Natural>`_ to reallocate + ## from a shared heap. + reallocImpl(p, newSize) + + template realloc0*(p: pointer, oldSize, newSize: Natural): pointer = ## Grows or shrinks a given memory block. ## ## If `p` is **nil** then a new memory block is returned. @@ -94,9 +153,14 @@ when hasAlloc: ## In other cases the block has to be freed with ## `dealloc(block) <#dealloc,pointer>`_. ## + ## The block is initialized with all bytes containing zero, so it is + ## somewhat safer then realloc + ## ## The allocated memory belongs to its allocating thread! ## Use `reallocShared <#reallocShared,pointer,Natural>`_ to reallocate ## from a shared heap. + realloc0Impl(p, oldSize, newSize) + proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline, benign, raises: [].} = ## Grows or shrinks a given memory block. ## @@ -110,7 +174,7 @@ when hasAlloc: ## from a shared heap. cast[ptr T](realloc(p, T.sizeof * newSize)) - proc dealloc*(p: pointer) {.noconv, rtl, tags: [], benign, raises: [].} + template dealloc*(p: pointer) = ## Frees the memory allocated with ``alloc``, ``alloc0`` or ## ``realloc``. ## @@ -121,8 +185,10 @@ when hasAlloc: ## ## The freed memory must belong to its allocating thread! ## Use `deallocShared <#deallocShared,pointer>`_ to deallocate from a shared heap. + incStat(deallocCount) + deallocImpl(p) - proc allocShared*(size: Natural): pointer {.noconv, rtl, benign, raises: [], tags: [].} + template allocShared*(size: Natural): pointer = ## Allocates a new memory block on the shared heap with at ## least ``size`` bytes. ## @@ -135,6 +201,9 @@ when hasAlloc: ## ## See also: ## `allocShared0 <#allocShared0,Natural>`_. + incStat(allocCount) + allocSharedImpl(size) + proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, tags: [], benign, raises: [].} = ## Allocates a new memory block on the shared heap with at @@ -151,7 +220,7 @@ when hasAlloc: ## * `createShared <#createShared,typedesc>`_ cast[ptr T](allocShared(T.sizeof * size)) - proc allocShared0*(size: Natural): pointer {.noconv, rtl, benign, raises: [], tags: [].} + template allocShared0*(size: Natural): pointer = ## Allocates a new memory block on the shared heap with at ## least ``size`` bytes. ## @@ -162,6 +231,9 @@ when hasAlloc: ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than ## `allocShared <#allocShared,Natural>`_. + incStat(allocCount) + allocShared0Impl(size) + proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} = ## Allocates a new memory block on the shared heap with at ## least ``T.sizeof * size`` bytes. @@ -175,8 +247,7 @@ when hasAlloc: ## `createSharedU <#createSharedU,typedesc>`_. cast[ptr T](allocShared0(T.sizeof * size)) - proc reallocShared*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], - benign, raises: [].} + template reallocShared*(p: pointer, newSize: Natural): pointer = ## Grows or shrinks a given memory block on the heap. ## ## If `p` is **nil** then a new memory block is returned. @@ -185,6 +256,22 @@ when hasAlloc: ## ``deallocShared(p)``. ## In other cases the block has to be freed with ## `deallocShared <#deallocShared,pointer>`_. + reallocSharedImpl(p, newSize) + + template reallocShared0*(p: pointer, oldSize, newSize: Natural): pointer = + ## Grows or shrinks a given memory block on the heap. + ## + ## When growing, the new bytes of the block is initialized with all bytes + ## containing zero, so it is somewhat safer then reallocShared + ## + ## If `p` is **nil** then a new memory block is returned. + ## In either way the block has at least ``newSize`` bytes. + ## If ``newSize == 0`` and `p` is not **nil** ``reallocShared`` calls + ## ``deallocShared(p)``. + ## In other cases the block has to be freed with + ## `deallocShared <#deallocShared,pointer>`_. + reallocShared0Impl(p, oldSize, newSize) + proc resizeShared*[T](p: ptr T, newSize: Natural): ptr T {.inline, raises: [].} = ## Grows or shrinks a given memory block on the heap. ## @@ -196,7 +283,7 @@ when hasAlloc: ## `freeShared <#freeShared,ptr.T>`_. cast[ptr T](reallocShared(p, T.sizeof * newSize)) - proc deallocShared*(p: pointer) {.noconv, rtl, benign, raises: [], tags: [].} + proc deallocShared*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} = ## Frees the memory allocated with ``allocShared``, ``allocShared0`` or ## ``reallocShared``. ## @@ -204,6 +291,9 @@ when hasAlloc: ## If one forgets to free the memory a leak occurs; if one tries to ## access freed memory (or just freeing it twice!) a core dump may happen ## or other memory may be corrupted. + incStat(deallocCount) + deallocSharedImpl(p) + proc freeShared*[T](p: ptr T) {.inline, benign, raises: [].} = ## Frees the memory allocated with ``createShared``, ``createSharedU`` or ## ``resizeShared``. @@ -214,6 +304,7 @@ when hasAlloc: ## or other memory may be corrupted. deallocShared(p) + {.pop.} # GC interface: @@ -239,11 +330,13 @@ when defined(js): proc alloc(size: Natural): pointer = discard proc alloc0(size: Natural): pointer = discard proc realloc(p: pointer, newsize: Natural): pointer = discard + proc realloc0(p: pointer, oldsize, newsize: Natural): pointer = discard proc allocShared(size: Natural): pointer = discard proc allocShared0(size: Natural): pointer = discard proc deallocShared(p: pointer) = discard proc reallocShared(p: pointer, newsize: Natural): pointer = discard + proc reallocShared0(p: pointer, oldsize, newsize: Natural): pointer = discard when hasAlloc and hasThreadSupport: diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index faa7813fb..1f42f1aa5 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -107,25 +107,26 @@ when defined(boehmgc): when not defined(useNimRtl): - proc alloc(size: Natural): pointer = + proc allocImpl(size: Natural): pointer = result = boehmAlloc(size) if result == nil: raiseOutOfMem() - proc alloc0(size: Natural): pointer = + proc alloc0Impl(size: Natural): pointer = result = alloc(size) - proc realloc(p: pointer, newSize: Natural): pointer = + proc reallocImpl(p: pointer, newSize: Natural): pointer = result = boehmRealloc(p, newSize) if result == nil: raiseOutOfMem() - proc dealloc(p: pointer) = boehmDealloc(p) - - proc allocShared(size: Natural): pointer = - result = boehmAlloc(size) - if result == nil: raiseOutOfMem() - proc allocShared0(size: Natural): pointer = - result = allocShared(size) - proc reallocShared(p: pointer, newSize: Natural): pointer = + proc realloc0Impl(p: pointer, oldSize, newSize: Natural): pointer = result = boehmRealloc(p, newSize) if result == nil: raiseOutOfMem() - proc deallocShared(p: pointer) = boehmDealloc(p) + if newsize > oldsize: + zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize) + proc deallocImpl(p: pointer) = boehmDealloc(p) + + proc allocSharedImpl(size: Natural): pointer = allocImpl(size) + proc allocShared0Impl(size: Natural): pointer = alloc0Impl(size) + proc reallocSharedImpl(p: pointer, newsize: Natural): pointer = reallocImpl(p, newsize) + proc reallocShared0Impl(p: pointer, oldsize, newsize: Natural): pointer = realloc0Impl(p, oldsize, newsize) + proc deallocSharedImpl(p: pointer) = deallocImpl(p) when hasThreadSupport: proc getFreeSharedMem(): int = @@ -265,28 +266,26 @@ elif defined(gogc): proc nimGC_setStackBottom(theStackBottom: pointer) = discard - proc alloc(size: Natural): pointer = + proc allocImpl(size: Natural): pointer = result = goMalloc(size.uint) - proc alloc0(size: Natural): pointer = + proc alloc0Impl(size: Natural): pointer = result = goMalloc(size.uint) - proc realloc(p: pointer, newsize: Natural): pointer = + proc reallocImpl(p: pointer, newsize: Natural): pointer = doAssert false, "not implemented" - proc dealloc(p: pointer) = - discard - - proc allocShared(size: Natural): pointer = - result = alloc(size) - - proc allocShared0(size: Natural): pointer = - result = alloc0(size) + proc realloc0Impl(p: pointer, oldsize, newsize: Natural): pointer = + doAssert false, "not implemented" - proc reallocShared(p: pointer, newsize: Natural): pointer = - result = realloc(p, newsize) + proc deallocImpl(p: pointer) = + discard - proc deallocShared(p: pointer) = dealloc(p) + proc allocSharedImpl(size: Natural): pointer = allocImpl(size) + proc allocShared0Impl(size: Natural): pointer = alloc0Impl(size) + proc reallocSharedImpl(p: pointer, newsize: Natural): pointer = reallocImpl(p, newsize) + proc reallocShared0Impl(p: pointer, oldsize, newsize: Natural): pointer = realloc0Impl(p, oldsize, newsize) + proc deallocSharedImpl(p: pointer) = deallocImpl(p) when hasThreadSupport: proc getFreeSharedMem(): int = discard @@ -349,7 +348,7 @@ elif defined(gogc): proc alloc(r: var MemRegion, size: int): pointer = result = alloc(size) proc alloc0(r: var MemRegion, size: int): pointer = - result = alloc0(size) + result = alloc0Impl(size) proc dealloc(r: var MemRegion, p: pointer) = dealloc(p) proc deallocOsPages(r: var MemRegion) {.inline.} = discard proc deallocOsPages() {.inline.} = discard @@ -357,42 +356,21 @@ elif defined(gogc): elif (defined(nogc) or defined(gcDestructors)) and defined(useMalloc): when not defined(useNimRtl): - proc alloc(size: Natural): pointer = - var x = c_malloc (size + sizeof(size)).csize_t - if x == nil: raiseOutOfMem() - - cast[ptr int](x)[] = size - result = cast[pointer](cast[int](x) + sizeof(size)) - - proc alloc0(size: Natural): pointer = - result = alloc(size) - zeroMem(result, size) - proc realloc(p: pointer, newsize: Natural): pointer = - var x = cast[pointer](cast[int](p) - sizeof(newsize)) - let oldsize = cast[ptr int](x)[] - - x = c_realloc(x, (newsize + sizeof(newsize)).csize_t) - - if x == nil: raiseOutOfMem() - - cast[ptr int](x)[] = newsize - result = cast[pointer](cast[int](x) + sizeof(newsize)) + proc allocImpl(size: Natural): pointer = c_malloc(size.csize_t) + proc alloc0Impl(size: Natural): pointer = c_calloc(size.csize_t, 1) + proc reallocImpl(p: pointer, newsize: Natural): pointer = c_realloc(p, newSize.csize_t) + proc realloc0Impl(p: pointer, oldsize, newsize: Natural): pointer = + result = realloc(p, newsize.csize_t) if newsize > oldsize: zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize) + proc deallocImpl(p: pointer) = c_free(p) - proc dealloc(p: pointer) = c_free(cast[pointer](cast[int](p) - sizeof(int))) - - proc allocShared(size: Natural): pointer = - result = c_malloc(size.csize_t) - if result == nil: raiseOutOfMem() - proc allocShared0(size: Natural): pointer = - result = alloc(size) - zeroMem(result, size) - proc reallocShared(p: pointer, newsize: Natural): pointer = - result = c_realloc(p, newsize.csize_t) - if result == nil: raiseOutOfMem() - proc deallocShared(p: pointer) = c_free(p) + proc allocSharedImpl(size: Natural): pointer = allocImpl(size) + proc allocShared0Impl(size: Natural): pointer = alloc0Impl(size) + proc reallocSharedImpl(p: pointer, newsize: Natural): pointer = reallocImpl(p, newsize) + proc reallocShared0Impl(p: pointer, oldsize, newsize: Natural): pointer = realloc0Impl(p, oldsize, newsize) + proc deallocSharedImpl(p: pointer) = deallocImpl(p) proc GC_disable() = discard proc GC_enable() = discard @@ -400,7 +378,6 @@ elif (defined(nogc) or defined(gcDestructors)) and defined(useMalloc): proc GC_setStrategy(strategy: GC_Strategy) = discard proc GC_enableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard - #proc GC_getStatistics(): string = return "" proc getOccupiedMem(): int = discard proc getFreeMem(): int = discard @@ -431,8 +408,8 @@ elif (defined(nogc) or defined(gcDestructors)) and defined(useMalloc): proc alloc(r: var MemRegion, size: int): pointer = result = alloc(size) - proc alloc0(r: var MemRegion, size: int): pointer = - result = alloc0(size) + proc alloc0Impl(r: var MemRegion, size: int): pointer = + result = alloc0Impl(size) proc dealloc(r: var MemRegion, p: pointer) = dealloc(p) proc deallocOsPages(r: var MemRegion) {.inline.} = discard proc deallocOsPages() {.inline.} = discard @@ -459,7 +436,7 @@ elif defined(nogc): proc GC_getStatistics(): string = return "" proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = - result = alloc0(size) + result = alloc0Impl(size) proc newObjNoInit(typ: PNimType, size: int): pointer = result = alloc(size) diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index ed49d3abc..f28bcadca 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -9,15 +9,18 @@ # import typetraits -# strs already imported allocators for us. +# strs already imported allocateds for us. proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".} ## Default seq implementation used by Nim's core. type + + NimSeqPayloadBase = object + cap: int + NimSeqPayload[T] = object cap: int - allocator: Allocator data: UncheckedArray[T] NimSeqV2*[T] = object @@ -26,22 +29,13 @@ type const nimSeqVersion {.core.} = 2 -template payloadSize(cap): int = cap * sizeof(T) + sizeof(int) + sizeof(Allocator) - # XXX make code memory safe for overflows in '*' -type - PayloadBase = object - cap: int - allocator: Allocator - proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl, raises: [].} = # we have to use type erasure here as Nim does not support generic # compilerProcs. Oh well, this will all be inlined anyway. if cap > 0: - let allocator = getLocalAllocator() - var p = cast[ptr PayloadBase](allocator.alloc(allocator, cap * elemSize + sizeof(int) + sizeof(Allocator))) - p.allocator = allocator + var p = cast[ptr NimSeqPayloadBase](allocShared0(cap * elemSize + sizeof(NimSeqPayloadBase))) p.cap = cap result = p else: @@ -53,7 +47,7 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {. template `+!`(p: pointer, s: int): pointer = cast[pointer](cast[int](p) +% s) - const headerSize = sizeof(int) + sizeof(Allocator) + const headerSize = sizeof(NimSeqPayloadBase) if addlen <= 0: result = p elif p == nil: @@ -61,23 +55,19 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {. else: # Note: this means we cannot support things that have internal pointers as # they get reallocated here. This needs to be documented clearly. - var p = cast[ptr PayloadBase](p) - let cap = max(resize(p.cap), len+addlen) - if p.allocator == nil: - let allocator = getLocalAllocator() - var q = cast[ptr PayloadBase](allocator.alloc(allocator, - headerSize + elemSize * cap)) + var p = cast[ptr NimSeqPayloadBase](p) + let oldCap = p.cap and not strlitFlag + let newCap = max(resize(oldCap), len+addlen) + if (p.cap and strlitFlag) == strlitFlag: + var q = cast[ptr NimSeqPayloadBase](allocShared0(headerSize + elemSize * newCap)) copyMem(q +! headerSize, p +! headerSize, len * elemSize) - q.allocator = allocator - q.cap = cap + q.cap = newCap result = q else: - let allocator = p.allocator - var q = cast[ptr PayloadBase](allocator.realloc(allocator, p, - headerSize + elemSize * p.cap, - headerSize + elemSize * cap)) - q.allocator = allocator - q.cap = cap + let oldSize = headerSize + elemSize * oldCap + let newSize = headerSize + elemSize * newCap + var q = cast[ptr NimSeqPayloadBase](reallocShared0(p, oldSize, newSize)) + q.cap = newCap result = q proc shrink*[T](x: var seq[T]; newLen: Natural) = diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 555028f31..b4faba749 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -9,12 +9,12 @@ ## Default new string implementation used by Nim's core. -import allocators - type + NimStrPayloadBase = object + cap: int + NimStrPayload {.core.} = object cap: int - allocator: Allocator data: UncheckedArray[char] NimStringV2 {.core.} = object @@ -23,13 +23,13 @@ type const nimStrVersion {.core.} = 2 -template isLiteral(s): bool = s.p == nil or s.p.allocator == nil +template isLiteral(s): bool = (s.p == nil) or (s.p.cap and strlitFlag) == strlitFlag -template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator) +template contentSize(cap): int = cap + 1 + sizeof(NimStrPayloadBase) template frees(s) = if not isLiteral(s): - s.p.allocator.dealloc(s.p.allocator, s.p, contentSize(s.p.cap)) + deallocShared(s.p) proc resize(old: int): int {.inline.} = if old <= 0: result = 4 @@ -41,19 +41,17 @@ proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} = if addlen > 0: let oldP = s.p # can't mutate a literal, so we need a fresh copy here: - let allocator = getLocalAllocator() - s.p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(s.len + addlen))) - s.p.allocator = allocator + s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len + addlen))) s.p.cap = s.len + addlen if s.len > 0: # we are about to append, so there is no need to copy the \0 terminator: copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len) - elif s.len + addlen > s.p.cap: - let cap = max(s.len + addlen, resize(s.p.cap)) - s.p = cast[ptr NimStrPayload](s.p.allocator.realloc(s.p.allocator, s.p, - oldSize = contentSize(s.p.cap), - newSize = contentSize(cap))) - s.p.cap = cap + else: + let oldCap = s.p.cap and not strlitFlag + if s.len + addlen > oldCap: + let newCap = max(s.len + addlen, resize(oldCap)) + s.p = cast[ptr NimStrPayload](reallocShared0(s.p, contentSize(oldCap), contentSize(newCap))) + s.p.cap = newCap proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} = prepareAdd(s, 1) @@ -65,9 +63,7 @@ proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerproc.} = if len <= 0: result = NimStringV2(len: 0, p: nil) else: - let allocator = getLocalAllocator() - var p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(len))) - p.allocator = allocator + var p = cast[ptr NimStrPayload](allocShared0(contentSize(len))) p.cap = len if len > 0: # we are about to append, so there is no need to copy the \0 terminator: @@ -98,9 +94,7 @@ proc rawNewString(space: int): NimStringV2 {.compilerproc.} = if space <= 0: result = NimStringV2(len: 0, p: nil) else: - let allocator = getLocalAllocator() - var p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(space))) - p.allocator = allocator + var p = cast[ptr NimStrPayload](allocShared0(contentSize(space))) p.cap = space result = NimStringV2(len: 0, p: p) @@ -108,9 +102,7 @@ proc mnewString(len: int): NimStringV2 {.compilerproc.} = if len <= 0: result = NimStringV2(len: 0, p: nil) else: - let allocator = getLocalAllocator() - var p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(len))) - p.allocator = allocator + var p = cast[ptr NimStrPayload](allocShared0(contentSize(len))) p.cap = len result = NimStringV2(len: len, p: p) @@ -130,24 +122,20 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} = a.len = b.len a.p = b.p else: - if isLiteral(a) or a.p.cap < b.len: - let allocator = if a.p != nil and a.p.allocator != nil: a.p.allocator else: getLocalAllocator() + if isLiteral(a) or (a.p.cap and not strlitFlag) < b.len: # we have to allocate the 'cap' here, consider # 'let y = newStringOfCap(); var x = y' # on the other hand... These get turned into moves now. frees(a) - a.p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(b.len))) - a.p.allocator = allocator + a.p = cast[ptr NimStrPayload](allocShared0(contentSize(b.len))) a.p.cap = b.len a.len = b.len copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1) proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl.} = - if s.p != nil and s.p.allocator == nil: + if s.p != nil and (s.p.cap and strlitFlag) == strlitFlag: let oldP = s.p # can't mutate a literal, so we need a fresh copy here: - let allocator = getLocalAllocator() - s.p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(s.len))) - s.p.allocator = allocator + s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len))) s.p.cap = s.len copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1) diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index f3a6f9d77..59e507a4d 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -18,8 +18,6 @@ type when defined(nimv2): - import system / allocators - type WideCString* = ptr UncheckedArray[Utf16Char] @@ -29,8 +27,7 @@ when defined(nimv2): proc `=destroy`(a: var WideCStringObj) = if a.data != nil: - let alor = getLocalAllocator() - alor.dealloc(alor, a.data, a.bytes) + deallocShared(a.data) a.data = nil proc `=`(a: var WideCStringObj; b: WideCStringObj) {.error.} @@ -41,8 +38,7 @@ when defined(nimv2): proc createWide(a: var WideCStringObj; bytes: int) = a.bytes = bytes - let alor = getLocalAllocator() - a.data = cast[typeof(a.data)](alor.alloc(alor, bytes)) + a.data = cast[typeof(a.data)](allocShared0(bytes)) template `[]`(a: WideCStringObj; idx: int): Utf16Char = a.data[idx] template `[]=`(a: WideCStringObj; idx: int; val: Utf16Char) = a.data[idx] = val |