summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgexprs.nim5
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/semtypes.nim4
-rw-r--r--compiler/types.nim2
-rw-r--r--lib/core/allocators.nim40
-rw-r--r--lib/core/strs.nim132
-rw-r--r--lib/system.nim34
-rw-r--r--lib/system/sysstr.nim10
8 files changed, 159 insertions, 69 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index b8a7f3b08..736701780 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1902,8 +1902,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
      mInSet:
     genSetOp(p, e, d, op)
-  of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit,
-      mParseBiggestFloat:
+  of mCopyStr, mCopyStrLast:
+    genCall(p, e, d)
+  of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat:
     var opr = e.sons[0].sym
     if lfNoDecl notin opr.loc.flags:
       discard cgsym(p.module, $opr.loc.r)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 8b2d36722..fcc7998df 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -72,3 +72,4 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimNoZeroTerminator")
   defineSymbol("nimNotNil")
   defineSymbol("nimVmExportFixed")
+  defineSymbol("nimNewRuntime")
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 17420111f..e888b7f7c 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1671,6 +1671,8 @@ proc processMagicType(c: PContext, m: PSym) =
   of mString:
     setMagicType(c.config, m, tyString, c.config.target.ptrSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
+    if c.config.selectedGc == gcDestructors:
+      incl m.typ.flags, tfHasAsgn
   of mCstring:
     setMagicType(c.config, m, tyCString, c.config.target.ptrSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
@@ -1710,6 +1712,8 @@ proc processMagicType(c: PContext, m: PSym) =
     setMagicType(c.config, m, tySet, 0)
   of mSeq:
     setMagicType(c.config, m, tySequence, 0)
+    if c.config.selectedGc == gcDestructors:
+      incl m.typ.flags, tfHasAsgn
   of mOpt:
     setMagicType(c.config, m, tyOpt, 0)
   of mOrdinal:
diff --git a/compiler/types.nim b/compiler/types.nim
index 4d3f99c31..66807cf97 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -278,6 +278,8 @@ proc analyseObjectWithTypeField(t: PType): TTypeFieldResult =
 proc isGCRef(t: PType): bool =
   result = t.kind in GcTypeKinds or
     (t.kind == tyProc and t.callConv == ccClosure)
+  if result and t.kind in {tyString, tySequence} and tfHasAsgn in t.flags:
+    result = false
 
 proc containsGarbageCollectedRef*(typ: PType): bool =
   # returns true if typ contains a reference, sequence or string (all the
diff --git a/lib/core/allocators.nim b/lib/core/allocators.nim
index 62f5e9756..74a8d3753 100644
--- a/lib/core/allocators.nim
+++ b/lib/core/allocators.nim
@@ -8,28 +8,40 @@
 #
 
 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 object {.inheritable.}
     alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.}
     dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.}
     realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.}
+    flags*: set[AllocatorFlag]
 
 var
-  currentAllocator {.threadvar.}: Allocator
+  localAllocator {.threadvar.}: Allocator
+  sharedAllocator: Allocator
 
-proc getCurrentAllocator*(): Allocator =
-  result = currentAllocator
+proc getLocalAllocator*(): Allocator =
+  result = localAllocator
 
-proc setCurrentAllocator*(a: Allocator) =
-  currentAllocator = a
+proc setLocalAllocator*(a: Allocator) =
+  localAllocator = a
 
-proc alloc*(size: int; alignment: int = 8): pointer =
-  let a = getCurrentAllocator()
-  result = a.alloc(a, size, alignment)
+proc getSharedAllocator*(): Allocator =
+  result = sharedAllocator
 
-proc dealloc*(p: pointer; size: int) =
-  let a = getCurrentAllocator()
-  a.dealloc(a, p, size)
+proc setSharedAllocator*(a: Allocator) =
+  sharedAllocator = a
 
-proc realloc*(p: pointer; oldSize, newSize: int): pointer =
-  let a = getCurrentAllocator()
-  result = a.realloc(a, p, oldSize, newSize)
+when false:
+  proc alloc*(size: int; alignment: int = 8): pointer =
+    let a = getCurrentAllocator()
+    result = a.alloc(a, size, alignment)
+
+  proc dealloc*(p: pointer; size: int) =
+    let a = getCurrentAllocator()
+    a.dealloc(a, p, size)
+
+  proc realloc*(p: pointer; oldSize, newSize: int): pointer =
+    let a = getCurrentAllocator()
+    result = a.realloc(a, p, oldSize, newSize)
diff --git a/lib/core/strs.nim b/lib/core/strs.nim
index ff38aef1d..1ef4f09de 100644
--- a/lib/core/strs.nim
+++ b/lib/core/strs.nim
@@ -7,54 +7,114 @@
 #    distribution, for details about the copyright.
 #
 
-## Default string implementation used by Nim's core.
+## Default new string implementation used by Nim's core.
+
+when false:
+  # these are to be implemented or changed in the code generator.
+
+  #proc rawNewStringNoInit(space: int): NimString {.compilerProc.}
+  # seems to be unused.
+  proc rawNewString(space: int): NimString {.compilerProc.}
+  proc mnewString(len: int): NimString {.compilerProc.}
+  proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.}
+  proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.}
+  proc copyStr(s: NimString, start: int): NimString {.compilerProc.}
+  proc toNimStr(str: cstring, len: int): NimString {.compilerProc.}
+  proc cstrToNimstr(str: cstring): NimString {.compilerRtl.}
+  proc copyString(src: NimString): NimString {.compilerRtl.}
+  proc copyStringRC1(src: NimString): NimString {.compilerRtl.}
+  proc copyDeepString(src: NimString): NimString {.inline.}
+  proc addChar(s: NimString, c: char): NimString
+  proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.}
+  proc appendString(dest, src: NimString) {.compilerproc, inline.}
+  proc appendChar(dest: NimString, c: char) {.compilerproc, inline.}
+  proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.}
+  # ----------------- sequences ----------------------------------------------
+
+  proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
+  proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
+      compilerRtl.}
+  proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
+  proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
 
 import allocators
 
 type
-  string {.core, exportc: "NimStringV2".} = object
-    len, cap: int
-    data: ptr UncheckedArray[char]
+  StrContent = object
+    cap: int
+    region: Allocator
+    data: UncheckedArray[char]
+
+  NimString {.core.} = object
+    len: int
+    p: ptr StrContent ## invariant. Never nil
 
 const nimStrVersion {.core.} = 2
 
-template frees(s) = dealloc(s.data, s.cap + 1)
+template isLiteral(s): bool = s.len == 0 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, contentSize(s.p.cap))
+
+proc `=destroy`(s: var NimString) =
+  frees(s)
+  s.len = 0
 
-proc `=destroy`(s: var string) =
-  if s.data != nil:
-    frees(s)
-    s.data = nil
-    s.len = 0
-    s.cap = 0
+template lose(a) =
+  frees(a)
 
-proc `=sink`(a: var string, b: string) =
+proc `=sink`(a: var NimString, b: NimString) =
   # we hope this is optimized away for not yet alive objects:
-  if a.data != nil and a.data != b.data:
-    frees(a)
+  if unlikely(a.p == b.p): return
+  lose(a)
   a.len = b.len
-  a.cap = b.cap
-  a.data = b.data
+  a.p = b.p
 
-proc `=`(a: var string; b: string) =
-  if a.data != nil and a.data != b.data:
-    frees(a)
-    a.data = nil
+proc `=`(a: var NimString; b: NimString) =
+  if unlikely(a.p == b.p): return
+  lose(a)
   a.len = b.len
-  a.cap = b.cap
-  if b.data != nil:
-    a.data = cast[type(a.data)](alloc(a.cap + 1))
-    copyMem(a.data, b.data, a.cap+1)
-
-proc resize(s: var string) =
-  let old = s.cap
-  if old == 0: s.cap = 8
-  else: s.cap = (s.cap * 3) shr 1
-  s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
-
-proc add*(s: var string; c: char) =
-  if s.len >= s.cap: resize(s)
-  s.data[s.len] = c
-  s.data[s.len+1] = '\0'
+  if isLiteral(b):
+    # we can shallow copy literals:
+    a.p = b.p
+  else:
+    let region = if 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 StrContent](region.alloc(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
+  elif old < 65536: result = old * 2
+  else: result = old * 3 div 2 # for large arrays * 3/2 is better
+
+proc prepareAdd(s: var NimString; addlen: int) =
+  if isLiteral(s):
+    let oldP = s.p
+    # can't mutate a literal, so we need a fresh copy here:
+    let region = getLocalAllocator()
+    s.p = cast[ptr StrContent](region.alloc(contentSize(s.len + addlen)))
+    s.p.region = region
+    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 = s.p.region.realloc(s.p, oldSize = contentSize(s.p.cap), newSize = contentSize(cap))
+    s.p.cap = cap
+
+proc nimAddCharV1(s: var NimString; c: char) {.compilerRtl.} =
+  prepareAdd(s, 1)
+  s.p.data[s.len] = c
+  s.p.data[s.len+1] = '\0'
   inc s.len
 
 proc ensure(s: var string; newLen: int) =
@@ -71,8 +131,6 @@ proc add*(s: var string; y: string) =
     copyMem(addr s.data[len], y.data, y.data.len + 1)
     s.len = newLen
 
-proc len*(s: string): int {.inline.} = s.len
-
 proc newString*(len: int): string =
   result.len = len
   result.cap = len
diff --git a/lib/system.nim b/lib/system.nim
index d8ecfd55f..620dff724 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -413,7 +413,7 @@ include "system/inclrtl"
 const NoFakeVars* = defined(nimscript) ## true if the backend doesn't support \
   ## "fake variables" like 'var EBADF {.importc.}: cint'.
 
-when not defined(JS):
+when not defined(JS) and not defined(gcDestructors):
   type
     TGenericSeq {.compilerproc, pure, inheritable.} = object
       len, reserved: int
@@ -1702,17 +1702,6 @@ proc addQuitProc*(QuitProc: proc() {.noconv.}) {.
 # In case of an unhandled exeption the exit handlers should
 # not be called explicitly! The user may decide to do this manually though.
 
-proc substr*(s: string, first = 0): string {.
-  magic: "CopyStr", importc: "copyStr", noSideEffect.}
-proc substr*(s: string, first, last: int): string {.
-  magic: "CopyStrLast", importc: "copyStrLast", noSideEffect.}
-  ## copies a slice of `s` into a new string and returns this new
-  ## string. The bounds `first` and `last` denote the indices of
-  ## the first and last characters that shall be copied. If ``last``
-  ## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len``
-  ## is used instead: This means ``substr`` can also be used to `cut`:idx:
-  ## or `limit`:idx: a string's length.
-
 when not defined(nimscript) and not defined(JS):
   proc zeroMem*(p: pointer, size: Natural) {.inline, benign.}
     ## overwrites the contents of the memory at ``p`` with the value 0.
@@ -3262,7 +3251,11 @@ when not defined(JS): #and not defined(nimscript):
     when hasAlloc: include "system/mmdisp"
     {.pop.}
     {.push stack_trace: off, profiler:off.}
-    when hasAlloc: include "system/sysstr"
+    when hasAlloc:
+      when defined(gcDestructors):
+        include "core/strs"
+      else:
+        include "system/sysstr"
     {.pop.}
     when hasAlloc: include "system/strmantle"
 
@@ -4171,6 +4164,21 @@ when not defined(js):
   proc toOpenArrayByte*(x: string; first, last: int): openarray[byte] {.
     magic: "Slice".}
 
+proc substr*(s: string, first, last: int): string =
+  let L = max(min(last, high(s)) - first + 1, 0)
+  result = newString(L)
+  for i in 0 .. L-1:
+    result[i] = s[i+first]
+
+proc substr*(s: string, first = 0): string =
+  ## copies a slice of `s` into a new string and returns this new
+  ## string. The bounds `first` and `last` denote the indices of
+  ## the first and last characters that shall be copied. If ``last``
+  ## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len``
+  ## is used instead: This means ``substr`` can also be used to `cut`:idx:
+  ## or `limit`:idx: a string's length.
+  result = substr(s, first, high(s))
+
 type
   ForLoopStmt* {.compilerProc.} = object ## special type that marks a macro
                                          ## as a `for-loop macro`:idx:
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 24ba02af6..9a73d3f9d 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -63,6 +63,8 @@ proc mnewString(len: int): NimString {.compilerProc.} =
   result.len = len
 
 proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
+  # This is not used by most recent versions of the compiler anymore, but
+  # required for bootstrapping purposes.
   let start = max(start, 0)
   let len = min(last, s.len-1) - start + 1
   if len > 0:
@@ -73,13 +75,15 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
   else:
     result = rawNewString(len)
 
+proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
+  # This is not used by most recent versions of the compiler anymore, but
+  # required for bootstrapping purposes.
+  result = copyStrLast(s, start, s.len-1)
+
 proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.} =
   if s == nil or s.len == 0: result = cstring""
   else: result = cstring(addr s.data)
 
-proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
-  result = copyStrLast(s, start, s.len-1)
-
 proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} =
   result = rawNewStringNoInit(len)
   result.len = len