introduce tfHasOwned for fast must-move checkings; removed tfAcyclic as the GC has ignored this hint for quite some time now
This commit is contained in:
parent
20a21aa184
commit
4be36d77f6
9 changed files with 30 additions and 63 deletions
|
|
@ -479,7 +479,7 @@ type
|
||||||
tfNoSideEffect, # procedure type does not allow side effects
|
tfNoSideEffect, # procedure type does not allow side effects
|
||||||
tfFinal, # is the object final?
|
tfFinal, # is the object final?
|
||||||
tfInheritable, # is the object inheritable?
|
tfInheritable, # is the object inheritable?
|
||||||
tfAcyclic, # type is acyclic (for GC optimization)
|
tfHasOwned, # type contains an 'owned' type and must be moved
|
||||||
tfEnumHasHoles, # enum cannot be mapped into a range
|
tfEnumHasHoles, # enum cannot be mapped into a range
|
||||||
tfShallow, # type can be shallow copied on assignment
|
tfShallow, # type can be shallow copied on assignment
|
||||||
tfThread, # proc type is marked as ``thread``; alias for ``gcsafe``
|
tfThread, # proc type is marked as ``thread``; alias for ``gcsafe``
|
||||||
|
|
@ -1472,6 +1472,13 @@ proc propagateToOwner*(owner, elem: PType) =
|
||||||
o2.flags.incl tfHasAsgn
|
o2.flags.incl tfHasAsgn
|
||||||
owner.flags.incl tfHasAsgn
|
owner.flags.incl tfHasAsgn
|
||||||
|
|
||||||
|
if tfHasOwned in elem.flags:
|
||||||
|
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||||
|
if o2.kind in {tyTuple, tyObject, tyArray,
|
||||||
|
tySequence, tyOpt, tySet, tyDistinct}:
|
||||||
|
o2.flags.incl tfHasOwned
|
||||||
|
owner.flags.incl tfHasOwned
|
||||||
|
|
||||||
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
|
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
|
||||||
tyGenericInvocation, tyPtr}:
|
tyGenericInvocation, tyPtr}:
|
||||||
let elemB = elem.skipTypes({tyGenericInst, tyAlias, tySink})
|
let elemB = elem.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,6 @@ when not declared(dynlib.libCandidates):
|
||||||
when options.hasTinyCBackend:
|
when options.hasTinyCBackend:
|
||||||
import tccgen
|
import tccgen
|
||||||
|
|
||||||
# implementation
|
|
||||||
|
|
||||||
proc hcrOn(m: BModule): bool = m.config.hcrOn
|
proc hcrOn(m: BModule): bool = m.config.hcrOn
|
||||||
proc hcrOn(p: BProc): bool = p.module.config.hcrOn
|
proc hcrOn(p: BProc): bool = p.module.config.hcrOn
|
||||||
|
|
||||||
|
|
@ -295,6 +293,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
|
||||||
includeHeader(p.module, "<new>")
|
includeHeader(p.module, "<new>")
|
||||||
linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t))
|
linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t))
|
||||||
|
|
||||||
|
if optNimV2 in p.config.globalOptions: return
|
||||||
case analyseObjectWithTypeField(t)
|
case analyseObjectWithTypeField(t)
|
||||||
of frNone:
|
of frNone:
|
||||||
discard
|
discard
|
||||||
|
|
@ -303,7 +302,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
|
||||||
if not takeAddr: r = "(*$1)" % [r]
|
if not takeAddr: r = "(*$1)" % [r]
|
||||||
var s = skipTypes(t, abstractInst)
|
var s = skipTypes(t, abstractInst)
|
||||||
if not p.module.compileToCpp:
|
if not p.module.compileToCpp:
|
||||||
while (s.kind == tyObject) and (s.sons[0] != nil):
|
while s.kind == tyObject and s.sons[0] != nil:
|
||||||
add(r, ".Sup")
|
add(r, ".Sup")
|
||||||
s = skipTypes(s.sons[0], skipPtrs)
|
s = skipTypes(s.sons[0], skipPtrs)
|
||||||
linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t, a.lode.info))
|
linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t, a.lode.info))
|
||||||
|
|
|
||||||
|
|
@ -596,7 +596,7 @@ proc genCall(c: var Con; n: PNode) =
|
||||||
gen(c, n[i])
|
gen(c, n[i])
|
||||||
when false:
|
when false:
|
||||||
if t != nil and i < t.len and t.sons[i].kind == tyVar:
|
if t != nil and i < t.len and t.sons[i].kind == tyVar:
|
||||||
# XXX This is wrong! Pass by var is a 'might def', not a 'must def'
|
# This is wrong! Pass by var is a 'might def', not a 'must def'
|
||||||
# like the other defs we emit. This is not good enough for a move
|
# like the other defs we emit. This is not good enough for a move
|
||||||
# optimizer.
|
# optimizer.
|
||||||
genDef(c, n[i])
|
genDef(c, n[i])
|
||||||
|
|
|
||||||
|
|
@ -933,7 +933,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
of wAcyclic:
|
of wAcyclic:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
if sym.typ == nil: invalidPragma(c, it)
|
if sym.typ == nil: invalidPragma(c, it)
|
||||||
else: incl(sym.typ.flags, tfAcyclic)
|
# now: ignored
|
||||||
of wShallow:
|
of wShallow:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
if sym.typ == nil: invalidPragma(c, it)
|
if sym.typ == nil: invalidPragma(c, it)
|
||||||
|
|
|
||||||
|
|
@ -1854,7 +1854,9 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||||
case m.name.s
|
case m.name.s
|
||||||
of "lent": setMagicType(c.config, m, tyLent, c.config.target.ptrSize)
|
of "lent": setMagicType(c.config, m, tyLent, c.config.target.ptrSize)
|
||||||
of "sink": setMagicType(c.config, m, tySink, szUncomputedSize)
|
of "sink": setMagicType(c.config, m, tySink, szUncomputedSize)
|
||||||
of "owned": setMagicType(c.config, m, tyOwned, c.config.target.ptrSize)
|
of "owned":
|
||||||
|
setMagicType(c.config, m, tyOwned, c.config.target.ptrSize)
|
||||||
|
incl m.typ.flags, tfHasOwned
|
||||||
else: localError(c.config, m.info, errTypeExpected)
|
else: localError(c.config, m.info, errTypeExpected)
|
||||||
else: localError(c.config, m.info, errTypeExpected)
|
else: localError(c.config, m.info, errTypeExpected)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,12 @@ const
|
||||||
tfInstClearedFlags = {tfHasMeta, tfUnresolved}
|
tfInstClearedFlags = {tfHasMeta, tfUnresolved}
|
||||||
|
|
||||||
proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) =
|
proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) =
|
||||||
if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
|
if t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
|
||||||
localError(conf, info, "invalid pragma: acyclic")
|
|
||||||
elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
|
|
||||||
localError(conf, info, "type 'var var' is not allowed")
|
localError(conf, info, "type 'var var' is not allowed")
|
||||||
|
|
||||||
proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
|
proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
|
||||||
var t = typ.skipTypes({tyDistinct})
|
var t = typ.skipTypes({tyDistinct})
|
||||||
if t.kind in tyTypeClasses: discard
|
if t.kind in tyTypeClasses: discard
|
||||||
elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
|
|
||||||
localError(conf, info, "invalid pragma: acyclic")
|
|
||||||
elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
|
elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
|
||||||
localError(conf, info, "type 'var var' is not allowed")
|
localError(conf, info, "type 'var var' is not allowed")
|
||||||
elif computeSize(conf, t) == szIllegalRecursion:
|
elif computeSize(conf, t) == szIllegalRecursion:
|
||||||
|
|
|
||||||
|
|
@ -893,7 +893,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
nkBlockStmt, nkBlockExpr}:
|
nkBlockStmt, nkBlockExpr}:
|
||||||
oldDeferAnchor = c.deferAnchor
|
oldDeferAnchor = c.deferAnchor
|
||||||
c.deferAnchor = n
|
c.deferAnchor = n
|
||||||
if n.typ != nil and tfHasAsgn in n.typ.flags:
|
if (n.typ != nil and tfHasAsgn in n.typ.flags) or
|
||||||
|
optNimV2 in c.graph.config.globalOptions:
|
||||||
c.needsDestroyPass = true
|
c.needsDestroyPass = true
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
|
|
|
||||||
|
|
@ -68,17 +68,6 @@ const
|
||||||
typedescPtrs* = abstractPtrs + {tyTypeDesc}
|
typedescPtrs* = abstractPtrs + {tyTypeDesc}
|
||||||
typedescInst* = abstractInst + {tyTypeDesc, tyOwned}
|
typedescInst* = abstractInst + {tyTypeDesc, tyOwned}
|
||||||
|
|
||||||
type
|
|
||||||
TTypeFieldResult* = enum
|
|
||||||
frNone, # type has no object type field
|
|
||||||
frHeader, # type has an object type field only in the header
|
|
||||||
frEmbedded # type has an object type field somewhere embedded
|
|
||||||
|
|
||||||
proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult
|
|
||||||
# this does a complex analysis whether a call to ``objectInit`` needs to be
|
|
||||||
# made or intializing of the type field suffices or if there is no type field
|
|
||||||
# at all in this type.
|
|
||||||
|
|
||||||
proc invalidGenericInst*(f: PType): bool =
|
proc invalidGenericInst*(f: PType): bool =
|
||||||
result = f.kind == tyGenericInst and lastSon(f) == nil
|
result = f.kind == tyGenericInst and lastSon(f) == nil
|
||||||
|
|
||||||
|
|
@ -242,11 +231,16 @@ proc containsObject*(t: PType): bool =
|
||||||
result = searchTypeFor(t, isObjectPredicate)
|
result = searchTypeFor(t, isObjectPredicate)
|
||||||
|
|
||||||
proc isObjectWithTypeFieldPredicate(t: PType): bool =
|
proc isObjectWithTypeFieldPredicate(t: PType): bool =
|
||||||
|
|
||||||
result = t.kind == tyObject and t.sons[0] == nil and
|
result = t.kind == tyObject and t.sons[0] == nil and
|
||||||
not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and
|
not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and
|
||||||
tfFinal notin t.flags
|
tfFinal notin t.flags
|
||||||
|
|
||||||
|
type
|
||||||
|
TTypeFieldResult* = enum
|
||||||
|
frNone, # type has no object type field
|
||||||
|
frHeader, # type has an object type field only in the header
|
||||||
|
frEmbedded # type has an object type field somewhere embedded
|
||||||
|
|
||||||
proc analyseObjectWithTypeFieldAux(t: PType,
|
proc analyseObjectWithTypeFieldAux(t: PType,
|
||||||
marker: var IntSet): TTypeFieldResult =
|
marker: var IntSet): TTypeFieldResult =
|
||||||
var res: TTypeFieldResult
|
var res: TTypeFieldResult
|
||||||
|
|
@ -276,7 +270,10 @@ proc analyseObjectWithTypeFieldAux(t: PType,
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc analyseObjectWithTypeField(t: PType): TTypeFieldResult =
|
proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult =
|
||||||
|
# this does a complex analysis whether a call to ``objectInit`` needs to be
|
||||||
|
# made or intializing of the type field suffices or if there is no type field
|
||||||
|
# at all in this type.
|
||||||
var marker = initIntSet()
|
var marker = initIntSet()
|
||||||
result = analyseObjectWithTypeFieldAux(t, marker)
|
result = analyseObjectWithTypeFieldAux(t, marker)
|
||||||
|
|
||||||
|
|
@ -323,9 +320,7 @@ proc canFormAcycleNode(marker: var IntSet, n: PNode, startId: int): bool =
|
||||||
proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
|
proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
|
||||||
result = false
|
result = false
|
||||||
if typ == nil: return
|
if typ == nil: return
|
||||||
if tfAcyclic in typ.flags: return
|
|
||||||
var t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
|
var t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
|
||||||
if tfAcyclic in t.flags: return
|
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyTuple, tyObject, tyRef, tySequence, tyArray, tyOpenArray, tyVarargs:
|
of tyTuple, tyObject, tyRef, tySequence, tyArray, tyOpenArray, tyVarargs:
|
||||||
if not containsOrIncl(marker, t.id):
|
if not containsOrIncl(marker, t.id):
|
||||||
|
|
|
||||||
|
|
@ -6925,41 +6925,8 @@ The ``noreturn`` pragma is used to mark a proc that never returns.
|
||||||
|
|
||||||
acyclic pragma
|
acyclic pragma
|
||||||
--------------
|
--------------
|
||||||
The ``acyclic`` pragma can be used for object types to mark them as acyclic
|
The ``acyclic`` pragma applies to type declarations. It is deprecated and
|
||||||
even though they seem to be cyclic. This is an **optimization** for the garbage
|
ignored.
|
||||||
collector to not consider objects of this type as part of a cycle:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
|
||||||
type
|
|
||||||
Node = ref NodeObj
|
|
||||||
NodeObj {.acyclic.} = object
|
|
||||||
left, right: Node
|
|
||||||
data: string
|
|
||||||
|
|
||||||
Or if we directly use a ref object:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
|
||||||
type
|
|
||||||
Node = ref object {.acyclic.}
|
|
||||||
left, right: Node
|
|
||||||
data: string
|
|
||||||
|
|
||||||
In the example a tree structure is declared with the ``Node`` type. Note that
|
|
||||||
the type definition is recursive and the GC has to assume that objects of
|
|
||||||
this type may form a cyclic graph. The ``acyclic`` pragma passes the
|
|
||||||
information that this cannot happen to the GC. If the programmer uses the
|
|
||||||
``acyclic`` pragma for data types that are in reality cyclic, the GC may leak
|
|
||||||
memory, but nothing worse happens.
|
|
||||||
|
|
||||||
**Future directions**: The ``acyclic`` pragma may become a property of a
|
|
||||||
``ref`` type:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
|
||||||
type
|
|
||||||
Node = acyclic ref NodeObj
|
|
||||||
NodeObj = object
|
|
||||||
left, right: Node
|
|
||||||
data: string
|
|
||||||
|
|
||||||
|
|
||||||
final pragma
|
final pragma
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue