@@ -87,6 +87,8 @@ 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
+ if (I == nullptr )
91
+ return nullptr ;
90
92
IntrinsicInst *II = dyn_cast<IntrinsicInst>(I);
91
93
if (II == nullptr )
92
94
return nullptr ;
@@ -102,6 +104,8 @@ BasicBlock *getDesignatedMergeBlock(Instruction *I) {
102
104
// Returns the continue block designated by I if I is an OpLoopMerge, nullptr
103
105
// otherwise.
104
106
BasicBlock *getDesignatedContinueBlock (Instruction *I) {
107
+ if (I == nullptr )
108
+ return nullptr ;
105
109
IntrinsicInst *II = dyn_cast<IntrinsicInst>(I);
106
110
if (II == nullptr )
107
111
return nullptr ;
@@ -447,55 +451,66 @@ class SPIRVStructurizer : public FunctionPass {
447
451
// clang-format on
448
452
std::vector<Edge>
449
453
createAliasBlocksForComplexEdges (std::vector<Edge> Edges) {
450
- std::unordered_map<BasicBlock *, BasicBlock *> Seen;
454
+ std::unordered_set< BasicBlock *> Seen;
451
455
std::vector<Edge> Output;
452
456
Output.reserve (Edges.size ());
453
457
454
458
for (auto &[Src, Dst] : Edges) {
455
- auto [iterator, inserted] = Seen.insert ({Src, Dst});
456
- if (inserted) {
457
- Output.emplace_back (Src, Dst);
458
- continue ;
459
+ auto [iterator, inserted] = Seen.insert (Src);
460
+ if (!inserted) {
461
+ // Src already a source node. Cannot have 2 edges from A to B.
462
+ // Creating alias source block.
463
+ BasicBlock *NewSrc =
464
+ BasicBlock::Create (F.getContext (), " new.src" , &F);
465
+ replaceBranchTargets (Src, Dst, NewSrc);
466
+ // replacePhiTargets(Dst, Src, NewSrc);
467
+ IRBuilder<> Builder (NewSrc);
468
+ Builder.CreateBr (Dst);
469
+ Src = NewSrc;
459
470
}
460
471
461
- // The exact same edge was already seen. Ignoring.
462
- if (iterator->second == Dst)
472
+ // Dst has a PHI node. We also need to create an alias output block.
473
+ if (!hasPhiNode (Dst)) {
474
+ Output.emplace_back (Src, Dst);
463
475
continue ;
476
+ }
464
477
465
- // The same Src block branches to 2 distinct blocks. This will be an
466
- // issue for the generated OpPhi. Creating alias block .
478
+ // Dst already targeted AND contains a PHI node. We'll need alias
479
+ // blocks .
467
480
BasicBlock *NewSrc =
468
- BasicBlock::Create (F.getContext (), " new.exit.src " , &F);
481
+ BasicBlock::Create (F.getContext (), " phi.alias " , &F);
469
482
replaceBranchTargets (Src, Dst, NewSrc);
470
- replacePhiTargets (Dst, Src, NewSrc);
471
-
483
+ // replacePhiTargets(Dst, Src, NewSrc);
472
484
IRBuilder<> Builder (NewSrc);
473
485
Builder.CreateBr (Dst);
474
-
475
- Seen.emplace (NewSrc, Dst);
476
- Output.emplace_back (NewSrc, Dst);
486
+ Output.emplace_back (Src, NewSrc);
477
487
}
478
488
479
489
return Output;
480
490
}
481
491
492
+ AllocaInst *CreateVariable (Function &F, Type *Type,
493
+ BasicBlock::iterator Position) {
494
+ const DataLayout &DL = F.getDataLayout ();
495
+ return new AllocaInst (Type, DL.getAllocaAddrSpace (), nullptr , " reg" ,
496
+ Position);
497
+ }
498
+
482
499
// Given a construct defined by |Header|, and a list of exiting edges
483
500
// |Edges|, creates a new single exit node, fixing up those edges.
484
501
BasicBlock *createSingleExitNode (BasicBlock *Header,
485
502
std::vector<Edge> &Edges) {
486
- auto NewExit = BasicBlock::Create (F.getContext (), " new.exit" , &F);
487
- IRBuilder<> ExitBuilder (NewExit);
488
-
489
- std::vector<BasicBlock *> Dsts;
490
- std::unordered_map<BasicBlock *, ConstantInt *> DstToIndex;
491
-
492
503
// Given 2 edges: Src1 -> Dst, Src2 -> Dst:
493
504
// If Dst has an PHI node, and Src1 and Src2 are both operands, both Src1
494
505
// and Src2 cannot be hidden by NewExit. Create 2 new nodes: Alias1,
495
506
// Alias2 to which NewExit will branch before going to Dst. Then, patchup
496
507
// Dst PHI node to look for Alias1 and Alias2.
497
508
std::vector<Edge> FixedEdges = createAliasBlocksForComplexEdges (Edges);
498
509
510
+ std::vector<BasicBlock *> Dsts;
511
+ std::unordered_map<BasicBlock *, ConstantInt *> DstToIndex;
512
+ auto NewExit = BasicBlock::Create (F.getContext (), " new.exit" , &F);
513
+ IRBuilder<> ExitBuilder (NewExit);
499
514
for (auto &[Src, Dst] : FixedEdges) {
500
515
if (DstToIndex.count (Dst) != 0 )
501
516
continue ;
@@ -506,33 +521,38 @@ class SPIRVStructurizer : public FunctionPass {
506
521
if (Dsts.size () == 1 ) {
507
522
for (auto &[Src, Dst] : FixedEdges) {
508
523
replaceBranchTargets (Src, Dst, NewExit);
509
- replacePhiTargets (Dst, Src, NewExit);
524
+ // replacePhiTargets(Dst, Src, NewExit);
510
525
}
511
526
ExitBuilder.CreateBr (Dsts[0 ]);
512
527
return NewExit;
513
528
}
514
529
515
- PHINode *PhiNode =
516
- ExitBuilder.CreatePHI (ExitBuilder.getInt32Ty (), FixedEdges.size ());
530
+ AllocaInst *Variable = CreateVariable (F, ExitBuilder.getInt32Ty (),
531
+ F.begin ()->getFirstInsertionPt ());
532
+ // PHINode *PhiNode = ExitBuilder.CreatePHI(ExitBuilder.getInt32Ty(),
533
+ // FixedEdges.size());
517
534
518
535
for (auto &[Src, Dst] : FixedEdges) {
519
- PhiNode->addIncoming (DstToIndex[Dst], Src);
536
+ IRBuilder<> B2 (Src);
537
+ B2.SetInsertPoint (Src->getFirstInsertionPt ());
538
+ B2.CreateStore (DstToIndex[Dst], Variable);
520
539
replaceBranchTargets (Src, Dst, NewExit);
521
- replacePhiTargets (Dst, Src, NewExit);
522
540
}
523
541
542
+ llvm::Value *Load =
543
+ ExitBuilder.CreateLoad (ExitBuilder.getInt32Ty (), Variable);
544
+
524
545
// If we can avoid an OpSwitch, generate an OpBranch. Reason is some
525
546
// OpBranch are allowed to exist without a new OpSelectionMerge if one of
526
547
// the branch is the parent's merge node, while OpSwitches are not.
527
548
if (Dsts.size () == 2 ) {
528
- Value *Condition = ExitBuilder. CreateCmp (CmpInst::ICMP_EQ,
529
- DstToIndex[Dsts[0 ]], PhiNode );
549
+ Value *Condition =
550
+ ExitBuilder. CreateCmp (CmpInst::ICMP_EQ, DstToIndex[Dsts[0 ]], Load );
530
551
ExitBuilder.CreateCondBr (Condition, Dsts[0 ], Dsts[1 ]);
531
552
return NewExit;
532
553
}
533
554
534
- SwitchInst *Sw =
535
- ExitBuilder.CreateSwitch (PhiNode, Dsts[0 ], Dsts.size () - 1 );
555
+ SwitchInst *Sw = ExitBuilder.CreateSwitch (Load, Dsts[0 ], Dsts.size () - 1 );
536
556
for (auto It = Dsts.begin () + 1 ; It != Dsts.end (); ++It) {
537
557
Sw->addCase (DstToIndex[*It], *It);
538
558
}
@@ -576,7 +596,7 @@ class SPIRVStructurizer : public FunctionPass {
576
596
577
597
// Creates a new basic block in F with a single OpUnreachable instruction.
578
598
BasicBlock *CreateUnreachable (Function &F) {
579
- BasicBlock *BB = BasicBlock::Create (F.getContext (), " new.exit " , &F);
599
+ BasicBlock *BB = BasicBlock::Create (F.getContext (), " unreachable " , &F);
580
600
IRBuilder<> Builder (BB);
581
601
Builder.CreateUnreachable ();
582
602
return BB;
@@ -1127,6 +1147,18 @@ class SPIRVStructurizer : public FunctionPass {
1127
1147
continue ;
1128
1148
1129
1149
Modified = true ;
1150
+
1151
+ if (Merge == nullptr ) {
1152
+ Merge = *successors (Header).begin ();
1153
+ IRBuilder<> Builder (Header);
1154
+ Builder.SetInsertPoint (Header->getTerminator ());
1155
+
1156
+ auto MergeAddress = BlockAddress::get (Merge->getParent (), Merge);
1157
+ SmallVector<Value *, 1 > Args = {MergeAddress};
1158
+ Builder.CreateIntrinsic (Intrinsic::spv_selection_merge, {}, {Args});
1159
+ continue ;
1160
+ }
1161
+
1130
1162
Instruction *SplitInstruction = Merge->getTerminator ();
1131
1163
if (isMergeInstruction (SplitInstruction->getPrevNode ()))
1132
1164
SplitInstruction = SplitInstruction->getPrevNode ();
0 commit comments