WIP: strings/seqs based on destructors
This commit is contained in:
parent
74bf316619
commit
32afdc09c6
13 changed files with 240 additions and 141 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -1224,6 +1224,12 @@ 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)
|
||||||
|
if p.config.selectedGC == gcDestructors:
|
||||||
|
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)
|
genNewSeqAux(p, a, b.rdLoc)
|
||||||
gcUsage(p.config, e)
|
gcUsage(p.config, e)
|
||||||
|
|
||||||
|
|
@ -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,6 +1570,9 @@ 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) =
|
||||||
|
if p.config.selectedGc == gcDestructors:
|
||||||
|
binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n")
|
||||||
|
else:
|
||||||
binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n")
|
binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n")
|
||||||
gcUsage(p.config, e)
|
gcUsage(p.config, e)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
if m.config.selectedGC == gcDestructors:
|
||||||
|
appcg(m, m.s[cfsSeqTypes],
|
||||||
|
"struct $2_Content { NI cap; void* allocator; $1 data[SEQ_DECL_SIZE];$n } " &
|
||||||
|
"struct $2 {$n" &
|
||||||
|
" NI len; $2_Content* p;$n" &
|
||||||
|
"};$n", [getTypeDescAux(m, t.sons[0], check), result])
|
||||||
|
else:
|
||||||
appcg(m, m.s[cfsSeqTypes],
|
appcg(m, m.s[cfsSeqTypes],
|
||||||
(if m.compileToCpp: cppSeq else: cSeq) &
|
(if m.compileToCpp: cppSeq else: cSeq) &
|
||||||
" $1 data[SEQ_DECL_SIZE];$n" &
|
" $1 data[SEQ_DECL_SIZE];$n" &
|
||||||
"};$n", [getTypeDescAux(m, t.sons[0], check), result])
|
"};$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
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,9 @@ 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 =
|
||||||
|
if p.config.selectedGc == gcDestructors:
|
||||||
|
result = rope".p->data"
|
||||||
|
else:
|
||||||
result = rope"->data"
|
result = rope"->data"
|
||||||
|
|
||||||
include ccgliterals
|
include ccgliterals
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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,6 +1619,7 @@ 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)
|
||||||
|
if s.magic == mNone:
|
||||||
fixupInstantiatedSymbols(c, s)
|
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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -7,104 +7,152 @@
|
||||||
# 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:
|
||||||
|
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.}
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
PayloadBase = object
|
||||||
|
cap: int
|
||||||
|
region: Allocator
|
||||||
|
|
||||||
|
proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} =
|
||||||
|
# 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 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):
|
||||||
|
for i in countdown(x.len - 1, newLen - 1):
|
||||||
|
`=destroy`(x[i])
|
||||||
|
|
||||||
|
cast[ptr NimSeqV2[T]](addr x).len = newLen
|
||||||
|
|
||||||
|
proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
|
||||||
|
let oldLen = x.len
|
||||||
|
if newLen <= oldLen: return
|
||||||
|
var xu = cast[ptr NimSeqV2[T]](addr x)
|
||||||
|
|
||||||
|
xu.p = prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))
|
||||||
|
xu.len = newLen
|
||||||
|
for i in oldLen .. newLen-1:
|
||||||
|
x.data[i] = value
|
||||||
|
|
||||||
|
proc setLen[T](s: var seq[T], newlen: Natural) =
|
||||||
|
if newlen < s.len:
|
||||||
|
shrink(s, newLen)
|
||||||
|
else:
|
||||||
|
var v: T # get the default value of 'v'
|
||||||
|
grow(s, newLen, v)
|
||||||
|
|
||||||
|
when false:
|
||||||
|
proc resize[T](s: var NimSeqV2[T]) =
|
||||||
let old = s.cap
|
let old = s.cap
|
||||||
if old == 0: s.cap = 8
|
if old == 0: s.cap = 8
|
||||||
else: s.cap = (s.cap * 3) shr 1
|
else: s.cap = (s.cap * 3) shr 1
|
||||||
s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
|
s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
|
||||||
|
|
||||||
proc reserveSlot[T](x: var seq[T]): ptr T =
|
proc reserveSlot[T](x: var NimSeqV2[T]): ptr T =
|
||||||
if x.len >= x.cap: resize(x)
|
if x.len >= x.cap: resize(x)
|
||||||
result = addr(x.data[x.len])
|
result = addr(x.data[x.len])
|
||||||
inc x.len
|
inc x.len
|
||||||
|
|
||||||
template add*[T](x: var seq[T]; y: T) =
|
template add*[T](x: var NimSeqV2[T]; y: T) =
|
||||||
reserveSlot(x)[] = y
|
reserveSlot(x)[] = y
|
||||||
|
|
||||||
proc shrink*[T](x: var seq[T]; newLen: int) =
|
template `[]`*[T](x: NimSeqV2[T]; i: Natural): T =
|
||||||
assert newLen <= x.len
|
|
||||||
assert newLen >= 0
|
|
||||||
when not supportsCopyMem(T):
|
|
||||||
for i in countdown(x.len - 1, newLen - 1):
|
|
||||||
`=destroy`(x.data[i])
|
|
||||||
x.len = newLen
|
|
||||||
|
|
||||||
proc grow*[T](x: var seq[T]; newLen: int; value: T) =
|
|
||||||
if newLen <= x.len: return
|
|
||||||
assert newLen >= 0
|
|
||||||
if x.cap == 0: x.cap = newLen
|
|
||||||
else: x.cap = max(newLen, (x.cap * 3) shr 1)
|
|
||||||
x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T)))
|
|
||||||
for i in x.len..<newLen:
|
|
||||||
x.data[i] = value
|
|
||||||
x.len = newLen
|
|
||||||
|
|
||||||
template default[T](t: typedesc[T]): T =
|
|
||||||
var v: T
|
|
||||||
v
|
|
||||||
|
|
||||||
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
|
assert i < x.len
|
||||||
x.data[i]
|
x.data[i]
|
||||||
|
|
||||||
template `[]=`*[T](x: seq[T]; i: Natural; y: T) =
|
template `[]=`*[T](x: NimSeqV2[T]; i: Natural; y: T) =
|
||||||
assert i < x.len
|
assert i < x.len
|
||||||
x.data[i] = y
|
x.data[i] = y
|
||||||
|
|
||||||
proc `@`*[T](elems: openArray[T]): seq[T] =
|
proc `@`*[T](elems: openArray[T]): NimSeqV2[T] =
|
||||||
result.cap = elems.len
|
result.cap = elems.len
|
||||||
result.len = elems.len
|
result.len = elems.len
|
||||||
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
|
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
|
||||||
|
|
@ -113,27 +161,3 @@ proc `@`*[T](elems: openArray[T]): seq[T] =
|
||||||
else:
|
else:
|
||||||
for i in 0..<result.len:
|
for i in 0..<result.len:
|
||||||
result.data[i] = elems[i]
|
result.data[i] = elems[i]
|
||||||
|
|
||||||
proc len*[T](x: seq[T]): int {.inline.} = x.len
|
|
||||||
|
|
||||||
proc `$`*[T](x: seq[T]): string =
|
|
||||||
result = "@["
|
|
||||||
var firstElement = true
|
|
||||||
for i in 0..<x.len:
|
|
||||||
let
|
|
||||||
value = x.data[i]
|
|
||||||
if firstElement:
|
|
||||||
firstElement = false
|
|
||||||
else:
|
|
||||||
result.add(", ")
|
|
||||||
|
|
||||||
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("]")
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.}
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue