implement everything
This commit is contained in:
parent
938d3ffad7
commit
69347f6c95
5 changed files with 93 additions and 67 deletions
|
|
@ -627,7 +627,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, mDestroy,
|
mMove, mDestroy,
|
||||||
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,
|
||||||
|
|
|
||||||
|
|
@ -1901,13 +1901,6 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||||
proc skipAddr(n: PNode): PNode =
|
proc skipAddr(n: PNode): PNode =
|
||||||
result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n
|
result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n
|
||||||
|
|
||||||
proc genWasMoved(p: BProc; n: PNode) =
|
|
||||||
var a: TLoc
|
|
||||||
initLocExpr(p, n[1].skipAddr, a)
|
|
||||||
resetLoc(p, a)
|
|
||||||
#linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
|
|
||||||
# addrLoc(p.config, a), getTypeDesc(p.module, a.t))
|
|
||||||
|
|
||||||
proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||||
if d.k == locNone: getTemp(p, n.typ, d)
|
if d.k == locNone: getTemp(p, n.typ, d)
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
|
|
@ -2046,7 +2039,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
initLocExpr(p, e.sons[2], b)
|
initLocExpr(p, e.sons[2], b)
|
||||||
genDeepCopy(p, a, b)
|
genDeepCopy(p, a, b)
|
||||||
of mDotDot, mEqCString: genCall(p, e, d)
|
of mDotDot, mEqCString: genCall(p, e, d)
|
||||||
of mWasMoved: genWasMoved(p, e)
|
|
||||||
of mMove: genMove(p, e, d)
|
of mMove: genMove(p, e, d)
|
||||||
of mDestroy: discard "ignore calls to the default destructor"
|
of mDestroy: discard "ignore calls to the default destructor"
|
||||||
of mSlice:
|
of mSlice:
|
||||||
|
|
|
||||||
|
|
@ -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); wasMoved(z)
|
3 x = lastReadOf z `=sink`(x, 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); wasMoved(y) # explicit moves empties 'y'
|
5.3 f_sink(move y) f_sink(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
|
||||||
|
|
@ -116,7 +116,7 @@ Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||||
strutils, options, dfa, lowerings, tables, modulegraphs,
|
strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
|
||||||
lineinfos, parampatterns
|
lineinfos, parampatterns
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -127,20 +127,13 @@ type
|
||||||
owner: PSym
|
owner: PSym
|
||||||
g: ControlFlowGraph
|
g: ControlFlowGraph
|
||||||
jumpTargets: IntSet
|
jumpTargets: IntSet
|
||||||
tmpObj: PType
|
topLevelVars: PNode
|
||||||
tmp: PSym
|
destroys: OrderedTable[int, tuple[enabled: bool, destroy_call: PNode]] # Symbol to destructor table
|
||||||
destroys, topLevelVars: PNode
|
|
||||||
toDropBit: Table[int, PSym]
|
toDropBit: Table[int, PSym]
|
||||||
graph: ModuleGraph
|
graph: ModuleGraph
|
||||||
emptyNode: PNode
|
emptyNode: PNode
|
||||||
otherRead: PNode
|
otherRead: PNode
|
||||||
|
|
||||||
proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
|
|
||||||
# XXX why are temps fields in an object here?
|
|
||||||
let f = newSym(skField, getIdent(c.graph.cache, ":d" & $c.tmpObj.n.len), c.owner, info)
|
|
||||||
f.typ = typ
|
|
||||||
rawAddField c.tmpObj, f
|
|
||||||
result = rawDirectAccess(c.tmp, f)
|
|
||||||
|
|
||||||
proc isHarmlessVar*(s: PSym; c: Con): bool =
|
proc isHarmlessVar*(s: PSym; c: Con): bool =
|
||||||
# 's' is harmless if it used only once and its
|
# 's' is harmless if it used only once and its
|
||||||
|
|
@ -333,6 +326,22 @@ proc dropBit(c: var Con; s: PSym): PSym =
|
||||||
result = c.toDropBit.getOrDefault(s.id)
|
result = c.toDropBit.getOrDefault(s.id)
|
||||||
assert result != nil
|
assert result != nil
|
||||||
|
|
||||||
|
proc addDestructor(c: var Con; s: PSym; destructor_call: PNode) =
|
||||||
|
let alreadyIn = c.destroys.hasKeyOrPut(s.id, (true, destructor_call))
|
||||||
|
if alreadyIn:
|
||||||
|
let lineInfo = if s.ast != nil: s.ast.info else: c.owner.info
|
||||||
|
internalError(c.graph.config, lineInfo, "Destructor call for sym " & s.name.s & " is already injected")
|
||||||
|
|
||||||
|
proc disableDestructor(c: var Con; s: PSym) =
|
||||||
|
## disable destructor, but do not delete such that it can be enabled back again later
|
||||||
|
c.destroys.with_value(s.id, value):
|
||||||
|
value.enabled = false
|
||||||
|
|
||||||
|
proc enableDestructor(c: var Con; s: PSym) =
|
||||||
|
## if destructor does not exist then ignore, otherwise make sure destructor is enabled
|
||||||
|
c.destroys.with_value(s.id, value):
|
||||||
|
value.enabled = true
|
||||||
|
|
||||||
proc registerDropBit(c: var Con; s: PSym) =
|
proc registerDropBit(c: var Con; s: PSym) =
|
||||||
let result = newSym(skTemp, getIdent(c.graph.cache, s.name.s & "_AliveBit"), c.owner, s.info)
|
let result = newSym(skTemp, getIdent(c.graph.cache, s.name.s & "_AliveBit"), c.owner, s.info)
|
||||||
result.typ = getSysType(c.graph, s.info, tyBool)
|
result.typ = getSysType(c.graph, s.info, tyBool)
|
||||||
|
|
@ -343,8 +352,14 @@ proc registerDropBit(c: var Con; s: PSym) =
|
||||||
# if not sinkParam_AliveBit: `=destroy`(sinkParam)
|
# if not sinkParam_AliveBit: `=destroy`(sinkParam)
|
||||||
let t = s.typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
let t = s.typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||||
if t.destructor != nil:
|
if t.destructor != nil:
|
||||||
c.destroys.add newTree(nkIfStmt,
|
c.addDestructor(s, newTree(nkIfStmt,
|
||||||
newTree(nkElifBranch, newSymNode result, genDestroy(c, t, newSymNode s)))
|
newTree(nkElifBranch, newSymNode result, genDestroy(c, t, newSymNode s))))
|
||||||
|
|
||||||
|
proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
|
||||||
|
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info)
|
||||||
|
sym.typ = typ
|
||||||
|
result = newSymNode(sym)
|
||||||
|
c.addTopVar(result)
|
||||||
|
|
||||||
proc p(n: PNode; c: var Con): PNode
|
proc p(n: PNode; c: var Con): PNode
|
||||||
|
|
||||||
|
|
@ -370,31 +385,6 @@ proc genMagicCall(n: PNode; c: var Con; magicname: string; m: TMagic): PNode =
|
||||||
result.add(newSymNode(createMagic(c.graph, magicname, m)))
|
result.add(newSymNode(createMagic(c.graph, magicname, m)))
|
||||||
result.add n
|
result.add n
|
||||||
|
|
||||||
proc genWasMoved(n: PNode; c: var Con): PNode =
|
|
||||||
# The mWasMoved builtin does not take the address.
|
|
||||||
result = genMagicCall(n, c, "wasMoved", mWasMoved)
|
|
||||||
|
|
||||||
proc destructiveMoveVar(n: PNode; c: var Con): PNode =
|
|
||||||
# generate: (let tmp = v; reset(v); tmp)
|
|
||||||
# XXX: Strictly speaking we can only move if there is a ``=sink`` defined
|
|
||||||
# or if no ``=sink`` is defined and also no assignment.
|
|
||||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
|
||||||
|
|
||||||
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info)
|
|
||||||
temp.typ = n.typ
|
|
||||||
var v = newNodeI(nkLetSection, n.info)
|
|
||||||
let tempAsNode = newSymNode(temp)
|
|
||||||
|
|
||||||
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
|
||||||
vpart.sons[0] = tempAsNode
|
|
||||||
vpart.sons[1] = c.emptyNode
|
|
||||||
vpart.sons[2] = n
|
|
||||||
add(v, vpart)
|
|
||||||
|
|
||||||
result.add v
|
|
||||||
result.add genWasMoved(n, c)
|
|
||||||
result.add tempAsNode
|
|
||||||
|
|
||||||
proc passCopyToSink(n: PNode; c: var Con): PNode =
|
proc passCopyToSink(n: PNode; c: var Con): PNode =
|
||||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||||
let tmp = getTemp(c, n.typ, n.info)
|
let tmp = getTemp(c, n.typ, n.info)
|
||||||
|
|
@ -431,9 +421,9 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
|
||||||
result = arg
|
result = arg
|
||||||
elif arg.kind == nkSym and arg.sym.kind in InterestingSyms and isLastRead(arg, c):
|
elif arg.kind == nkSym and arg.sym.kind in InterestingSyms and isLastRead(arg, c):
|
||||||
# if x is a variable and it its last read we eliminate its
|
# if x is a variable and it its last read we eliminate its
|
||||||
# destructor invokation, but don't. We need to reset its memory
|
# destructor invocation
|
||||||
# to disable its destructor which we have not elided:
|
c.disableDestructor(arg.sym)
|
||||||
result = destructiveMoveVar(arg, c)
|
result = arg
|
||||||
elif arg.kind == nkSym and isSinkParam(arg.sym):
|
elif arg.kind == nkSym and isSinkParam(arg.sym):
|
||||||
# mark the sink parameter as used:
|
# mark the sink parameter as used:
|
||||||
result = destructiveMoveSink(arg, c)
|
result = destructiveMoveSink(arg, c)
|
||||||
|
|
@ -566,9 +556,8 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||||
of nkSym:
|
of nkSym:
|
||||||
if ri.sym.kind != skParam and isLastRead(ri, c):
|
if ri.sym.kind != skParam and isLastRead(ri, c):
|
||||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||||
var snk = genSink(c, dest.typ, dest, ri)
|
result = genSink(c, dest.typ, dest, ri)
|
||||||
snk.add p(ri, c)
|
result.add p(ri, c)
|
||||||
result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
|
|
||||||
elif isSinkParam(ri.sym):
|
elif isSinkParam(ri.sym):
|
||||||
result = genSink(c, dest.typ, dest, ri)
|
result = genSink(c, dest.typ, dest, ri)
|
||||||
result.add destructiveMoveSink(ri, c)
|
result.add destructiveMoveSink(ri, c)
|
||||||
|
|
@ -599,7 +588,7 @@ proc p(n: PNode; c: var Con): PNode =
|
||||||
# move the variable declaration to the top of the frame:
|
# move the variable declaration to the top of the frame:
|
||||||
c.addTopVar v
|
c.addTopVar v
|
||||||
# make sure it's destroyed at the end of the proc:
|
# make sure it's destroyed at the end of the proc:
|
||||||
c.destroys.add genDestroy(c, v.typ, v)
|
c.addDestructor(v.sym, genDestroy(c, v.typ, v))
|
||||||
if ri.kind != nkEmpty:
|
if ri.kind != nkEmpty:
|
||||||
let r = moveOrCopy(v, ri, c)
|
let r = moveOrCopy(v, ri, c)
|
||||||
result.add r
|
result.add r
|
||||||
|
|
@ -625,12 +614,13 @@ proc p(n: PNode; c: var Con): PNode =
|
||||||
sinkExpr.add n
|
sinkExpr.add n
|
||||||
result.add sinkExpr
|
result.add sinkExpr
|
||||||
result.add tmp
|
result.add tmp
|
||||||
c.destroys.add genDestroy(c, n.typ, tmp)
|
c.addDestructor(tmp.sym, genDestroy(c, n.typ, tmp))
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
if hasDestructor(n[0].typ):
|
if hasDestructor(n[0].typ):
|
||||||
result = moveOrCopy(n[0], n[1], c)
|
result = moveOrCopy(n[0], n[1], c)
|
||||||
|
c.enableDestructor(n[0].sym)
|
||||||
else:
|
else:
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
recurse(n, result)
|
recurse(n, result)
|
||||||
|
|
@ -646,12 +636,9 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
||||||
# echo "injecting into ", n
|
# echo "injecting into ", n
|
||||||
var c: Con
|
var c: Con
|
||||||
c.owner = owner
|
c.owner = owner
|
||||||
c.tmp = newSym(skTemp, getIdent(g.cache, ":d"), owner, n.info)
|
|
||||||
c.tmpObj = createObj(g, owner, n.info)
|
|
||||||
c.tmp.typ = c.tmpObj
|
|
||||||
c.destroys = newNodeI(nkStmtList, n.info)
|
|
||||||
c.topLevelVars = newNodeI(nkVarSection, n.info)
|
c.topLevelVars = newNodeI(nkVarSection, n.info)
|
||||||
c.toDropBit = initTable[int, PSym]()
|
c.toDropBit = initTable[int, PSym](16)
|
||||||
|
c.destroys = initOrderedTable[int, (bool, PNode)](16)
|
||||||
c.graph = g
|
c.graph = g
|
||||||
c.emptyNode = newNodeI(nkEmpty, n.info)
|
c.emptyNode = newNodeI(nkEmpty, n.info)
|
||||||
let cfg = constructCfg(owner, n)
|
let cfg = constructCfg(owner, n)
|
||||||
|
|
@ -668,13 +655,15 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
||||||
let param = params[i].sym
|
let param = params[i].sym
|
||||||
if param.typ.kind == tySink: registerDropBit(c, param)
|
if param.typ.kind == tySink: registerDropBit(c, param)
|
||||||
let body = p(n, c)
|
let body = p(n, c)
|
||||||
if c.tmp.typ.n.len > 0:
|
|
||||||
c.addTopVar(newSymNode c.tmp)
|
|
||||||
result = newNodeI(nkStmtList, n.info)
|
result = newNodeI(nkStmtList, n.info)
|
||||||
if c.topLevelVars.len > 0:
|
if c.topLevelVars.len > 0:
|
||||||
result.add c.topLevelVars
|
result.add c.topLevelVars
|
||||||
if c.destroys.len > 0:
|
if c.destroys.len > 0:
|
||||||
result.add newTryFinally(body, c.destroys)
|
var destroy_list = newNodeI(nkStmtList, n.info)
|
||||||
|
for val in c.destroys.values:
|
||||||
|
if val.enabled:
|
||||||
|
destroy_list.add val.destroy_call
|
||||||
|
result.add newTryFinally(body, destroy_list)
|
||||||
else:
|
else:
|
||||||
result.add body
|
result.add body
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -614,8 +614,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
|
||||||
const
|
const
|
||||||
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
|
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
|
||||||
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
|
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
|
||||||
mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy, mMove,
|
mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy, mMove}
|
||||||
mWasMoved}
|
|
||||||
|
|
||||||
# get the real type of the callee
|
# get the real type of the callee
|
||||||
# it may be a proc var with a generic alias type, so we skip over them
|
# it may be a proc var with a generic alias type, so we skip over them
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,7 @@ template withValue*[A, B](t: var Table[A, B], key: A,
|
||||||
else:
|
else:
|
||||||
body2
|
body2
|
||||||
|
|
||||||
|
|
||||||
iterator allValues*[A, B](t: Table[A, B]; key: A): B =
|
iterator allValues*[A, B](t: Table[A, B]; key: A): B =
|
||||||
## iterates over any value in the table ``t`` that belongs to the given ``key``.
|
## iterates over any value in the table ``t`` that belongs to the given ``key``.
|
||||||
var h: Hash = genHash(key) and high(t.data)
|
var h: Hash = genHash(key) and high(t.data)
|
||||||
|
|
@ -879,6 +880,51 @@ proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
|
||||||
## if the key does not exist.
|
## if the key does not exist.
|
||||||
t[].del(key)
|
t[].del(key)
|
||||||
|
|
||||||
|
|
||||||
|
template withValue*[A, B](t: var OrderedTable[A, B], key: A, value, body: untyped) =
|
||||||
|
## retrieves the value at ``t[key]``.
|
||||||
|
## ``value`` can be modified in the scope of the ``withValue`` call.
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
##
|
||||||
|
## orderedTable.withValue(key, value) do:
|
||||||
|
## # block is executed only if ``key`` in ``t``
|
||||||
|
## value.name = "username"
|
||||||
|
## value.uid = 1000
|
||||||
|
##
|
||||||
|
mixin rawGet
|
||||||
|
var hc: Hash
|
||||||
|
var index = rawGet(t, key, hc)
|
||||||
|
let hasKey = index >= 0
|
||||||
|
if hasKey:
|
||||||
|
var value {.inject.} = addr(t.data[index].val)
|
||||||
|
body
|
||||||
|
|
||||||
|
template withValue*[A, B](t: var OrderedTable[A, B], key: A,
|
||||||
|
value, body1, body2: untyped) =
|
||||||
|
## retrieves the value at ``t[key]``.
|
||||||
|
## ``value`` can be modified in the scope of the ``withValue`` call.
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
##
|
||||||
|
## orderedTable.withValue(key, value) do:
|
||||||
|
## # block is executed only if ``key`` in ``t``
|
||||||
|
## value.name = "username"
|
||||||
|
## value.uid = 1000
|
||||||
|
## do:
|
||||||
|
## # block is executed when ``key`` not in ``t``
|
||||||
|
## raise newException(KeyError, "Key not found")
|
||||||
|
##
|
||||||
|
mixin rawGet
|
||||||
|
var hc: Hash
|
||||||
|
var index = rawGet(t, key, hc)
|
||||||
|
let hasKey = index >= 0
|
||||||
|
if hasKey:
|
||||||
|
var value {.inject.} = addr(t.data[index].val)
|
||||||
|
body1
|
||||||
|
else:
|
||||||
|
body2
|
||||||
|
|
||||||
# ------------------------------ count tables -------------------------------
|
# ------------------------------ count tables -------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue