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/opt/scalar_analysis.h"
16fd4e5da5Sopenharmony_ci
17fd4e5da5Sopenharmony_ci#include <memory>
18fd4e5da5Sopenharmony_ci#include <vector>
19fd4e5da5Sopenharmony_ci
20fd4e5da5Sopenharmony_ci#include "gmock/gmock.h"
21fd4e5da5Sopenharmony_ci#include "source/opt/pass.h"
22fd4e5da5Sopenharmony_ci#include "test/opt/assembly_builder.h"
23fd4e5da5Sopenharmony_ci#include "test/opt/function_utils.h"
24fd4e5da5Sopenharmony_ci#include "test/opt/pass_fixture.h"
25fd4e5da5Sopenharmony_ci#include "test/opt/pass_utils.h"
26fd4e5da5Sopenharmony_ci
27fd4e5da5Sopenharmony_cinamespace spvtools {
28fd4e5da5Sopenharmony_cinamespace opt {
29fd4e5da5Sopenharmony_cinamespace {
30fd4e5da5Sopenharmony_ci
31fd4e5da5Sopenharmony_ciusing ::testing::UnorderedElementsAre;
32fd4e5da5Sopenharmony_ciusing ScalarAnalysisTest = PassTest<::testing::Test>;
33fd4e5da5Sopenharmony_ci
34fd4e5da5Sopenharmony_ci/*
35fd4e5da5Sopenharmony_ciGenerated from the following GLSL + --eliminate-local-multi-store
36fd4e5da5Sopenharmony_ci
37fd4e5da5Sopenharmony_ci#version 410 core
38fd4e5da5Sopenharmony_cilayout (location = 1) out float array[10];
39fd4e5da5Sopenharmony_civoid main() {
40fd4e5da5Sopenharmony_ci  for (int i = 0; i < 10; ++i) {
41fd4e5da5Sopenharmony_ci    array[i] = array[i+1];
42fd4e5da5Sopenharmony_ci  }
43fd4e5da5Sopenharmony_ci}
44fd4e5da5Sopenharmony_ci*/
45fd4e5da5Sopenharmony_ciTEST_F(ScalarAnalysisTest, BasicEvolutionTest) {
46fd4e5da5Sopenharmony_ci  const std::string text = R"(
47fd4e5da5Sopenharmony_ci               OpCapability Shader
48fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
49fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
50fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %4 "main" %24
51fd4e5da5Sopenharmony_ci               OpExecutionMode %4 OriginUpperLeft
52fd4e5da5Sopenharmony_ci               OpSource GLSL 410
53fd4e5da5Sopenharmony_ci               OpName %4 "main"
54fd4e5da5Sopenharmony_ci               OpName %24 "array"
55fd4e5da5Sopenharmony_ci               OpDecorate %24 Location 1
56fd4e5da5Sopenharmony_ci          %2 = OpTypeVoid
57fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %2
58fd4e5da5Sopenharmony_ci          %6 = OpTypeInt 32 1
59fd4e5da5Sopenharmony_ci          %7 = OpTypePointer Function %6
60fd4e5da5Sopenharmony_ci          %9 = OpConstant %6 0
61fd4e5da5Sopenharmony_ci         %16 = OpConstant %6 10
62fd4e5da5Sopenharmony_ci         %17 = OpTypeBool
63fd4e5da5Sopenharmony_ci         %19 = OpTypeFloat 32
64fd4e5da5Sopenharmony_ci         %20 = OpTypeInt 32 0
65fd4e5da5Sopenharmony_ci         %21 = OpConstant %20 10
66fd4e5da5Sopenharmony_ci         %22 = OpTypeArray %19 %21
67fd4e5da5Sopenharmony_ci         %23 = OpTypePointer Output %22
68fd4e5da5Sopenharmony_ci         %24 = OpVariable %23 Output
69fd4e5da5Sopenharmony_ci         %27 = OpConstant %6 1
70fd4e5da5Sopenharmony_ci         %29 = OpTypePointer Output %19
71fd4e5da5Sopenharmony_ci          %4 = OpFunction %2 None %3
72fd4e5da5Sopenharmony_ci          %5 = OpLabel
73fd4e5da5Sopenharmony_ci               OpBranch %10
74fd4e5da5Sopenharmony_ci         %10 = OpLabel
75fd4e5da5Sopenharmony_ci         %35 = OpPhi %6 %9 %5 %34 %13
76fd4e5da5Sopenharmony_ci               OpLoopMerge %12 %13 None
77fd4e5da5Sopenharmony_ci               OpBranch %14
78fd4e5da5Sopenharmony_ci         %14 = OpLabel
79fd4e5da5Sopenharmony_ci         %18 = OpSLessThan %17 %35 %16
80fd4e5da5Sopenharmony_ci               OpBranchConditional %18 %11 %12
81fd4e5da5Sopenharmony_ci         %11 = OpLabel
82fd4e5da5Sopenharmony_ci         %28 = OpIAdd %6 %35 %27
83fd4e5da5Sopenharmony_ci         %30 = OpAccessChain %29 %24 %28
84fd4e5da5Sopenharmony_ci         %31 = OpLoad %19 %30
85fd4e5da5Sopenharmony_ci         %32 = OpAccessChain %29 %24 %35
86fd4e5da5Sopenharmony_ci               OpStore %32 %31
87fd4e5da5Sopenharmony_ci               OpBranch %13
88fd4e5da5Sopenharmony_ci         %13 = OpLabel
89fd4e5da5Sopenharmony_ci         %34 = OpIAdd %6 %35 %27
90fd4e5da5Sopenharmony_ci               OpBranch %10
91fd4e5da5Sopenharmony_ci         %12 = OpLabel
92fd4e5da5Sopenharmony_ci               OpReturn
93fd4e5da5Sopenharmony_ci               OpFunctionEnd
94fd4e5da5Sopenharmony_ci  )";
95fd4e5da5Sopenharmony_ci  // clang-format on
96fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> context =
97fd4e5da5Sopenharmony_ci      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
98fd4e5da5Sopenharmony_ci                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
99fd4e5da5Sopenharmony_ci  Module* module = context->module();
100fd4e5da5Sopenharmony_ci  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
101fd4e5da5Sopenharmony_ci                             << text << std::endl;
102fd4e5da5Sopenharmony_ci  const Function* f = spvtest::GetFunction(module, 4);
103fd4e5da5Sopenharmony_ci  ScalarEvolutionAnalysis analysis{context.get()};
104fd4e5da5Sopenharmony_ci
105fd4e5da5Sopenharmony_ci  const Instruction* store = nullptr;
106fd4e5da5Sopenharmony_ci  const Instruction* load = nullptr;
107fd4e5da5Sopenharmony_ci  for (const Instruction& inst : *spvtest::GetBasicBlock(f, 11)) {
108fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpStore) {
109fd4e5da5Sopenharmony_ci      store = &inst;
110fd4e5da5Sopenharmony_ci    }
111fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpLoad) {
112fd4e5da5Sopenharmony_ci      load = &inst;
113fd4e5da5Sopenharmony_ci    }
114fd4e5da5Sopenharmony_ci  }
115fd4e5da5Sopenharmony_ci
116fd4e5da5Sopenharmony_ci  EXPECT_NE(load, nullptr);
117fd4e5da5Sopenharmony_ci  EXPECT_NE(store, nullptr);
118fd4e5da5Sopenharmony_ci
119fd4e5da5Sopenharmony_ci  Instruction* access_chain =
120fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(load->GetSingleWordInOperand(0));
121fd4e5da5Sopenharmony_ci
122fd4e5da5Sopenharmony_ci  Instruction* child = context->get_def_use_mgr()->GetDef(
123fd4e5da5Sopenharmony_ci      access_chain->GetSingleWordInOperand(1));
124fd4e5da5Sopenharmony_ci  const SENode* node = analysis.AnalyzeInstruction(child);
125fd4e5da5Sopenharmony_ci
126fd4e5da5Sopenharmony_ci  EXPECT_NE(node, nullptr);
127fd4e5da5Sopenharmony_ci
128fd4e5da5Sopenharmony_ci  // Unsimplified node should have the form of ADD(REC(0,1), 1)
129fd4e5da5Sopenharmony_ci  EXPECT_EQ(node->GetType(), SENode::Add);
130fd4e5da5Sopenharmony_ci
131fd4e5da5Sopenharmony_ci  const SENode* child_1 = node->GetChild(0);
132fd4e5da5Sopenharmony_ci  EXPECT_TRUE(child_1->GetType() == SENode::Constant ||
133fd4e5da5Sopenharmony_ci              child_1->GetType() == SENode::RecurrentAddExpr);
134fd4e5da5Sopenharmony_ci
135fd4e5da5Sopenharmony_ci  const SENode* child_2 = node->GetChild(1);
136fd4e5da5Sopenharmony_ci  EXPECT_TRUE(child_2->GetType() == SENode::Constant ||
137fd4e5da5Sopenharmony_ci              child_2->GetType() == SENode::RecurrentAddExpr);
138fd4e5da5Sopenharmony_ci
139fd4e5da5Sopenharmony_ci  SENode* simplified = analysis.SimplifyExpression(const_cast<SENode*>(node));
140fd4e5da5Sopenharmony_ci  // Simplified should be in the form of REC(1,1)
141fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified->GetType(), SENode::RecurrentAddExpr);
142fd4e5da5Sopenharmony_ci
143fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified->GetChild(0)->GetType(), SENode::Constant);
144fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified->GetChild(0)->AsSEConstantNode()->FoldToSingleValue(),
145fd4e5da5Sopenharmony_ci            1);
146fd4e5da5Sopenharmony_ci
147fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified->GetChild(1)->GetType(), SENode::Constant);
148fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified->GetChild(1)->AsSEConstantNode()->FoldToSingleValue(),
149fd4e5da5Sopenharmony_ci            1);
150fd4e5da5Sopenharmony_ci
151fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified->GetChild(0), simplified->GetChild(1));
152fd4e5da5Sopenharmony_ci}
153fd4e5da5Sopenharmony_ci
154fd4e5da5Sopenharmony_ci/*
155fd4e5da5Sopenharmony_ciGenerated from the following GLSL + --eliminate-local-multi-store
156fd4e5da5Sopenharmony_ci
157fd4e5da5Sopenharmony_ci#version 410 core
158fd4e5da5Sopenharmony_cilayout (location = 1) out float array[10];
159fd4e5da5Sopenharmony_cilayout (location = 2) flat in int loop_invariant;
160fd4e5da5Sopenharmony_civoid main() {
161fd4e5da5Sopenharmony_ci  for (int i = 0; i < 10; ++i) {
162fd4e5da5Sopenharmony_ci    array[i] = array[i+loop_invariant];
163fd4e5da5Sopenharmony_ci  }
164fd4e5da5Sopenharmony_ci}
165fd4e5da5Sopenharmony_ci
166fd4e5da5Sopenharmony_ci*/
167fd4e5da5Sopenharmony_ciTEST_F(ScalarAnalysisTest, LoadTest) {
168fd4e5da5Sopenharmony_ci  const std::string text = R"(
169fd4e5da5Sopenharmony_ci               OpCapability Shader
170fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
171fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
172fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %2 "main" %3 %4
173fd4e5da5Sopenharmony_ci               OpExecutionMode %2 OriginUpperLeft
174fd4e5da5Sopenharmony_ci               OpSource GLSL 430
175fd4e5da5Sopenharmony_ci               OpName %2 "main"
176fd4e5da5Sopenharmony_ci               OpName %3 "array"
177fd4e5da5Sopenharmony_ci               OpName %4 "loop_invariant"
178fd4e5da5Sopenharmony_ci               OpDecorate %3 Location 1
179fd4e5da5Sopenharmony_ci               OpDecorate %4 Flat
180fd4e5da5Sopenharmony_ci               OpDecorate %4 Location 2
181fd4e5da5Sopenharmony_ci          %5 = OpTypeVoid
182fd4e5da5Sopenharmony_ci          %6 = OpTypeFunction %5
183fd4e5da5Sopenharmony_ci          %7 = OpTypeInt 32 1
184fd4e5da5Sopenharmony_ci          %8 = OpTypePointer Function %7
185fd4e5da5Sopenharmony_ci          %9 = OpConstant %7 0
186fd4e5da5Sopenharmony_ci         %10 = OpConstant %7 10
187fd4e5da5Sopenharmony_ci         %11 = OpTypeBool
188fd4e5da5Sopenharmony_ci         %12 = OpTypeFloat 32
189fd4e5da5Sopenharmony_ci         %13 = OpTypeInt 32 0
190fd4e5da5Sopenharmony_ci         %14 = OpConstant %13 10
191fd4e5da5Sopenharmony_ci         %15 = OpTypeArray %12 %14
192fd4e5da5Sopenharmony_ci         %16 = OpTypePointer Output %15
193fd4e5da5Sopenharmony_ci          %3 = OpVariable %16 Output
194fd4e5da5Sopenharmony_ci         %17 = OpTypePointer Input %7
195fd4e5da5Sopenharmony_ci          %4 = OpVariable %17 Input
196fd4e5da5Sopenharmony_ci         %18 = OpTypePointer Output %12
197fd4e5da5Sopenharmony_ci         %19 = OpConstant %7 1
198fd4e5da5Sopenharmony_ci          %2 = OpFunction %5 None %6
199fd4e5da5Sopenharmony_ci         %20 = OpLabel
200fd4e5da5Sopenharmony_ci               OpBranch %21
201fd4e5da5Sopenharmony_ci         %21 = OpLabel
202fd4e5da5Sopenharmony_ci         %22 = OpPhi %7 %9 %20 %23 %24
203fd4e5da5Sopenharmony_ci               OpLoopMerge %25 %24 None
204fd4e5da5Sopenharmony_ci               OpBranch %26
205fd4e5da5Sopenharmony_ci         %26 = OpLabel
206fd4e5da5Sopenharmony_ci         %27 = OpSLessThan %11 %22 %10
207fd4e5da5Sopenharmony_ci               OpBranchConditional %27 %28 %25
208fd4e5da5Sopenharmony_ci         %28 = OpLabel
209fd4e5da5Sopenharmony_ci         %29 = OpLoad %7 %4
210fd4e5da5Sopenharmony_ci         %30 = OpIAdd %7 %22 %29
211fd4e5da5Sopenharmony_ci         %31 = OpAccessChain %18 %3 %30
212fd4e5da5Sopenharmony_ci         %32 = OpLoad %12 %31
213fd4e5da5Sopenharmony_ci         %33 = OpAccessChain %18 %3 %22
214fd4e5da5Sopenharmony_ci               OpStore %33 %32
215fd4e5da5Sopenharmony_ci               OpBranch %24
216fd4e5da5Sopenharmony_ci         %24 = OpLabel
217fd4e5da5Sopenharmony_ci         %23 = OpIAdd %7 %22 %19
218fd4e5da5Sopenharmony_ci               OpBranch %21
219fd4e5da5Sopenharmony_ci         %25 = OpLabel
220fd4e5da5Sopenharmony_ci               OpReturn
221fd4e5da5Sopenharmony_ci               OpFunctionEnd
222fd4e5da5Sopenharmony_ci)";
223fd4e5da5Sopenharmony_ci  // clang-format on
224fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> context =
225fd4e5da5Sopenharmony_ci      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
226fd4e5da5Sopenharmony_ci                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
227fd4e5da5Sopenharmony_ci  Module* module = context->module();
228fd4e5da5Sopenharmony_ci  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
229fd4e5da5Sopenharmony_ci                             << text << std::endl;
230fd4e5da5Sopenharmony_ci  const Function* f = spvtest::GetFunction(module, 2);
231fd4e5da5Sopenharmony_ci  ScalarEvolutionAnalysis analysis{context.get()};
232fd4e5da5Sopenharmony_ci
233fd4e5da5Sopenharmony_ci  const Instruction* load = nullptr;
234fd4e5da5Sopenharmony_ci  for (const Instruction& inst : *spvtest::GetBasicBlock(f, 28)) {
235fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpLoad) {
236fd4e5da5Sopenharmony_ci      load = &inst;
237fd4e5da5Sopenharmony_ci    }
238fd4e5da5Sopenharmony_ci  }
239fd4e5da5Sopenharmony_ci
240fd4e5da5Sopenharmony_ci  EXPECT_NE(load, nullptr);
241fd4e5da5Sopenharmony_ci
242fd4e5da5Sopenharmony_ci  Instruction* access_chain =
243fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(load->GetSingleWordInOperand(0));
244fd4e5da5Sopenharmony_ci
245fd4e5da5Sopenharmony_ci  Instruction* child = context->get_def_use_mgr()->GetDef(
246fd4e5da5Sopenharmony_ci      access_chain->GetSingleWordInOperand(1));
247fd4e5da5Sopenharmony_ci  //  const SENode* node =
248fd4e5da5Sopenharmony_ci  //  analysis.GetNodeFromInstruction(child->unique_id());
249fd4e5da5Sopenharmony_ci
250fd4e5da5Sopenharmony_ci  const SENode* node = analysis.AnalyzeInstruction(child);
251fd4e5da5Sopenharmony_ci
252fd4e5da5Sopenharmony_ci  EXPECT_NE(node, nullptr);
253fd4e5da5Sopenharmony_ci
254fd4e5da5Sopenharmony_ci  // Unsimplified node should have the form of ADD(REC(0,1), X)
255fd4e5da5Sopenharmony_ci  EXPECT_EQ(node->GetType(), SENode::Add);
256fd4e5da5Sopenharmony_ci
257fd4e5da5Sopenharmony_ci  const SENode* child_1 = node->GetChild(0);
258fd4e5da5Sopenharmony_ci  EXPECT_TRUE(child_1->GetType() == SENode::ValueUnknown ||
259fd4e5da5Sopenharmony_ci              child_1->GetType() == SENode::RecurrentAddExpr);
260fd4e5da5Sopenharmony_ci
261fd4e5da5Sopenharmony_ci  const SENode* child_2 = node->GetChild(1);
262fd4e5da5Sopenharmony_ci  EXPECT_TRUE(child_2->GetType() == SENode::ValueUnknown ||
263fd4e5da5Sopenharmony_ci              child_2->GetType() == SENode::RecurrentAddExpr);
264fd4e5da5Sopenharmony_ci
265fd4e5da5Sopenharmony_ci  SENode* simplified = analysis.SimplifyExpression(const_cast<SENode*>(node));
266fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified->GetType(), SENode::RecurrentAddExpr);
267fd4e5da5Sopenharmony_ci
268fd4e5da5Sopenharmony_ci  const SERecurrentNode* rec = simplified->AsSERecurrentNode();
269fd4e5da5Sopenharmony_ci
270fd4e5da5Sopenharmony_ci  EXPECT_NE(rec->GetChild(0), rec->GetChild(1));
271fd4e5da5Sopenharmony_ci
272fd4e5da5Sopenharmony_ci  EXPECT_EQ(rec->GetOffset()->GetType(), SENode::ValueUnknown);
273fd4e5da5Sopenharmony_ci
274fd4e5da5Sopenharmony_ci  EXPECT_EQ(rec->GetCoefficient()->GetType(), SENode::Constant);
275fd4e5da5Sopenharmony_ci  EXPECT_EQ(rec->GetCoefficient()->AsSEConstantNode()->FoldToSingleValue(), 1u);
276fd4e5da5Sopenharmony_ci}
277fd4e5da5Sopenharmony_ci
278fd4e5da5Sopenharmony_ci/*
279fd4e5da5Sopenharmony_ciGenerated from the following GLSL + --eliminate-local-multi-store
280fd4e5da5Sopenharmony_ci
281fd4e5da5Sopenharmony_ci#version 410 core
282fd4e5da5Sopenharmony_cilayout (location = 1) out float array[10];
283fd4e5da5Sopenharmony_cilayout (location = 2) flat in int loop_invariant;
284fd4e5da5Sopenharmony_civoid main() {
285fd4e5da5Sopenharmony_ci  array[0] = array[loop_invariant * 2 + 4 + 5 - 24 - loop_invariant -
286fd4e5da5Sopenharmony_ciloop_invariant+ 16 * 3];
287fd4e5da5Sopenharmony_ci}
288fd4e5da5Sopenharmony_ci
289fd4e5da5Sopenharmony_ci*/
290fd4e5da5Sopenharmony_ciTEST_F(ScalarAnalysisTest, SimplifySimple) {
291fd4e5da5Sopenharmony_ci  const std::string text = R"(
292fd4e5da5Sopenharmony_ci               OpCapability Shader
293fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
294fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
295fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %2 "main" %3 %4
296fd4e5da5Sopenharmony_ci               OpExecutionMode %2 OriginUpperLeft
297fd4e5da5Sopenharmony_ci               OpSource GLSL 430
298fd4e5da5Sopenharmony_ci               OpName %2 "main"
299fd4e5da5Sopenharmony_ci               OpName %3 "array"
300fd4e5da5Sopenharmony_ci               OpName %4 "loop_invariant"
301fd4e5da5Sopenharmony_ci               OpDecorate %3 Location 1
302fd4e5da5Sopenharmony_ci               OpDecorate %4 Flat
303fd4e5da5Sopenharmony_ci               OpDecorate %4 Location 2
304fd4e5da5Sopenharmony_ci          %5 = OpTypeVoid
305fd4e5da5Sopenharmony_ci          %6 = OpTypeFunction %5
306fd4e5da5Sopenharmony_ci          %7 = OpTypeFloat 32
307fd4e5da5Sopenharmony_ci          %8 = OpTypeInt 32 0
308fd4e5da5Sopenharmony_ci          %9 = OpConstant %8 10
309fd4e5da5Sopenharmony_ci         %10 = OpTypeArray %7 %9
310fd4e5da5Sopenharmony_ci         %11 = OpTypePointer Output %10
311fd4e5da5Sopenharmony_ci          %3 = OpVariable %11 Output
312fd4e5da5Sopenharmony_ci         %12 = OpTypeInt 32 1
313fd4e5da5Sopenharmony_ci         %13 = OpConstant %12 0
314fd4e5da5Sopenharmony_ci         %14 = OpTypePointer Input %12
315fd4e5da5Sopenharmony_ci          %4 = OpVariable %14 Input
316fd4e5da5Sopenharmony_ci         %15 = OpConstant %12 2
317fd4e5da5Sopenharmony_ci         %16 = OpConstant %12 4
318fd4e5da5Sopenharmony_ci         %17 = OpConstant %12 5
319fd4e5da5Sopenharmony_ci         %18 = OpConstant %12 24
320fd4e5da5Sopenharmony_ci         %19 = OpConstant %12 48
321fd4e5da5Sopenharmony_ci         %20 = OpTypePointer Output %7
322fd4e5da5Sopenharmony_ci          %2 = OpFunction %5 None %6
323fd4e5da5Sopenharmony_ci         %21 = OpLabel
324fd4e5da5Sopenharmony_ci         %22 = OpLoad %12 %4
325fd4e5da5Sopenharmony_ci         %23 = OpIMul %12 %22 %15
326fd4e5da5Sopenharmony_ci         %24 = OpIAdd %12 %23 %16
327fd4e5da5Sopenharmony_ci         %25 = OpIAdd %12 %24 %17
328fd4e5da5Sopenharmony_ci         %26 = OpISub %12 %25 %18
329fd4e5da5Sopenharmony_ci         %28 = OpISub %12 %26 %22
330fd4e5da5Sopenharmony_ci         %30 = OpISub %12 %28 %22
331fd4e5da5Sopenharmony_ci         %31 = OpIAdd %12 %30 %19
332fd4e5da5Sopenharmony_ci         %32 = OpAccessChain %20 %3 %31
333fd4e5da5Sopenharmony_ci         %33 = OpLoad %7 %32
334fd4e5da5Sopenharmony_ci         %34 = OpAccessChain %20 %3 %13
335fd4e5da5Sopenharmony_ci               OpStore %34 %33
336fd4e5da5Sopenharmony_ci               OpReturn
337fd4e5da5Sopenharmony_ci               OpFunctionEnd
338fd4e5da5Sopenharmony_ci    )";
339fd4e5da5Sopenharmony_ci  // clang-format on
340fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> context =
341fd4e5da5Sopenharmony_ci      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
342fd4e5da5Sopenharmony_ci                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
343fd4e5da5Sopenharmony_ci  Module* module = context->module();
344fd4e5da5Sopenharmony_ci  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
345fd4e5da5Sopenharmony_ci                             << text << std::endl;
346fd4e5da5Sopenharmony_ci  const Function* f = spvtest::GetFunction(module, 2);
347fd4e5da5Sopenharmony_ci  ScalarEvolutionAnalysis analysis{context.get()};
348fd4e5da5Sopenharmony_ci
349fd4e5da5Sopenharmony_ci  const Instruction* load = nullptr;
350fd4e5da5Sopenharmony_ci  for (const Instruction& inst : *spvtest::GetBasicBlock(f, 21)) {
351fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpLoad && inst.result_id() == 33) {
352fd4e5da5Sopenharmony_ci      load = &inst;
353fd4e5da5Sopenharmony_ci    }
354fd4e5da5Sopenharmony_ci  }
355fd4e5da5Sopenharmony_ci
356fd4e5da5Sopenharmony_ci  EXPECT_NE(load, nullptr);
357fd4e5da5Sopenharmony_ci
358fd4e5da5Sopenharmony_ci  Instruction* access_chain =
359fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(load->GetSingleWordInOperand(0));
360fd4e5da5Sopenharmony_ci
361fd4e5da5Sopenharmony_ci  Instruction* child = context->get_def_use_mgr()->GetDef(
362fd4e5da5Sopenharmony_ci      access_chain->GetSingleWordInOperand(1));
363fd4e5da5Sopenharmony_ci
364fd4e5da5Sopenharmony_ci  const SENode* node = analysis.AnalyzeInstruction(child);
365fd4e5da5Sopenharmony_ci
366fd4e5da5Sopenharmony_ci  // Unsimplified is a very large graph with an add at the top.
367fd4e5da5Sopenharmony_ci  EXPECT_NE(node, nullptr);
368fd4e5da5Sopenharmony_ci  EXPECT_EQ(node->GetType(), SENode::Add);
369fd4e5da5Sopenharmony_ci
370fd4e5da5Sopenharmony_ci  // Simplified node should resolve down to a constant expression as the loads
371fd4e5da5Sopenharmony_ci  // will eliminate themselves.
372fd4e5da5Sopenharmony_ci  SENode* simplified = analysis.SimplifyExpression(const_cast<SENode*>(node));
373fd4e5da5Sopenharmony_ci
374fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified->GetType(), SENode::Constant);
375fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified->AsSEConstantNode()->FoldToSingleValue(), 33u);
376fd4e5da5Sopenharmony_ci}
377fd4e5da5Sopenharmony_ci
378fd4e5da5Sopenharmony_ci/*
379fd4e5da5Sopenharmony_ciGenerated from the following GLSL + --eliminate-local-multi-store
380fd4e5da5Sopenharmony_ci
381fd4e5da5Sopenharmony_ci#version 410 core
382fd4e5da5Sopenharmony_cilayout(location = 0) in vec4 c;
383fd4e5da5Sopenharmony_cilayout (location = 1) out float array[10];
384fd4e5da5Sopenharmony_civoid main() {
385fd4e5da5Sopenharmony_ci  int N = int(c.x);
386fd4e5da5Sopenharmony_ci  for (int i = 0; i < 10; ++i) {
387fd4e5da5Sopenharmony_ci    array[i] = array[i];
388fd4e5da5Sopenharmony_ci    array[i] = array[i-1];
389fd4e5da5Sopenharmony_ci    array[i] = array[i+1];
390fd4e5da5Sopenharmony_ci    array[i+1] = array[i+1];
391fd4e5da5Sopenharmony_ci    array[i+N] = array[i+N];
392fd4e5da5Sopenharmony_ci    array[i] = array[i+N];
393fd4e5da5Sopenharmony_ci  }
394fd4e5da5Sopenharmony_ci}
395fd4e5da5Sopenharmony_ci
396fd4e5da5Sopenharmony_ci*/
397fd4e5da5Sopenharmony_ciTEST_F(ScalarAnalysisTest, Simplify) {
398fd4e5da5Sopenharmony_ci  const std::string text = R"(               OpCapability Shader
399fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
400fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
401fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %4 "main" %12 %33
402fd4e5da5Sopenharmony_ci               OpExecutionMode %4 OriginUpperLeft
403fd4e5da5Sopenharmony_ci               OpSource GLSL 410
404fd4e5da5Sopenharmony_ci               OpName %4 "main"
405fd4e5da5Sopenharmony_ci               OpName %8 "N"
406fd4e5da5Sopenharmony_ci               OpName %12 "c"
407fd4e5da5Sopenharmony_ci               OpName %19 "i"
408fd4e5da5Sopenharmony_ci               OpName %33 "array"
409fd4e5da5Sopenharmony_ci               OpDecorate %12 Location 0
410fd4e5da5Sopenharmony_ci               OpDecorate %33 Location 1
411fd4e5da5Sopenharmony_ci          %2 = OpTypeVoid
412fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %2
413fd4e5da5Sopenharmony_ci          %6 = OpTypeInt 32 1
414fd4e5da5Sopenharmony_ci          %7 = OpTypePointer Function %6
415fd4e5da5Sopenharmony_ci          %9 = OpTypeFloat 32
416fd4e5da5Sopenharmony_ci         %10 = OpTypeVector %9 4
417fd4e5da5Sopenharmony_ci         %11 = OpTypePointer Input %10
418fd4e5da5Sopenharmony_ci         %12 = OpVariable %11 Input
419fd4e5da5Sopenharmony_ci         %13 = OpTypeInt 32 0
420fd4e5da5Sopenharmony_ci         %14 = OpConstant %13 0
421fd4e5da5Sopenharmony_ci         %15 = OpTypePointer Input %9
422fd4e5da5Sopenharmony_ci         %20 = OpConstant %6 0
423fd4e5da5Sopenharmony_ci         %27 = OpConstant %6 10
424fd4e5da5Sopenharmony_ci         %28 = OpTypeBool
425fd4e5da5Sopenharmony_ci         %30 = OpConstant %13 10
426fd4e5da5Sopenharmony_ci         %31 = OpTypeArray %9 %30
427fd4e5da5Sopenharmony_ci         %32 = OpTypePointer Output %31
428fd4e5da5Sopenharmony_ci         %33 = OpVariable %32 Output
429fd4e5da5Sopenharmony_ci         %36 = OpTypePointer Output %9
430fd4e5da5Sopenharmony_ci         %42 = OpConstant %6 1
431fd4e5da5Sopenharmony_ci          %4 = OpFunction %2 None %3
432fd4e5da5Sopenharmony_ci          %5 = OpLabel
433fd4e5da5Sopenharmony_ci          %8 = OpVariable %7 Function
434fd4e5da5Sopenharmony_ci         %19 = OpVariable %7 Function
435fd4e5da5Sopenharmony_ci         %16 = OpAccessChain %15 %12 %14
436fd4e5da5Sopenharmony_ci         %17 = OpLoad %9 %16
437fd4e5da5Sopenharmony_ci         %18 = OpConvertFToS %6 %17
438fd4e5da5Sopenharmony_ci               OpStore %8 %18
439fd4e5da5Sopenharmony_ci               OpStore %19 %20
440fd4e5da5Sopenharmony_ci               OpBranch %21
441fd4e5da5Sopenharmony_ci         %21 = OpLabel
442fd4e5da5Sopenharmony_ci         %78 = OpPhi %6 %20 %5 %77 %24
443fd4e5da5Sopenharmony_ci               OpLoopMerge %23 %24 None
444fd4e5da5Sopenharmony_ci               OpBranch %25
445fd4e5da5Sopenharmony_ci         %25 = OpLabel
446fd4e5da5Sopenharmony_ci         %29 = OpSLessThan %28 %78 %27
447fd4e5da5Sopenharmony_ci               OpBranchConditional %29 %22 %23
448fd4e5da5Sopenharmony_ci         %22 = OpLabel
449fd4e5da5Sopenharmony_ci         %37 = OpAccessChain %36 %33 %78
450fd4e5da5Sopenharmony_ci         %38 = OpLoad %9 %37
451fd4e5da5Sopenharmony_ci         %39 = OpAccessChain %36 %33 %78
452fd4e5da5Sopenharmony_ci               OpStore %39 %38
453fd4e5da5Sopenharmony_ci         %43 = OpISub %6 %78 %42
454fd4e5da5Sopenharmony_ci         %44 = OpAccessChain %36 %33 %43
455fd4e5da5Sopenharmony_ci         %45 = OpLoad %9 %44
456fd4e5da5Sopenharmony_ci         %46 = OpAccessChain %36 %33 %78
457fd4e5da5Sopenharmony_ci               OpStore %46 %45
458fd4e5da5Sopenharmony_ci         %49 = OpIAdd %6 %78 %42
459fd4e5da5Sopenharmony_ci         %50 = OpAccessChain %36 %33 %49
460fd4e5da5Sopenharmony_ci         %51 = OpLoad %9 %50
461fd4e5da5Sopenharmony_ci         %52 = OpAccessChain %36 %33 %78
462fd4e5da5Sopenharmony_ci               OpStore %52 %51
463fd4e5da5Sopenharmony_ci         %54 = OpIAdd %6 %78 %42
464fd4e5da5Sopenharmony_ci         %56 = OpIAdd %6 %78 %42
465fd4e5da5Sopenharmony_ci         %57 = OpAccessChain %36 %33 %56
466fd4e5da5Sopenharmony_ci         %58 = OpLoad %9 %57
467fd4e5da5Sopenharmony_ci         %59 = OpAccessChain %36 %33 %54
468fd4e5da5Sopenharmony_ci               OpStore %59 %58
469fd4e5da5Sopenharmony_ci         %62 = OpIAdd %6 %78 %18
470fd4e5da5Sopenharmony_ci         %65 = OpIAdd %6 %78 %18
471fd4e5da5Sopenharmony_ci         %66 = OpAccessChain %36 %33 %65
472fd4e5da5Sopenharmony_ci         %67 = OpLoad %9 %66
473fd4e5da5Sopenharmony_ci         %68 = OpAccessChain %36 %33 %62
474fd4e5da5Sopenharmony_ci               OpStore %68 %67
475fd4e5da5Sopenharmony_ci         %72 = OpIAdd %6 %78 %18
476fd4e5da5Sopenharmony_ci         %73 = OpAccessChain %36 %33 %72
477fd4e5da5Sopenharmony_ci         %74 = OpLoad %9 %73
478fd4e5da5Sopenharmony_ci         %75 = OpAccessChain %36 %33 %78
479fd4e5da5Sopenharmony_ci               OpStore %75 %74
480fd4e5da5Sopenharmony_ci               OpBranch %24
481fd4e5da5Sopenharmony_ci         %24 = OpLabel
482fd4e5da5Sopenharmony_ci         %77 = OpIAdd %6 %78 %42
483fd4e5da5Sopenharmony_ci               OpStore %19 %77
484fd4e5da5Sopenharmony_ci               OpBranch %21
485fd4e5da5Sopenharmony_ci         %23 = OpLabel
486fd4e5da5Sopenharmony_ci               OpReturn
487fd4e5da5Sopenharmony_ci               OpFunctionEnd
488fd4e5da5Sopenharmony_ci)";
489fd4e5da5Sopenharmony_ci  // clang-format on
490fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> context =
491fd4e5da5Sopenharmony_ci      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
492fd4e5da5Sopenharmony_ci                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
493fd4e5da5Sopenharmony_ci  Module* module = context->module();
494fd4e5da5Sopenharmony_ci  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
495fd4e5da5Sopenharmony_ci                             << text << std::endl;
496fd4e5da5Sopenharmony_ci  const Function* f = spvtest::GetFunction(module, 4);
497fd4e5da5Sopenharmony_ci  ScalarEvolutionAnalysis analysis{context.get()};
498fd4e5da5Sopenharmony_ci
499fd4e5da5Sopenharmony_ci  const Instruction* loads[6];
500fd4e5da5Sopenharmony_ci  const Instruction* stores[6];
501fd4e5da5Sopenharmony_ci  int load_count = 0;
502fd4e5da5Sopenharmony_ci  int store_count = 0;
503fd4e5da5Sopenharmony_ci
504fd4e5da5Sopenharmony_ci  for (const Instruction& inst : *spvtest::GetBasicBlock(f, 22)) {
505fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpLoad) {
506fd4e5da5Sopenharmony_ci      loads[load_count] = &inst;
507fd4e5da5Sopenharmony_ci      ++load_count;
508fd4e5da5Sopenharmony_ci    }
509fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpStore) {
510fd4e5da5Sopenharmony_ci      stores[store_count] = &inst;
511fd4e5da5Sopenharmony_ci      ++store_count;
512fd4e5da5Sopenharmony_ci    }
513fd4e5da5Sopenharmony_ci  }
514fd4e5da5Sopenharmony_ci
515fd4e5da5Sopenharmony_ci  EXPECT_EQ(load_count, 6);
516fd4e5da5Sopenharmony_ci  EXPECT_EQ(store_count, 6);
517fd4e5da5Sopenharmony_ci
518fd4e5da5Sopenharmony_ci  Instruction* load_access_chain;
519fd4e5da5Sopenharmony_ci  Instruction* store_access_chain;
520fd4e5da5Sopenharmony_ci  Instruction* load_child;
521fd4e5da5Sopenharmony_ci  Instruction* store_child;
522fd4e5da5Sopenharmony_ci  SENode* load_node;
523fd4e5da5Sopenharmony_ci  SENode* store_node;
524fd4e5da5Sopenharmony_ci  SENode* subtract_node;
525fd4e5da5Sopenharmony_ci  SENode* simplified_node;
526fd4e5da5Sopenharmony_ci
527fd4e5da5Sopenharmony_ci  // Testing [i] - [i] == 0
528fd4e5da5Sopenharmony_ci  load_access_chain =
529fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(loads[0]->GetSingleWordInOperand(0));
530fd4e5da5Sopenharmony_ci  store_access_chain =
531fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(stores[0]->GetSingleWordInOperand(0));
532fd4e5da5Sopenharmony_ci
533fd4e5da5Sopenharmony_ci  load_child = context->get_def_use_mgr()->GetDef(
534fd4e5da5Sopenharmony_ci      load_access_chain->GetSingleWordInOperand(1));
535fd4e5da5Sopenharmony_ci  store_child = context->get_def_use_mgr()->GetDef(
536fd4e5da5Sopenharmony_ci      store_access_chain->GetSingleWordInOperand(1));
537fd4e5da5Sopenharmony_ci
538fd4e5da5Sopenharmony_ci  load_node = analysis.AnalyzeInstruction(load_child);
539fd4e5da5Sopenharmony_ci  store_node = analysis.AnalyzeInstruction(store_child);
540fd4e5da5Sopenharmony_ci
541fd4e5da5Sopenharmony_ci  subtract_node = analysis.CreateSubtraction(store_node, load_node);
542fd4e5da5Sopenharmony_ci  simplified_node = analysis.SimplifyExpression(subtract_node);
543fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->GetType(), SENode::Constant);
544fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->AsSEConstantNode()->FoldToSingleValue(), 0u);
545fd4e5da5Sopenharmony_ci
546fd4e5da5Sopenharmony_ci  // Testing [i] - [i-1] == 1
547fd4e5da5Sopenharmony_ci  load_access_chain =
548fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(loads[1]->GetSingleWordInOperand(0));
549fd4e5da5Sopenharmony_ci  store_access_chain =
550fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(stores[1]->GetSingleWordInOperand(0));
551fd4e5da5Sopenharmony_ci
552fd4e5da5Sopenharmony_ci  load_child = context->get_def_use_mgr()->GetDef(
553fd4e5da5Sopenharmony_ci      load_access_chain->GetSingleWordInOperand(1));
554fd4e5da5Sopenharmony_ci  store_child = context->get_def_use_mgr()->GetDef(
555fd4e5da5Sopenharmony_ci      store_access_chain->GetSingleWordInOperand(1));
556fd4e5da5Sopenharmony_ci
557fd4e5da5Sopenharmony_ci  load_node = analysis.AnalyzeInstruction(load_child);
558fd4e5da5Sopenharmony_ci  store_node = analysis.AnalyzeInstruction(store_child);
559fd4e5da5Sopenharmony_ci
560fd4e5da5Sopenharmony_ci  subtract_node = analysis.CreateSubtraction(store_node, load_node);
561fd4e5da5Sopenharmony_ci  simplified_node = analysis.SimplifyExpression(subtract_node);
562fd4e5da5Sopenharmony_ci
563fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->GetType(), SENode::Constant);
564fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->AsSEConstantNode()->FoldToSingleValue(), 1u);
565fd4e5da5Sopenharmony_ci
566fd4e5da5Sopenharmony_ci  // Testing [i] - [i+1] == -1
567fd4e5da5Sopenharmony_ci  load_access_chain =
568fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(loads[2]->GetSingleWordInOperand(0));
569fd4e5da5Sopenharmony_ci  store_access_chain =
570fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(stores[2]->GetSingleWordInOperand(0));
571fd4e5da5Sopenharmony_ci
572fd4e5da5Sopenharmony_ci  load_child = context->get_def_use_mgr()->GetDef(
573fd4e5da5Sopenharmony_ci      load_access_chain->GetSingleWordInOperand(1));
574fd4e5da5Sopenharmony_ci  store_child = context->get_def_use_mgr()->GetDef(
575fd4e5da5Sopenharmony_ci      store_access_chain->GetSingleWordInOperand(1));
576fd4e5da5Sopenharmony_ci
577fd4e5da5Sopenharmony_ci  load_node = analysis.AnalyzeInstruction(load_child);
578fd4e5da5Sopenharmony_ci  store_node = analysis.AnalyzeInstruction(store_child);
579fd4e5da5Sopenharmony_ci
580fd4e5da5Sopenharmony_ci  subtract_node = analysis.CreateSubtraction(store_node, load_node);
581fd4e5da5Sopenharmony_ci  simplified_node = analysis.SimplifyExpression(subtract_node);
582fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->GetType(), SENode::Constant);
583fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->AsSEConstantNode()->FoldToSingleValue(), -1);
584fd4e5da5Sopenharmony_ci
585fd4e5da5Sopenharmony_ci  // Testing [i+1] - [i+1] == 0
586fd4e5da5Sopenharmony_ci  load_access_chain =
587fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(loads[3]->GetSingleWordInOperand(0));
588fd4e5da5Sopenharmony_ci  store_access_chain =
589fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(stores[3]->GetSingleWordInOperand(0));
590fd4e5da5Sopenharmony_ci
591fd4e5da5Sopenharmony_ci  load_child = context->get_def_use_mgr()->GetDef(
592fd4e5da5Sopenharmony_ci      load_access_chain->GetSingleWordInOperand(1));
593fd4e5da5Sopenharmony_ci  store_child = context->get_def_use_mgr()->GetDef(
594fd4e5da5Sopenharmony_ci      store_access_chain->GetSingleWordInOperand(1));
595fd4e5da5Sopenharmony_ci
596fd4e5da5Sopenharmony_ci  load_node = analysis.AnalyzeInstruction(load_child);
597fd4e5da5Sopenharmony_ci  store_node = analysis.AnalyzeInstruction(store_child);
598fd4e5da5Sopenharmony_ci
599fd4e5da5Sopenharmony_ci  subtract_node = analysis.CreateSubtraction(store_node, load_node);
600fd4e5da5Sopenharmony_ci  simplified_node = analysis.SimplifyExpression(subtract_node);
601fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->GetType(), SENode::Constant);
602fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->AsSEConstantNode()->FoldToSingleValue(), 0u);
603fd4e5da5Sopenharmony_ci
604fd4e5da5Sopenharmony_ci  // Testing [i+N] - [i+N] == 0
605fd4e5da5Sopenharmony_ci  load_access_chain =
606fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(loads[4]->GetSingleWordInOperand(0));
607fd4e5da5Sopenharmony_ci  store_access_chain =
608fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(stores[4]->GetSingleWordInOperand(0));
609fd4e5da5Sopenharmony_ci
610fd4e5da5Sopenharmony_ci  load_child = context->get_def_use_mgr()->GetDef(
611fd4e5da5Sopenharmony_ci      load_access_chain->GetSingleWordInOperand(1));
612fd4e5da5Sopenharmony_ci  store_child = context->get_def_use_mgr()->GetDef(
613fd4e5da5Sopenharmony_ci      store_access_chain->GetSingleWordInOperand(1));
614fd4e5da5Sopenharmony_ci
615fd4e5da5Sopenharmony_ci  load_node = analysis.AnalyzeInstruction(load_child);
616fd4e5da5Sopenharmony_ci  store_node = analysis.AnalyzeInstruction(store_child);
617fd4e5da5Sopenharmony_ci
618fd4e5da5Sopenharmony_ci  subtract_node = analysis.CreateSubtraction(store_node, load_node);
619fd4e5da5Sopenharmony_ci
620fd4e5da5Sopenharmony_ci  simplified_node = analysis.SimplifyExpression(subtract_node);
621fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->GetType(), SENode::Constant);
622fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->AsSEConstantNode()->FoldToSingleValue(), 0u);
623fd4e5da5Sopenharmony_ci
624fd4e5da5Sopenharmony_ci  // Testing [i] - [i+N] == -N
625fd4e5da5Sopenharmony_ci  load_access_chain =
626fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(loads[5]->GetSingleWordInOperand(0));
627fd4e5da5Sopenharmony_ci  store_access_chain =
628fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(stores[5]->GetSingleWordInOperand(0));
629fd4e5da5Sopenharmony_ci
630fd4e5da5Sopenharmony_ci  load_child = context->get_def_use_mgr()->GetDef(
631fd4e5da5Sopenharmony_ci      load_access_chain->GetSingleWordInOperand(1));
632fd4e5da5Sopenharmony_ci  store_child = context->get_def_use_mgr()->GetDef(
633fd4e5da5Sopenharmony_ci      store_access_chain->GetSingleWordInOperand(1));
634fd4e5da5Sopenharmony_ci
635fd4e5da5Sopenharmony_ci  load_node = analysis.AnalyzeInstruction(load_child);
636fd4e5da5Sopenharmony_ci  store_node = analysis.AnalyzeInstruction(store_child);
637fd4e5da5Sopenharmony_ci
638fd4e5da5Sopenharmony_ci  subtract_node = analysis.CreateSubtraction(store_node, load_node);
639fd4e5da5Sopenharmony_ci  simplified_node = analysis.SimplifyExpression(subtract_node);
640fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_node->GetType(), SENode::Negative);
641fd4e5da5Sopenharmony_ci}
642fd4e5da5Sopenharmony_ci
643fd4e5da5Sopenharmony_ci/*
644fd4e5da5Sopenharmony_ciGenerated from the following GLSL + --eliminate-local-multi-store
645fd4e5da5Sopenharmony_ci
646fd4e5da5Sopenharmony_ci#version 430
647fd4e5da5Sopenharmony_cilayout(location = 1) out float array[10];
648fd4e5da5Sopenharmony_cilayout(location = 2) flat in int loop_invariant;
649fd4e5da5Sopenharmony_civoid main(void) {
650fd4e5da5Sopenharmony_ci  for (int i = 0; i < 10; ++i) {
651fd4e5da5Sopenharmony_ci    array[i * 2 + i * 5] = array[i * i * 2];
652fd4e5da5Sopenharmony_ci    array[i * 2] = array[i * 5];
653fd4e5da5Sopenharmony_ci  }
654fd4e5da5Sopenharmony_ci}
655fd4e5da5Sopenharmony_ci
656fd4e5da5Sopenharmony_ci*/
657fd4e5da5Sopenharmony_ci
658fd4e5da5Sopenharmony_ciTEST_F(ScalarAnalysisTest, SimplifyMultiplyInductions) {
659fd4e5da5Sopenharmony_ci  const std::string text = R"(
660fd4e5da5Sopenharmony_ci               OpCapability Shader
661fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
662fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
663fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %2 "main" %3 %4
664fd4e5da5Sopenharmony_ci               OpExecutionMode %2 OriginUpperLeft
665fd4e5da5Sopenharmony_ci               OpSource GLSL 430
666fd4e5da5Sopenharmony_ci               OpName %2 "main"
667fd4e5da5Sopenharmony_ci               OpName %5 "i"
668fd4e5da5Sopenharmony_ci               OpName %3 "array"
669fd4e5da5Sopenharmony_ci               OpName %4 "loop_invariant"
670fd4e5da5Sopenharmony_ci               OpDecorate %3 Location 1
671fd4e5da5Sopenharmony_ci               OpDecorate %4 Flat
672fd4e5da5Sopenharmony_ci               OpDecorate %4 Location 2
673fd4e5da5Sopenharmony_ci          %6 = OpTypeVoid
674fd4e5da5Sopenharmony_ci          %7 = OpTypeFunction %6
675fd4e5da5Sopenharmony_ci          %8 = OpTypeInt 32 1
676fd4e5da5Sopenharmony_ci          %9 = OpTypePointer Function %8
677fd4e5da5Sopenharmony_ci         %10 = OpConstant %8 0
678fd4e5da5Sopenharmony_ci         %11 = OpConstant %8 10
679fd4e5da5Sopenharmony_ci         %12 = OpTypeBool
680fd4e5da5Sopenharmony_ci         %13 = OpTypeFloat 32
681fd4e5da5Sopenharmony_ci         %14 = OpTypeInt 32 0
682fd4e5da5Sopenharmony_ci         %15 = OpConstant %14 10
683fd4e5da5Sopenharmony_ci         %16 = OpTypeArray %13 %15
684fd4e5da5Sopenharmony_ci         %17 = OpTypePointer Output %16
685fd4e5da5Sopenharmony_ci          %3 = OpVariable %17 Output
686fd4e5da5Sopenharmony_ci         %18 = OpConstant %8 2
687fd4e5da5Sopenharmony_ci         %19 = OpConstant %8 5
688fd4e5da5Sopenharmony_ci         %20 = OpTypePointer Output %13
689fd4e5da5Sopenharmony_ci         %21 = OpConstant %8 1
690fd4e5da5Sopenharmony_ci         %22 = OpTypePointer Input %8
691fd4e5da5Sopenharmony_ci          %4 = OpVariable %22 Input
692fd4e5da5Sopenharmony_ci          %2 = OpFunction %6 None %7
693fd4e5da5Sopenharmony_ci         %23 = OpLabel
694fd4e5da5Sopenharmony_ci          %5 = OpVariable %9 Function
695fd4e5da5Sopenharmony_ci               OpStore %5 %10
696fd4e5da5Sopenharmony_ci               OpBranch %24
697fd4e5da5Sopenharmony_ci         %24 = OpLabel
698fd4e5da5Sopenharmony_ci         %25 = OpPhi %8 %10 %23 %26 %27
699fd4e5da5Sopenharmony_ci               OpLoopMerge %28 %27 None
700fd4e5da5Sopenharmony_ci               OpBranch %29
701fd4e5da5Sopenharmony_ci         %29 = OpLabel
702fd4e5da5Sopenharmony_ci         %30 = OpSLessThan %12 %25 %11
703fd4e5da5Sopenharmony_ci               OpBranchConditional %30 %31 %28
704fd4e5da5Sopenharmony_ci         %31 = OpLabel
705fd4e5da5Sopenharmony_ci         %32 = OpIMul %8 %25 %18
706fd4e5da5Sopenharmony_ci         %33 = OpIMul %8 %25 %19
707fd4e5da5Sopenharmony_ci         %34 = OpIAdd %8 %32 %33
708fd4e5da5Sopenharmony_ci         %35 = OpIMul %8 %25 %25
709fd4e5da5Sopenharmony_ci         %36 = OpIMul %8 %35 %18
710fd4e5da5Sopenharmony_ci         %37 = OpAccessChain %20 %3 %36
711fd4e5da5Sopenharmony_ci         %38 = OpLoad %13 %37
712fd4e5da5Sopenharmony_ci         %39 = OpAccessChain %20 %3 %34
713fd4e5da5Sopenharmony_ci               OpStore %39 %38
714fd4e5da5Sopenharmony_ci         %40 = OpIMul %8 %25 %18
715fd4e5da5Sopenharmony_ci         %41 = OpIMul %8 %25 %19
716fd4e5da5Sopenharmony_ci         %42 = OpAccessChain %20 %3 %41
717fd4e5da5Sopenharmony_ci         %43 = OpLoad %13 %42
718fd4e5da5Sopenharmony_ci         %44 = OpAccessChain %20 %3 %40
719fd4e5da5Sopenharmony_ci               OpStore %44 %43
720fd4e5da5Sopenharmony_ci               OpBranch %27
721fd4e5da5Sopenharmony_ci         %27 = OpLabel
722fd4e5da5Sopenharmony_ci         %26 = OpIAdd %8 %25 %21
723fd4e5da5Sopenharmony_ci               OpStore %5 %26
724fd4e5da5Sopenharmony_ci               OpBranch %24
725fd4e5da5Sopenharmony_ci         %28 = OpLabel
726fd4e5da5Sopenharmony_ci               OpReturn
727fd4e5da5Sopenharmony_ci               OpFunctionEnd
728fd4e5da5Sopenharmony_ci    )";
729fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> context =
730fd4e5da5Sopenharmony_ci      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
731fd4e5da5Sopenharmony_ci                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
732fd4e5da5Sopenharmony_ci  Module* module = context->module();
733fd4e5da5Sopenharmony_ci  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
734fd4e5da5Sopenharmony_ci                             << text << std::endl;
735fd4e5da5Sopenharmony_ci  const Function* f = spvtest::GetFunction(module, 2);
736fd4e5da5Sopenharmony_ci  ScalarEvolutionAnalysis analysis{context.get()};
737fd4e5da5Sopenharmony_ci
738fd4e5da5Sopenharmony_ci  const Instruction* loads[2] = {nullptr, nullptr};
739fd4e5da5Sopenharmony_ci  const Instruction* stores[2] = {nullptr, nullptr};
740fd4e5da5Sopenharmony_ci  int load_count = 0;
741fd4e5da5Sopenharmony_ci  int store_count = 0;
742fd4e5da5Sopenharmony_ci
743fd4e5da5Sopenharmony_ci  for (const Instruction& inst : *spvtest::GetBasicBlock(f, 31)) {
744fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpLoad) {
745fd4e5da5Sopenharmony_ci      loads[load_count] = &inst;
746fd4e5da5Sopenharmony_ci      ++load_count;
747fd4e5da5Sopenharmony_ci    }
748fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpStore) {
749fd4e5da5Sopenharmony_ci      stores[store_count] = &inst;
750fd4e5da5Sopenharmony_ci      ++store_count;
751fd4e5da5Sopenharmony_ci    }
752fd4e5da5Sopenharmony_ci  }
753fd4e5da5Sopenharmony_ci
754fd4e5da5Sopenharmony_ci  EXPECT_EQ(load_count, 2);
755fd4e5da5Sopenharmony_ci  EXPECT_EQ(store_count, 2);
756fd4e5da5Sopenharmony_ci
757fd4e5da5Sopenharmony_ci  Instruction* load_access_chain =
758fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(loads[0]->GetSingleWordInOperand(0));
759fd4e5da5Sopenharmony_ci  Instruction* store_access_chain =
760fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(stores[0]->GetSingleWordInOperand(0));
761fd4e5da5Sopenharmony_ci
762fd4e5da5Sopenharmony_ci  Instruction* load_child = context->get_def_use_mgr()->GetDef(
763fd4e5da5Sopenharmony_ci      load_access_chain->GetSingleWordInOperand(1));
764fd4e5da5Sopenharmony_ci  Instruction* store_child = context->get_def_use_mgr()->GetDef(
765fd4e5da5Sopenharmony_ci      store_access_chain->GetSingleWordInOperand(1));
766fd4e5da5Sopenharmony_ci
767fd4e5da5Sopenharmony_ci  SENode* store_node = analysis.AnalyzeInstruction(store_child);
768fd4e5da5Sopenharmony_ci
769fd4e5da5Sopenharmony_ci  SENode* store_simplified = analysis.SimplifyExpression(store_node);
770fd4e5da5Sopenharmony_ci
771fd4e5da5Sopenharmony_ci  load_access_chain =
772fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(loads[1]->GetSingleWordInOperand(0));
773fd4e5da5Sopenharmony_ci  store_access_chain =
774fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(stores[1]->GetSingleWordInOperand(0));
775fd4e5da5Sopenharmony_ci  load_child = context->get_def_use_mgr()->GetDef(
776fd4e5da5Sopenharmony_ci      load_access_chain->GetSingleWordInOperand(1));
777fd4e5da5Sopenharmony_ci  store_child = context->get_def_use_mgr()->GetDef(
778fd4e5da5Sopenharmony_ci      store_access_chain->GetSingleWordInOperand(1));
779fd4e5da5Sopenharmony_ci
780fd4e5da5Sopenharmony_ci  SENode* second_store =
781fd4e5da5Sopenharmony_ci      analysis.SimplifyExpression(analysis.AnalyzeInstruction(store_child));
782fd4e5da5Sopenharmony_ci  SENode* second_load =
783fd4e5da5Sopenharmony_ci      analysis.SimplifyExpression(analysis.AnalyzeInstruction(load_child));
784fd4e5da5Sopenharmony_ci  SENode* combined_add = analysis.SimplifyExpression(
785fd4e5da5Sopenharmony_ci      analysis.CreateAddNode(second_load, second_store));
786fd4e5da5Sopenharmony_ci
787fd4e5da5Sopenharmony_ci  // We're checking that the two recurrent expression have been correctly
788fd4e5da5Sopenharmony_ci  // folded. In store_simplified they will have been folded as the entire
789fd4e5da5Sopenharmony_ci  // expression was simplified as one. In combined_add the two expressions have
790fd4e5da5Sopenharmony_ci  // been simplified one after the other which means the recurrent expressions
791fd4e5da5Sopenharmony_ci  // aren't exactly the same but should still be folded as they are with respect
792fd4e5da5Sopenharmony_ci  // to the same loop.
793fd4e5da5Sopenharmony_ci  EXPECT_EQ(combined_add, store_simplified);
794fd4e5da5Sopenharmony_ci}
795fd4e5da5Sopenharmony_ci
796fd4e5da5Sopenharmony_ci/*
797fd4e5da5Sopenharmony_ciGenerated from the following GLSL + --eliminate-local-multi-store
798fd4e5da5Sopenharmony_ci
799fd4e5da5Sopenharmony_ci#version 430
800fd4e5da5Sopenharmony_civoid main(void) {
801fd4e5da5Sopenharmony_ci    for (int i = 0; i < 10; --i) {
802fd4e5da5Sopenharmony_ci        array[i] = array[i];
803fd4e5da5Sopenharmony_ci    }
804fd4e5da5Sopenharmony_ci}
805fd4e5da5Sopenharmony_ci
806fd4e5da5Sopenharmony_ci*/
807fd4e5da5Sopenharmony_ci
808fd4e5da5Sopenharmony_ciTEST_F(ScalarAnalysisTest, SimplifyNegativeSteps) {
809fd4e5da5Sopenharmony_ci  const std::string text = R"(
810fd4e5da5Sopenharmony_ci               OpCapability Shader
811fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
812fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
813fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %2 "main" %3 %4
814fd4e5da5Sopenharmony_ci               OpExecutionMode %2 OriginUpperLeft
815fd4e5da5Sopenharmony_ci               OpSource GLSL 430
816fd4e5da5Sopenharmony_ci               OpName %2 "main"
817fd4e5da5Sopenharmony_ci               OpName %5 "i"
818fd4e5da5Sopenharmony_ci               OpName %3 "array"
819fd4e5da5Sopenharmony_ci               OpName %4 "loop_invariant"
820fd4e5da5Sopenharmony_ci               OpDecorate %3 Location 1
821fd4e5da5Sopenharmony_ci               OpDecorate %4 Flat
822fd4e5da5Sopenharmony_ci               OpDecorate %4 Location 2
823fd4e5da5Sopenharmony_ci          %6 = OpTypeVoid
824fd4e5da5Sopenharmony_ci          %7 = OpTypeFunction %6
825fd4e5da5Sopenharmony_ci          %8 = OpTypeInt 32 1
826fd4e5da5Sopenharmony_ci          %9 = OpTypePointer Function %8
827fd4e5da5Sopenharmony_ci         %10 = OpConstant %8 0
828fd4e5da5Sopenharmony_ci         %11 = OpConstant %8 10
829fd4e5da5Sopenharmony_ci         %12 = OpTypeBool
830fd4e5da5Sopenharmony_ci         %13 = OpTypeFloat 32
831fd4e5da5Sopenharmony_ci         %14 = OpTypeInt 32 0
832fd4e5da5Sopenharmony_ci         %15 = OpConstant %14 10
833fd4e5da5Sopenharmony_ci         %16 = OpTypeArray %13 %15
834fd4e5da5Sopenharmony_ci         %17 = OpTypePointer Output %16
835fd4e5da5Sopenharmony_ci          %3 = OpVariable %17 Output
836fd4e5da5Sopenharmony_ci         %18 = OpTypePointer Output %13
837fd4e5da5Sopenharmony_ci         %19 = OpConstant %8 1
838fd4e5da5Sopenharmony_ci         %20 = OpTypePointer Input %8
839fd4e5da5Sopenharmony_ci          %4 = OpVariable %20 Input
840fd4e5da5Sopenharmony_ci          %2 = OpFunction %6 None %7
841fd4e5da5Sopenharmony_ci         %21 = OpLabel
842fd4e5da5Sopenharmony_ci          %5 = OpVariable %9 Function
843fd4e5da5Sopenharmony_ci               OpStore %5 %10
844fd4e5da5Sopenharmony_ci               OpBranch %22
845fd4e5da5Sopenharmony_ci         %22 = OpLabel
846fd4e5da5Sopenharmony_ci         %23 = OpPhi %8 %10 %21 %24 %25
847fd4e5da5Sopenharmony_ci               OpLoopMerge %26 %25 None
848fd4e5da5Sopenharmony_ci               OpBranch %27
849fd4e5da5Sopenharmony_ci         %27 = OpLabel
850fd4e5da5Sopenharmony_ci         %28 = OpSLessThan %12 %23 %11
851fd4e5da5Sopenharmony_ci               OpBranchConditional %28 %29 %26
852fd4e5da5Sopenharmony_ci         %29 = OpLabel
853fd4e5da5Sopenharmony_ci         %30 = OpAccessChain %18 %3 %23
854fd4e5da5Sopenharmony_ci         %31 = OpLoad %13 %30
855fd4e5da5Sopenharmony_ci         %32 = OpAccessChain %18 %3 %23
856fd4e5da5Sopenharmony_ci               OpStore %32 %31
857fd4e5da5Sopenharmony_ci               OpBranch %25
858fd4e5da5Sopenharmony_ci         %25 = OpLabel
859fd4e5da5Sopenharmony_ci         %24 = OpISub %8 %23 %19
860fd4e5da5Sopenharmony_ci               OpStore %5 %24
861fd4e5da5Sopenharmony_ci               OpBranch %22
862fd4e5da5Sopenharmony_ci         %26 = OpLabel
863fd4e5da5Sopenharmony_ci               OpReturn
864fd4e5da5Sopenharmony_ci               OpFunctionEnd
865fd4e5da5Sopenharmony_ci    )";
866fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> context =
867fd4e5da5Sopenharmony_ci      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
868fd4e5da5Sopenharmony_ci                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
869fd4e5da5Sopenharmony_ci  Module* module = context->module();
870fd4e5da5Sopenharmony_ci  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
871fd4e5da5Sopenharmony_ci                             << text << std::endl;
872fd4e5da5Sopenharmony_ci  const Function* f = spvtest::GetFunction(module, 2);
873fd4e5da5Sopenharmony_ci  ScalarEvolutionAnalysis analysis{context.get()};
874fd4e5da5Sopenharmony_ci
875fd4e5da5Sopenharmony_ci  const Instruction* loads[1] = {nullptr};
876fd4e5da5Sopenharmony_ci  int load_count = 0;
877fd4e5da5Sopenharmony_ci
878fd4e5da5Sopenharmony_ci  for (const Instruction& inst : *spvtest::GetBasicBlock(f, 29)) {
879fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpLoad) {
880fd4e5da5Sopenharmony_ci      loads[load_count] = &inst;
881fd4e5da5Sopenharmony_ci      ++load_count;
882fd4e5da5Sopenharmony_ci    }
883fd4e5da5Sopenharmony_ci  }
884fd4e5da5Sopenharmony_ci
885fd4e5da5Sopenharmony_ci  EXPECT_EQ(load_count, 1);
886fd4e5da5Sopenharmony_ci
887fd4e5da5Sopenharmony_ci  Instruction* load_access_chain =
888fd4e5da5Sopenharmony_ci      context->get_def_use_mgr()->GetDef(loads[0]->GetSingleWordInOperand(0));
889fd4e5da5Sopenharmony_ci  Instruction* load_child = context->get_def_use_mgr()->GetDef(
890fd4e5da5Sopenharmony_ci      load_access_chain->GetSingleWordInOperand(1));
891fd4e5da5Sopenharmony_ci
892fd4e5da5Sopenharmony_ci  SENode* load_node = analysis.AnalyzeInstruction(load_child);
893fd4e5da5Sopenharmony_ci
894fd4e5da5Sopenharmony_ci  EXPECT_TRUE(load_node);
895fd4e5da5Sopenharmony_ci  EXPECT_EQ(load_node->GetType(), SENode::RecurrentAddExpr);
896fd4e5da5Sopenharmony_ci  EXPECT_TRUE(load_node->AsSERecurrentNode());
897fd4e5da5Sopenharmony_ci
898fd4e5da5Sopenharmony_ci  SENode* child_1 = load_node->AsSERecurrentNode()->GetCoefficient();
899fd4e5da5Sopenharmony_ci  SENode* child_2 = load_node->AsSERecurrentNode()->GetOffset();
900fd4e5da5Sopenharmony_ci
901fd4e5da5Sopenharmony_ci  EXPECT_EQ(child_1->GetType(), SENode::Constant);
902fd4e5da5Sopenharmony_ci  EXPECT_EQ(child_2->GetType(), SENode::Constant);
903fd4e5da5Sopenharmony_ci
904fd4e5da5Sopenharmony_ci  EXPECT_EQ(child_1->AsSEConstantNode()->FoldToSingleValue(), -1);
905fd4e5da5Sopenharmony_ci  EXPECT_EQ(child_2->AsSEConstantNode()->FoldToSingleValue(), 0u);
906fd4e5da5Sopenharmony_ci
907fd4e5da5Sopenharmony_ci  SERecurrentNode* load_simplified =
908fd4e5da5Sopenharmony_ci      analysis.SimplifyExpression(load_node)->AsSERecurrentNode();
909fd4e5da5Sopenharmony_ci
910fd4e5da5Sopenharmony_ci  EXPECT_TRUE(load_simplified);
911fd4e5da5Sopenharmony_ci  EXPECT_EQ(load_node, load_simplified);
912fd4e5da5Sopenharmony_ci
913fd4e5da5Sopenharmony_ci  EXPECT_EQ(load_simplified->GetType(), SENode::RecurrentAddExpr);
914fd4e5da5Sopenharmony_ci  EXPECT_TRUE(load_simplified->AsSERecurrentNode());
915fd4e5da5Sopenharmony_ci
916fd4e5da5Sopenharmony_ci  SENode* simplified_child_1 =
917fd4e5da5Sopenharmony_ci      load_simplified->AsSERecurrentNode()->GetCoefficient();
918fd4e5da5Sopenharmony_ci  SENode* simplified_child_2 =
919fd4e5da5Sopenharmony_ci      load_simplified->AsSERecurrentNode()->GetOffset();
920fd4e5da5Sopenharmony_ci
921fd4e5da5Sopenharmony_ci  EXPECT_EQ(child_1, simplified_child_1);
922fd4e5da5Sopenharmony_ci  EXPECT_EQ(child_2, simplified_child_2);
923fd4e5da5Sopenharmony_ci}
924fd4e5da5Sopenharmony_ci
925fd4e5da5Sopenharmony_ci/*
926fd4e5da5Sopenharmony_ciGenerated from the following GLSL + --eliminate-local-multi-store
927fd4e5da5Sopenharmony_ci
928fd4e5da5Sopenharmony_ci#version 430
929fd4e5da5Sopenharmony_civoid main(void) {
930fd4e5da5Sopenharmony_ci    for (int i = 0; i < 10; --i) {
931fd4e5da5Sopenharmony_ci        array[i] = array[i];
932fd4e5da5Sopenharmony_ci    }
933fd4e5da5Sopenharmony_ci}
934fd4e5da5Sopenharmony_ci
935fd4e5da5Sopenharmony_ci*/
936fd4e5da5Sopenharmony_ci
937fd4e5da5Sopenharmony_ciTEST_F(ScalarAnalysisTest, SimplifyInductionsAndLoads) {
938fd4e5da5Sopenharmony_ci  const std::string text = R"(
939fd4e5da5Sopenharmony_ci               OpCapability Shader
940fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
941fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
942fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %2 "main" %3 %4
943fd4e5da5Sopenharmony_ci               OpExecutionMode %2 OriginUpperLeft
944fd4e5da5Sopenharmony_ci               OpSource GLSL 430
945fd4e5da5Sopenharmony_ci               OpName %2 "main"
946fd4e5da5Sopenharmony_ci               OpName %5 "i"
947fd4e5da5Sopenharmony_ci               OpName %3 "array"
948fd4e5da5Sopenharmony_ci               OpName %4 "N"
949fd4e5da5Sopenharmony_ci               OpDecorate %3 Location 1
950fd4e5da5Sopenharmony_ci               OpDecorate %4 Flat
951fd4e5da5Sopenharmony_ci               OpDecorate %4 Location 2
952fd4e5da5Sopenharmony_ci          %6 = OpTypeVoid
953fd4e5da5Sopenharmony_ci          %7 = OpTypeFunction %6
954fd4e5da5Sopenharmony_ci          %8 = OpTypeInt 32 1
955fd4e5da5Sopenharmony_ci          %9 = OpTypePointer Function %8
956fd4e5da5Sopenharmony_ci         %10 = OpConstant %8 0
957fd4e5da5Sopenharmony_ci         %11 = OpConstant %8 10
958fd4e5da5Sopenharmony_ci         %12 = OpTypeBool
959fd4e5da5Sopenharmony_ci         %13 = OpTypeFloat 32
960fd4e5da5Sopenharmony_ci         %14 = OpTypeInt 32 0
961fd4e5da5Sopenharmony_ci         %15 = OpConstant %14 10
962fd4e5da5Sopenharmony_ci         %16 = OpTypeArray %13 %15
963fd4e5da5Sopenharmony_ci         %17 = OpTypePointer Output %16
964fd4e5da5Sopenharmony_ci          %3 = OpVariable %17 Output
965fd4e5da5Sopenharmony_ci         %18 = OpConstant %8 2
966fd4e5da5Sopenharmony_ci         %19 = OpTypePointer Input %8
967fd4e5da5Sopenharmony_ci          %4 = OpVariable %19 Input
968fd4e5da5Sopenharmony_ci         %20 = OpTypePointer Output %13
969fd4e5da5Sopenharmony_ci         %21 = OpConstant %8 1
970fd4e5da5Sopenharmony_ci          %2 = OpFunction %6 None %7
971fd4e5da5Sopenharmony_ci         %22 = OpLabel
972fd4e5da5Sopenharmony_ci          %5 = OpVariable %9 Function
973fd4e5da5Sopenharmony_ci               OpStore %5 %10
974fd4e5da5Sopenharmony_ci               OpBranch %23
975fd4e5da5Sopenharmony_ci         %23 = OpLabel
976fd4e5da5Sopenharmony_ci         %24 = OpPhi %8 %10 %22 %25 %26
977fd4e5da5Sopenharmony_ci               OpLoopMerge %27 %26 None
978fd4e5da5Sopenharmony_ci               OpBranch %28
979fd4e5da5Sopenharmony_ci         %28 = OpLabel
980fd4e5da5Sopenharmony_ci         %29 = OpSLessThan %12 %24 %11
981fd4e5da5Sopenharmony_ci               OpBranchConditional %29 %30 %27
982fd4e5da5Sopenharmony_ci         %30 = OpLabel
983fd4e5da5Sopenharmony_ci         %31 = OpLoad %8 %4
984fd4e5da5Sopenharmony_ci         %32 = OpIMul %8 %18 %31
985fd4e5da5Sopenharmony_ci         %33 = OpIAdd %8 %24 %32
986fd4e5da5Sopenharmony_ci         %35 = OpIAdd %8 %24 %31
987fd4e5da5Sopenharmony_ci         %36 = OpAccessChain %20 %3 %35
988fd4e5da5Sopenharmony_ci         %37 = OpLoad %13 %36
989fd4e5da5Sopenharmony_ci         %38 = OpAccessChain %20 %3 %33
990fd4e5da5Sopenharmony_ci               OpStore %38 %37
991fd4e5da5Sopenharmony_ci         %39 = OpIMul %8 %18 %24
992fd4e5da5Sopenharmony_ci         %41 = OpIMul %8 %18 %31
993fd4e5da5Sopenharmony_ci         %42 = OpIAdd %8 %39 %41
994fd4e5da5Sopenharmony_ci         %43 = OpIAdd %8 %42 %21
995fd4e5da5Sopenharmony_ci         %44 = OpIMul %8 %18 %24
996fd4e5da5Sopenharmony_ci         %46 = OpIAdd %8 %44 %31
997fd4e5da5Sopenharmony_ci         %47 = OpIAdd %8 %46 %21
998fd4e5da5Sopenharmony_ci         %48 = OpAccessChain %20 %3 %47
999fd4e5da5Sopenharmony_ci         %49 = OpLoad %13 %48
1000fd4e5da5Sopenharmony_ci         %50 = OpAccessChain %20 %3 %43
1001fd4e5da5Sopenharmony_ci               OpStore %50 %49
1002fd4e5da5Sopenharmony_ci               OpBranch %26
1003fd4e5da5Sopenharmony_ci         %26 = OpLabel
1004fd4e5da5Sopenharmony_ci         %25 = OpISub %8 %24 %21
1005fd4e5da5Sopenharmony_ci               OpStore %5 %25
1006fd4e5da5Sopenharmony_ci               OpBranch %23
1007fd4e5da5Sopenharmony_ci         %27 = OpLabel
1008fd4e5da5Sopenharmony_ci               OpReturn
1009fd4e5da5Sopenharmony_ci               OpFunctionEnd
1010fd4e5da5Sopenharmony_ci    )";
1011fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> context =
1012fd4e5da5Sopenharmony_ci      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1013fd4e5da5Sopenharmony_ci                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1014fd4e5da5Sopenharmony_ci  Module* module = context->module();
1015fd4e5da5Sopenharmony_ci  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1016fd4e5da5Sopenharmony_ci                             << text << std::endl;
1017fd4e5da5Sopenharmony_ci  const Function* f = spvtest::GetFunction(module, 2);
1018fd4e5da5Sopenharmony_ci  ScalarEvolutionAnalysis analysis{context.get()};
1019fd4e5da5Sopenharmony_ci
1020fd4e5da5Sopenharmony_ci  std::vector<const Instruction*> loads{};
1021fd4e5da5Sopenharmony_ci  std::vector<const Instruction*> stores{};
1022fd4e5da5Sopenharmony_ci
1023fd4e5da5Sopenharmony_ci  for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) {
1024fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpLoad) {
1025fd4e5da5Sopenharmony_ci      loads.push_back(&inst);
1026fd4e5da5Sopenharmony_ci    }
1027fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpStore) {
1028fd4e5da5Sopenharmony_ci      stores.push_back(&inst);
1029fd4e5da5Sopenharmony_ci    }
1030fd4e5da5Sopenharmony_ci  }
1031fd4e5da5Sopenharmony_ci
1032fd4e5da5Sopenharmony_ci  EXPECT_EQ(loads.size(), 3u);
1033fd4e5da5Sopenharmony_ci  EXPECT_EQ(stores.size(), 2u);
1034fd4e5da5Sopenharmony_ci  {
1035fd4e5da5Sopenharmony_ci    Instruction* store_access_chain = context->get_def_use_mgr()->GetDef(
1036fd4e5da5Sopenharmony_ci        stores[0]->GetSingleWordInOperand(0));
1037fd4e5da5Sopenharmony_ci
1038fd4e5da5Sopenharmony_ci    Instruction* store_child = context->get_def_use_mgr()->GetDef(
1039fd4e5da5Sopenharmony_ci        store_access_chain->GetSingleWordInOperand(1));
1040fd4e5da5Sopenharmony_ci
1041fd4e5da5Sopenharmony_ci    SENode* store_node = analysis.AnalyzeInstruction(store_child);
1042fd4e5da5Sopenharmony_ci
1043fd4e5da5Sopenharmony_ci    SENode* store_simplified = analysis.SimplifyExpression(store_node);
1044fd4e5da5Sopenharmony_ci
1045fd4e5da5Sopenharmony_ci    Instruction* load_access_chain =
1046fd4e5da5Sopenharmony_ci        context->get_def_use_mgr()->GetDef(loads[1]->GetSingleWordInOperand(0));
1047fd4e5da5Sopenharmony_ci
1048fd4e5da5Sopenharmony_ci    Instruction* load_child = context->get_def_use_mgr()->GetDef(
1049fd4e5da5Sopenharmony_ci        load_access_chain->GetSingleWordInOperand(1));
1050fd4e5da5Sopenharmony_ci
1051fd4e5da5Sopenharmony_ci    SENode* load_node = analysis.AnalyzeInstruction(load_child);
1052fd4e5da5Sopenharmony_ci
1053fd4e5da5Sopenharmony_ci    SENode* load_simplified = analysis.SimplifyExpression(load_node);
1054fd4e5da5Sopenharmony_ci
1055fd4e5da5Sopenharmony_ci    SENode* difference =
1056fd4e5da5Sopenharmony_ci        analysis.CreateSubtraction(store_simplified, load_simplified);
1057fd4e5da5Sopenharmony_ci
1058fd4e5da5Sopenharmony_ci    SENode* difference_simplified = analysis.SimplifyExpression(difference);
1059fd4e5da5Sopenharmony_ci
1060fd4e5da5Sopenharmony_ci    // Check that i+2*N  -  i*N, turns into just N when both sides have already
1061fd4e5da5Sopenharmony_ci    // been simplified into a single recurrent expression.
1062fd4e5da5Sopenharmony_ci    EXPECT_EQ(difference_simplified->GetType(), SENode::ValueUnknown);
1063fd4e5da5Sopenharmony_ci
1064fd4e5da5Sopenharmony_ci    // Check that the inverse, i*N - i+2*N turns into -N.
1065fd4e5da5Sopenharmony_ci    SENode* difference_inverse = analysis.SimplifyExpression(
1066fd4e5da5Sopenharmony_ci        analysis.CreateSubtraction(load_simplified, store_simplified));
1067fd4e5da5Sopenharmony_ci
1068fd4e5da5Sopenharmony_ci    EXPECT_EQ(difference_inverse->GetType(), SENode::Negative);
1069fd4e5da5Sopenharmony_ci    EXPECT_EQ(difference_inverse->GetChild(0)->GetType(), SENode::ValueUnknown);
1070fd4e5da5Sopenharmony_ci    EXPECT_EQ(difference_inverse->GetChild(0), difference_simplified);
1071fd4e5da5Sopenharmony_ci  }
1072fd4e5da5Sopenharmony_ci
1073fd4e5da5Sopenharmony_ci  {
1074fd4e5da5Sopenharmony_ci    Instruction* store_access_chain = context->get_def_use_mgr()->GetDef(
1075fd4e5da5Sopenharmony_ci        stores[1]->GetSingleWordInOperand(0));
1076fd4e5da5Sopenharmony_ci
1077fd4e5da5Sopenharmony_ci    Instruction* store_child = context->get_def_use_mgr()->GetDef(
1078fd4e5da5Sopenharmony_ci        store_access_chain->GetSingleWordInOperand(1));
1079fd4e5da5Sopenharmony_ci    SENode* store_node = analysis.AnalyzeInstruction(store_child);
1080fd4e5da5Sopenharmony_ci    SENode* store_simplified = analysis.SimplifyExpression(store_node);
1081fd4e5da5Sopenharmony_ci
1082fd4e5da5Sopenharmony_ci    Instruction* load_access_chain =
1083fd4e5da5Sopenharmony_ci        context->get_def_use_mgr()->GetDef(loads[2]->GetSingleWordInOperand(0));
1084fd4e5da5Sopenharmony_ci
1085fd4e5da5Sopenharmony_ci    Instruction* load_child = context->get_def_use_mgr()->GetDef(
1086fd4e5da5Sopenharmony_ci        load_access_chain->GetSingleWordInOperand(1));
1087fd4e5da5Sopenharmony_ci
1088fd4e5da5Sopenharmony_ci    SENode* load_node = analysis.AnalyzeInstruction(load_child);
1089fd4e5da5Sopenharmony_ci
1090fd4e5da5Sopenharmony_ci    SENode* load_simplified = analysis.SimplifyExpression(load_node);
1091fd4e5da5Sopenharmony_ci
1092fd4e5da5Sopenharmony_ci    SENode* difference =
1093fd4e5da5Sopenharmony_ci        analysis.CreateSubtraction(store_simplified, load_simplified);
1094fd4e5da5Sopenharmony_ci    SENode* difference_simplified = analysis.SimplifyExpression(difference);
1095fd4e5da5Sopenharmony_ci
1096fd4e5da5Sopenharmony_ci    // Check that 2*i + 2*N + 1  -  2*i + N + 1, turns into just N when both
1097fd4e5da5Sopenharmony_ci    // sides have already been simplified into a single recurrent expression.
1098fd4e5da5Sopenharmony_ci    EXPECT_EQ(difference_simplified->GetType(), SENode::ValueUnknown);
1099fd4e5da5Sopenharmony_ci
1100fd4e5da5Sopenharmony_ci    // Check that the inverse, (2*i + N + 1)  -  (2*i + 2*N + 1) turns into -N.
1101fd4e5da5Sopenharmony_ci    SENode* difference_inverse = analysis.SimplifyExpression(
1102fd4e5da5Sopenharmony_ci        analysis.CreateSubtraction(load_simplified, store_simplified));
1103fd4e5da5Sopenharmony_ci
1104fd4e5da5Sopenharmony_ci    EXPECT_EQ(difference_inverse->GetType(), SENode::Negative);
1105fd4e5da5Sopenharmony_ci    EXPECT_EQ(difference_inverse->GetChild(0)->GetType(), SENode::ValueUnknown);
1106fd4e5da5Sopenharmony_ci    EXPECT_EQ(difference_inverse->GetChild(0), difference_simplified);
1107fd4e5da5Sopenharmony_ci  }
1108fd4e5da5Sopenharmony_ci}
1109fd4e5da5Sopenharmony_ci
1110fd4e5da5Sopenharmony_ci/* Generated from the following GLSL + --eliminate-local-multi-store
1111fd4e5da5Sopenharmony_ci
1112fd4e5da5Sopenharmony_ci  #version 430
1113fd4e5da5Sopenharmony_ci  layout(location = 1) out float array[10];
1114fd4e5da5Sopenharmony_ci  layout(location = 2) flat in int N;
1115fd4e5da5Sopenharmony_ci  void main(void) {
1116fd4e5da5Sopenharmony_ci    int step = 0;
1117fd4e5da5Sopenharmony_ci    for (int i = 0; i < N; i += step) {
1118fd4e5da5Sopenharmony_ci      step++;
1119fd4e5da5Sopenharmony_ci    }
1120fd4e5da5Sopenharmony_ci  }
1121fd4e5da5Sopenharmony_ci*/
1122fd4e5da5Sopenharmony_ciTEST_F(ScalarAnalysisTest, InductionWithVariantStep) {
1123fd4e5da5Sopenharmony_ci  const std::string text = R"(
1124fd4e5da5Sopenharmony_ci               OpCapability Shader
1125fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
1126fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
1127fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %2 "main" %3 %4
1128fd4e5da5Sopenharmony_ci               OpExecutionMode %2 OriginUpperLeft
1129fd4e5da5Sopenharmony_ci               OpSource GLSL 430
1130fd4e5da5Sopenharmony_ci               OpName %2 "main"
1131fd4e5da5Sopenharmony_ci               OpName %5 "step"
1132fd4e5da5Sopenharmony_ci               OpName %6 "i"
1133fd4e5da5Sopenharmony_ci               OpName %3 "N"
1134fd4e5da5Sopenharmony_ci               OpName %4 "array"
1135fd4e5da5Sopenharmony_ci               OpDecorate %3 Flat
1136fd4e5da5Sopenharmony_ci               OpDecorate %3 Location 2
1137fd4e5da5Sopenharmony_ci               OpDecorate %4 Location 1
1138fd4e5da5Sopenharmony_ci          %7 = OpTypeVoid
1139fd4e5da5Sopenharmony_ci          %8 = OpTypeFunction %7
1140fd4e5da5Sopenharmony_ci          %9 = OpTypeInt 32 1
1141fd4e5da5Sopenharmony_ci         %10 = OpTypePointer Function %9
1142fd4e5da5Sopenharmony_ci         %11 = OpConstant %9 0
1143fd4e5da5Sopenharmony_ci         %12 = OpTypePointer Input %9
1144fd4e5da5Sopenharmony_ci          %3 = OpVariable %12 Input
1145fd4e5da5Sopenharmony_ci         %13 = OpTypeBool
1146fd4e5da5Sopenharmony_ci         %14 = OpConstant %9 1
1147fd4e5da5Sopenharmony_ci         %15 = OpTypeFloat 32
1148fd4e5da5Sopenharmony_ci         %16 = OpTypeInt 32 0
1149fd4e5da5Sopenharmony_ci         %17 = OpConstant %16 10
1150fd4e5da5Sopenharmony_ci         %18 = OpTypeArray %15 %17
1151fd4e5da5Sopenharmony_ci         %19 = OpTypePointer Output %18
1152fd4e5da5Sopenharmony_ci          %4 = OpVariable %19 Output
1153fd4e5da5Sopenharmony_ci          %2 = OpFunction %7 None %8
1154fd4e5da5Sopenharmony_ci         %20 = OpLabel
1155fd4e5da5Sopenharmony_ci          %5 = OpVariable %10 Function
1156fd4e5da5Sopenharmony_ci          %6 = OpVariable %10 Function
1157fd4e5da5Sopenharmony_ci               OpStore %5 %11
1158fd4e5da5Sopenharmony_ci               OpStore %6 %11
1159fd4e5da5Sopenharmony_ci               OpBranch %21
1160fd4e5da5Sopenharmony_ci         %21 = OpLabel
1161fd4e5da5Sopenharmony_ci         %22 = OpPhi %9 %11 %20 %23 %24
1162fd4e5da5Sopenharmony_ci         %25 = OpPhi %9 %11 %20 %26 %24
1163fd4e5da5Sopenharmony_ci               OpLoopMerge %27 %24 None
1164fd4e5da5Sopenharmony_ci               OpBranch %28
1165fd4e5da5Sopenharmony_ci         %28 = OpLabel
1166fd4e5da5Sopenharmony_ci         %29 = OpLoad %9 %3
1167fd4e5da5Sopenharmony_ci         %30 = OpSLessThan %13 %25 %29
1168fd4e5da5Sopenharmony_ci               OpBranchConditional %30 %31 %27
1169fd4e5da5Sopenharmony_ci         %31 = OpLabel
1170fd4e5da5Sopenharmony_ci         %23 = OpIAdd %9 %22 %14
1171fd4e5da5Sopenharmony_ci               OpStore %5 %23
1172fd4e5da5Sopenharmony_ci               OpBranch %24
1173fd4e5da5Sopenharmony_ci         %24 = OpLabel
1174fd4e5da5Sopenharmony_ci         %26 = OpIAdd %9 %25 %23
1175fd4e5da5Sopenharmony_ci               OpStore %6 %26
1176fd4e5da5Sopenharmony_ci               OpBranch %21
1177fd4e5da5Sopenharmony_ci         %27 = OpLabel
1178fd4e5da5Sopenharmony_ci               OpReturn
1179fd4e5da5Sopenharmony_ci               OpFunctionEnd
1180fd4e5da5Sopenharmony_ci  )";
1181fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> context =
1182fd4e5da5Sopenharmony_ci      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1183fd4e5da5Sopenharmony_ci                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1184fd4e5da5Sopenharmony_ci  Module* module = context->module();
1185fd4e5da5Sopenharmony_ci  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1186fd4e5da5Sopenharmony_ci                             << text << std::endl;
1187fd4e5da5Sopenharmony_ci  const Function* f = spvtest::GetFunction(module, 2);
1188fd4e5da5Sopenharmony_ci  ScalarEvolutionAnalysis analysis{context.get()};
1189fd4e5da5Sopenharmony_ci
1190fd4e5da5Sopenharmony_ci  std::vector<const Instruction*> phis{};
1191fd4e5da5Sopenharmony_ci
1192fd4e5da5Sopenharmony_ci  for (const Instruction& inst : *spvtest::GetBasicBlock(f, 21)) {
1193fd4e5da5Sopenharmony_ci    if (inst.opcode() == spv::Op::OpPhi) {
1194fd4e5da5Sopenharmony_ci      phis.push_back(&inst);
1195fd4e5da5Sopenharmony_ci    }
1196fd4e5da5Sopenharmony_ci  }
1197fd4e5da5Sopenharmony_ci
1198fd4e5da5Sopenharmony_ci  EXPECT_EQ(phis.size(), 2u);
1199fd4e5da5Sopenharmony_ci  SENode* phi_node_1 = analysis.AnalyzeInstruction(phis[0]);
1200fd4e5da5Sopenharmony_ci  SENode* phi_node_2 = analysis.AnalyzeInstruction(phis[1]);
1201fd4e5da5Sopenharmony_ci  EXPECT_NE(phi_node_1, nullptr);
1202fd4e5da5Sopenharmony_ci  EXPECT_NE(phi_node_2, nullptr);
1203fd4e5da5Sopenharmony_ci
1204fd4e5da5Sopenharmony_ci  EXPECT_EQ(phi_node_1->GetType(), SENode::RecurrentAddExpr);
1205fd4e5da5Sopenharmony_ci  EXPECT_EQ(phi_node_2->GetType(), SENode::CanNotCompute);
1206fd4e5da5Sopenharmony_ci
1207fd4e5da5Sopenharmony_ci  SENode* simplified_1 = analysis.SimplifyExpression(phi_node_1);
1208fd4e5da5Sopenharmony_ci  SENode* simplified_2 = analysis.SimplifyExpression(phi_node_2);
1209fd4e5da5Sopenharmony_ci
1210fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_1->GetType(), SENode::RecurrentAddExpr);
1211fd4e5da5Sopenharmony_ci  EXPECT_EQ(simplified_2->GetType(), SENode::CanNotCompute);
1212fd4e5da5Sopenharmony_ci}
1213fd4e5da5Sopenharmony_ci
1214fd4e5da5Sopenharmony_ci}  // namespace
1215fd4e5da5Sopenharmony_ci}  // namespace opt
1216fd4e5da5Sopenharmony_ci}  // namespace spvtools
1217