You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: doc/cfgcpuReadme.md
+39Lines changed: 39 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -314,6 +314,45 @@ An `ExecutionContext` tracks graph state within a single flow of control:
314
314
-**Variant merging** minimizes SelectorNode creation. Most self-modifying code only touches operands (non-final fields), not opcodes.
315
315
-**Null wildcards in signatures** give a uniform matching mechanism that works for both merged instructions and SelectorNode dispatch.
316
316
-**Observer-based replacement** (`InstructionReplacerRegistry`) keeps caches and graph consistent when nodes are merged, without coupling components.
317
+
-**CfgBlock as a structural overlay** - blocks group straight-line instruction sequences without duplicating edge state. Successors/predecessors delegate by reference to the terminator/entry, so they stay in sync with the instruction-level graph automatically.
318
+
-**O(1) block liveness** via a maintained counter rather than iterating all contained instructions on every check.
319
+
-**Block boundary from instruction properties** - the linker determines boundaries exclusively from `IsBlockTerminator` and `IsBlockStarter` flags (set at parse time) plus static memory adjacency, never from `Kind` or `MaxSuccessorsCount` directly.
320
+
-**Monotonic discovery** - `IsDiscoveryComplete` flips from false to true exactly once and never back, simplifying reasoning about block state.
321
+
322
+
## CfgBlock
323
+
324
+
A `CfgBlock` groups a contiguous sequence of instructions that always execute together: one entry, one exit, no intermediate join points. It's a structural overlay on the instruction-level CFG - the instruction-level edges remain the single source of truth.
325
+
326
+
### Structure
327
+
328
+
-`Entry` - first instruction in the block.
329
+
-`Terminator` - last instruction (may be a `CfgInstruction` or a `SelectorNode`).
330
+
-`Instructions` - ordered list from entry through terminator inclusive.
331
+
-`IsDiscoveryComplete` - true once the linker has finalised the block.
332
+
-`IsLive` - true if every contained instruction is live (O(1) via maintained counter).
333
+
334
+
### Edge Delegation
335
+
336
+
Block-level `Successors` returns `Terminator.Successors` by reference. Block-level `Predecessors` returns `Entry.Predecessors` by reference. No separate block-edge state is stored - this eliminates sync bugs.
337
+
338
+
### Block Construction (NodeLinker)
339
+
340
+
The linker builds blocks incrementally as instruction-level edges are added:
341
+
342
+
1.**Bootstrap** - first edge from a node opens a one-node block for it.
343
+
2.**Continuation** - if the predecessor is not a terminator, the next node is not a starter, and they're memory-adjacent, the next node is appended to the predecessor's block.
344
+
3.**Boundary** - otherwise, the predecessor's block is closed and the next node gets its own block (new or split from an existing one).
345
+
4.**Split** - when a new edge targets the interior of an existing block, the block is split at that point.
346
+
347
+
### Self-Modifying Code and Blocks
348
+
349
+
When `ReplaceInstruction` fires (variant merging), the replacement happens in-place within the block (`ReplaceInPlace`). The new instruction inherits the block position and back-pointer. Subsequent edge rewires hit the intra-block idempotency check and don't cause spurious splits.
350
+
351
+
When a `SelectorNode` is injected via `CreateSelectorNodeBetween`, it either absorbed into the predecessor's block as its terminator (if the continuation rule applies) or lands in its own one-node block (if the predecessor is already a terminator).
352
+
353
+
### Hot-Path Execution
354
+
355
+
When the next node belongs to a discovered, live block, `CfgCpu.ExecuteBlock` walks the block's instruction list directly without re-resolving through the feeder between steps. This skips the cold-path overhead (feeder lookup, linker call, memory reconciliation) for every non-terminator instruction in the block. External interrupts fire once at the block boundary, not between every instruction.
0 commit comments