1 //===- SPIRVConvergenceRegionAnalysisTests.cpp ----------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "Analysis/SPIRVConvergenceRegionAnalysis.h"
10 #include "llvm/Analysis/DominanceFrontier.h"
11 #include "llvm/Analysis/PostDominators.h"
12 #include "llvm/AsmParser/Parser.h"
13 #include "llvm/IR/Instructions.h"
14 #include "llvm/IR/LLVMContext.h"
15 #include "llvm/IR/LegacyPassManager.h"
16 #include "llvm/IR/Module.h"
17 #include "llvm/IR/PassInstrumentation.h"
18 #include "llvm/IR/Type.h"
19 #include "llvm/IR/TypedPointerType.h"
20 #include "llvm/Support/SourceMgr.h"
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
26 using ::testing::Contains
;
27 using ::testing::Pair
;
30 using namespace llvm::SPIRV
;
32 template <typename T
> struct IsA
{
33 friend bool operator==(const Value
*V
, const IsA
&) { return isa
<T
>(V
); }
36 class SPIRVConvergenceRegionAnalysisTest
: public testing::Test
{
38 void SetUp() override
{
39 // Required for tests.
40 FAM
.registerPass([&] { return PassInstrumentationAnalysis(); });
41 MAM
.registerPass([&] { return PassInstrumentationAnalysis(); });
43 // Required for ConvergenceRegionAnalysis.
44 FAM
.registerPass([&] { return DominatorTreeAnalysis(); });
45 FAM
.registerPass([&] { return LoopAnalysis(); });
47 FAM
.registerPass([&] { return SPIRVConvergenceRegionAnalysis(); });
50 void TearDown() override
{ M
.reset(); }
52 SPIRVConvergenceRegionAnalysis::Result
&runAnalysis(StringRef Assembly
) {
53 assert(M
== nullptr &&
54 "Calling runAnalysis multiple times is unsafe. See getAnalysis().");
57 M
= parseAssemblyString(Assembly
, Error
, Context
);
58 assert(M
&& "Bad assembly. Bad test?");
59 auto *F
= getFunction();
61 ModulePassManager MPM
;
63 return FAM
.getResult
<SPIRVConvergenceRegionAnalysis
>(*F
);
66 SPIRVConvergenceRegionAnalysis::Result
&getAnalysis() {
67 assert(M
!= nullptr && "Has runAnalysis been called before?");
68 return FAM
.getResult
<SPIRVConvergenceRegionAnalysis
>(*getFunction());
71 Function
*getFunction() const {
72 assert(M
!= nullptr && "Has runAnalysis been called before?");
73 return M
->getFunction("main");
76 const BasicBlock
*getBlock(StringRef Name
) {
77 assert(M
!= nullptr && "Has runAnalysis been called before?");
79 auto *F
= getFunction();
80 for (BasicBlock
&BB
: *F
) {
81 if (BB
.getName() == Name
)
85 ADD_FAILURE() << "Error: Could not locate requested block. Bad test?";
89 const ConvergenceRegion
*getRegionWithEntry(StringRef Name
) {
90 assert(M
!= nullptr && "Has runAnalysis been called before?");
92 std::queue
<const ConvergenceRegion
*> ToProcess
;
93 ToProcess
.push(getAnalysis().getTopLevelRegion());
95 while (ToProcess
.size() != 0) {
96 auto *R
= ToProcess
.front();
98 for (auto *Child
: R
->Children
)
99 ToProcess
.push(Child
);
101 if (R
->Entry
->getName() == Name
)
105 ADD_FAILURE() << "Error: Could not locate requested region. Bad test?";
109 void checkRegionBlocks(const ConvergenceRegion
*R
,
110 std::initializer_list
<const char *> InRegion
,
111 std::initializer_list
<const char *> NotInRegion
) {
112 for (const char *Name
: InRegion
) {
113 EXPECT_TRUE(R
->contains(getBlock(Name
)))
114 << "error: " << Name
<< " not in region " << R
->Entry
->getName();
117 for (const char *Name
: NotInRegion
) {
118 EXPECT_FALSE(R
->contains(getBlock(Name
)))
119 << "error: " << Name
<< " in region " << R
->Entry
->getName();
125 FunctionAnalysisManager FAM
;
126 ModuleAnalysisManager MAM
;
127 std::unique_ptr
<Module
> M
;
130 MATCHER_P(ContainsBasicBlock
, label
, "") {
131 for (const auto *bb
: arg
)
132 if (bb
->getName() == label
)
137 TEST_F(SPIRVConvergenceRegionAnalysisTest
, DefaultRegion
) {
138 StringRef Assembly
= R
"(
139 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
144 const auto *CR
= runAnalysis(Assembly
).getTopLevelRegion();
146 EXPECT_EQ(CR
->Parent
, nullptr);
147 EXPECT_EQ(CR
->ConvergenceToken
, std::nullopt
);
148 EXPECT_EQ(CR
->Children
.size(), 0u);
151 TEST_F(SPIRVConvergenceRegionAnalysisTest
, DefaultRegionWithToken
) {
152 StringRef Assembly
= R
"(
153 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
154 %t1 = call token @llvm.experimental.convergence.entry()
158 declare token @llvm.experimental.convergence.entry()
161 const auto *CR
= runAnalysis(Assembly
).getTopLevelRegion();
163 EXPECT_EQ(CR
->Parent
, nullptr);
164 EXPECT_EQ(CR
->Children
.size(), 0u);
165 EXPECT_TRUE(CR
->ConvergenceToken
.has_value());
166 EXPECT_EQ(CR
->ConvergenceToken
.value()->getIntrinsicID(),
167 Intrinsic::experimental_convergence_entry
);
170 TEST_F(SPIRVConvergenceRegionAnalysisTest
, SingleLoopOneRegion
) {
171 StringRef Assembly
= R
"(
172 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
173 %t1 = call token @llvm.experimental.convergence.entry()
174 %1 = icmp ne i32 0, 0
178 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
179 br i1 %1, label %l1_body, label %l1_end
182 br label %l1_continue
194 declare token @llvm.experimental.convergence.entry()
195 declare token @llvm.experimental.convergence.control()
196 declare token @llvm.experimental.convergence.loop()
199 const auto *CR
= runAnalysis(Assembly
).getTopLevelRegion();
201 EXPECT_EQ(CR
->Parent
, nullptr);
202 EXPECT_EQ(CR
->ConvergenceToken
.value()->getName(), "t1");
203 EXPECT_TRUE(CR
->ConvergenceToken
.has_value());
204 EXPECT_EQ(CR
->ConvergenceToken
.value()->getIntrinsicID(),
205 Intrinsic::experimental_convergence_entry
);
206 EXPECT_EQ(CR
->Children
.size(), 1u);
209 TEST_F(SPIRVConvergenceRegionAnalysisTest
,
210 SingleLoopLoopRegionParentsIsTopLevelRegion
) {
211 StringRef Assembly
= R
"(
212 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
213 %t1 = call token @llvm.experimental.convergence.entry()
214 %1 = icmp ne i32 0, 0
218 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
219 br i1 %1, label %l1_body, label %l1_end
222 br label %l1_continue
234 declare token @llvm.experimental.convergence.entry()
235 declare token @llvm.experimental.convergence.control()
236 declare token @llvm.experimental.convergence.loop()
239 const auto *CR
= runAnalysis(Assembly
).getTopLevelRegion();
241 EXPECT_EQ(CR
->Parent
, nullptr);
242 EXPECT_EQ(CR
->ConvergenceToken
.value()->getName(), "t1");
243 EXPECT_EQ(CR
->Children
[0]->Parent
, CR
);
244 EXPECT_EQ(CR
->Children
[0]->ConvergenceToken
.value()->getName(), "tl1");
247 TEST_F(SPIRVConvergenceRegionAnalysisTest
, SingleLoopExits
) {
248 StringRef Assembly
= R
"(
249 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
250 %t1 = call token @llvm.experimental.convergence.entry()
251 %1 = icmp ne i32 0, 0
255 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
256 br i1 %1, label %l1_body, label %l1_end
259 br label %l1_continue
271 declare token @llvm.experimental.convergence.entry()
272 declare token @llvm.experimental.convergence.control()
273 declare token @llvm.experimental.convergence.loop()
276 const auto *CR
= runAnalysis(Assembly
).getTopLevelRegion();
277 const auto *L
= CR
->Children
[0];
279 EXPECT_EQ(L
->Exits
.size(), 1ul);
280 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1"));
283 TEST_F(SPIRVConvergenceRegionAnalysisTest
, SingleLoopWithBreakExits
) {
284 StringRef Assembly
= R
"(
285 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
286 %t1 = call token @llvm.experimental.convergence.entry()
287 %1 = icmp ne i32 0, 0
291 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
292 br i1 %1, label %l1_body, label %end.loopexit
295 %2 = icmp ne i32 0, 0
296 br i1 %2, label %l1_condition_true, label %l1_condition_false
299 %call = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl
"(token %tl1) ]
303 br label %l1_continue
315 declare token @llvm.experimental.convergence.entry()
316 declare token @llvm.experimental.convergence.control()
317 declare token @llvm.experimental.convergence.loop()
319 ; This intrinsic is not convergent. This is only because the backend doesn't
320 ; support convergent operations yet.
321 declare spir_func i32 @_Z3absi(i32) convergent
324 const auto *CR
= runAnalysis(Assembly
).getTopLevelRegion();
325 const auto *L
= CR
->Children
[0];
327 EXPECT_EQ(L
->Exits
.size(), 2ul);
328 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_header"));
329 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_condition_true"));
331 EXPECT_TRUE(CR
->contains(getBlock("l1_header")));
332 EXPECT_TRUE(CR
->contains(getBlock("l1_condition_true")));
335 TEST_F(SPIRVConvergenceRegionAnalysisTest
, SingleLoopWithBreakRegionBlocks
) {
336 StringRef Assembly
= R
"(
337 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
338 %t1 = call token @llvm.experimental.convergence.entry()
339 %1 = icmp ne i32 0, 0
343 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
344 br i1 %1, label %l1_body, label %end.loopexit
347 %2 = icmp ne i32 0, 0
348 br i1 %2, label %l1_condition_true, label %l1_condition_false
351 %call = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl
"(token %tl1) ]
355 br label %l1_continue
367 declare token @llvm.experimental.convergence.entry()
368 declare token @llvm.experimental.convergence.control()
369 declare token @llvm.experimental.convergence.loop()
371 ; This intrinsic is not convergent. This is only because the backend doesn't
372 ; support convergent operations yet.
373 declare spir_func i32 @_Z3absi(i32) convergent
376 const auto *CR
= runAnalysis(Assembly
).getTopLevelRegion();
377 const auto *L
= CR
->Children
[0];
379 EXPECT_TRUE(CR
->contains(getBlock("l1_header")));
380 EXPECT_TRUE(L
->contains(getBlock("l1_header")));
382 EXPECT_TRUE(CR
->contains(getBlock("l1_body")));
383 EXPECT_TRUE(L
->contains(getBlock("l1_body")));
385 EXPECT_TRUE(CR
->contains(getBlock("l1_condition_true")));
386 EXPECT_TRUE(L
->contains(getBlock("l1_condition_true")));
388 EXPECT_TRUE(CR
->contains(getBlock("l1_condition_false")));
389 EXPECT_TRUE(L
->contains(getBlock("l1_condition_false")));
391 EXPECT_TRUE(CR
->contains(getBlock("l1_continue")));
392 EXPECT_TRUE(L
->contains(getBlock("l1_continue")));
394 EXPECT_TRUE(CR
->contains(getBlock("end.loopexit")));
395 EXPECT_FALSE(L
->contains(getBlock("end.loopexit")));
397 EXPECT_TRUE(CR
->contains(getBlock("end")));
398 EXPECT_FALSE(L
->contains(getBlock("end")));
401 // Exact same test as before, except the 'if() break' condition in the loop is
402 // not marked with any convergence intrinsic. In such case, it is valid to
403 // consider it outside of the loop.
404 TEST_F(SPIRVConvergenceRegionAnalysisTest
,
405 SingleLoopWithBreakNoConvergenceControl
) {
406 StringRef Assembly
= R
"(
407 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
408 %t1 = call token @llvm.experimental.convergence.entry()
409 %1 = icmp ne i32 0, 0
413 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
414 br i1 %1, label %l1_body, label %end.loopexit
417 %2 = icmp ne i32 0, 0
418 br i1 %2, label %l1_condition_true, label %l1_condition_false
424 br label %l1_continue
436 declare token @llvm.experimental.convergence.entry()
437 declare token @llvm.experimental.convergence.control()
438 declare token @llvm.experimental.convergence.loop()
441 runAnalysis(Assembly
);
442 const auto *L
= getRegionWithEntry("l1_header");
444 EXPECT_EQ(L
->Entry
->getName(), "l1_header");
445 EXPECT_EQ(L
->Exits
.size(), 2ul);
446 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_header"));
447 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_body"));
449 EXPECT_TRUE(L
->contains(getBlock("l1_header")));
450 EXPECT_TRUE(L
->contains(getBlock("l1_body")));
451 EXPECT_FALSE(L
->contains(getBlock("l1_condition_true")));
452 EXPECT_TRUE(L
->contains(getBlock("l1_condition_false")));
453 EXPECT_TRUE(L
->contains(getBlock("l1_continue")));
454 EXPECT_FALSE(L
->contains(getBlock("end.loopexit")));
455 EXPECT_FALSE(L
->contains(getBlock("end")));
458 TEST_F(SPIRVConvergenceRegionAnalysisTest
, TwoLoopsWithControl
) {
459 StringRef Assembly
= R
"(
460 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
461 %t1 = call token @llvm.experimental.convergence.entry()
462 %1 = icmp ne i32 0, 0
466 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
467 br i1 %1, label %l1_body, label %l1_exit
470 br i1 %1, label %l1_condition_true, label %l1_condition_false
476 br label %l1_continue
488 %tl2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
489 br i1 %1, label %l2_body, label %l2_exit
492 br i1 %1, label %l2_condition_true, label %l2_condition_false
498 br label %l2_continue
510 declare token @llvm.experimental.convergence.entry()
511 declare token @llvm.experimental.convergence.control()
512 declare token @llvm.experimental.convergence.loop()
515 runAnalysis(Assembly
);
518 const auto *L
= getRegionWithEntry("l1_header");
520 EXPECT_EQ(L
->Entry
->getName(), "l1_header");
521 EXPECT_EQ(L
->Exits
.size(), 2ul);
522 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_header"));
523 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_body"));
526 L
, {"l1_header", "l1_body", "l1_condition_false", "l1_continue"},
527 {"", "l2_header", "l2_body", "l2_condition_true", "l2_condition_false",
528 "l2_continue", "l2_exit", "l1_condition_true", "l1_exit", "end"});
531 const auto *L
= getRegionWithEntry("l2_header");
533 EXPECT_EQ(L
->Entry
->getName(), "l2_header");
534 EXPECT_EQ(L
->Exits
.size(), 2ul);
535 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l2_header"));
536 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l2_body"));
539 L
, {"l2_header", "l2_body", "l2_condition_false", "l2_continue"},
540 {"", "l1_header", "l1_body", "l1_condition_true", "l1_condition_false",
541 "l1_continue", "l1_exit", "l2_condition_true", "l2_exit", "end"});
545 // Both branches in the loop condition break. This means the loop continue
546 // targets are unreachable, meaning no reachable back-edge. This should
547 // transform the loop condition into a simple condition, meaning we have a
548 // single convergence region.
549 TEST_F(SPIRVConvergenceRegionAnalysisTest
, LoopBothBranchExits
) {
550 StringRef Assembly
= R
"(
551 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
552 %t1 = call token @llvm.experimental.convergence.entry()
553 %1 = icmp ne i32 0, 0
557 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
558 br i1 %1, label %l1_body, label %l1_exit
561 br i1 %1, label %l1_condition_true, label %l1_condition_false
564 %call_true = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl
"(token %tl1) ]
568 %call_false = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl
"(token %tl1) ]
581 declare token @llvm.experimental.convergence.entry()
582 declare token @llvm.experimental.convergence.control()
583 declare token @llvm.experimental.convergence.loop()
585 ; This intrinsic is not convergent. This is only because the backend doesn't
586 ; support convergent operations yet.
587 declare spir_func i32 @_Z3absi(i32) convergent
591 const auto *R
= runAnalysis(Assembly
).getTopLevelRegion();
593 ASSERT_EQ(R
->Children
.size(), 0ul);
594 EXPECT_EQ(R
->Exits
.size(), 1ul);
595 EXPECT_THAT(R
->Exits
, ContainsBasicBlock("end"));
598 TEST_F(SPIRVConvergenceRegionAnalysisTest
, InnerLoopBreaks
) {
599 StringRef Assembly
= R
"(
600 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
601 %t1 = call token @llvm.experimental.convergence.entry()
602 %1 = icmp ne i32 0, 0
606 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
607 br i1 %1, label %l1_body, label %l1_exit
613 %tl2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %tl1) ]
614 br i1 %1, label %l2_body, label %l2_exit
617 br i1 %1, label %l2_condition_true, label %l2_condition_false
620 %call_true = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl
"(token %tl1) ]
624 br label %l2_continue
630 br label %l1_continue
642 declare token @llvm.experimental.convergence.entry()
643 declare token @llvm.experimental.convergence.control()
644 declare token @llvm.experimental.convergence.loop()
646 ; This intrinsic is not convergent. This is only because the backend doesn't
647 ; support convergent operations yet.
648 declare spir_func i32 @_Z3absi(i32) convergent
651 const auto *R
= runAnalysis(Assembly
).getTopLevelRegion();
652 const auto *L1
= getRegionWithEntry("l1_header");
653 const auto *L2
= getRegionWithEntry("l2_header");
655 EXPECT_EQ(R
->Children
.size(), 1ul);
656 EXPECT_EQ(L1
->Children
.size(), 1ul);
657 EXPECT_EQ(L1
->Parent
, R
);
658 EXPECT_EQ(L2
->Parent
, L1
);
660 EXPECT_EQ(R
->Entry
->getName(), "");
661 EXPECT_EQ(R
->Exits
.size(), 1ul);
662 EXPECT_THAT(R
->Exits
, ContainsBasicBlock("end"));
664 EXPECT_EQ(L1
->Entry
->getName(), "l1_header");
665 EXPECT_EQ(L1
->Exits
.size(), 2ul);
666 EXPECT_THAT(L1
->Exits
, ContainsBasicBlock("l1_header"));
667 EXPECT_THAT(L1
->Exits
, ContainsBasicBlock("l2_condition_true"));
669 checkRegionBlocks(L1
,
670 {"l1_header", "l1_body", "l2_header", "l2_body",
671 "l2_condition_false", "l2_condition_true", "l2_continue",
672 "l2_exit", "l1_continue"},
673 {"", "l1_exit", "end"});
675 EXPECT_EQ(L2
->Entry
->getName(), "l2_header");
676 EXPECT_EQ(L2
->Exits
.size(), 2ul);
677 EXPECT_THAT(L2
->Exits
, ContainsBasicBlock("l2_header"));
678 EXPECT_THAT(L2
->Exits
, ContainsBasicBlock("l2_body"));
680 L2
, {"l2_header", "l2_body", "l2_condition_false", "l2_continue"},
681 {"", "l1_header", "l1_body", "l2_exit", "l1_continue",
682 "l2_condition_true", "l1_exit", "end"});
685 TEST_F(SPIRVConvergenceRegionAnalysisTest
, SingleLoopMultipleExits
) {
686 StringRef Assembly
= R
"(
687 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
688 %t1 = call token @llvm.experimental.convergence.entry()
689 %cond = icmp ne i32 0, 0
693 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
694 br i1 %cond, label %l1_body, label %l1_exit
697 switch i32 0, label %sw.default.exit [
713 br label %l1_continue
731 declare token @llvm.experimental.convergence.entry()
732 declare token @llvm.experimental.convergence.control()
733 declare token @llvm.experimental.convergence.loop()
736 runAnalysis(Assembly
).getTopLevelRegion();
737 const auto *L
= getRegionWithEntry("l1");
738 ASSERT_NE(L
, nullptr);
740 EXPECT_EQ(L
->Entry
, getBlock("l1"));
741 EXPECT_EQ(L
->Exits
.size(), 2ul);
742 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1"));
743 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_body"));
745 checkRegionBlocks(L
, {"l1", "l1_body", "l1_continue", "sw.bb1"},
746 {"", "sw.default.exit", "sw.default", "l1_end", "end",
747 "sw.bb", "sw.bb2", "l1_exit"});
750 TEST_F(SPIRVConvergenceRegionAnalysisTest
,
751 SingleLoopMultipleExitsWithPartialConvergence
) {
752 StringRef Assembly
= R
"(
753 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
754 %t1 = call token @llvm.experimental.convergence.entry()
755 %cond = icmp ne i32 0, 0
759 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
760 br i1 %cond, label %l1_body, label %l1_exit
763 switch i32 0, label %sw.default.exit [
773 %call = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl
"(token %tl1) ]
780 br label %l1_continue
798 declare token @llvm.experimental.convergence.entry()
799 declare token @llvm.experimental.convergence.control()
800 declare token @llvm.experimental.convergence.loop()
802 ; This intrinsic is not convergent. This is only because the backend doesn't
803 ; support convergent operations yet.
804 declare spir_func i32 @_Z3absi(i32) convergent
807 runAnalysis(Assembly
).getTopLevelRegion();
808 const auto *L
= getRegionWithEntry("l1");
809 ASSERT_NE(L
, nullptr);
811 EXPECT_EQ(L
->Entry
, getBlock("l1"));
812 EXPECT_EQ(L
->Exits
.size(), 3ul);
813 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1"));
814 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_body"));
815 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("sw.default"));
818 {"l1", "l1_body", "l1_continue", "sw.bb1",
819 "sw.default.exit", "sw.bb2", "sw.default"},
820 {"", "l1_end", "end", "sw.bb", "l1_exit"});
823 TEST_F(SPIRVConvergenceRegionAnalysisTest
,
824 SingleLoopWithDeepConvergenceBranch
) {
825 StringRef Assembly
= R
"(
826 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
827 %t1 = call token @llvm.experimental.convergence.entry()
828 %1 = icmp ne i32 0, 0
832 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
833 br i1 %1, label %l1_body, label %l1_end
836 %2 = icmp ne i32 0, 0
837 br i1 %2, label %l1_condition_true, label %l1_condition_false
849 %call = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl
"(token %tl1) ]
853 br label %l1_continue
865 declare token @llvm.experimental.convergence.entry()
866 declare token @llvm.experimental.convergence.control()
867 declare token @llvm.experimental.convergence.loop()
869 ; This intrinsic is not convergent. This is only because the backend doesn't
870 ; support convergent operations yet.
871 declare spir_func i32 @_Z3absi(i32) convergent
874 runAnalysis(Assembly
).getTopLevelRegion();
875 const auto *L
= getRegionWithEntry("l1_header");
876 ASSERT_NE(L
, nullptr);
878 EXPECT_EQ(L
->Entry
, getBlock("l1_header"));
879 EXPECT_EQ(L
->Exits
.size(), 2ul);
880 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_header"));
881 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("c"));
884 {"l1_header", "l1_body", "l1_continue",
885 "l1_condition_false", "l1_condition_true", "a", "b", "c"},
886 {"", "l1_end", "end"});
889 TEST_F(SPIRVConvergenceRegionAnalysisTest
,
890 SingleLoopWithDeepConvergenceLateBranch
) {
891 StringRef Assembly
= R
"(
892 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
893 %t1 = call token @llvm.experimental.convergence.entry()
894 %1 = icmp ne i32 0, 0
898 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
899 br i1 %1, label %l1_body, label %l1_end
902 %2 = icmp ne i32 0, 0
903 br i1 %2, label %l1_condition_true, label %l1_condition_false
912 br i1 %2, label %c, label %d
915 %call = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl
"(token %tl1) ]
922 br label %l1_continue
934 declare token @llvm.experimental.convergence.entry()
935 declare token @llvm.experimental.convergence.control()
936 declare token @llvm.experimental.convergence.loop()
938 ; This intrinsic is not convergent. This is only because the backend doesn't
939 ; support convergent operations yet.
940 declare spir_func i32 @_Z3absi(i32) convergent
943 runAnalysis(Assembly
).getTopLevelRegion();
944 const auto *L
= getRegionWithEntry("l1_header");
945 ASSERT_NE(L
, nullptr);
947 EXPECT_EQ(L
->Entry
, getBlock("l1_header"));
948 EXPECT_EQ(L
->Exits
.size(), 3ul);
949 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_header"));
950 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("b"));
951 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("c"));
954 {"l1_header", "l1_body", "l1_continue",
955 "l1_condition_false", "l1_condition_true", "a", "b", "c"},
956 {"", "l1_end", "end", "d"});
959 TEST_F(SPIRVConvergenceRegionAnalysisTest
,
960 SingleLoopWithNoConvergenceIntrinsics
) {
961 StringRef Assembly
= R
"(
962 define void @main() "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
963 %1 = icmp ne i32 0, 0
967 br i1 %1, label %l1_body, label %l1_end
970 %2 = icmp ne i32 0, 0
971 br i1 %2, label %l1_condition_true, label %l1_condition_false
980 br label %l1_continue
993 runAnalysis(Assembly
).getTopLevelRegion();
994 const auto *L
= getRegionWithEntry("l1_header");
995 ASSERT_NE(L
, nullptr);
997 EXPECT_EQ(L
->Entry
, getBlock("l1_header"));
998 EXPECT_EQ(L
->Exits
.size(), 2ul);
999 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_header"));
1000 EXPECT_THAT(L
->Exits
, ContainsBasicBlock("l1_body"));
1003 L
, {"l1_header", "l1_body", "l1_continue", "l1_condition_false"},
1004 {"", "l1_end", "end", "l1_condition_true", "a"});
1007 TEST_F(SPIRVConvergenceRegionAnalysisTest
, SimpleFunction
) {
1008 StringRef Assembly
= R
"(
1009 define void @main() "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
1014 const auto *R
= runAnalysis(Assembly
).getTopLevelRegion();
1015 ASSERT_NE(R
, nullptr);
1017 EXPECT_EQ(R
->Entry
, getBlock(""));
1018 EXPECT_EQ(R
->Exits
.size(), 1ul);
1019 EXPECT_THAT(R
->Exits
, ContainsBasicBlock(""));
1020 EXPECT_TRUE(R
->contains(getBlock("")));
1023 TEST_F(SPIRVConvergenceRegionAnalysisTest
, NestedLoopInBreak
) {
1024 StringRef Assembly
= R
"(
1025 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
1026 %t1 = call token @llvm.experimental.convergence.entry()
1027 %1 = icmp ne i32 0, 0
1031 %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %t1) ]
1032 br i1 %1, label %l1_body, label %l1_to_end
1035 br i1 %1, label %cond_inner, label %l1_continue
1041 %tl2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl
"(token %tl1) ]
1042 br i1 %1, label %l2_body, label %l2_end
1045 %call = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl
"(token %tl2) ]
1046 br label %l2_continue
1055 %call2 = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl
"(token %tl1) ]
1071 declare token @llvm.experimental.convergence.entry()
1072 declare token @llvm.experimental.convergence.control()
1073 declare token @llvm.experimental.convergence.loop()
1074 declare spir_func i32 @_Z3absi(i32) convergent
1077 const auto *R
= runAnalysis(Assembly
).getTopLevelRegion();
1078 ASSERT_NE(R
, nullptr);
1080 EXPECT_EQ(R
->Children
.size(), 1ul);
1082 const auto *L1
= R
->Children
[0];
1083 EXPECT_EQ(L1
->Children
.size(), 1ul);
1084 EXPECT_EQ(L1
->Entry
->getName(), "l1");
1085 EXPECT_EQ(L1
->Exits
.size(), 2ul);
1086 EXPECT_THAT(L1
->Exits
, ContainsBasicBlock("l1"));
1087 EXPECT_THAT(L1
->Exits
, ContainsBasicBlock("l2_exit"));
1088 checkRegionBlocks(L1
,
1089 {"l1", "l1_body", "l1_continue", "cond_inner", "l2",
1090 "l2_body", "l2_end", "l2_continue", "l2_exit"},
1091 {"", "l1_to_end", "l1_end", "end"});
1093 const auto *L2
= L1
->Children
[0];
1094 EXPECT_EQ(L2
->Children
.size(), 0ul);
1095 EXPECT_EQ(L2
->Entry
->getName(), "l2");
1096 EXPECT_EQ(L2
->Exits
.size(), 1ul);
1097 EXPECT_THAT(L2
->Exits
, ContainsBasicBlock("l2"));
1098 checkRegionBlocks(L2
, {"l2", "l2_body", "l2_continue"},
1099 {"", "l1_to_end", "l1_end", "end", "l1", "l1_body",
1100 "l1_continue", "cond_inner", "l2_end", "l2_exit"});