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