1// Copyright (c) 2016 Google Inc. 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 "source/opt/module.h" 16 17#include <memory> 18#include <vector> 19 20#include "gmock/gmock.h" 21#include "gtest/gtest.h" 22#include "source/opt/build_module.h" 23#include "spirv-tools/libspirv.hpp" 24#include "test/opt/module_utils.h" 25 26namespace spvtools { 27namespace opt { 28namespace { 29 30using ::testing::Eq; 31using spvtest::GetIdBound; 32 33TEST(ModuleTest, SetIdBound) { 34 Module m; 35 // It's initialized to 0. 36 EXPECT_EQ(0u, GetIdBound(m)); 37 38 m.SetIdBound(19); 39 EXPECT_EQ(19u, GetIdBound(m)); 40 41 m.SetIdBound(102); 42 EXPECT_EQ(102u, GetIdBound(m)); 43} 44 45// Returns an IRContext owning the module formed by assembling the given text, 46// then loading the result. 47inline std::unique_ptr<IRContext> BuildModule(std::string text) { 48 return spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 49 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 50} 51 52TEST(ModuleTest, ComputeIdBound) { 53 // Empty module case. 54 EXPECT_EQ(1u, BuildModule("")->module()->ComputeIdBound()); 55 // Sensitive to result id 56 EXPECT_EQ(2u, BuildModule("%void = OpTypeVoid")->module()->ComputeIdBound()); 57 // Sensitive to type id 58 EXPECT_EQ(1000u, 59 BuildModule("%a = OpTypeArray !999 3")->module()->ComputeIdBound()); 60 // Sensitive to a regular Id parameter 61 EXPECT_EQ(2000u, 62 BuildModule("OpDecorate !1999 0")->module()->ComputeIdBound()); 63 // Sensitive to a scope Id parameter. 64 EXPECT_EQ(3000u, 65 BuildModule("%f = OpFunction %void None %fntype %a = OpLabel " 66 "OpMemoryBarrier !2999 %b\n") 67 ->module() 68 ->ComputeIdBound()); 69 // Sensitive to a semantics Id parameter 70 EXPECT_EQ(4000u, 71 BuildModule("%f = OpFunction %void None %fntype %a = OpLabel " 72 "OpMemoryBarrier %b !3999\n") 73 ->module() 74 ->ComputeIdBound()); 75} 76 77TEST(ModuleTest, OstreamOperator) { 78 const std::string text = R"(OpCapability Shader 79OpCapability Linkage 80OpMemoryModel Logical GLSL450 81OpName %7 "restrict" 82OpDecorate %8 Restrict 83%9 = OpTypeVoid 84%10 = OpTypeInt 32 0 85%11 = OpTypeStruct %10 %10 86%12 = OpTypePointer Function %10 87%13 = OpTypePointer Function %11 88%14 = OpConstant %10 0 89%15 = OpConstant %10 1 90%7 = OpTypeFunction %9 91%1 = OpFunction %9 None %7 92%2 = OpLabel 93%8 = OpVariable %13 Function 94%3 = OpAccessChain %12 %8 %14 95%4 = OpLoad %10 %3 96%5 = OpAccessChain %12 %8 %15 97%6 = OpLoad %10 %5 98OpReturn 99OpFunctionEnd)"; 100 101 std::string s; 102 std::ostringstream str(s); 103 str << *BuildModule(text)->module(); 104 EXPECT_EQ(text, str.str()); 105} 106 107TEST(ModuleTest, OstreamOperatorInt64) { 108 const std::string text = R"(OpCapability Shader 109OpCapability Linkage 110OpCapability Int64 111OpMemoryModel Logical GLSL450 112OpName %7 "restrict" 113OpDecorate %5 Restrict 114%9 = OpTypeVoid 115%10 = OpTypeInt 64 0 116%11 = OpTypeStruct %10 %10 117%12 = OpTypePointer Function %10 118%13 = OpTypePointer Function %11 119%14 = OpConstant %10 0 120%15 = OpConstant %10 1 121%16 = OpConstant %10 4294967297 122%7 = OpTypeFunction %9 123%1 = OpFunction %9 None %7 124%2 = OpLabel 125%5 = OpVariable %12 Function 126%6 = OpLoad %10 %5 127OpSelectionMerge %3 None 128OpSwitch %6 %3 4294967297 %4 129%4 = OpLabel 130OpBranch %3 131%3 = OpLabel 132OpReturn 133OpFunctionEnd)"; 134 135 std::string s; 136 std::ostringstream str(s); 137 str << *BuildModule(text)->module(); 138 EXPECT_EQ(text, str.str()); 139} 140 141TEST(ModuleTest, IdBoundTestAtLimit) { 142 const std::string text = R"( 143OpCapability Shader 144OpCapability Linkage 145OpMemoryModel Logical GLSL450 146%1 = OpTypeVoid 147%2 = OpTypeFunction %1 148%3 = OpFunction %1 None %2 149%4 = OpLabel 150OpReturn 151OpFunctionEnd)"; 152 153 std::unique_ptr<IRContext> context = BuildModule(text); 154 uint32_t current_bound = context->module()->id_bound(); 155 context->set_max_id_bound(current_bound); 156 uint32_t next_id_bound = context->module()->TakeNextIdBound(); 157 EXPECT_EQ(next_id_bound, 0); 158 EXPECT_EQ(current_bound, context->module()->id_bound()); 159 next_id_bound = context->module()->TakeNextIdBound(); 160 EXPECT_EQ(next_id_bound, 0); 161} 162 163TEST(ModuleTest, IdBoundTestBelowLimit) { 164 const std::string text = R"( 165OpCapability Shader 166OpCapability Linkage 167OpMemoryModel Logical GLSL450 168%1 = OpTypeVoid 169%2 = OpTypeFunction %1 170%3 = OpFunction %1 None %2 171%4 = OpLabel 172OpReturn 173OpFunctionEnd)"; 174 175 std::unique_ptr<IRContext> context = BuildModule(text); 176 uint32_t current_bound = context->module()->id_bound(); 177 context->set_max_id_bound(current_bound + 100); 178 uint32_t next_id_bound = context->module()->TakeNextIdBound(); 179 EXPECT_EQ(next_id_bound, current_bound); 180 EXPECT_EQ(current_bound + 1, context->module()->id_bound()); 181 next_id_bound = context->module()->TakeNextIdBound(); 182 EXPECT_EQ(next_id_bound, current_bound + 1); 183} 184 185TEST(ModuleTest, IdBoundTestNearLimit) { 186 const std::string text = R"( 187OpCapability Shader 188OpCapability Linkage 189OpMemoryModel Logical GLSL450 190%1 = OpTypeVoid 191%2 = OpTypeFunction %1 192%3 = OpFunction %1 None %2 193%4 = OpLabel 194OpReturn 195OpFunctionEnd)"; 196 197 std::unique_ptr<IRContext> context = BuildModule(text); 198 uint32_t current_bound = context->module()->id_bound(); 199 context->set_max_id_bound(current_bound + 1); 200 uint32_t next_id_bound = context->module()->TakeNextIdBound(); 201 EXPECT_EQ(next_id_bound, current_bound); 202 EXPECT_EQ(current_bound + 1, context->module()->id_bound()); 203 next_id_bound = context->module()->TakeNextIdBound(); 204 EXPECT_EQ(next_id_bound, 0); 205} 206 207TEST(ModuleTest, IdBoundTestUIntMax) { 208 const std::string text = R"( 209OpCapability Shader 210OpCapability Linkage 211OpMemoryModel Logical GLSL450 212%1 = OpTypeVoid 213%2 = OpTypeFunction %1 214%3 = OpFunction %1 None %2 215%4294967294 = OpLabel ; ID is UINT_MAX-1 216OpReturn 217OpFunctionEnd)"; 218 219 std::unique_ptr<IRContext> context = BuildModule(text); 220 uint32_t current_bound = context->module()->id_bound(); 221 222 // Expecting |BuildModule| to preserve the numeric ids. 223 EXPECT_EQ(current_bound, std::numeric_limits<uint32_t>::max()); 224 225 context->set_max_id_bound(current_bound); 226 uint32_t next_id_bound = context->module()->TakeNextIdBound(); 227 EXPECT_EQ(next_id_bound, 0); 228 EXPECT_EQ(current_bound, context->module()->id_bound()); 229} 230 231// Tests that "text" does not change when it is assembled, converted into a 232// module, converted back to a binary, and then disassembled. 233void AssembleAndDisassemble(const std::string& text) { 234 std::unique_ptr<IRContext> context = BuildModule(text); 235 std::vector<uint32_t> binary; 236 237 context->module()->ToBinary(&binary, false); 238 239 SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); 240 std::string s; 241 tools.Disassemble(binary, &s); 242 EXPECT_EQ(s, text); 243} 244 245TEST(ModuleTest, TrailingOpLine) { 246 const std::string text = R"(OpCapability Shader 247OpCapability Linkage 248OpMemoryModel Logical GLSL450 249%5 = OpString "file.ext" 250%void = OpTypeVoid 251%2 = OpTypeFunction %void 252%3 = OpFunction %void None %2 253%4 = OpLabel 254OpReturn 255OpFunctionEnd 256OpLine %5 1 0 257)"; 258 259 AssembleAndDisassemble(text); 260} 261 262TEST(ModuleTest, TrailingOpNoLine) { 263 const std::string text = R"(OpCapability Shader 264OpCapability Linkage 265OpMemoryModel Logical GLSL450 266%void = OpTypeVoid 267%2 = OpTypeFunction %void 268%3 = OpFunction %void None %2 269%4 = OpLabel 270OpReturn 271OpFunctionEnd 272OpNoLine 273)"; 274 275 AssembleAndDisassemble(text); 276} 277 278TEST(ModuleTest, MulitpleTrailingOpLine) { 279 const std::string text = R"(OpCapability Shader 280OpCapability Linkage 281OpMemoryModel Logical GLSL450 282%5 = OpString "file.ext" 283%void = OpTypeVoid 284%2 = OpTypeFunction %void 285%3 = OpFunction %void None %2 286%4 = OpLabel 287OpReturn 288OpFunctionEnd 289OpLine %5 1 0 290OpNoLine 291OpLine %5 1 1 292)"; 293 294 AssembleAndDisassemble(text); 295} 296 297TEST(ModuleTest, NonSemanticInfoIteration) { 298 const std::string text = R"( 299OpCapability Shader 300OpCapability Linkage 301OpExtension "SPV_KHR_non_semantic_info" 302%1 = OpExtInstImport "NonSemantic.Test" 303OpMemoryModel Logical GLSL450 304%2 = OpTypeVoid 305%3 = OpTypeFunction %2 306%4 = OpExtInst %2 %1 1 307%5 = OpFunction %2 None %3 308%6 = OpLabel 309%7 = OpExtInst %2 %1 1 310OpReturn 311OpFunctionEnd 312%8 = OpExtInst %2 %1 1 313%9 = OpFunction %2 None %3 314%10 = OpLabel 315%11 = OpExtInst %2 %1 1 316OpReturn 317OpFunctionEnd 318%12 = OpExtInst %2 %1 1 319)"; 320 321 std::unique_ptr<IRContext> context = BuildModule(text); 322 std::unordered_set<uint32_t> non_semantic_ids; 323 context->module()->ForEachInst( 324 [&non_semantic_ids](const Instruction* inst) { 325 if (inst->opcode() == spv::Op::OpExtInst) { 326 non_semantic_ids.insert(inst->result_id()); 327 } 328 }, 329 false); 330 331 EXPECT_EQ(1, non_semantic_ids.count(4)); 332 EXPECT_EQ(1, non_semantic_ids.count(7)); 333 EXPECT_EQ(1, non_semantic_ids.count(8)); 334 EXPECT_EQ(1, non_semantic_ids.count(11)); 335 EXPECT_EQ(1, non_semantic_ids.count(12)); 336} 337} // namespace 338} // namespace opt 339} // namespace spvtools 340