1fd4e5da5Sopenharmony_ci// Copyright (c) 2018 Google LLC
2fd4e5da5Sopenharmony_ci//
3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License.
5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at
6fd4e5da5Sopenharmony_ci//
7fd4e5da5Sopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
8fd4e5da5Sopenharmony_ci//
9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and
13fd4e5da5Sopenharmony_ci// limitations under the License.
14fd4e5da5Sopenharmony_ci
15fd4e5da5Sopenharmony_ci#include "source/reduce/reducer.h"
16fd4e5da5Sopenharmony_ci#include "source/reduce/reduction_opportunity.h"
17fd4e5da5Sopenharmony_ci#include "source/reduce/remove_instruction_reduction_opportunity.h"
18fd4e5da5Sopenharmony_ci#include "test/reduce/reduce_test_util.h"
19fd4e5da5Sopenharmony_ci
20fd4e5da5Sopenharmony_cinamespace spvtools {
21fd4e5da5Sopenharmony_cinamespace reduce {
22fd4e5da5Sopenharmony_cinamespace {
23fd4e5da5Sopenharmony_ci
24fd4e5da5Sopenharmony_ciusing opt::Function;
25fd4e5da5Sopenharmony_ciusing opt::IRContext;
26fd4e5da5Sopenharmony_ciusing opt::Instruction;
27fd4e5da5Sopenharmony_ci
28fd4e5da5Sopenharmony_ci// A reduction opportunity finder that finds opportunities to remove global
29fd4e5da5Sopenharmony_ci// values regardless of whether they are referenced. This is very likely to make
30fd4e5da5Sopenharmony_ci// the resulting module invalid.  We use this to test the reducer's behavior in
31fd4e5da5Sopenharmony_ci// the scenario where a bad reduction pass leads to an invalid module.
32fd4e5da5Sopenharmony_ciclass BlindlyRemoveGlobalValuesReductionOpportunityFinder
33fd4e5da5Sopenharmony_ci    : public ReductionOpportunityFinder {
34fd4e5da5Sopenharmony_ci public:
35fd4e5da5Sopenharmony_ci  BlindlyRemoveGlobalValuesReductionOpportunityFinder() = default;
36fd4e5da5Sopenharmony_ci
37fd4e5da5Sopenharmony_ci  ~BlindlyRemoveGlobalValuesReductionOpportunityFinder() override = default;
38fd4e5da5Sopenharmony_ci
39fd4e5da5Sopenharmony_ci  // The name of this pass.
40fd4e5da5Sopenharmony_ci  std::string GetName() const final { return "BlindlyRemoveGlobalValuesPass"; }
41fd4e5da5Sopenharmony_ci
42fd4e5da5Sopenharmony_ci  // Finds opportunities to remove all global values.  Assuming they are all
43fd4e5da5Sopenharmony_ci  // referenced (directly or indirectly) from elsewhere in the module, each such
44fd4e5da5Sopenharmony_ci  // opportunity will make the module invalid.
45fd4e5da5Sopenharmony_ci  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
46fd4e5da5Sopenharmony_ci      IRContext* context, uint32_t /*unused*/) const final {
47fd4e5da5Sopenharmony_ci    std::vector<std::unique_ptr<ReductionOpportunity>> result;
48fd4e5da5Sopenharmony_ci    for (auto& inst : context->module()->types_values()) {
49fd4e5da5Sopenharmony_ci      if (inst.HasResultId()) {
50fd4e5da5Sopenharmony_ci        result.push_back(
51fd4e5da5Sopenharmony_ci            MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
52fd4e5da5Sopenharmony_ci      }
53fd4e5da5Sopenharmony_ci    }
54fd4e5da5Sopenharmony_ci    return result;
55fd4e5da5Sopenharmony_ci  }
56fd4e5da5Sopenharmony_ci};
57fd4e5da5Sopenharmony_ci
58fd4e5da5Sopenharmony_ci// A reduction opportunity that exists at the start of every function whose
59fd4e5da5Sopenharmony_ci// first instruction is an OpVariable instruction. When applied, the OpVariable
60fd4e5da5Sopenharmony_ci// instruction is duplicated (with a fresh result id). This allows each
61fd4e5da5Sopenharmony_ci// reduction step to increase the number of variables to check if the validator
62fd4e5da5Sopenharmony_ci// limits are enforced.
63fd4e5da5Sopenharmony_ciclass OpVariableDuplicatorReductionOpportunity : public ReductionOpportunity {
64fd4e5da5Sopenharmony_ci public:
65fd4e5da5Sopenharmony_ci  OpVariableDuplicatorReductionOpportunity(Function* function)
66fd4e5da5Sopenharmony_ci      : function_(function) {}
67fd4e5da5Sopenharmony_ci
68fd4e5da5Sopenharmony_ci  bool PreconditionHolds() override {
69fd4e5da5Sopenharmony_ci    Instruction* first_instruction = &*function_->begin()[0].begin();
70fd4e5da5Sopenharmony_ci    return first_instruction->opcode() == spv::Op::OpVariable;
71fd4e5da5Sopenharmony_ci  }
72fd4e5da5Sopenharmony_ci
73fd4e5da5Sopenharmony_ci protected:
74fd4e5da5Sopenharmony_ci  void Apply() override {
75fd4e5da5Sopenharmony_ci    // Duplicate the first OpVariable instruction.
76fd4e5da5Sopenharmony_ci
77fd4e5da5Sopenharmony_ci    Instruction* first_instruction = &*function_->begin()[0].begin();
78fd4e5da5Sopenharmony_ci    assert(first_instruction->opcode() == spv::Op::OpVariable &&
79fd4e5da5Sopenharmony_ci           "Expected first instruction to be OpVariable");
80fd4e5da5Sopenharmony_ci    IRContext* context = first_instruction->context();
81fd4e5da5Sopenharmony_ci    Instruction* cloned_instruction = first_instruction->Clone(context);
82fd4e5da5Sopenharmony_ci    cloned_instruction->SetResultId(context->TakeNextId());
83fd4e5da5Sopenharmony_ci    cloned_instruction->InsertBefore(first_instruction);
84fd4e5da5Sopenharmony_ci  }
85fd4e5da5Sopenharmony_ci
86fd4e5da5Sopenharmony_ci private:
87fd4e5da5Sopenharmony_ci  Function* function_;
88fd4e5da5Sopenharmony_ci};
89fd4e5da5Sopenharmony_ci
90fd4e5da5Sopenharmony_ci// A reduction opportunity finder that finds
91fd4e5da5Sopenharmony_ci// OpVariableDuplicatorReductionOpportunity.
92fd4e5da5Sopenharmony_ciclass OpVariableDuplicatorReductionOpportunityFinder
93fd4e5da5Sopenharmony_ci    : public ReductionOpportunityFinder {
94fd4e5da5Sopenharmony_ci public:
95fd4e5da5Sopenharmony_ci  OpVariableDuplicatorReductionOpportunityFinder() = default;
96fd4e5da5Sopenharmony_ci
97fd4e5da5Sopenharmony_ci  ~OpVariableDuplicatorReductionOpportunityFinder() override = default;
98fd4e5da5Sopenharmony_ci
99fd4e5da5Sopenharmony_ci  std::string GetName() const final {
100fd4e5da5Sopenharmony_ci    return "LocalVariableAdderReductionOpportunityFinder";
101fd4e5da5Sopenharmony_ci  }
102fd4e5da5Sopenharmony_ci
103fd4e5da5Sopenharmony_ci  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
104fd4e5da5Sopenharmony_ci      IRContext* context, uint32_t /*unused*/) const final {
105fd4e5da5Sopenharmony_ci    std::vector<std::unique_ptr<ReductionOpportunity>> result;
106fd4e5da5Sopenharmony_ci    for (auto& function : *context->module()) {
107fd4e5da5Sopenharmony_ci      Instruction* first_instruction = &*function.begin()[0].begin();
108fd4e5da5Sopenharmony_ci      if (first_instruction->opcode() == spv::Op::OpVariable) {
109fd4e5da5Sopenharmony_ci        result.push_back(
110fd4e5da5Sopenharmony_ci            MakeUnique<OpVariableDuplicatorReductionOpportunity>(&function));
111fd4e5da5Sopenharmony_ci      }
112fd4e5da5Sopenharmony_ci    }
113fd4e5da5Sopenharmony_ci    return result;
114fd4e5da5Sopenharmony_ci  }
115fd4e5da5Sopenharmony_ci};
116fd4e5da5Sopenharmony_ci
117fd4e5da5Sopenharmony_ciTEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) {
118fd4e5da5Sopenharmony_ci  // A module whose global values are all referenced, so that any application of
119fd4e5da5Sopenharmony_ci  // MakeModuleInvalidPass will make the module invalid. Check that the reducer
120fd4e5da5Sopenharmony_ci  // makes no progress, as every step will be invalid and treated as
121fd4e5da5Sopenharmony_ci  // uninteresting.
122fd4e5da5Sopenharmony_ci  std::string original = R"(
123fd4e5da5Sopenharmony_ci               OpCapability Shader
124fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
125fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
126fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %4 "main" %60
127fd4e5da5Sopenharmony_ci               OpExecutionMode %4 OriginUpperLeft
128fd4e5da5Sopenharmony_ci               OpSource ESSL 310
129fd4e5da5Sopenharmony_ci               OpName %4 "main"
130fd4e5da5Sopenharmony_ci               OpName %16 "buf2"
131fd4e5da5Sopenharmony_ci               OpMemberName %16 0 "i"
132fd4e5da5Sopenharmony_ci               OpName %18 ""
133fd4e5da5Sopenharmony_ci               OpName %25 "buf1"
134fd4e5da5Sopenharmony_ci               OpMemberName %25 0 "f"
135fd4e5da5Sopenharmony_ci               OpName %27 ""
136fd4e5da5Sopenharmony_ci               OpName %60 "_GLF_color"
137fd4e5da5Sopenharmony_ci               OpMemberDecorate %16 0 Offset 0
138fd4e5da5Sopenharmony_ci               OpDecorate %16 Block
139fd4e5da5Sopenharmony_ci               OpDecorate %18 DescriptorSet 0
140fd4e5da5Sopenharmony_ci               OpDecorate %18 Binding 2
141fd4e5da5Sopenharmony_ci               OpMemberDecorate %25 0 Offset 0
142fd4e5da5Sopenharmony_ci               OpDecorate %25 Block
143fd4e5da5Sopenharmony_ci               OpDecorate %27 DescriptorSet 0
144fd4e5da5Sopenharmony_ci               OpDecorate %27 Binding 1
145fd4e5da5Sopenharmony_ci               OpDecorate %60 Location 0
146fd4e5da5Sopenharmony_ci          %2 = OpTypeVoid
147fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %2
148fd4e5da5Sopenharmony_ci          %6 = OpTypeInt 32 1
149fd4e5da5Sopenharmony_ci          %9 = OpConstant %6 0
150fd4e5da5Sopenharmony_ci         %16 = OpTypeStruct %6
151fd4e5da5Sopenharmony_ci         %17 = OpTypePointer Uniform %16
152fd4e5da5Sopenharmony_ci         %18 = OpVariable %17 Uniform
153fd4e5da5Sopenharmony_ci         %19 = OpTypePointer Uniform %6
154fd4e5da5Sopenharmony_ci         %22 = OpTypeBool
155fd4e5da5Sopenharmony_ci         %24 = OpTypeFloat 32
156fd4e5da5Sopenharmony_ci         %25 = OpTypeStruct %24
157fd4e5da5Sopenharmony_ci         %26 = OpTypePointer Uniform %25
158fd4e5da5Sopenharmony_ci         %27 = OpVariable %26 Uniform
159fd4e5da5Sopenharmony_ci         %28 = OpTypePointer Uniform %24
160fd4e5da5Sopenharmony_ci         %31 = OpConstant %24 2
161fd4e5da5Sopenharmony_ci         %56 = OpConstant %6 1
162fd4e5da5Sopenharmony_ci         %58 = OpTypeVector %24 4
163fd4e5da5Sopenharmony_ci         %59 = OpTypePointer Output %58
164fd4e5da5Sopenharmony_ci         %60 = OpVariable %59 Output
165fd4e5da5Sopenharmony_ci         %72 = OpUndef %24
166fd4e5da5Sopenharmony_ci         %74 = OpUndef %6
167fd4e5da5Sopenharmony_ci          %4 = OpFunction %2 None %3
168fd4e5da5Sopenharmony_ci          %5 = OpLabel
169fd4e5da5Sopenharmony_ci               OpBranch %10
170fd4e5da5Sopenharmony_ci         %10 = OpLabel
171fd4e5da5Sopenharmony_ci         %73 = OpPhi %6 %74 %5 %77 %34
172fd4e5da5Sopenharmony_ci         %71 = OpPhi %24 %72 %5 %76 %34
173fd4e5da5Sopenharmony_ci         %70 = OpPhi %6 %9 %5 %57 %34
174fd4e5da5Sopenharmony_ci         %20 = OpAccessChain %19 %18 %9
175fd4e5da5Sopenharmony_ci         %21 = OpLoad %6 %20
176fd4e5da5Sopenharmony_ci         %23 = OpSLessThan %22 %70 %21
177fd4e5da5Sopenharmony_ci               OpLoopMerge %12 %34 None
178fd4e5da5Sopenharmony_ci               OpBranchConditional %23 %11 %12
179fd4e5da5Sopenharmony_ci         %11 = OpLabel
180fd4e5da5Sopenharmony_ci         %29 = OpAccessChain %28 %27 %9
181fd4e5da5Sopenharmony_ci         %30 = OpLoad %24 %29
182fd4e5da5Sopenharmony_ci         %32 = OpFOrdGreaterThan %22 %30 %31
183fd4e5da5Sopenharmony_ci               OpSelectionMerge %90 None
184fd4e5da5Sopenharmony_ci               OpBranchConditional %32 %33 %46
185fd4e5da5Sopenharmony_ci         %33 = OpLabel
186fd4e5da5Sopenharmony_ci         %40 = OpFAdd %24 %71 %30
187fd4e5da5Sopenharmony_ci         %45 = OpISub %6 %73 %21
188fd4e5da5Sopenharmony_ci               OpBranch %90
189fd4e5da5Sopenharmony_ci         %46 = OpLabel
190fd4e5da5Sopenharmony_ci         %50 = OpFMul %24 %71 %30
191fd4e5da5Sopenharmony_ci         %54 = OpSDiv %6 %73 %21
192fd4e5da5Sopenharmony_ci               OpBranch %90
193fd4e5da5Sopenharmony_ci         %90 = OpLabel
194fd4e5da5Sopenharmony_ci         %77 = OpPhi %6 %45 %33 %54 %46
195fd4e5da5Sopenharmony_ci         %76 = OpPhi %24 %40 %33 %50 %46
196fd4e5da5Sopenharmony_ci               OpBranch %34
197fd4e5da5Sopenharmony_ci         %34 = OpLabel
198fd4e5da5Sopenharmony_ci         %57 = OpIAdd %6 %70 %56
199fd4e5da5Sopenharmony_ci               OpBranch %10
200fd4e5da5Sopenharmony_ci         %12 = OpLabel
201fd4e5da5Sopenharmony_ci         %61 = OpAccessChain %28 %27 %9
202fd4e5da5Sopenharmony_ci         %62 = OpLoad %24 %61
203fd4e5da5Sopenharmony_ci         %66 = OpConvertSToF %24 %21
204fd4e5da5Sopenharmony_ci         %68 = OpConvertSToF %24 %73
205fd4e5da5Sopenharmony_ci         %69 = OpCompositeConstruct %58 %62 %71 %66 %68
206fd4e5da5Sopenharmony_ci               OpStore %60 %69
207fd4e5da5Sopenharmony_ci               OpReturn
208fd4e5da5Sopenharmony_ci               OpFunctionEnd
209fd4e5da5Sopenharmony_ci  )";
210fd4e5da5Sopenharmony_ci
211fd4e5da5Sopenharmony_ci  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
212fd4e5da5Sopenharmony_ci  Reducer reducer(env);
213fd4e5da5Sopenharmony_ci  reducer.SetMessageConsumer(NopDiagnostic);
214fd4e5da5Sopenharmony_ci
215fd4e5da5Sopenharmony_ci  // Say that every module is interesting.
216fd4e5da5Sopenharmony_ci  reducer.SetInterestingnessFunction(
217fd4e5da5Sopenharmony_ci      [](const std::vector<uint32_t>&, uint32_t) -> bool { return true; });
218fd4e5da5Sopenharmony_ci
219fd4e5da5Sopenharmony_ci  reducer.AddReductionPass(
220fd4e5da5Sopenharmony_ci      MakeUnique<BlindlyRemoveGlobalValuesReductionOpportunityFinder>());
221fd4e5da5Sopenharmony_ci
222fd4e5da5Sopenharmony_ci  std::vector<uint32_t> binary_in;
223fd4e5da5Sopenharmony_ci  SpirvTools t(env);
224fd4e5da5Sopenharmony_ci
225fd4e5da5Sopenharmony_ci  ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
226fd4e5da5Sopenharmony_ci  std::vector<uint32_t> binary_out;
227fd4e5da5Sopenharmony_ci  spvtools::ReducerOptions reducer_options;
228fd4e5da5Sopenharmony_ci  reducer_options.set_step_limit(500);
229fd4e5da5Sopenharmony_ci  // Don't fail on a validation error; just treat it as uninteresting.
230fd4e5da5Sopenharmony_ci  reducer_options.set_fail_on_validation_error(false);
231fd4e5da5Sopenharmony_ci  spvtools::ValidatorOptions validator_options;
232fd4e5da5Sopenharmony_ci
233fd4e5da5Sopenharmony_ci  Reducer::ReductionResultStatus status = reducer.Run(
234fd4e5da5Sopenharmony_ci      std::move(binary_in), &binary_out, reducer_options, validator_options);
235fd4e5da5Sopenharmony_ci
236fd4e5da5Sopenharmony_ci  ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
237fd4e5da5Sopenharmony_ci
238fd4e5da5Sopenharmony_ci  // The reducer should have no impact.
239fd4e5da5Sopenharmony_ci  CheckEqual(env, original, binary_out);
240fd4e5da5Sopenharmony_ci}
241fd4e5da5Sopenharmony_ci
242fd4e5da5Sopenharmony_ciTEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) {
243fd4e5da5Sopenharmony_ci  // A module with just one unreferenced global value.  All but one application
244fd4e5da5Sopenharmony_ci  // of MakeModuleInvalidPass will make the module invalid.
245fd4e5da5Sopenharmony_ci  std::string original = R"(
246fd4e5da5Sopenharmony_ci               OpCapability Shader
247fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
248fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
249fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %4 "main" %60
250fd4e5da5Sopenharmony_ci               OpExecutionMode %4 OriginUpperLeft
251fd4e5da5Sopenharmony_ci               OpSource ESSL 310
252fd4e5da5Sopenharmony_ci               OpName %4 "main"
253fd4e5da5Sopenharmony_ci               OpName %16 "buf2"
254fd4e5da5Sopenharmony_ci               OpMemberName %16 0 "i"
255fd4e5da5Sopenharmony_ci               OpName %18 ""
256fd4e5da5Sopenharmony_ci               OpName %25 "buf1"
257fd4e5da5Sopenharmony_ci               OpMemberName %25 0 "f"
258fd4e5da5Sopenharmony_ci               OpName %27 ""
259fd4e5da5Sopenharmony_ci               OpName %60 "_GLF_color"
260fd4e5da5Sopenharmony_ci               OpMemberDecorate %16 0 Offset 0
261fd4e5da5Sopenharmony_ci               OpDecorate %16 Block
262fd4e5da5Sopenharmony_ci               OpDecorate %18 DescriptorSet 0
263fd4e5da5Sopenharmony_ci               OpDecorate %18 Binding 2
264fd4e5da5Sopenharmony_ci               OpMemberDecorate %25 0 Offset 0
265fd4e5da5Sopenharmony_ci               OpDecorate %25 Block
266fd4e5da5Sopenharmony_ci               OpDecorate %27 DescriptorSet 0
267fd4e5da5Sopenharmony_ci               OpDecorate %27 Binding 1
268fd4e5da5Sopenharmony_ci               OpDecorate %60 Location 0
269fd4e5da5Sopenharmony_ci          %2 = OpTypeVoid
270fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %2
271fd4e5da5Sopenharmony_ci          %6 = OpTypeInt 32 1
272fd4e5da5Sopenharmony_ci          %9 = OpConstant %6 0
273fd4e5da5Sopenharmony_ci         %16 = OpTypeStruct %6
274fd4e5da5Sopenharmony_ci         %17 = OpTypePointer Uniform %16
275fd4e5da5Sopenharmony_ci         %18 = OpVariable %17 Uniform
276fd4e5da5Sopenharmony_ci         %19 = OpTypePointer Uniform %6
277fd4e5da5Sopenharmony_ci         %22 = OpTypeBool
278fd4e5da5Sopenharmony_ci         %24 = OpTypeFloat 32
279fd4e5da5Sopenharmony_ci         %25 = OpTypeStruct %24
280fd4e5da5Sopenharmony_ci         %26 = OpTypePointer Uniform %25
281fd4e5da5Sopenharmony_ci         %27 = OpVariable %26 Uniform
282fd4e5da5Sopenharmony_ci         %28 = OpTypePointer Uniform %24
283fd4e5da5Sopenharmony_ci         %31 = OpConstant %24 2
284fd4e5da5Sopenharmony_ci         %56 = OpConstant %6 1
285fd4e5da5Sopenharmony_ci       %1000 = OpConstant %6 1000 ; It should be possible to remove this instruction without making the module invalid.
286fd4e5da5Sopenharmony_ci         %58 = OpTypeVector %24 4
287fd4e5da5Sopenharmony_ci         %59 = OpTypePointer Output %58
288fd4e5da5Sopenharmony_ci         %60 = OpVariable %59 Output
289fd4e5da5Sopenharmony_ci         %72 = OpUndef %24
290fd4e5da5Sopenharmony_ci         %74 = OpUndef %6
291fd4e5da5Sopenharmony_ci          %4 = OpFunction %2 None %3
292fd4e5da5Sopenharmony_ci          %5 = OpLabel
293fd4e5da5Sopenharmony_ci               OpBranch %10
294fd4e5da5Sopenharmony_ci         %10 = OpLabel
295fd4e5da5Sopenharmony_ci         %73 = OpPhi %6 %74 %5 %77 %34
296fd4e5da5Sopenharmony_ci         %71 = OpPhi %24 %72 %5 %76 %34
297fd4e5da5Sopenharmony_ci         %70 = OpPhi %6 %9 %5 %57 %34
298fd4e5da5Sopenharmony_ci         %20 = OpAccessChain %19 %18 %9
299fd4e5da5Sopenharmony_ci         %21 = OpLoad %6 %20
300fd4e5da5Sopenharmony_ci         %23 = OpSLessThan %22 %70 %21
301fd4e5da5Sopenharmony_ci               OpLoopMerge %12 %34 None
302fd4e5da5Sopenharmony_ci               OpBranchConditional %23 %11 %12
303fd4e5da5Sopenharmony_ci         %11 = OpLabel
304fd4e5da5Sopenharmony_ci         %29 = OpAccessChain %28 %27 %9
305fd4e5da5Sopenharmony_ci         %30 = OpLoad %24 %29
306fd4e5da5Sopenharmony_ci         %32 = OpFOrdGreaterThan %22 %30 %31
307fd4e5da5Sopenharmony_ci               OpSelectionMerge %90 None
308fd4e5da5Sopenharmony_ci               OpBranchConditional %32 %33 %46
309fd4e5da5Sopenharmony_ci         %33 = OpLabel
310fd4e5da5Sopenharmony_ci         %40 = OpFAdd %24 %71 %30
311fd4e5da5Sopenharmony_ci         %45 = OpISub %6 %73 %21
312fd4e5da5Sopenharmony_ci               OpBranch %90
313fd4e5da5Sopenharmony_ci         %46 = OpLabel
314fd4e5da5Sopenharmony_ci         %50 = OpFMul %24 %71 %30
315fd4e5da5Sopenharmony_ci         %54 = OpSDiv %6 %73 %21
316fd4e5da5Sopenharmony_ci               OpBranch %90
317fd4e5da5Sopenharmony_ci         %90 = OpLabel
318fd4e5da5Sopenharmony_ci         %77 = OpPhi %6 %45 %33 %54 %46
319fd4e5da5Sopenharmony_ci         %76 = OpPhi %24 %40 %33 %50 %46
320fd4e5da5Sopenharmony_ci               OpBranch %34
321fd4e5da5Sopenharmony_ci         %34 = OpLabel
322fd4e5da5Sopenharmony_ci         %57 = OpIAdd %6 %70 %56
323fd4e5da5Sopenharmony_ci               OpBranch %10
324fd4e5da5Sopenharmony_ci         %12 = OpLabel
325fd4e5da5Sopenharmony_ci         %61 = OpAccessChain %28 %27 %9
326fd4e5da5Sopenharmony_ci         %62 = OpLoad %24 %61
327fd4e5da5Sopenharmony_ci         %66 = OpConvertSToF %24 %21
328fd4e5da5Sopenharmony_ci         %68 = OpConvertSToF %24 %73
329fd4e5da5Sopenharmony_ci         %69 = OpCompositeConstruct %58 %62 %71 %66 %68
330fd4e5da5Sopenharmony_ci               OpStore %60 %69
331fd4e5da5Sopenharmony_ci               OpReturn
332fd4e5da5Sopenharmony_ci               OpFunctionEnd
333fd4e5da5Sopenharmony_ci  )";
334fd4e5da5Sopenharmony_ci
335fd4e5da5Sopenharmony_ci  // This is the same as the original, except that the constant declaration of
336fd4e5da5Sopenharmony_ci  // 1000 is gone.
337fd4e5da5Sopenharmony_ci  std::string expected = R"(
338fd4e5da5Sopenharmony_ci               OpCapability Shader
339fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
340fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
341fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %4 "main" %60
342fd4e5da5Sopenharmony_ci               OpExecutionMode %4 OriginUpperLeft
343fd4e5da5Sopenharmony_ci               OpSource ESSL 310
344fd4e5da5Sopenharmony_ci               OpName %4 "main"
345fd4e5da5Sopenharmony_ci               OpName %16 "buf2"
346fd4e5da5Sopenharmony_ci               OpMemberName %16 0 "i"
347fd4e5da5Sopenharmony_ci               OpName %18 ""
348fd4e5da5Sopenharmony_ci               OpName %25 "buf1"
349fd4e5da5Sopenharmony_ci               OpMemberName %25 0 "f"
350fd4e5da5Sopenharmony_ci               OpName %27 ""
351fd4e5da5Sopenharmony_ci               OpName %60 "_GLF_color"
352fd4e5da5Sopenharmony_ci               OpMemberDecorate %16 0 Offset 0
353fd4e5da5Sopenharmony_ci               OpDecorate %16 Block
354fd4e5da5Sopenharmony_ci               OpDecorate %18 DescriptorSet 0
355fd4e5da5Sopenharmony_ci               OpDecorate %18 Binding 2
356fd4e5da5Sopenharmony_ci               OpMemberDecorate %25 0 Offset 0
357fd4e5da5Sopenharmony_ci               OpDecorate %25 Block
358fd4e5da5Sopenharmony_ci               OpDecorate %27 DescriptorSet 0
359fd4e5da5Sopenharmony_ci               OpDecorate %27 Binding 1
360fd4e5da5Sopenharmony_ci               OpDecorate %60 Location 0
361fd4e5da5Sopenharmony_ci          %2 = OpTypeVoid
362fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %2
363fd4e5da5Sopenharmony_ci          %6 = OpTypeInt 32 1
364fd4e5da5Sopenharmony_ci          %9 = OpConstant %6 0
365fd4e5da5Sopenharmony_ci         %16 = OpTypeStruct %6
366fd4e5da5Sopenharmony_ci         %17 = OpTypePointer Uniform %16
367fd4e5da5Sopenharmony_ci         %18 = OpVariable %17 Uniform
368fd4e5da5Sopenharmony_ci         %19 = OpTypePointer Uniform %6
369fd4e5da5Sopenharmony_ci         %22 = OpTypeBool
370fd4e5da5Sopenharmony_ci         %24 = OpTypeFloat 32
371fd4e5da5Sopenharmony_ci         %25 = OpTypeStruct %24
372fd4e5da5Sopenharmony_ci         %26 = OpTypePointer Uniform %25
373fd4e5da5Sopenharmony_ci         %27 = OpVariable %26 Uniform
374fd4e5da5Sopenharmony_ci         %28 = OpTypePointer Uniform %24
375fd4e5da5Sopenharmony_ci         %31 = OpConstant %24 2
376fd4e5da5Sopenharmony_ci         %56 = OpConstant %6 1
377fd4e5da5Sopenharmony_ci         %58 = OpTypeVector %24 4
378fd4e5da5Sopenharmony_ci         %59 = OpTypePointer Output %58
379fd4e5da5Sopenharmony_ci         %60 = OpVariable %59 Output
380fd4e5da5Sopenharmony_ci         %72 = OpUndef %24
381fd4e5da5Sopenharmony_ci         %74 = OpUndef %6
382fd4e5da5Sopenharmony_ci          %4 = OpFunction %2 None %3
383fd4e5da5Sopenharmony_ci          %5 = OpLabel
384fd4e5da5Sopenharmony_ci               OpBranch %10
385fd4e5da5Sopenharmony_ci         %10 = OpLabel
386fd4e5da5Sopenharmony_ci         %73 = OpPhi %6 %74 %5 %77 %34
387fd4e5da5Sopenharmony_ci         %71 = OpPhi %24 %72 %5 %76 %34
388fd4e5da5Sopenharmony_ci         %70 = OpPhi %6 %9 %5 %57 %34
389fd4e5da5Sopenharmony_ci         %20 = OpAccessChain %19 %18 %9
390fd4e5da5Sopenharmony_ci         %21 = OpLoad %6 %20
391fd4e5da5Sopenharmony_ci         %23 = OpSLessThan %22 %70 %21
392fd4e5da5Sopenharmony_ci               OpLoopMerge %12 %34 None
393fd4e5da5Sopenharmony_ci               OpBranchConditional %23 %11 %12
394fd4e5da5Sopenharmony_ci         %11 = OpLabel
395fd4e5da5Sopenharmony_ci         %29 = OpAccessChain %28 %27 %9
396fd4e5da5Sopenharmony_ci         %30 = OpLoad %24 %29
397fd4e5da5Sopenharmony_ci         %32 = OpFOrdGreaterThan %22 %30 %31
398fd4e5da5Sopenharmony_ci               OpSelectionMerge %90 None
399fd4e5da5Sopenharmony_ci               OpBranchConditional %32 %33 %46
400fd4e5da5Sopenharmony_ci         %33 = OpLabel
401fd4e5da5Sopenharmony_ci         %40 = OpFAdd %24 %71 %30
402fd4e5da5Sopenharmony_ci         %45 = OpISub %6 %73 %21
403fd4e5da5Sopenharmony_ci               OpBranch %90
404fd4e5da5Sopenharmony_ci         %46 = OpLabel
405fd4e5da5Sopenharmony_ci         %50 = OpFMul %24 %71 %30
406fd4e5da5Sopenharmony_ci         %54 = OpSDiv %6 %73 %21
407fd4e5da5Sopenharmony_ci               OpBranch %90
408fd4e5da5Sopenharmony_ci         %90 = OpLabel
409fd4e5da5Sopenharmony_ci         %77 = OpPhi %6 %45 %33 %54 %46
410fd4e5da5Sopenharmony_ci         %76 = OpPhi %24 %40 %33 %50 %46
411fd4e5da5Sopenharmony_ci               OpBranch %34
412fd4e5da5Sopenharmony_ci         %34 = OpLabel
413fd4e5da5Sopenharmony_ci         %57 = OpIAdd %6 %70 %56
414fd4e5da5Sopenharmony_ci               OpBranch %10
415fd4e5da5Sopenharmony_ci         %12 = OpLabel
416fd4e5da5Sopenharmony_ci         %61 = OpAccessChain %28 %27 %9
417fd4e5da5Sopenharmony_ci         %62 = OpLoad %24 %61
418fd4e5da5Sopenharmony_ci         %66 = OpConvertSToF %24 %21
419fd4e5da5Sopenharmony_ci         %68 = OpConvertSToF %24 %73
420fd4e5da5Sopenharmony_ci         %69 = OpCompositeConstruct %58 %62 %71 %66 %68
421fd4e5da5Sopenharmony_ci               OpStore %60 %69
422fd4e5da5Sopenharmony_ci               OpReturn
423fd4e5da5Sopenharmony_ci               OpFunctionEnd
424fd4e5da5Sopenharmony_ci  )";
425fd4e5da5Sopenharmony_ci
426fd4e5da5Sopenharmony_ci  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
427fd4e5da5Sopenharmony_ci  Reducer reducer(env);
428fd4e5da5Sopenharmony_ci  reducer.SetMessageConsumer(NopDiagnostic);
429fd4e5da5Sopenharmony_ci
430fd4e5da5Sopenharmony_ci  // Say that every module is interesting.
431fd4e5da5Sopenharmony_ci  reducer.SetInterestingnessFunction(
432fd4e5da5Sopenharmony_ci      [](const std::vector<uint32_t>&, uint32_t) -> bool { return true; });
433fd4e5da5Sopenharmony_ci
434fd4e5da5Sopenharmony_ci  reducer.AddReductionPass(
435fd4e5da5Sopenharmony_ci      MakeUnique<BlindlyRemoveGlobalValuesReductionOpportunityFinder>());
436fd4e5da5Sopenharmony_ci
437fd4e5da5Sopenharmony_ci  std::vector<uint32_t> binary_in;
438fd4e5da5Sopenharmony_ci  SpirvTools t(env);
439fd4e5da5Sopenharmony_ci
440fd4e5da5Sopenharmony_ci  ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
441fd4e5da5Sopenharmony_ci  std::vector<uint32_t> binary_out;
442fd4e5da5Sopenharmony_ci  spvtools::ReducerOptions reducer_options;
443fd4e5da5Sopenharmony_ci  reducer_options.set_step_limit(500);
444fd4e5da5Sopenharmony_ci  // Don't fail on a validation error; just treat it as uninteresting.
445fd4e5da5Sopenharmony_ci  reducer_options.set_fail_on_validation_error(false);
446fd4e5da5Sopenharmony_ci  spvtools::ValidatorOptions validator_options;
447fd4e5da5Sopenharmony_ci
448fd4e5da5Sopenharmony_ci  Reducer::ReductionResultStatus status = reducer.Run(
449fd4e5da5Sopenharmony_ci      std::move(binary_in), &binary_out, reducer_options, validator_options);
450fd4e5da5Sopenharmony_ci
451fd4e5da5Sopenharmony_ci  ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
452fd4e5da5Sopenharmony_ci
453fd4e5da5Sopenharmony_ci  CheckEqual(env, expected, binary_out);
454fd4e5da5Sopenharmony_ci}
455fd4e5da5Sopenharmony_ci
456fd4e5da5Sopenharmony_ci// Sets up a Reducer for use in the CheckValidationOptions test; avoids
457fd4e5da5Sopenharmony_ci// repetition.
458fd4e5da5Sopenharmony_civoid SetupReducerForCheckValidationOptions(Reducer* reducer) {
459fd4e5da5Sopenharmony_ci  reducer->SetMessageConsumer(NopDiagnostic);
460fd4e5da5Sopenharmony_ci
461fd4e5da5Sopenharmony_ci  // Say that every module is interesting.
462fd4e5da5Sopenharmony_ci  reducer->SetInterestingnessFunction(
463fd4e5da5Sopenharmony_ci      [](const std::vector<uint32_t>&, uint32_t) -> bool { return true; });
464fd4e5da5Sopenharmony_ci
465fd4e5da5Sopenharmony_ci  // Each "reduction" step will duplicate the first OpVariable instruction in
466fd4e5da5Sopenharmony_ci  // the function.
467fd4e5da5Sopenharmony_ci  reducer->AddReductionPass(
468fd4e5da5Sopenharmony_ci      MakeUnique<OpVariableDuplicatorReductionOpportunityFinder>());
469fd4e5da5Sopenharmony_ci}
470fd4e5da5Sopenharmony_ci
471fd4e5da5Sopenharmony_ciTEST(ValidationDuringReductionTest, CheckValidationOptions) {
472fd4e5da5Sopenharmony_ci  // A module that only validates when the "skip-block-layout" validator option
473fd4e5da5Sopenharmony_ci  // is used. Also, the entry point's first instruction creates a local
474fd4e5da5Sopenharmony_ci  // variable; this instruction will be duplicated on each reduction step.
475fd4e5da5Sopenharmony_ci  std::string original = R"(
476fd4e5da5Sopenharmony_ci               OpCapability Shader
477fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
478fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
479fd4e5da5Sopenharmony_ci               OpEntryPoint Vertex %2 "Main" %3
480fd4e5da5Sopenharmony_ci               OpSource HLSL 600
481fd4e5da5Sopenharmony_ci               OpDecorate %3 BuiltIn Position
482fd4e5da5Sopenharmony_ci               OpDecorate %4 DescriptorSet 0
483fd4e5da5Sopenharmony_ci               OpDecorate %4 Binding 99
484fd4e5da5Sopenharmony_ci               OpDecorate %5 ArrayStride 16
485fd4e5da5Sopenharmony_ci               OpMemberDecorate %6 0 Offset 0
486fd4e5da5Sopenharmony_ci               OpMemberDecorate %6 1 Offset 32
487fd4e5da5Sopenharmony_ci               OpMemberDecorate %6 1 MatrixStride 16
488fd4e5da5Sopenharmony_ci               OpMemberDecorate %6 1 ColMajor
489fd4e5da5Sopenharmony_ci               OpMemberDecorate %6 2 Offset 96
490fd4e5da5Sopenharmony_ci               OpMemberDecorate %6 3 Offset 100
491fd4e5da5Sopenharmony_ci               OpMemberDecorate %6 4 Offset 112
492fd4e5da5Sopenharmony_ci               OpMemberDecorate %6 4 MatrixStride 16
493fd4e5da5Sopenharmony_ci               OpMemberDecorate %6 4 ColMajor
494fd4e5da5Sopenharmony_ci               OpMemberDecorate %6 5 Offset 176
495fd4e5da5Sopenharmony_ci               OpDecorate %6 Block
496fd4e5da5Sopenharmony_ci          %7 = OpTypeFloat 32
497fd4e5da5Sopenharmony_ci          %8 = OpTypeVector %7 4
498fd4e5da5Sopenharmony_ci          %9 = OpTypeMatrix %8 4
499fd4e5da5Sopenharmony_ci         %10 = OpTypeVector %7 2
500fd4e5da5Sopenharmony_ci         %11 = OpTypeInt 32 1
501fd4e5da5Sopenharmony_ci         %12 = OpTypeInt 32 0
502fd4e5da5Sopenharmony_ci         %13 = OpConstant %12 2
503fd4e5da5Sopenharmony_ci         %14 = OpConstant %11 1
504fd4e5da5Sopenharmony_ci         %15 = OpConstant %11 5
505fd4e5da5Sopenharmony_ci          %5 = OpTypeArray %8 %13
506fd4e5da5Sopenharmony_ci          %6 = OpTypeStruct %5 %9 %12 %10 %9 %7
507fd4e5da5Sopenharmony_ci         %16 = OpTypePointer Uniform %6
508fd4e5da5Sopenharmony_ci         %17 = OpTypePointer Output %8
509fd4e5da5Sopenharmony_ci         %18 = OpTypeVoid
510fd4e5da5Sopenharmony_ci         %19 = OpTypeFunction %18
511fd4e5da5Sopenharmony_ci         %20 = OpTypePointer Uniform %7
512fd4e5da5Sopenharmony_ci          %4 = OpVariable %16 Uniform
513fd4e5da5Sopenharmony_ci          %3 = OpVariable %17 Output
514fd4e5da5Sopenharmony_ci         %21 = OpTypePointer Function %11
515fd4e5da5Sopenharmony_ci          %2 = OpFunction %18 None %19
516fd4e5da5Sopenharmony_ci         %22 = OpLabel
517fd4e5da5Sopenharmony_ci         %23 = OpVariable %21 Function
518fd4e5da5Sopenharmony_ci         %24 = OpAccessChain %20 %4 %15
519fd4e5da5Sopenharmony_ci         %25 = OpLoad %7 %24
520fd4e5da5Sopenharmony_ci         %26 = OpCompositeConstruct %8 %25 %25 %25 %25
521fd4e5da5Sopenharmony_ci               OpStore %3 %26
522fd4e5da5Sopenharmony_ci               OpReturn
523fd4e5da5Sopenharmony_ci               OpFunctionEnd
524fd4e5da5Sopenharmony_ci  )";
525fd4e5da5Sopenharmony_ci
526fd4e5da5Sopenharmony_ci  spv_target_env env = SPV_ENV_VULKAN_1_0;
527fd4e5da5Sopenharmony_ci  std::vector<uint32_t> binary_in;
528fd4e5da5Sopenharmony_ci  SpirvTools t(env);
529fd4e5da5Sopenharmony_ci
530fd4e5da5Sopenharmony_ci  ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
531fd4e5da5Sopenharmony_ci  std::vector<uint32_t> binary_out;
532fd4e5da5Sopenharmony_ci  spvtools::ReducerOptions reducer_options;
533fd4e5da5Sopenharmony_ci  spvtools::ValidatorOptions validator_options;
534fd4e5da5Sopenharmony_ci
535fd4e5da5Sopenharmony_ci  reducer_options.set_step_limit(3);
536fd4e5da5Sopenharmony_ci  reducer_options.set_fail_on_validation_error(true);
537fd4e5da5Sopenharmony_ci
538fd4e5da5Sopenharmony_ci  // Reduction should fail because the initial state is invalid without the
539fd4e5da5Sopenharmony_ci  // "skip-block-layout" validator option. Note that the interestingness test
540fd4e5da5Sopenharmony_ci  // always returns true.
541fd4e5da5Sopenharmony_ci  {
542fd4e5da5Sopenharmony_ci    Reducer reducer(env);
543fd4e5da5Sopenharmony_ci    SetupReducerForCheckValidationOptions(&reducer);
544fd4e5da5Sopenharmony_ci
545fd4e5da5Sopenharmony_ci    Reducer::ReductionResultStatus status =
546fd4e5da5Sopenharmony_ci        reducer.Run(std::vector<uint32_t>(binary_in), &binary_out,
547fd4e5da5Sopenharmony_ci                    reducer_options, validator_options);
548fd4e5da5Sopenharmony_ci
549fd4e5da5Sopenharmony_ci    ASSERT_EQ(status, Reducer::ReductionResultStatus::kInitialStateInvalid);
550fd4e5da5Sopenharmony_ci  }
551fd4e5da5Sopenharmony_ci
552fd4e5da5Sopenharmony_ci  // Try again with validator option.
553fd4e5da5Sopenharmony_ci  validator_options.SetSkipBlockLayout(true);
554fd4e5da5Sopenharmony_ci
555fd4e5da5Sopenharmony_ci  // Reduction should hit step limit; module is seen as valid, interestingness
556fd4e5da5Sopenharmony_ci  // test always succeeds, and the finder yields infinite opportunities.
557fd4e5da5Sopenharmony_ci  {
558fd4e5da5Sopenharmony_ci    Reducer reducer(env);
559fd4e5da5Sopenharmony_ci    SetupReducerForCheckValidationOptions(&reducer);
560fd4e5da5Sopenharmony_ci
561fd4e5da5Sopenharmony_ci    Reducer::ReductionResultStatus status =
562fd4e5da5Sopenharmony_ci        reducer.Run(std::vector<uint32_t>(binary_in), &binary_out,
563fd4e5da5Sopenharmony_ci                    reducer_options, validator_options);
564fd4e5da5Sopenharmony_ci
565fd4e5da5Sopenharmony_ci    ASSERT_EQ(status, Reducer::ReductionResultStatus::kReachedStepLimit);
566fd4e5da5Sopenharmony_ci  }
567fd4e5da5Sopenharmony_ci
568fd4e5da5Sopenharmony_ci  // Now set a limit on the number of local variables.
569fd4e5da5Sopenharmony_ci  validator_options.SetUniversalLimit(spv_validator_limit_max_local_variables,
570fd4e5da5Sopenharmony_ci                                      2);
571fd4e5da5Sopenharmony_ci
572fd4e5da5Sopenharmony_ci  // Reduction should now fail due to reaching an invalid state; after one step,
573fd4e5da5Sopenharmony_ci  // a local variable is added and the module becomes "invalid" given the
574fd4e5da5Sopenharmony_ci  // validator limits.
575fd4e5da5Sopenharmony_ci  {
576fd4e5da5Sopenharmony_ci    Reducer reducer(env);
577fd4e5da5Sopenharmony_ci    SetupReducerForCheckValidationOptions(&reducer);
578fd4e5da5Sopenharmony_ci
579fd4e5da5Sopenharmony_ci    Reducer::ReductionResultStatus status =
580fd4e5da5Sopenharmony_ci        reducer.Run(std::vector<uint32_t>(binary_in), &binary_out,
581fd4e5da5Sopenharmony_ci                    reducer_options, validator_options);
582fd4e5da5Sopenharmony_ci
583fd4e5da5Sopenharmony_ci    ASSERT_EQ(status, Reducer::ReductionResultStatus::kStateInvalid);
584fd4e5da5Sopenharmony_ci  }
585fd4e5da5Sopenharmony_ci}
586fd4e5da5Sopenharmony_ci
587fd4e5da5Sopenharmony_ci}  // namespace
588fd4e5da5Sopenharmony_ci}  // namespace reduce
589fd4e5da5Sopenharmony_ci}  // namespace spvtools
590