1// Copyright (c) 2017 Pierre Moreau 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#include <memory> 16#include <string> 17#include <vector> 18 19#include "source/opt/build_module.h" 20#include "source/opt/ir_context.h" 21#include "source/opt/pass_manager.h" 22#include "source/opt/remove_duplicates_pass.h" 23#include "source/spirv_constant.h" 24#include "test/unit_spirv.h" 25 26namespace spvtools { 27namespace opt { 28namespace { 29 30class RemoveDuplicatesTest : public ::testing::Test { 31 public: 32 RemoveDuplicatesTest() 33 : tools_(SPV_ENV_UNIVERSAL_1_2), 34 context_(), 35 consumer_([this](spv_message_level_t level, const char*, 36 const spv_position_t& position, const char* message) { 37 if (!error_message_.empty()) error_message_ += "\n"; 38 switch (level) { 39 case SPV_MSG_FATAL: 40 case SPV_MSG_INTERNAL_ERROR: 41 case SPV_MSG_ERROR: 42 error_message_ += "ERROR"; 43 break; 44 case SPV_MSG_WARNING: 45 error_message_ += "WARNING"; 46 break; 47 case SPV_MSG_INFO: 48 error_message_ += "INFO"; 49 break; 50 case SPV_MSG_DEBUG: 51 error_message_ += "DEBUG"; 52 break; 53 } 54 error_message_ += 55 ": " + std::to_string(position.index) + ": " + message; 56 }), 57 disassemble_options_(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER), 58 error_message_() { 59 tools_.SetMessageConsumer(consumer_); 60 } 61 62 void TearDown() override { error_message_.clear(); } 63 64 std::string RunPass(const std::string& text) { 65 context_ = spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer_, text); 66 if (!context_.get()) return std::string(); 67 68 PassManager manager; 69 manager.SetMessageConsumer(consumer_); 70 manager.AddPass<RemoveDuplicatesPass>(); 71 72 Pass::Status pass_res = manager.Run(context_.get()); 73 if (pass_res == Pass::Status::Failure) return std::string(); 74 75 return ModuleToText(); 76 } 77 78 // Disassembles |binary| and outputs the result in |text|. If |text| is a 79 // null pointer, SPV_ERROR_INVALID_POINTER is returned. 80 spv_result_t Disassemble(const std::vector<uint32_t>& binary, 81 std::string* text) { 82 if (!text) return SPV_ERROR_INVALID_POINTER; 83 return tools_.Disassemble(binary, text, disassemble_options_) 84 ? SPV_SUCCESS 85 : SPV_ERROR_INVALID_BINARY; 86 } 87 88 // Returns the accumulated error messages for the test. 89 std::string GetErrorMessage() const { return error_message_; } 90 91 std::string ToText(const std::vector<Instruction*>& inst) { 92 std::vector<uint32_t> binary = {spv::MagicNumber, 0x10200, 0u, 2u, 0u}; 93 for (const Instruction* i : inst) 94 i->ToBinaryWithoutAttachedDebugInsts(&binary); 95 std::string text; 96 Disassemble(binary, &text); 97 return text; 98 } 99 100 std::string ModuleToText() { 101 std::vector<uint32_t> binary; 102 context_->module()->ToBinary(&binary, false); 103 std::string text; 104 Disassemble(binary, &text); 105 return text; 106 } 107 108 private: 109 spvtools::SpirvTools 110 tools_; // An instance for calling SPIRV-Tools functionalities. 111 std::unique_ptr<IRContext> context_; 112 spvtools::MessageConsumer consumer_; 113 uint32_t disassemble_options_; 114 std::string error_message_; 115}; 116 117TEST_F(RemoveDuplicatesTest, DuplicateCapabilities) { 118 const std::string spirv = R"( 119OpCapability Shader 120OpCapability Linkage 121OpCapability Shader 122OpMemoryModel Logical GLSL450 123)"; 124 const std::string after = R"(OpCapability Shader 125OpCapability Linkage 126OpMemoryModel Logical GLSL450 127)"; 128 129 EXPECT_EQ(RunPass(spirv), after); 130 EXPECT_EQ(GetErrorMessage(), ""); 131} 132 133TEST_F(RemoveDuplicatesTest, DuplicateExtInstImports) { 134 const std::string spirv = R"( 135OpCapability Shader 136OpCapability Linkage 137%1 = OpExtInstImport "OpenCL.std" 138%2 = OpExtInstImport "OpenCL.std" 139%3 = OpExtInstImport "GLSL.std.450" 140OpMemoryModel Logical GLSL450 141)"; 142 const std::string after = R"(OpCapability Shader 143OpCapability Linkage 144%1 = OpExtInstImport "OpenCL.std" 145%3 = OpExtInstImport "GLSL.std.450" 146OpMemoryModel Logical GLSL450 147)"; 148 149 EXPECT_EQ(RunPass(spirv), after); 150 EXPECT_EQ(GetErrorMessage(), ""); 151} 152 153TEST_F(RemoveDuplicatesTest, DuplicateTypes) { 154 const std::string spirv = R"( 155OpCapability Shader 156OpCapability Linkage 157OpMemoryModel Logical GLSL450 158%1 = OpTypeInt 32 0 159%2 = OpTypeInt 32 0 160%3 = OpTypeStruct %1 %2 161)"; 162 const std::string after = R"(OpCapability Shader 163OpCapability Linkage 164OpMemoryModel Logical GLSL450 165%1 = OpTypeInt 32 0 166%3 = OpTypeStruct %1 %1 167)"; 168 169 EXPECT_EQ(RunPass(spirv), after); 170 EXPECT_EQ(GetErrorMessage(), ""); 171} 172 173TEST_F(RemoveDuplicatesTest, SameTypeDifferentMemberDecoration) { 174 const std::string spirv = R"( 175OpCapability Shader 176OpCapability Linkage 177OpMemoryModel Logical GLSL450 178OpDecorate %1 GLSLPacked 179%2 = OpTypeInt 32 0 180%1 = OpTypeStruct %2 %2 181%3 = OpTypeStruct %2 %2 182)"; 183 const std::string after = R"(OpCapability Shader 184OpCapability Linkage 185OpMemoryModel Logical GLSL450 186OpDecorate %1 GLSLPacked 187%2 = OpTypeInt 32 0 188%1 = OpTypeStruct %2 %2 189%3 = OpTypeStruct %2 %2 190)"; 191 192 EXPECT_EQ(RunPass(spirv), after); 193 EXPECT_EQ(GetErrorMessage(), ""); 194} 195 196TEST_F(RemoveDuplicatesTest, SameTypeAndMemberDecoration) { 197 const std::string spirv = R"( 198OpCapability Shader 199OpCapability Linkage 200OpMemoryModel Logical GLSL450 201OpDecorate %1 GLSLPacked 202OpDecorate %2 GLSLPacked 203%3 = OpTypeInt 32 0 204%1 = OpTypeStruct %3 %3 205%2 = OpTypeStruct %3 %3 206)"; 207 const std::string after = R"(OpCapability Shader 208OpCapability Linkage 209OpMemoryModel Logical GLSL450 210OpDecorate %1 GLSLPacked 211%3 = OpTypeInt 32 0 212%1 = OpTypeStruct %3 %3 213)"; 214 215 EXPECT_EQ(RunPass(spirv), after); 216 EXPECT_EQ(GetErrorMessage(), ""); 217} 218 219TEST_F(RemoveDuplicatesTest, SameTypeAndDifferentName) { 220 const std::string spirv = R"( 221OpCapability Shader 222OpCapability Linkage 223OpMemoryModel Logical GLSL450 224OpName %1 "Type1" 225OpName %2 "Type2" 226%3 = OpTypeInt 32 0 227%1 = OpTypeStruct %3 %3 228%2 = OpTypeStruct %3 %3 229)"; 230 const std::string after = R"(OpCapability Shader 231OpCapability Linkage 232OpMemoryModel Logical GLSL450 233OpName %1 "Type1" 234%3 = OpTypeInt 32 0 235%1 = OpTypeStruct %3 %3 236)"; 237 238 EXPECT_EQ(RunPass(spirv), after); 239 EXPECT_EQ(GetErrorMessage(), ""); 240} 241 242// Check that #1033 has been fixed. 243TEST_F(RemoveDuplicatesTest, DoNotRemoveDifferentOpDecorationGroup) { 244 const std::string spirv = R"( 245OpCapability Shader 246OpCapability Linkage 247OpMemoryModel Logical GLSL450 248OpDecorate %1 Constant 249%1 = OpDecorationGroup 250OpDecorate %2 Restrict 251%2 = OpDecorationGroup 252OpGroupDecorate %3 %1 %2 253%4 = OpTypeInt 32 0 254%3 = OpVariable %4 Uniform 255)"; 256 const std::string after = R"(OpCapability Shader 257OpCapability Linkage 258OpMemoryModel Logical GLSL450 259OpDecorate %1 Constant 260%1 = OpDecorationGroup 261OpDecorate %2 Restrict 262%2 = OpDecorationGroup 263OpGroupDecorate %3 %1 %2 264%4 = OpTypeInt 32 0 265%3 = OpVariable %4 Uniform 266)"; 267 268 EXPECT_EQ(RunPass(spirv), after); 269 EXPECT_EQ(GetErrorMessage(), ""); 270} 271 272TEST_F(RemoveDuplicatesTest, DifferentDecorationGroup) { 273 const std::string spirv = R"( 274OpCapability Shader 275OpCapability Linkage 276OpMemoryModel Logical GLSL450 277OpDecorate %1 Constant 278OpDecorate %1 Restrict 279%1 = OpDecorationGroup 280OpDecorate %2 Constant 281%2 = OpDecorationGroup 282OpGroupDecorate %1 %3 283OpGroupDecorate %2 %4 284%5 = OpTypeInt 32 0 285%3 = OpVariable %5 Uniform 286%4 = OpVariable %5 Uniform 287)"; 288 const std::string after = R"(OpCapability Shader 289OpCapability Linkage 290OpMemoryModel Logical GLSL450 291OpDecorate %1 Constant 292OpDecorate %1 Restrict 293%1 = OpDecorationGroup 294OpDecorate %2 Constant 295%2 = OpDecorationGroup 296OpGroupDecorate %1 %3 297OpGroupDecorate %2 %4 298%5 = OpTypeInt 32 0 299%3 = OpVariable %5 Uniform 300%4 = OpVariable %5 Uniform 301)"; 302 303 EXPECT_EQ(RunPass(spirv), after); 304 EXPECT_EQ(GetErrorMessage(), ""); 305} 306 307// Test what happens when a type is a resource type. For now we are merging 308// them, but, if we want to merge types and make reflection work (issue #1372), 309// we will not be able to merge %2 and %3 below. 310TEST_F(RemoveDuplicatesTest, DontMergeNestedResourceTypes) { 311 const std::string spirv = R"(OpCapability Shader 312OpMemoryModel Logical GLSL450 313OpSource HLSL 600 314OpName %1 "PositionAdjust" 315OpMemberName %1 0 "XAdjust" 316OpName %2 "NormalAdjust" 317OpMemberName %2 0 "XDir" 318OpMemberName %3 0 "AdjustXYZ" 319OpMemberName %3 1 "AdjustDir" 320OpName %4 "Constants" 321OpMemberDecorate %1 0 Offset 0 322OpMemberDecorate %2 0 Offset 0 323OpMemberDecorate %3 0 Offset 0 324OpMemberDecorate %3 1 Offset 16 325OpDecorate %3 Block 326OpDecorate %4 DescriptorSet 0 327OpDecorate %4 Binding 0 328%5 = OpTypeFloat 32 329%6 = OpTypeVector %5 3 330%1 = OpTypeStruct %6 331%2 = OpTypeStruct %6 332%3 = OpTypeStruct %1 %2 333%7 = OpTypePointer Uniform %3 334%4 = OpVariable %7 Uniform 335)"; 336 337 const std::string result = R"(OpCapability Shader 338OpMemoryModel Logical GLSL450 339OpSource HLSL 600 340OpName %1 "PositionAdjust" 341OpMemberName %1 0 "XAdjust" 342OpMemberName %3 0 "AdjustXYZ" 343OpMemberName %3 1 "AdjustDir" 344OpName %4 "Constants" 345OpMemberDecorate %1 0 Offset 0 346OpMemberDecorate %3 0 Offset 0 347OpMemberDecorate %3 1 Offset 16 348OpDecorate %3 Block 349OpDecorate %4 DescriptorSet 0 350OpDecorate %4 Binding 0 351%5 = OpTypeFloat 32 352%6 = OpTypeVector %5 3 353%1 = OpTypeStruct %6 354%3 = OpTypeStruct %1 %1 355%7 = OpTypePointer Uniform %3 356%4 = OpVariable %7 Uniform 357)"; 358 359 EXPECT_EQ(RunPass(spirv), result); 360 EXPECT_EQ(GetErrorMessage(), ""); 361} 362 363// See comment for DontMergeNestedResourceTypes. 364TEST_F(RemoveDuplicatesTest, DontMergeResourceTypes) { 365 const std::string spirv = R"(OpCapability Shader 366OpMemoryModel Logical GLSL450 367OpSource HLSL 600 368OpName %1 "PositionAdjust" 369OpMemberName %1 0 "XAdjust" 370OpName %2 "NormalAdjust" 371OpMemberName %2 0 "XDir" 372OpName %3 "Constants" 373OpMemberDecorate %1 0 Offset 0 374OpMemberDecorate %2 0 Offset 0 375OpDecorate %3 DescriptorSet 0 376OpDecorate %3 Binding 0 377OpDecorate %4 DescriptorSet 1 378OpDecorate %4 Binding 0 379%5 = OpTypeFloat 32 380%6 = OpTypeVector %5 3 381%1 = OpTypeStruct %6 382%2 = OpTypeStruct %6 383%7 = OpTypePointer Uniform %1 384%8 = OpTypePointer Uniform %2 385%3 = OpVariable %7 Uniform 386%4 = OpVariable %8 Uniform 387)"; 388 389 const std::string result = R"(OpCapability Shader 390OpMemoryModel Logical GLSL450 391OpSource HLSL 600 392OpName %1 "PositionAdjust" 393OpMemberName %1 0 "XAdjust" 394OpName %3 "Constants" 395OpMemberDecorate %1 0 Offset 0 396OpDecorate %3 DescriptorSet 0 397OpDecorate %3 Binding 0 398OpDecorate %4 DescriptorSet 1 399OpDecorate %4 Binding 0 400%5 = OpTypeFloat 32 401%6 = OpTypeVector %5 3 402%1 = OpTypeStruct %6 403%7 = OpTypePointer Uniform %1 404%3 = OpVariable %7 Uniform 405%4 = OpVariable %7 Uniform 406)"; 407 408 EXPECT_EQ(RunPass(spirv), result); 409 EXPECT_EQ(GetErrorMessage(), ""); 410} 411 412// See comment for DontMergeNestedResourceTypes. 413TEST_F(RemoveDuplicatesTest, DontMergeResourceTypesContainingArray) { 414 const std::string spirv = R"(OpCapability Shader 415OpMemoryModel Logical GLSL450 416OpSource HLSL 600 417OpName %1 "PositionAdjust" 418OpMemberName %1 0 "XAdjust" 419OpName %2 "NormalAdjust" 420OpMemberName %2 0 "XDir" 421OpName %3 "Constants" 422OpMemberDecorate %1 0 Offset 0 423OpMemberDecorate %2 0 Offset 0 424OpDecorate %3 DescriptorSet 0 425OpDecorate %3 Binding 0 426OpDecorate %4 DescriptorSet 1 427OpDecorate %4 Binding 0 428%5 = OpTypeFloat 32 429%6 = OpTypeVector %5 3 430%1 = OpTypeStruct %6 431%2 = OpTypeStruct %6 432%7 = OpTypeInt 32 0 433%8 = OpConstant %7 4 434%9 = OpTypeArray %1 %8 435%10 = OpTypeArray %2 %8 436%11 = OpTypePointer Uniform %9 437%12 = OpTypePointer Uniform %10 438%3 = OpVariable %11 Uniform 439%4 = OpVariable %12 Uniform 440)"; 441 442 const std::string result = R"(OpCapability Shader 443OpMemoryModel Logical GLSL450 444OpSource HLSL 600 445OpName %1 "PositionAdjust" 446OpMemberName %1 0 "XAdjust" 447OpName %3 "Constants" 448OpMemberDecorate %1 0 Offset 0 449OpDecorate %3 DescriptorSet 0 450OpDecorate %3 Binding 0 451OpDecorate %4 DescriptorSet 1 452OpDecorate %4 Binding 0 453%5 = OpTypeFloat 32 454%6 = OpTypeVector %5 3 455%1 = OpTypeStruct %6 456%7 = OpTypeInt 32 0 457%8 = OpConstant %7 4 458%9 = OpTypeArray %1 %8 459%11 = OpTypePointer Uniform %9 460%3 = OpVariable %11 Uniform 461%4 = OpVariable %11 Uniform 462)"; 463 464 EXPECT_EQ(RunPass(spirv), result); 465 EXPECT_EQ(GetErrorMessage(), ""); 466} 467 468// Test that we merge the type of a resource with a type that is not the type 469// a resource. The resource type appears first in this case. We must keep 470// the resource type. 471TEST_F(RemoveDuplicatesTest, MergeResourceTypeWithNonresourceType1) { 472 const std::string spirv = R"(OpCapability Shader 473OpMemoryModel Logical GLSL450 474OpSource HLSL 600 475OpName %1 "PositionAdjust" 476OpMemberName %1 0 "XAdjust" 477OpName %2 "NormalAdjust" 478OpMemberName %2 0 "XDir" 479OpName %3 "Constants" 480OpMemberDecorate %1 0 Offset 0 481OpMemberDecorate %2 0 Offset 0 482OpDecorate %3 DescriptorSet 0 483OpDecorate %3 Binding 0 484%4 = OpTypeFloat 32 485%5 = OpTypeVector %4 3 486%1 = OpTypeStruct %5 487%2 = OpTypeStruct %5 488%6 = OpTypePointer Uniform %1 489%7 = OpTypePointer Uniform %2 490%3 = OpVariable %6 Uniform 491%8 = OpVariable %7 Uniform 492)"; 493 494 const std::string result = R"(OpCapability Shader 495OpMemoryModel Logical GLSL450 496OpSource HLSL 600 497OpName %1 "PositionAdjust" 498OpMemberName %1 0 "XAdjust" 499OpName %3 "Constants" 500OpMemberDecorate %1 0 Offset 0 501OpDecorate %3 DescriptorSet 0 502OpDecorate %3 Binding 0 503%4 = OpTypeFloat 32 504%5 = OpTypeVector %4 3 505%1 = OpTypeStruct %5 506%6 = OpTypePointer Uniform %1 507%3 = OpVariable %6 Uniform 508%8 = OpVariable %6 Uniform 509)"; 510 511 EXPECT_EQ(RunPass(spirv), result); 512 EXPECT_EQ(GetErrorMessage(), ""); 513} 514 515// Test that we merge the type of a resource with a type that is not the type 516// a resource. The resource type appears second in this case. We must keep 517// the resource type. 518// 519// See comment for DontMergeNestedResourceTypes. 520TEST_F(RemoveDuplicatesTest, MergeResourceTypeWithNonresourceType2) { 521 const std::string spirv = R"(OpCapability Shader 522OpMemoryModel Logical GLSL450 523OpSource HLSL 600 524OpName %1 "PositionAdjust" 525OpMemberName %1 0 "XAdjust" 526OpName %2 "NormalAdjust" 527OpMemberName %2 0 "XDir" 528OpName %3 "Constants" 529OpMemberDecorate %1 0 Offset 0 530OpMemberDecorate %2 0 Offset 0 531OpDecorate %3 DescriptorSet 0 532OpDecorate %3 Binding 0 533%4 = OpTypeFloat 32 534%5 = OpTypeVector %4 3 535%1 = OpTypeStruct %5 536%2 = OpTypeStruct %5 537%6 = OpTypePointer Uniform %1 538%7 = OpTypePointer Uniform %2 539%8 = OpVariable %6 Uniform 540%3 = OpVariable %7 Uniform 541)"; 542 543 const std::string result = R"(OpCapability Shader 544OpMemoryModel Logical GLSL450 545OpSource HLSL 600 546OpName %1 "PositionAdjust" 547OpMemberName %1 0 "XAdjust" 548OpName %3 "Constants" 549OpMemberDecorate %1 0 Offset 0 550OpDecorate %3 DescriptorSet 0 551OpDecorate %3 Binding 0 552%4 = OpTypeFloat 32 553%5 = OpTypeVector %4 3 554%1 = OpTypeStruct %5 555%6 = OpTypePointer Uniform %1 556%8 = OpVariable %6 Uniform 557%3 = OpVariable %6 Uniform 558)"; 559 560 EXPECT_EQ(RunPass(spirv), result); 561 EXPECT_EQ(GetErrorMessage(), ""); 562} 563 564// In this test, %8 and %9 are the same and only %9 is used in a resource. 565// However, we cannot merge them unless we also merge %2 and %3, which cannot 566// happen because both are used in resources. 567// 568// If we try to avoid replaces resource types, then remove duplicates should 569// have not change in this case. That is not currently implemented. 570TEST_F(RemoveDuplicatesTest, MergeResourceTypeWithNonresourceType3) { 571 const std::string spirv = R"(OpCapability Shader 572OpMemoryModel Logical GLSL450 573OpEntryPoint GLCompute %1 "main" 574OpSource HLSL 600 575OpName %2 "PositionAdjust" 576OpMemberName %2 0 "XAdjust" 577OpName %3 "NormalAdjust" 578OpMemberName %3 0 "XDir" 579OpName %4 "Constants" 580OpMemberDecorate %2 0 Offset 0 581OpMemberDecorate %3 0 Offset 0 582OpDecorate %4 DescriptorSet 0 583OpDecorate %4 Binding 0 584OpDecorate %5 DescriptorSet 1 585OpDecorate %5 Binding 0 586%6 = OpTypeFloat 32 587%7 = OpTypeVector %6 3 588%2 = OpTypeStruct %7 589%3 = OpTypeStruct %7 590%8 = OpTypePointer Uniform %3 591%9 = OpTypePointer Uniform %2 592%10 = OpTypeStruct %3 593%11 = OpTypePointer Uniform %10 594%5 = OpVariable %9 Uniform 595%4 = OpVariable %11 Uniform 596%12 = OpTypeVoid 597%13 = OpTypeFunction %12 598%14 = OpTypeInt 32 0 599%15 = OpConstant %14 0 600%1 = OpFunction %12 None %13 601%16 = OpLabel 602%17 = OpAccessChain %8 %4 %15 603OpReturn 604OpFunctionEnd 605)"; 606 607 const std::string result = R"(OpCapability Shader 608OpMemoryModel Logical GLSL450 609OpEntryPoint GLCompute %1 "main" 610OpSource HLSL 600 611OpName %2 "PositionAdjust" 612OpMemberName %2 0 "XAdjust" 613OpName %4 "Constants" 614OpMemberDecorate %2 0 Offset 0 615OpDecorate %4 DescriptorSet 0 616OpDecorate %4 Binding 0 617OpDecorate %5 DescriptorSet 1 618OpDecorate %5 Binding 0 619%6 = OpTypeFloat 32 620%7 = OpTypeVector %6 3 621%2 = OpTypeStruct %7 622%8 = OpTypePointer Uniform %2 623%10 = OpTypeStruct %2 624%11 = OpTypePointer Uniform %10 625%5 = OpVariable %8 Uniform 626%4 = OpVariable %11 Uniform 627%12 = OpTypeVoid 628%13 = OpTypeFunction %12 629%14 = OpTypeInt 32 0 630%15 = OpConstant %14 0 631%1 = OpFunction %12 None %13 632%16 = OpLabel 633%17 = OpAccessChain %8 %4 %15 634OpReturn 635OpFunctionEnd 636)"; 637 638 EXPECT_EQ(RunPass(spirv), result); 639 EXPECT_EQ(GetErrorMessage(), ""); 640} 641 642} // namespace 643} // namespace opt 644} // namespace spvtools 645