1 // Copyright (c) 2020 Vasyl Teliman
2 //
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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
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.
14
15 #include "source/fuzz/transformation_permute_phi_operands.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "test/fuzz/fuzz_test_util.h"
20
21 namespace spvtools {
22 namespace fuzz {
23 namespace {
24
TEST(TransformationPermutePhiOperandsTest,BasicTest)25 TEST(TransformationPermutePhiOperandsTest, BasicTest) {
26 std::string shader = R"(
27 OpCapability Shader
28 %1 = OpExtInstImport "GLSL.std.450"
29 OpMemoryModel Logical GLSL450
30 OpEntryPoint Fragment %4 "main"
31 OpExecutionMode %4 OriginUpperLeft
32 OpSource ESSL 310
33 %2 = OpTypeVoid
34 %3 = OpTypeFunction %2
35 %6 = OpTypeInt 32 1
36 %7 = OpTypePointer Function %6
37 %9 = OpConstant %6 0
38 %11 = OpConstant %6 1
39 %14 = OpTypeBool
40 %4 = OpFunction %2 None %3
41 %5 = OpLabel
42 %8 = OpVariable %7 Function
43 %10 = OpVariable %7 Function
44 OpStore %8 %9
45 OpStore %10 %11
46 %12 = OpLoad %6 %8
47 %13 = OpLoad %6 %10
48 %15 = OpSLessThan %14 %12 %13
49 OpSelectionMerge %17 None
50 OpBranchConditional %15 %16 %21
51 %16 = OpLabel
52 %18 = OpLoad %6 %10
53 %19 = OpLoad %6 %8
54 %20 = OpIAdd %6 %19 %18
55 OpBranch %17
56 %21 = OpLabel
57 %22 = OpLoad %6 %10
58 %23 = OpLoad %6 %8
59 %24 = OpISub %6 %23 %22
60 OpBranch %17
61 %17 = OpLabel
62 %25 = OpPhi %6 %20 %16 %24 %21
63 %30 = OpIAdd %6 %25 %25
64 OpStore %8 %25
65 OpReturn
66 OpFunctionEnd
67 )";
68
69 const auto env = SPV_ENV_UNIVERSAL_1_3;
70 const auto consumer = nullptr;
71 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
72 spvtools::ValidatorOptions validator_options;
73 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
74 kConsoleMessageConsumer));
75 TransformationContext transformation_context(
76 MakeUnique<FactManager>(context.get()), validator_options);
77 // Result id is invalid.
78 ASSERT_FALSE(TransformationPermutePhiOperands(26, {}).IsApplicable(
79 context.get(), transformation_context));
80
81 // Result id is not of an OpPhi instruction.
82 ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable(
83 context.get(), transformation_context));
84
85 // Result id is not of an OpPhi instruction.
86 ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable(
87 context.get(), transformation_context));
88
89 // Permutation has invalid size.
90 ASSERT_FALSE(TransformationPermutePhiOperands(25, {0, 1, 2})
91 .IsApplicable(context.get(), transformation_context));
92
93 #ifndef NDEBUG
94 // Permutation has duplicates.
95 ASSERT_DEATH(TransformationPermutePhiOperands(25, {0, 0})
96 .IsApplicable(context.get(), transformation_context),
97 "Permutation has duplicates");
98 #endif
99
100 // Permutation's values are not in range.
101 ASSERT_FALSE(TransformationPermutePhiOperands(25, {1, 2})
102 .IsApplicable(context.get(), transformation_context));
103
104 TransformationPermutePhiOperands transformation(25, {1, 0});
105 ASSERT_TRUE(
106 transformation.IsApplicable(context.get(), transformation_context));
107 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
108
109 // Check that the def-use manager knows that the phi instruction's ids have
110 // been permuted.
111 std::vector<std::pair<uint32_t, uint32_t>> phi_operand_to_new_operand_index =
112 {{20, 4}, {16, 5}, {24, 2}, {21, 3}};
113 for (std::pair<uint32_t, uint32_t>& entry :
114 phi_operand_to_new_operand_index) {
115 context->get_def_use_mgr()->WhileEachUse(
116 entry.first,
117 [&entry](opt::Instruction* inst, uint32_t operand_index) -> bool {
118 if (inst->result_id() == 25) {
119 EXPECT_EQ(entry.second, operand_index);
120 return false;
121 }
122 return true;
123 });
124 }
125 bool found_use_in_store = false;
126 bool found_use_in_add_lhs = false;
127 bool found_use_in_add_rhs = false;
128 context->get_def_use_mgr()->ForEachUse(
129 25, [&found_use_in_store, &found_use_in_add_lhs, &found_use_in_add_rhs](
130 opt::Instruction* inst, uint32_t operand_index) {
131 if (inst->opcode() == SpvOpStore) {
132 ASSERT_FALSE(found_use_in_store);
133 found_use_in_store = true;
134 } else {
135 ASSERT_EQ(SpvOpIAdd, inst->opcode());
136 if (operand_index == 2) {
137 ASSERT_FALSE(found_use_in_add_lhs);
138 found_use_in_add_lhs = true;
139 } else {
140 ASSERT_EQ(3, operand_index);
141 ASSERT_FALSE(found_use_in_add_rhs);
142 found_use_in_add_rhs = true;
143 }
144 }
145 });
146 ASSERT_TRUE(found_use_in_store);
147 ASSERT_TRUE(found_use_in_add_lhs);
148 ASSERT_TRUE(found_use_in_add_rhs);
149
150 std::string after_transformation = R"(
151 OpCapability Shader
152 %1 = OpExtInstImport "GLSL.std.450"
153 OpMemoryModel Logical GLSL450
154 OpEntryPoint Fragment %4 "main"
155 OpExecutionMode %4 OriginUpperLeft
156 OpSource ESSL 310
157 %2 = OpTypeVoid
158 %3 = OpTypeFunction %2
159 %6 = OpTypeInt 32 1
160 %7 = OpTypePointer Function %6
161 %9 = OpConstant %6 0
162 %11 = OpConstant %6 1
163 %14 = OpTypeBool
164 %4 = OpFunction %2 None %3
165 %5 = OpLabel
166 %8 = OpVariable %7 Function
167 %10 = OpVariable %7 Function
168 OpStore %8 %9
169 OpStore %10 %11
170 %12 = OpLoad %6 %8
171 %13 = OpLoad %6 %10
172 %15 = OpSLessThan %14 %12 %13
173 OpSelectionMerge %17 None
174 OpBranchConditional %15 %16 %21
175 %16 = OpLabel
176 %18 = OpLoad %6 %10
177 %19 = OpLoad %6 %8
178 %20 = OpIAdd %6 %19 %18
179 OpBranch %17
180 %21 = OpLabel
181 %22 = OpLoad %6 %10
182 %23 = OpLoad %6 %8
183 %24 = OpISub %6 %23 %22
184 OpBranch %17
185 %17 = OpLabel
186 %25 = OpPhi %6 %24 %21 %20 %16
187 %30 = OpIAdd %6 %25 %25
188 OpStore %8 %25
189 OpReturn
190 OpFunctionEnd
191 )";
192
193 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
194 }
195
196 } // namespace
197 } // namespace fuzz
198 } // namespace spvtools
199