@@ -87,7 +87,7 @@ BasicBlock *getExitFor(const ConvergenceRegion *CR) {
87
87
// Returns the merge block designated by I if I is a merge instruction, nullptr
88
88
// otherwise.
89
89
BasicBlock *getDesignatedMergeBlock (Instruction *I) {
90
- IntrinsicInst *II = dyn_cast <IntrinsicInst>(I);
90
+ IntrinsicInst *II = dyn_cast_or_null <IntrinsicInst>(I);
91
91
if (II == nullptr )
92
92
return nullptr ;
93
93
@@ -102,7 +102,7 @@ BasicBlock *getDesignatedMergeBlock(Instruction *I) {
102
102
// Returns the continue block designated by I if I is an OpLoopMerge, nullptr
103
103
// otherwise.
104
104
BasicBlock *getDesignatedContinueBlock (Instruction *I) {
105
- IntrinsicInst *II = dyn_cast <IntrinsicInst>(I);
105
+ IntrinsicInst *II = dyn_cast_or_null <IntrinsicInst>(I);
106
106
if (II == nullptr )
107
107
return nullptr ;
108
108
@@ -284,18 +284,6 @@ void replaceBranchTargets(BasicBlock *BB, BasicBlock *OldTarget,
284
284
assert (false && " Unhandled terminator type." );
285
285
}
286
286
287
- // Replaces basic bloc operands |OldSrc| or OpPhi instructions in |BB| by
288
- // |NewSrc|. This function does not simplify the OpPhi instruction once
289
- // transformed.
290
- void replacePhiTargets (BasicBlock *BB, BasicBlock *OldSrc, BasicBlock *NewSrc) {
291
- for (PHINode &Phi : BB->phis ()) {
292
- int index = Phi.getBasicBlockIndex (OldSrc);
293
- if (index == -1 )
294
- continue ;
295
- Phi.setIncomingBlock (index, NewSrc);
296
- }
297
- }
298
-
299
287
} // anonymous namespace
300
288
301
289
// Given a reducible CFG, produces a structurized CFG in the SPIR-V sense,
@@ -423,7 +411,7 @@ class SPIRVStructurizer : public FunctionPass {
423
411
}
424
412
425
413
// Splits the given edges by recreating proxy nodes so that the destination
426
- // OpPhi instruction can still be viable .
414
+ // has unique incoming edges from this region .
427
415
//
428
416
// clang-format off
429
417
//
@@ -436,66 +424,58 @@ class SPIRVStructurizer : public FunctionPass {
436
424
// A -> D -> C
437
425
// B -> D -> C
438
426
//
439
- // But if C had a phi node, adding such proxy-block breaks it. In such case, we must add 1 new block per
440
- // exit, and patchup the phi node:
427
+ // This is fine (assuming C has no PHI nodes), but requires handling the merge instruction here.
428
+ // By adding a proxy node, we create a regular divergent shape which can easily be regularized later on.
441
429
// A -> D -> D1 -> C
442
430
// B -> D -> D2 -> C
443
431
//
444
- // A, B, D belongs to the construct. D is the exit. D1 and D2 are empty, just used as
445
- // source operands for C's phi node.
432
+ // A, B, D belongs to the construct. D is the exit. D1 and D2 are empty.
446
433
//
447
434
// clang-format on
448
435
std::vector<Edge>
449
436
createAliasBlocksForComplexEdges (std::vector<Edge> Edges) {
450
- std::unordered_map<BasicBlock *, BasicBlock *> Seen;
437
+ std::unordered_set< BasicBlock *> Seen;
451
438
std::vector<Edge> Output;
452
439
Output.reserve (Edges.size ());
453
440
454
441
for (auto &[Src, Dst] : Edges) {
455
- auto [iterator, inserted] = Seen.insert ({Src, Dst});
456
- if (inserted) {
457
- Output.emplace_back (Src, Dst);
458
- continue ;
442
+ auto [Iterator, Inserted] = Seen.insert (Src);
443
+ if (!Inserted) {
444
+ // Src already a source node. Cannot have 2 edges from A to B.
445
+ // Creating alias source block.
446
+ BasicBlock *NewSrc = BasicBlock::Create (
447
+ F.getContext (), Src->getName () + " .new.src" , &F);
448
+ replaceBranchTargets (Src, Dst, NewSrc);
449
+ IRBuilder<> Builder (NewSrc);
450
+ Builder.CreateBr (Dst);
451
+ Src = NewSrc;
459
452
}
460
453
461
- // The exact same edge was already seen. Ignoring.
462
- if (iterator->second == Dst)
463
- continue ;
464
-
465
- // The same Src block branches to 2 distinct blocks. This will be an
466
- // issue for the generated OpPhi. Creating alias block.
467
- BasicBlock *NewSrc =
468
- BasicBlock::Create (F.getContext (), " new.exit.src" , &F);
469
- replaceBranchTargets (Src, Dst, NewSrc);
470
- replacePhiTargets (Dst, Src, NewSrc);
471
-
472
- IRBuilder<> Builder (NewSrc);
473
- Builder.CreateBr (Dst);
474
-
475
- Seen.emplace (NewSrc, Dst);
476
- Output.emplace_back (NewSrc, Dst);
454
+ Output.emplace_back (Src, Dst);
477
455
}
478
456
479
457
return Output;
480
458
}
481
459
460
+ AllocaInst *CreateVariable (Function &F, Type *Type,
461
+ BasicBlock::iterator Position) {
462
+ const DataLayout &DL = F.getDataLayout ();
463
+ return new AllocaInst (Type, DL.getAllocaAddrSpace (), nullptr , " reg" ,
464
+ Position);
465
+ }
466
+
482
467
// Given a construct defined by |Header|, and a list of exiting edges
483
468
// |Edges|, creates a new single exit node, fixing up those edges.
484
469
BasicBlock *createSingleExitNode (BasicBlock *Header,
485
470
std::vector<Edge> &Edges) {
486
- auto NewExit = BasicBlock::Create (F.getContext (), " new.exit" , &F);
487
- IRBuilder<> ExitBuilder (NewExit);
488
471
489
- std::vector<BasicBlock *> Dsts;
490
- std::unordered_map<BasicBlock *, ConstantInt *> DstToIndex;
491
-
492
- // Given 2 edges: Src1 -> Dst, Src2 -> Dst:
493
- // If Dst has an PHI node, and Src1 and Src2 are both operands, both Src1
494
- // and Src2 cannot be hidden by NewExit. Create 2 new nodes: Alias1,
495
- // Alias2 to which NewExit will branch before going to Dst. Then, patchup
496
- // Dst PHI node to look for Alias1 and Alias2.
497
472
std::vector<Edge> FixedEdges = createAliasBlocksForComplexEdges (Edges);
498
473
474
+ std::vector<BasicBlock *> Dsts;
475
+ std::unordered_map<BasicBlock *, ConstantInt *> DstToIndex;
476
+ auto NewExit = BasicBlock::Create (F.getContext (),
477
+ Header->getName () + " .new.exit" , &F);
478
+ IRBuilder<> ExitBuilder (NewExit);
499
479
for (auto &[Src, Dst] : FixedEdges) {
500
480
if (DstToIndex.count (Dst) != 0 )
501
481
continue ;
@@ -506,33 +486,34 @@ class SPIRVStructurizer : public FunctionPass {
506
486
if (Dsts.size () == 1 ) {
507
487
for (auto &[Src, Dst] : FixedEdges) {
508
488
replaceBranchTargets (Src, Dst, NewExit);
509
- replacePhiTargets (Dst, Src, NewExit);
510
489
}
511
490
ExitBuilder.CreateBr (Dsts[0 ]);
512
491
return NewExit;
513
492
}
514
493
515
- PHINode *PhiNode =
516
- ExitBuilder.CreatePHI (ExitBuilder.getInt32Ty (), FixedEdges.size ());
517
-
494
+ AllocaInst *Variable = CreateVariable (F, ExitBuilder.getInt32Ty (),
495
+ F.begin ()->getFirstInsertionPt ());
518
496
for (auto &[Src, Dst] : FixedEdges) {
519
- PhiNode->addIncoming (DstToIndex[Dst], Src);
497
+ IRBuilder<> B2 (Src);
498
+ B2.SetInsertPoint (Src->getFirstInsertionPt ());
499
+ B2.CreateStore (DstToIndex[Dst], Variable);
520
500
replaceBranchTargets (Src, Dst, NewExit);
521
- replacePhiTargets (Dst, Src, NewExit);
522
501
}
523
502
503
+ llvm::Value *Load =
504
+ ExitBuilder.CreateLoad (ExitBuilder.getInt32Ty (), Variable);
505
+
524
506
// If we can avoid an OpSwitch, generate an OpBranch. Reason is some
525
507
// OpBranch are allowed to exist without a new OpSelectionMerge if one of
526
508
// the branch is the parent's merge node, while OpSwitches are not.
527
509
if (Dsts.size () == 2 ) {
528
- Value *Condition = ExitBuilder. CreateCmp (CmpInst::ICMP_EQ,
529
- DstToIndex[Dsts[0 ]], PhiNode );
510
+ Value *Condition =
511
+ ExitBuilder. CreateCmp (CmpInst::ICMP_EQ, DstToIndex[Dsts[0 ]], Load );
530
512
ExitBuilder.CreateCondBr (Condition, Dsts[0 ], Dsts[1 ]);
531
513
return NewExit;
532
514
}
533
515
534
- SwitchInst *Sw =
535
- ExitBuilder.CreateSwitch (PhiNode, Dsts[0 ], Dsts.size () - 1 );
516
+ SwitchInst *Sw = ExitBuilder.CreateSwitch (Load, Dsts[0 ], Dsts.size () - 1 );
536
517
for (auto It = Dsts.begin () + 1 ; It != Dsts.end (); ++It) {
537
518
Sw->addCase (DstToIndex[*It], *It);
538
519
}
@@ -576,7 +557,7 @@ class SPIRVStructurizer : public FunctionPass {
576
557
577
558
// Creates a new basic block in F with a single OpUnreachable instruction.
578
559
BasicBlock *CreateUnreachable (Function &F) {
579
- BasicBlock *BB = BasicBlock::Create (F.getContext (), " new.exit " , &F);
560
+ BasicBlock *BB = BasicBlock::Create (F.getContext (), " unreachable " , &F);
580
561
IRBuilder<> Builder (BB);
581
562
Builder.CreateUnreachable ();
582
563
return BB;
@@ -1027,17 +1008,8 @@ class SPIRVStructurizer : public FunctionPass {
1027
1008
return Modified;
1028
1009
}
1029
1010
1030
- bool IsRequiredForPhiNode (BasicBlock *BB) {
1031
- for (BasicBlock *Successor : successors (BB)) {
1032
- for (PHINode &Phi : Successor->phis ()) {
1033
- if (Phi.getBasicBlockIndex (BB) != -1 )
1034
- return true ;
1035
- }
1036
- }
1037
-
1038
- return false ;
1039
- }
1040
-
1011
+ // Removes blocks not contributing to any structured CFG. This assumes there
1012
+ // is no PHI nodes.
1041
1013
bool removeUselessBlocks (Function &F) {
1042
1014
std::vector<BasicBlock *> ToRemove;
1043
1015
@@ -1054,9 +1026,6 @@ class SPIRVStructurizer : public FunctionPass {
1054
1026
if (MergeBlocks.count (&BB) != 0 || ContinueBlocks.count (&BB) != 0 )
1055
1027
continue ;
1056
1028
1057
- if (IsRequiredForPhiNode (&BB))
1058
- continue ;
1059
-
1060
1029
if (BB.getUniqueSuccessor () == nullptr )
1061
1030
continue ;
1062
1031
@@ -1127,6 +1096,18 @@ class SPIRVStructurizer : public FunctionPass {
1127
1096
continue ;
1128
1097
1129
1098
Modified = true ;
1099
+
1100
+ if (Merge == nullptr ) {
1101
+ Merge = *successors (Header).begin ();
1102
+ IRBuilder<> Builder (Header);
1103
+ Builder.SetInsertPoint (Header->getTerminator ());
1104
+
1105
+ auto MergeAddress = BlockAddress::get (Merge->getParent (), Merge);
1106
+ SmallVector<Value *, 1 > Args = {MergeAddress};
1107
+ Builder.CreateIntrinsic (Intrinsic::spv_selection_merge, {}, {Args});
1108
+ continue ;
1109
+ }
1110
+
1130
1111
Instruction *SplitInstruction = Merge->getTerminator ();
1131
1112
if (isMergeInstruction (SplitInstruction->getPrevNode ()))
1132
1113
SplitInstruction = SplitInstruction->getPrevNode ();
0 commit comments