Skip to content

Commit d6a1602

Browse files
authored
IC: backend: remember produced type information (#17440)
1 parent 1d19cd6 commit d6a1602

File tree

9 files changed

+93
-42
lines changed

9 files changed

+93
-42
lines changed

compiler/ast.nim

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,17 +1229,10 @@ proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
12291229
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
12301230
options: options, owner: owner, offset: defaultOffset)
12311231
when false:
1232-
if id.item > 2141:
1233-
let s = getStackTrace()
1234-
const words = ["createTypeBoundOps",
1235-
"initOperators",
1236-
"generateInstance",
1237-
"semIdentDef", "addLocalDecl"]
1238-
for w in words:
1239-
if w in s:
1240-
x.inc w
1241-
return
1242-
x.inc "<no category>"
1232+
if id.module == 48 and id.item == 39:
1233+
writeStackTrace()
1234+
echo "kind ", symKind, " ", name.s
1235+
if owner != nil: echo owner.name.s
12431236

12441237
proc astdef*(s: PSym): PNode =
12451238
# get only the definition (initializer) portion of the ast

compiler/ccgtypes.nim

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,7 +1386,6 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
13861386
let owner = t.skipTypes(typedescPtrs).itemId.module
13871387
if owner != m.module.position and moduleOpenForCodegen(m, owner):
13881388
# make sure the type info is created in the owner module
1389-
assert m.g.modules[owner] != nil
13901389
discard genTypeInfoV2(m.g.modules[owner], origType, info)
13911390
# reference the type info as extern here
13921391
discard cgsym(m, "TNimTypeV2")
@@ -1456,18 +1455,27 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
14561455
result = "NTI$1$2_" % [rope(typeToC(t)), rope($sig)]
14571456
m.typeInfoMarker[sig] = result
14581457

1459-
let owner = t.skipTypes(typedescPtrs).itemId.module
1458+
let old = m.g.graph.emittedTypeInfo.getOrDefault($result)
1459+
if old != FileIndex(0):
1460+
discard cgsym(m, "TNimType")
1461+
discard cgsym(m, "TNimNode")
1462+
declareNimType(m, "TNimType", result, old.int)
1463+
return prefixTI.rope & result & ")".rope
1464+
1465+
var owner = t.skipTypes(typedescPtrs).itemId.module
14601466
if owner != m.module.position and moduleOpenForCodegen(m, owner):
14611467
# make sure the type info is created in the owner module
1462-
assert m.g.modules[owner] != nil
14631468
discard genTypeInfoV1(m.g.modules[owner], origType, info)
14641469
# reference the type info as extern here
14651470
discard cgsym(m, "TNimType")
14661471
discard cgsym(m, "TNimNode")
14671472
declareNimType(m, "TNimType", result, owner)
14681473
return prefixTI.rope & result & ")".rope
1474+
else:
1475+
owner = m.module.position.int32
14691476

14701477
m.g.typeInfoMarker[sig] = (str: result, owner: owner)
1478+
rememberEmittedTypeInfo(m.g.graph, FileIndex(owner), $result)
14711479

14721480
case t.kind
14731481
of tyEmpty, tyVoid: result = rope"0"

compiler/ic/cbackend.nim

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
## also doing cross-module dependency tracking and DCE that we don't need
1919
## anymore. DCE is now done as prepass over the entire packed module graph.
2020

21-
import std/[packedsets, algorithm]
21+
import std/[packedsets, algorithm, tables]
2222
# std/intsets would give `UnusedImport`, pending https://github.com/nim-lang/Nim/issues/14246
2323
import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
2424
pathutils, extccomp, msgs]
@@ -45,6 +45,10 @@ proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var Alive
4545

4646
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
4747

48+
proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
49+
for x in mitems(m.fromDisk.emittedTypeInfo):
50+
g.emittedTypeInfo[x] = origin
51+
4852
proc addFileToLink(config: ConfigRef; m: PSym) =
4953
let filename = AbsoluteFile toFullPath(config, m.position.FileIndex)
5054
let ext =
@@ -59,11 +63,28 @@ proc addFileToLink(config: ConfigRef; m: PSym) =
5963
flags: {CfileFlag.Cached})
6064
addFileToCompile(config, cf)
6165

62-
proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
66+
when defined(debugDce):
67+
import std / [os, packedsets]
68+
69+
proc storeAliveSymsImpl(asymFile: AbsoluteFile; s: seq[int32]) =
70+
var f = rodfiles.create(asymFile.string)
71+
f.storeHeader()
72+
f.storeSection aliveSymsSection
73+
f.storeSeq(s)
74+
close f
75+
76+
template prepare {.dirty.} =
6377
let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms")
6478
var s = newSeqOfCap[int32](alive[position].len)
6579
for a in items(alive[position]): s.add int32(a)
6680
sort(s)
81+
82+
proc storeAliveSyms(config: ConfigRef; position: int; alive: AliveSyms) =
83+
prepare()
84+
storeAliveSymsImpl(asymFile, s)
85+
86+
proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
87+
prepare()
6788
var f2 = rodfiles.open(asymFile.string)
6889
f2.loadHeader()
6990
f2.loadSection aliveSymsSection
@@ -73,12 +94,17 @@ proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool
7394
if f2.err == ok and oldData == s:
7495
result = false
7596
else:
97+
when defined(debugDce):
98+
let oldAsSet = toPackedSet[int32](oldData)
99+
let newAsSet = toPackedSet[int32](s)
100+
echo "set of live symbols changed ", asymFile.changeFileExt("rod"), " ", position, " ", f2.err
101+
echo "in old but not in new ", oldAsSet.difference(newAsSet)
102+
echo "in new but not in old ", newAsSet.difference(oldAsSet)
103+
104+
if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0:
105+
echo "command failed"
76106
result = true
77-
var f = rodfiles.create(asymFile.string)
78-
f.storeHeader()
79-
f.storeSection aliveSymsSection
80-
f.storeSeq(s)
81-
close f
107+
storeAliveSymsImpl(asymFile, s)
82108

83109
proc generateCode*(g: ModuleGraph) =
84110
## The single entry point, generate C(++) code for the entire
@@ -95,6 +121,8 @@ proc generateCode*(g: ModuleGraph) =
95121
assert false
96122
of storing, outdated:
97123
generateCodeForModule(g, g.packed[i], alive)
124+
closeRodFile(g, g.packed[i].module)
125+
storeAliveSyms(g.config, g.packed[i].module.position, alive)
98126
of loaded:
99127
# Even though this module didn't change, DCE might trigger a change.
100128
# Consider this case: Module A uses symbol S from B and B does not use
@@ -104,3 +132,4 @@ proc generateCode*(g: ModuleGraph) =
104132
generateCodeForModule(g, g.packed[i], alive)
105133
else:
106134
addFileToLink(g.config, g.packed[i].module)
135+
replayTypeInfo(g, g.packed[i], FileIndex(i))

compiler/ic/ic.nim

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ type
4242
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
4343
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
4444

45+
emittedTypeInfo*: seq[string]
46+
4547
sh*: Shared
4648
cfg: PackedConfig
4749

@@ -58,7 +60,7 @@ type
5860
config*: ConfigRef
5961

6062
proc isActive*(e: PackedEncoder): bool = e.config != nil
61-
proc disable*(e: var PackedEncoder) = e.config = nil
63+
proc disable(e: var PackedEncoder) = e.config = nil
6264

6365
template primConfigFields(fn: untyped) {.dirty.} =
6466
fn backend
@@ -552,6 +554,7 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
552554
loadSeqSection attachedOpsSection, m.attachedOps
553555
loadSeqSection methodsPerTypeSection, m.methodsPerType
554556
loadSeqSection enumToStringProcsSection, m.enumToStringProcs
557+
loadSeqSection typeInfoSection, m.emittedTypeInfo
555558

556559
close(f)
557560
result = f.err
@@ -614,6 +617,7 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
614617
storeSeqSection attachedOpsSection, m.attachedOps
615618
storeSeqSection methodsPerTypeSection, m.methodsPerType
616619
storeSeqSection enumToStringProcsSection, m.enumToStringProcs
620+
storeSeqSection typeInfoSection, m.emittedTypeInfo
617621

618622
close(f)
619623
encoder.disable()
@@ -1139,7 +1143,10 @@ proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) =
11391143

11401144
echo "all symbols"
11411145
for i in 0..high(m.sh.syms):
1142-
echo " ", m.sh.strings[m.sh.syms[i].name], " local ID: ", i
1146+
if m.sh.syms[i].name != LitId(0):
1147+
echo " ", m.sh.strings[m.sh.syms[i].name], " local ID: ", i, " kind ", m.sh.syms[i].kind
1148+
else:
1149+
echo " <anon symbol?> local ID: ", i, " kind ", m.sh.syms[i].kind
11431150

11441151
echo "symbols: ", m.sh.syms.len, " types: ", m.sh.types.len,
11451152
" top level nodes: ", m.topLevel.nodes.len, " other nodes: ", m.bodies.nodes.len,

compiler/ic/rodfiles.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type
3636
attachedOpsSection
3737
methodsPerTypeSection
3838
enumToStringProcsSection
39+
typeInfoSection # required by the backend
3940
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
4041

4142
RodFileError* = enum

compiler/modulegraphs.nim

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@
1111
## represents a complete Nim project. Single modules can either be kept in RAM
1212
## or stored in a rod-file.
1313

14-
import ast, astalgo, intsets, tables, options, lineinfos, hashes, idents,
15-
btrees, md5, ropes, msgs
16-
14+
import std / [intsets, tables, hashes, md5]
15+
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils
1716
import ic / [packed_ast, ic]
1817

1918
type
@@ -60,6 +59,7 @@ type
6059
attachedOps*: array[TTypeAttachedOp, Table[ItemId, PSym]] # Type ID, destructors, etc.
6160
methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
6261
enumToStringProcs*: Table[ItemId, LazySym]
62+
emittedTypeInfo*: Table[string, FileIndex]
6363

6464
startupPackedConfig*: PackedConfig
6565
packageSyms*: TStrTable
@@ -68,7 +68,6 @@ type
6868
importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
6969
suggestMode*: bool # whether we are in nimsuggest mode or not.
7070
invalidTransitiveClosure: bool
71-
systemModuleComplete*: bool
7271
inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
7372
# first module that included it
7473
importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
@@ -435,6 +434,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
435434
result.canonTypes = initTable[SigHash, PType]()
436435
result.symBodyHashes = initTable[int, SigHash]()
437436
result.operators = initOperators(result)
437+
result.emittedTypeInfo = initTable[string, FileIndex]()
438438

439439
proc resetAllModules*(g: ModuleGraph) =
440440
initStrTable(g.packageSyms)
@@ -455,6 +455,27 @@ proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
455455
elif fileIdx.int32 < g.ifaces.len:
456456
result = g.ifaces[fileIdx.int32].module
457457

458+
proc rememberEmittedTypeInfo*(g: ModuleGraph; m: FileIndex; ti: string) =
459+
#assert(not isCachedModule(g, m.int32))
460+
if g.config.symbolFiles != disabledSf:
461+
#assert g.encoders[m.int32].isActive
462+
g.packed[m.int32].fromDisk.emittedTypeInfo.add ti
463+
464+
proc closeRodFile*(g: ModuleGraph; m: PSym) =
465+
if g.config.symbolFiles in {readOnlySf, v2Sf}:
466+
# For stress testing we seek to reload the symbols from memory. This
467+
# way much of the logic is tested but the test is reproducible as it does
468+
# not depend on the hard disk contents!
469+
let mint = m.position
470+
saveRodFile(toRodFile(g.config, AbsoluteFile toFullPath(g.config, FileIndex(mint))),
471+
g.encoders[mint], g.packed[mint].fromDisk)
472+
elif g.config.symbolFiles == stressTest:
473+
# debug code, but maybe a good idea for production? Could reduce the compiler's
474+
# memory consumption considerably at the cost of more loads from disk.
475+
let mint = m.position
476+
simulateCachedModule(g, m, g.packed[mint].fromDisk)
477+
g.packed[mint].status = loaded
478+
458479
proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
459480

460481
proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =

compiler/passes.nim

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,9 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
187187
closeParser(p)
188188
if s.kind != llsStdIn: break
189189
closePasses(graph, a)
190+
if graph.config.backend notin {backendC, backendCpp, backendObjc}:
191+
# We only write rod files here if no C-like backend is active.
192+
# The C-like backends have been patched to support the IC mechanism.
193+
# They are responsible for closing the rod files. See `cbackend.nim`.
194+
closeRodFile(graph, module)
190195
result = true

compiler/sem.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
661661
result.add(c.module.ast)
662662
popOwner(c)
663663
popProcCon(c)
664-
saveRodFile(c)
664+
sealRodFile(c)
665665

666666
const semPass* = makePass(myOpen, myProcess, myClose,
667667
isFrontend = true)

compiler/semdata.nim

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -560,23 +560,10 @@ proc addToGenericCache*(c: PContext; s: PSym; inst: PType) =
560560
if c.config.symbolFiles != disabledSf:
561561
storeTypeInst(c.encoder, c.packedRepr, s, inst)
562562

563-
proc saveRodFile*(c: PContext) =
563+
proc sealRodFile*(c: PContext) =
564564
if c.config.symbolFiles != disabledSf:
565565
if c.graph.vm != nil:
566566
for (m, n) in PCtx(c.graph.vm).vmstateDiff:
567567
if m == c.module:
568568
addPragmaComputation(c, n)
569-
if sfSystemModule in c.module.flags:
570-
c.graph.systemModuleComplete = true
571569
c.idgen.sealed = true # no further additions are allowed
572-
if c.config.symbolFiles != stressTest:
573-
# For stress testing we seek to reload the symbols from memory. This
574-
# way much of the logic is tested but the test is reproducible as it does
575-
# not depend on the hard disk contents!
576-
saveRodFile(toRodFile(c.config, AbsoluteFile toFullPath(c.config, FileIndex c.module.position)),
577-
c.encoder, c.packedRepr)
578-
else:
579-
# debug code, but maybe a good idea for production? Could reduce the compiler's
580-
# memory consumption considerably at the cost of more loads from disk.
581-
simulateCachedModule(c.graph, c.module, c.packedRepr)
582-
c.graph.packed[c.module.position].status = loaded

0 commit comments

Comments
 (0)