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/operand_to_const_reduction_opportunity_finder.h"
16fd4e5da5Sopenharmony_ci
17fd4e5da5Sopenharmony_ci#include "source/opt/build_module.h"
18fd4e5da5Sopenharmony_ci#include "source/reduce/reduction_opportunity.h"
19fd4e5da5Sopenharmony_ci#include "test/reduce/reduce_test_util.h"
20fd4e5da5Sopenharmony_ci
21fd4e5da5Sopenharmony_cinamespace spvtools {
22fd4e5da5Sopenharmony_cinamespace reduce {
23fd4e5da5Sopenharmony_cinamespace {
24fd4e5da5Sopenharmony_ci
25fd4e5da5Sopenharmony_ciTEST(OperandToConstantReductionPassTest, BasicCheck) {
26fd4e5da5Sopenharmony_ci  std::string prologue = R"(
27fd4e5da5Sopenharmony_ci               OpCapability Shader
28fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
29fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
30fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %4 "main" %37
31fd4e5da5Sopenharmony_ci               OpExecutionMode %4 OriginUpperLeft
32fd4e5da5Sopenharmony_ci               OpSource ESSL 310
33fd4e5da5Sopenharmony_ci               OpName %4 "main"
34fd4e5da5Sopenharmony_ci               OpName %9 "buf1"
35fd4e5da5Sopenharmony_ci               OpMemberName %9 0 "f"
36fd4e5da5Sopenharmony_ci               OpName %11 ""
37fd4e5da5Sopenharmony_ci               OpName %24 "buf2"
38fd4e5da5Sopenharmony_ci               OpMemberName %24 0 "i"
39fd4e5da5Sopenharmony_ci               OpName %26 ""
40fd4e5da5Sopenharmony_ci               OpName %37 "_GLF_color"
41fd4e5da5Sopenharmony_ci               OpMemberDecorate %9 0 Offset 0
42fd4e5da5Sopenharmony_ci               OpDecorate %9 Block
43fd4e5da5Sopenharmony_ci               OpDecorate %11 DescriptorSet 0
44fd4e5da5Sopenharmony_ci               OpDecorate %11 Binding 1
45fd4e5da5Sopenharmony_ci               OpMemberDecorate %24 0 Offset 0
46fd4e5da5Sopenharmony_ci               OpDecorate %24 Block
47fd4e5da5Sopenharmony_ci               OpDecorate %26 DescriptorSet 0
48fd4e5da5Sopenharmony_ci               OpDecorate %26 Binding 2
49fd4e5da5Sopenharmony_ci               OpDecorate %37 Location 0
50fd4e5da5Sopenharmony_ci          %2 = OpTypeVoid
51fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %2
52fd4e5da5Sopenharmony_ci          %6 = OpTypeFloat 32
53fd4e5da5Sopenharmony_ci          %9 = OpTypeStruct %6
54fd4e5da5Sopenharmony_ci         %10 = OpTypePointer Uniform %9
55fd4e5da5Sopenharmony_ci         %11 = OpVariable %10 Uniform
56fd4e5da5Sopenharmony_ci         %12 = OpTypeInt 32 1
57fd4e5da5Sopenharmony_ci         %13 = OpConstant %12 0
58fd4e5da5Sopenharmony_ci         %14 = OpTypePointer Uniform %6
59fd4e5da5Sopenharmony_ci         %20 = OpConstant %6 2
60fd4e5da5Sopenharmony_ci         %24 = OpTypeStruct %12
61fd4e5da5Sopenharmony_ci         %25 = OpTypePointer Uniform %24
62fd4e5da5Sopenharmony_ci         %26 = OpVariable %25 Uniform
63fd4e5da5Sopenharmony_ci         %27 = OpTypePointer Uniform %12
64fd4e5da5Sopenharmony_ci         %33 = OpConstant %12 3
65fd4e5da5Sopenharmony_ci         %35 = OpTypeVector %6 4
66fd4e5da5Sopenharmony_ci         %36 = OpTypePointer Output %35
67fd4e5da5Sopenharmony_ci         %37 = OpVariable %36 Output
68fd4e5da5Sopenharmony_ci          %4 = OpFunction %2 None %3
69fd4e5da5Sopenharmony_ci          %5 = OpLabel
70fd4e5da5Sopenharmony_ci         %15 = OpAccessChain %14 %11 %13
71fd4e5da5Sopenharmony_ci         %16 = OpLoad %6 %15
72fd4e5da5Sopenharmony_ci         %19 = OpFAdd %6 %16 %16
73fd4e5da5Sopenharmony_ci         %21 = OpFAdd %6 %19 %20
74fd4e5da5Sopenharmony_ci         %28 = OpAccessChain %27 %26 %13
75fd4e5da5Sopenharmony_ci         %29 = OpLoad %12 %28
76fd4e5da5Sopenharmony_ci  )";
77fd4e5da5Sopenharmony_ci
78fd4e5da5Sopenharmony_ci  std::string epilogue = R"(
79fd4e5da5Sopenharmony_ci         %45 = OpConvertSToF %6 %34
80fd4e5da5Sopenharmony_ci         %46 = OpCompositeConstruct %35 %16 %21 %43 %45
81fd4e5da5Sopenharmony_ci               OpStore %37 %46
82fd4e5da5Sopenharmony_ci               OpReturn
83fd4e5da5Sopenharmony_ci               OpFunctionEnd
84fd4e5da5Sopenharmony_ci  )";
85fd4e5da5Sopenharmony_ci
86fd4e5da5Sopenharmony_ci  std::string original = prologue + R"(
87fd4e5da5Sopenharmony_ci         %32 = OpIAdd %12 %29 %29
88fd4e5da5Sopenharmony_ci         %34 = OpIAdd %12 %32 %33
89fd4e5da5Sopenharmony_ci         %43 = OpConvertSToF %6 %29
90fd4e5da5Sopenharmony_ci  )" + epilogue;
91fd4e5da5Sopenharmony_ci
92fd4e5da5Sopenharmony_ci  std::string expected = prologue + R"(
93fd4e5da5Sopenharmony_ci         %32 = OpIAdd %12 %13 %13 ; %29 -> %13 x 2
94fd4e5da5Sopenharmony_ci         %34 = OpIAdd %12 %13 %33 ; %32 -> %13
95fd4e5da5Sopenharmony_ci         %43 = OpConvertSToF %6 %13 ; %29 -> %13
96fd4e5da5Sopenharmony_ci  )" + epilogue;
97fd4e5da5Sopenharmony_ci
98fd4e5da5Sopenharmony_ci  const auto env = SPV_ENV_UNIVERSAL_1_3;
99fd4e5da5Sopenharmony_ci  const auto consumer = nullptr;
100fd4e5da5Sopenharmony_ci  const auto context =
101fd4e5da5Sopenharmony_ci      BuildModule(env, consumer, original, kReduceAssembleOption);
102fd4e5da5Sopenharmony_ci  const auto ops =
103fd4e5da5Sopenharmony_ci      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
104fd4e5da5Sopenharmony_ci          context.get(), 0);
105fd4e5da5Sopenharmony_ci  ASSERT_EQ(17, ops.size());
106fd4e5da5Sopenharmony_ci  ASSERT_TRUE(ops[0]->PreconditionHolds());
107fd4e5da5Sopenharmony_ci  ops[0]->TryToApply();
108fd4e5da5Sopenharmony_ci  ASSERT_TRUE(ops[1]->PreconditionHolds());
109fd4e5da5Sopenharmony_ci  ops[1]->TryToApply();
110fd4e5da5Sopenharmony_ci  ASSERT_TRUE(ops[2]->PreconditionHolds());
111fd4e5da5Sopenharmony_ci  ops[2]->TryToApply();
112fd4e5da5Sopenharmony_ci  ASSERT_TRUE(ops[3]->PreconditionHolds());
113fd4e5da5Sopenharmony_ci  ops[3]->TryToApply();
114fd4e5da5Sopenharmony_ci
115fd4e5da5Sopenharmony_ci  CheckEqual(env, expected, context.get());
116fd4e5da5Sopenharmony_ci}
117fd4e5da5Sopenharmony_ci
118fd4e5da5Sopenharmony_ciTEST(OperandToConstantReductionPassTest, WithCalledFunction) {
119fd4e5da5Sopenharmony_ci  std::string shader = R"(
120fd4e5da5Sopenharmony_ci               OpCapability Shader
121fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
122fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
123fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %4 "main" %10 %12
124fd4e5da5Sopenharmony_ci               OpExecutionMode %4 OriginUpperLeft
125fd4e5da5Sopenharmony_ci               OpSource ESSL 310
126fd4e5da5Sopenharmony_ci          %2 = OpTypeVoid
127fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %2
128fd4e5da5Sopenharmony_ci          %6 = OpTypeFloat 32
129fd4e5da5Sopenharmony_ci          %7 = OpTypeVector %6 4
130fd4e5da5Sopenharmony_ci          %8 = OpTypeFunction %7
131fd4e5da5Sopenharmony_ci          %9 = OpTypePointer Output %7
132fd4e5da5Sopenharmony_ci         %10 = OpVariable %9 Output
133fd4e5da5Sopenharmony_ci         %11 = OpTypePointer Input %7
134fd4e5da5Sopenharmony_ci         %12 = OpVariable %11 Input
135fd4e5da5Sopenharmony_ci         %13 = OpConstant %6 0
136fd4e5da5Sopenharmony_ci         %14 = OpConstantComposite %7 %13 %13 %13 %13
137fd4e5da5Sopenharmony_ci          %4 = OpFunction %2 None %3
138fd4e5da5Sopenharmony_ci          %5 = OpLabel
139fd4e5da5Sopenharmony_ci         %15 = OpFunctionCall %7 %16
140fd4e5da5Sopenharmony_ci               OpReturn
141fd4e5da5Sopenharmony_ci               OpFunctionEnd
142fd4e5da5Sopenharmony_ci         %16 = OpFunction %7 None %8
143fd4e5da5Sopenharmony_ci         %17 = OpLabel
144fd4e5da5Sopenharmony_ci               OpReturnValue %14
145fd4e5da5Sopenharmony_ci               OpFunctionEnd
146fd4e5da5Sopenharmony_ci  )";
147fd4e5da5Sopenharmony_ci
148fd4e5da5Sopenharmony_ci  const auto env = SPV_ENV_UNIVERSAL_1_3;
149fd4e5da5Sopenharmony_ci  const auto consumer = nullptr;
150fd4e5da5Sopenharmony_ci  const auto context =
151fd4e5da5Sopenharmony_ci      BuildModule(env, consumer, shader, kReduceAssembleOption);
152fd4e5da5Sopenharmony_ci  const auto ops =
153fd4e5da5Sopenharmony_ci      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
154fd4e5da5Sopenharmony_ci          context.get(), 0);
155fd4e5da5Sopenharmony_ci  ASSERT_EQ(0, ops.size());
156fd4e5da5Sopenharmony_ci}
157fd4e5da5Sopenharmony_ci
158fd4e5da5Sopenharmony_ciTEST(OperandToConstantReductionPassTest, TargetSpecificFunction) {
159fd4e5da5Sopenharmony_ci  std::string shader = R"(
160fd4e5da5Sopenharmony_ci               OpCapability Shader
161fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
162fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
163fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %4 "main"
164fd4e5da5Sopenharmony_ci               OpExecutionMode %4 OriginUpperLeft
165fd4e5da5Sopenharmony_ci               OpSource ESSL 320
166fd4e5da5Sopenharmony_ci          %2 = OpTypeVoid
167fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %2
168fd4e5da5Sopenharmony_ci          %6 = OpTypeInt 32 1
169fd4e5da5Sopenharmony_ci          %7 = OpTypePointer Function %6
170fd4e5da5Sopenharmony_ci          %8 = OpTypeFunction %6 %7
171fd4e5da5Sopenharmony_ci         %17 = OpConstant %6 1
172fd4e5da5Sopenharmony_ci         %20 = OpConstant %6 2
173fd4e5da5Sopenharmony_ci         %23 = OpConstant %6 0
174fd4e5da5Sopenharmony_ci         %24 = OpTypeBool
175fd4e5da5Sopenharmony_ci         %35 = OpConstant %6 3
176fd4e5da5Sopenharmony_ci         %53 = OpConstant %6 10
177fd4e5da5Sopenharmony_ci          %4 = OpFunction %2 None %3
178fd4e5da5Sopenharmony_ci          %5 = OpLabel
179fd4e5da5Sopenharmony_ci         %65 = OpVariable %7 Function
180fd4e5da5Sopenharmony_ci         %68 = OpVariable %7 Function
181fd4e5da5Sopenharmony_ci         %73 = OpVariable %7 Function
182fd4e5da5Sopenharmony_ci               OpStore %65 %35
183fd4e5da5Sopenharmony_ci         %66 = OpLoad %6 %65
184fd4e5da5Sopenharmony_ci         %67 = OpIAdd %6 %66 %17
185fd4e5da5Sopenharmony_ci               OpStore %65 %67
186fd4e5da5Sopenharmony_ci         %69 = OpLoad %6 %65
187fd4e5da5Sopenharmony_ci               OpStore %68 %69
188fd4e5da5Sopenharmony_ci         %70 = OpFunctionCall %6 %13 %68
189fd4e5da5Sopenharmony_ci         %71 = OpLoad %6 %65
190fd4e5da5Sopenharmony_ci         %72 = OpIAdd %6 %71 %70
191fd4e5da5Sopenharmony_ci               OpStore %65 %72
192fd4e5da5Sopenharmony_ci         %74 = OpLoad %6 %65
193fd4e5da5Sopenharmony_ci               OpStore %73 %74
194fd4e5da5Sopenharmony_ci         %75 = OpFunctionCall %6 %10 %73
195fd4e5da5Sopenharmony_ci         %76 = OpLoad %6 %65
196fd4e5da5Sopenharmony_ci         %77 = OpIAdd %6 %76 %75
197fd4e5da5Sopenharmony_ci               OpStore %65 %77
198fd4e5da5Sopenharmony_ci               OpReturn
199fd4e5da5Sopenharmony_ci               OpFunctionEnd
200fd4e5da5Sopenharmony_ci         %10 = OpFunction %6 None %8
201fd4e5da5Sopenharmony_ci          %9 = OpFunctionParameter %7
202fd4e5da5Sopenharmony_ci         %11 = OpLabel
203fd4e5da5Sopenharmony_ci         %15 = OpVariable %7 Function
204fd4e5da5Sopenharmony_ci         %16 = OpLoad %6 %9
205fd4e5da5Sopenharmony_ci         %18 = OpIAdd %6 %16 %17
206fd4e5da5Sopenharmony_ci               OpStore %15 %18
207fd4e5da5Sopenharmony_ci         %19 = OpLoad %6 %15
208fd4e5da5Sopenharmony_ci         %21 = OpIAdd %6 %19 %20
209fd4e5da5Sopenharmony_ci               OpStore %15 %21
210fd4e5da5Sopenharmony_ci         %22 = OpLoad %6 %15
211fd4e5da5Sopenharmony_ci         %25 = OpSGreaterThan %24 %22 %23
212fd4e5da5Sopenharmony_ci               OpSelectionMerge %27 None
213fd4e5da5Sopenharmony_ci               OpBranchConditional %25 %26 %27
214fd4e5da5Sopenharmony_ci         %26 = OpLabel
215fd4e5da5Sopenharmony_ci         %28 = OpLoad %6 %9
216fd4e5da5Sopenharmony_ci               OpReturnValue %28
217fd4e5da5Sopenharmony_ci         %27 = OpLabel
218fd4e5da5Sopenharmony_ci         %30 = OpLoad %6 %9
219fd4e5da5Sopenharmony_ci         %31 = OpIAdd %6 %30 %17
220fd4e5da5Sopenharmony_ci               OpReturnValue %31
221fd4e5da5Sopenharmony_ci               OpFunctionEnd
222fd4e5da5Sopenharmony_ci         %13 = OpFunction %6 None %8
223fd4e5da5Sopenharmony_ci         %12 = OpFunctionParameter %7
224fd4e5da5Sopenharmony_ci         %14 = OpLabel
225fd4e5da5Sopenharmony_ci         %41 = OpVariable %7 Function
226fd4e5da5Sopenharmony_ci         %46 = OpVariable %7 Function
227fd4e5da5Sopenharmony_ci         %55 = OpVariable %7 Function
228fd4e5da5Sopenharmony_ci         %34 = OpLoad %6 %12
229fd4e5da5Sopenharmony_ci         %36 = OpIEqual %24 %34 %35
230fd4e5da5Sopenharmony_ci               OpSelectionMerge %38 None
231fd4e5da5Sopenharmony_ci               OpBranchConditional %36 %37 %38
232fd4e5da5Sopenharmony_ci         %37 = OpLabel
233fd4e5da5Sopenharmony_ci         %39 = OpLoad %6 %12
234fd4e5da5Sopenharmony_ci         %40 = OpIMul %6 %20 %39
235fd4e5da5Sopenharmony_ci               OpStore %41 %40
236fd4e5da5Sopenharmony_ci         %42 = OpFunctionCall %6 %10 %41
237fd4e5da5Sopenharmony_ci               OpReturnValue %42
238fd4e5da5Sopenharmony_ci         %38 = OpLabel
239fd4e5da5Sopenharmony_ci         %44 = OpLoad %6 %12
240fd4e5da5Sopenharmony_ci         %45 = OpIAdd %6 %44 %17
241fd4e5da5Sopenharmony_ci               OpStore %12 %45
242fd4e5da5Sopenharmony_ci               OpStore %46 %23
243fd4e5da5Sopenharmony_ci               OpBranch %47
244fd4e5da5Sopenharmony_ci         %47 = OpLabel
245fd4e5da5Sopenharmony_ci               OpLoopMerge %49 %50 None
246fd4e5da5Sopenharmony_ci               OpBranch %51
247fd4e5da5Sopenharmony_ci         %51 = OpLabel
248fd4e5da5Sopenharmony_ci         %52 = OpLoad %6 %46
249fd4e5da5Sopenharmony_ci         %54 = OpSLessThan %24 %52 %53
250fd4e5da5Sopenharmony_ci               OpBranchConditional %54 %48 %49
251fd4e5da5Sopenharmony_ci         %48 = OpLabel
252fd4e5da5Sopenharmony_ci         %56 = OpLoad %6 %12
253fd4e5da5Sopenharmony_ci               OpStore %55 %56
254fd4e5da5Sopenharmony_ci         %57 = OpFunctionCall %6 %10 %55
255fd4e5da5Sopenharmony_ci         %58 = OpLoad %6 %12
256fd4e5da5Sopenharmony_ci         %59 = OpIAdd %6 %58 %57
257fd4e5da5Sopenharmony_ci               OpStore %12 %59
258fd4e5da5Sopenharmony_ci               OpBranch %50
259fd4e5da5Sopenharmony_ci         %50 = OpLabel
260fd4e5da5Sopenharmony_ci         %60 = OpLoad %6 %46
261fd4e5da5Sopenharmony_ci         %61 = OpIAdd %6 %60 %17
262fd4e5da5Sopenharmony_ci               OpStore %46 %61
263fd4e5da5Sopenharmony_ci               OpBranch %47
264fd4e5da5Sopenharmony_ci         %49 = OpLabel
265fd4e5da5Sopenharmony_ci         %62 = OpLoad %6 %12
266fd4e5da5Sopenharmony_ci               OpReturnValue %62
267fd4e5da5Sopenharmony_ci               OpFunctionEnd
268fd4e5da5Sopenharmony_ci  )";
269fd4e5da5Sopenharmony_ci
270fd4e5da5Sopenharmony_ci  const auto env = SPV_ENV_UNIVERSAL_1_3;
271fd4e5da5Sopenharmony_ci  const auto consumer = nullptr;
272fd4e5da5Sopenharmony_ci  const auto context =
273fd4e5da5Sopenharmony_ci      BuildModule(env, consumer, shader, kReduceAssembleOption);
274fd4e5da5Sopenharmony_ci
275fd4e5da5Sopenharmony_ci  // Targeting all functions, there are quite a few opportunities.  To avoid
276fd4e5da5Sopenharmony_ci  // making the test too sensitive, we check that there are more than a number
277fd4e5da5Sopenharmony_ci  // somewhat lower than the real number.
278fd4e5da5Sopenharmony_ci  const auto all_ops =
279fd4e5da5Sopenharmony_ci      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
280fd4e5da5Sopenharmony_ci          context.get(), 0);
281fd4e5da5Sopenharmony_ci  ASSERT_TRUE(all_ops.size() > 100);
282fd4e5da5Sopenharmony_ci
283fd4e5da5Sopenharmony_ci  // Targeting individual functions, there are fewer opportunities.  Again, we
284fd4e5da5Sopenharmony_ci  // avoid checking against an exact number so that the test is not too
285fd4e5da5Sopenharmony_ci  // sensitive.
286fd4e5da5Sopenharmony_ci  const auto ops_for_function_4 =
287fd4e5da5Sopenharmony_ci      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
288fd4e5da5Sopenharmony_ci          context.get(), 4);
289fd4e5da5Sopenharmony_ci  const auto ops_for_function_10 =
290fd4e5da5Sopenharmony_ci      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
291fd4e5da5Sopenharmony_ci          context.get(), 10);
292fd4e5da5Sopenharmony_ci  const auto ops_for_function_13 =
293fd4e5da5Sopenharmony_ci      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
294fd4e5da5Sopenharmony_ci          context.get(), 13);
295fd4e5da5Sopenharmony_ci  ASSERT_TRUE(ops_for_function_4.size() < 60);
296fd4e5da5Sopenharmony_ci  ASSERT_TRUE(ops_for_function_10.size() < 50);
297fd4e5da5Sopenharmony_ci  ASSERT_TRUE(ops_for_function_13.size() < 80);
298fd4e5da5Sopenharmony_ci
299fd4e5da5Sopenharmony_ci  // The total number of opportunities should be the sum of the per-function
300fd4e5da5Sopenharmony_ci  // opportunities.
301fd4e5da5Sopenharmony_ci  ASSERT_EQ(all_ops.size(), ops_for_function_4.size() +
302fd4e5da5Sopenharmony_ci                                ops_for_function_10.size() +
303fd4e5da5Sopenharmony_ci                                ops_for_function_13.size());
304fd4e5da5Sopenharmony_ci}
305fd4e5da5Sopenharmony_ci
306fd4e5da5Sopenharmony_ci}  // namespace
307fd4e5da5Sopenharmony_ci}  // namespace reduce
308fd4e5da5Sopenharmony_ci}  // namespace spvtools
309