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