mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 19:33:42 +00:00
`genProcBody` is handed `BNode(bodyBuf.rootCursor)` under `-d:newIcBackend`, so
`expr` and the ~160 procs under it read the routine body through a cursor rather
than a tree. This had to land as one change: `expr` dispatches to all of them, so
they move together or the dispatch converts at every node.
The evidence that it works is not that it compiles. Cursor-driven and
`PNode`-driven builds emit BYTE-IDENTICAL `.c` (50/50 on an 89k-line target,
12/12 on the grind target), the built program runs and prints the right thing,
and — the part that makes the first number mean something — sabotaging
`bnode.intVal` changes all 12 files. The generator is genuinely reading through
the cursor, not quietly falling back.
Four kinds of site could not simply take `AnyNode`, and each is marked where it
sits rather than left for the next person to rediscover:
* THE GENERATOR REWRITES. `mAppendSeqElem`, `mNewSeq`, `genSetLengthSeq`,
`genWasMoved` and `genArrToSeq` replace a child or a type IN PLACE, and
`genEnumToStr`/`mAsgn`/`spawn` build fresh trees. Those run on `origin(n)` —
the very node the buffer was encoded from — so the mutation lands exactly
where it always did. Where the mutation is then READ (`genArrToSeq` retypes a
bracket, `genArg` replaces a `var` param's type), generation continues on the
origin too, because the buffer does not see the write and a cursor would keep
reading the slot as encoded.
* NILABLE NODES stay `PNode`: a cursor has no standalone nil. That is the
assignment DESTINATION throughout the call family (`genCall` passes nil), the
`check` of an object-constructor field, `exvar`, `stepNode`, the `fin` of a
try statement.
* `PNode`-KEYED TABLES AND ANALYSES take `origin`: `dataCache`, `isPartOf`,
`lhsDoesAlias`, `potentialAlias`, the type-record walkers.
* SHARED PREDICATES in `ast.nim` cannot see `BNode`, so `skipHiddenAddr`,
`isInfixAs` and `getStr` join `canRaise`/`getInt` as templates instantiated
for both. `skipPragmaExpr` is a deliberate exception: it sits above the point
in `ast.nim` where `firstSon` for a `PNode` exists, so `bnode` carries a
one-line spelling with a pointer back.
Two Nim details worth recording. Repeated occurrences of a type class in one
signature share ONE implicit generic, so any proc whose two node parameters can
differ in representation needs explicit params — `genSingleVar`,
`genFieldObjConstr`, `callGlobalVarCppCtor`. And a `{.dirty.}` template inside a
generic resolves its identifiers at instantiation, so `genClosureCall`'s local
`rawProc` had to be bound before the template that uses it or it lost to the
module-level proc of the same name.
Verified: grind clean (1431 bodies, 260_431 nodes, 0 disagreements, origins
exact); the default path is byte-identical to HEAD; all four build
configurations compile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XEF7FJvUkGKvG9LSGuEaNR
232 lines
8.2 KiB
Nim
232 lines
8.2 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2013 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
## Generates traversal procs for the C backend.
|
|
|
|
# included from cgen.nim
|
|
|
|
type
|
|
TTraversalClosure = object
|
|
p: BProc
|
|
visitorFrmt: string
|
|
|
|
|
|
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
|
|
proc genCaseRange(p: BProc, branch: AnyNode, info: var SwitchCaseBuilder)
|
|
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc
|
|
|
|
proc visit(p: BProc, data, visitor: Snippet) =
|
|
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCvisit"),
|
|
cCast(CPointer, data),
|
|
visitor)
|
|
|
|
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
|
typ: PType) =
|
|
if n == nil: return
|
|
case n.kind
|
|
of nkRecList:
|
|
for it in sons(n):
|
|
genTraverseProc(c, accessor, it, typ)
|
|
of nkRecCase:
|
|
if (n.firstSon.kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
|
|
var p = c.p
|
|
let disc = n.firstSon.sym
|
|
if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
|
|
if disc.loc.t == nil:
|
|
internalError(c.p.config, n.info, "genTraverseProc()")
|
|
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(c.p, branch, caseBuilder)
|
|
else:
|
|
p.s(cpsStmts).addCaseElse(caseBuilder)
|
|
do:
|
|
genTraverseProc(c, accessor, lastSon(branch), typ)
|
|
p.s(cpsStmts).addBreak()
|
|
of nkSym:
|
|
let field = n.sym
|
|
if field.typ.kind == tyVoid: return
|
|
if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
|
|
if field.loc.t == nil:
|
|
internalError(c.p.config, n.info, "genTraverseProc()")
|
|
genTraverseProc(c, dotField(accessor, field.loc.snippet), field.loc.t)
|
|
else: internalError(c.p.config, n.info, "genTraverseProc()")
|
|
|
|
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
|
|
if not m.compileToCpp:
|
|
result = dotField(accessor, "Sup")
|
|
else:
|
|
result = accessor
|
|
|
|
proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType)
|
|
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
|
if typ == nil: return
|
|
|
|
var p = c.p
|
|
case typ.kind
|
|
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
|
|
tySink, tyOwned:
|
|
genTraverseProc(c, accessor, skipModifier(typ))
|
|
of tyArray:
|
|
let arraySize = lengthOrd(c.p.config, typ.indexType)
|
|
var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
|
|
var oldCode = p.s(cpsStmts)
|
|
var oldLen, newLen: int
|
|
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), cIntValue(arraySize)):
|
|
oldLen = p.s(cpsStmts).buf.len
|
|
genTraverseProc(c, subscript(accessor, i.snippet), typ.elementType)
|
|
newLen = p.s(cpsStmts).buf.len
|
|
if oldLen == newLen:
|
|
# do not emit dummy long loops for faster debug builds:
|
|
p.s(cpsStmts) = oldCode
|
|
of tyObject:
|
|
var x = typ.baseClass
|
|
if x != nil: x = x.skipTypes(skipPtrs)
|
|
genTraverseProc(c, accessor.parentObj(c.p.module), x)
|
|
if typ.n != nil: genTraverseProc(c, accessor, typ.n, typ)
|
|
of tyTuple:
|
|
let typ = getUniqueType(typ)
|
|
for i, a in typ.ikids:
|
|
genTraverseProc(c, dotField(accessor, "Field" & $i), a)
|
|
of tyRef:
|
|
visit(p, accessor, c.visitorFrmt)
|
|
of tySequence:
|
|
if optSeqDestructors notin c.p.module.config.globalOptions:
|
|
visit(p, accessor, c.visitorFrmt)
|
|
elif containsGarbageCollectedRef(typ.elementType):
|
|
# destructor based seqs are themselves not traced but their data is, if
|
|
# they contain a GC'ed type:
|
|
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCvisitSeq"),
|
|
cCast(CPointer, accessor),
|
|
c.visitorFrmt)
|
|
#genTraverseProcSeq(c, accessor, typ)
|
|
of tyString:
|
|
if tfHasAsgn notin typ.flags:
|
|
visit(p, accessor, c.visitorFrmt)
|
|
of tyProc:
|
|
if typ.callConv == ccClosure:
|
|
visit(p, dotField(accessor, "ClE_0"), c.visitorFrmt)
|
|
else:
|
|
discard
|
|
|
|
proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
|
var p = c.p
|
|
assert typ.kind == tySequence
|
|
var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
|
|
var oldCode = p.s(cpsStmts)
|
|
var oldLen, newLen: int
|
|
var a = TLoc(snippet: accessor)
|
|
let le = lenExpr(c.p, a)
|
|
|
|
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), le):
|
|
oldLen = p.s(cpsStmts).buf.len
|
|
genTraverseProc(c, subscript(dataField(c.p, accessor), i.snippet), typ.elementType)
|
|
newLen = p.s(cpsStmts).buf.len
|
|
if newLen == oldLen:
|
|
# do not emit dummy long loops for faster debug builds:
|
|
p.s(cpsStmts) = oldCode
|
|
|
|
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
|
var p = newProc(nil, m)
|
|
result = "Marker_" & getTypeName(m, origTyp, sig)
|
|
let
|
|
hcrOn = m.hcrOn
|
|
typ = origTyp.skipTypes(abstractInstOwned)
|
|
markerName = if hcrOn: result & "_actual" else: result
|
|
t = getTypeDesc(m, typ)
|
|
|
|
p.s(cpsLocals).addVar(kind = Local, name = "a", typ = t)
|
|
p.s(cpsInit).addAssignment("a", cCast(t, "p"))
|
|
|
|
var c = TTraversalClosure(p: p,
|
|
visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n"
|
|
)
|
|
|
|
assert typ.kind != tyTypeDesc
|
|
if typ.kind == tySequence:
|
|
genTraverseProcSeq(c, "a".rope, typ)
|
|
else:
|
|
if skipTypes(typ.elementType, typedescInst+{tyOwned}).kind == tyArray:
|
|
# C's arrays are broken beyond repair:
|
|
genTraverseProc(c, "a".rope, typ.elementType)
|
|
else:
|
|
genTraverseProc(c, cDeref("a"), typ.elementType)
|
|
|
|
var headerBuilder = newBuilder("")
|
|
headerBuilder.addProcHeaderWithParams(ccNimCall, markerName, CVoid):
|
|
var paramBuilder: ProcParamBuilder
|
|
headerBuilder.addProcParams(paramBuilder):
|
|
headerBuilder.addParam(paramBuilder, name = "p", typ = CPointer)
|
|
headerBuilder.addParam(paramBuilder, name = "op", typ = NimInt)
|
|
let header = extract(headerBuilder)
|
|
|
|
m.s[cfsProcHeaders].addDeclWithVisibility(StaticProc):
|
|
m.s[cfsProcHeaders].add(header)
|
|
m.s[cfsProcHeaders].finishProcHeaderAsProto()
|
|
m.s[cfsProcs].addDeclWithVisibility(StaticProc):
|
|
m.s[cfsProcs].add(header)
|
|
m.s[cfsProcs].finishProcHeaderWithBody():
|
|
m.s[cfsProcs].add(extract(p.s(cpsLocals)))
|
|
m.s[cfsProcs].add(extract(p.s(cpsInit)))
|
|
m.s[cfsProcs].add(extract(p.s(cpsStmts)))
|
|
|
|
if hcrOn:
|
|
var desc = newBuilder("")
|
|
var unnamedParamBuilder: ProcParamBuilder
|
|
desc.addProcParams(unnamedParamBuilder):
|
|
desc.addUnnamedParam(unnamedParamBuilder, CPointer)
|
|
desc.addUnnamedParam(unnamedParamBuilder, NimInt)
|
|
let unnamedParams = extract(desc)
|
|
m.s[cfsProcHeaders].addProcVar(ccNimCall, result, unnamedParams, CVoid)
|
|
m.s[cfsDynLibInit].addAssignmentWithValue(result):
|
|
m.s[cfsDynLibInit].addCast(procPtrTypeUnnamed(ccNimCall, CVoid, unnamedParams)):
|
|
m.s[cfsDynLibInit].addCall("hcrRegisterProc",
|
|
getModuleDllPath(m),
|
|
'"' & result & '"',
|
|
cCast(CPointer, markerName))
|
|
|
|
proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
|
|
discard genTypeInfoV1(m, s.loc.t, info)
|
|
|
|
var p = newProc(nil, m)
|
|
var sLoc = rdLoc(s.loc)
|
|
result = getTempName(m)
|
|
|
|
if sfThread in s.flags and emulatedThreadVars(m.config):
|
|
accessThreadLocalVar(p, s)
|
|
sLoc = derefField("NimTV_", sLoc)
|
|
|
|
var c = TTraversalClosure(p: p,
|
|
visitorFrmt: cIntValue(0) # "#nimGCvisit((void*)$1, 0);$n"
|
|
)
|
|
|
|
genTraverseProc(c, sLoc, s.loc.t)
|
|
|
|
var headerBuilder = newBuilder("")
|
|
headerBuilder.addProcHeaderWithParams(ccNimCall, result, CVoid):
|
|
var paramBuilder: ProcParamBuilder
|
|
headerBuilder.addProcParams(paramBuilder):
|
|
# (void)
|
|
discard
|
|
let header = extract(headerBuilder)
|
|
|
|
m.s[cfsProcHeaders].addDeclWithVisibility(StaticProc):
|
|
m.s[cfsProcHeaders].add(header)
|
|
m.s[cfsProcHeaders].finishProcHeaderAsProto()
|
|
m.s[cfsProcs].addDeclWithVisibility(StaticProc):
|
|
m.s[cfsProcs].add(header)
|
|
m.s[cfsProcs].finishProcHeaderWithBody():
|
|
m.s[cfsProcs].add(extract(p.s(cpsLocals)))
|
|
m.s[cfsProcs].add(extract(p.s(cpsInit)))
|
|
m.s[cfsProcs].add(extract(p.s(cpsStmts)))
|