1 // Copyright (c) 2017 Google Inc.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 #include <gmock/gmock.h>
19 #include <unordered_set>
22 #include "../assembly_builder.h"
23 #include "../function_utils.h"
24 #include "../pass_fixture.h"
25 #include "../pass_utils.h"
27 #include "opt/iterator.h"
28 #include "opt/loop_descriptor.h"
30 #include "opt/tree_iterator.h"
34 using namespace spvtools;
35 using ::testing::UnorderedElementsAre;
37 using PassClassTest = PassTest<::testing::Test>;
40 Generated from the following GLSL
42 layout(location = 0) out vec4 c;
48 for (; j < 11; ++j) {}
49 for (; k < 12; ++k) {}
53 TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
54 const std::string text = R"(
56 %1 = OpExtInstImport "GLSL.std.450"
57 OpMemoryModel Logical GLSL450
58 OpEntryPoint Fragment %2 "main" %3
59 OpExecutionMode %2 OriginUpperLeft
66 OpDecorate %3 Location 0
68 %8 = OpTypeFunction %7
70 %10 = OpTypePointer Function %9
72 %12 = OpConstant %9 10
74 %14 = OpConstant %9 11
76 %16 = OpConstant %9 12
78 %18 = OpTypeVector %17 4
79 %19 = OpTypePointer Output %18
80 %3 = OpVariable %19 Output
81 %2 = OpFunction %7 None %8
83 %4 = OpVariable %10 Function
84 %5 = OpVariable %10 Function
85 %6 = OpVariable %10 Function
89 OpLoopMerge %22 %23 None
93 %26 = OpSLessThan %13 %25 %12
94 OpBranchConditional %26 %27 %22
100 OpLoopMerge %29 %30 None
104 %33 = OpSLessThan %13 %32 %14
105 OpBranchConditional %33 %34 %29
110 %36 = OpIAdd %9 %35 %15
116 OpLoopMerge %38 %39 None
120 %42 = OpSLessThan %13 %41 %16
121 OpBranchConditional %42 %43 %38
126 %45 = OpIAdd %9 %44 %15
133 %47 = OpIAdd %9 %46 %15
141 std::unique_ptr<ir::IRContext> context =
142 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
143 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
144 ir::Module* module = context->module();
145 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
146 << text << std::endl;
147 const ir::Function* f = spvtest::GetFunction(module, 2);
148 ir::LoopDescriptor& ld = *context->GetLoopDescriptor(f);
150 EXPECT_EQ(ld.NumLoops(), 3u);
152 // Invalid basic block id.
153 EXPECT_EQ(ld[0u], nullptr);
154 // Not a loop header.
155 EXPECT_EQ(ld[20], nullptr);
157 ir::Loop& parent_loop = *ld[21];
158 EXPECT_TRUE(parent_loop.HasNestedLoops());
159 EXPECT_FALSE(parent_loop.IsNested());
160 EXPECT_EQ(parent_loop.GetDepth(), 1u);
161 EXPECT_EQ(std::distance(parent_loop.begin(), parent_loop.end()), 2u);
162 EXPECT_EQ(parent_loop.GetHeaderBlock(), spvtest::GetBasicBlock(f, 21));
163 EXPECT_EQ(parent_loop.GetLatchBlock(), spvtest::GetBasicBlock(f, 23));
164 EXPECT_EQ(parent_loop.GetMergeBlock(), spvtest::GetBasicBlock(f, 22));
166 ir::Loop& child_loop_1 = *ld[28];
167 EXPECT_FALSE(child_loop_1.HasNestedLoops());
168 EXPECT_TRUE(child_loop_1.IsNested());
169 EXPECT_EQ(child_loop_1.GetDepth(), 2u);
170 EXPECT_EQ(std::distance(child_loop_1.begin(), child_loop_1.end()), 0u);
171 EXPECT_EQ(child_loop_1.GetHeaderBlock(), spvtest::GetBasicBlock(f, 28));
172 EXPECT_EQ(child_loop_1.GetLatchBlock(), spvtest::GetBasicBlock(f, 30));
173 EXPECT_EQ(child_loop_1.GetMergeBlock(), spvtest::GetBasicBlock(f, 29));
175 ir::Loop& child_loop_2 = *ld[37];
176 EXPECT_FALSE(child_loop_2.HasNestedLoops());
177 EXPECT_TRUE(child_loop_2.IsNested());
178 EXPECT_EQ(child_loop_2.GetDepth(), 2u);
179 EXPECT_EQ(std::distance(child_loop_2.begin(), child_loop_2.end()), 0u);
180 EXPECT_EQ(child_loop_2.GetHeaderBlock(), spvtest::GetBasicBlock(f, 37));
181 EXPECT_EQ(child_loop_2.GetLatchBlock(), spvtest::GetBasicBlock(f, 39));
182 EXPECT_EQ(child_loop_2.GetMergeBlock(), spvtest::GetBasicBlock(f, 38));
185 static void CheckLoopBlocks(ir::Loop* loop,
186 std::unordered_set<uint32_t>* expected_ids) {
187 SCOPED_TRACE("Check loop " + std::to_string(loop->GetHeaderBlock()->id()));
188 for (uint32_t bb_id : loop->GetBlocks()) {
189 EXPECT_EQ(expected_ids->count(bb_id), 1u);
190 expected_ids->erase(bb_id);
192 EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
193 EXPECT_EQ(expected_ids->size(), 0u);
197 Generated from the following GLSL
199 layout(location = 0) out vec4 c;
202 for (; i < 10; ++i) {
203 for (int j = 0; j < 11; ++j) {
205 for (int k = 0; k < 12; ++k) {}
208 for (int k = 0; k < 12; ++k) {}
212 TEST_F(PassClassTest, TripleNestedLoop) {
213 const std::string text = R"(
215 %1 = OpExtInstImport "GLSL.std.450"
216 OpMemoryModel Logical GLSL450
217 OpEntryPoint Fragment %2 "main" %3
218 OpExecutionMode %2 OriginUpperLeft
226 OpDecorate %3 Location 0
228 %9 = OpTypeFunction %8
230 %11 = OpTypePointer Function %10
231 %12 = OpConstant %10 0
232 %13 = OpConstant %10 10
234 %15 = OpConstant %10 11
235 %16 = OpConstant %10 5
236 %17 = OpConstant %10 12
237 %18 = OpConstant %10 1
239 %20 = OpTypeVector %19 4
240 %21 = OpTypePointer Output %20
241 %3 = OpVariable %21 Output
242 %2 = OpFunction %8 None %9
244 %4 = OpVariable %11 Function
245 %5 = OpVariable %11 Function
246 %6 = OpVariable %11 Function
247 %7 = OpVariable %11 Function
251 OpLoopMerge %24 %25 None
255 %28 = OpSLessThan %14 %27 %13
256 OpBranchConditional %28 %29 %24
261 OpLoopMerge %31 %32 None
265 %35 = OpSLessThan %14 %34 %15
266 OpBranchConditional %35 %36 %31
269 %38 = OpSLessThan %14 %37 %16
270 OpSelectionMerge %39 None
271 OpBranchConditional %38 %40 %39
276 OpLoopMerge %42 %43 None
280 %46 = OpSLessThan %14 %45 %17
281 OpBranchConditional %46 %47 %42
286 %49 = OpIAdd %10 %48 %18
295 OpLoopMerge %51 %52 None
299 %55 = OpSLessThan %14 %54 %17
300 OpBranchConditional %55 %56 %51
305 %58 = OpIAdd %10 %57 %18
312 %60 = OpIAdd %10 %59 %18
319 %62 = OpIAdd %10 %61 %18
327 std::unique_ptr<ir::IRContext> context =
328 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
329 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
330 ir::Module* module = context->module();
331 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
332 << text << std::endl;
333 const ir::Function* f = spvtest::GetFunction(module, 2);
334 ir::LoopDescriptor& ld = *context->GetLoopDescriptor(f);
336 EXPECT_EQ(ld.NumLoops(), 4u);
338 // Invalid basic block id.
339 EXPECT_EQ(ld[0u], nullptr);
341 EXPECT_EQ(ld[22], nullptr);
343 // Check that we can map basic block to the correct loop.
344 // The following block ids do not belong to a loop.
345 for (uint32_t bb_id : {22, 24}) EXPECT_EQ(ld[bb_id], nullptr);
348 std::unordered_set<uint32_t> basic_block_in_loop = {
349 {23, 26, 29, 30, 33, 36, 40, 41, 44, 47, 43,
350 42, 39, 50, 53, 56, 52, 51, 32, 31, 25}};
351 ir::Loop* loop = ld[23];
352 CheckLoopBlocks(loop, &basic_block_in_loop);
354 EXPECT_TRUE(loop->HasNestedLoops());
355 EXPECT_FALSE(loop->IsNested());
356 EXPECT_EQ(loop->GetDepth(), 1u);
357 EXPECT_EQ(std::distance(loop->begin(), loop->end()), 1u);
358 EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 22));
359 EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 23));
360 EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 25));
361 EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 24));
362 EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
363 EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
367 std::unordered_set<uint32_t> basic_block_in_loop = {
368 {30, 33, 36, 40, 41, 44, 47, 43, 42, 39, 50, 53, 56, 52, 51, 32}};
369 ir::Loop* loop = ld[30];
370 CheckLoopBlocks(loop, &basic_block_in_loop);
372 EXPECT_TRUE(loop->HasNestedLoops());
373 EXPECT_TRUE(loop->IsNested());
374 EXPECT_EQ(loop->GetDepth(), 2u);
375 EXPECT_EQ(std::distance(loop->begin(), loop->end()), 2u);
376 EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 29));
377 EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 30));
378 EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 32));
379 EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 31));
380 EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
381 EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
385 std::unordered_set<uint32_t> basic_block_in_loop = {{41, 44, 47, 43}};
386 ir::Loop* loop = ld[41];
387 CheckLoopBlocks(loop, &basic_block_in_loop);
389 EXPECT_FALSE(loop->HasNestedLoops());
390 EXPECT_TRUE(loop->IsNested());
391 EXPECT_EQ(loop->GetDepth(), 3u);
392 EXPECT_EQ(std::distance(loop->begin(), loop->end()), 0u);
393 EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 40));
394 EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 41));
395 EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 43));
396 EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 42));
397 EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
398 EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
402 std::unordered_set<uint32_t> basic_block_in_loop = {{50, 53, 56, 52}};
403 ir::Loop* loop = ld[50];
404 CheckLoopBlocks(loop, &basic_block_in_loop);
406 EXPECT_FALSE(loop->HasNestedLoops());
407 EXPECT_TRUE(loop->IsNested());
408 EXPECT_EQ(loop->GetDepth(), 3u);
409 EXPECT_EQ(std::distance(loop->begin(), loop->end()), 0u);
410 EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 39));
411 EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 50));
412 EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 52));
413 EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 51));
414 EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
415 EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
418 // Make sure LoopDescriptor gives us the inner most loop when we query for
420 for (const ir::BasicBlock& bb : *f) {
421 if (ir::Loop* loop = ld[&bb]) {
422 for (ir::Loop& sub_loop :
423 ir::make_range(++opt::TreeDFIterator<ir::Loop>(loop),
424 opt::TreeDFIterator<ir::Loop>())) {
425 EXPECT_FALSE(sub_loop.IsInsideLoop(bb.id()));
432 Generated from the following GLSL
434 layout(location = 0) out vec4 c;
436 for (int i = 0; i < 10; ++i) {
437 for (int j = 0; j < 11; ++j) {
438 for (int k = 0; k < 11; ++k) {}
440 for (int k = 0; k < 12; ++k) {}
444 TEST_F(PassClassTest, LoopParentTest) {
445 const std::string text = R"(
447 %1 = OpExtInstImport "GLSL.std.450"
448 OpMemoryModel Logical GLSL450
449 OpEntryPoint Fragment %2 "main" %3
450 OpExecutionMode %2 OriginUpperLeft
458 OpDecorate %3 Location 0
460 %9 = OpTypeFunction %8
462 %11 = OpTypePointer Function %10
463 %12 = OpConstant %10 0
464 %13 = OpConstant %10 10
466 %15 = OpConstant %10 11
467 %16 = OpConstant %10 1
468 %17 = OpConstant %10 12
470 %19 = OpTypeVector %18 4
471 %20 = OpTypePointer Output %19
472 %3 = OpVariable %20 Output
473 %2 = OpFunction %8 None %9
475 %4 = OpVariable %11 Function
476 %5 = OpVariable %11 Function
477 %6 = OpVariable %11 Function
478 %7 = OpVariable %11 Function
482 OpLoopMerge %23 %24 None
486 %27 = OpSLessThan %14 %26 %13
487 OpBranchConditional %27 %28 %23
492 OpLoopMerge %30 %31 None
496 %34 = OpSLessThan %14 %33 %15
497 OpBranchConditional %34 %35 %30
502 OpLoopMerge %37 %38 None
506 %41 = OpSLessThan %14 %40 %15
507 OpBranchConditional %41 %42 %37
512 %44 = OpIAdd %10 %43 %16
519 %46 = OpIAdd %10 %45 %16
526 OpLoopMerge %48 %49 None
530 %52 = OpSLessThan %14 %51 %17
531 OpBranchConditional %52 %53 %48
536 %55 = OpIAdd %10 %54 %16
543 %57 = OpIAdd %10 %56 %16
551 std::unique_ptr<ir::IRContext> context =
552 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
553 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
554 ir::Module* module = context->module();
555 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
556 << text << std::endl;
557 const ir::Function* f = spvtest::GetFunction(module, 2);
558 ir::LoopDescriptor& ld = *context->GetLoopDescriptor(f);
560 EXPECT_EQ(ld.NumLoops(), 4u);
563 ir::Loop& loop = *ld[22];
564 EXPECT_TRUE(loop.HasNestedLoops());
565 EXPECT_FALSE(loop.IsNested());
566 EXPECT_EQ(loop.GetDepth(), 1u);
567 EXPECT_EQ(loop.GetParent(), nullptr);
571 ir::Loop& loop = *ld[29];
572 EXPECT_TRUE(loop.HasNestedLoops());
573 EXPECT_TRUE(loop.IsNested());
574 EXPECT_EQ(loop.GetDepth(), 2u);
575 EXPECT_EQ(loop.GetParent(), ld[22]);
579 ir::Loop& loop = *ld[36];
580 EXPECT_FALSE(loop.HasNestedLoops());
581 EXPECT_TRUE(loop.IsNested());
582 EXPECT_EQ(loop.GetDepth(), 3u);
583 EXPECT_EQ(loop.GetParent(), ld[29]);
587 ir::Loop& loop = *ld[47];
588 EXPECT_FALSE(loop.HasNestedLoops());
589 EXPECT_TRUE(loop.IsNested());
590 EXPECT_EQ(loop.GetDepth(), 2u);
591 EXPECT_EQ(loop.GetParent(), ld[22]);