max(0, 1 − sum of PBT residual-risk estimates). Uses each distinct available PBT’s latest-run estimate and its own sampling scope; PBTs without estimates are reported and omitted. An observed violation remains the suite result; when other PBTs have estimates, their estimate is still shown. Assumes no independence. This is a combined point estimate, not a statistical confidence bound or deployment reliability. Uncovered passages are outside its scope.
Missing confidence evidence (omitted from the estimate):
Coverage added over the baseline, grouped by PBT. Only files with gains appear below.
| Source file | Baseline coverage | Baseline + input | Contributing input |
|---|---|---|---|
…/lib/Dataflow/IR/DataflowGraphValidation.cppMS0V | 1033/1540lines67.1% 571/1056branches54.1% | 1033/1540lines67.1%+0 572/1056branches54.2%+1 | |
1 newly covered branch740 | |||
…/lib/Dataflow/IR/DataflowActorSemantics.cppMS0V | 1089/1621lines67.2% 747/1330branches56.2% | 1090/1621lines67.2%+1 751/1330branches56.5%+4 | +1 line · +4 branchesOpen PBT +1 line · +4 branchesOpen PBT |
1 newly covered line · 4 newly covered branches283 | |||
…/lib/Dataflow/IR/DataflowGraphCausality.cppMS0V | 169/189lines89.4% 79/98branches80.6% | 169/189lines89.4%+0 81/98branches82.7%+2 | |
2 newly covered branches25 | |||
…/lib/Dataflow/IR/DataflowGraphValidation.cppMS0V | 1033/1540lines67.1% 571/1056branches54.1% | 1041/1540lines67.6%+8 579/1056branches54.8%+8 | +8 lines · +8 branchesOpen PBT +8 lines · +8 branchesOpen PBT |
8 newly covered lines · 8 newly covered branches254 | |||
…/lib/Dataflow/IR/OperationSchema.cppMS0V | 274/716lines38.3% 127/350branches36.3% | 274/716lines38.3%+0 129/350branches36.9%+2 | +1 branchOpen PBT +1 branchOpen PBT |
2 newly covered branches728 | |||
…/lib/Frontend/Lowering/GraphParallelLowering.cppMS0V | 651/1243lines52.4% 284/786branches36.1% | 698/1243lines56.2%+47 316/786branches40.2%+32 | +5 lines · +5 branchesOpen PBT +5 lines · +5 branchesOpen PBT |
47 newly covered lines · 32 newly covered branches424 | |||
…/lib/Frontend/Lowering/LowerGraphConstantsPass.cppMS0V | 80/83lines96.4% 18/24branches75.0% | 80/83lines96.4%+0 19/24branches79.2%+1 | |
1 newly covered branch68 | |||
…/lib/Dataflow/IR/DataflowActorSemantics.cppMS0V | 1089/1621lines67.2% 747/1330branches56.2% | 1090/1621lines67.2%+1 750/1330branches56.4%+3 | |
1 newly covered line · 3 newly covered branches283 | |||
…/lib/Dataflow/IR/DataflowGraphValidation.cppMS0V | 1033/1540lines67.1% 571/1056branches54.1% | 1038/1540lines67.4%+5 575/1056branches54.5%+4 | |
5 newly covered lines · 4 newly covered branches711 | |||
…/lib/Dataflow/IR/OperationSchema.cppMS0V | 274/716lines38.3% 127/350branches36.3% | 274/716lines38.3%+0 128/350branches36.6%+1 | |
1 newly covered branch736 | |||
…/lib/Dataflow/IR/DataflowDialect.cppMS1V | 22/32lines68.8% 4/10branches40.0% | 25/32lines78.1%+3 7/10branches70.0%+3 | |
3 newly covered lines · 3 newly covered branches24 | |||
…/lib/Dataflow/IR/DataflowFunctionLikeOps.cppMS1V | 693/1097lines63.2% 264/592branches44.6% | 747/1097lines68.1%+54 298/592branches50.3%+34 | |
54 newly covered lines · 34 newly covered branches102 | |||
…/lib/Frontend/Lowering/LowerForallToThreadPass.cppMS1V | 30/34lines88.2% 2/4branches50.0% | 30/34lines88.2%+0 3/4branches75.0%+1 | |
1 newly covered branch45 | |||
…/lib/Dataflow/IR/DataflowFunctionLikeOps.cppMS1V | 693/1097lines63.2% 264/592branches44.6% | 734/1097lines66.9%+41 287/592branches48.5%+23 | |
41 newly covered lines · 23 newly covered branches102 | |||
…/lib/Frontend/Lowering/LowerForallToThreadPass.cppMS1V | 30/34lines88.2% 2/4branches50.0% | 31/34lines91.2%+1 4/4branches100.0%+2 | |
1 newly covered line · 2 newly covered branches37 | |||
| Source file | Baseline coverage | Baseline + input | Contributing input |
|---|---|---|---|
| derived from the coverage profile | not measured | not measured | no drafts yet |
…/loom/include/Common/Artifact.hMS0V | 13/37lines35.1% 3/18branches16.7% | 13/37lines35.1%+0 3/18branches16.7%+0 | Open PBT |
…/include/Frontend/Lowering/StreamLoopAttrs.hMS0V | 31/41lines75.6% 10/14branches71.4% | 31/41lines75.6%+0 10/14branches71.4%+0 | Open PBT |
…/loom/lib/Common/DiagnosticVerbosity.cppMS0V | 12/41lines29.3% 1/28branches3.6% | 12/41lines29.3%+0 1/28branches3.6%+0 | Open PBT |
…/loom/lib/Common/IndexWidth.cppMS0V | 63/84lines75.0% 28/42branches66.7% | 63/84lines75.0%+0 28/42branches66.7%+0 | Open PBT |
…/loom/lib/Common/InvocationDiagnosticLog.cppMS0V | 6/100lines6.0% 1/70branches1.4% | 6/100lines6.0%+0 1/70branches1.4%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowActorSemantics.cppMS0V | 1089/1621lines67.2% 747/1330branches56.2% | 1089/1621lines67.2%+0 747/1330branches56.2%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowChannelOps.cppMS0V | 59/88lines67.0% 13/38branches34.2% | 59/88lines67.0%+0 13/38branches34.2%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowDialect.cppMS0V | 22/32lines68.8% 4/10branches40.0% | 22/32lines68.8%+0 4/10branches40.0%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowFunctionLikeOps.cppMS0V | 693/1097lines63.2% 264/592branches44.6% | 693/1097lines63.2%+0 264/592branches44.6%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowGraphCausality.cppMS0V | 169/189lines89.4% 79/98branches80.6% | 169/189lines89.4%+0 79/98branches80.6%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowMemoryContracts.cppMS0V | 198/371lines53.4% 98/218branches45.0% | 198/371lines53.4%+0 98/218branches45.0%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowOps.cppMS0V | 270/410lines65.9% 103/228branches45.2% | 270/410lines65.9%+0 103/228branches45.2%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowProgramValidation.cppMS0V | 367/502lines73.1% 156/276branches56.5% | 367/502lines73.1%+0 156/276branches56.5%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowThreadCompletion.cppMS0V | 288/361lines79.8% 148/218branches67.9% | 288/361lines79.8%+0 148/218branches67.9%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowVectorSemantics.cppMS0V | 81/121lines66.9% 53/106branches50.0% | 81/121lines66.9%+0 53/106branches50.0%+0 | Open PBT |
…/lib/Dataflow/IR/OperationSchema.cppMS0V | 274/716lines38.3% 127/350branches36.3% | 274/716lines38.3%+0 127/350branches36.3%+0 | Open PBT |
…/lib/Dataflow/IR/OperationSchemaCodecInternal.hMS0V | 28/120lines23.3% 4/44branches9.1% | 28/120lines23.3%+0 4/44branches9.1%+0 | Open PBT |
…/lib/Dataflow/IR/OperationSchemaTypeCodec.cppMS0V | 85/516lines16.5% 55/374branches14.7% | 85/516lines16.5%+0 55/374branches14.7%+0 | Open PBT |
…/lib/Dataflow/Transforms/DataflowRewritePass.cppMS0V | 338/559lines60.5% 172/340branches50.6% | 338/559lines60.5%+0 172/340branches50.6%+0 | Open PBT |
…/lib/Frontend/IR/LoomDialect.cppMS0V | 6/6lines100.0% branchesnot measured | 6/6lines100.0%+0 branchesnot measured | Open PBT |
…/lib/Frontend/IR/LoomOps.cppMS0V | 120/184lines65.2% 71/112branches63.4% | 120/184lines65.2%+0 71/112branches63.4%+0 | Open PBT |
…/lib/Frontend/Lowering/ExactMemRefLayout.cppMS0V | 61/143lines42.7% 26/80branches32.5% | 61/143lines42.7%+0 26/80branches32.5%+0 | Open PBT |
…/lib/Frontend/Lowering/ExpandGraphMemrefCopyPass.cppMS0V | 79/90lines87.8% 20/22branches90.9% | 79/90lines87.8%+0 20/22branches90.9%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphIndexLowering.cppMS0V | 260/288lines90.3% 129/168branches76.8% | 260/288lines90.3%+0 129/168branches76.8%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphParallelLowering.cppMS0V | 651/1243lines52.4% 284/786branches36.1% | 651/1243lines52.4%+0 284/786branches36.1%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphRegionAdmission.cppMS0V | 58/110lines52.7% 29/92branches31.5% | 58/110lines52.7%+0 29/92branches31.5%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphRegionLowering.cppMS0V | 1416/1626lines87.1% 532/680branches78.2% | 1416/1626lines87.1%+0 532/680branches78.2%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphStreamBoundaryLowering.cppMS0V | 934/1072lines87.1% 299/432branches69.2% | 934/1072lines87.1%+0 299/432branches69.2%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphStreamBoundaryLowering.hMS0V | 4/4lines100.0% 4/4branches100.0% | 4/4lines100.0%+0 4/4branches100.0%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerForToGraphPass.cppMS0V | 1033/1240lines83.3% 374/540branches69.3% | 1033/1240lines83.3%+0 374/540branches69.3%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerForallToThreadPass.cppMS0V | 30/34lines88.2% 2/4branches50.0% | 30/34lines88.2%+0 2/4branches50.0%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerGraphConstantsPass.cppMS0V | 80/83lines96.4% 18/24branches75.0% | 80/83lines96.4%+0 18/24branches75.0%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerGraphMemoryPass.cppMS0V | 525/841lines62.4% 208/400branches52.0% | 525/841lines62.4%+0 208/400branches52.0%+0 | Open PBT |
…/lib/Frontend/Lowering/Pipeline.cppMS0V | 18/21lines85.7% branchesnot measured | 18/21lines85.7%+0 branchesnot measured | Open PBT |
…/lib/Frontend/Lowering/RankedMemRefLowering.cppMS0V | 60/133lines45.1% 27/94branches28.7% | 60/133lines45.1%+0 27/94branches28.7%+0 | Open PBT |
…/lib/Frontend/Raising/DeduplicateSCFWhileStatePass.cppMS0V | 13/135lines9.6% 0/60branches0.0% | 13/135lines9.6%+0 0/60branches0.0%+0 | Open PBT |
…/lib/Frontend/Raising/LLVMArithToArithPass.cppMS0V | 300/319lines94.0% 94/116branches81.0% | 300/319lines94.0%+0 94/116branches81.0%+0 | Open PBT |
…/lib/Frontend/Raising/LLVMCfToCfPass.cppMS0V | 77/80lines96.2% 8/8branches100.0% | 77/80lines96.2%+0 8/8branches100.0%+0 | Open PBT |
…/lib/Frontend/Raising/LiftCFToSCFPass.cppMS0V | 616/694lines88.8% 293/386branches75.9% | 616/694lines88.8%+0 293/386branches75.9%+0 | Open PBT |
…/lib/Frontend/Raising/MaterializeFMulAddPass.cppMS0V | 70/106lines66.0% 11/26branches42.3% | 70/106lines66.0%+0 11/26branches42.3%+0 | Open PBT |
…/lib/Frontend/Raising/NormalizeLiftedSCFExitPass.cppMS0V | 232/250lines92.8% 120/182branches65.9% | 232/250lines92.8%+0 120/182branches65.9%+0 | Open PBT |
…/lib/Frontend/Raising/Pipeline.cppMS0V | 10/19lines52.6% branchesnot measured | 10/19lines52.6%+0 branchesnot measured | Open PBT |
…/lib/Frontend/Raising/SCFForToForallPass.cppMS0V | 494/738lines66.9% 241/458branches52.6% | 494/738lines66.9%+0 241/458branches52.6%+0 | Open PBT |
…/lib/Frontend/Raising/SCFWhileToForPass.cppMS0V | 164/176lines93.2% 70/94branches74.5% | 164/176lines93.2%+0 70/94branches74.5%+0 | Open PBT |
…/loom/tools/loom-raise-opt/loom-raise-opt.cppMS0V | 12/12lines100.0% branchesnot measured | 12/12lines100.0%+0 branchesnot measured | Open PBT |
…/loom/include/Common/Artifact.hMS0V | 13/37lines35.1% 3/18branches16.7% | 13/37lines35.1%+0 3/18branches16.7%+0 | Open PBT |
…/include/Dataflow/IR/OperationSchema.hMS0V | 8/91lines8.8% 5/66branches7.6% | 8/91lines8.8%+0 5/66branches7.6%+0 | Open PBT |
…/include/Frontend/Lowering/StreamLoopAttrs.hMS0V | 31/41lines75.6% 10/14branches71.4% | 31/41lines75.6%+0 10/14branches71.4%+0 | Open PBT |
…/loom/lib/Common/DiagnosticVerbosity.cppMS0V | 12/41lines29.3% 1/28branches3.6% | 12/41lines29.3%+0 1/28branches3.6%+0 | Open PBT |
…/loom/lib/Common/IndexWidth.cppMS0V | 63/84lines75.0% 28/42branches66.7% | 63/84lines75.0%+0 28/42branches66.7%+0 | Open PBT |
…/loom/lib/Common/InvocationDiagnosticLog.cppMS0V | 6/100lines6.0% 1/70branches1.4% | 6/100lines6.0%+0 1/70branches1.4%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowChannelOps.cppMS0V | 59/88lines67.0% 13/38branches34.2% | 59/88lines67.0%+0 13/38branches34.2%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowDialect.cppMS0V | 22/32lines68.8% 4/10branches40.0% | 22/32lines68.8%+0 4/10branches40.0%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowFunctionLikeOps.cppMS0V | 693/1097lines63.2% 264/592branches44.6% | 693/1097lines63.2%+0 264/592branches44.6%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowMemoryContracts.cppMS0V | 198/371lines53.4% 98/218branches45.0% | 198/371lines53.4%+0 98/218branches45.0%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowOps.cppMS0V | 270/410lines65.9% 103/228branches45.2% | 270/410lines65.9%+0 103/228branches45.2%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowProgramValidation.cppMS0V | 367/502lines73.1% 156/276branches56.5% | 367/502lines73.1%+0 156/276branches56.5%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowSyncRendezvous.cppMS0V | 21/21lines100.0% 6/12branches50.0% | 21/21lines100.0%+0 6/12branches50.0%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowThreadCompletion.cppMS0V | 288/361lines79.8% 148/218branches67.9% | 288/361lines79.8%+0 148/218branches67.9%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowVectorSemantics.cppMS0V | 81/121lines66.9% 53/106branches50.0% | 81/121lines66.9%+0 53/106branches50.0%+0 | Open PBT |
…/lib/Dataflow/IR/OperationSchemaCodecInternal.hMS0V | 28/120lines23.3% 4/44branches9.1% | 28/120lines23.3%+0 4/44branches9.1%+0 | Open PBT |
…/lib/Dataflow/IR/OperationSchemaTypeCodec.cppMS0V | 85/516lines16.5% 55/374branches14.7% | 85/516lines16.5%+0 55/374branches14.7%+0 | Open PBT |
…/lib/Dataflow/Transforms/DataflowRewritePass.cppMS0V | 338/559lines60.5% 172/340branches50.6% | 338/559lines60.5%+0 172/340branches50.6%+0 | Open PBT |
…/lib/Frontend/Analysis/MemoryProvenance.cppMS0V | 153/317lines48.3% 80/326branches24.5% | 153/317lines48.3%+0 80/326branches24.5%+0 | Open PBT |
…/lib/Frontend/IR/LoomDialect.cppMS0V | 6/6lines100.0% branchesnot measured | 6/6lines100.0%+0 branchesnot measured | Open PBT |
…/lib/Frontend/IR/LoomOps.cppMS0V | 120/184lines65.2% 71/112branches63.4% | 120/184lines65.2%+0 71/112branches63.4%+0 | Open PBT |
…/lib/Frontend/Lowering/ExactMemRefLayout.cppMS0V | 61/143lines42.7% 26/80branches32.5% | 61/143lines42.7%+0 26/80branches32.5%+0 | Open PBT |
…/lib/Frontend/Lowering/ExpandGraphMemrefCopyPass.cppMS0V | 79/90lines87.8% 20/22branches90.9% | 79/90lines87.8%+0 20/22branches90.9%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphIndexLowering.cppMS0V | 260/288lines90.3% 129/168branches76.8% | 260/288lines90.3%+0 129/168branches76.8%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphRegionAdmission.cppMS0V | 58/110lines52.7% 29/92branches31.5% | 58/110lines52.7%+0 29/92branches31.5%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphRegionLowering.cppMS0V | 1416/1626lines87.1% 532/680branches78.2% | 1416/1626lines87.1%+0 532/680branches78.2%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphStreamBoundaryLowering.cppMS0V | 934/1072lines87.1% 299/432branches69.2% | 934/1072lines87.1%+0 299/432branches69.2%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphStreamBoundaryLowering.hMS0V | 4/4lines100.0% 4/4branches100.0% | 4/4lines100.0%+0 4/4branches100.0%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerForToGraphPass.cppMS0V | 1033/1240lines83.3% 374/540branches69.3% | 1033/1240lines83.3%+0 374/540branches69.3%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerForallToThreadPass.cppMS0V | 30/34lines88.2% 2/4branches50.0% | 30/34lines88.2%+0 2/4branches50.0%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerGraphMemoryPass.cppMS0V | 525/841lines62.4% 208/400branches52.0% | 525/841lines62.4%+0 208/400branches52.0%+0 | Open PBT |
…/lib/Frontend/Lowering/Pipeline.cppMS0V | 18/21lines85.7% branchesnot measured | 18/21lines85.7%+0 branchesnot measured | Open PBT |
…/lib/Frontend/Lowering/RankedMemRefLowering.cppMS0V | 60/133lines45.1% 27/94branches28.7% | 60/133lines45.1%+0 27/94branches28.7%+0 | Open PBT |
…/lib/Frontend/Raising/DeduplicateSCFWhileStatePass.cppMS0V | 13/135lines9.6% 0/60branches0.0% | 13/135lines9.6%+0 0/60branches0.0%+0 | Open PBT |
…/lib/Frontend/Raising/LLVMArithToArithPass.cppMS0V | 300/319lines94.0% 94/116branches81.0% | 300/319lines94.0%+0 94/116branches81.0%+0 | Open PBT |
…/lib/Frontend/Raising/LLVMCfToCfPass.cppMS0V | 77/80lines96.2% 8/8branches100.0% | 77/80lines96.2%+0 8/8branches100.0%+0 | Open PBT |
…/lib/Frontend/Raising/LiftCFToSCFPass.cppMS0V | 616/694lines88.8% 293/386branches75.9% | 616/694lines88.8%+0 293/386branches75.9%+0 | Open PBT |
…/lib/Frontend/Raising/MaterializeFMulAddPass.cppMS0V | 70/106lines66.0% 11/26branches42.3% | 70/106lines66.0%+0 11/26branches42.3%+0 | Open PBT |
…/lib/Frontend/Raising/NormalizeLiftedSCFExitPass.cppMS0V | 232/250lines92.8% 120/182branches65.9% | 232/250lines92.8%+0 120/182branches65.9%+0 | Open PBT |
…/lib/Frontend/Raising/Pipeline.cppMS0V | 10/19lines52.6% branchesnot measured | 10/19lines52.6%+0 branchesnot measured | Open PBT |
…/lib/Frontend/Raising/SCFForToForallPass.cppMS0V | 494/738lines66.9% 241/458branches52.6% | 494/738lines66.9%+0 241/458branches52.6%+0 | Open PBT |
…/lib/Frontend/Raising/SCFWhileToForPass.cppMS0V | 164/176lines93.2% 70/94branches74.5% | 164/176lines93.2%+0 70/94branches74.5%+0 | Open PBT |
…/loom/tools/loom-raise-opt/loom-raise-opt.cppMS0V | 12/12lines100.0% branchesnot measured | 12/12lines100.0%+0 branchesnot measured | Open PBT |
| derived from the coverage profile | not measured | not measured | no drafts yet |
…/loom/include/Common/Artifact.hMS0V | 13/37lines35.1% 3/18branches16.7% | 13/37lines35.1%+0 3/18branches16.7%+0 | Open PBT |
…/include/Dataflow/IR/OperationSchema.hMS0V | 8/91lines8.8% 5/66branches7.6% | 8/91lines8.8%+0 5/66branches7.6%+0 | Open PBT |
…/include/Frontend/Lowering/StreamLoopAttrs.hMS0V | 31/41lines75.6% 10/14branches71.4% | 31/41lines75.6%+0 10/14branches71.4%+0 | Open PBT |
…/loom/lib/Common/DiagnosticVerbosity.cppMS0V | 12/41lines29.3% 1/28branches3.6% | 12/41lines29.3%+0 1/28branches3.6%+0 | Open PBT |
…/loom/lib/Common/IndexWidth.cppMS0V | 63/84lines75.0% 28/42branches66.7% | 63/84lines75.0%+0 28/42branches66.7%+0 | Open PBT |
…/loom/lib/Common/InvocationDiagnosticLog.cppMS0V | 6/100lines6.0% 1/70branches1.4% | 6/100lines6.0%+0 1/70branches1.4%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowChannelOps.cppMS0V | 59/88lines67.0% 13/38branches34.2% | 59/88lines67.0%+0 13/38branches34.2%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowDialect.cppMS0V | 22/32lines68.8% 4/10branches40.0% | 22/32lines68.8%+0 4/10branches40.0%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowFunctionLikeOps.cppMS0V | 693/1097lines63.2% 264/592branches44.6% | 693/1097lines63.2%+0 264/592branches44.6%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowGraphCausality.cppMS0V | 169/189lines89.4% 79/98branches80.6% | 169/189lines89.4%+0 79/98branches80.6%+0 | |
…/lib/Dataflow/IR/DataflowMemoryContracts.cppMS0V | 198/371lines53.4% 98/218branches45.0% | 198/371lines53.4%+0 98/218branches45.0%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowOps.cppMS0V | 270/410lines65.9% 103/228branches45.2% | 270/410lines65.9%+0 103/228branches45.2%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowProgramValidation.cppMS0V | 367/502lines73.1% 156/276branches56.5% | 367/502lines73.1%+0 156/276branches56.5%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowSyncRendezvous.cppMS0V | 21/21lines100.0% 6/12branches50.0% | 21/21lines100.0%+0 6/12branches50.0%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowThreadCompletion.cppMS0V | 288/361lines79.8% 148/218branches67.9% | 288/361lines79.8%+0 148/218branches67.9%+0 | Open PBT |
…/lib/Dataflow/IR/DataflowVectorSemantics.cppMS0V | 81/121lines66.9% 53/106branches50.0% | 81/121lines66.9%+0 53/106branches50.0%+0 | Open PBT |
…/lib/Dataflow/IR/OperationSchemaCodecInternal.hMS0V | 28/120lines23.3% 4/44branches9.1% | 28/120lines23.3%+0 4/44branches9.1%+0 | Open PBT |
…/lib/Dataflow/IR/OperationSchemaTypeCodec.cppMS0V | 85/516lines16.5% 55/374branches14.7% | 85/516lines16.5%+0 55/374branches14.7%+0 | Open PBT |
…/lib/Dataflow/Transforms/DataflowRewritePass.cppMS0V | 338/559lines60.5% 172/340branches50.6% | 338/559lines60.5%+0 172/340branches50.6%+0 | Open PBT |
…/lib/Frontend/Analysis/MemoryProvenance.cppMS0V | 153/317lines48.3% 80/326branches24.5% | 153/317lines48.3%+0 80/326branches24.5%+0 | Open PBT |
…/lib/Frontend/IR/LoomDialect.cppMS0V | 6/6lines100.0% branchesnot measured | 6/6lines100.0%+0 branchesnot measured | Open PBT |
…/lib/Frontend/IR/LoomOps.cppMS0V | 120/184lines65.2% 71/112branches63.4% | 120/184lines65.2%+0 71/112branches63.4%+0 | Open PBT |
…/lib/Frontend/Lowering/ExactMemRefLayout.cppMS0V | 61/143lines42.7% 26/80branches32.5% | 61/143lines42.7%+0 26/80branches32.5%+0 | Open PBT |
…/lib/Frontend/Lowering/ExpandGraphMemrefCopyPass.cppMS0V | 79/90lines87.8% 20/22branches90.9% | 79/90lines87.8%+0 20/22branches90.9%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphIndexLowering.cppMS0V | 260/288lines90.3% 129/168branches76.8% | 260/288lines90.3%+0 129/168branches76.8%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphParallelLowering.cppMS0V | 651/1243lines52.4% 284/786branches36.1% | 651/1243lines52.4%+0 284/786branches36.1%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphRegionAdmission.cppMS0V | 58/110lines52.7% 29/92branches31.5% | 58/110lines52.7%+0 29/92branches31.5%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphRegionLowering.cppMS0V | 1416/1626lines87.1% 532/680branches78.2% | 1416/1626lines87.1%+0 532/680branches78.2%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphStreamBoundaryLowering.cppMS0V | 934/1072lines87.1% 299/432branches69.2% | 934/1072lines87.1%+0 299/432branches69.2%+0 | Open PBT |
…/lib/Frontend/Lowering/GraphStreamBoundaryLowering.hMS0V | 4/4lines100.0% 4/4branches100.0% | 4/4lines100.0%+0 4/4branches100.0%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerForToGraphPass.cppMS0V | 1033/1240lines83.3% 374/540branches69.3% | 1033/1240lines83.3%+0 374/540branches69.3%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerForallToThreadPass.cppMS0V | 30/34lines88.2% 2/4branches50.0% | 30/34lines88.2%+0 2/4branches50.0%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerGraphConstantsPass.cppMS0V | 80/83lines96.4% 18/24branches75.0% | 80/83lines96.4%+0 18/24branches75.0%+0 | |
…/lib/Frontend/Lowering/LowerGraphMemoryPass.cppMS0V | 525/841lines62.4% 208/400branches52.0% | 525/841lines62.4%+0 208/400branches52.0%+0 | Open PBT |
…/lib/Frontend/Lowering/Pipeline.cppMS0V | 18/21lines85.7% branchesnot measured | 18/21lines85.7%+0 branchesnot measured | Open PBT |
…/lib/Frontend/Lowering/RankedMemRefLowering.cppMS0V | 60/133lines45.1% 27/94branches28.7% | 60/133lines45.1%+0 27/94branches28.7%+0 | Open PBT |
…/lib/Frontend/Raising/DeduplicateSCFWhileStatePass.cppMS0V | 13/135lines9.6% 0/60branches0.0% | 13/135lines9.6%+0 0/60branches0.0%+0 | Open PBT |
…/lib/Frontend/Raising/LLVMArithToArithPass.cppMS0V | 300/319lines94.0% 94/116branches81.0% | 300/319lines94.0%+0 94/116branches81.0%+0 | Open PBT |
…/lib/Frontend/Raising/LLVMCfToCfPass.cppMS0V | 77/80lines96.2% 8/8branches100.0% | 77/80lines96.2%+0 8/8branches100.0%+0 | Open PBT |
…/lib/Frontend/Raising/LiftCFToSCFPass.cppMS0V | 616/694lines88.8% 293/386branches75.9% | 616/694lines88.8%+0 293/386branches75.9%+0 | Open PBT |
…/lib/Frontend/Raising/MaterializeFMulAddPass.cppMS0V | 70/106lines66.0% 11/26branches42.3% | 70/106lines66.0%+0 11/26branches42.3%+0 | Open PBT |
…/lib/Frontend/Raising/NormalizeLiftedSCFExitPass.cppMS0V | 232/250lines92.8% 120/182branches65.9% | 232/250lines92.8%+0 120/182branches65.9%+0 | Open PBT |
…/lib/Frontend/Raising/Pipeline.cppMS0V | 10/19lines52.6% branchesnot measured | 10/19lines52.6%+0 branchesnot measured | Open PBT |
…/lib/Frontend/Raising/SCFForToForallPass.cppMS0V | 494/738lines66.9% 241/458branches52.6% | 494/738lines66.9%+0 241/458branches52.6%+0 | Open PBT |
…/lib/Frontend/Raising/SCFWhileToForPass.cppMS0V | 164/176lines93.2% 70/94branches74.5% | 164/176lines93.2%+0 70/94branches74.5%+0 | Open PBT |
…/loom/tools/loom-raise-opt/loom-raise-opt.cppMS0V | 12/12lines100.0% branchesnot measured | 12/12lines100.0%+0 branchesnot measured | Open PBT |
| derived from the coverage profile | not measured | not measured | no drafts yet |
…/lib/Dataflow/IR/DataflowChannelOps.cppMS1V | 59/88lines67.0% 13/38branches34.2% | 59/88lines67.0%+0 13/38branches34.2%+0 | Open PBT |
…/lib/Dataflow/IR/OperationSchema.cppMS1V | 274/716lines38.3% 127/350branches36.3% | 274/716lines38.3%+0 127/350branches36.3%+0 | Open PBT |
…/lib/Dataflow/Transforms/DataflowRewritePass.cppMS1V | 338/559lines60.5% 172/340branches50.6% | 338/559lines60.5%+0 172/340branches50.6%+0 | Open PBT |
…/lib/Frontend/Lowering/ExpandGraphMemrefCopyPass.cppMS1V | 79/90lines87.8% 20/22branches90.9% | 79/90lines87.8%+0 20/22branches90.9%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerForToGraphPass.cppMS1V | 1033/1240lines83.3% 374/540branches69.3% | 1033/1240lines83.3%+0 374/540branches69.3%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerGraphConstantsPass.cppMS1V | 80/83lines96.4% 18/24branches75.0% | 80/83lines96.4%+0 18/24branches75.0%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerGraphMemoryPass.cppMS1V | 525/841lines62.4% 208/400branches52.0% | 525/841lines62.4%+0 208/400branches52.0%+0 | Open PBT |
…/lib/Frontend/Lowering/Pipeline.cppMS1V | 18/21lines85.7% branchesnot measured | 18/21lines85.7%+0 branchesnot measured | Open PBT |
…/lib/Frontend/Raising/DeduplicateSCFWhileStatePass.cppMS1V | 13/135lines9.6% 0/60branches0.0% | 13/135lines9.6%+0 0/60branches0.0%+0 | Open PBT |
…/lib/Frontend/Raising/LLVMArithToArithPass.cppMS1V | 300/319lines94.0% 94/116branches81.0% | 300/319lines94.0%+0 94/116branches81.0%+0 | Open PBT |
…/lib/Frontend/Raising/LLVMCfToCfPass.cppMS1V | 77/80lines96.2% 8/8branches100.0% | 77/80lines96.2%+0 8/8branches100.0%+0 | Open PBT |
…/lib/Frontend/Raising/LiftCFToSCFPass.cppMS1V | 616/694lines88.8% 293/386branches75.9% | 616/694lines88.8%+0 293/386branches75.9%+0 | Open PBT |
…/lib/Frontend/Raising/MaterializeFMulAddPass.cppMS1V | 70/106lines66.0% 11/26branches42.3% | 70/106lines66.0%+0 11/26branches42.3%+0 | Open PBT |
…/lib/Frontend/Raising/NormalizeLiftedSCFExitPass.cppMS1V | 232/250lines92.8% 120/182branches65.9% | 232/250lines92.8%+0 120/182branches65.9%+0 | Open PBT |
…/lib/Frontend/Raising/Pipeline.cppMS1V | 10/19lines52.6% branchesnot measured | 10/19lines52.6%+0 branchesnot measured | Open PBT |
…/lib/Frontend/Raising/SCFForToForallPass.cppMS1V | 494/738lines66.9% 241/458branches52.6% | 494/738lines66.9%+0 241/458branches52.6%+0 | Open PBT |
…/lib/Frontend/Raising/SCFWhileToForPass.cppMS1V | 164/176lines93.2% 70/94branches74.5% | 164/176lines93.2%+0 70/94branches74.5%+0 | Open PBT |
…/loom/tools/loom-raise-opt/loom-raise-opt.cppMS1V | 12/12lines100.0% branchesnot measured | 12/12lines100.0%+0 branchesnot measured | Open PBT |
…/lib/Dataflow/IR/DataflowDialect.cppMS1V | 22/32lines68.8% 4/10branches40.0% | 22/32lines68.8%+0 4/10branches40.0%+0 | Open PBT |
…/lib/Dataflow/IR/OperationSchema.cppMS1V | 274/716lines38.3% 127/350branches36.3% | 274/716lines38.3%+0 127/350branches36.3%+0 | Open PBT |
…/lib/Dataflow/Transforms/DataflowRewritePass.cppMS1V | 338/559lines60.5% 172/340branches50.6% | 338/559lines60.5%+0 172/340branches50.6%+0 | Open PBT |
…/lib/Frontend/Lowering/ExpandGraphMemrefCopyPass.cppMS1V | 79/90lines87.8% 20/22branches90.9% | 79/90lines87.8%+0 20/22branches90.9%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerForToGraphPass.cppMS1V | 1033/1240lines83.3% 374/540branches69.3% | 1033/1240lines83.3%+0 374/540branches69.3%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerGraphConstantsPass.cppMS1V | 80/83lines96.4% 18/24branches75.0% | 80/83lines96.4%+0 18/24branches75.0%+0 | Open PBT |
…/lib/Frontend/Lowering/LowerGraphMemoryPass.cppMS1V | 525/841lines62.4% 208/400branches52.0% | 525/841lines62.4%+0 208/400branches52.0%+0 | Open PBT |
…/lib/Frontend/Lowering/Pipeline.cppMS1V | 18/21lines85.7% branchesnot measured | 18/21lines85.7%+0 branchesnot measured | Open PBT |
…/lib/Frontend/Raising/DeduplicateSCFWhileStatePass.cppMS1V | 13/135lines9.6% 0/60branches0.0% | 13/135lines9.6%+0 0/60branches0.0%+0 | Open PBT |
…/lib/Frontend/Raising/LLVMArithToArithPass.cppMS1V | 300/319lines94.0% 94/116branches81.0% | 300/319lines94.0%+0 94/116branches81.0%+0 | Open PBT |
…/lib/Frontend/Raising/LLVMCfToCfPass.cppMS1V | 77/80lines96.2% 8/8branches100.0% | 77/80lines96.2%+0 8/8branches100.0%+0 | Open PBT |
…/lib/Frontend/Raising/LiftCFToSCFPass.cppMS1V | 616/694lines88.8% 293/386branches75.9% | 616/694lines88.8%+0 293/386branches75.9%+0 | Open PBT |
…/lib/Frontend/Raising/MaterializeFMulAddPass.cppMS1V | 70/106lines66.0% 11/26branches42.3% | 70/106lines66.0%+0 11/26branches42.3%+0 | Open PBT |
…/lib/Frontend/Raising/NormalizeLiftedSCFExitPass.cppMS1V | 232/250lines92.8% 120/182branches65.9% | 232/250lines92.8%+0 120/182branches65.9%+0 | Open PBT |
…/lib/Frontend/Raising/Pipeline.cppMS1V | 10/19lines52.6% branchesnot measured | 10/19lines52.6%+0 branchesnot measured | Open PBT |
…/lib/Frontend/Raising/SCFForToForallPass.cppMS1V | 494/738lines66.9% 241/458branches52.6% | 494/738lines66.9%+0 241/458branches52.6%+0 | Open PBT |
…/lib/Frontend/Raising/SCFWhileToForPass.cppMS1V | 164/176lines93.2% 70/94branches74.5% | 164/176lines93.2%+0 70/94branches74.5%+0 | Open PBT |
…/loom/tools/loom-raise-opt/loom-raise-opt.cppMS1V | 12/12lines100.0% branchesnot measured | 12/12lines100.0%+0 branchesnot measured | Open PBT |
This document specifies the third compiler part of the Loom front-end: mechanically lowering selected SCF-stage accelerator regions into the initial Canonical Dataflow Program, Loom's final target-independent software IR.
Translation and recovery through the initial SCF-stage IR are mechanical. SCF-to-SCF transformation is the primary compiler optimization and DSE domain; its selected immutable Structured Program Candidate must already materialize the schedule, parallel, vector, reduction, memory-overlap, AccCore ownership, and SpatialCore ownership decisions consumed here. Part 3 does not choose or repair those decisions.
The target Part 3 dataflow surface uses module-scope, Symbol-bearing,
function-like definitions for both dataflow.thread and
dataflow.graph. Execution is materialized only by
dataflow.thread.launch and dataflow.graph.launch.MS0V699.7%1 · MS0V6 selected-output · selected outputproperty — checked on every compiled pairSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedtrace evidence 1000 samples / 3 classesopen PBT → Graph control
ports are explicit in the current graph ABI: ctrl_in and launch-facing
done_out are invocation protocol endpoints represented at every launch
site, not application payload slots in the dataflow.graph function type.
The graph body does not return done_out; its structural
dataflow.graph.return.complete frontier is the unique authority from which
the launch result is derived. Part 3 consumes each explicit
loom.spatial_region inside its owning dataflow.thread and publishes the
corresponding graph definition and launch only after complete conversion and
native finalization succeed.MS1V5 · MS1V linked-input-162 · linked input 162generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V64 · MS0V6 linked-input-162 · linked input 162generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V33 · MS0V3 linked-input-162 · linked input 162generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V22 · MS0V2 linked-input-162 · linked input 162generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT →
The precise timing semantics of dataflow.stream, dataflow.carry,
dataflow.invariant, and dataflow.gate are specified separately in
docs/spec-dataflow-part-1-streaming.md. The precise firing semantics
of dataflow.constant, dataflow.sync, dataflow.mux, and
dataflow.demux are specified separately in
docs/spec-dataflow-part-2-control.md.
The initial Canonical Dataflow Program may subsequently participate in its own typed, semantics-preserving Dataflow optimization lineage. Such a rewrite produces another immutable Canonical Dataflow Program and may use pure Dataflow or optional Fabric-aware Evaluation to rank candidates. It must not reselect a structured schedule or ownership boundary, and Fabric facts never become Canonical Dataflow semantics.
For a selected-Spatial scalar ScalarMath* actor, semantics include the exact
SpecialMathAccuracyTier selected by the Structured candidate under
docs/spec-compiler-part-2-scf.md. Mechanical lowering copies that owner-coded
tier into the actor's closed semantic projection. Every D0-to-D* rewrite
preserves it exactly; changing, dropping, or newly choosing the tier is not a
semantics-preserving Dataflow transformation.
This document owns the target contract and the closed Canonical Dataflow rewrite catalog: IR boundaries, structured-control flattening, memory-dependence integration, rewrite equivalence and decision semantics, and verifier invariants. Pass decomposition, test layout, and maintenance sequencing are implementation choices and are not a second tracked specification.
Fabric realization and actor grouping are TechMapping concerns. Part 3
performs only structural eligibility and canonical graph publication inside
an already established dataflow.thread; it does not assign a target or
retain target-specific grouping in program IR. Absence of a realization on a
particular Fabric is a Mapping result, not a graph-validity failure.
The compiler front-end is documented in four parts:
loom.spatial_region candidates inside the selected Structured Program
Candidate's dataflow.thread definitions and mechanically publishes
dataflow.graph definitions plus dataflow.graph.launch ops at those
candidate sites.docs/spec-compiler-part-4-partitioned-data.md).Between Parts 2 and 3, SCF optimization and DSE produce the selected Structured Program Candidate. That domain owns all performance-distinct structured choices. Part 3 begins only after those choices and their typed ownership carriers are explicit.MS0V87 · MS0V8 linked-input-203 · linked input 203generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:d927678b3d8d4549df5bd40f with its governing context; report any stage attr5000 generated · 5000 pairedopen PBT →MS0V56 · MS0V5 linked-input-203 · linked input 203generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:f9b33846c2d82708b7bf1867 with its governing context; report any stage attr1000 generated · 964 paired · 36 rejectedopen PBT →
Input to graph extraction is an MLIR module containing module-scope
dataflow.thread definitions. Every selected SpatialCore candidate is already
materialized as a loom.spatial_region inside exactly one thread. Other thread
body code remains InstructionCore-resident, including SCF-shaped code outside
an explicit spatial boundary. Imported Host or InstructionCore code remains in
its llvm.func envelope; genuinely standard-MLIR-native func.func callables
may coexist in the module. Either callable is ownership-neutral and does not
authorize graph creation through its signature, body shape, memory effects, or
return convention.MS0V89 · MS0V8 linked-input-76 · linked input 76generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:d927678b3d8d4549df5bd40f with its governing context; report any stage attr5000 generated · 5000 pairedopen PBT →MS0V58 · MS0V5 linked-input-76 · linked input 76generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:f9b33846c2d82708b7bf1867 with its governing context; report any stage attr1000 generated · 964 paired · 36 rejectedopen PBT →
Output is an initial Canonical Dataflow Program: module-level llvm.func
symbols for imported LLVM callables, any genuinely native func.func helpers,
module-level
dataflow.thread definitions reached by zero or more
dataflow.thread.launch ops; and module-level dataflow.graph
definitions reached by zero or more dataflow.graph.launch ops
inside thread definitions. No scf.* op is left inside any
dataflow.graph definition's body after successful graph-region lowering.MS0V5n/a10 · MS0V5 selected-output · selected outputproperty — checked on every compiled pairSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:f9b33846c2d82708b7bf1867 with its governing context; report any stage attr1000 generated · 964 paired · 36 rejectedtrace evidence 964 samples / 431 classesopen PBT →
The recursive lowering contract accepts arbitrary nesting of
scf.if, source-sequential scf.for, scf.while, and fixed-width
graph-owned scf.parallel or effect-form scf.forall. A graph-owned parallel
op must have a compile-time fixed domain, and all facts needed to establish
ownership, width, and cross-lane legality must be present in the current
Structured Program Candidate's semantic IR and resolved lowering config.
The lowerer re-proves those facts; lineage, cached analyses, and externalMS0V511 · MS0V5 linked-input-69 · linked input 69generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:f9b33846c2d82708b7bf1867 with its governing context; report any stage attr1000 generated · 964 paired · 36 rejectedopen PBT →MS0V812 · MS0V8 linked-input-69 · linked input 69generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:d927678b3d8d4549df5bd40f with its governing context; report any stage attr5000 generated · 5000 pairedopen PBT →
provenance cannot make an otherwise invalid candidate legal. Dynamic-width,MS0V511 · MS0V5 linked-input-69 · linked input 69generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:f9b33846c2d82708b7bf1867 with its governing context; report any stage attr1000 generated · 964 paired · 36 rejectedopen PBT →MS0V513 · MS0V5 linked-input-92 · linked input 92generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:f9b33846c2d82708b7bf1867 with its governing context; report any stage attr1000 generated · 964 paired · 36 rejectedopen PBT →MS0V812 · MS0V8 linked-input-69 · linked input 69generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:d927678b3d8d4549df5bd40f with its governing context; report any stage attr5000 generated · 5000 pairedopen PBT →MS0V814 · MS0V8 linked-input-92 · linked input 92generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:d927678b3d8d4549df5bd40f with its governing context; report any stage attr5000 generated · 5000 pairedopen PBT →
resource-mapped, and result or reduction forms fail before any graph is mutated; the graph owner does not infer ownership, serialization, unrolling, or reduction order. TheMS0V513 · MS0V5 linked-input-92 · linked input 92generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:f9b33846c2d82708b7bf1867 with its governing context; report any stage attr1000 generated · 964 paired · 36 rejectedopen PBT →MS0V814 · MS0V8 linked-input-92 · linked input 92generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:d927678b3d8d4549df5bd40f with its governing context; report any stage attr5000 generated · 5000 pairedopen PBT →
dataflow.thread.launch op carries the completion token and
mapped-memory data transfer; the def remains a callable kernel
body, not a tensor-result returning op. Memory dependence
construction runs in the recursive graph owner using basic graph-local alias
roots and per-partition write/read frontiers (see
docs/spec-compiler-part-3-mem.md).
The Structured Transfer Algebra defines graph-owned parallel composition only after the Structured Program Candidate has materialized its P[] ownership and schedule form in semantic SCF. That fixed-domain SCF is the transient input representation for mechanical lowering. It is recursively replicated into static lanes and removed; no parallel control op or schedule record survives in canonical graph IR.MS0V817 · MS0V8 linked-input-176 · linked input 176generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:d927678b3d8d4549df5bd40f with its governing context; report any stage attr5000 generated · 5000 pairedopen PBT →MS0V878.7%16 · MS0V8 selected-output · selected outputproperty — checked on every compiled pairSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:d927678b3d8d4549df5bd40f with its governing context; report any stage attr5000 generated · 5000 pairedtrace evidence 1000 samples / 213 classesopen PBT →MS0V515 · MS0V5 linked-input-176 · linked input 176generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:f9b33846c2d82708b7bf1867 with its governing context; report any stage attr1000 generated · 964 paired · 36 rejectedopen PBT →
Graph candidate eligibility and atomic publication are governed by this document. TechMapping, SpatialMapping, and SystemMapping realization are outside this IR.
This section is the sole normative owner of the closed Canonical Dataflow rewrite catalog, its decision wire, and the cross-rule equivalence boundary. Rule-specific documents may own a rule's detailed operation semantics, and the DSE specification owns enumeration and work accounting, but neither may add, remove, rename, or reorder a rewrite kind.
Every rewrite preserves, for all legal input, value, stream, and memory response traces under the abstract fair and resource-unbounded Dataflow execution semantics, the same externally ordered values and streams, visible memory behavior, completion and retirement, and termination or nontermination. It may change internal actor identities, topology, firing traces, and cycle count. A rewrite may neither introduce permanent deadlock nor permanently refuse an input accepted by its parent. Concrete Fabric capacity, routing, latency, arbitration, and physical deadlock remain Mapping or Evaluation facts and cannot weaken this software-equivalence contract.
The closed catalog is version 2.0:
| Ordinal | DataflowRewriteKind |
Normalized decision |
|---|---|---|
| 0 | SyncRendezvousRefactor |
exact root ActorRef and DirectToTree or TreeToDirect |
| 1 | PackUnpackRoundTripEliminate |
exact outer-adapter ActorRef |
| 2 | ParallelizeSerializeRoundTripEliminate |
exact outer serialize ActorRef |
| 3 | ElementwiseCardinalityCommute |
exact Compute ActorRef, complete cardinality-adapter ActorRef set, and MoveInside or MoveOutside |
| 4 | PureComputeFanoutRefactor |
Replicate with one exact Compute ActorRef, or Factor with the complete canonical Compute-replica ActorRef set |
| 5 | ActivationPreservingConstantFold |
exact Compute ActorRef |
| 6 | GraphDefinitionRefactor |
Split or Merge parameters below |
| 7 | ElementwiseVectorDecompose |
exact Compute ActorRef and LeadingChunk(C) or Scalarize |
GraphDefinitionRefactor::Split carries one exact GraphRef and a canonical
nonempty proper sorted-unique set of StaticGraphLaunchRef values that call
that graph. The selected set's canonical sequence must be lexicographically
smaller than its nonempty complement, so the two sides of one bipartition
cannot encode two decisions. GraphDefinitionRefactor::Merge carries a
canonically ordered pair of distinct GraphRef values. LeadingChunk(C)
carries a positive u64 per-chunk leading extent; the detailed divisor and
scalarization domain is owned by
Explicit Elementwise Decomposition.
The two decomposition modes are parameters of one rule kind because they use
one operation-schema eligibility owner and one observable-equivalence
contract. They are not two catalog extensions.
The cardinality-adapter set is nonempty, sorted, unique, and contains every
source parallelize or serialize actor in the one connected shell crossed
by the commute; a proper subset is not a match. A fanout Factor set is
sorted, unique, contains at least two Compute actors, and names the complete
coupled replica group selected by that decision. These sets are decision
parameters rather than inferred matcher state because the same Compute actor
or first replica can participate in more than one otherwise legal local
match.
Every decision reference must belong to the exact parent Canonical Dataflow
Artifact on its sole lineage edge. That parent is the semantic context for the
decision payload, so parent-local references encode only their canonical
EntityId and never repeat the ArtifactIdentity. One decision applies exactly
one matched source pattern and publishes at most one child. Applying the same
kind at two matches requires two lineage edges, so intermediate immutable
candidates remain available to DSE. Match enumeration uses complete typed
references and canonical decision bytes, never textual positions, symbol
spelling, pointer order, or pass traversal order. A no-op publishes no child.
Equal finalized semantic content deduplicates by ArtifactIdentity even when
reached by different decision paths.
The decision schema identity is loom.dataflow_rewrite.decision, version
2.0. Its exact descriptor bytes are the ASCII bytes
loom.dataflow_rewrite.decision.2.0 without a trailing zero byte. Canonical
payload primitives are:
kind = u32be(kind_ordinal)
enum = u32be(owner_local_ordinal)
ref = u64be(parent_local_EntityId)
refs = u64be(count), followed by the sorted unique ref values
Concatenation has no implicit tag, padding, terminator, or length other than
the stated refs count. The exact payloads are:
| Kind | Exact left-to-right payload |
|---|---|
SyncRendezvousRefactor |
kind, root_ref, direction |
PackUnpackRoundTripEliminate |
kind, outer_adapter_ref |
ParallelizeSerializeRoundTripEliminate |
kind, outer_serialize_ref |
ElementwiseCardinalityCommute |
kind, compute_ref, adapter_refs, direction |
PureComputeFanoutRefactor::Replicate |
kind, variant, compute_ref |
PureComputeFanoutRefactor::Factor |
kind, variant, replica_refs |
ActivationPreservingConstantFold |
kind, compute_ref |
GraphDefinitionRefactor::Split |
kind, variant, graph_ref, launch_refs |
GraphDefinitionRefactor::Merge |
kind, variant, lower_graph_ref, higher_graph_ref |
ElementwiseVectorDecompose::LeadingChunk(C) |
kind, compute_ref, mode, u64be(C) |
ElementwiseVectorDecompose::Scalarize |
kind, compute_ref, mode |
The owner-local direction or variant ordinals are DirectToTree = 0,
TreeToDirect = 1, MoveInside = 0, MoveOutside = 1, Replicate = 0,
Factor = 1, Split = 0, Merge = 1, LeadingChunk = 0, and
Scalarize = 1. Decoding requires exact length, known ordinals, parent
ownership, canonical sets and pair order, semantic legality, and byte-for-byte
re-encoding.
Canonical decision enumeration compares kind ordinal first. Within a kind it
compares, in order: root then direction for kind 0; outer adapter for kinds 1
and 2; Compute, adapter-ref sequence, then direction for kind 3; variant then
Compute or replica-ref sequence for kind 4; Compute for kind 5; variant then
Graph and launch-ref sequence or Graph pair for kind 6; and Compute for kind
7. For the same kind-7 Compute, proper leading divisors are enumerated by
descending C, followed by Scalarize when legal. Reference sequences use
unsigned EntityId order and ordinary lexicographic sequence order, with a
strict prefix ordered before its extension. This semantic enumeration order
is not inferred from lexicographic payload-byte order; in particular,
u64be(C) would order chunks in the wrong direction.
Version 1.0 admitted only
PackUnpackRoundTripEliminate,
ParallelizeSerializeRoundTripEliminate, and
ActivationPreservingConstantFold as fixed kinds, plus chunk and
scalarization as separate outer decision variants. Its fixed kinds carried no
per-match anchor. It is incompatible and must be rejected rather than
reinterpreted as this catalog.
The eight rules have these exact legality boundaries:
SyncRendezvousRefactor converts an N-way dataflow.sync, for N > 2,
with exactly one externally live carried result to or from the rule's
canonical binary rendezvous tree. The ordered input range is recursively
split into contiguous halves with the left half receiving the extra element.
Every internal node remains an independent canonical actor. A subtree that
contains the original input corresponding to the sole live result carries
that value; every other subtree carries the value of its lowest original
input ordinal. An ancestor containing the live input therefore selects that
descendant's carrier, while every original input remains a prerequisite
even when its local result is dead. The reverse direction accepts only that
exact carrier choice and tree, with no side use of an internal result. Only
a maximal canonical tree root is a normalized reverse decision; a proper
subtree of another recognized tree is not independently enumerated. This is
an optional topology alternative, not mandatory wide-to-binary lowering.
Graph lowering publishes a pure event join with more than four prerequisites
directly in this canonical tree form when only its control carrier is live.
The maximal reverse decision still exposes the equivalent wide rendezvous to
later exploration.PackUnpackRoundTripEliminate removes exact unpack(pack(vector)) or
pack(unpack(bits)) round trips only when source and outer result have the
same complete type, total bit width, lane order, and bit representation and
the intermediate result has no side use. The composed registered
exceptional-state projections must be identity over the source's complete
proven value-state domain. The packed-scalar direction satisfies that
requirement for its defined, poison, and undef states. The vector direction
requires the source domain to contain only the homogeneous states admitted
by the vector owner, commonly by proving every runtime vector token fully
defined; a general mixed-lane vector is not eligible. The rule never inserts
or implies a physical transport adapter.ParallelizeSerializeRoundTripEliminate removes only exact
serialize(parallelize(scalar stream)) compositions whose width, data and
mask shape, phase connection, group order, partial-tail behavior, and
intermediate use counts agree. Every vector, mask, and group-phase result
of the parallelize has exactly its corresponding serialize operand use
and no side use. The reverse composition is not an identity:
serialization may discard inactive lanes before a later parallelizer
compacts values across the original group boundary.ElementwiseCardinalityCommute moves one exact elementwise Compute actor
across already existing parallelize or serialize boundaries without
selecting a vector factor, tail policy, logical domain, or graph boundary.
The initial catalog admits a Compute with at least one operand and exactly
one result. Registered scalar and vector forms must be lane-wise identical;
attributes, phase, masks, and every operand correspondence must agree; and
the operation must have no state, memory effect, cross-lane behavior, or
blocking side use.Let P(x, phase) denote one parallelize and S(v, mask, group_phase) one
serialize. Subscript i follows Compute operand ordinal. The only four
source-to-result shells are:
| Direction and source shell | Deterministic result shell |
|---|---|
MoveInside: compute_scalar(x_i) -> P(result, phase) |
P_i(x_i, phase) -> compute_vector(v_i) |
MoveInside: S_i(v_i, mask, group_phase) -> compute_scalar(x_i) |
compute_vector(v_i) -> S(result, mask, group_phase) |
MoveOutside: P_i(x_i, phase) -> compute_vector(v_i) |
compute_scalar(x_i) -> P(result, phase) |
MoveOutside: compute_vector(v_i) -> S(result, mask, group_phase) |
S_i(v_i, mask, group_phase) -> compute_scalar(x_i) |
The decision's adapter set is exactly the source shell's one result-side
adapter or all operand-side adapters. Operand-side adapters have identical
width and sideband inputs. The adapter at Compute operand ordinal zero is
the canonical sideband representative: only its mask and group-phase
results for P_i, or scalar-phase result for S_i, may escape an
operand-side shell; the corresponding results of every other operand-side
adapter have no side use. A result-side adapter's sideband results form the
source shell boundary. Result construction creates operand-side adapters in
Compute operand order, uses ordinal zero as the result boundary
representative, or creates the one result-side adapter. It replaces every
source boundary data and sideband use with that exact result boundary and
removes the complete source shell. Every source adapter data result has only
its matching Compute operand use, or the Compute result has only the one
result-side adapter data use. No other shell result may have a side use.
An inactive partial lane may be evaluated only when that evaluation is
proven unobservable and safe to speculate; otherwise the representative
mask must be proven all-active.
* PureComputeFanoutRefactor converts between one single-result Compute actor
followed by broadcast and coupled-input broadcasts followed by replicas.
The Compute actor must be deterministic, total, stateless, regionless, and
effect-free. Replication preserves one common correspondence across every
operand and requires at least two result uses. It creates one replica per
member of the result's complete canonical sink set, ordered by the existing
CanonicalGraphConsumerEndpointRef wire. Factoring requires exact equality
of operation schema, attributes, types, ordered input sequences, and
activation correspondence for all replicas in the decision's complete set.
It uses the lowest canonical replica ActorRef as the construction source,
redirects every named replica result use to its one factored result, and
removes the other named replicas. Replication consumes the complete source
fanout and factoring consumes exactly the named replica group; no unlisted
branch or replica is silently rewritten. A generic purity trait alone is
insufficient.
* ActivationPreservingConstantFold replaces one foldable single-result
Compute actor and its otherwise unused constant operands with one exact
typed dataflow.constant only when all constants use the same control
activation and the registered operation folds exactly. It cannot create a
timeless constant, merge distinct activation streams, or erase a constant
that has another consumer.
* GraphDefinitionRefactor::Split clones one graph definition for the named
nonempty proper partition of its static launches and retargets only those
launches. The graph definition is private and every launch is a static,
module-local site inside a private thread definition under the baseline
graph ABI; the complement is derived from the parent's complete static call
set. Merge accepts only graph definitions
with equal signatures, protocols, memory and stream semantics, and complete
alpha-isomorphic actor graphs. Split assigns the normalized selected side to
the clone. Merge uses the lower canonical GraphRef as its source body and
retargets launches of the higher reference. Both preserve every launch
identity, launch-owned source map, and launch-local binding. Dynamic
invocations are never enumerated or individually cloned.
* ElementwiseVectorDecompose has exactly the operation, shape, mask,
poison, activation, and construction contract defined by its linked owner.
It decomposes an already selected semantic vector actor; it does not revisit
Structured vectorization, memory atomicity, reduction order, or Mapping.
The catalog excludes unrestricted graph CSE or DCE, a greedy canonicalizer, generic selector factoring, stateful or stream fusion, memory forwarding or coalescing, associative arithmetic reassociation, and hidden route, buffer, tag, grouping, or representation changes. A future rule requires a new catalog version and its own typed pattern, complete legality proof procedure, normalized parameters, deterministic result construction, and focused semantic anchors. Neither a plugin registry nor a generic rewrite-property DSL is part of this contract.
Mandatory Canonical Dataflow finalization remains non-branching and separate. It canonicalizes and verifies the private result but cannot choose a rewrite, merge graph definitions, select synchronization topology, or prove arbitrary parent-child equivalence. Each rule's legality procedure is the sole authority for that edge. Differential simulation or formal tools may validate an implementation, but finite observations do not replace the rule contract.
Loom's execution target is a heterogeneous system containing HostCore
execution and one or more AccCore execution resources. Fabric owns each typed
AccCore occurrence, its node-local InstructionCore description, its
SpatialCore occurrence, and the typed attachment to an exact fabric.module
template. fabric.module remains the SpatialCore or CGRA template only; it
does not own the physical AccCore occurrence or InstructionCore description.
The typed system ownership contract is specified by
docs/spec-fabric-system-adg.md.
The front-end IR in this document remains a software and logical execution model. SystemMapping binds logical execution cells to physical AccCore instances. TechMapping and SpatialMapping realize canonical graph execution on the selected SpatialCore resources.
The front-end IR separates these execution roles:
| Execution role | Front-end IR carrier |
|---|---|
| HostCore | Host-call-context callable body code outside any dataflow.thread.launch; imported LLVM callables remain llvm.func |
| Logical execution domain | A dataflow.thread definition (Symbol-bearing, module-scope) plus each caller-side dataflow.thread.launch. A dense instance is identified by its coordinate tuple; a dynamic-work instance is identified by its WorkItemId. SystemMapping binds either logical identity to an AccCore. |
| InstructionCore | The body of a dataflow.thread definition, minus its dataflow.graph.launch ops, plus InstructionCore-legal llvm.call or func.call callees after inlining or specialization. The body is "what one logical execution cell runs once binding maps it to a physical AccCore". |
| SpatialCore | Each dataflow.graph definition referenced by a dataflow.graph.launch inside a dataflow.thread definition's body, again per bound logical execution cell. |
A single dataflow.thread.launch starts exactly one domain instance of the
kind declared by the referenced dataflow.thread: either a multidimensional,
zero-based dense domain or a responsibility-tracked dynamic work domain. The
thread body is "what one logical execution point runs"; dense coordinates or
the current work-item identity distinguish executions. The domain is a
software concept and does not commit to a specific fabric topology.
A fabric whose physical PE / memory graph is not a Cartesian mesh
is supported by the same Mapping profiles. The binding from a
logical execution point to a physical AccCore is a SystemMapping concern;
see docs/spec-mapping-artifact.md and docs/spec-pnr.md for SystemMapping
execution binding and PnR.
Every dataflow.thread body may contain InstructionCore code and
dataflow.graph.launch ops, but it cannot launch another thread. A dynamic
worker may use dataflow.work.spawn to publish a child in its current domain;
that operation does not create or target another thread launch.
Dynamic instances become physical AccCore execution slots only through
SystemMapping.
An InstructionCore-only thread body is legal. Failure to form a canonical graph must not synthesize a new accelerator boundary or move unselected host code into a thread.
Thread completion and graph/dataflow control are distinct token domains.
!dataflow.thread_token is the inter-thread asynchronous completion
token produced by dataflow.thread.launch. none values are the
graph-control, graph-completion, streaming-control, and memory-order
tokens used inside dataflow. There is no implicit cast or general
conversion between the two domains. dataflow.thread.wait consumes
one or more !dataflow.thread_token values for caller-side causal
synchronization and emits no SSA value or graph-control value. It is
not a memory barrier.
Thread hierarchy transforms before SystemMapping are legal only as explicit optimization policies. They may reorder independent thread levels, collapse adjacent independent levels, or tile and split a level when the transform preserves the logical instance set, each instance's scalar values, memory-order constraints, and thread-completion causal order. Launch placement remains caller-side only. The deterministic baseline policy performs only annotation and canonicalization; it must not silently change hierarchy shape as a verifier or parsing side effect.
llvm.func remains the sole function and ABI owner for a callable imported
from the final linked LLVM module. func.func is used only for a genuinely
standard-MLIR-native callable or helper; it cannot mirror the LLVM ABI.
Neither callable kind chooses HostCore or AccCore ownership. Call-context
classification decides where calls are legal.loom.spatial_region is temporary compiler IR inside a
dataflow.thread. It owns one structured graph candidate with normalized
value, stream-channel, and memory boundary segments. It never appears in a
finalized Canonical Dataflow Program.MS1V21 · MS1V linked-input-179 · linked input 179generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V620 · MS0V6 linked-input-179 · linked input 179generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V319 · MS0V3 linked-input-179 · linked input 179generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V218 · MS0V2 linked-input-179 · linked input 179generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT →dataflow.thread is the logical accelerator execution-domain
definition (Symbol-bearing, module-scope, function-like). It owns the
kernel body and one domain kind. A dense definition owns coordinate rank
through its canonical entry-block shape; a dynamic definition designates
one ordinary argument as its work-item payload.
It does not itself execute; dynamic logical instances are
materialized by one or more dataflow.thread.launch ops at use
sites, then SystemMapping decides which instances occupy physical AccCore
slots.dataflow.thread.launch is the logical accelerator execution
boundary. It references a dataflow.thread callable by symbol, supplies
async dependencies and ordinary body operands, plus one non-negative extent
per dense coordinate dimension. A dynamic launch instead supplies one root
work item and no extents. Both produce one collective completion token.dataflow.work.spawn publishes one child of the currently executing dynamic
work item after atomically acquiring its termination responsibility. It is
illegal in a dense thread or any graph and is not nested thread launch.dataflow.graph is the SpatialCore leaf DFG definition
(Symbol-bearing, module-scope, function-like). Its body cannot
contain callable definitions, llvm.call, func.call,
dataflow.thread.launch, dataflow.graph.launch, or another
dataflow.graph definition.MS0V3n/a22 · MS0V3 selected-output · selected outputproperty — checked on every compiled pairMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedtrace evidence 824 samples / 57 classesopen PBT →It is final target-independent software IR: its validity does not assert that any current Fabric can realize it. TechMapping owns that decision.
dataflow.graph.launch is the SpatialCore execution boundary
inside a dataflow.thread definition's body. It references a
dataflow.graph callable by symbol, supplies dependency events, value
inputs, stream channel bindings, and memory imports, and yields value
outputs, memory exports, and a trailing done : none result.MS0V299.5%23 · MS0V2 selected-output · selected outputproperty — checked on every compiled pairMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedtrace evidence 1000 samples / 14 classesopen PBT →Function definitions remain module-level symbols in this design.
dataflow.thread definitions are also module-level symbols (and
not symbol tables themselves) and do not physically contain
llvm.func or func.func definitions. An llvm.call or func.call inside a
dataflow.thread definition's body is an InstructionCore call. If the callee
contains code that must become a dataflow.graph definition, Part 3 must
inline or specialize that callee into the active thread definition
before graph extraction. A dataflow.thread.launch is invalid
transitively inside every thread or graph definition. Non-inlined
InstructionCore calls may remain only when their callee body is graph-free
after this preparation.MS1V27 · MS1V linked-input-140 · linked input 140generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V626 · MS0V6 linked-input-140 · linked input 140generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V325 · MS0V3 linked-input-140 · linked input 140generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V224 · MS0V2 linked-input-140 · linked input 140generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT →
The eight rules below are invariants that downstream passes and verifiers must enforce; the rest of this spec is a refinement of how each rule lands in IR.
dataflow.thread is the logical execution-domain
primitive used for selected accelerator work. It is a
Symbol-bearing, module-scope, function-like definition (Part 3
Section 5.4.1); logical instances are materialized by
dataflow.thread.launch and, for one dynamic domain only, its controlled
dataflow.work.spawn operations. Launches appear only in host/runtime
orchestration outside every thread or graph definition. An instance becomes
a physical AccCore execution slot only through SystemMapping.
The thread definition's body has a thread_ctrl : none block
argument that fires once the logical thread instance starts executing
(dense entry-block layout: (args_*, thread_ctrl, coord_*); dynamic work has
no coordinate suffix, see Section 5.4.1).
The body may contain InstructionCore operations and InstructionCore-legal
llvm.call or func.call operations, but not callable definitions or
dataflow.thread.launch ops.dataflow.graph is a leaf-level definition. It is also a Symbol-
bearing, module-scope, function-like definition (Part 3 Section 5.5);
execution is materialized by dataflow.graph.launch ops inside a
thread definition's body. Its body must not contain any
llvm.func, func.func, llvm.call, func.call,
dataflow.thread.launch,
dataflow.graph.launch, or another dataflow.graph definition.
The graph body is a single graph-kind region; it already permits
feedback edges (accepted semantics). A thread body may contain
InstructionCore code and dataflow.graph.launch ops, but it
never launches another thread. The verifier enforces this launch
containment transitively (see Section 9).dataflow.graph definition has an explicit %start : none
entry value and a structural dataflow.graph.return with four
segments: values, streams, memories, and complete.
complete is a mandatory non-empty variadic unordered all-of set of
none values. A no-work graph may return %start as its sole
completion witness; real work, including zero-output work, must expose a
causally derived frontier. The launch-facing result is exactly
done_out = all_of(graph.return.complete). It never appears among the
return operands, and no effect scan or graph-quiescence rule can replace
the explicit frontier.dataflow.thread.launch. Values, memrefs, dynamic source lower bounds,
source steps, and other launch parameters cross directly as typed SSA
operands and matching thread block arguments. Dedicated map_info,
partition-domain, or layout carrier ops are not part of this ABI.docs/spec-compiler-part-3-mem.md.loom.spatial_region is the temporary publication boundary inside an
existing dataflow.thread. Its operands are normalized as value inputs,
stream input channels, memory inputs, and stream output channels; its
results are value outputs followed by memory outputs. Each stream input
has one affine source_map from the consumer thread domain to the producer
thread domain.MS1V31 · MS1V linked-input-150 · linked input 150generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V630 · MS0V6 linked-input-150 · linked input 150generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V329 · MS0V3 linked-input-150 · linked input 150generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V228 · MS0V2 linked-input-150 · linked input 150generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT → The lowering collects all explicit candidates beforeattempting publication, performs conversion and native validation on a scratch module, and replaces the live module only on success. A public pass failure therefore leaves temporary candidates and never exposes a partial
canonical graph. Current publication supports nested scf.if completion
propagation.MS1V35 · MS1V linked-input-90 · linked input 90generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V634 · MS0V6 linked-input-90 · linked input 90generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V333 · MS0V3 linked-input-90 · linked input 90generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V232 · MS0V2 linked-input-90 · linked input 90generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT → Stream channel segments become payload-typed graph stream
ports plus launch-site channel bindings; receive/send sites rendezvous with
the recursively lowered execution frontier and are removed. Input
source_map attributes are preserved exactly, and channel handles never
enter the canonical graph body. One binding denotes one ordered dynamic event sequence. A fixed structured scope may contain multiple sequential or structured mutually exclusive sites. Lowering emits one fixed ordinal schedule, filters inactive branch sites, demuxes each input from the filtered ordinal, and muxes outputs back into that same dynamic order.MS1V39 · MS1V linked-input-36 · linked input 36generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V638 · MS0V6 linked-input-36 · linked input 36generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V337 · MS0V3 linked-input-36 · linked input 36generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V236 · MS0V2 linked-input-36 · linked input 36generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT →
A schedule with more than one static endpoint is materialized as a balanced tree of two-lane selective routers. Each internal node derives its in-range branch selector from the same proved endpoint ordinal and routes both the payload and the remaining local ordinal only into the selected subtree. Output collection uses the corresponding selector phase. Publication never creates one mux or demux whose lane count grows with the number of static endpoint sites; this is a mechanical projection of the fixed schedule, not a Fabric-dependent fan limit or a Dataflow rewrite decision.
Branches may have unequal or empty site sets, and a later branch selector
may depend on an earlier input event. Enclosing loops repeatedly activate
the same schedule, so one or several static body sites may each fire
dynamically. Across repeated thread launches, each endpoint binding and
logical point concatenates these per-instance sequences in deterministic
launch issue order. Channel delivery pairs the resulting producer and
consumer sequences by message ordinal after applying source_map; it doesMS0V240 · MS0V2 linked-input-55 · linked input 55generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT →MS0V341 · MS0V3 linked-input-55 · linked input 55generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V642 · MS0V6 linked-input-55 · linked input 55generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS1V43 · MS1V linked-input-55 · linked input 55generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →
not pair thread activations or create activation-owned segments. EndpointMS0V240 · MS0V2 linked-input-55 · linked input 55generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT →MS0V244 · MS0V2 linked-input-180 · linked input 180generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT →MS0V341 · MS0V3 linked-input-55 · linked input 55generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V345 · MS0V3 linked-input-180 · linked input 180generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V642 · MS0V6 linked-input-55 · linked input 55generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V646 · MS0V6 linked-input-180 · linked input 180generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS1V43 · MS1V linked-input-55 · linked input 55generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS1V47 · MS1V linked-input-180 · linked input 180generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →
sites nested under scf.parallel or scf.forall have no inferred
traversal order and fail before publication. Unselected or non-fixed
graph-owned parallel forms also fail closed.MS0V244 · MS0V2 linked-input-180 · linked input 180generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT →MS0V345 · MS0V3 linked-input-180 · linked input 180generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V646 · MS0V6 linked-input-180 · linked input 180generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS1V47 · MS1V linked-input-180 · linked input 180generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →
dataflow.thread and dataflow.graph definitions are both
IsolatedFromAbove. No operation inside either definition's body
may directly use an SSA value defined in the surrounding scope.
Every boundary value must appear as an explicit launch op
operand and as a matching entry block argument of the
referenced definition. For a dataflow.thread.launch, the
operand list is the HostCore-to-AccCore launch ABI: every value crosses as
an ordinary typed operand. For a dataflow.graph.launch,
operands and results are the explicit SpatialCore data/control
ports. A dataflow.thread.launch completion token expresses
launch-retirement causality only. The
dataflow.graph.launch op resolves its callee through
SymbolUserOpInterface; it does not implement
MemoryEffectsOpInterface or project sibling-callee effects.
Each definition carries RecursiveMemoryEffects so walkers of
that callable can observe its body effects. Launch-site ordering
and retirement are represented by explicit dependencies, the
segmented memory-capability ABI, the done result, and native
finalized-program validation.MemoryEffectOpInterface (or an equivalent recursive trait)
accurately enough that generic optimizers preserve the intended
observable behavior. Causal ordering and completion are defined
by their individual op contracts, not by memory effects. The
baseline policy uses MLIR's default-resource barrier pattern --
broad, conservative MemRead + MemWrite declarations -- where a
precise per-resource binding would require op-side machinery
outside this contract.
Tighter per-resource bindings (for example, load/store keyed on
the $mem operand) are explicit extensions.dataflow.thread.launch. Imported LLVM code remains in its
llvm.func envelope.fabric.system, containing a node-local InstructionCore description and a
SpatialCore occurrence attached to an exact fabric.module template. Part
3 does not create physical AccCore occurrences; it creates logical
accelerator work that SystemMapping later binds to AccCore resources.llvm.func or native
func.func that Part 2 classified as legal to call from code running inside
a dataflow.thread definition's body. Such a function remains a symbol;
Part 3 either preserves calls to it as InstructionCore calls or inlines or
specializes it before graph extraction.index value in the trailing
coordinate segment of a dataflow.thread entry block. A rank-K launch
provides a coordinate tuple in the Cartesian domain
[0, extent_0) x ... x [0, extent_K). Coordinates carry no physical
topology or execution-order promise.DynamicWork domain starts with one root and grows through registered child
work. SystemMapping, rather than a software mapping attribute, decides how
either kind shares or occupies AccCore resources.!dataflow.thread_token, a
one-shot completion signal modelled on !async.token. It belongs to
the inter-thread asynchronous-completion domain, not to the
none-typed graph/control token domain.none-typed entry-block argument of
a dataflow.thread definition's body (named thread_ctrl,
positioned after the function-signature args per Section 5.4.1). It is
the per-instance AccCore start signal used to launch root
dataflow.graph.launch ops.none SSA causal edge emitted by
the recursive graph owner from the current per-partition frontier. No
persistent dependence snapshot is retained.(write_frontier, read_frontier) pair carried recursively for one
alias partition. Touched components are materialized with independent
dataflow.carry and false/true dataflow.demux projections. Specified in
docs/spec-compiler-part-3-mem.md.dataflow.stream for
counted loops: it fires true once per body iteration and one
trailing false token that closes the activation. The combined
(true, ..., true, false) stream phases structural state and may
select the body and exit projections of loop-carried memory state, but is
not itself a memory frontier. The exact timing semantics live
in docs/spec-dataflow-part-1-streaming.md.dataflow.stream, dataflow.gate,
dataflow.invariant, and dataflow.carry. Payloads may be ordinary data,
i1 phase, or none control according to the operation contract. Streaming
tokens carry phase, iteration, or payload information rather than
memory-frontier authority; their precise timing semantics are owned by
docs/spec-dataflow-part-1-streaming.md. The phase bit above is one specific
streaming token.none-typed token used to encode one
component or join of
alias-aware ordering between memory accesses inside a
dataflow.graph definition's body. Each per-partition state pair
(see Canonical Frontier State in
docs/spec-compiler-part-3-mem.md) flows through its own
memory-order tokens; the One Recursive Owner transfer in that document combines a
structural permission token with a
memory-order predecessor token at each load / store. Memory-order
tokens do not encode dynamic execution path (that is the
structural execution role of Section 2.1 there).scf.forall with shared_outs,
op results, or non-empty scf.forall.in_parallel combining actions
such as tensor.parallel_insert_slice.scf.forall with no shared_outs, no
op results, and an empty scf.forall.in_parallel terminator. Its
observable behavior is expressed through explicit memory effects.This section enumerates every new dialect element the front-end
introduces. All additions are local to the dataflow and loom
namespaces; nothing outside this list is added.
!dataflow.thread_token!async.token for the
Loom front-end.none-typed graph-control token, and there is no
implicit cast between the two domains.This spec introduces no other types. Host-to-AccCore values cross the launch boundary as ordinary typed SSA operands; no wrapper or provenance-only type is introduced.
No Loom-specific thread mapping attribute is introduced. Coordinate rank and
domain come from the definition body shape and launch extents. Parallel versus
temporal AccCore use is a SystemMapping relation plus event-relative
ResourceUse, not a property copied into the program ABI.
Every operation admitted as a Canonical Dataflow actor resolves to exactly one
registered OperationSchemaId. The operation's native definition remains the
owner of its source semantics. A typed
CanonicalDataflowActorOpInterface, implemented directly or through an
external model, projects only the Loom contract needed downstream:
CanonicalActorSchemaProjection {
operation_schema_id
actor_kind
closed_semantic_attribute_projection
instance_verifier
transition_descriptor_identity
}
The registry normally selects the complete native operation class. A native
operation that is intentionally a generic semantic carrier may instead own a
finite set of disjoint typed instance selectors. llvm.call_intrinsic uses
this mechanism for irreducible LLVM intrinsics: LLVM's intrinsic ID and
signature registry select the schema, and the reconstructed canonical
overloaded spelling must equal the stored spelling byte for byte. Fast-math,
operand bundles, argument or result attributes, unknown intrinsic IDs,
invalid signatures, and noncanonical overload spellings fail closed unless
the selected schema explicitly owns that state. A generic carrier name alone
is never an actor identity, and no consumer may maintain an intrinsic-name
table.
This notation describes one registered typed projection, not an IR operation, attribute, Artifact, or second semantic language. The projection must classify every property and attribute that can affect one firing. Unknown or unclassified actor state is rejected; consumers never copy an arbitrary attribute dictionary. A field may be excluded only when its owning spec proves it nonsemantic, as for source provenance.
For every scalar operation in a registered ScalarMath* family, the closed
semantic attribute projection contains exactly one owner-coded
SpecialMathAccuracyTier. A selected-Spatial operation missing that field, a
non-special operation carrying it, or a projection that reconstructs it from
afn, Fabric capability, or provider choice is invalid. afn remains the
native source permission for approximate functions; the selected tier is the
narrower canonical actor contract.
For memory actors the closed semantic projection contains the complete
aggregate contract owned by docs/spec-dataflow-memory-consistency.md.
Atomic load/store, atomic RMW, and compare-exchange projections include the
exact source_alignment_bytes; dropping it while retaining ordering, scope,
granularity, or volatility is an invalid partial projection. Geometry remains
the separate nonpersistent CanonicalMemoryAccessView, but Fabric capability
matching consumes both projections and may not reconstruct alignment from a
type, endpoint width, or selected service.
Graph admission, canonical relation construction, Configured Function
materialization, simulator dispatch, and Fabric capability matching all consume
the same OperationSchemaId and projection. The canonical actor classifier is
a derived query over this registry, not another whitelist. Simulator providers
own executable transition implementations, while Hardware Sharing Groups own
only genuine physical sharing relations. Neither may redefine software
semantics or maintain a competing operation-name table.
The OperationSchema registry owns the stable versioned canonical codec and
validator for OperationSchemaId. The Dataflow specifications that own closed
projection atoms, including Canonical Service roles, memory access and mask
forms, atomic ordering and RMW kind, vector atomic granularity, and
SyncScopeRef, likewise own their stable wire tags and payload codecs. C++
enum values, TableGen case numbers, registration order, and printer spelling
are not persistent encodings. A downstream artifact embeds the exact
owner-produced bytes and fails import on an unknown or malformed value; it
cannot define a local ordinal table.
Canonical actor values distinguish defined, poison, and undef state. A defined state carries the exact type-appropriate bits or logical identity; fixed vectors may carry state independently per lane. The owning operation schema defines propagation, masking, non-observation, freezing, and undefined behavior. In particular, selection does not observe its unselected value, inactive masked-memory lanes do not observe address or data, active stores may store poison and loads restore it, and graph outputs may carry poison or undef. There is no global rule that a terminal exceptional value is an execution error.
Canonical Dataflow 3.0 distinguishes three semantic categories:
index is a root-relative element coordinate whose selected representation
width is owned by the Structured candidate; and!llvm.ptr<AS> is a first-class dynamic pointer value whose representation
and arithmetic semantics are owned by LLVM and the exact module DataLayout.No category is an alias for another. In particular, a pointer is not a memory
capability and is not an index value with a different spelling. Value and
stream graph ports may carry scalar LLVM pointer values. Memory graph ports
remain ranked or unranked memrefs. Pointer values may pass through ordinary
token-plane actors and channels only when the selected Fabric transport admits
their exact representation width.
For address space AS, the exact module-owned LLVM DataLayout derives:
PointerLayout(AS) = {
representation_bits : P(AS)
address_bits : A(AS)
kind : StableIntegral | NonIntegral |
ExternalState | Unstable
}
P(AS) is the complete stored pointer representation. A(AS) is the width
used by GEP address arithmetic. Neither is the canonical index width, the
system physical-address width, nor a Fabric endpoint capacity. The same
production DataLayout resolver is used by lowering, OperationSchema, Fabric
admission, simulation, and Mapping verification. A missing, malformed, or
inconsistent DataLayout is invalid. A non-integral, external-state, or unstable
pointer layout without an exact provider is typed Unsupported; it is never
silently projected to an integer.
LLVM pointer operations retain their source-owned operations and semantics.
The initial registered pointer-arithmetic set contains
llvm.getelementptr. OperationSchema owns only the stable typed projection
required by downstream consumers: source element type,
constant-versus-dynamic index pattern and constants, and no-wrap flags. The
function type already owns the base, dynamic-index, and result types.
llvm.ptrtoint, llvm.inttoptr, llvm.ptrtoaddr, pointer comparison, and
address-space conversion remain source-valid LLVM operations but are not
Canonical Dataflow 3.0 actors until each has an exact OperationSchema, Fabric
admission rule, and execution provider. Replacing any of these operations with
a new Dataflow pointer opcode would duplicate LLVM semantics.
GEP computes a DataLayout-derived byte offset from its complete typed index
path. The final address formation adds that offset to the low A(AS) bits and
preserves the high P(AS)-A(AS) representation bits. The exact address space,
provenance, and inbounds/nusw/nuw poison contracts remain observable.
Lowering may factor a GEP into ordinary integer offset arithmetic plus one final
typed GEP only when it proves the same offset, poison, provenance, and
intermediate-bound semantics. Otherwise the original typed operation remains
one canonical actor.
Each Dataflow memory actor has one closed address projection:
MemoryAddressProjection =
RootRelative {
capability : memref
element_index : index | fixed vector<index>
}
| PointerAddressed {
service_capability : memref
pointer : !llvm.ptr<AS> | fixed vector<!llvm.ptr<AS>>
}
RootRelative computes the address from the capability base, element index,
and exact element layout. PointerAddressed performs no extra base addition or
element scaling: the pointer already denotes the byte address, while the
capability selects the exact memory service, ordering domain, and provider.
The service must resolve the pointer to one admitted object or region in the
same address space. Encoding pointer access as base zero, byte element type,
or integer index is forbidden. Loading or storing a pointer as ordinary data
is legal only when the memory capability and physical data path admit all
P(AS) bits and preserve the exact pointer kind.
An object-scoped memory service cannot be acquired from a statically
exceptional pointer. Finalized-program validation resolves each service sourced
from a thread formal through every exact root dataflow.thread.launch binding
and rejects an undef or poison binding. Exceptional pointers remain legal
value-plane data when no memory service is acquired from them. A structured
candidate that hoists an unobserved exceptional branch placeholder into an
unconditional service binding is non-finalizable; ownership selection must keep
the controlling code outside that Spatial slice or choose a smaller
dependency-closed region.
SCF-to-SCF optimization may choose rooted capability-plus-index addressing, first-class pointer execution in a SpatialCore, or InstructionCore ownership. That choice is an Evaluation/DSE result, not a type-system prohibition. Every pointer operation retained in a graph must have an exact OperationSchema, Fabric capability, provider, and simulator transition; otherwise that candidate fails closed.
Finalized Canonical Dataflow Programs use one derived identity attribute:
#dataflow.entity_id<42>
The payload is an unsigned 64-bit value; 42 above is illustrative. The
Dataflow finalizer is the sole producer of this attribute. It appears as
the namespaced dataflow.entity_id attribute on entity-bearing operations and
inside the existing function-like argument dictionary for an imported logical
memory root. It is never an authoring input, Mapping annotation, provenance
handle, execution occurrence, or target binding. The canonical-labeling
contract in Canonical Artifact Finalization And Entity Identity defines its
closed carrier set and validation.
No separate operation interface is introduced for thread completion. The launch, wait, and yield contracts are specified directly below.
Each op below is given by its TableGen-level signature: arguments, results, regions, traits. Implementation bodies are out of scope for this spec.
The thread half of the front-end IR is split into a definition
op (dataflow.thread, Section 5.4.1) and a launcher op
(dataflow.thread.launch, Section 5.4.2). The definition op is a Symbol-
bearing, function-like, module-scope callable; the launcher op
references the definition by symbol and materializes one async
launch instance per use site. Every executable thread in the IR is a
def + at least one launch. This split mirrors gpu.func /
gpu.launch_func.
dataflow.thread (definition)arguments:
TypeAttr:$function_type,
SymbolNameAttr:$sym_name,
StrAttr:$sym_visibility,
Dataflow_ThreadDomainAttr:$domain,
OptionalAttr<DictArrayAttr>:$arg_attrs;
results:
none;
regions:
SizedRegion<1>:$body;
traits:
AutomaticAllocationScope,
IsolatedFromAbove,
Symbol,
HasParent<"ModuleOp">,
SingleBlockImplicitTerminator<"ThreadYieldOp">,
DeclareOpInterfaceMethods<CallableOpInterface>,
DeclareOpInterfaceMethods<FunctionOpInterface>,
RecursiveMemoryEffects.
dataflow.thread is a Symbol-bearing, module-scope, function-
like callable. It does not itself execute; one or more
dataflow.thread.launch ops materialize launches of it.function_type is a FunctionType whose inputs are the kernel's
user-data operand types (T0, ..., TN) and whose results are
empty. The thread definition has no SSA data results; the
per-launch completion token is launch-side, not part of the
callable signature. Asynchronous execution is expressed by launch
dependencies and the mandatory launch completion token, not by the
function type.MS1V363.2%48 · MS1V3 selected-output · selected outputproperty — checked on every compiled pairSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:d1dc2174ff936b5d9497d7eb with its governing context; report any stag5000 generated · 5000 pairedtrace evidence 1000 samples / 21 classesopen PBT →sym_name is required and module-unique. sym_visibility is
required and must equal "private" under the baseline visibility
policy. The verifier rejects "public" and "nested" unless
cross-module linkage is enabled by a separate spec.domain is the closed DenseRectangular or
DynamicWork { work_item_arg_ordinal } value owned by Part 4. A dynamic
definition has coordinate rank zero and its ordinal must select exactly one
function_type input. A dense definition has no work-item ordinal.(args_*, thread_ctrl, coord_*); a
dynamic entry block has (args_*, thread_ctrl):N block arguments mirror function_type.inputs
exactly (each user body operand). Putting the signature args
first preserves the upstream FunctionOpInterface invariant
that the entry block's first N arguments correspond to
function_type.inputs[0..N]. This matches the gpu.func
precedent of "function args first, implicit extras after".MS1V891.1%49 · MS1V8 selected-output · selected outputproperty — checked on every compiled pairSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:20eb96f51c52af18e8b31078 with its governing context; report any stag5000 generated · 5000 pairedtrace evidence 1000 samples / 15 classesopen PBT →thread_ctrl : none is the per-launch AccCore start signal.
It is produced by the launch op once async dependencies are
satisfied and the AccCore instance begins execution. Root
dataflow.graph.launch ops with no InstructionCore predecessor use
this value as their ctrl_in operand.coord_0, ..., coord_{K-1} : index are the
per-instance logical
coordinates, one per launch-domain dimension, in source-dimension order.
Their count is the definition's coordinate rank. Rank is derived from
this canonical suffix after the function_type inputs and unique
thread_ctrl; there is no duplicate rank, grid, or mapping attribute.WorkItemId is execution identity,
not an additional SSA argument or payload wrapper.arg_attrs is indexed only by the args_* payload prefix. Forall
promotion copies each captured source function argument dictionary,
including arbitrary attributes such as llvm.noalias, into capture order.
Locally defined captures have an empty dictionary. thread_ctrl and
coord_* are not payload arguments and never inherit source argument
metadata.IsolatedFromAbove. No SSA value defined outside
the def's body may be used inside it; the launch's body operands
are the only inputs.dataflow.thread.launcharguments:
Variadic<Dataflow_ThreadToken>:$asyncDependencies,
Variadic<Index>:$extents,
Variadic<AnyType>:$bodyOperands,
FlatSymbolRefAttr:$callee;
results:
Dataflow_ThreadToken:$asyncToken;
traits:
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<SymbolUserOpInterface>.
dataflow.thread.launch deliberately does not implement
CallOpInterface. The op's only result is a !dataflow.thread_token,
which is a launch-level async-completion handle, not a callable
return value (the callee's function_type results are empty by
Section 5.4.1). Generic call-graph and inliner consumers that read
CallOpInterface::getResults() would get a misleading "call returns
a thread token" picture; matching the upstream gpu.launch_func
precedent (which also exposes async tokens but does not implement
CallOpInterface), thread launch carries only SymbolUserOpInterface
and resolves its callee through the explicit callee attribute.
callee is a flat symbol reference that must resolve to a
dataflow.thread definition in the same module. The verifier
rejects launches whose callee cannot be resolved or whose
resolved op is not a dataflow.thread.bodyOperands types must equal callee.function_type.inputs
position-by-position. Memrefs, values, source lower bounds, source steps,
and other launch parameters all use this same ordinary operand segment.extents contains exactly one index value per dense callee coordinate.
Each extent must be non-negative. A dense rank-zero launch has no extent
operands and creates one instance. If any dense extent is zero, the launch
creates no instances and its collective token retires after its dependencies.
Static verification rejects a provably negative extent; runtime admission
rejects a negative value before creating any instance. A dynamic-work callee
also has no extents, but creates one root item from its designated ordinary
body operand; the callee's domain kind distinguishes it from dense rank zero.source_iv = lower + coordinate * step. The ABI specifies no
row-major linearization, issue order, physical grid, or topology.asyncDependencies is the variadic prefix of incoming
!dataflow.thread_token dependencies. They form an all-of start
ordering. The op always produces exactly one
!dataflow.thread_token asyncToken result for collective
retirement of all logical instances. For a dynamic-work launch this means
the root source is closed and the active responsibility set owned by Part 4
is empty; queue emptiness alone is insufficient.dataflow.thread.yieldarguments:
Variadic<NoneType>:$completionFrontier;
results:
none;
regions:
none;
traits:
Terminator,
ParentOneOf<["::dataflow::ThreadOp"]>.
completionFrontier is a variadic unordered all-of frontier of
none values. Structural verification checks only that each operand
has type none and that the op terminates a dataflow.thread body.
Finalized-program validation additionally requires the frontier to be a
duplicate-free minimal terminal antichain for every
dataflow.graph.launch in the thread body. Each launch's mandatory
done : none must be yielded directly or lie in the path-aware causal
closure of a yielded terminal event. Every executable SCF predecessor on
which the launch exists must forward or cover its completion; a fallback is
valid only on a mutually exclusive path where that branch-local launch does
not exist. A dataflow.mux similarly covers a completion only on every
selector lane where the completion may exist. Matching dataflow.demux
activation can prove a launch absent from the other lanes.Non-region causal edges come only from explicit completion semantics:
dataflow.graph.launch done follows its dependencies, and dataflow.sync
waits for all inputs. Other operations do not acquire completion semantics
merely by accepting a none operand. Two distinct yielded events are
invalid when either causally covers the other. Each remaining frontier
member must also be necessary for at least one graph-launch completion that
no other member covers. Independent launch events must all be covered, while
a chain yields only its terminal event. A thread with no graph launch has an
empty frontier. These checks derive from SSA and structured-control
causality, never from textual operation order, and do not infer additional
completion obligations from effects, DMA, or other operations.
Any supported tensor-result aggregation has already been materialized by
Part 2 as accepted explicit effects; an unmaterialized form is
non-finalizable. The frontier therefore carries no thread data result.
In a DynamicWork thread, completion of the frontier retires the current
work item exactly once. It does not directly retire the launch token; the
token retires only after the domain responsibility set becomes empty.
dataflow.work.spawnarguments:
AnyType:$childItem;
results:
none;
regions:
none;
traits:
DeclareOpInterfaceMethods<MemoryEffectOpInterface>.
dataflow.thread with a
DynamicWork domain and outside every dataflow.graph. Its operand type must
equal the definition's designated work-item argument type.dataflow.thread.launch.
Exact identity and termination are owned by Part 4.dataflow.thread.waitarguments:
Variadic<Dataflow_ThreadToken>:$asyncDependencies;
results:
none;
traits:
AtLeastNOperands<1>.
none value. It
is not a memory barrier and does not define memory visibility.Pure; it remains a causal wait in the stored program.No dedicated boundary or partition-carrier operation is part of the canonical thread ABI. A launch operand and its matching ordinary thread block argument are the same typed software value. Alias, no-alias, bounds, and access-summary facts used by optimization remain ordinary MLIR analysis facts or semantic candidate operations; they are not encoded by a provenance-only passthrough op.
Code inside the thread may derive source induction variables, subviews, local
ranges, or an explicit linear id from ordinary launch operands and trailing
logical coordinates. Those computations are program semantics and therefore
survive whenever their results remain observable. Part 4 defines this boundary
without introducing map_info, partition-domain, layout, or coordinate-query
ops.
The graph half of the front-end IR is split into a definition
op (dataflow.graph, Section 5.5.1) and a launcher op
(dataflow.graph.launch). The definition
op is a Symbol-bearing, function-like, module-scope callable; the launcher
op references the definition by symbol from inside a
dataflow.thread definition's body, supplies a per-launch
ctrl_in : none operand and user data operands, and produces a
per-launch done_out : none result and user data results. Every
executable graph in the IR is a def + at least one launch.
dataflow.graph (definition)arguments:
TypeAttr:$function_type,
SymbolNameAttr:$sym_name,
StrAttr:$sym_visibility,
OptionalAttr<DictArrayAttr>:$arg_attrs,
OptionalAttr<DictArrayAttr>:$res_attrs;
results:
none;
regions:
SizedRegion<1>:$body;
traits:
IsolatedFromAbove,
Symbol,
HasParent<"ModuleOp">,
DeclareOpInterfaceMethods<CallableOpInterface>,
DeclareOpInterfaceMethods<FunctionOpInterface>,
RecursiveMemoryEffects.
dataflow.graph is a Symbol-bearing, module-scope, function-like
callable. It does not itself execute; one or more
dataflow.graph.launch ops materialize launches of it.function_type ABI is (T0, ..., TN) -> (R0, ..., RM) and
contains only application payloads. input_segments and result_segments
classify those payloads as values, streams, and memories. The graph
%start and launch-facing done_out are explicit invocation protocol
endpoints outside the function type. graph.return payload segments match
all result types, and graph.return.complete derives done_out.CanonicalMemoryAccessView.sym_name is required and module-unique. sym_visibility is
required and must equal "private" under the baseline visibility
policy. The verifier rejects "public" and "nested" unless
cross-module linkage is enabled by a separate spec.IsolatedFromAbove. All values used inside the
graph definition's body must enter through the entry block.(%ctrl_in : none, %arg_0 : T0,
..., %arg_N : TN). The application arguments match
function_type.inputs; the distinguished leading ctrl_in block argument
is the per-launch start signal and is not part of the function type.
Accordingly, arg_attrs is indexed only by application arguments and has no
entry for ctrl_in; res_attrs is indexed by application results. The
custom assembly form preserves both arrays through textual and bytecode
serialization.text
dataflow.graph.return
values(%final_values...)
streams(%output_streams...)
memories(%output_memories...)
complete(%retirement_frontier...)
The payload segments, in that order, match all function results.
complete contains one or more none witnesses and is the only
completion truth. The compact %complete, %values... : none, types...
form is permitted when there is one witness and the stream and memory
segments are empty.
* dataflow.graph lit tests use module-scope graph definitions with
deterministic symbol names and dataflow.graph.launch use sites. Tests
anchor the explicit start argument, segmented return payloads, non-empty
completion frontier, and launch-facing done result.
* C++ builders construct dataflow.graph as a function-like
definition from (StringRef sym_name, FunctionType functionType,
ArrayRef<NamedAttribute> attrs), with optional arg_attrs / res_attrs
arrays carried in the function-interface attributes. The body is added via
the standard FunctionOpInterface body-construction path, with the entry
block carrying the leading none ctrl_in block argument and the
user-data block arguments.
* The op declares RecursiveMemoryEffects so module-scope walkers
can observe per-callable effects. This does not provide an alternate
launch-completion rule; retirement remains owned exclusively by
graph.return.complete.
dataflow.graph.launcharguments:
FlatSymbolRefAttr:$callee,
Variadic<NoneType>:$dependencies,
Variadic<AnyType>:$valueInputs,
Variadic<ChannelType>:$streamInputs,
Variadic<AnyType>:$memoryInputs,
Variadic<ChannelType>:$streamOutputs;
results:
Variadic<AnyType>:$valueResults,
Variadic<AnyType>:$memoryResults,
none:$done;
traits:
DeclareOpInterfaceMethods<SymbolUserOpInterface>.
callee is a flat symbol reference that must resolve to a
dataflow.graph definition in the same module. The verifier
rejects launches whose callee cannot be resolved or whose
resolved op is not a dataflow.graph.[value, stream, memory] FunctionType segments. Stream payloads
bind to consumer or producer !dataflow.channel<T> endpoints; they are not
launch SSA data results. The mandatory trailing done : none result is the
per-launch retirement event.Value and stream bindings require their exact existing type relations. Memory input binding uses one closed relation, owned solely by this graph ABI:
GraphLaunchMemoryBinding =
Identity {
actual: ranked or unranked memref<T>
formal: the exact same memref<T>
}
An admitted memref view already resolves through its Dataflow-owned
LogicalMemoryViewRef; graph launch neither derives another view nor accepts a
pointer in the memory segment. A pointer needed by the graph is passed through
an exact value or stream binding. Every memory type mismatch is invalid.
Memory results exactly match the callee's memref result types. Pointer results
are legal only in the value or stream result segment.
source_map. Its
dimensions are the consumer thread coordinates and its results select the
producer thread coordinates. Direction is derived from the launch operand
segment: stream inputs are consumer bindings and stream outputs are producer
bindings. There is no independent channel direction or mode attribute.
Graph-launch verification owns local count and consumer-domain checks. The
finalized-program validator owns the cross-launch relation: one producer,
at least one consumer, producer/result-rank agreement, bounds over the full
consumer domain, and complete permitted channel use topology.
Both endpoint domains must be DenseRectangular; a DynamicWork domain is not
interpreted as dense rank zero.done_out is the all-of of the callee's
graph.return.complete operands. Their causal closure covers final values,
stream close and boundary commit, memory capability establishment and
promised visibility, all observable effects, invocation-local state
close/reset, and non-detached async work. A graph with real work cannot use
raw %start as a fake completion witness.dataflow.thread definition's body,
not at host scope and not inside another dataflow.graph
definition's body. The verifier enforces this placement.MemoryEffectsOpInterface and
does not project effects from its sibling callee. The native
finalized-program validator proves that the callee's explicit complete
frontier covers all outputs, state closure, and observable effects; explicit
dependencies and memory capability ports carry launch-site ordering.dataflow.graph.waitarguments:
Variadic<NoneType>:$completionFrontier;
results:
none;
traits:
AtLeastNOperands<1>.
dataflow.thread
definition. It is invalid at host scope, inside dataflow.graph, or inside a
nested thread definition.dataflow.graph.launch done result or an event
whose path-aware causal closure contains at least one such result. The
finalized-program validator proves that every operand is a valid terminal
graph-completion frontier; textual order and generic none use do not
establish that fact.!dataflow.thread_token.Pure. A lowering inserts it only before the first
stored-program continuation that actually requires retirement. Deferred SSA
value readiness, launch dependencies, channel transport, and
dataflow.thread.yield remain their existing finer- or coarser-grained
mechanisms and must not acquire redundant waits.docs/spec-dataflow-part-1-streaming.md: the exact channel instance and
endpoint bindings, the consumer-to-producer source_map, applicable path
predicates, producer publication and consumer observation positions, and
equality of the symbolic producer and consumer event positions. Coordinate
equality, static-site equality, or identity source_map alone never proves
that two repeated launch occurrences use the same message. Unknown
cardinality or ordering fails closed and retains the required retirement
dependency.loom.spatial_region is a transparent structured boundary. A blocking
receive inside that region cannot be justified by a send that follows the
region in the same stored-program strand merely because the published graph
launch becomes asynchronous. Such a transformation would turn an
inline-semantics deadlock into progress. A resulting retirement/send cycle
therefore identifies a deadlocking or incorrectly cut candidate; lowering
must not remove a wait by inventing a same-activation channel witness.MS1V53 · MS1V linked-input-204 · linked input 204generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V652 · MS0V6 linked-input-204 · linked input 204generator constraint — feeds the generated inputsSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:037b2351ec38125b2cd5ae56 with its governing context; report any stage at5000 generated · 5000 pairedopen PBT →MS0V351 · MS0V3 linked-input-204 · linked input 204generator constraint — feeds the generated inputsMLIR passage 4: Graph bodies exclude calls and nested callables1000 generated · 824 paired · 176 rejectedopen PBT →MS0V250 · MS0V2 linked-input-204 · linked input 204generator constraint — feeds the generated inputsMLIR passage 2: Graph launch execution boundary5000 generated · 5000 pairedopen PBT →dataflow.load and dataflow.store.dataflow.load declares MemoryEffects<[MemRead]>.dataflow.store declares MemoryEffects<[MemWrite]>.RecursiveMemoryEffects trait. It does not create launch-site
effect projection.Tightening these effects to a per-$mem-operand declaration
(so two loads on disjoint memrefs become reorderable) is
an explicit dataflow dialect extension.
No other dataflow op is modified by this spec.
Graph-region lowering carries execution permission, captured values, and per-partition
(write_frontier, read_frontier) state in one recursive traversal. The
compiler-local transfer is:
lower_region(E_in, values_in, {W_in[p], R_in[p]})
-> (E_out, values_out, {W_out[p], R_out[p]})
This transfer is not an IR object. The final graph contains only ordinary SSA
values and the existing Dataflow primitives. Memref bindings remain static;
only values, addresses, data, selectors, and event streams are projected.
Leaf memory completion updates W/R but never silently replaces execution
permission.
This section records Dataflow templates for SCF boundaries. Recursive lowering
applies the same transfer to scf.if, normalized scf.index_switch,
source-sequential scf.for, scf.while, and fixed-domain
effect-form scf.parallel / scf.forall. A zero-case scf.index_switch is
replaced by its default region during structured normalization. Other
unsupported source forms must be normalized by Part 2 before handoff of the
selected Structured Program Candidate and are rejected if they remain in a
graph.MS0V855 · MS0V8 linked-input-83 · linked input 83generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:d927678b3d8d4549df5bd40f with its governing context; report any stage attr5000 generated · 5000 pairedopen PBT →MS0V554 · MS0V5 linked-input-83 · linked input 83generator constraint — feeds the generated inputsSelected stage: loom-lower-scf-to-dfg. Test exactly output obligation mlir-obligation:f9b33846c2d82708b7bf1867 with its governing context; report any stage attr1000 generated · 964 paired · 36 rejectedopen PBT →
The dataflow primitive set is
(stream, carry, invariant, gate, mux, demux, sync,
constant, load, store, yield). This section describes how SCF
ops are mechanically rewritten with those primitives. The precise
state machines and token lengths of stream, carry, invariant,
and gate are the single source of truth in
docs/spec-dataflow-part-1-streaming.md. The precise firing semantics
of constant, sync, mux, and demux are the single source of
truth in docs/spec-dataflow-part-2-control.md.
The control op set is mux, demux, sync, constant. Crucially:
the phase bit fed into carry / invariant / gate does not have to
come from stream; any i1 SSA stream from arbitrary computation
inside the graph plays the same role. This is what lets
scf.while lower without a new op.
Selection lanes follow the control-op contract. For i1 selectors,
lane 0 is the false lane and lane 1 is the true lane:
%false_value, %true_value = demux %cond, %value : (i1, T) -> (T, T)
%value = mux %cond, %false_value, %true_value : (i1, T, T) -> T
For index selectors, lane k is operand/result position k.
This convention is required for the templates below to be mechanical.
dataflow.mux is selective: it consumes only the selector and selected
input lane. dataflow.demux is selective: it emits only the selected
output lane. dataflow.sync is the all-input rendezvous op.
Control-only and mixed boundary-publication syncs are canonical software
actors. A mixed boundary-publication sync has canonical shape
(none, T) -> (none, T). TechMapping may realize a control-only sync with a
wider all-control Fabric capability and must prove compatible arity and
positional semantic widths for every selected realization. Lack of such a
capability on one Fabric does not invalidate the canonical graph.
Registered pure compute actors inside dataflow.graph, including the
registered arithmetic, math, and LLVM computation operations, follow strict
all-operand firing: each dynamic firing consumes one token from every
operand and emits one token on every result. In particular,
arith.select is an eager three-input compute op in this model, not a
short-circuiting dataflow mux.
SSA multi-use is token broadcast. If one SSA stream value has multiple
uses, each use observes the same ordered token sequence. This is not a
destructive single-consumer read. The scf.for template relies on
this property because the loop phase stream independently drives
carry, gate, and demux; those consumers do not need to fire in
lockstep.
Frontend memref<...> values are not stream values in this sense.
They represent memory-region bindings for dataflow.load /
dataflow.store. Lowering must not feed memref bindings through
stream-shaping ops; it shapes address, data, operation, and explicit
none memory-order streams instead. The generic result-selection
templates below apply to scalar/data streams and none ordering
streams. A memref-typed structured-control result inside graph
extraction must be rewritten to explicit memory effects, kept in
InstructionCore code, or rejected before graph lowering.
Graph memory normalization must reject every residual LLVM load, store,
memcpy, memset, atomic, volatile, or fence operation. They do not implement the
canonical Dataflow memory capability and explicit completion-event contract;
source order, a value result, or an effect scan cannot substitute for it. A
supported LLVM memory operation must first normalize to dataflow.load,
dataflow.store, or another explicitly specified canonical memory actor.
The templates below show user-visible SSA value lowering. The same recursive
owner threads independent none-typed write and read frontiers through each
boundary as specified in docs/spec-compiler-part-3-mem.md; this is not an
optional optimization or a later reconstruction pass.
The pseudocode templates in Section 6.1-Section 6.8 below show the graph body
contents for clarity. Every template's actual lowering output is a
dataflow.graph definition + a dataflow.graph.launch pair, with
the body shown lifted to module scope and the launch carrying the
per-instance ctrl/done plumbing:
// At module scope (sibling of callable definitions):
dataflow.graph @<construction_local_sym>
(%start : none, <user inputs>) -> (<user results>) {
// <body contents per the template>
dataflow.graph.return values(<user yield values>) streams() memories()
complete(<retirement frontier>)
}
// At the explicit spatial-region site inside the enclosing
// dataflow.thread definition's body:
<user value results>, <memory results>, %done =
dataflow.graph.launch @<construction_local_sym>
deps(%dependency events) values(<value operands>)
stream_inputs(<consumer channels>) memories(<memory imports>)
stream_outputs(<producer channels>)
: (<operand types>) -> (<value result types>, <memory result types>, none)
The publisher creates a deterministic, collision-free construction-local
symbol for each outlined graph. An existing loom.spatial_region.graph_name
may be used only as a readability or debug stem. The temporary region does not
own graph identity, and symbol spelling does not encode cut selection, source
order, graph identity, or artifact identity.
The templates therefore omit the def + launch wrap to keep the
body's structural diff readable. The wrap is mandatory output, not an optimization, and is verified by the front-end's standard verifier rules in Section 9.MS1V88.1%56 · MS1V selected-output · selected outputproperty — checked on every compiled pairSelected stage: loom-lower-for-to-graph. Test exactly output obligation mlir-obligation:cb03cffe9a6fce2f72e3c64f with its governing context; report any stage at5000 generated · 5000 pairedtrace evidence 1000 samples / 6 classesopen PBT →
A phase stream is loop control, not a plain valid bit. For a counted loop
with N body executions, dataflow.stream emits N IV tokens and a phase
stream T^N F. The final false token closes the activation and resets each
stateful consumer, but it has no paired IV or body execution.
The stream IV already has body cardinality and enters body arithmetic and
memory directly. Parent-domain captured values from invariant have
N + 1 tokens and are projected through dataflow.gate before body use.
Recurrence values that also need a false-lane exit use selector-matched
dataflow.demux; loop results and memory-frontier exits consume that false
lane. A true body-local condition means the current body execution is not the
last execution; a false body-local condition means it is the last execution.
Different regions of one source loop may therefore have different phase streams. The loop-level phase decides whether the source loop continues or exits; a gated body phase controls state local to the body region whose value stream has already been normalized.
scf.ifSource shape:
%r... = scf.if %cond -> (T_r, ...) {
... then computation using live-in streams ...
scf.yield %then_r... : T_r, ...
} else {
... else computation using live-in streams ...
scf.yield %else_r... : T_r, ...
}
scf.if regions have no block arguments, but graph lowering must not
let branch-local computation directly consume parent-phase data streams.
For every non-memref stream live-in used by either branch, the lowering
projects the stream into branch phase with the same selector that
routes control.
The %ctrl stream is supplied by the current lowering context: graph
ctrl_in for a top-level if, loop body control for an if inside a
loop body, or a selected parent-branch control stream for a nested if.
# Lane convention: lane 0 = false, lane 1 = true
# demux %cond, %v : (i1, T) -> (T, T) yields (%v_else, %v_then)
# mux %cond, %v_else, %v_then : (i1, T, T) -> T (operand order:
# false-lane first, true-lane second)
%cond : i1
%t_else, %t_then = demux %cond, %ctrl : i1 -> (none, none)
# For every non-memref stream live-in %x : T used in either branch:
%x_else, %x_then = demux %cond, %x : (i1, T) -> (T, T)
# then-region runs with %t_then and %x_then...; produces %v_then...
# else-region runs with %t_else and %x_else...; produces %v_else...
%result = mux %cond, %v_else, %v_then : (i1, T, T) -> T
%done_after = mux %cond, %done_else, %done_then : (i1, none, none) -> none
memref<...> bindings are not demuxed. The branch-specific
address, data, operation, and explicit none order streams are
demuxed instead.dataflow.drop op or
runtime queue.mux, not sync.
sync is only used inside one dynamically executed path, where all
inputs are expected to fire. The un-selected branch produces no
done token because demux only fires the selected output, while the
exit mux waits only for the selected branch's done token.scf.if has no else body, the false-path done is the
false-path local ctrl token. If a branch has no memory side effect
or other control-only work, that branch's done is its local ctrl
token.scf.if has results. An
scf.if without an else region therefore has no results; only the
control token needs to be joined.scf.if lowers one result mux per result position,
all driven by the same %cond stream.For a three-token parent-phase invocation with %cond = [T, F, T]
and a scalar live-in %x = [10, 20, 30]:
| Stream | Tokens |
|---|---|
%x_then |
[10, 30] |
%x_else |
[20] |
%v_then |
[then(10), then(30)] |
%v_else |
[else(20)] |
%result |
[then(10), else(20), then(30)] |
%done_after |
[done_then0, done_else1, done_then2] |
Branch live-in demuxing is required for phase correctness. Without it, tokens for an unselected branch can remain buffered inside branch-local ops and be consumed by a later selected invocation at the wrong dynamic position.
The capture owner may encounter an exceptional scalar placeholder introduced
by CFG structuring at a graph boundary. It may project that lane to a defined
zero wire value only when the corresponding graph entry argument has no SSA
uses in the complete graph body, the lane is non-pointer and scalar, and the
capture record retains an unusedByGraph provenance fact. This preserves the
graph ABI while proving that the substituted bits cannot affect graph
semantics. A value with any graph use, a pointer lane, or an unproven
correspondence remains Undef/Poison and is rejected by the ordinary wire
legality owner; no blanket exceptional-value replacement is permitted.
This translation uses the recursive graph owner. The condition
demuxes execution, captured non-memref values, and both frontier components
for every partition touched by either branch. Each branch is lowered
recursively. The same condition then muxes execution, each result position,
W_P, and R_P componentwise.
A missing else is an identity false lane. An unexecuted path forwards its
incoming frontier and never performs a safe-address access or emits a fake
completion. Same-path prerequisites use sync; mutually exclusive exits use
mux. Execution remains distinct from both memory components.
scf.while with scf.conditionSource shape:
%res... = scf.while (%a0_i = %init_i, ...) : (A_i, ...) -> (B_j, ...) {
^before(%a_i : A_i, ...):
%cond, %b_j... = ... before computation ...
scf.condition(%cond) %b_j... : B_j, ...
} do {
^after(%b_after_j : B_j, ...):
%a_next_i... = ... after computation ...
scf.yield %a_next_i... : A_i, ...
}
The before-argument types A_i and the after/result types B_j are
independent. If the after region executes K times, the before region
executes K + 1 times. The scf.condition operands are therefore in
before phase: true-cycle operands enter the after region; the single
false-cycle operand tuple becomes the while result tuple.
Emitted lowering skeleton:
# Structural loop entry and loop-back control. This exists even when
# the source while has no data inits.
%iter_ctrl = carry %cond, %entry_ctrl, %after_done : none
# Each before block argument is loop-carried in before phase.
%a_i = carry %cond, %init_i, %a_next_i : A_i
# The before region consumes %iter_ctrl and %a_i..., then produces:
# %cond : i1
# %b_j : B_j, one stream per scf.condition trailing operand
# %before_done : none, the tail of before-region side effects
# scf.condition true operands enter after; false operands are results.
# Lane convention: lane 0 = false, lane 1 = true.
%b_exit_j, %b_after_j =
demux %cond, %b_j : (i1, B_j) -> (B_j, B_j)
# The recursively lowered before exit is projected with the same selector.
%while_done, %unused_true =
demux %cond, %before_done : (i1, none) -> (none, none)
%after_phase, %after_ctrl =
gate %cond, %before_done : (i1, none) -> (i1, none)
# The after region consumes %after_ctrl and %b_after_j..., then
# produces:
# %a_next_i... : A_i, the scf.yield operands
# %after_done : none, the after-region completion token; if the
# region has no side effects and no extra control-only
# work, this may be %after_ctrl
%res_j = %b_exit_j
%cond is the i1 token computed by the before-region's
scf.condition. There is no stream op here; an arbitrary i1
stream produced by before-region computation drives the loop.K after permissions and one while exit. The
final false before execution is therefore part of loop completion.%b_exit_j becomes the loop result. The same selector projects values,
execution, and memory-frontier components into matching phases.%a_next_i has length K, one value from each after-region
execution. dataflow.carry consumes a next value only with
cond=true; cond=false closes and resets the carry without
consuming feedback.%cond stream.
After-region-only invariants are replayed in before phase and projected
through a true-lane demux. This keeps zero-trip loops from producing an
after-only value.%iter_ctrl. Before starts from
their outputs. True lanes enter after and feed the next before activation;
false lanes are the loop exits. This preserves memory effects performed by
the final condition-checking iteration.For K = 2, the dynamic sequence is:
before0: cond0 = true, b0 -> after0
after0: yield a1
before1: cond1 = true, b1 -> after1
after1: yield a2
before2: cond2 = false, b2 -> while result
The corresponding token lengths are:
| Stream | Tokens |
|---|---|
%cond |
[T, T, F] |
%a_i |
[a0, a1, a2] |
%b_j |
[b0, b1, b2] |
%b_after_j |
[b0, b1] |
%b_exit_j |
[b2] |
%after_ctrl |
[before_done0, before_done1] |
%while_done |
[before_done2] |
%a_next_i |
[a1, a2] |
The final %cond = false token is consumed without %a_next_i or
%after_done. It emits no new before value and returns each carry to
its init state. Independent write-frontier and read-frontier carries follow
the same selector contract in docs/spec-compiler-part-3-mem.md.
This translation uses condition-driven carry rings for
execution, source inits, and each touched W_P/R_P component. Carry outputs
enter before directly. After before is lowered, the false lanes are the while
execution, result, and frontier exits. dataflow.gate projects execution and
captured values into after phase; true condition-argument and frontier lanes
enter after through their selector-matched projections. After exits feed the
next before activation.
Before therefore executes K + 1 times when after executes K. The final
false before effects are included in the outgoing pair. A final-false read
updates R_P at loop exit and a following write must wait for it. False does
not consume dummy feedback.
scf.for with scf.yieldThere are two distinct cases.
Source:
scf.for %i = %c0 to %n step %c1 {
%x = memref.load %A[%i] : memref<?xi32>
memref.store %x, %B[%i] : memref<?xi32>
}
Lowering:
# Source scf.for IVs are typed `index`. dataflow.stream requires its
# %init / %limit / %step / iv stream to share a scalar signless integer
# type (see docs/spec-dataflow-part-1-streaming.md). The lowering
# therefore inserts arith.index_cast at the boundary: %lb / %ub /
# %step are cast from index to a chosen iN, and the body IV %i is
# cast back to index before memref indexing. The chosen iN is Loom's
# configured index-width integer type.
%lb_iN, %ub_iN, %step_iN = arith.index_cast %lb, %ub, %step : index to iN
%i_iN, %loop_phase = stream %lb_iN, %ub_iN, %step_iN
step add while slt : iN
%i = arith.index_cast %i_iN : iN to index
# body memory and address computation consume %i directly
# Source-sequential execution recurrence and zero-trip exit:
%ctrl_raw = carry %loop_phase, %ctrl_in, %body_done : none
%loop_exit_ctrl, %body_ctrl =
demux %loop_phase, %ctrl_raw : (i1, none) -> (none, none)
For N dynamic body executions:
| Stream | Length | Meaning |
|---|---|---|
%loop_phase |
N + 1 |
N true tokens plus one false close |
%i_iN / %i |
N |
body induction values |
%ctrl_raw |
N + 1 |
initial permission plus body feedback |
%body_ctrl |
N |
source-sequential body permissions |
%loop_exit_ctrl |
1 |
structured exit token |
The no-result case has no data loop result to compute. The stream emits
exactly one IV per body execution and no IV for the close transition. The
recursively lowered body returns %body_done, which authorizes the next
source iteration. Memory leaves additionally wait on their partition
frontiers as specified in docs/spec-compiler-part-3-mem.md.
Loop-invariant memref operands are not replayed with
dataflow.invariant; they remain memory bindings on the lowered
loads and stores.
Source:
%sum = scf.for %i = %c0 to %n step %c1
iter_args(%acc = %init) -> i32 {
%x = memref.load %A[%i] : memref<?xi32>
%next = arith.addi %acc, %x : i32
scf.yield %next : i32
}
Lowering:
# Same IV index<->iN cast pattern as the No Iter Args case, see
# the lowering above.
%lb_iN, %ub_iN, %step_iN = arith.index_cast %lb, %ub, %step : index to iN
%i_iN, %loop_phase = stream %lb_iN, %ub_iN, %step_iN
step add while slt : iN
%i = arith.index_cast %i_iN : iN to index
%acc_raw = carry %loop_phase, %init, %next : i32
%acc_exit, %acc_body =
demux %loop_phase, %acc_raw : (i1, i32) -> (i32, i32)
# body executes only in body phase
%x = dataflow.load %A[%i], ... : memref<?xi32>
%next = arith.addi %acc_body, %x : i32
%sum = %acc_exit
The iter-arg state stream is deliberately in loop phase, not body
phase. carry sees %loop_phase, so it emits an N + 1 state stream:
the initial value, then one carried value after each true iteration.
The same %loop_phase demuxes that state stream. The true lane produces
exactly N %acc_body values and the false lane produces exactly one
%acc_exit value used as the loop result.
The feedback to carry has length N: %next is produced once per
true iteration. On the final false phase, carry consumes no next
value, emits no additional state, and returns to its init state.
For N = 0:
| Stream | Tokens |
|---|---|
%loop_phase |
[F] |
%i |
[] |
%acc_raw |
[init] |
%acc_body |
[] |
%next |
[] |
%acc_exit |
[init] |
%sum |
init |
For N = 1:
| Stream | Tokens |
|---|---|
%loop_phase |
[T, F] |
%i |
[0] |
%acc_raw |
[init, next0] |
%acc_body |
[init] |
%next |
[next0] |
%acc_exit |
[next0] |
%sum |
next0 |
For N = 2:
| Stream | Tokens |
|---|---|
%loop_phase |
[T, T, F] |
%i |
[0, 1] |
%acc_raw |
[init, next0, next1] |
%acc_body |
[init, next0] |
%next |
[next0, next1] |
%acc_exit |
[next1] |
%sum |
next1 |
Multiple iter_args lower independently using the same pattern, one
carry / demux state ring per iter_arg. Body operations may
freely combine the body-lane values from multiple iter_args before
feeding the corresponding yielded values directly to their carries.
Memref operands are not iter_arg-like stream state; only explicit
none memory-order state is carried for memory dependences.
none carries for W_P and R_P. Both are initialized from the incoming
frontier pair, driven by %loop_phase, sent to the body on the true lane,
and returned as loop exits on the false lane. The zero-trip case forwards
both initial components.This translation uses one loop selector from
dataflow.stream and independent carry -> demux rings for execution,
iter_args, and each touched W_P/R_P component. True lanes enter the
recursively lowered body; false lanes are loop exits. Captured non-memref
values use invariant followed by true-lane projection.
The body feeds every ring independently. Zero trip produces only the false selector token, so init execution, values, and frontier components transfer unchanged. Read-only state does not create RAR order; write feedback preserves RAW, WAR, and WAW across source-sequential iterations.
scf.forallThe Ownership Materialization And Handoff section of
docs/spec-compiler-part-2-scf.md is the sole normative owner of forall
normalization. Before Part 3 begins, the selected Structured Program Candidate
must have:
dataflow.thread definition and dataflow.thread.launch;P[] width, ownership, and
cross-lane legality are materialized in semantic IR and can be re-proved;MS1V858 · MS1V8 linked-input-79 · linked input 79generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:20eb96f51c52af18e8b31078 with its governing context; report any stag5000 generated · 5000 pairedopen PBT →MS1V357 · MS1V3 linked-input-79 · linked input 79generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:d1dc2174ff936b5d9497d7eb with its governing context; report any stag5000 generated · 5000 pairedopen PBT →and
Part 3 does not convert aggregation form, decide thread ownership, rewrite
forall to parallel as an optimization policy, infer P[], serialize lanes, or
select a reduction strategy. A dynamic domain, mapping attribute, shared output, result, combining action, or failed legality re-proof causes atomic failure before canonical graph publication. Cached provenance never changes this result.MS1V862 · MS1V8 linked-input-139 · linked input 139generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:20eb96f51c52af18e8b31078 with its governing context; report any stag5000 generated · 5000 pairedopen PBT →MS1V361 · MS1V3 linked-input-139 · linked input 139generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:d1dc2174ff936b5d9497d7eb with its governing context; report any stag5000 generated · 5000 pairedopen PBT →
For this boundary, an accepted effect-form forall has no shared_outs, no op
results, and an empty scf.forall.in_parallel terminator.MS1V864 · MS1V8 linked-input-199 · linked input 199generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:20eb96f51c52af18e8b31078 with its governing context; report any stag5000 generated · 5000 pairedopen PBT →MS1V363 · MS1V3 linked-input-199 · linked input 199generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:d1dc2174ff936b5d9497d7eb with its governing context; report any stag5000 generated · 5000 pairedopen PBT → In the example,
%N must resolve to the selected candidate's compile-time fixed extent:
scf.forall (%i) in (%N) {
%x = memref.load %A[%i] : memref<?xf32>
%y = arith.mulf %x, %x : f32
memref.store %y, %B[%i] : memref<?xf32>
scf.forall.in_parallel {}
}
Its result is represented only by explicit side effects in the body.
The following is an aggregation form and is not accepted by Part 3:
%out = scf.forall (%i) in (%N)
shared_outs(%o = %init) -> tensor<?xf32> {
%v = compute(%i) : f32
%slice = tensor.from_elements %v : tensor<1xf32>
scf.forall.in_parallel {
tensor.parallel_insert_slice %slice into %o[%i] [1] [1]
: tensor<1xf32> into tensor<?xf32>
}
}
Part 3 rejects this form before graph mutation. It never drops the combining
region or publishes a dataflow.graph that omits the aggregation. Any legal
materialization belongs to the Part 2 owner; this document intentionally does
not define a bufferization or combining algorithm.MS1V866 · MS1V8 linked-input-4 · linked input 4generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:20eb96f51c52af18e8b31078 with its governing context; report any stag5000 generated · 5000 pairedopen PBT →MS1V365 · MS1V3 linked-input-4 · linked input 4generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:d1dc2174ff936b5d9497d7eb with its governing context; report any stag5000 generated · 5000 pairedopen PBT →
If Part 2 selects an effect-form forall as an AccCore thread domain, the input accepted by Part 3 is already the definition-and-launch carrier shape below. The rank-one source sketch is retained only to relate the source induction variable to the canonical logical-coordinate ABI; it is not a Part 3 transformation:MS1V868 · MS1V8 linked-input-49 · linked input 49generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:20eb96f51c52af18e8b31078 with its governing context; report any stag5000 generated · 5000 pairedopen PBT →MS1V367 · MS1V3 linked-input-49 · linked input 49generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:d1dc2174ff936b5d9497d7eb with its governing context; report any stag5000 generated · 5000 pairedopen PBT →
scf.forall (%tx) in (%N) {
memref.store %v, %B[%tx] : memref<?xf32>
scf.forall.in_parallel {}
}
// At module scope (sibling of callable definitions):
dataflow.thread @t_<funcSym>_<seq>(%B_arg : memref<?xf32>, ...)
attributes { sym_visibility = "private" } {
^bb0(%B_arg : memref<?xf32>, ..., %thread_ctrl : none, %coord : index):
// For normalized zero-based unit-step forall, source IV equals coordinate.
memref.store %v, %B_arg[%coord] : memref<?xf32>
dataflow.thread.yield
}
// At the original scf.forall site:
%tok = dataflow.thread.launch @t_<funcSym>_<seq>
extents(%N) args(%B, ...)
: (memref<?xf32>, ...) -> !dataflow.thread_token
dataflow.thread.wait %tok : !dataflow.thread_token
The launch extents own an arbitrary-rank dense zero-based domain. The thread
entry block has one trailing logical-coordinate argument per extent, in source
dimension order. For each dimension, nonzero or dynamic source lower bounds
and steps cross as ordinary operands and the body reconstructs
source_iv = lower + coordinate * step. Values captured from outside the
source forall likewise become ordinary launch operands and matching definition
arguments. Sections 5.4.1 and 5.4.2 and
docs/spec-compiler-part-4-partitioned-data.md own the complete ABI.
Code inside the thread definition remains InstructionCore code unless the
selected Structured Program Candidate explicitly wraps it in
loom.spatial_region. That compiler-internal region remains the temporary
SpatialCore ownership carrier until Part 3 atomically replaces it with a
finalized dataflow.graph definition and launch.MS1V870 · MS1V8 linked-input-44 · linked input 44generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:20eb96f51c52af18e8b31078 with its governing context; report any stag5000 generated · 5000 pairedopen PBT →MS1V369 · MS1V3 linked-input-44 · linked input 44generator constraint — feeds the generated inputsSelected stage: loom-lower-forall-to-thread. Test exactly output obligation mlir-obligation:d1dc2174ff936b5d9497d7eb with its governing context; report any stag5000 generated · 5000 pairedopen PBT → Memory operations outside
the region remain in the InstructionCore body. The thread token preserves the
source continuation dependency through another launch dependency or an
explicit dataflow.thread.wait.
Within an explicit loom.spatial_region, an accepted graph-owned forall is
recursively replicated into its already selected static lanes. Every lane
starts from the same incoming execution and per-partition (W, R) frontier,
and lane exits are reduced with fixed-arity all-of joins. Empty domains are
identity transfers. The forall and its empty
scf.forall.in_parallel terminator are removed. No forall boundary,
partition id, dependence summary, or traversal order survives into canonical
graph IR.
scf.parallel with scf.reduceParallel normalization is owned exclusively by the Structured Program
Candidate lineage specified by the "Ownership Materialization And Handoff"
section of docs/spec-compiler-part-2-scf.md. Part 3 accepts a graph-owned
scf.parallel only when it is:
scf.reduce terminator;loom.spatial_region carrier;
andP[] widths and cross-lane legality are explicit in the candidate and can
be re-proved from current semantics.Dynamic-width, resultful, reduction-bearing, mapped, or otherwise unproved
forms fail before graph mutation. Part 3 does not choose any chunk count
K, including K = 1; flatten, serialize, or partition the iteration space
as policy; invent P[]; or select and inline a reduction order. Any such
choice must already be materialized as accepted semantic IR by Part 2. This
document intentionally defines no future upstream chunking or reduction
algorithm.
For an accepted one-dimensional effect-form loop, %N denotes a
compile-time-resolved extent and the candidate has already selected
P[] = [%N]:
scf.parallel (%i) = (%c0) to (%N) step (%c1) {
%x = memref.load %A[%i] : memref<?xf32>
%y = arith.mulf %x, %x : f32
memref.store %y, %B[%i] : memref<?xf32>
scf.reduce
}
scf.parallel is not a second Dataflow loop primitive. No
dataflow.parallel, dataflow.reduce, reduction enum, schedule record, or
parallel control op is introduced.
For rank r and fixed widths P[], the graph owner creates one static lane
for each logical coordinate tuple in the selected Cartesian domain. Each lane
starts from the same incoming execution and per-partition (W, R) frontier,
substitutes its already selected source induction values, and recursively
lowers the existing body. Incomparable exits are joined with fixed-arity
all-of; an empty domain is an identity transfer. The Cartesian rank is not
bounded by this lowering contract.
Lane enumeration is an implementation detail and never creates
cross-iteration program order. A failed independence or ownership re-proof
causes atomic failure. No parallel boundary, coordinate tuple, P[] record,
dependence summary, or traversal order survives into canonical graph IR.
scf.index_switchStructured normalization replaces a zero-case scf.index_switch with its
default region. Every remaining switch lowers through the same recursive
selection transfer used by scf.if. The canonical graph contains only
ordinary selector arithmetic and the existing dataflow.demux /
dataflow.mux actors; it does not retain SCF or introduce a second switch
abstraction.
scf.index_switch has the same selected-region shape as scf.if, but
its source selector is an arbitrary index value matched against a
dense array of case constants. dataflow.mux and dataflow.demux
require dense lane selectors, so lowering first normalizes the source
argument to a dataflow lane id.
Lane convention is a normalized lowering convention (it is not the
print order of the source op, which lists case regions before the
default region in the MLIR scf.index_switch op):
lane 0 = default region
lane i + 1 = case region i
The one-case form has two dynamic lanes and uses an i1 selector: false
selects default and true selects the single case. With two or more cases,
the normalized selector has index type. The zero-case form has only the
default region and is eliminated before this template is applied.
For two or more cases, the normalized selector is computed as ordinary
data, not with dataflow.mux. A dataflow.mux is selective and would
leave each unselected case-lane constant token in its queue. Across
many switch invocations those leftover tokens would accumulate without
bound under any bounded-buffer runtime and eventually apply backpressure.
They cannot be discarded because every candidate lane remains semantically
live for a later selector token. Ordinary arith.select follows
all-operand firing, so it consumes every candidate lane value on each
firing and leaves no residue.
# Normalize arbitrary case values to dense dataflow lanes.
# Lane convention: lane 0 = default region, lane i+1 = case region i
# (this is the lowering's normalized lane order; the source op prints
# case regions before the default region in MLIR's scf.index_switch).
# demux yields default-lane first, then case 0, case 1, ...; mux
# operand order matches.
%lane0 = dataflow.constant %ctrl {const_value = 0 : index} : index
%lane = ... compare %arg to each case value and arith.select lane i+1
%default_ctrl, %case0_ctrl, %case1_ctrl, ... =
demux %lane, %ctrl : (index, none) -> (none, none, none, ...)
# For every non-memref stream live-in %x : T used by any selected region:
%x_default, %x_case0, %x_case1, ... =
demux %lane, %x : (index, T) -> (T, T, T, ...)
... each selected region produces one result tuple and one done token ...
%result =
mux %lane, %r_default, %r_case0, %r_case1, ... : (index, T, T, T, ...) -> T
%done =
mux %lane, %done_default, %done_case0, %done_case1, ...
: (index, none, none, none, ...) -> none
scf.if's template after selector
normalization. Demux routes control and non-memref live-in streams
to exactly one selected region; mux collects the selected result and
done token.i
participates as lane i + 1. This is different from source case
values; case values are used only while computing %lane.%lane is constructed to be in range [0, num_cases]: unmatched
source values keep lane 0, while matched case i selects lane
i + 1. No dynamic selector-out-of-range diagnostic is required at
this lowering point.i1 demux/mux with the same lane convention:
false is default and true is the single case. The comparison
result is an ordinary SSA stream; multiple demuxes and muxes reuse it
by token broadcast.scf.index_switch lowers one result mux per result
position, all driven by the same normalized selector.For cases [2, 5] and argument stream [2, 7, 5], the normalized
selector stream is [1, 0, 2]:
| Stream | Tokens |
|---|---|
%lane |
[1, 0, 2] |
%default_ctrl |
[ctrl1] |
%case0_ctrl |
[ctrl0] |
%case1_ctrl |
[ctrl2] |
%arg_default |
[7] |
%arg_case0 |
[2] |
%arg_case1 |
[5] |
%r_default |
[default(arg=7)] |
%r_case0 |
[case2(arg=2)] |
%r_case1 |
[case5(arg=5)] |
%result |
[case2(arg=2), default(arg=7), case5(arg=5)] |
%done_default |
[done_default0] |
%done_case0 |
[done_case0_0] |
%done_case1 |
[done_case1_0] |
%done |
[done_case0_0, done_default0, done_case1_0] |
GraphRegionLowering normalizes the source argument once, orders lanes as
default followed by source case order, and invokes the shared recursive
selection transfer. That exact selector drives execution permission,
projected non-memory captures, every result mux, selected execution
completion, and each touched partition's W_P and R_P demux/mux pair.
Each region is recursively lowered only from its lane-specific inputs, so an
unselected region receives no execution or frontier token and its effects do
not execute. Multiple results produce one mux per result position. A branch
that does not touch a partition returns its lane-specific incoming frontier,
while a selected branch contributes its recursively reduced causal frontier.
scf.execute_regionStructured normalization inlines a supported scf.execute_region before
graph-region lowering. A residual region means the Structured Program
Candidate is not finalizable and is rejected before graph mutation.
After upstream inlining, the contents participate in ordinary sequential recursive lowering. No dedicated Dataflow actor or persistent region summary is required.
scf.yielddocs/spec-compiler-part-3-mem.md specifies the single recursive owner,
basic graph-local alias partitions, leaf transfer equations, and independent
write/read recurrence state. Section 6 of this document specifies how the
same selectors project execution, values, and both frontier components at
each supported SCF boundary.
docs/spec-compiler-part-4-partitioned-data.md specifies the two canonical
launch domains, dynamic responsibility transfer and termination, source-IV
reconstruction, and derived-view boundary. These semantics add no partition
carrier or mapping attribute to SCF-to-DFG flattening.
The Canonical Dataflow Program is the single semantic root of the fixed Artifact family:
loom.canonical_dataflow 3.0
Version 3.0 removes residual conversion bridges while admitting first-class
LLVM pointer values through the closed pointer and address projections above.
It removes pointer-to-memref launch adaptation entirely: the typed
graph launch memory binding accepts only an
exact memref capability relation. loom.canonical_dataflow 1.0 and 2.0 bytes
are not accepted or silently upgraded; a producer must rebuild and finalize
the program through the current graph ABI.
The family owns its admitted module surface, canonical semantic relation graph, canonical writer, artifact-local entity catalog, and importer. Common owns only the shared Artifact envelope, schema/version framing, SHA-256 v1 identity calculation, and collision-checked publication. Mapping, simulation, Evaluation, visualization, and native caches consume Dataflow-owned references; none may assign or reinterpret a Dataflow entity ID.
Finalization is failure-atomic. It operates on a private clone of the complete
program, validates every canonical graph and the whole-program thread, launch,
channel, memory-root, symbol, and completion relations, constructs canonical
bytes, invokes the Common finalizer, and publishes only the complete valid
Artifact. There is no is_finalized operation attribute or partially
finalized program state. A valid Common envelope, exact schema descriptor,
canonical bytes, and successful independent family verification together
define a finalized Artifact.
The first schema has exactly five independently referenceable entity kinds:
CanonicalDataflowEntityKind =
Graph
| Actor
| RootThreadLaunch
| StaticGraphLaunch
| LogicalMemoryRoot
Their carriers are:
dataflow.graph definition.dataflow.thread.launch site.
Thread launches cannot occur inside another thread or graph, so every such
site is a root launch.dataflow.graph.launch site in
a thread definition.dataflow.thread definitions do not receive IDs in this schema. Every
persistent use begins at a root launch and recovers the definition through its
typed callee relation. Private functions, thread definitions, actor
operands/results, graph boundaries, software edges, memory views, channel
branches, and dynamic invocation, work-item, memory-object, or firing
occurrences are likewise not independent entities. They are recovered through
identified owners plus typed semantic ordinals, canonical relations, or
execution-local identity. A future independently referenceable semantic
object requires an explicit schema catalog change; a consumer cannot mint an
ID for convenience.
Absence of a thread-definition EntityId does not remove dynamic thread
identity. The logical-domain contract derives each dense point from a root
launch and its coordinate tuple, or each dynamic point from a root launch and
its WorkItemId. Runtime adds one execution-local dispatch occurrence for a
concrete invocation. A definition ID, logical point, dispatch occurrence, and
physical AccCore binding are different domains and must never be substituted
for one another.
All five kinds share one Artifact-global unsigned 64-bit EntityId namespace.
Zero is a valid ID and there is no sentinel value. The finalizer assigns the
dense range [0, entity_count) in canonical-slot order, but serialized record
position is not identity and consumers must resolve the explicit ID.
The complete typed persistent references are:
GraphRef = (CanonicalDataflow ArtifactIdentity, Graph EntityId)
ActorRef = (CanonicalDataflow ArtifactIdentity, Actor EntityId)
RootThreadLaunchRef = (CanonicalDataflow ArtifactIdentity,
RootThreadLaunch EntityId)
StaticGraphLaunchRef = (CanonicalDataflow ArtifactIdentity,
StaticGraphLaunch EntityId)
LogicalMemoryRootRef = (CanonicalDataflow ArtifactIdentity,
LogicalMemoryRoot EntityId)
An artifact that already binds the exact Dataflow identity may use a compact typed local ID on the wire. The full meaning still includes that binding. Wrong-kind, foreign-artifact, missing, duplicate, out-of-range, or noncanonical IDs are invalid.
Objects below the five entity kinds use closed owner-relative structural
references. They do not receive another EntityId, and consumers must not
replace them with symbol paths, operation positions, generic field paths, or
native dense indices.
A graph launch is interpreted in the context of one root thread launch:
RootedGraphLaunchRef =
(RootThreadLaunchRef, StaticGraphLaunchRef)
The referenced static graph-launch site must belong to the thread definition resolved from the root launch. This context is required because one thread definition may be used by several root launches with different channel bindings, memory roots, execution bindings, or physical targets. It remains a static structural reference, not a dynamic invocation identity.
Graph-local token endpoints use the following closed forms:
GraphIngressTokenRef =
Start(GraphRef)
| ValueInput(GraphRef, value-input ordinal)
| StreamInput(GraphRef, stream-input ordinal)
GraphEgressTokenRef =
ValueOutput(GraphRef, value-output ordinal)
| StreamOutput(GraphRef, stream-output ordinal)
| CompletionFrontier(GraphRef, completion-frontier ordinal)
ActorTokenResultRef = (ActorRef, result ordinal)
ActorTokenOperandRef = (ActorRef, operand ordinal)
CanonicalGraphProducerEndpointRef =
GraphIngressTokenRef
| ActorTokenResultRef
CanonicalGraphConsumerEndpointRef =
ActorTokenOperandRef
| GraphEgressTokenRef
The Dataflow actor-port classifier must validate every actor ordinal as a token-plane port. A memory capability operand or result is never admitted by these unions. The exact producer endpoint and Dataflow def-use relation derive one complete canonical sink set. TechMapping may remove sinks proven internal to a selected realization, but it cannot change endpoint identity or create a second software-edge catalog.
The thread/graph ABI exposes one-message boundary transfers separately from thread-level channels:
RootThreadBoundaryTransferRef =
Start(RootThreadLaunchRef)
| ValueInput(RootThreadLaunchRef, value body-operand ordinal)
| Completion(RootThreadLaunchRef)
GraphLaunchBoundaryTransferRef =
Start(RootedGraphLaunchRef)
| ValueInput(RootedGraphLaunchRef, value-input ordinal)
| ValueResult(RootedGraphLaunchRef, value-result ordinal)
| Done(RootedGraphLaunchRef)
Root-thread value inputs exclude channel handles and memory capabilities. Extents and derived coordinates belong to the Thread Dispatch parameter contract rather than this message catalog. Each boundary-transfer reference owns exactly one source terminal and one sink terminal. Root start and value inputs flow from the runtime boundary to the selected InstructionCore; root completion flows back to runtime retirement. Graph start and value inputs flow from the selected InstructionCore to its SpatialCore; graph value results and done flow in the reverse direction. Explicit thread-token dependencies remain part of Thread Dispatch and do not create a second completion-message graph.
Channel endpoints are:
ThreadChannelSendSiteRef =
(RootThreadLaunchRef, canonical send-site ordinal)
ThreadChannelReceiveSiteRef =
(RootThreadLaunchRef, canonical receive-site ordinal)
ChannelProducerRef =
GraphStreamOutput(RootedGraphLaunchRef, stream-output ordinal)
| ThreadSend(ThreadChannelSendSiteRef)
ChannelConsumerRef =
GraphStreamInput(RootedGraphLaunchRef, stream-input ordinal)
| ThreadReceive(ThreadChannelReceiveSiteRef)
Send and receive ordinals index Dataflow-owned canonical endpoint inventories
for the rooted thread. They are not textual positions. The exact channel
relation and each consumer-owned source_map derive the complete canonical
consumer set for one producer. Dynamic message correspondence remains the
ordered event relation specified by
docs/spec-dataflow-part-1-streaming.md; no message ordinal, activation
pairing, epoch, or Physical Tag enters these static references.
The complete transfer-terminal unions are:
CanonicalProducerTerminalRef =
RootThreadBoundarySource(RootThreadBoundaryTransferRef)
| GraphLaunchBoundarySource(GraphLaunchBoundaryTransferRef)
| ChannelProducer(ChannelProducerRef)
CanonicalSinkTerminalRef =
RootThreadBoundarySink(RootThreadBoundaryTransferRef)
| GraphLaunchBoundarySink(GraphLaunchBoundaryTransferRef)
| ChannelConsumer(ChannelConsumerRef)
A boundary source derives its one paired sink. A channel producer derives its complete non-empty sorted consumer set. Graph value results and later graph value inputs remain two distinct InstructionCore-facing ABI transfers. A compiler that wants direct graph-to-graph streaming must represent it with a channel/stream or fuse the graphs; Mapping cannot invent that rewrite.
Memory references remain on the capability plane:
LogicalMemoryViewRef =
(LogicalMemoryRootRef, canonical root-local view ordinal)
LogicalMemoryRootOrViewRef =
Root(LogicalMemoryRootRef)
| View(LogicalMemoryViewRef)
ContextualActorRef =
(RootedGraphLaunchRef, ActorRef)
MemoryExposureRef =
(RootedGraphLaunchRef, graph memory-result ordinal)
FenceActorFamilyRef =
ActorRef validated as dataflow.fence
The root-local view inventory owns each root-preserving static view relation. Instantiating one reusable graph view under different logical roots therefore produces distinct structural references in the corresponding root inventories, without allocating view entities. A contextual actor must belong to the graph called by its rooted launch. A memory exposure identifies a launch-contextual graph memory result and resolves through the Dataflow memory relation to exactly one logical root or view.
System service members use one closed obligation-relative union:
ServiceMemberRef =
MessageTransfer
| AddressedMemoryActor(ContextualActorRef)
| FenceActor(ContextualActorRef)
MessageTransfer is the singleton member of a transfer obligation, including
multicast. Addressed-memory and fence members derive their exact Canonical
Service kind and local legs from the actor semantics. A memory exposure is not
a service member and has no request or response leg; it is a capability
boundary selected by a service target binding or Mapping exposure entry.
System resource-time anchors reuse transfer terminals and rooted actor transitions already owned by Dataflow:
StaticTransferEventRef =
Produced(CanonicalProducerTerminalRef)
| Consumed(CanonicalSinkTerminalRef)
ContextualActorTransitionEventRef =
(ContextualActorRef, transition_case_ordinal)
EventFamilyKey =
Transfer(StaticTransferEventRef)
| ActorTransition(ContextualActorTransitionEventRef)
EventLogicalInputSlot =
Coordinate(event-domain coordinate ordinal)
| LaunchParameter(root-launch parameter ordinal)
EventLogicalProjection(EventFamilyKey) =
canonical sorted unique array<EventLogicalInputSlot>
A graph-local endpoint event has one Dataflow-owned rooted projection into that same event-family domain:
RootedGraphEndpointEventProjection(
RootedGraphLaunchRef,
Produced(CanonicalGraphProducerEndpointRef)
| Consumed(CanonicalGraphConsumerEndpointRef)
) -> canonical nonempty set<EventFamilyKey>
For Produced(ActorTokenResultRef), the set contains exactly the rooted
ActorTransition cases whose OperationSchema-owned activeResults contains
that result ordinal. For Consumed(ActorTokenOperandRef), it contains exactly
the rooted cases whose consumedInputs contains that operand ordinal. This is
an alternatives set: occurrence of any member is occurrence of the original
endpoint event. Several cases producing or consuming one port do not become an
AllOf relation.
A graph start or value-input ingress maps to the consumed exact graph-launch boundary terminal. A stream-input ingress maps to the consumed exact channel consumer terminal. A value-output egress maps to the produced exact graph- launch result terminal, and a stream-output egress maps to the produced exact channel producer terminal. A completion-frontier egress maps recursively through its unique canonical graph producer because one frontier token is not the graph-wide done event. The recursive projection must terminate at an ingress or actor result in the acyclic SSA definition relation; malformed, empty, foreign, or duplicate results are rejected.
This projection is a removable view of the Canonical Dataflow Program and its
OperationSchema projections. It has no EntityId, persistent record, digest, or
Mapping/Deployment override. Consumers use the existing EventFamilyKey
comparison wire for its sorted unique result.
There is no static-event EntityId, and the projection is not a field of the
key. Every terminal and contextual actor transition resolves through the exact
program to one rooted logical event domain. The transition ordinal resolves
in the exact actor's canonical ActorHandshakeCase projection from
OperationSchema. It is not a service-local request ID, a Mapping event, or a
second transition catalog. For every addressed-memory and fence actor, the
unique transition-case commit is issue of one logical operation as specified
by docs/spec-dataflow-memory-consistency.md; a missing or non-unique issue
transition is invalid for a System resource-time anchor.
Coordinate indexes the resolved event domain's canonical coordinate
inventory. LaunchParameter indexes the owning root thread launch's canonical
parameter inventory. That inventory contains the launch extents in coordinate
order followed by ordinary bodyOperands of index or signless-integer type
in body-operand order. Channel handles, memory capabilities, tokens, pointers,
floats, vectors, and aggregates are not launch parameters. For a DynamicWork
launch, the designated work-item operand is also excluded because its identity
is owned by the stable-item projection. Types, ranks, values, bounds, and
domain membership are recovered from the exact Canonical Dataflow Program and
are never copied into a slot.
The event-domain coordinate inventory is likewise Dataflow-owned. A dense root boundary uses the thread coordinate suffix in source-dimension order; a graph or channel terminal uses the exact rooted event may-domain and endpoint relation. A repeated stored-program launch does not acquire a hidden iteration slot. If Mapping must distinguish such iterations, the selected program must already expose the distinction as a logical coordinate, launch parameter, or DynamicWork stable-item component.
The projection contains every coordinate and launch-parameter slot available
to Mapping or admission for this event. It is derived once by
CanonicalDataflowProgramView; it is not selected by Mapping, Deployment, or
runtime. An empty projection is valid. A missing, duplicate, wrong-owner,
wrong-kind, out-of-range, or noncanonical slot is invalid. Dynamic-work stable
item components remain in their separately owned stable-key projection. An
event-rooted Mapping relation may consume that projection in addition to this
coordinate/launch-parameter projection, but neither schema is embedded in the
event key.
Canonical projection order is the complete EventLogicalInputSlot key order:
Coordinate before LaunchParameter, then unsigned ordinal. Variant
discriminants are zero-based u32be values in declaration order, ordinals and
counts are u64be, and the projection comparison wire is:
u64be(slot_count)
repeat slot_count times:
u32be(slot_kind)
u64be(slot_ordinal)
EventFamilyKey canonical comparison wire starts with the zero-based u32be
declaration-order discriminant Transfer or ActorTransition. Transfer is
followed by the existing StaticTransferEventRef wire: its zero-based
u32be Produced or Consumed discriminant and recursively encoded terminal
reference. ActorTransition is followed by the recursively encoded
ContextualActorRef and the u64be transition-case ordinal. Closed-union
variants use declaration-order u32be discriminants; Dataflow EntityId and
owner-local ordinals use u64be; nested structural references are encoded in
field order. When the containing artifact already binds the exact Canonical
Dataflow identity, the local wire omits that repeated identity; a standalone
reference uses the Common exact ArtifactReference framing first. There is no
alternate textual, JSON-specific, or native-index ordering authority.
This comparison wire fixes cross-family ordering and round-trip semantics; it does not create a parallel binary Artifact schema. A Mapping canonical assembly or Deployment canonical JSON writer renders the same typed variants and fields in its sole owner format, then rederives the comparison wire for ordering and validation.
One runtime event occurrence consists of the key, concrete values matching the derived projection, any applicable separately owned DynamicWork item identity, and a transient occurrence handle. Concrete values, dynamic item identity, and the handle never enter Artifact identity, Mapping keys, channel ordering, or Physical Tag assignment. SpatialMapping-local actor activity remains owned by the SpatialMapping. System closure uses the rooted contextual form only when a selected System resource is activated by that exact actor transition; it does not copy the SpatialMapping record or create another dynamic occurrence.
Before labeling, the finalizer removes every pre-existing
dataflow.entity_id from its private clone. The relation graph contains the
complete semantic program, not only the five entity nodes. It includes:
OperationSchemaId, exact types, and closed
schema-owned semantic property and attribute projection;A dataflow.graph body is a graph region, so actor textual order contributes
no relation. Module and symbol-table order are also nonsemantic. Stored-program
block/control/operation order is semantic and remains in the relation graph.
This distinction prevents a generic "ignore operation order" rule from
silently changing InstructionCore behavior.
SSA and block labels, private symbol spelling, source and filesystem locations, debug/provenance metadata, visual coordinates, printer order, and builder insertion order are excluded. Private symbols are resolved to typed relations and receive canonical printed labels. An externally visible linkage name is ABI semantics rather than a private printer label and is included together with its linkage and visibility contract. Every other registered non-actor field is semantic by default. Every actor field must be classified by its closed operation-schema projection. Excluding any field requires an explicit owner-spec rule rather than an open ignore list.
The equivalence boundary is exact typed and attributed structural isomorphism. It does not prove algebraic or whole-program functional equivalence. A semantics-preserving Dataflow rewrite whose graph is non-isomorphic therefore produces a different Canonical Dataflow Artifact, as required by the Dataflow optimization lineage.
Canonical labeling determines semantic slots independently of source handles. Entities in one automorphism orbit have no recoverable nonsemantic source identity. The finalizer may return a source-object-to-final-ID provenance map for the current derivation, but it does not enter canonical bytes and is not a reference authority.
After canonical slots are fixed, the finalizer assigns IDs and materializes
the single dataflow.entity_id attribute on each entity carrier. A logical
memory root carried by an ordinary function-like argument uses that argument's
existing attribute dictionary. The canonical writer emits normalized private
symbol, SSA, and block labels, canonical unordered collections, the derived
IDs, and all semantic relations. It omits locations and the explicitly
nonsemantic metadata above. The Common finalizer hashes exactly those
family-owned canonical bytes.
The derived IDs are excluded while recomputing canonical labels, avoiding a circular identity definition. A finalized importer independently reconstructs the relation graph and requires every materialized ID to match the canonical assignment. A mutable authoring program may omit IDs; any supplied values are discarded on the private finalization clone rather than trusted.
One Dataflow-owned read-only CanonicalDataflowProgramView projects the five
typed ID maps, every closed structural-reference inventory above, canonical
actor and endpoint relations, rooted launch contexts, channel producer and
consumer relations, launch-callee closure, logical-memory root/view and
exposure relations, service-member derivation, static transfer events, and
their exact EventLogicalProjection values. Its native indices and lookup
tables are disposable caches. Mapping's
draft/search structures and simulator event tables may cache this view, but
cannot define another persistent graph, actor, launch, terminal, member,
event, or memory catalog.
LogicalMemoryRootRef identifies a static software root role, not one runtime
object. An imported runtime object is bound by the exact launch and runtime
memory registry. A fresh graph allocation instance is derived from its static
root reference and graph invocation occurrence. If two imported roles alias at
runtime, the runtime registry relates them to the same object without merging
their static entity IDs. A memory view remains a typed structural reference
whose root relation resolves to exactly one LogicalMemoryRootRef.
The importer derives each admitted view from its exact root-preserving memref
relation. It never searches for or executes a graph-body conversion operation.
Anchor-level tests cover:
builtin.unrealized_conversion_cast;Tests do not pin printer whitespace, a particular graph-labeling algorithm, native container layout, source-handle provenance, or a broad operation fixture matrix.
In addition to the Dataflow dialect and finalized-program verifier set:
dataflow.thread (definition, Section 5.4.1)ModuleOp (HasParent<"ModuleOp">).sym_name is required and module-unique among
dataflow.thread definitions and other Symbol-bearing ops in
the same module.sym_visibility is required and must equal "private" under the
baseline visibility policy. "public" and "nested" are rejected
unless cross-module linkage is enabled by a separate spec.function_type inputs are the user body operand types
(T0..TN); function_type results are empty.domain is one closed Part 4 domain. For DenseRectangular, entry block
argument count equals numBodyOperands + 1 + coordinateRank. For
DynamicWork, the rank is zero, the count is numBodyOperands + 1, and
work_item_arg_ordinal selects one ordinary input. The dense block-arg
layout is (args_*, thread_ctrl, coord_*): the first
N == numBodyOperands
block args mirror function_type.inputs exactly, then one
none-typed thread_ctrl block arg, then one index-typed
block arg per logical coordinate (in source-dimension order). The
coordinate suffix length is the sole definition of rank. This ordering
keeps the first N block args aligned with
function_type.inputs, satisfying the upstream
FunctionOpInterface invariant.IsolatedFromAbove: every SSA value used in the
body and defined outside it is rejected.dataflow.graph definition (a graph
definition is a sibling at module scope, not a body element).
A dataflow.graph.launch is the only way to invoke a graph
callable from inside a thread definition's body.dataflow.thread definition or a
dataflow.thread.launch; thread definitions are module-scope
siblings and launches are caller-side only. The launch verifier
checks this restriction transitively through nested regions.dataflow.work.spawn is legal only in a DynamicWork definition and
outside every nested dataflow.graph; its operand type equals the
designated work-item input.DynamicWork definition rejects channel-typed captures, channel create,
send, receive, and graph stream bindings.dataflow.graph.launch ops are allowed in a
thread body. An InstructionCore-only body with no graph launch is also
legal; this verifier rule does not itself select AccCore execution.llvm.call or func.call only when the callee has
been proven InstructionCore-legal or is scheduled for inlining before
graph extraction. Body must not contain llvm.func or func.func
definitions.Reachability is a pipeline invariant, not a local verifier rule. The verifier may accept an unreferenced private definition as dead IR, but finalized program publication removes unreachable private symbols.
dataflow.thread.launch (Section 5.4.2)
callee resolves to a dataflow.thread definition in the
same module (verifier rejects unresolved or wrong-kind callee).bodyOperands types equal callee.function_type.inputs
position-by-position.extents count equals coordinate rank. Every extent
has index type. Statically known negative extents are rejected; runtime
values are checked before instance creation. Dense rank zero creates one
instance and any zero extent creates none. A dynamic callee has no extents
and the designated body operand supplies exactly one root work item.!dataflow.thread_token
result for collective retirement of all logical instances. Dynamic work
retires only after its active responsibility set is empty.Must appear outside every dataflow.thread and dataflow.graph
definition, including through nested regions.
dataflow.thread.yield
none operands as an unordered all-of
completion frontier. The parent dataflow.thread definition has
no data results; the per-launch completion token is produced by
the launch op, not yielded as a body value. The verifier checks
only frontier operand types and terminator placement.dataflow.thread definition (enforced by
ParentOneOf<["::dataflow::ThreadOp"]>).In a dynamic definition, the terminator retires the current item exactly once after its completion frontier; it does not close a channel or retire the collective token while another responsibility remains active.
dataflow.work.spawn
DynamicWork thread and outside every
graph. Dense threads, host code, and graph bodies reject it.It is effectful, has no result or target, and acquires responsibility before making the child visible.
dataflow.thread.wait
!dataflow.thread_token produced
by a dataflow.thread.launch.dataflow.thread and dataflow.graph
definition, including through nested regions.The op has no SSA result and therefore produces no graph-control
none value. It is an ordered stored-program causal wait, not a
memory barrier.
dataflow.graph (definition, Section 5.5.1)
ModuleOp (HasParent<"ModuleOp">).sym_name is required and module-unique among
dataflow.graph definitions and other Symbol-bearing ops in
the same module.sym_visibility is required and must equal "private" in the
baseline visibility policy. "public" and "nested" are rejected
unless cross-module linkage is enabled by a separate spec.function_type inputs are (T0..TN) and results are (R0..RM), containing
only application payloads. Normalized input_segments and
result_segments classify value, stream, and memory ports. The graph
start and launch done endpoints are not function-type slots.IsolatedFromAbove: every SSA
value used in the body and defined outside it is rejected.(%ctrl_in : none, %arg_0 : T0, ...,
%arg_N : TN): the trailing arguments mirror function_type.inputs, while
%ctrl_in is the explicit start protocol endpoint.dataflow.graph.return terminator has values, streams,
memories, and mandatory non-empty complete segments. Concatenated
payload segments match all function_type.results. Done is not a return
payload or function-type slot.OperationSchemaId and passes its
CanonicalDataflowActorOpInterface instance verifier. The derived
canonical actor classifier consumes this registry for compute, control,
and memory actors; it is not a separate whitelist. Lowering does not infer
actor support from dialect or operation names.arith or math actor is non-canonical and must have been normalized
before graph finalization. Exact fused FMA uses math.fma; a non-fused
multiply-add remains two explicit actors.scf.*, llvm.func, func.func, llvm.call,
func.call, builtin.unrealized_conversion_cast,
dataflow.thread.launch, dataflow.graph.launch,
dataflow.thread.wait, dataflow.graph.wait,
another dataflow.graph definition, or a dataflow.thread
definition.The op declares RecursiveMemoryEffects so module-scope
walkers can observe per-callable effects. Launch completion is still
defined only by the explicit return frontier.
callee resolves to a dataflow.graph definition in the
same module (verifier rejects unresolved or wrong-kind callee).done : none result is the retirement protocol
endpoint and equals all_of(callee.graph.return.complete). No effect scan or
quiescence rule provides an alternate completion authority.dataflow.thread definition's
body, not at host scope and not inside another
dataflow.graph definition's body.The launch does not reconstruct completion from callee effects. Native finalization validates that the explicit return frontier covers every observable effect before mapping or simulation.
dataflow.graph.wait (Section 5.5.3)
none completion events
and has no result.dataflow.thread definition
and must not appear at host scope or inside a dataflow.graph definition.done result or a path-aware terminal event whose causal closure contains
one. Generic none values and textual order are insufficient.It is a stored-program graph-retirement wait, not a memory barrier, channel drain, thread-token conversion, or alternate completion authority.
Dataflow_GraphReturnOp
complete is non-empty, variadic, unordered all-of, and none-typed.values, streams, and memories, in that order, match the parent
payload result types.%complete, %values... syntax; all other shapes print named
segments.The following are explicitly out of scope for the scf-to-dfg contract:
dataflow.thread directly to a fabric.module symbol. The thread
remains target-independent software IR; SystemMapping binds logical thread
execution to an AccCore, while TechMapping and SpatialMapping realize each
selected graph on that AccCore's SpatialCore. The thread is already isolated
and has an explicit boundary operand list.dataflow.thread data results, async value types, thread
groups, and thread-level aggregation regions. Part 2 must materialize any
supported tensor-result aggregation into accepted effect form before thread
promotion; a residual aggregation form fails finalizability.fabric.pe or fabric.mem instance.source_map while mechanically
converting region-local endpoints to the canonical graph stream network.
One binding owns one ordered dynamic event sequence: sequential and
structured mutually exclusive static sites share a fixed ordinal schedule,
with inactive choice sites filtered from that sequence. Repeated launch
instances concatenate their contributions in deterministic issue order;
producer and consumer events correspond by flat sequence ordinal, not by a
one-to-one activation relation. A repeat nested under any selected region is
refused until a conditional event-count projection can prove how inactive
lanes suppress every repeated event; an invariant repeat bound alone is not
such a proof. Publication does not invent routing, endpoint creation, a
parallel channel mode, or a traversal order for ambiguous parallel endpoint
sites.docs/spec-fabric-module.md, docs/spec-fabric-pe.md,
docs/spec-fabric-fu.md -- Fabric hardware semantics consumed by Mapping,
not embedded in the Canonical Dataflow Program.docs/spec-compiler-part-1-source.md -- high-level source
integration and metadata emission.docs/spec-compiler-part-2-scf.md -- LLVM-to-SCF raising and structured
thread-boundary preparation.docs/spec-compiler-part-3-mem.md -- recursive graph-region memory
lowering, basic alias partitions, write/read frontier transfers, and
structured recurrence used inside each dataflow.graph.docs/spec-core-dialect-boundary.md -- compiler, Dataflow, Fabric, Mapping,
and runtime ownership outside the canonical graph ABI.docs/spec-mapping-artifact.md, docs/spec-mapping-memory.md, and
docs/spec-pnr.md -- TechMapping, canonical-memory realization, and
Spatial/System physical realization after canonical graph publication.docs/spec-compiler-part-4-partitioned-data.md -- canonical logical launch
domains, source-IV reconstruction, ordinary data views, and the physical
Mapping boundary.docs/spec-dataflow-part-1-streaming.md -- precise timing
semantics for dataflow.stream, dataflow.carry,
dataflow.invariant, and dataflow.gate.docs/spec-dataflow-part-2-control.md -- precise firing semantics
for dataflow.constant, dataflow.sync, dataflow.mux, and
dataflow.demux.externals/llvm/mlir/...):Dialect/SCF/IR/SCFOps.td.Dialect/Async/IR/AsyncOps.td,
Dialect/Async/IR/AsyncTypes.td.Dialect/GPU/IR/GPUOps.td, Dialect/GPU/IR/GPUBase.td.Dialect/OpenMP/IR/OpenMPOps.td,
Dialect/OpenACC/IR/OpenACCOps.td.Conversion/SCFToGPU/SCFToGPU.cpp.