mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 19:33:42 +00:00
IC: take the last leaf readers, and map where the leaf migration ends
`skipAddr`, `skipAddrDeref` and `isInactiveDestructorCall` move to the seam — all three pure, none of them touching a field symbol. The node-returning ones are graded by a `checkNodeResult` template rather than a hand-written pair, so adding the next one is a line. The more useful half of this commit is the map. Counting rather than guessing: of the 191 `PNode`-taking procs across `cgen` and the `ccg*` files, 160 EMIT — they take a `Builder`/`TLoc` out-param or write into `p` directly. Those do not move one at a time. They are one mutual recursion rooted at `expr`/`genStmts`, so the generator moves as a unit or not at all, and doing that needs write-side capability the seam does not have. What is left over is not a backlog, it is six named blockers, and `bnode`'s module doc now says which proc each one holds up and why: * a type's RECORD TREE is not a body (`PType.n` stays `PNode` by design, so `asgnComplexity` and friends were never candidates); * RETURNS A NODE OR NIL (`getPragmaStmt` — no nil token exists to return); * WRITES TO THE NODE (`easyResultAsgn` sets `nfPreventCg`; the seam is read-only); * NEEDS RENDERING (`preventNrvo` interpolates `$le` into a warning); * NEEDS STABLE FIELD IDENTITY (`lhsDoesAlias`, `potentialAlias`, through `isPartOf` — see the `sym` note, this one is blocked on a property rather than on effort); * MIXED REPRESENTATION (`potentialAlias`, `getPotentialReads` carry a `seq[PNode]` beside the node). Stating it this way because the alternative is someone re-deriving each blocker by trying the migration and watching it fail, which is how three of the six were found. Verified: grind clean (67_857 nodes, 0 disagreements); 215/215 byte-identical `.c` against HEAD on the default path; all four build configurations compile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XEF7FJvUkGKvG9LSGuEaNR
This commit is contained in:
@@ -161,6 +161,35 @@
|
||||
## `genericHead` and the `kids` / `ikids` / `paramTypes` / `signature`
|
||||
## iterators, which name the child and go through `[]`.
|
||||
##
|
||||
## WHERE THE LEAF MIGRATION ENDS. Of the 191 `PNode`-taking procs across
|
||||
## `cgen` and the `ccg*` files, 160 EMIT — they take a `Builder`/`TLoc`
|
||||
## out-param or write into `p` directly. Those do not move one at a time: they
|
||||
## are one mutual recursion rooted at `expr`/`genStmts`, so the whole generator
|
||||
## has to move together, and it needs write-side capability this seam does not
|
||||
## have. What is left over splits into named blockers rather than a backlog, and
|
||||
## each is recorded at its own site:
|
||||
##
|
||||
## * A type's RECORD TREE is not a body. `asgnComplexity`, `isEmptyCaseObjectBranch`,
|
||||
## `containsOpaqueImportcFieldAux`, `genRecordFieldsAux`, `fillResult` and the
|
||||
## type-section walkers read `PType.n`, which stays a `PNode` by design (see
|
||||
## the `BType` note below). They are not migration candidates at all.
|
||||
## * RETURNS A NODE OR NIL — `ccgutils.getPragmaStmt`. `.bif` spells a missing
|
||||
## child as a `DotToken` INSIDE a tree; there is no nil token to hand back as a
|
||||
## return value and a `Cursor` is not nilable. The fix is to split the
|
||||
## predicate out, as `stmtsContainPragma` does.
|
||||
## * WRITES TO THE NODE — `cgen.easyResultAsgn` does `incl n.flags, nfPreventCg`.
|
||||
## The seam is read-only and a `Cursor` points into a shared token buffer.
|
||||
## * NEEDS RENDERING — `ccgcalls.preventNrvo` interpolates `$le` into
|
||||
## `warnObservableStores`. Reconstructing source text is a different job from
|
||||
## reading a node, and only a diagnostic wants it.
|
||||
## * NEEDS STABLE FIELD IDENTITY — `lhsDoesAlias` and `potentialAlias` through
|
||||
## `aliases.isPartOf`, which compares field `sym.id`. See the note on `sym`
|
||||
## below: it is not idempotent for fields, so this one is not blocked on
|
||||
## effort, it is blocked on a property the seam does not currently have.
|
||||
## * MIXED REPRESENTATION — `potentialAlias` and `getPotentialReads` build and
|
||||
## consume a `seq[PNode]` alongside the node, so both sides would have to be
|
||||
## the same spelling.
|
||||
##
|
||||
## There is deliberately no `BType` alongside `BNode`. Types stay `PType`s even
|
||||
## under `newIcBackend` — `typ` below returns one — because the backend asks
|
||||
## them semantic questions (`skipTypes`, `getSize`, `lengthOrd`, the record
|
||||
|
||||
@@ -696,7 +696,7 @@ y.v() --> y.v() is correct
|
||||
|
||||
"""
|
||||
|
||||
proc skipAddrDeref(node: PNode): PNode =
|
||||
proc skipAddrDeref[T: AnyNode](node: T): T =
|
||||
var n = node
|
||||
var isAddr = false
|
||||
case n.kind
|
||||
@@ -915,7 +915,7 @@ proc notYetAlive(n: AnyNode): bool {.inline.} =
|
||||
let r = getRoot(n)
|
||||
result = r != nil and r.loc.lode == nil
|
||||
|
||||
proc isInactiveDestructorCall(p: BProc, e: PNode): bool =
|
||||
proc isInactiveDestructorCall(p: BProc, e: AnyNode): bool =
|
||||
#[ Consider this example.
|
||||
|
||||
var :tmpD_3281815
|
||||
@@ -932,7 +932,7 @@ proc isInactiveDestructorCall(p: BProc, e: PNode): bool =
|
||||
We want to return early but the 'finally' section is traversed before
|
||||
the 'let args = ...' statement. We exploit this to generate better
|
||||
code for 'return'. ]#
|
||||
result = e.len == 2 and e.firstSon.kind == nkSym and
|
||||
result = e.safeLen == 2 and e.firstSon.kind == nkSym and
|
||||
e.firstSon.sym.name.s == "=destroy" and notYetAlive(e.secondSon.skipAddr)
|
||||
|
||||
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
|
||||
@@ -1622,6 +1622,7 @@ when defined(newIcBackend):
|
||||
check "isDeepConstExpr", isDeepConstExpr(n)
|
||||
check "stmtsContainPragma", stmtsContainPragma(n, wLinearScanEnd)
|
||||
check "notYetAlive", notYetAlive(n)
|
||||
check "isInactiveDestructorCall", isInactiveDestructorCall(p, n)
|
||||
check "getInt", (if n.kind in nkIntLits: $getInt(n) else: "")
|
||||
check "sameValue self", sameValue(n, n)
|
||||
|
||||
@@ -1655,14 +1656,22 @@ when defined(newIcBackend):
|
||||
# spine, so two different stopping points on the same input differ in one or
|
||||
# the other unless the tree has two identical nodes at one position, which
|
||||
# would make the choice immaterial anyway.
|
||||
block:
|
||||
let cs = skipTrivialIndirections(c)
|
||||
let a2 = skipTrivialIndirections(a)
|
||||
if cs.kind != a2.kind:
|
||||
bail("skipTrivialIndirections kind", $cs.kind, $a2.kind)
|
||||
if cs.info != a2.info:
|
||||
bail("skipTrivialIndirections info",
|
||||
$(m.config, cs.info), $(m.config, a2.info))
|
||||
template checkNodeResult(what: string; call: untyped) =
|
||||
block:
|
||||
let cs = block:
|
||||
let n {.inject.} = c
|
||||
call
|
||||
let a2 = block:
|
||||
let n {.inject.} = a
|
||||
call
|
||||
if cs.kind != a2.kind:
|
||||
bail(what & " kind", $cs.kind, $a2.kind)
|
||||
if cs.info != a2.info:
|
||||
bail(what & " info", $(m.config, cs.info), $(m.config, a2.info))
|
||||
|
||||
checkNodeResult "skipTrivialIndirections", skipTrivialIndirections(n)
|
||||
checkNodeResult "skipAddr", skipAddr(n)
|
||||
checkNodeResult "skipAddrDeref", skipAddrDeref(n)
|
||||
|
||||
# Shape-guarded, matching the contexts production calls them from.
|
||||
if a.kind in nkCallKinds and a.safeLen > 0:
|
||||
|
||||
@@ -254,8 +254,8 @@ proc isRunnableExamples*(n: PNode): bool =
|
||||
result = n.kind == nkSym and n.sym.magic == mRunnableExamples or
|
||||
n.kind == nkIdent and n.ident.id == ord(wRunnableExamples)
|
||||
|
||||
proc skipAddr*(n: PNode): PNode {.inline.} =
|
||||
result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n
|
||||
proc skipAddr*[T: AnyNode](n: T): T {.inline.} =
|
||||
result = if n.kind in {nkAddr, nkHiddenAddr}: n.firstSon else: n
|
||||
|
||||
proc getPotentialWrites*(n: PNode; mutate: bool; result: var seq[PNode]) =
|
||||
case n.kind:
|
||||
|
||||
Reference in New Issue
Block a user