WIP: strings/seqs based on destructors

This commit is contained in:
Andreas Rumpf 2018-07-17 13:19:42 +02:00
commit 32afdc09c6
13 changed files with 240 additions and 141 deletions

View file

@ -626,6 +626,7 @@ type
mIsPartOf, mAstToStr, mParallel, mIsPartOf, mAstToStr, mParallel,
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
mNewString, mNewStringOfCap, mParseBiggestFloat, mNewString, mNewStringOfCap, mParseBiggestFloat,
mMove, mWasMoved,
mReset, mReset,
mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mRef, mPtr, mVar, mDistinct, mVoid, mTuple,

View file

@ -1224,8 +1224,14 @@ proc genNewSeq(p: BProc, e: PNode) =
var a, b: TLoc var a, b: TLoc
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
genNewSeqAux(p, a, b.rdLoc) if p.config.selectedGC == gcDestructors:
gcUsage(p.config, e) let seqtype = skipTypes(e.sons[1].typ, abstractVarRange)
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
getSeqPayloadType(p.module, seqtype))
else:
genNewSeqAux(p, a, b.rdLoc)
gcUsage(p.config, e)
proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
let seqtype = skipTypes(e.typ, abstractVarRange) let seqtype = skipTypes(e.typ, abstractVarRange)
@ -1543,6 +1549,9 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
else: internalError(p.config, e.info, "genArrayLen()") else: internalError(p.config, e.info, "genArrayLen()")
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
if p.config.selectedGc == gcDestructors:
genCall(p, e, d)
return
var a, b: TLoc var a, b: TLoc
assert(d.k == locNone) assert(d.k == locNone)
var x = e.sons[1] var x = e.sons[1]
@ -1561,8 +1570,11 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
gcUsage(p.config, e) gcUsage(p.config, e)
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n") if p.config.selectedGc == gcDestructors:
gcUsage(p.config, e) binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n")
else:
binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n")
gcUsage(p.config, e)
proc genSwap(p: BProc, e: PNode, d: var TLoc) = proc genSwap(p: BProc, e: PNode, d: var TLoc) =
# swap(a, b) --> # swap(a, b) -->

View file

@ -53,16 +53,20 @@ proc genStringLiteralV1(m: BModule; n: PNode): Rope =
proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope =
result = getTempName(m) result = getTempName(m)
addf(m.s[cfsData], " static const NIM_CHAR $1[$2] = $3;$n", addf(m.s[cfsData], "static const struct {$n" &
" NI cap; void* allocator; NIM_CHAR[$2] data;$n" &
"} $1 = { $2, NIM_NIL, $3 };$n",
[result, rope(len(s)+1), makeCString(s)]) [result, rope(len(s)+1), makeCString(s)])
proc genStringLiteralV2(m: BModule; n: PNode): Rope = proc genStringLiteralV2(m: BModule; n: PNode): Rope =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels) let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
if id == m.labels: if id == m.labels:
discard cgsym(m, "NimStrPayload")
discard cgsym(m, "NimStringV2")
# string literal not found in the cache: # string literal not found in the cache:
let pureLit = genStringLiteralDataOnlyV2(m, n.strVal) let pureLit = genStringLiteralDataOnlyV2(m, n.strVal)
result = getTempName(m) result = getTempName(m)
addf(m.s[cfsData], "static const #NimStringV2 $1 = {$2, $2, $3};$n", addf(m.s[cfsData], "static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
[result, rope(len(n.strVal)+1), pureLit]) [result, rope(len(n.strVal)+1), pureLit])
else: else:
result = m.tmpBase & rope(id) result = m.tmpBase & rope(id)

View file

@ -324,6 +324,10 @@ proc getForwardStructFormat(m: BModule): string =
if m.compileToCpp: result = "$1 $2;$n" if m.compileToCpp: result = "$1 $2;$n"
else: result = "typedef $1 $2 $2;$n" else: result = "typedef $1 $2 $2;$n"
proc seqStar(m: BModule): string =
if m.config.selectedGC == gcDestructors: result = ""
else: result = "*"
proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
result = cacheGetType(m.forwTypeCache, sig) result = cacheGetType(m.forwTypeCache, sig)
if result != nil: return if result != nil: return
@ -355,7 +359,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
result = getTypeForward(m, t, hashType(t)) result = getTypeForward(m, t, hashType(t))
pushType(m, t) pushType(m, t)
of tySequence: of tySequence:
result = getTypeForward(m, t, hashType(t)) & "*" result = getTypeForward(m, t, hashType(t)) & seqStar(m)
pushType(m, t) pushType(m, t)
else: else:
result = getTypeDescAux(m, t, check) result = getTypeDescAux(m, t, check)
@ -487,7 +491,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags: if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
addf(result, "$1 $2[SEQ_DECL_SIZE];$n", addf(result, "$1 $2[SEQ_DECL_SIZE];$n",
[getTypeDescAux(m, fieldType.elemType, check), sname]) [getTypeDescAux(m, fieldType.elemType, check), sname])
elif fieldType.kind in {tySequence, tyOpt}: elif fieldType.kind == tySequence:
# we need to use a weak dependency here for trecursive_table. # we need to use a weak dependency here for trecursive_table.
addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
elif field.bitsize != 0: elif field.bitsize != 0:
@ -601,6 +605,14 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
result = if result.kind == tyGenericInst: result.sons[1] result = if result.kind == tyGenericInst: result.sons[1]
else: result.elemType else: result.elemType
proc getSeqPayloadType(m: BModule; t: PType): Rope =
var check = initIntSet()
result = getTypeForward(m, t, hashType(t))
# XXX remove this duplication:
appcg(m, m.s[cfsSeqTypes],
"struct $2_Content { NI cap; void* allocator; $1 data[SEQ_DECL_SIZE];$n } ",
[getTypeDescAux(m, t.sons[0], check), result])
proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
# returns only the type's name # returns only the type's name
var t = origTyp.skipTypes(irrelevantForBackend) var t = origTyp.skipTypes(irrelevantForBackend)
@ -641,7 +653,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
of tySequence: of tySequence:
# no restriction! We have a forward declaration for structs # no restriction! We have a forward declaration for structs
let name = getTypeForward(m, et, hashType et) let name = getTypeForward(m, et, hashType et)
result = name & "*" & star result = name & seqStar(m) & star
m.typeCache[sig] = result m.typeCache[sig] = result
pushType(m, et) pushType(m, et)
else: else:
@ -705,20 +717,29 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
[structOrUnion(t), result]) [structOrUnion(t), result])
m.forwTypeCache[sig] = result m.forwTypeCache[sig] = result
assert(cacheGetType(m.typeCache, sig) == nil) assert(cacheGetType(m.typeCache, sig) == nil)
m.typeCache[sig] = result & "*" m.typeCache[sig] = result & seqStar(m)
if not isImportedType(t): if not isImportedType(t):
if skipTypes(t.sons[0], typedescInst).kind != tyEmpty: if skipTypes(t.sons[0], typedescInst).kind != tyEmpty:
const const
cppSeq = "struct $2 : #TGenericSeq {$n" cppSeq = "struct $2 : #TGenericSeq {$n"
cSeq = "struct $2 {$n" & cSeq = "struct $2 {$n" &
" #TGenericSeq Sup;$n" " #TGenericSeq Sup;$n"
appcg(m, m.s[cfsSeqTypes], if m.config.selectedGC == gcDestructors:
(if m.compileToCpp: cppSeq else: cSeq) & appcg(m, m.s[cfsSeqTypes],
" $1 data[SEQ_DECL_SIZE];$n" & "struct $2_Content { NI cap; void* allocator; $1 data[SEQ_DECL_SIZE];$n } " &
"};$n", [getTypeDescAux(m, t.sons[0], check), result]) "struct $2 {$n" &
" NI len; $2_Content* p;$n" &
"};$n", [getTypeDescAux(m, t.sons[0], check), result])
else:
appcg(m, m.s[cfsSeqTypes],
(if m.compileToCpp: cppSeq else: cSeq) &
" $1 data[SEQ_DECL_SIZE];$n" &
"};$n", [getTypeDescAux(m, t.sons[0], check), result])
elif m.config.selectedGC == gcDestructors:
internalError(m.config, "cannot map the empty seq type to a C type")
else: else:
result = rope("TGenericSeq") result = rope("TGenericSeq")
add(result, "*") add(result, seqStar(m))
of tyArray: of tyArray:
var n: BiggestInt = lengthOrd(m.config, t) var n: BiggestInt = lengthOrd(m.config, t)
if n <= 0: n = 1 # make an array of at least one element if n <= 0: n = 1 # make an array of at least one element

View file

@ -250,7 +250,10 @@ proc lenExpr(p: BProc; a: TLoc): Rope =
result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)] result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
proc dataField(p: BProc): Rope = proc dataField(p: BProc): Rope =
result = rope"->data" if p.config.selectedGc == gcDestructors:
result = rope".p->data"
else:
result = rope"->data"
include ccgliterals include ccgliterals
include ccgtypes include ccgtypes

View file

@ -100,12 +100,12 @@ Rule Pattern Transformed into
finally: `=destroy`(x) finally: `=destroy`(x)
1.2 var x: sink T; stmts var x: sink T; stmts; ensureEmpty(x) 1.2 var x: sink T; stmts var x: sink T; stmts; ensureEmpty(x)
2 x = f() `=sink`(x, f()) 2 x = f() `=sink`(x, f())
3 x = lastReadOf z `=sink`(x, z) 3 x = lastReadOf z `=sink`(x, z); wasMoved(z)
4.1 y = sinkParam `=sink`(y, sinkParam) 4.1 y = sinkParam `=sink`(y, sinkParam)
4.2 x = y `=`(x, y) # a copy 4.2 x = y `=`(x, y) # a copy
5.1 f_sink(g()) f_sink(g()) 5.1 f_sink(g()) f_sink(g())
5.2 f_sink(y) f_sink(copy y); # copy unless we can see it's the last read 5.2 f_sink(y) f_sink(copy y); # copy unless we can see it's the last read
5.3 f_sink(move y) f_sink(y); reset(y) # explicit moves empties 'y' 5.3 f_sink(move y) f_sink(y); wasMoved(y) # explicit moves empties 'y'
5.4 f_noSink(g()) var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp) 5.4 f_noSink(g()) var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp)
Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently
@ -282,6 +282,11 @@ proc destructiveMoveSink(n: PNode; c: var Con): PNode =
newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool))) newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool)))
result.add n result.add n
proc genMagicCall(n: PNode; c: var Con; magicname: string; m: TMagic): PNode =
result = newNodeI(nkCall, n.info)
result.add(newSymNode(createMagic(c.graph, magicname, m)))
result.add n
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
if ri.kind in constrExprs: if ri.kind in constrExprs:
result = genSink(c, ri.typ, dest) result = genSink(c, ri.typ, dest)
@ -290,8 +295,10 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
recurse(ri, ri2) recurse(ri, ri2)
result.add ri2 result.add ri2
elif ri.kind == nkSym and isHarmlessVar(ri.sym, c): elif ri.kind == nkSym and isHarmlessVar(ri.sym, c):
result = genSink(c, ri.typ, dest) # Rule 3: `=sink`(x, z); wasMoved(z)
result.add p(ri, c) var snk = genSink(c, ri.typ, dest)
snk.add p(ri, c)
result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
elif ri.kind == nkSym and isSinkParam(ri.sym): elif ri.kind == nkSym and isSinkParam(ri.sym):
result = genSink(c, ri.typ, dest) result = genSink(c, ri.typ, dest)
result.add destructiveMoveSink(ri, c) result.add destructiveMoveSink(ri, c)
@ -313,11 +320,9 @@ proc passCopyToSink(n: PNode; c: var Con): PNode =
result.add newTree(nkAsgn, tmp, p(n, c)) result.add newTree(nkAsgn, tmp, p(n, c))
result.add tmp result.add tmp
proc genReset(n: PNode; c: var Con): PNode = proc genWasMoved(n: PNode; c: var Con): PNode =
result = newNodeI(nkCall, n.info) # The mWasMoved builtin does not take the address.
result.add(newSymNode(createMagic(c.graph, "reset", mReset))) result = genMagicCall(n, c, "wasMoved", mWasMoved)
# The mReset builtin does not take the address:
result.add n
proc destructiveMoveVar(n: PNode; c: var Con): PNode = proc destructiveMoveVar(n: PNode; c: var Con): PNode =
# generate: (let tmp = v; reset(v); tmp) # generate: (let tmp = v; reset(v); tmp)
@ -334,7 +339,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
add(v, vpart) add(v, vpart)
result.add v result.add v
result.add genReset(n, c) result.add genWasMoved(n, c)
result.add tempAsNode result.add tempAsNode
proc p(n: PNode; c: var Con): PNode = proc p(n: PNode; c: var Con): PNode =

View file

@ -1553,7 +1553,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags: if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags:
localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX %
("'" & proto.name.s & "' from " & c.config$proto.info)) ("'" & proto.name.s & "' from " & c.config$proto.info))
if sfForward notin proto.flags: if sfForward notin proto.flags and proto.magic == mNone:
wrongRedefinition(c, n.info, proto.name.s) wrongRedefinition(c, n.info, proto.name.s)
excl(proto.flags, sfForward) excl(proto.flags, sfForward)
closeScope(c) # close scope with wrong parameter symbols closeScope(c) # close scope with wrong parameter symbols
@ -1619,7 +1619,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
openScope(c) openScope(c)
n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos]) n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
closeScope(c) closeScope(c)
fixupInstantiatedSymbols(c, s) if s.magic == mNone:
fixupInstantiatedSymbols(c, s)
if s.kind == skMethod: semMethodPrototype(c, s, n) if s.kind == skMethod: semMethodPrototype(c, s, n)
if sfImportc in s.flags: if sfImportc in s.flags:
# so we just ignore the body after semantic checking for importc: # so we just ignore the body after semantic checking for importc:

View file

@ -1335,14 +1335,23 @@ proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt
if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize
else: result = conf.target.ptrSize else: result = conf.target.ptrSize
a = conf.target.ptrSize a = conf.target.ptrSize
of tyString, tyNil: of tyString:
if tfHasAsgn in typ.flags:
result = conf.target.ptrSize * 2
else:
result = conf.target.ptrSize
of tyNil:
result = conf.target.ptrSize result = conf.target.ptrSize
a = result a = result
of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray: of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
let base = typ.lastSon let base = typ.lastSon
if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion): if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion):
result = szIllegalRecursion result = szIllegalRecursion
else: result = conf.target.ptrSize else:
if typ.kind == tySequence and tfHasAsgn in typ.flags:
result = conf.target.ptrSize * 2
else:
result = conf.target.ptrSize
a = result a = result
of tyArray: of tyArray:
let elemSize = computeSizeAux(conf, typ.sons[1], a) let elemSize = computeSizeAux(conf, typ.sons[1], a)

View file

@ -7,133 +7,157 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import allocators, typetraits
import typetraits
# strs already imported allocators for us.
## Default seq implementation used by Nim's core. ## Default seq implementation used by Nim's core.
type type
seq*[T] = object NimSeqPayload {.core.}[T] = object
len, cap: int cap: int
data: ptr UncheckedArray[T] region: Allocator
data: UncheckedArray[T]
NimSeqV2*[T] = object
len: int
p: ptr NimSeqPayload[T]
const nimSeqVersion {.core.} = 2 const nimSeqVersion {.core.} = 2
template frees(s) = dealloc(s.data, s.cap * sizeof(T)) template payloadSize(cap): int = cap * sizeof(T) + sizeof(int) + sizeof(Allocator)
# XXX make code memory safe for overflows in '*' # XXX make code memory safe for overflows in '*'
when defined(nimHasTrace): when false:
proc `=trace`[T](s: seq[T]; a: Allocator) = # this is currently not part of Nim's type bound operators and so it's
for i in 0 ..< s.len: `=trace`(s.data[i], a) # built into the tracing proc generation just like before.
proc `=trace`[T](s: NimSeqV2[T]) =
for i in 0 ..< s.len: `=trace`(s.data[i])
proc `=destroy`[T](x: var seq[T]) = proc `=destroy`[T](x: var NimSeqV2[T]) =
if x.data != nil: var p = x.p
if p != nil:
when not supportsCopyMem(T): when not supportsCopyMem(T):
for i in 0..<x.len: `=destroy`(x[i]) for i in 0..<x.len: `=destroy`(p.data[i])
frees(x) p.region.dealloc(p.region, p, payloadSize(p.cap))
x.data = nil x.p = nil
x.len = 0 x.len = 0
x.cap = 0
proc `=`[T](a: var seq[T]; b: seq[T]) = proc `=`[T](a: var NimSeqV2[T]; b: NimSeqV2[T]) =
if a.data == b.data: return if a.p == b.p: return
if a.data != nil: `=destroy`(a)
frees(a)
a.data = nil
a.len = b.len a.len = b.len
a.cap = b.cap if b.p != nil:
if b.data != nil: a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
when supportsCopyMem(T): when supportsCopyMem(T):
copyMem(a.data, b.data, a.cap * sizeof(T)) if a.len > 0:
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
else: else:
for i in 0..<a.len: for i in 0..<a.len:
a.data[i] = b.data[i] a.p.data[i] = b.p.data[i]
proc `=sink`[T](a: var seq[T]; b: seq[T]) = proc `=sink`[T](a: var NimSeqV2[T]; b: NimSeqV2[T]) =
if a.data != nil and a.data != b.data: if a.p != nil and a.p != b.p:
frees(a) `=destroy`(a)
a.len = b.len a.len = b.len
a.cap = b.cap a.p = b.p
a.data = b.data
proc resize[T](s: var seq[T]) = when false:
let old = s.cap proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
if old == 0: s.cap = 8 proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
else: s.cap = (s.cap * 3) shr 1 compilerRtl.}
s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T))) proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
proc reserveSlot[T](x: var seq[T]): ptr T =
if x.len >= x.cap: resize(x)
result = addr(x.data[x.len])
inc x.len
template add*[T](x: var seq[T]; y: T) = type
reserveSlot(x)[] = y PayloadBase = object
cap: int
region: Allocator
proc shrink*[T](x: var seq[T]; newLen: int) = proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} =
assert newLen <= x.len # we have to use type erasure here as Nim does not support generic
assert newLen >= 0 # compilerProcs. Oh well, this will all be inlined anyway.
if cap <= 0:
let region = getLocalAllocator()
var p = cast[ptr PayloadBase](region.alloc(region, cap * elemSize + sizeof(int) + sizeof(Allocator)))
p.region = region
p.cap = cap
result = p
else:
result = nil
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.compilerRtl.} =
if len+addlen <= len:
result = p
elif p == nil:
result = newSeqPayload(len+addlen, elemSize)
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 region = if p.region == nil: getLocalAllocator() else: p.region
let cap = max(resize(p.cap), len+addlen)
var q = cast[ptr PayloadBase](region.realloc(region, p,
sizeof(int) + sizeof(Allocator) + elemSize * p.cap,
sizeof(int) + sizeof(Allocator) + elemSize * cap))
q.region = region
q.cap = cap
result = q
proc shrink*[T](x: var seq[T]; newLen: Natural) =
sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'"
when not supportsCopyMem(T): when not supportsCopyMem(T):
for i in countdown(x.len - 1, newLen - 1): for i in countdown(x.len - 1, newLen - 1):
`=destroy`(x.data[i]) `=destroy`(x[i])
x.len = newLen
proc grow*[T](x: var seq[T]; newLen: int; value: T) = cast[ptr NimSeqV2[T]](addr x).len = newLen
if newLen <= x.len: return
assert newLen >= 0 proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
if x.cap == 0: x.cap = newLen let oldLen = x.len
else: x.cap = max(newLen, (x.cap * 3) shr 1) if newLen <= oldLen: return
x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T))) var xu = cast[ptr NimSeqV2[T]](addr x)
for i in x.len..<newLen:
xu.p = prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))
xu.len = newLen
for i in oldLen .. newLen-1:
x.data[i] = value x.data[i] = value
x.len = newLen
template default[T](t: typedesc[T]): T = proc setLen[T](s: var seq[T], newlen: Natural) =
var v: T if newlen < s.len:
v shrink(s, newLen)
proc setLen*[T](x: var seq[T]; newLen: int) {.deprecated.} =
if newlen < x.len: shrink(x, newLen)
else: grow(x, newLen, default(T))
template `[]`*[T](x: seq[T]; i: Natural): T =
assert i < x.len
x.data[i]
template `[]=`*[T](x: seq[T]; i: Natural; y: T) =
assert i < x.len
x.data[i] = y
proc `@`*[T](elems: openArray[T]): seq[T] =
result.cap = elems.len
result.len = elems.len
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
when supportsCopyMem(T):
copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
else: else:
for i in 0..<result.len: var v: T # get the default value of 'v'
result.data[i] = elems[i] grow(s, newLen, v)
proc len*[T](x: seq[T]): int {.inline.} = x.len when false:
proc resize[T](s: var NimSeqV2[T]) =
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 * sizeof(T), s.cap * sizeof(T)))
proc `$`*[T](x: seq[T]): string = proc reserveSlot[T](x: var NimSeqV2[T]): ptr T =
result = "@[" if x.len >= x.cap: resize(x)
var firstElement = true result = addr(x.data[x.len])
for i in 0..<x.len: inc x.len
let
value = x.data[i] template add*[T](x: var NimSeqV2[T]; y: T) =
if firstElement: reserveSlot(x)[] = y
firstElement = false
template `[]`*[T](x: NimSeqV2[T]; i: Natural): T =
assert i < x.len
x.data[i]
template `[]=`*[T](x: NimSeqV2[T]; i: Natural; y: T) =
assert i < x.len
x.data[i] = y
proc `@`*[T](elems: openArray[T]): NimSeqV2[T] =
result.cap = elems.len
result.len = elems.len
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
when supportsCopyMem(T):
copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
else: else:
result.add(", ") for i in 0..<result.len:
result.data[i] = elems[i]
when compiles(value.isNil):
# this branch should not be necessary
if value.isNil:
result.add "nil"
else:
result.addQuoted(value)
else:
result.addQuoted(value)
result.add("]")

View file

@ -15,7 +15,6 @@ when false:
#proc rawNewStringNoInit(space: int): NimString {.compilerProc.} #proc rawNewStringNoInit(space: int): NimString {.compilerProc.}
# seems to be unused. # seems to be unused.
proc copyDeepString(src: NimString): NimString {.inline.} proc copyDeepString(src: NimString): NimString {.inline.}
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.}
# ----------------- sequences ---------------------------------------------- # ----------------- sequences ----------------------------------------------
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
@ -27,14 +26,14 @@ when false:
import allocators import allocators
type type
StrContent = object NimStrPayload {.core.} = object
cap: int cap: int
region: Allocator region: Allocator
data: UncheckedArray[char] data: UncheckedArray[char]
NimStringV2 {.core.} = object NimStringV2 {.core.} = object
len: int len: int
p: ptr StrContent ## can be nil if len == 0. p: ptr NimStrPayload ## can be nil if len == 0.
const nimStrVersion {.core.} = 2 const nimStrVersion {.core.} = 2
@ -73,7 +72,7 @@ proc `=`(a: var NimStringV2; b: NimStringV2) =
# we have to allocate the 'cap' here, consider # we have to allocate the 'cap' here, consider
# 'let y = newStringOfCap(); var x = y' # 'let y = newStringOfCap(); var x = y'
# on the other hand... These get turned into moves now. # on the other hand... These get turned into moves now.
a.p = cast[ptr StrContent](region.alloc(region, contentSize(b.len))) a.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(b.len)))
a.p.region = region a.p.region = region
a.p.cap = b.len a.p.cap = b.len
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1) copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
@ -88,7 +87,7 @@ proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} =
let oldP = s.p let oldP = s.p
# can't mutate a literal, so we need a fresh copy here: # can't mutate a literal, so we need a fresh copy here:
let region = getLocalAllocator() let region = getLocalAllocator()
s.p = cast[ptr StrContent](region.alloc(region, contentSize(s.len + addlen))) s.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(s.len + addlen)))
s.p.region = region s.p.region = region
s.p.cap = s.len + addlen s.p.cap = s.len + addlen
if s.len > 0: if s.len > 0:
@ -96,7 +95,7 @@ proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} =
copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len) copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len)
elif s.len + addlen > s.p.cap: elif s.len + addlen > s.p.cap:
let cap = max(s.len + addlen, resize(s.p.cap)) let cap = max(s.len + addlen, resize(s.p.cap))
s.p = cast[ptr StrContent](s.p.region.realloc(s.p.region, s.p, s.p = cast[ptr NimStrPayload](s.p.region.realloc(s.p.region, s.p,
oldSize = contentSize(s.p.cap), oldSize = contentSize(s.p.cap),
newSize = contentSize(cap))) newSize = contentSize(cap)))
s.p.cap = cap s.p.cap = cap
@ -112,7 +111,7 @@ proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerProc.} =
result = NimStringV2(len: 0, p: nil) result = NimStringV2(len: 0, p: nil)
else: else:
let region = getLocalAllocator() let region = getLocalAllocator()
var p = cast[ptr StrContent](region.alloc(region, contentSize(len))) var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len)))
p.region = region p.region = region
p.cap = len p.cap = len
if len > 0: if len > 0:
@ -144,7 +143,7 @@ proc rawNewString(space: int): NimStringV2 {.compilerProc.} =
result = NimStringV2(len: 0, p: nil) result = NimStringV2(len: 0, p: nil)
else: else:
let region = getLocalAllocator() let region = getLocalAllocator()
var p = cast[ptr StrContent](region.alloc(region, contentSize(space))) var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(space)))
p.region = region p.region = region
p.cap = space p.cap = space
result = NimStringV2(len: 0, p: p) result = NimStringV2(len: 0, p: p)
@ -154,7 +153,15 @@ proc mnewString(len: int): NimStringV2 {.compilerProc.} =
result = NimStringV2(len: 0, p: nil) result = NimStringV2(len: 0, p: nil)
else: else:
let region = getLocalAllocator() let region = getLocalAllocator()
var p = cast[ptr StrContent](region.alloc(region, contentSize(len))) var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len)))
p.region = region p.region = region
p.cap = len p.cap = len
result = NimStringV2(len: len, p: p) result = NimStringV2(len: len, p: p)
proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
if newLen > s.len:
prepareAdd(s, newLen - s.len)
else:
s.len = newLen
# this also only works because the destructor
# looks at s.p and not s.len

View file

@ -230,6 +230,17 @@ proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.}
## resets an object `obj` to its initial (binary zero) value. This needs to ## resets an object `obj` to its initial (binary zero) value. This needs to
## be called before any possible `object branch transition`:idx:. ## be called before any possible `object branch transition`:idx:.
when defined(nimNewRuntime):
proc wasMoved*[T](obj: var T) {.magic: "WasMoved", noSideEffect.} =
## resets an object `obj` to its initial (binary zero) value to signify
## it was "moved" and to signify its destructor should do nothing and
## ideally be optimized away.
discard
proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} =
result = x
wasMoved(x)
type type
range*{.magic: "Range".}[T] ## Generic type to construct range types. range*{.magic: "Range".}[T] ## Generic type to construct range types.
array*{.magic: "Array".}[I, T] ## Generic type to construct array*{.magic: "Array".}[I, T] ## Generic type to construct
@ -3310,6 +3321,7 @@ when not defined(JS): #and not defined(nimscript):
when hasAlloc: when hasAlloc:
when defined(gcDestructors): when defined(gcDestructors):
include "core/strs" include "core/strs"
include "core/seqs"
else: else:
include "system/sysstr" include "system/sysstr"
{.pop.} {.pop.}

View file

@ -504,7 +504,6 @@ proc chckNilDisp(p: pointer) {.compilerproc.} =
if p == nil: if p == nil:
sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil") sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
const ThisIsSystem = true # for hti.nim
include "system/hti" include "system/hti"
proc isFatPointer(ti: PNimType): bool = proc isFatPointer(ti: PNimType): bool =

View file

@ -43,9 +43,10 @@ proc `=destroy`*[T](x: var myseq[T]) =
proc `=`*[T](a: var myseq[T]; b: myseq[T]) = proc `=`*[T](a: var myseq[T]; b: myseq[T]) =
if a.data == b.data: return if a.data == b.data: return
if a.data != nil: if a.data != nil:
dealloc(a.data) `=destroy`(a)
inc deallocCount #dealloc(a.data)
a.data = nil #inc deallocCount
#a.data = nil
a.len = b.len a.len = b.len
a.cap = b.cap a.cap = b.cap
if b.data != nil: if b.data != nil: