mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-02 11:53:45 +00:00
Completes the vocabulary migration started with `BNode`: every child READ in
the cgen files now goes through an accessor that a `.bif` `Cursor` can also
serve, so flipping `newIcBackend` is a matter of implementing the vocabulary
rather than rewriting call sites.
* constant indices -> `firstSon` / `secondSon` / `lastSon` / `son(n, k)`,
including the `namePos`/`paramsPos`/`bodyPos`/... slot reads;
* indexed loops -> `sons` / `sonsFrom` / `sonsButLast` and the index-yielding
`isons` / `isonsButLast`. `for i in 0..<n.len: n[i]` is quadratic once
`BNode` is a `Cursor`, because reaching child `i` costs one `skip` per
preceding SUBTREE;
* `n.len == 0` / `> 0` on a node -> `hasSons`, which does not count.
`astdef` gains `sonsButLast(n, count)` and `isonsButLast` — the
`nkOfBranch`/`nkExceptBranch` shape, whose last child is the branch body, and
with `count = 2` the `nkVarTuple`/`nkIdentDefs` shape.
Three places needed more than a rename:
* the C++/goto/setjmp try generators re-subscripted `t[i]` up to ten times
per iteration of their `while i < t.len` walk; the branch node is now
hoisted once per step;
* `genParams` scans the arguments BACKWARDS to decide which need a temporary,
which a `Cursor` cannot do at all. It materializes them in one forward pass
and indexes that — the same order of work, since `needTmp` already
allocates per call;
* loops that stop at a computed position (`casePos`, `until`, `splitPoint`)
walk forward and break instead of counting up to the bound.
What is left is exactly what a `Cursor` backend will not do: writes that build
a fresh `nkProcDef`, and subscripts of a `PType`, `string`, `seq` or `Table`.
`bnode.nim` records the invariant and the `PType` trap — `ast.sons(t: PType)`
is a proc returning `var TTypeSeq`, not the iterator of the same name — which
the type checker enforces, since the `firstSon`/`secondSon`/`lastSon`/`son`
family exists for `PNode` only.
Pure refactor, verified as one: all 216 generated `.c` files byte-identical to
the parent commit; metamorphic IC 16/16; icSuite 19/19 fragments; categories
gc 78, arc 140, destructor 97, closure 23, iter 71, trmacros 6, cpp 50,
exception 47, casestmt 16 with one pre-existing environmental failure
(tests/cpp/tasync_cpp.nim: `cannot open file: jester`).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMyRHByv7hhaQJ4Pa1bHbE
136 lines
5.3 KiB
Nim
136 lines
5.3 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2020 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
# included from cgen.nim
|
|
|
|
## Code specialization instead of the old, incredibly slow 'genericReset'
|
|
## implementation.
|
|
|
|
proc specializeResetT(p: BProc, accessor: Rope, typ: PType)
|
|
|
|
proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
|
typ: PType) =
|
|
if n == nil: return
|
|
case n.kind
|
|
of nkRecList:
|
|
for it in sons(n):
|
|
specializeResetN(p, accessor, it, typ)
|
|
of nkRecCase:
|
|
if (n.firstSon.kind != nkSym): internalError(p.config, n.info, "specializeResetN")
|
|
let disc = n.firstSon.sym
|
|
if disc.loc.snippet == "": fillObjectFields(p.module, typ)
|
|
if disc.loc.t == nil:
|
|
internalError(p.config, n.info, "specializeResetN()")
|
|
let discField = dotField(accessor, disc.loc.snippet)
|
|
p.s(cpsStmts).addSwitchStmt(discField):
|
|
for branch in sonsFrom(n, 1):
|
|
assert branch.kind in {nkOfBranch, nkElse}
|
|
var caseBuilder: SwitchCaseBuilder
|
|
p.s(cpsStmts).addSwitchCase(caseBuilder):
|
|
if branch.kind == nkOfBranch:
|
|
genCaseRange(p, branch, caseBuilder)
|
|
else:
|
|
p.s(cpsStmts).addCaseElse(caseBuilder)
|
|
do:
|
|
specializeResetN(p, accessor, lastSon(branch), typ)
|
|
p.s(cpsStmts).addBreak()
|
|
specializeResetT(p, discField, disc.loc.t)
|
|
of nkSym:
|
|
let field = n.sym
|
|
if field.typ.kind == tyVoid: return
|
|
if field.loc.snippet == "": fillObjectFields(p.module, typ)
|
|
if field.loc.t == nil:
|
|
internalError(p.config, n.info, "specializeResetN()")
|
|
specializeResetT(p, dotField(accessor, field.loc.snippet), field.loc.t)
|
|
else: internalError(p.config, n.info, "specializeResetN()")
|
|
|
|
proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
|
if typ == nil: return
|
|
|
|
case typ.kind
|
|
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
|
|
tySink, tyOwned:
|
|
specializeResetT(p, accessor, skipModifier(typ))
|
|
of tyArray:
|
|
let arraySize = lengthOrd(p.config, typ.indexType)
|
|
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
|
|
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), cIntValue(arraySize)):
|
|
specializeResetT(p, subscript(accessor, i.snippet), typ.elementType)
|
|
of tyObject:
|
|
var x = typ.baseClass
|
|
if x != nil: x = x.skipTypes(skipPtrs)
|
|
specializeResetT(p, accessor.parentObj(p.module), x)
|
|
if typ.n != nil:
|
|
if typ.sym != nil and sfImportc in typ.sym.flags:
|
|
# imported C struct, nimZeroMem
|
|
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
|
|
cCast(ptrType(CPointer), cAddr(accessor)),
|
|
cSizeof(getTypeDesc(p.module, typ)))
|
|
else:
|
|
specializeResetN(p, accessor, typ.n, typ)
|
|
if isCaseObj(typ.n):
|
|
# The active branch was released above. Clear the complete object so
|
|
# stale bytes from overlapping branches cannot be traced by the GC.
|
|
# type
|
|
# Foo = object
|
|
# case kind: bool
|
|
# of true:
|
|
# a: ref Bar # 8 bytes (pointer)
|
|
# of false:
|
|
# b: int # 4 bytes
|
|
# specializeResetT for b emits accessor.b = 0 — writes 4 bytes
|
|
# But the union is 8 bytes wide (sized by the largest branch)
|
|
# The remaining 4 bytes where a used to live are untouched
|
|
# Those stale bytes could contain a heap pointer the GC traces → crash
|
|
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
|
|
cCast(CPointer, cAddr(accessor)),
|
|
cSizeof(getTypeDesc(p.module, typ)))
|
|
of tyTuple:
|
|
let typ = getUniqueType(typ)
|
|
for i, a in typ.ikids:
|
|
specializeResetT(p, dotField(accessor, "Field" & $i), a)
|
|
|
|
of tyString, tyRef, tySequence:
|
|
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "unsureAsgnRef"),
|
|
cCast(ptrType(CPointer), cAddr(accessor)),
|
|
NimNil)
|
|
|
|
of tyProc:
|
|
if typ.callConv == ccClosure:
|
|
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "unsureAsgnRef"),
|
|
cCast(ptrType(CPointer), cAddr(dotField(accessor, "ClE_0"))),
|
|
NimNil)
|
|
p.s(cpsStmts).addFieldAssignment(accessor, "ClP_0", NimNil)
|
|
else:
|
|
p.s(cpsStmts).addAssignment(accessor, NimNil)
|
|
of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
|
|
p.s(cpsStmts).addAssignment(accessor, cIntValue(0))
|
|
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
|
|
p.s(cpsStmts).addAssignment(accessor, NimNil)
|
|
of tySet:
|
|
case mapSetType(p.config, typ)
|
|
of ctArray:
|
|
let t = getTypeDesc(p.module, typ)
|
|
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
|
|
accessor,
|
|
cSizeof(t))
|
|
of ctInt8, ctInt16, ctInt32, ctInt64:
|
|
p.s(cpsStmts).addAssignment(accessor, cIntValue(0))
|
|
else:
|
|
raiseAssert "unexpected set type kind"
|
|
of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
|
|
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
|
|
tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
|
|
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
|
|
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
|
|
discard
|
|
|
|
proc specializeReset(p: BProc, a: TLoc) =
|
|
specializeResetT(p, rdLoc(a), a.t)
|