1 //===- SPIRVSortBlocksTests.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 "SPIRVUtils.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"
27 using namespace llvm::SPIRV
;
29 class SPIRVSortBlocksTest
: public testing::Test
{
31 void TearDown() override
{ M
.reset(); }
33 bool run(StringRef Assembly
) {
34 assert(M
== nullptr &&
35 "Calling runAnalysis multiple times is unsafe. See getAnalysis().");
38 M
= parseAssemblyString(Assembly
, Error
, Context
);
39 assert(M
&& "Bad assembly. Bad test?");
40 llvm::Function
*F
= M
->getFunction("main");
41 return sortBlocks(*F
);
44 void checkBasicBlockOrder(std::vector
<const char *> &&Expected
) {
45 llvm::Function
*F
= M
->getFunction("main");
47 for (const char *Name
: Expected
) {
48 ASSERT_TRUE(It
!= F
->end())
49 << "Expected block \"" << Name
50 << "\" but reached the end of the function instead.";
51 ASSERT_TRUE(It
->getName() == Name
)
52 << "Error: expected block \"" << Name
<< "\" got \"" << It
->getName()
56 ASSERT_TRUE(It
== F
->end())
57 << "No more blocks were expected, but function has more.";
62 std::unique_ptr
<Module
> M
;
65 TEST_F(SPIRVSortBlocksTest
, DefaultRegion
) {
66 StringRef Assembly
= R
"(
67 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
72 // No sorting is required.
73 EXPECT_FALSE(run(Assembly
));
76 TEST_F(SPIRVSortBlocksTest
, BasicBlockSwap
) {
77 StringRef Assembly
= R
"(
78 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
88 EXPECT_TRUE(run(Assembly
));
89 checkBasicBlockOrder({"entry", "middle", "exit"});
96 TEST_F(SPIRVSortBlocksTest
, SkipCondition
) {
97 StringRef Assembly
= R
"(
98 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
100 %1 = icmp ne i32 0, 0
101 br i1 %1, label %c, label %a
109 EXPECT_TRUE(run(Assembly
));
110 checkBasicBlockOrder({"entry", "a", "c"});
114 // entry -> header <-----------------+
115 // | `-> body -> continue -+
117 TEST_F(SPIRVSortBlocksTest
, LoopOrdering
) {
118 StringRef Assembly
= R
"(
119 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
121 %1 = icmp ne i32 0, 0
130 br i1 %1, label %body, label %end
134 EXPECT_TRUE(run(Assembly
));
135 checkBasicBlockOrder({"entry", "header", "end", "body", "continue"});
138 // Diamond condition:
143 // A and B order can be flipped with no effect, but it must be remain
144 // deterministic/stable.
145 TEST_F(SPIRVSortBlocksTest
, DiamondCondition
) {
146 StringRef Assembly
= R
"(
147 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
149 %1 = icmp ne i32 0, 0
150 br i1 %1, label %b, label %a
160 EXPECT_TRUE(run(Assembly
));
161 checkBasicBlockOrder({"entry", "a", "b", "c"});
164 // Crossing conditions:
167 // entry -+ +--_|_-+ +-> E
169 // +------+----> D -+
171 // A & B have the same rank.
172 // C & D have the same rank, but are after A & B.
173 // E if the last block.
174 TEST_F(SPIRVSortBlocksTest
, CrossingCondition
) {
175 StringRef Assembly
= R
"(
176 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
178 %1 = icmp ne i32 0, 0
179 br i1 %1, label %b, label %a
185 br i1 %1, label %d, label %c
189 br i1 %1, label %d, label %c
193 EXPECT_TRUE(run(Assembly
));
194 checkBasicBlockOrder({"entry", "a", "b", "c", "d", "e"});
210 // Order starts with Entry and A. Order of B and C can change, but must remain
212 // In such case, rank will be defined by the arbitrary traversal order. What's
213 // important is to have a stable value.
214 TEST_F(SPIRVSortBlocksTest
, IrreducibleOrdering
) {
215 StringRef Assembly
= R
"(
216 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
218 %1 = icmp ne i32 0, 0
222 br i1 %1, label %a, label %c
228 br i1 %1, label %b, label %c
233 EXPECT_TRUE(run(Assembly
));
234 checkBasicBlockOrder({"entry", "a", "b", "c"});
237 TEST_F(SPIRVSortBlocksTest
, IrreducibleOrderingBeforeReduction
) {
238 StringRef Assembly
= R
"(
239 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
241 %1 = icmp ne i32 0, 0
245 br i1 %1, label %e, label %d
251 br i1 %1, label %c, label %d
257 br i1 %1, label %b, label %c
262 EXPECT_TRUE(run(Assembly
));
263 checkBasicBlockOrder({"entry", "a", "b", "c", "d", "e"});
266 TEST_F(SPIRVSortBlocksTest
, LoopDiamond
) {
267 StringRef Assembly
= R
"(
268 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
270 %1 = icmp ne i32 0, 0
273 br i1 %1, label %body, label %end
275 br i1 %1, label %break, label %inside_a
279 br i1 %1, label %inside_d, label %inside_c
293 EXPECT_TRUE(run(Assembly
));
294 checkBasicBlockOrder({"entry", "header", "body", "inside_a", "inside_b",
295 "inside_c", "inside_d", "continue", "break", "end"});
298 TEST_F(SPIRVSortBlocksTest
, LoopNested
) {
299 StringRef Assembly
= R
"(
300 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
302 %1 = icmp ne i32 0, 0
305 br i1 %1, label %h, label %b
309 br i1 %1, label %d, label %e
323 EXPECT_TRUE(run(Assembly
));
324 checkBasicBlockOrder({"entry", "a", "b", "c", "e", "f", "d", "g", "h"});
327 TEST_F(SPIRVSortBlocksTest
, IfNested
) {
328 StringRef Assembly
= R
"(
329 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
331 br i1 true, label %d, label %a
337 br i1 true, label %b, label %c
339 br i1 true, label %f, label %e
345 br i1 true, label %h, label %g
354 EXPECT_TRUE(run(Assembly
));
355 checkBasicBlockOrder(
356 {"entry", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j"});
359 // Same as above, but this time blocks are already sorted, so no need to reorder
361 TEST_F(SPIRVSortBlocksTest
, IfNestedSorted
) {
362 StringRef Assembly
= R
"(
363 define void @main() convergent "hlsl
.numthreads
"="4,8,16" "hlsl
.shader
"="compute
" {
365 br i1 true, label %d, label %z
367 br i1 true, label %b, label %c
373 br i1 true, label %f, label %e
377 br i1 true, label %h, label %g
388 EXPECT_FALSE(run(Assembly
));