1fd4e5da5Sopenharmony_ci// Copyright (c) 2015-2016 The Khronos Group Inc.
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// Validation tests for Control Flow Graph
16fd4e5da5Sopenharmony_ci
17fd4e5da5Sopenharmony_ci#include <array>
18fd4e5da5Sopenharmony_ci#include <functional>
19fd4e5da5Sopenharmony_ci#include <sstream>
20fd4e5da5Sopenharmony_ci#include <string>
21fd4e5da5Sopenharmony_ci#include <utility>
22fd4e5da5Sopenharmony_ci#include <vector>
23fd4e5da5Sopenharmony_ci
24fd4e5da5Sopenharmony_ci#include "gmock/gmock.h"
25fd4e5da5Sopenharmony_ci#include "source/spirv_target_env.h"
26fd4e5da5Sopenharmony_ci#include "source/val/validate.h"
27fd4e5da5Sopenharmony_ci#include "test/test_fixture.h"
28fd4e5da5Sopenharmony_ci#include "test/unit_spirv.h"
29fd4e5da5Sopenharmony_ci#include "test/val/val_fixtures.h"
30fd4e5da5Sopenharmony_ci
31fd4e5da5Sopenharmony_cinamespace spvtools {
32fd4e5da5Sopenharmony_cinamespace val {
33fd4e5da5Sopenharmony_cinamespace {
34fd4e5da5Sopenharmony_ci
35fd4e5da5Sopenharmony_ciusing ::testing::HasSubstr;
36fd4e5da5Sopenharmony_ciusing ::testing::MatchesRegex;
37fd4e5da5Sopenharmony_ci
38fd4e5da5Sopenharmony_ciusing ValidateCFG = spvtest::ValidateBase<spv::Capability>;
39fd4e5da5Sopenharmony_ciusing spvtest::ScopedContext;
40fd4e5da5Sopenharmony_ci
41fd4e5da5Sopenharmony_cistd::string nameOps() { return ""; }
42fd4e5da5Sopenharmony_ci
43fd4e5da5Sopenharmony_citemplate <typename... Args>
44fd4e5da5Sopenharmony_cistd::string nameOps(std::pair<std::string, std::string> head, Args... names) {
45fd4e5da5Sopenharmony_ci  return "OpName %" + head.first + " \"" + head.second + "\"\n" +
46fd4e5da5Sopenharmony_ci         nameOps(names...);
47fd4e5da5Sopenharmony_ci}
48fd4e5da5Sopenharmony_ci
49fd4e5da5Sopenharmony_citemplate <typename... Args>
50fd4e5da5Sopenharmony_cistd::string nameOps(std::string head, Args... names) {
51fd4e5da5Sopenharmony_ci  return "OpName %" + head + " \"" + head + "\"\n" + nameOps(names...);
52fd4e5da5Sopenharmony_ci}
53fd4e5da5Sopenharmony_ci
54fd4e5da5Sopenharmony_ci/// This class allows the easy creation of complex control flow without writing
55fd4e5da5Sopenharmony_ci/// SPIR-V. This class is used in the test cases below.
56fd4e5da5Sopenharmony_ciclass Block {
57fd4e5da5Sopenharmony_ci  std::string label_;
58fd4e5da5Sopenharmony_ci  std::string body_;
59fd4e5da5Sopenharmony_ci  spv::Op type_;
60fd4e5da5Sopenharmony_ci  std::vector<Block> successors_;
61fd4e5da5Sopenharmony_ci
62fd4e5da5Sopenharmony_ci public:
63fd4e5da5Sopenharmony_ci  /// Creates a Block with a given label
64fd4e5da5Sopenharmony_ci  ///
65fd4e5da5Sopenharmony_ci  /// @param[in]: label the label id of the block
66fd4e5da5Sopenharmony_ci  /// @param[in]: type the branch instruction that ends the block
67fd4e5da5Sopenharmony_ci  explicit Block(std::string label, spv::Op type = spv::Op::OpBranch)
68fd4e5da5Sopenharmony_ci      : label_(label), body_(), type_(type), successors_() {}
69fd4e5da5Sopenharmony_ci
70fd4e5da5Sopenharmony_ci  /// Sets the instructions which will appear in the body of the block
71fd4e5da5Sopenharmony_ci  Block& SetBody(std::string body) {
72fd4e5da5Sopenharmony_ci    body_ = body;
73fd4e5da5Sopenharmony_ci    return *this;
74fd4e5da5Sopenharmony_ci  }
75fd4e5da5Sopenharmony_ci
76fd4e5da5Sopenharmony_ci  Block& AppendBody(std::string body) {
77fd4e5da5Sopenharmony_ci    body_ += body;
78fd4e5da5Sopenharmony_ci    return *this;
79fd4e5da5Sopenharmony_ci  }
80fd4e5da5Sopenharmony_ci
81fd4e5da5Sopenharmony_ci  /// Converts the block into a SPIR-V string
82fd4e5da5Sopenharmony_ci  operator std::string() {
83fd4e5da5Sopenharmony_ci    std::stringstream out;
84fd4e5da5Sopenharmony_ci    out << std::setw(8) << "%" + label_ + "  = OpLabel \n";
85fd4e5da5Sopenharmony_ci    if (!body_.empty()) {
86fd4e5da5Sopenharmony_ci      out << body_;
87fd4e5da5Sopenharmony_ci    }
88fd4e5da5Sopenharmony_ci
89fd4e5da5Sopenharmony_ci    switch (type_) {
90fd4e5da5Sopenharmony_ci      case spv::Op::OpBranchConditional:
91fd4e5da5Sopenharmony_ci        out << "OpBranchConditional %cond ";
92fd4e5da5Sopenharmony_ci        for (Block& b : successors_) {
93fd4e5da5Sopenharmony_ci          out << "%" + b.label_ + " ";
94fd4e5da5Sopenharmony_ci        }
95fd4e5da5Sopenharmony_ci        break;
96fd4e5da5Sopenharmony_ci      case spv::Op::OpSwitch: {
97fd4e5da5Sopenharmony_ci        out << "OpSwitch %one %" + successors_.front().label_;
98fd4e5da5Sopenharmony_ci        std::stringstream ss;
99fd4e5da5Sopenharmony_ci        for (size_t i = 1; i < successors_.size(); i++) {
100fd4e5da5Sopenharmony_ci          ss << " " << i << " %" << successors_[i].label_;
101fd4e5da5Sopenharmony_ci        }
102fd4e5da5Sopenharmony_ci        out << ss.str();
103fd4e5da5Sopenharmony_ci      } break;
104fd4e5da5Sopenharmony_ci      case spv::Op::OpLoopMerge: {
105fd4e5da5Sopenharmony_ci        assert(successors_.size() == 2);
106fd4e5da5Sopenharmony_ci        out << "OpLoopMerge %" + successors_[0].label_ + " %" +
107fd4e5da5Sopenharmony_ci                   successors_[0].label_ + "None";
108fd4e5da5Sopenharmony_ci      } break;
109fd4e5da5Sopenharmony_ci
110fd4e5da5Sopenharmony_ci      case spv::Op::OpReturn:
111fd4e5da5Sopenharmony_ci        assert(successors_.size() == 0);
112fd4e5da5Sopenharmony_ci        out << "OpReturn\n";
113fd4e5da5Sopenharmony_ci        break;
114fd4e5da5Sopenharmony_ci      case spv::Op::OpUnreachable:
115fd4e5da5Sopenharmony_ci        assert(successors_.size() == 0);
116fd4e5da5Sopenharmony_ci        out << "OpUnreachable\n";
117fd4e5da5Sopenharmony_ci        break;
118fd4e5da5Sopenharmony_ci      case spv::Op::OpBranch:
119fd4e5da5Sopenharmony_ci        assert(successors_.size() == 1);
120fd4e5da5Sopenharmony_ci        out << "OpBranch %" + successors_.front().label_;
121fd4e5da5Sopenharmony_ci        break;
122fd4e5da5Sopenharmony_ci      case spv::Op::OpKill:
123fd4e5da5Sopenharmony_ci        assert(successors_.size() == 0);
124fd4e5da5Sopenharmony_ci        out << "OpKill\n";
125fd4e5da5Sopenharmony_ci        break;
126fd4e5da5Sopenharmony_ci      default:
127fd4e5da5Sopenharmony_ci        assert(1 == 0 && "Unhandled");
128fd4e5da5Sopenharmony_ci    }
129fd4e5da5Sopenharmony_ci    out << "\n";
130fd4e5da5Sopenharmony_ci
131fd4e5da5Sopenharmony_ci    return out.str();
132fd4e5da5Sopenharmony_ci  }
133fd4e5da5Sopenharmony_ci  friend Block& operator>>(Block& curr, std::vector<Block> successors);
134fd4e5da5Sopenharmony_ci  friend Block& operator>>(Block& lhs, Block& successor);
135fd4e5da5Sopenharmony_ci};
136fd4e5da5Sopenharmony_ci
137fd4e5da5Sopenharmony_ci/// Assigns the successors for the Block on the lhs
138fd4e5da5Sopenharmony_ciBlock& operator>>(Block& lhs, std::vector<Block> successors) {
139fd4e5da5Sopenharmony_ci  if (lhs.type_ == spv::Op::OpBranchConditional) {
140fd4e5da5Sopenharmony_ci    assert(successors.size() == 2);
141fd4e5da5Sopenharmony_ci  } else if (lhs.type_ == spv::Op::OpSwitch) {
142fd4e5da5Sopenharmony_ci    assert(successors.size() > 1);
143fd4e5da5Sopenharmony_ci  }
144fd4e5da5Sopenharmony_ci  lhs.successors_ = successors;
145fd4e5da5Sopenharmony_ci  return lhs;
146fd4e5da5Sopenharmony_ci}
147fd4e5da5Sopenharmony_ci
148fd4e5da5Sopenharmony_ci/// Assigns the successor for the Block on the lhs
149fd4e5da5Sopenharmony_ciBlock& operator>>(Block& lhs, Block& successor) {
150fd4e5da5Sopenharmony_ci  assert(lhs.type_ == spv::Op::OpBranch);
151fd4e5da5Sopenharmony_ci  lhs.successors_.push_back(successor);
152fd4e5da5Sopenharmony_ci  return lhs;
153fd4e5da5Sopenharmony_ci}
154fd4e5da5Sopenharmony_ci
155fd4e5da5Sopenharmony_ciconst std::string& GetDefaultHeader(spv::Capability cap) {
156fd4e5da5Sopenharmony_ci  static const std::string shader_header =
157fd4e5da5Sopenharmony_ci      "OpCapability Shader\n"
158fd4e5da5Sopenharmony_ci      "OpCapability Linkage\n"
159fd4e5da5Sopenharmony_ci      "OpMemoryModel Logical GLSL450\n";
160fd4e5da5Sopenharmony_ci
161fd4e5da5Sopenharmony_ci  static const std::string kernel_header =
162fd4e5da5Sopenharmony_ci      "OpCapability Kernel\n"
163fd4e5da5Sopenharmony_ci      "OpCapability Linkage\n"
164fd4e5da5Sopenharmony_ci      "OpMemoryModel Logical OpenCL\n";
165fd4e5da5Sopenharmony_ci
166fd4e5da5Sopenharmony_ci  return (cap == spv::Capability::Shader) ? shader_header : kernel_header;
167fd4e5da5Sopenharmony_ci}
168fd4e5da5Sopenharmony_ci
169fd4e5da5Sopenharmony_ciconst std::string& types_consts() {
170fd4e5da5Sopenharmony_ci  static const std::string types =
171fd4e5da5Sopenharmony_ci      "%voidt   = OpTypeVoid\n"
172fd4e5da5Sopenharmony_ci      "%boolt   = OpTypeBool\n"
173fd4e5da5Sopenharmony_ci      "%intt    = OpTypeInt 32 0\n"
174fd4e5da5Sopenharmony_ci      "%one     = OpConstant %intt 1\n"
175fd4e5da5Sopenharmony_ci      "%two     = OpConstant %intt 2\n"
176fd4e5da5Sopenharmony_ci      "%ptrt    = OpTypePointer Function %intt\n"
177fd4e5da5Sopenharmony_ci      "%funct   = OpTypeFunction %voidt\n";
178fd4e5da5Sopenharmony_ci  return types;
179fd4e5da5Sopenharmony_ci}
180fd4e5da5Sopenharmony_ci
181fd4e5da5Sopenharmony_ciINSTANTIATE_TEST_SUITE_P(StructuredControlFlow, ValidateCFG,
182fd4e5da5Sopenharmony_ci                         ::testing::Values(spv::Capability::Shader,
183fd4e5da5Sopenharmony_ci                                           spv::Capability::Kernel));
184fd4e5da5Sopenharmony_ci
185fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, LoopReachableFromEntryButNeverLeadingToReturn) {
186fd4e5da5Sopenharmony_ci  // In this case, the loop is reachable from a node without a predecessor,
187fd4e5da5Sopenharmony_ci  // but never reaches a node with a return.
188fd4e5da5Sopenharmony_ci  //
189fd4e5da5Sopenharmony_ci  // This motivates the need for the pseudo-exit node to have a node
190fd4e5da5Sopenharmony_ci  // from a cycle in its predecessors list.  Otherwise the validator's
191fd4e5da5Sopenharmony_ci  // post-dominance calculation will go into an infinite loop.
192fd4e5da5Sopenharmony_ci  //
193fd4e5da5Sopenharmony_ci  // For more motivation, see
194fd4e5da5Sopenharmony_ci  // https://github.com/KhronosGroup/SPIRV-Tools/issues/279
195fd4e5da5Sopenharmony_ci  std::string str = R"(
196fd4e5da5Sopenharmony_ci           OpCapability Shader
197fd4e5da5Sopenharmony_ci           OpCapability Linkage
198fd4e5da5Sopenharmony_ci           OpMemoryModel Logical GLSL450
199fd4e5da5Sopenharmony_ci
200fd4e5da5Sopenharmony_ci           OpName %entry "entry"
201fd4e5da5Sopenharmony_ci           OpName %loop "loop"
202fd4e5da5Sopenharmony_ci           OpName %exit "exit"
203fd4e5da5Sopenharmony_ci
204fd4e5da5Sopenharmony_ci%voidt   = OpTypeVoid
205fd4e5da5Sopenharmony_ci%funct   = OpTypeFunction %voidt
206fd4e5da5Sopenharmony_ci
207fd4e5da5Sopenharmony_ci%main    = OpFunction %voidt None %funct
208fd4e5da5Sopenharmony_ci%entry   = OpLabel
209fd4e5da5Sopenharmony_ci           OpBranch %loop
210fd4e5da5Sopenharmony_ci%loop    = OpLabel
211fd4e5da5Sopenharmony_ci           OpLoopMerge %exit %loop None
212fd4e5da5Sopenharmony_ci           OpBranch %loop
213fd4e5da5Sopenharmony_ci%exit    = OpLabel
214fd4e5da5Sopenharmony_ci           OpReturn
215fd4e5da5Sopenharmony_ci           OpFunctionEnd
216fd4e5da5Sopenharmony_ci  )";
217fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
218fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str;
219fd4e5da5Sopenharmony_ci}
220fd4e5da5Sopenharmony_ci
221fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, LoopUnreachableFromEntryButLeadingToReturn) {
222fd4e5da5Sopenharmony_ci  // In this case, the loop is not reachable from a node without a
223fd4e5da5Sopenharmony_ci  // predecessor, but eventually reaches a node with a return.
224fd4e5da5Sopenharmony_ci  //
225fd4e5da5Sopenharmony_ci  // This motivates the need for the pseudo-entry node to have a node
226fd4e5da5Sopenharmony_ci  // from a cycle in its successors list.  Otherwise the validator's
227fd4e5da5Sopenharmony_ci  // dominance calculation will go into an infinite loop.
228fd4e5da5Sopenharmony_ci  //
229fd4e5da5Sopenharmony_ci  // For more motivation, see
230fd4e5da5Sopenharmony_ci  // https://github.com/KhronosGroup/SPIRV-Tools/issues/279
231fd4e5da5Sopenharmony_ci  // Before that fix, we'd have an infinite loop when calculating
232fd4e5da5Sopenharmony_ci  // post-dominators.
233fd4e5da5Sopenharmony_ci  std::string str = R"(
234fd4e5da5Sopenharmony_ci           OpCapability Shader
235fd4e5da5Sopenharmony_ci           OpCapability Linkage
236fd4e5da5Sopenharmony_ci           OpMemoryModel Logical GLSL450
237fd4e5da5Sopenharmony_ci
238fd4e5da5Sopenharmony_ci           OpName %entry "entry"
239fd4e5da5Sopenharmony_ci           OpName %loop "loop"
240fd4e5da5Sopenharmony_ci           OpName %cont "cont"
241fd4e5da5Sopenharmony_ci           OpName %exit "exit"
242fd4e5da5Sopenharmony_ci
243fd4e5da5Sopenharmony_ci%voidt   = OpTypeVoid
244fd4e5da5Sopenharmony_ci%funct   = OpTypeFunction %voidt
245fd4e5da5Sopenharmony_ci%boolt   = OpTypeBool
246fd4e5da5Sopenharmony_ci%false   = OpConstantFalse %boolt
247fd4e5da5Sopenharmony_ci
248fd4e5da5Sopenharmony_ci%main    = OpFunction %voidt None %funct
249fd4e5da5Sopenharmony_ci%entry   = OpLabel
250fd4e5da5Sopenharmony_ci           OpReturn
251fd4e5da5Sopenharmony_ci
252fd4e5da5Sopenharmony_ci%loop    = OpLabel
253fd4e5da5Sopenharmony_ci           OpLoopMerge %exit %cont None
254fd4e5da5Sopenharmony_ci           OpBranch %cont
255fd4e5da5Sopenharmony_ci
256fd4e5da5Sopenharmony_ci%cont    = OpLabel
257fd4e5da5Sopenharmony_ci           OpBranchConditional %false %loop %exit
258fd4e5da5Sopenharmony_ci
259fd4e5da5Sopenharmony_ci%exit    = OpLabel
260fd4e5da5Sopenharmony_ci           OpReturn
261fd4e5da5Sopenharmony_ci           OpFunctionEnd
262fd4e5da5Sopenharmony_ci  )";
263fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
264fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions())
265fd4e5da5Sopenharmony_ci      << str << getDiagnosticString();
266fd4e5da5Sopenharmony_ci}
267fd4e5da5Sopenharmony_ci
268fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, Simple) {
269fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
270fd4e5da5Sopenharmony_ci  Block entry("entry");
271fd4e5da5Sopenharmony_ci  Block loop("loop", spv::Op::OpBranchConditional);
272fd4e5da5Sopenharmony_ci  Block cont("cont");
273fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
274fd4e5da5Sopenharmony_ci
275fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
276fd4e5da5Sopenharmony_ci  if (is_shader) {
277fd4e5da5Sopenharmony_ci    loop.SetBody("OpLoopMerge %merge %cont None\n");
278fd4e5da5Sopenharmony_ci  }
279fd4e5da5Sopenharmony_ci
280fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
281fd4e5da5Sopenharmony_ci                    nameOps("loop", "entry", "cont", "merge",
282fd4e5da5Sopenharmony_ci                            std::make_pair("func", "Main")) +
283fd4e5da5Sopenharmony_ci                    types_consts() +
284fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
285fd4e5da5Sopenharmony_ci
286fd4e5da5Sopenharmony_ci  str += entry >> loop;
287fd4e5da5Sopenharmony_ci  str += loop >> std::vector<Block>({cont, merge});
288fd4e5da5Sopenharmony_ci  str += cont >> loop;
289fd4e5da5Sopenharmony_ci  str += merge;
290fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
291fd4e5da5Sopenharmony_ci
292fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
293fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
294fd4e5da5Sopenharmony_ci}
295fd4e5da5Sopenharmony_ci
296fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, Variable) {
297fd4e5da5Sopenharmony_ci  Block entry("entry");
298fd4e5da5Sopenharmony_ci  Block cont("cont");
299fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
300fd4e5da5Sopenharmony_ci
301fd4e5da5Sopenharmony_ci  entry.SetBody("%var = OpVariable %ptrt Function\n");
302fd4e5da5Sopenharmony_ci
303fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
304fd4e5da5Sopenharmony_ci                    nameOps(std::make_pair("func", "Main")) + types_consts() +
305fd4e5da5Sopenharmony_ci                    " %func    = OpFunction %voidt None %funct\n";
306fd4e5da5Sopenharmony_ci  str += entry >> cont;
307fd4e5da5Sopenharmony_ci  str += cont >> exit;
308fd4e5da5Sopenharmony_ci  str += exit;
309fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
310fd4e5da5Sopenharmony_ci
311fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
312fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
313fd4e5da5Sopenharmony_ci}
314fd4e5da5Sopenharmony_ci
315fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, VariableNotInFirstBlockBad) {
316fd4e5da5Sopenharmony_ci  Block entry("entry");
317fd4e5da5Sopenharmony_ci  Block cont("cont");
318fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
319fd4e5da5Sopenharmony_ci
320fd4e5da5Sopenharmony_ci  // This operation should only be performed in the entry block
321fd4e5da5Sopenharmony_ci  cont.SetBody("%var = OpVariable %ptrt Function\n");
322fd4e5da5Sopenharmony_ci
323fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
324fd4e5da5Sopenharmony_ci                    nameOps(std::make_pair("func", "Main")) + types_consts() +
325fd4e5da5Sopenharmony_ci                    " %func    = OpFunction %voidt None %funct\n";
326fd4e5da5Sopenharmony_ci
327fd4e5da5Sopenharmony_ci  str += entry >> cont;
328fd4e5da5Sopenharmony_ci  str += cont >> exit;
329fd4e5da5Sopenharmony_ci  str += exit;
330fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
331fd4e5da5Sopenharmony_ci
332fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
333fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
334fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
335fd4e5da5Sopenharmony_ci              HasSubstr("All OpVariable instructions in a function must be the "
336fd4e5da5Sopenharmony_ci                        "first instructions in the first block"));
337fd4e5da5Sopenharmony_ci}
338fd4e5da5Sopenharmony_ci
339fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BlockSelfLoopIsOk) {
340fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
341fd4e5da5Sopenharmony_ci  Block entry("entry");
342fd4e5da5Sopenharmony_ci  Block loop("loop", spv::Op::OpBranchConditional);
343fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
344fd4e5da5Sopenharmony_ci
345fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
346fd4e5da5Sopenharmony_ci  if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
347fd4e5da5Sopenharmony_ci
348fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
349fd4e5da5Sopenharmony_ci                    nameOps("loop", "merge", std::make_pair("func", "Main")) +
350fd4e5da5Sopenharmony_ci                    types_consts() +
351fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
352fd4e5da5Sopenharmony_ci
353fd4e5da5Sopenharmony_ci  str += entry >> loop;
354fd4e5da5Sopenharmony_ci  // loop branches to itself, but does not trigger an error.
355fd4e5da5Sopenharmony_ci  str += loop >> std::vector<Block>({merge, loop});
356fd4e5da5Sopenharmony_ci  str += merge;
357fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
358fd4e5da5Sopenharmony_ci
359fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
360fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
361fd4e5da5Sopenharmony_ci}
362fd4e5da5Sopenharmony_ci
363fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BlockAppearsBeforeDominatorBad) {
364fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
365fd4e5da5Sopenharmony_ci  Block entry("entry");
366fd4e5da5Sopenharmony_ci  Block cont("cont");
367fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranchConditional);
368fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
369fd4e5da5Sopenharmony_ci
370fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
371fd4e5da5Sopenharmony_ci  if (is_shader) branch.SetBody("OpSelectionMerge %merge None\n");
372fd4e5da5Sopenharmony_ci
373fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
374fd4e5da5Sopenharmony_ci                    nameOps("cont", "branch", std::make_pair("func", "Main")) +
375fd4e5da5Sopenharmony_ci                    types_consts() +
376fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
377fd4e5da5Sopenharmony_ci
378fd4e5da5Sopenharmony_ci  str += entry >> branch;
379fd4e5da5Sopenharmony_ci  str += cont >> merge;  // cont appears before its dominator
380fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({cont, merge});
381fd4e5da5Sopenharmony_ci  str += merge;
382fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
383fd4e5da5Sopenharmony_ci
384fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
385fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
386fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
387fd4e5da5Sopenharmony_ci              MatchesRegex("Block '.\\[%cont\\]' appears in the binary "
388fd4e5da5Sopenharmony_ci                           "before its dominator '.\\[%branch\\]'\n"
389fd4e5da5Sopenharmony_ci                           "  %branch = OpLabel\n"));
390fd4e5da5Sopenharmony_ci}
391fd4e5da5Sopenharmony_ci
392fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksBad) {
393fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
394fd4e5da5Sopenharmony_ci  Block entry("entry");
395fd4e5da5Sopenharmony_ci  Block loop("loop");
396fd4e5da5Sopenharmony_ci  Block selection("selection", spv::Op::OpBranchConditional);
397fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
398fd4e5da5Sopenharmony_ci
399fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
400fd4e5da5Sopenharmony_ci  if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n");
401fd4e5da5Sopenharmony_ci
402fd4e5da5Sopenharmony_ci  // cannot share the same merge
403fd4e5da5Sopenharmony_ci  if (is_shader) selection.SetBody("OpSelectionMerge %merge None\n");
404fd4e5da5Sopenharmony_ci
405fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
406fd4e5da5Sopenharmony_ci                    nameOps("merge", std::make_pair("func", "Main")) +
407fd4e5da5Sopenharmony_ci                    types_consts() +
408fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
409fd4e5da5Sopenharmony_ci
410fd4e5da5Sopenharmony_ci  str += entry >> loop;
411fd4e5da5Sopenharmony_ci  str += loop >> selection;
412fd4e5da5Sopenharmony_ci  str += selection >> std::vector<Block>({loop, merge});
413fd4e5da5Sopenharmony_ci  str += merge;
414fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
415fd4e5da5Sopenharmony_ci
416fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
417fd4e5da5Sopenharmony_ci  if (is_shader) {
418fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
419fd4e5da5Sopenharmony_ci    EXPECT_THAT(getDiagnosticString(),
420fd4e5da5Sopenharmony_ci                MatchesRegex("Block '.\\[%merge\\]' is already a merge block "
421fd4e5da5Sopenharmony_ci                             "for another header\n"
422fd4e5da5Sopenharmony_ci                             "  %Main = OpFunction %void None %9\n"));
423fd4e5da5Sopenharmony_ci  } else {
424fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
425fd4e5da5Sopenharmony_ci  }
426fd4e5da5Sopenharmony_ci}
427fd4e5da5Sopenharmony_ci
428fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksSelectionBad) {
429fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
430fd4e5da5Sopenharmony_ci  Block entry("entry");
431fd4e5da5Sopenharmony_ci  Block loop("loop", spv::Op::OpBranchConditional);
432fd4e5da5Sopenharmony_ci  Block selection("selection", spv::Op::OpBranchConditional);
433fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
434fd4e5da5Sopenharmony_ci
435fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
436fd4e5da5Sopenharmony_ci  if (is_shader) selection.SetBody(" OpSelectionMerge %merge None\n");
437fd4e5da5Sopenharmony_ci
438fd4e5da5Sopenharmony_ci  // cannot share the same merge
439fd4e5da5Sopenharmony_ci  if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n");
440fd4e5da5Sopenharmony_ci
441fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
442fd4e5da5Sopenharmony_ci                    nameOps("merge", std::make_pair("func", "Main")) +
443fd4e5da5Sopenharmony_ci                    types_consts() +
444fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
445fd4e5da5Sopenharmony_ci
446fd4e5da5Sopenharmony_ci  str += entry >> selection;
447fd4e5da5Sopenharmony_ci  str += selection >> std::vector<Block>({merge, loop});
448fd4e5da5Sopenharmony_ci  str += loop >> std::vector<Block>({loop, merge});
449fd4e5da5Sopenharmony_ci  str += merge;
450fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
451fd4e5da5Sopenharmony_ci
452fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
453fd4e5da5Sopenharmony_ci  if (is_shader) {
454fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
455fd4e5da5Sopenharmony_ci    EXPECT_THAT(getDiagnosticString(),
456fd4e5da5Sopenharmony_ci                MatchesRegex("Block '.\\[%merge\\]' is already a merge block "
457fd4e5da5Sopenharmony_ci                             "for another header\n"
458fd4e5da5Sopenharmony_ci                             "  %Main = OpFunction %void None %9\n"));
459fd4e5da5Sopenharmony_ci  } else {
460fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
461fd4e5da5Sopenharmony_ci  }
462fd4e5da5Sopenharmony_ci}
463fd4e5da5Sopenharmony_ci
464fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceEntryBlock) {
465fd4e5da5Sopenharmony_ci  Block entry("entry");
466fd4e5da5Sopenharmony_ci  Block bad("bad");
467fd4e5da5Sopenharmony_ci  Block end("end", spv::Op::OpReturn);
468fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
469fd4e5da5Sopenharmony_ci                    nameOps("entry", "bad", std::make_pair("func", "Main")) +
470fd4e5da5Sopenharmony_ci                    types_consts() +
471fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
472fd4e5da5Sopenharmony_ci
473fd4e5da5Sopenharmony_ci  str += entry >> bad;
474fd4e5da5Sopenharmony_ci  str += bad >> entry;  // Cannot target entry block
475fd4e5da5Sopenharmony_ci  str += end;
476fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
477fd4e5da5Sopenharmony_ci
478fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
479fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
480fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
481fd4e5da5Sopenharmony_ci              MatchesRegex("First block '.\\[%entry\\]' of function "
482fd4e5da5Sopenharmony_ci                           "'.\\[%Main\\]' is targeted by block '.\\[%bad\\]'\n"
483fd4e5da5Sopenharmony_ci                           "  %Main = OpFunction %void None %10\n"));
484fd4e5da5Sopenharmony_ci}
485fd4e5da5Sopenharmony_ci
486fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceValue) {
487fd4e5da5Sopenharmony_ci  Block entry("entry");
488fd4e5da5Sopenharmony_ci  entry.SetBody("%undef = OpUndef %boolt\n");
489fd4e5da5Sopenharmony_ci  Block bad("bad");
490fd4e5da5Sopenharmony_ci  Block end("end", spv::Op::OpReturn);
491fd4e5da5Sopenharmony_ci  Block badvalue("undef");  // This references the OpUndef.
492fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
493fd4e5da5Sopenharmony_ci                    nameOps("entry", "bad", std::make_pair("func", "Main")) +
494fd4e5da5Sopenharmony_ci                    types_consts() +
495fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
496fd4e5da5Sopenharmony_ci
497fd4e5da5Sopenharmony_ci  str += entry >> bad;
498fd4e5da5Sopenharmony_ci  str +=
499fd4e5da5Sopenharmony_ci      bad >> badvalue;  // Check branch to a function value (it's not a block!)
500fd4e5da5Sopenharmony_ci  str += end;
501fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
502fd4e5da5Sopenharmony_ci
503fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
504fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
505fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
506fd4e5da5Sopenharmony_ci              HasSubstr("'Target Label' operands for OpBranch must "
507fd4e5da5Sopenharmony_ci                        "be the ID of an OpLabel instruction"));
508fd4e5da5Sopenharmony_ci}
509fd4e5da5Sopenharmony_ci
510fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BranchConditionalTrueTargetFirstBlockBad) {
511fd4e5da5Sopenharmony_ci  Block entry("entry");
512fd4e5da5Sopenharmony_ci  Block bad("bad", spv::Op::OpBranchConditional);
513fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
514fd4e5da5Sopenharmony_ci
515fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
516fd4e5da5Sopenharmony_ci  bad.SetBody(" OpLoopMerge %entry %exit None\n");
517fd4e5da5Sopenharmony_ci
518fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
519fd4e5da5Sopenharmony_ci                    nameOps("entry", "bad", std::make_pair("func", "Main")) +
520fd4e5da5Sopenharmony_ci                    types_consts() +
521fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
522fd4e5da5Sopenharmony_ci
523fd4e5da5Sopenharmony_ci  str += entry >> bad;
524fd4e5da5Sopenharmony_ci  str += bad >> std::vector<Block>({entry, exit});  // cannot target entry block
525fd4e5da5Sopenharmony_ci  str += exit;
526fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
527fd4e5da5Sopenharmony_ci
528fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
529fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
530fd4e5da5Sopenharmony_ci  EXPECT_THAT(
531fd4e5da5Sopenharmony_ci      getDiagnosticString(),
532fd4e5da5Sopenharmony_ci      MatchesRegex("First block '.\\[%entry\\]' of function '.\\[%Main\\]' "
533fd4e5da5Sopenharmony_ci                   "is targeted by block '.\\[%bad\\]'\n"
534fd4e5da5Sopenharmony_ci                   "  %Main = OpFunction %void None %10\n"));
535fd4e5da5Sopenharmony_ci}
536fd4e5da5Sopenharmony_ci
537fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BranchConditionalFalseTargetFirstBlockBad) {
538fd4e5da5Sopenharmony_ci  Block entry("entry");
539fd4e5da5Sopenharmony_ci  Block bad("bad", spv::Op::OpBranchConditional);
540fd4e5da5Sopenharmony_ci  Block t("t");
541fd4e5da5Sopenharmony_ci  Block merge("merge");
542fd4e5da5Sopenharmony_ci  Block end("end", spv::Op::OpReturn);
543fd4e5da5Sopenharmony_ci
544fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
545fd4e5da5Sopenharmony_ci  bad.SetBody("OpLoopMerge %merge %cont None\n");
546fd4e5da5Sopenharmony_ci
547fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
548fd4e5da5Sopenharmony_ci                    nameOps("entry", "bad", std::make_pair("func", "Main")) +
549fd4e5da5Sopenharmony_ci                    types_consts() +
550fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
551fd4e5da5Sopenharmony_ci
552fd4e5da5Sopenharmony_ci  str += entry >> bad;
553fd4e5da5Sopenharmony_ci  str += bad >> std::vector<Block>({t, entry});
554fd4e5da5Sopenharmony_ci  str += merge >> end;
555fd4e5da5Sopenharmony_ci  str += end;
556fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
557fd4e5da5Sopenharmony_ci
558fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
559fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
560fd4e5da5Sopenharmony_ci  EXPECT_THAT(
561fd4e5da5Sopenharmony_ci      getDiagnosticString(),
562fd4e5da5Sopenharmony_ci      MatchesRegex("First block '.\\[%entry\\]' of function '.\\[%Main\\]' "
563fd4e5da5Sopenharmony_ci                   "is targeted by block '.\\[%bad\\]'\n"
564fd4e5da5Sopenharmony_ci                   "  %Main = OpFunction %void None %10\n"));
565fd4e5da5Sopenharmony_ci}
566fd4e5da5Sopenharmony_ci
567fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, SwitchTargetFirstBlockBad) {
568fd4e5da5Sopenharmony_ci  Block entry("entry");
569fd4e5da5Sopenharmony_ci  Block bad("bad", spv::Op::OpSwitch);
570fd4e5da5Sopenharmony_ci  Block block1("block1");
571fd4e5da5Sopenharmony_ci  Block block2("block2");
572fd4e5da5Sopenharmony_ci  Block block3("block3");
573fd4e5da5Sopenharmony_ci  Block def("def");  // default block
574fd4e5da5Sopenharmony_ci  Block merge("merge");
575fd4e5da5Sopenharmony_ci  Block end("end", spv::Op::OpReturn);
576fd4e5da5Sopenharmony_ci
577fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
578fd4e5da5Sopenharmony_ci  bad.SetBody("OpSelectionMerge %merge None\n");
579fd4e5da5Sopenharmony_ci
580fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
581fd4e5da5Sopenharmony_ci                    nameOps("entry", "bad", std::make_pair("func", "Main")) +
582fd4e5da5Sopenharmony_ci                    types_consts() +
583fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
584fd4e5da5Sopenharmony_ci
585fd4e5da5Sopenharmony_ci  str += entry >> bad;
586fd4e5da5Sopenharmony_ci  str += bad >> std::vector<Block>({def, block1, block2, block3, entry});
587fd4e5da5Sopenharmony_ci  str += def >> merge;
588fd4e5da5Sopenharmony_ci  str += block1 >> merge;
589fd4e5da5Sopenharmony_ci  str += block2 >> merge;
590fd4e5da5Sopenharmony_ci  str += block3 >> merge;
591fd4e5da5Sopenharmony_ci  str += merge >> end;
592fd4e5da5Sopenharmony_ci  str += end;
593fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
594fd4e5da5Sopenharmony_ci
595fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
596fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
597fd4e5da5Sopenharmony_ci  EXPECT_THAT(
598fd4e5da5Sopenharmony_ci      getDiagnosticString(),
599fd4e5da5Sopenharmony_ci      MatchesRegex("First block '.\\[%entry\\]' of function '.\\[%Main\\]' "
600fd4e5da5Sopenharmony_ci                   "is targeted by block '.\\[%bad\\]'\n"
601fd4e5da5Sopenharmony_ci                   "  %Main = OpFunction %void None %10\n"));
602fd4e5da5Sopenharmony_ci}
603fd4e5da5Sopenharmony_ci
604fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) {
605fd4e5da5Sopenharmony_ci  Block entry("entry");
606fd4e5da5Sopenharmony_ci  Block middle("middle", spv::Op::OpBranchConditional);
607fd4e5da5Sopenharmony_ci  Block end("end", spv::Op::OpReturn);
608fd4e5da5Sopenharmony_ci
609fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
610fd4e5da5Sopenharmony_ci  middle.SetBody("OpSelectionMerge %end None\n");
611fd4e5da5Sopenharmony_ci
612fd4e5da5Sopenharmony_ci  Block entry2("entry2");
613fd4e5da5Sopenharmony_ci  Block middle2("middle2");
614fd4e5da5Sopenharmony_ci  Block end2("end2", spv::Op::OpReturn);
615fd4e5da5Sopenharmony_ci
616fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
617fd4e5da5Sopenharmony_ci                    nameOps("middle2", std::make_pair("func", "Main")) +
618fd4e5da5Sopenharmony_ci                    types_consts() +
619fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
620fd4e5da5Sopenharmony_ci
621fd4e5da5Sopenharmony_ci  str += entry >> middle;
622fd4e5da5Sopenharmony_ci  str += middle >> std::vector<Block>({end, middle2});
623fd4e5da5Sopenharmony_ci  str += end;
624fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
625fd4e5da5Sopenharmony_ci
626fd4e5da5Sopenharmony_ci  str += "%func2    = OpFunction %voidt None %funct\n";
627fd4e5da5Sopenharmony_ci  str += entry2 >> middle2;
628fd4e5da5Sopenharmony_ci  str += middle2 >> end2;
629fd4e5da5Sopenharmony_ci  str += end2;
630fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
631fd4e5da5Sopenharmony_ci
632fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
633fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
634fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
635fd4e5da5Sopenharmony_ci              MatchesRegex(
636fd4e5da5Sopenharmony_ci                  "Block\\(s\\) \\{'.\\[%middle2\\]'\\} are referenced but not "
637fd4e5da5Sopenharmony_ci                  "defined in function '.\\[%Main\\]'\n"
638fd4e5da5Sopenharmony_ci                  "  %Main = OpFunction %void None %9\n"));
639fd4e5da5Sopenharmony_ci}
640fd4e5da5Sopenharmony_ci
641fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) {
642fd4e5da5Sopenharmony_ci  // If a merge block is reachable, then it must be strictly dominated by
643fd4e5da5Sopenharmony_ci  // its header block.
644fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
645fd4e5da5Sopenharmony_ci  Block head("head", spv::Op::OpBranchConditional);
646fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
647fd4e5da5Sopenharmony_ci
648fd4e5da5Sopenharmony_ci  head.SetBody("%cond = OpSLessThan %boolt %one %two\n");
649fd4e5da5Sopenharmony_ci
650fd4e5da5Sopenharmony_ci  if (is_shader) head.AppendBody("OpSelectionMerge %head None\n");
651fd4e5da5Sopenharmony_ci
652fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
653fd4e5da5Sopenharmony_ci                    nameOps("head", "exit", std::make_pair("func", "Main")) +
654fd4e5da5Sopenharmony_ci                    types_consts() +
655fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
656fd4e5da5Sopenharmony_ci
657fd4e5da5Sopenharmony_ci  str += head >> std::vector<Block>({exit, exit});
658fd4e5da5Sopenharmony_ci  str += exit;
659fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
660fd4e5da5Sopenharmony_ci
661fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
662fd4e5da5Sopenharmony_ci  if (is_shader) {
663fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
664fd4e5da5Sopenharmony_ci    EXPECT_THAT(
665fd4e5da5Sopenharmony_ci        getDiagnosticString(),
666fd4e5da5Sopenharmony_ci        MatchesRegex(
667fd4e5da5Sopenharmony_ci            "The selection construct with the selection header "
668fd4e5da5Sopenharmony_ci            "'.\\[%head\\]' does not strictly structurally dominate the "
669fd4e5da5Sopenharmony_ci            "merge block "
670fd4e5da5Sopenharmony_ci            "'.\\[%head\\]'\n  %head = OpLabel\n"));
671fd4e5da5Sopenharmony_ci  } else {
672fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str;
673fd4e5da5Sopenharmony_ci  }
674fd4e5da5Sopenharmony_ci}
675fd4e5da5Sopenharmony_ci
676fd4e5da5Sopenharmony_cistd::string GetUnreachableMergeNoMergeInst(spv::Capability cap) {
677fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
678fd4e5da5Sopenharmony_ci  Block entry("entry");
679fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranchConditional);
680fd4e5da5Sopenharmony_ci  Block t("t", spv::Op::OpReturn);
681fd4e5da5Sopenharmony_ci  Block f("f", spv::Op::OpReturn);
682fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
683fd4e5da5Sopenharmony_ci
684fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
685fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader)
686fd4e5da5Sopenharmony_ci    branch.AppendBody("OpSelectionMerge %merge None\n");
687fd4e5da5Sopenharmony_ci
688fd4e5da5Sopenharmony_ci  std::string str = header;
689fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", std::make_pair("func", "Main"));
690fd4e5da5Sopenharmony_ci  str += types_consts() + "%func    = OpFunction %voidt None %funct\n";
691fd4e5da5Sopenharmony_ci  str += entry >> branch;
692fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({t, f});
693fd4e5da5Sopenharmony_ci  str += t;
694fd4e5da5Sopenharmony_ci  str += f;
695fd4e5da5Sopenharmony_ci  str += merge;
696fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
697fd4e5da5Sopenharmony_ci
698fd4e5da5Sopenharmony_ci  return str;
699fd4e5da5Sopenharmony_ci}
700fd4e5da5Sopenharmony_ci
701fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableMergeNoMergeInst) {
702fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableMergeNoMergeInst(GetParam()));
703fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
704fd4e5da5Sopenharmony_ci}
705fd4e5da5Sopenharmony_ci
706fd4e5da5Sopenharmony_cistd::string GetUnreachableMergeTerminatedBy(spv::Capability cap, spv::Op op) {
707fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
708fd4e5da5Sopenharmony_ci
709fd4e5da5Sopenharmony_ci  Block entry("entry");
710fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranchConditional);
711fd4e5da5Sopenharmony_ci  Block t("t", spv::Op::OpReturn);
712fd4e5da5Sopenharmony_ci  Block f("f", spv::Op::OpReturn);
713fd4e5da5Sopenharmony_ci  Block merge("merge", op);
714fd4e5da5Sopenharmony_ci
715fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
716fd4e5da5Sopenharmony_ci  std::string str = header;
717fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader)
718fd4e5da5Sopenharmony_ci    branch.AppendBody("OpSelectionMerge %merge None\n");
719fd4e5da5Sopenharmony_ci
720fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", std::make_pair("func", "Main"));
721fd4e5da5Sopenharmony_ci  str += types_consts();
722fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
723fd4e5da5Sopenharmony_ci  str += entry >> branch;
724fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({t, f});
725fd4e5da5Sopenharmony_ci  str += t;
726fd4e5da5Sopenharmony_ci  str += f;
727fd4e5da5Sopenharmony_ci  str += merge;
728fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
729fd4e5da5Sopenharmony_ci
730fd4e5da5Sopenharmony_ci  return str;
731fd4e5da5Sopenharmony_ci}
732fd4e5da5Sopenharmony_ci
733fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableMergeTerminatedByOpUnreachable) {
734fd4e5da5Sopenharmony_ci  CompileSuccessfully(
735fd4e5da5Sopenharmony_ci      GetUnreachableMergeTerminatedBy(GetParam(), spv::Op::OpUnreachable));
736fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
737fd4e5da5Sopenharmony_ci}
738fd4e5da5Sopenharmony_ci
739fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, UnreachableMergeTerminatedByOpKill) {
740fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableMergeTerminatedBy(spv::Capability::Shader,
741fd4e5da5Sopenharmony_ci                                                      spv::Op::OpKill));
742fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
743fd4e5da5Sopenharmony_ci}
744fd4e5da5Sopenharmony_ci
745fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableMergeTerminatedByOpReturn) {
746fd4e5da5Sopenharmony_ci  CompileSuccessfully(
747fd4e5da5Sopenharmony_ci      GetUnreachableMergeTerminatedBy(GetParam(), spv::Op::OpReturn));
748fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
749fd4e5da5Sopenharmony_ci}
750fd4e5da5Sopenharmony_ci
751fd4e5da5Sopenharmony_cistd::string GetUnreachableContinueTerminatedBy(spv::Capability cap,
752fd4e5da5Sopenharmony_ci                                               spv::Op op) {
753fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
754fd4e5da5Sopenharmony_ci
755fd4e5da5Sopenharmony_ci  Block entry("entry");
756fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranch);
757fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
758fd4e5da5Sopenharmony_ci  Block target("target", op);
759fd4e5da5Sopenharmony_ci
760fd4e5da5Sopenharmony_ci  if (op == spv::Op::OpBranch) target >> branch;
761fd4e5da5Sopenharmony_ci
762fd4e5da5Sopenharmony_ci  std::string str = header;
763fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader)
764fd4e5da5Sopenharmony_ci    branch.AppendBody("OpLoopMerge %merge %target None\n");
765fd4e5da5Sopenharmony_ci
766fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
767fd4e5da5Sopenharmony_ci  str += types_consts();
768fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
769fd4e5da5Sopenharmony_ci  str += entry >> branch;
770fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({merge});
771fd4e5da5Sopenharmony_ci  str += merge;
772fd4e5da5Sopenharmony_ci  str += target;
773fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
774fd4e5da5Sopenharmony_ci
775fd4e5da5Sopenharmony_ci  return str;
776fd4e5da5Sopenharmony_ci}
777fd4e5da5Sopenharmony_ci
778fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableContinueTerminatedByOpUnreachable) {
779fd4e5da5Sopenharmony_ci  CompileSuccessfully(
780fd4e5da5Sopenharmony_ci      GetUnreachableContinueTerminatedBy(GetParam(), spv::Op::OpUnreachable));
781fd4e5da5Sopenharmony_ci  if (GetParam() == spv::Capability::Shader) {
782fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
783fd4e5da5Sopenharmony_ci    EXPECT_THAT(getDiagnosticString(),
784fd4e5da5Sopenharmony_ci                HasSubstr("targeted by 0 back-edge blocks"));
785fd4e5da5Sopenharmony_ci  } else {
786fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
787fd4e5da5Sopenharmony_ci  }
788fd4e5da5Sopenharmony_ci}
789fd4e5da5Sopenharmony_ci
790fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, UnreachableContinueTerminatedByOpKill) {
791fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableContinueTerminatedBy(
792fd4e5da5Sopenharmony_ci      spv::Capability::Shader, spv::Op::OpKill));
793fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
794fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
795fd4e5da5Sopenharmony_ci              HasSubstr("targeted by 0 back-edge blocks"));
796fd4e5da5Sopenharmony_ci}
797fd4e5da5Sopenharmony_ci
798fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableContinueTerminatedByOpReturn) {
799fd4e5da5Sopenharmony_ci  CompileSuccessfully(
800fd4e5da5Sopenharmony_ci      GetUnreachableContinueTerminatedBy(GetParam(), spv::Op::OpReturn));
801fd4e5da5Sopenharmony_ci  if (GetParam() == spv::Capability::Shader) {
802fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
803fd4e5da5Sopenharmony_ci    EXPECT_THAT(getDiagnosticString(),
804fd4e5da5Sopenharmony_ci                HasSubstr("targeted by 0 back-edge blocks"));
805fd4e5da5Sopenharmony_ci  } else {
806fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
807fd4e5da5Sopenharmony_ci  }
808fd4e5da5Sopenharmony_ci}
809fd4e5da5Sopenharmony_ci
810fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableContinueTerminatedByOpBranch) {
811fd4e5da5Sopenharmony_ci  CompileSuccessfully(
812fd4e5da5Sopenharmony_ci      GetUnreachableContinueTerminatedBy(GetParam(), spv::Op::OpBranch));
813fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
814fd4e5da5Sopenharmony_ci}
815fd4e5da5Sopenharmony_ci
816fd4e5da5Sopenharmony_cistd::string GetUnreachableMergeUnreachableMergeInst(spv::Capability cap) {
817fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
818fd4e5da5Sopenharmony_ci
819fd4e5da5Sopenharmony_ci  Block body("body", spv::Op::OpReturn);
820fd4e5da5Sopenharmony_ci  Block entry("entry");
821fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranchConditional);
822fd4e5da5Sopenharmony_ci  Block t("t", spv::Op::OpReturn);
823fd4e5da5Sopenharmony_ci  Block f("f", spv::Op::OpReturn);
824fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpUnreachable);
825fd4e5da5Sopenharmony_ci
826fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
827fd4e5da5Sopenharmony_ci  std::string str = header;
828fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader)
829fd4e5da5Sopenharmony_ci    branch.AppendBody("OpSelectionMerge %merge None\n");
830fd4e5da5Sopenharmony_ci
831fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", std::make_pair("func", "Main"));
832fd4e5da5Sopenharmony_ci  str += types_consts();
833fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
834fd4e5da5Sopenharmony_ci  str += body;
835fd4e5da5Sopenharmony_ci  str += merge;
836fd4e5da5Sopenharmony_ci  str += entry >> branch;
837fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({t, f});
838fd4e5da5Sopenharmony_ci  str += t;
839fd4e5da5Sopenharmony_ci  str += f;
840fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
841fd4e5da5Sopenharmony_ci
842fd4e5da5Sopenharmony_ci  return str;
843fd4e5da5Sopenharmony_ci}
844fd4e5da5Sopenharmony_ci
845fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableMergeUnreachableMergeInst) {
846fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst(GetParam()));
847fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
848fd4e5da5Sopenharmony_ci}
849fd4e5da5Sopenharmony_ci
850fd4e5da5Sopenharmony_cistd::string GetUnreachableContinueUnreachableLoopInst(spv::Capability cap) {
851fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
852fd4e5da5Sopenharmony_ci
853fd4e5da5Sopenharmony_ci  Block body("body", spv::Op::OpReturn);
854fd4e5da5Sopenharmony_ci  Block entry("entry");
855fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranch);
856fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
857fd4e5da5Sopenharmony_ci  Block target("target", spv::Op::OpBranch);
858fd4e5da5Sopenharmony_ci
859fd4e5da5Sopenharmony_ci  target >> branch;
860fd4e5da5Sopenharmony_ci
861fd4e5da5Sopenharmony_ci  std::string str = header;
862fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader)
863fd4e5da5Sopenharmony_ci    branch.AppendBody("OpLoopMerge %merge %target None\n");
864fd4e5da5Sopenharmony_ci
865fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
866fd4e5da5Sopenharmony_ci  str += types_consts();
867fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
868fd4e5da5Sopenharmony_ci  str += body;
869fd4e5da5Sopenharmony_ci  str += target;
870fd4e5da5Sopenharmony_ci  str += merge;
871fd4e5da5Sopenharmony_ci  str += entry >> branch;
872fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({merge});
873fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
874fd4e5da5Sopenharmony_ci
875fd4e5da5Sopenharmony_ci  return str;
876fd4e5da5Sopenharmony_ci}
877fd4e5da5Sopenharmony_ci
878fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) {
879fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst(GetParam()));
880fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
881fd4e5da5Sopenharmony_ci}
882fd4e5da5Sopenharmony_ci
883fd4e5da5Sopenharmony_cistd::string GetUnreachableMergeWithComplexBody(spv::Capability cap) {
884fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
885fd4e5da5Sopenharmony_ci
886fd4e5da5Sopenharmony_ci  Block entry("entry");
887fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranchConditional);
888fd4e5da5Sopenharmony_ci  Block t("t", spv::Op::OpReturn);
889fd4e5da5Sopenharmony_ci  Block f("f", spv::Op::OpReturn);
890fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpUnreachable);
891fd4e5da5Sopenharmony_ci
892fd4e5da5Sopenharmony_ci  entry.AppendBody("%placeholder   = OpVariable %intptrt Function\n");
893fd4e5da5Sopenharmony_ci  entry.AppendBody("%cond    = OpSLessThan %boolt %one %two\n");
894fd4e5da5Sopenharmony_ci  merge.AppendBody("OpStore %placeholder %one\n");
895fd4e5da5Sopenharmony_ci
896fd4e5da5Sopenharmony_ci  std::string str = header;
897fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader)
898fd4e5da5Sopenharmony_ci    branch.AppendBody("OpSelectionMerge %merge None\n");
899fd4e5da5Sopenharmony_ci
900fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", std::make_pair("func", "Main"));
901fd4e5da5Sopenharmony_ci  str += types_consts();
902fd4e5da5Sopenharmony_ci  str += "%intptrt = OpTypePointer Function %intt\n";
903fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
904fd4e5da5Sopenharmony_ci  str += entry >> branch;
905fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({t, f});
906fd4e5da5Sopenharmony_ci  str += t;
907fd4e5da5Sopenharmony_ci  str += f;
908fd4e5da5Sopenharmony_ci  str += merge;
909fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
910fd4e5da5Sopenharmony_ci
911fd4e5da5Sopenharmony_ci  return str;
912fd4e5da5Sopenharmony_ci}
913fd4e5da5Sopenharmony_ci
914fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableMergeWithComplexBody) {
915fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableMergeWithComplexBody(GetParam()));
916fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
917fd4e5da5Sopenharmony_ci}
918fd4e5da5Sopenharmony_ci
919fd4e5da5Sopenharmony_cistd::string GetUnreachableContinueWithComplexBody(spv::Capability cap) {
920fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
921fd4e5da5Sopenharmony_ci
922fd4e5da5Sopenharmony_ci  Block entry("entry");
923fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranch);
924fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
925fd4e5da5Sopenharmony_ci  Block target("target", spv::Op::OpBranch);
926fd4e5da5Sopenharmony_ci
927fd4e5da5Sopenharmony_ci  target >> branch;
928fd4e5da5Sopenharmony_ci
929fd4e5da5Sopenharmony_ci  entry.AppendBody("%placeholder   = OpVariable %intptrt Function\n");
930fd4e5da5Sopenharmony_ci  target.AppendBody("OpStore %placeholder %one\n");
931fd4e5da5Sopenharmony_ci
932fd4e5da5Sopenharmony_ci  std::string str = header;
933fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader)
934fd4e5da5Sopenharmony_ci    branch.AppendBody("OpLoopMerge %merge %target None\n");
935fd4e5da5Sopenharmony_ci
936fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
937fd4e5da5Sopenharmony_ci  str += types_consts();
938fd4e5da5Sopenharmony_ci  str += "%intptrt = OpTypePointer Function %intt\n";
939fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
940fd4e5da5Sopenharmony_ci  str += entry >> branch;
941fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({merge});
942fd4e5da5Sopenharmony_ci  str += merge;
943fd4e5da5Sopenharmony_ci  str += target;
944fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
945fd4e5da5Sopenharmony_ci
946fd4e5da5Sopenharmony_ci  return str;
947fd4e5da5Sopenharmony_ci}
948fd4e5da5Sopenharmony_ci
949fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableContinueWithComplexBody) {
950fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableContinueWithComplexBody(GetParam()));
951fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
952fd4e5da5Sopenharmony_ci}
953fd4e5da5Sopenharmony_ci
954fd4e5da5Sopenharmony_cistd::string GetUnreachableMergeWithBranchUse(spv::Capability cap) {
955fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
956fd4e5da5Sopenharmony_ci
957fd4e5da5Sopenharmony_ci  Block entry("entry");
958fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranchConditional);
959fd4e5da5Sopenharmony_ci  Block t("t", spv::Op::OpBranch);
960fd4e5da5Sopenharmony_ci  Block f("f", spv::Op::OpReturn);
961fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpUnreachable);
962fd4e5da5Sopenharmony_ci
963fd4e5da5Sopenharmony_ci  entry.AppendBody("%cond    = OpSLessThan %boolt %one %two\n");
964fd4e5da5Sopenharmony_ci
965fd4e5da5Sopenharmony_ci  std::string str = header;
966fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader)
967fd4e5da5Sopenharmony_ci    branch.AppendBody("OpSelectionMerge %merge None\n");
968fd4e5da5Sopenharmony_ci
969fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", std::make_pair("func", "Main"));
970fd4e5da5Sopenharmony_ci  str += types_consts();
971fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
972fd4e5da5Sopenharmony_ci  str += entry >> branch;
973fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({t, f});
974fd4e5da5Sopenharmony_ci  str += t >> merge;
975fd4e5da5Sopenharmony_ci  str += f;
976fd4e5da5Sopenharmony_ci  str += merge;
977fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
978fd4e5da5Sopenharmony_ci
979fd4e5da5Sopenharmony_ci  return str;
980fd4e5da5Sopenharmony_ci}
981fd4e5da5Sopenharmony_ci
982fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableMergeWithBranchUse) {
983fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableMergeWithBranchUse(GetParam()));
984fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
985fd4e5da5Sopenharmony_ci}
986fd4e5da5Sopenharmony_ci
987fd4e5da5Sopenharmony_cistd::string GetUnreachableMergeWithMultipleUses(spv::Capability cap) {
988fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
989fd4e5da5Sopenharmony_ci
990fd4e5da5Sopenharmony_ci  Block entry("entry");
991fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranchConditional);
992fd4e5da5Sopenharmony_ci  Block t("t", spv::Op::OpReturn);
993fd4e5da5Sopenharmony_ci  Block f("f", spv::Op::OpReturn);
994fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpUnreachable);
995fd4e5da5Sopenharmony_ci  Block duplicate("duplicate", spv::Op::OpBranchConditional);
996fd4e5da5Sopenharmony_ci
997fd4e5da5Sopenharmony_ci  entry.AppendBody("%cond    = OpSLessThan %boolt %one %two\n");
998fd4e5da5Sopenharmony_ci
999fd4e5da5Sopenharmony_ci  std::string str = header;
1000fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader) {
1001fd4e5da5Sopenharmony_ci    branch.AppendBody("OpSelectionMerge %merge None\n");
1002fd4e5da5Sopenharmony_ci    duplicate.AppendBody("OpSelectionMerge %merge None\n");
1003fd4e5da5Sopenharmony_ci  }
1004fd4e5da5Sopenharmony_ci
1005fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", std::make_pair("func", "Main"));
1006fd4e5da5Sopenharmony_ci  str += types_consts();
1007fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
1008fd4e5da5Sopenharmony_ci  str += entry >> branch;
1009fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({t, f});
1010fd4e5da5Sopenharmony_ci  str += duplicate >> std::vector<Block>({t, f});
1011fd4e5da5Sopenharmony_ci  str += t;
1012fd4e5da5Sopenharmony_ci  str += f;
1013fd4e5da5Sopenharmony_ci  str += merge;
1014fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
1015fd4e5da5Sopenharmony_ci
1016fd4e5da5Sopenharmony_ci  return str;
1017fd4e5da5Sopenharmony_ci}
1018fd4e5da5Sopenharmony_ci
1019fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableMergeWithMultipleUses) {
1020fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableMergeWithMultipleUses(GetParam()));
1021fd4e5da5Sopenharmony_ci  if (GetParam() == spv::Capability::Shader) {
1022fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1023fd4e5da5Sopenharmony_ci    EXPECT_THAT(getDiagnosticString(),
1024fd4e5da5Sopenharmony_ci                HasSubstr("is already a merge block for another header"));
1025fd4e5da5Sopenharmony_ci  } else {
1026fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1027fd4e5da5Sopenharmony_ci  }
1028fd4e5da5Sopenharmony_ci}
1029fd4e5da5Sopenharmony_ci
1030fd4e5da5Sopenharmony_cistd::string GetUnreachableContinueWithBranchUse(spv::Capability cap) {
1031fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
1032fd4e5da5Sopenharmony_ci
1033fd4e5da5Sopenharmony_ci  Block entry("entry");
1034fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranch);
1035fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
1036fd4e5da5Sopenharmony_ci  Block target("target", spv::Op::OpBranch);
1037fd4e5da5Sopenharmony_ci
1038fd4e5da5Sopenharmony_ci  target >> branch;
1039fd4e5da5Sopenharmony_ci
1040fd4e5da5Sopenharmony_ci  entry.AppendBody("%placeholder   = OpVariable %intptrt Function\n");
1041fd4e5da5Sopenharmony_ci
1042fd4e5da5Sopenharmony_ci  std::string str = header;
1043fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader)
1044fd4e5da5Sopenharmony_ci    branch.AppendBody("OpLoopMerge %merge %target None\n");
1045fd4e5da5Sopenharmony_ci
1046fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
1047fd4e5da5Sopenharmony_ci  str += types_consts();
1048fd4e5da5Sopenharmony_ci  str += "%intptrt = OpTypePointer Function %intt\n";
1049fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
1050fd4e5da5Sopenharmony_ci  str += entry >> branch;
1051fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({merge});
1052fd4e5da5Sopenharmony_ci  str += merge;
1053fd4e5da5Sopenharmony_ci  str += target;
1054fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
1055fd4e5da5Sopenharmony_ci
1056fd4e5da5Sopenharmony_ci  return str;
1057fd4e5da5Sopenharmony_ci}
1058fd4e5da5Sopenharmony_ci
1059fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableContinueWithBranchUse) {
1060fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableContinueWithBranchUse(GetParam()));
1061fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1062fd4e5da5Sopenharmony_ci}
1063fd4e5da5Sopenharmony_ci
1064fd4e5da5Sopenharmony_cistd::string GetReachableMergeAndContinue(spv::Capability cap) {
1065fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
1066fd4e5da5Sopenharmony_ci
1067fd4e5da5Sopenharmony_ci  Block entry("entry");
1068fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranch);
1069fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
1070fd4e5da5Sopenharmony_ci  Block target("target", spv::Op::OpBranch);
1071fd4e5da5Sopenharmony_ci  Block body("body", spv::Op::OpBranchConditional);
1072fd4e5da5Sopenharmony_ci  Block t("t", spv::Op::OpBranch);
1073fd4e5da5Sopenharmony_ci  Block f("f", spv::Op::OpBranch);
1074fd4e5da5Sopenharmony_ci
1075fd4e5da5Sopenharmony_ci  target >> branch;
1076fd4e5da5Sopenharmony_ci  body.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1077fd4e5da5Sopenharmony_ci  t >> merge;
1078fd4e5da5Sopenharmony_ci  f >> target;
1079fd4e5da5Sopenharmony_ci
1080fd4e5da5Sopenharmony_ci  std::string str = header;
1081fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader) {
1082fd4e5da5Sopenharmony_ci    branch.AppendBody("OpLoopMerge %merge %target None\n");
1083fd4e5da5Sopenharmony_ci    body.AppendBody("OpSelectionMerge %f None\n");
1084fd4e5da5Sopenharmony_ci  }
1085fd4e5da5Sopenharmony_ci
1086fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", "target", "body", "t", "f",
1087fd4e5da5Sopenharmony_ci                 std::make_pair("func", "Main"));
1088fd4e5da5Sopenharmony_ci  str += types_consts();
1089fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
1090fd4e5da5Sopenharmony_ci  str += entry >> branch;
1091fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({body});
1092fd4e5da5Sopenharmony_ci  str += body >> std::vector<Block>({t, f});
1093fd4e5da5Sopenharmony_ci  str += t;
1094fd4e5da5Sopenharmony_ci  str += f;
1095fd4e5da5Sopenharmony_ci  str += merge;
1096fd4e5da5Sopenharmony_ci  str += target;
1097fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
1098fd4e5da5Sopenharmony_ci
1099fd4e5da5Sopenharmony_ci  return str;
1100fd4e5da5Sopenharmony_ci}
1101fd4e5da5Sopenharmony_ci
1102fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, ReachableMergeAndContinue) {
1103fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetReachableMergeAndContinue(GetParam()));
1104fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1105fd4e5da5Sopenharmony_ci}
1106fd4e5da5Sopenharmony_ci
1107fd4e5da5Sopenharmony_cistd::string GetUnreachableMergeAndContinue(spv::Capability cap) {
1108fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
1109fd4e5da5Sopenharmony_ci
1110fd4e5da5Sopenharmony_ci  Block entry("entry");
1111fd4e5da5Sopenharmony_ci  Block branch("branch", spv::Op::OpBranch);
1112fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
1113fd4e5da5Sopenharmony_ci  Block target("target", spv::Op::OpBranch);
1114fd4e5da5Sopenharmony_ci  Block body("body", spv::Op::OpBranchConditional);
1115fd4e5da5Sopenharmony_ci  Block t("t", spv::Op::OpReturn);
1116fd4e5da5Sopenharmony_ci  Block f("f", spv::Op::OpReturn);
1117fd4e5da5Sopenharmony_ci  Block pre_target("pre_target", spv::Op::OpBranch);
1118fd4e5da5Sopenharmony_ci
1119fd4e5da5Sopenharmony_ci  target >> branch;
1120fd4e5da5Sopenharmony_ci  body.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1121fd4e5da5Sopenharmony_ci
1122fd4e5da5Sopenharmony_ci  std::string str = header;
1123fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader) {
1124fd4e5da5Sopenharmony_ci    branch.AppendBody("OpLoopMerge %merge %target None\n");
1125fd4e5da5Sopenharmony_ci    body.AppendBody("OpSelectionMerge %pre_target None\n");
1126fd4e5da5Sopenharmony_ci  }
1127fd4e5da5Sopenharmony_ci
1128fd4e5da5Sopenharmony_ci  str += nameOps("branch", "merge", "pre_target", "target", "body", "t", "f",
1129fd4e5da5Sopenharmony_ci                 std::make_pair("func", "Main"));
1130fd4e5da5Sopenharmony_ci  str += types_consts();
1131fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
1132fd4e5da5Sopenharmony_ci  str += entry >> branch;
1133fd4e5da5Sopenharmony_ci  str += branch >> std::vector<Block>({body});
1134fd4e5da5Sopenharmony_ci  str += body >> std::vector<Block>({t, f});
1135fd4e5da5Sopenharmony_ci  str += t;
1136fd4e5da5Sopenharmony_ci  str += f;
1137fd4e5da5Sopenharmony_ci  str += merge;
1138fd4e5da5Sopenharmony_ci  str += pre_target >> target;
1139fd4e5da5Sopenharmony_ci  str += target;
1140fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
1141fd4e5da5Sopenharmony_ci
1142fd4e5da5Sopenharmony_ci  return str;
1143fd4e5da5Sopenharmony_ci}
1144fd4e5da5Sopenharmony_ci
1145fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableMergeAndContinue) {
1146fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableMergeAndContinue(GetParam()));
1147fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1148fd4e5da5Sopenharmony_ci}
1149fd4e5da5Sopenharmony_ci
1150fd4e5da5Sopenharmony_cistd::string GetUnreachableBlock(spv::Capability cap) {
1151fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
1152fd4e5da5Sopenharmony_ci
1153fd4e5da5Sopenharmony_ci  Block entry("entry");
1154fd4e5da5Sopenharmony_ci  Block unreachable("unreachable");
1155fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
1156fd4e5da5Sopenharmony_ci
1157fd4e5da5Sopenharmony_ci  std::string str = header;
1158fd4e5da5Sopenharmony_ci  str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
1159fd4e5da5Sopenharmony_ci  str += types_consts();
1160fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
1161fd4e5da5Sopenharmony_ci  str += entry >> exit;
1162fd4e5da5Sopenharmony_ci  str += unreachable >> exit;
1163fd4e5da5Sopenharmony_ci  str += exit;
1164fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
1165fd4e5da5Sopenharmony_ci
1166fd4e5da5Sopenharmony_ci  return str;
1167fd4e5da5Sopenharmony_ci}
1168fd4e5da5Sopenharmony_ci
1169fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableBlock) {
1170fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableBlock(GetParam()));
1171fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1172fd4e5da5Sopenharmony_ci}
1173fd4e5da5Sopenharmony_ci
1174fd4e5da5Sopenharmony_cistd::string GetUnreachableBranch(spv::Capability cap) {
1175fd4e5da5Sopenharmony_ci  std::string header = GetDefaultHeader(cap);
1176fd4e5da5Sopenharmony_ci
1177fd4e5da5Sopenharmony_ci  Block entry("entry");
1178fd4e5da5Sopenharmony_ci  Block unreachable("unreachable", spv::Op::OpBranchConditional);
1179fd4e5da5Sopenharmony_ci  Block unreachablechildt("unreachablechildt");
1180fd4e5da5Sopenharmony_ci  Block unreachablechildf("unreachablechildf");
1181fd4e5da5Sopenharmony_ci  Block merge("merge");
1182fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
1183fd4e5da5Sopenharmony_ci
1184fd4e5da5Sopenharmony_ci  unreachable.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1185fd4e5da5Sopenharmony_ci  if (cap == spv::Capability::Shader)
1186fd4e5da5Sopenharmony_ci    unreachable.AppendBody("OpSelectionMerge %merge None\n");
1187fd4e5da5Sopenharmony_ci
1188fd4e5da5Sopenharmony_ci  std::string str = header;
1189fd4e5da5Sopenharmony_ci  str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
1190fd4e5da5Sopenharmony_ci  str += types_consts();
1191fd4e5da5Sopenharmony_ci  str += "%func    = OpFunction %voidt None %funct\n";
1192fd4e5da5Sopenharmony_ci
1193fd4e5da5Sopenharmony_ci  str += entry >> exit;
1194fd4e5da5Sopenharmony_ci  str +=
1195fd4e5da5Sopenharmony_ci      unreachable >> std::vector<Block>({unreachablechildt, unreachablechildf});
1196fd4e5da5Sopenharmony_ci  str += unreachablechildt >> merge;
1197fd4e5da5Sopenharmony_ci  str += unreachablechildf >> merge;
1198fd4e5da5Sopenharmony_ci  str += merge >> exit;
1199fd4e5da5Sopenharmony_ci  str += exit;
1200fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd\n";
1201fd4e5da5Sopenharmony_ci
1202fd4e5da5Sopenharmony_ci  return str;
1203fd4e5da5Sopenharmony_ci}
1204fd4e5da5Sopenharmony_ci
1205fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, UnreachableBranch) {
1206fd4e5da5Sopenharmony_ci  CompileSuccessfully(GetUnreachableBranch(GetParam()));
1207fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1208fd4e5da5Sopenharmony_ci}
1209fd4e5da5Sopenharmony_ci
1210fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, EmptyFunction) {
1211fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
1212fd4e5da5Sopenharmony_ci                    R"(%func    = OpFunction %voidt None %funct
1213fd4e5da5Sopenharmony_ci                  %l = OpLabel
1214fd4e5da5Sopenharmony_ci                  OpReturn
1215fd4e5da5Sopenharmony_ci                  OpFunctionEnd)";
1216fd4e5da5Sopenharmony_ci
1217fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1218fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1219fd4e5da5Sopenharmony_ci}
1220fd4e5da5Sopenharmony_ci
1221fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, SingleBlockLoop) {
1222fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1223fd4e5da5Sopenharmony_ci  Block entry("entry");
1224fd4e5da5Sopenharmony_ci  Block loop("loop", spv::Op::OpBranchConditional);
1225fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
1226fd4e5da5Sopenharmony_ci
1227fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1228fd4e5da5Sopenharmony_ci  if (is_shader) loop.AppendBody("OpLoopMerge %exit %loop None\n");
1229fd4e5da5Sopenharmony_ci
1230fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
1231fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
1232fd4e5da5Sopenharmony_ci
1233fd4e5da5Sopenharmony_ci  str += entry >> loop;
1234fd4e5da5Sopenharmony_ci  str += loop >> std::vector<Block>({loop, exit});
1235fd4e5da5Sopenharmony_ci  str += exit;
1236fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1237fd4e5da5Sopenharmony_ci
1238fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1239fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1240fd4e5da5Sopenharmony_ci}
1241fd4e5da5Sopenharmony_ci
1242fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, NestedLoops) {
1243fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1244fd4e5da5Sopenharmony_ci  Block entry("entry");
1245fd4e5da5Sopenharmony_ci  Block loop1("loop1");
1246fd4e5da5Sopenharmony_ci  Block loop1_cont_break_block("loop1_cont_break_block",
1247fd4e5da5Sopenharmony_ci                               spv::Op::OpBranchConditional);
1248fd4e5da5Sopenharmony_ci  Block loop2("loop2", spv::Op::OpBranchConditional);
1249fd4e5da5Sopenharmony_ci  Block loop2_merge("loop2_merge");
1250fd4e5da5Sopenharmony_ci  Block loop1_merge("loop1_merge");
1251fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
1252fd4e5da5Sopenharmony_ci
1253fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1254fd4e5da5Sopenharmony_ci  if (is_shader) {
1255fd4e5da5Sopenharmony_ci    loop1.SetBody("OpLoopMerge %loop1_merge %loop2 None\n");
1256fd4e5da5Sopenharmony_ci    loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
1257fd4e5da5Sopenharmony_ci  }
1258fd4e5da5Sopenharmony_ci
1259fd4e5da5Sopenharmony_ci  std::string str =
1260fd4e5da5Sopenharmony_ci      GetDefaultHeader(GetParam()) +
1261fd4e5da5Sopenharmony_ci      nameOps("loop1", "loop1_cont_break_block", "loop2", "loop2_merge") +
1262fd4e5da5Sopenharmony_ci      types_consts() + "%func    = OpFunction %voidt None %funct\n";
1263fd4e5da5Sopenharmony_ci
1264fd4e5da5Sopenharmony_ci  str += entry >> loop1;
1265fd4e5da5Sopenharmony_ci  str += loop1 >> loop1_cont_break_block;
1266fd4e5da5Sopenharmony_ci  str += loop1_cont_break_block >> std::vector<Block>({loop1_merge, loop2});
1267fd4e5da5Sopenharmony_ci  str += loop2 >> std::vector<Block>({loop2, loop2_merge});
1268fd4e5da5Sopenharmony_ci  str += loop2_merge >> loop1;
1269fd4e5da5Sopenharmony_ci  str += loop1_merge >> exit;
1270fd4e5da5Sopenharmony_ci  str += exit;
1271fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1272fd4e5da5Sopenharmony_ci
1273fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1274fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1275fd4e5da5Sopenharmony_ci}
1276fd4e5da5Sopenharmony_ci
1277fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, NestedSelection) {
1278fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1279fd4e5da5Sopenharmony_ci  Block entry("entry");
1280fd4e5da5Sopenharmony_ci  const int N = 256;
1281fd4e5da5Sopenharmony_ci  std::vector<Block> if_blocks;
1282fd4e5da5Sopenharmony_ci  std::vector<Block> merge_blocks;
1283fd4e5da5Sopenharmony_ci  Block inner("inner");
1284fd4e5da5Sopenharmony_ci
1285fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1286fd4e5da5Sopenharmony_ci
1287fd4e5da5Sopenharmony_ci  if_blocks.emplace_back("if0", spv::Op::OpBranchConditional);
1288fd4e5da5Sopenharmony_ci
1289fd4e5da5Sopenharmony_ci  if (is_shader) if_blocks[0].SetBody("OpSelectionMerge %if_merge0 None\n");
1290fd4e5da5Sopenharmony_ci  merge_blocks.emplace_back("if_merge0", spv::Op::OpReturn);
1291fd4e5da5Sopenharmony_ci
1292fd4e5da5Sopenharmony_ci  for (int i = 1; i < N; i++) {
1293fd4e5da5Sopenharmony_ci    std::stringstream ss;
1294fd4e5da5Sopenharmony_ci    ss << i;
1295fd4e5da5Sopenharmony_ci    if_blocks.emplace_back("if" + ss.str(), spv::Op::OpBranchConditional);
1296fd4e5da5Sopenharmony_ci    if (is_shader)
1297fd4e5da5Sopenharmony_ci      if_blocks[i].SetBody("OpSelectionMerge %if_merge" + ss.str() + " None\n");
1298fd4e5da5Sopenharmony_ci    merge_blocks.emplace_back("if_merge" + ss.str(), spv::Op::OpBranch);
1299fd4e5da5Sopenharmony_ci  }
1300fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
1301fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
1302fd4e5da5Sopenharmony_ci
1303fd4e5da5Sopenharmony_ci  str += entry >> if_blocks[0];
1304fd4e5da5Sopenharmony_ci  for (int i = 0; i < N - 1; i++) {
1305fd4e5da5Sopenharmony_ci    str +=
1306fd4e5da5Sopenharmony_ci        if_blocks[i] >> std::vector<Block>({if_blocks[i + 1], merge_blocks[i]});
1307fd4e5da5Sopenharmony_ci  }
1308fd4e5da5Sopenharmony_ci  str += if_blocks.back() >> std::vector<Block>({inner, merge_blocks.back()});
1309fd4e5da5Sopenharmony_ci  str += inner >> merge_blocks.back();
1310fd4e5da5Sopenharmony_ci  for (int i = N - 1; i > 0; i--) {
1311fd4e5da5Sopenharmony_ci    str += merge_blocks[i] >> merge_blocks[i - 1];
1312fd4e5da5Sopenharmony_ci  }
1313fd4e5da5Sopenharmony_ci  str += merge_blocks[0];
1314fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1315fd4e5da5Sopenharmony_ci
1316fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1317fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1318fd4e5da5Sopenharmony_ci}
1319fd4e5da5Sopenharmony_ci
1320fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) {
1321fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1322fd4e5da5Sopenharmony_ci  Block entry("entry");
1323fd4e5da5Sopenharmony_ci  Block loop1("loop1", spv::Op::OpBranchConditional);
1324fd4e5da5Sopenharmony_ci  Block loop2("loop2", spv::Op::OpBranchConditional);
1325fd4e5da5Sopenharmony_ci  Block loop2_merge("loop2_merge");
1326fd4e5da5Sopenharmony_ci  Block loop1_cont("loop1_cont", spv::Op::OpBranchConditional);
1327fd4e5da5Sopenharmony_ci  Block be_block("be_block");
1328fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
1329fd4e5da5Sopenharmony_ci
1330fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1331fd4e5da5Sopenharmony_ci  if (is_shader) {
1332fd4e5da5Sopenharmony_ci    loop1.SetBody("OpLoopMerge %exit %loop1_cont None\n");
1333fd4e5da5Sopenharmony_ci    loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
1334fd4e5da5Sopenharmony_ci  }
1335fd4e5da5Sopenharmony_ci
1336fd4e5da5Sopenharmony_ci  std::string str =
1337fd4e5da5Sopenharmony_ci      GetDefaultHeader(GetParam()) +
1338fd4e5da5Sopenharmony_ci      nameOps("loop1", "loop2", "be_block", "loop1_cont", "loop2_merge") +
1339fd4e5da5Sopenharmony_ci      types_consts() + "%func    = OpFunction %voidt None %funct\n";
1340fd4e5da5Sopenharmony_ci
1341fd4e5da5Sopenharmony_ci  str += entry >> loop1;
1342fd4e5da5Sopenharmony_ci  str += loop1 >> std::vector<Block>({loop2, exit});
1343fd4e5da5Sopenharmony_ci  str += loop2 >> std::vector<Block>({loop2, loop2_merge});
1344fd4e5da5Sopenharmony_ci  str += loop2_merge >> loop1_cont;
1345fd4e5da5Sopenharmony_ci  str += loop1_cont >> std::vector<Block>({be_block, exit});
1346fd4e5da5Sopenharmony_ci  str += be_block >> loop1;
1347fd4e5da5Sopenharmony_ci  str += exit;
1348fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1349fd4e5da5Sopenharmony_ci
1350fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1351fd4e5da5Sopenharmony_ci  if (GetParam() == spv::Capability::Shader) {
1352fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1353fd4e5da5Sopenharmony_ci    EXPECT_THAT(
1354fd4e5da5Sopenharmony_ci        getDiagnosticString(),
1355fd4e5da5Sopenharmony_ci        MatchesRegex(
1356fd4e5da5Sopenharmony_ci            "The continue construct with the continue target "
1357fd4e5da5Sopenharmony_ci            "'.\\[%loop1_cont\\]' is not structurally post dominated by the "
1358fd4e5da5Sopenharmony_ci            "back-edge block '.\\[%be_block\\]'\n"
1359fd4e5da5Sopenharmony_ci            "  %be_block = OpLabel\n"));
1360fd4e5da5Sopenharmony_ci  } else {
1361fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1362fd4e5da5Sopenharmony_ci  }
1363fd4e5da5Sopenharmony_ci}
1364fd4e5da5Sopenharmony_ci
1365fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BranchingToNonLoopHeaderBlockBad) {
1366fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1367fd4e5da5Sopenharmony_ci  Block entry("entry");
1368fd4e5da5Sopenharmony_ci  Block split("split", spv::Op::OpBranchConditional);
1369fd4e5da5Sopenharmony_ci  Block t("t");
1370fd4e5da5Sopenharmony_ci  Block f("f");
1371fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
1372fd4e5da5Sopenharmony_ci
1373fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1374fd4e5da5Sopenharmony_ci  if (is_shader) split.SetBody("OpSelectionMerge %exit None\n");
1375fd4e5da5Sopenharmony_ci
1376fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) + nameOps("split", "f") +
1377fd4e5da5Sopenharmony_ci                    types_consts() +
1378fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
1379fd4e5da5Sopenharmony_ci
1380fd4e5da5Sopenharmony_ci  str += entry >> split;
1381fd4e5da5Sopenharmony_ci  str += split >> std::vector<Block>({t, f});
1382fd4e5da5Sopenharmony_ci  str += t >> exit;
1383fd4e5da5Sopenharmony_ci  str += f >> split;
1384fd4e5da5Sopenharmony_ci  str += exit;
1385fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1386fd4e5da5Sopenharmony_ci
1387fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1388fd4e5da5Sopenharmony_ci  if (is_shader) {
1389fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1390fd4e5da5Sopenharmony_ci    EXPECT_THAT(
1391fd4e5da5Sopenharmony_ci        getDiagnosticString(),
1392fd4e5da5Sopenharmony_ci        MatchesRegex("Back-edges \\('.\\[%f\\]' -> '.\\[%split\\]'\\) can only "
1393fd4e5da5Sopenharmony_ci                     "be formed between a block and a loop header.\n"
1394fd4e5da5Sopenharmony_ci                     "  %f = OpLabel\n"));
1395fd4e5da5Sopenharmony_ci  } else {
1396fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1397fd4e5da5Sopenharmony_ci  }
1398fd4e5da5Sopenharmony_ci}
1399fd4e5da5Sopenharmony_ci
1400fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BranchingToSameNonLoopHeaderBlockBad) {
1401fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1402fd4e5da5Sopenharmony_ci  Block entry("entry");
1403fd4e5da5Sopenharmony_ci  Block split("split", spv::Op::OpBranchConditional);
1404fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
1405fd4e5da5Sopenharmony_ci
1406fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1407fd4e5da5Sopenharmony_ci  if (is_shader) split.SetBody("OpSelectionMerge %exit None\n");
1408fd4e5da5Sopenharmony_ci
1409fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) + nameOps("split") +
1410fd4e5da5Sopenharmony_ci                    types_consts() +
1411fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
1412fd4e5da5Sopenharmony_ci
1413fd4e5da5Sopenharmony_ci  str += entry >> split;
1414fd4e5da5Sopenharmony_ci  str += split >> std::vector<Block>({split, exit});
1415fd4e5da5Sopenharmony_ci  str += exit;
1416fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1417fd4e5da5Sopenharmony_ci
1418fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1419fd4e5da5Sopenharmony_ci  if (is_shader) {
1420fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1421fd4e5da5Sopenharmony_ci    EXPECT_THAT(
1422fd4e5da5Sopenharmony_ci        getDiagnosticString(),
1423fd4e5da5Sopenharmony_ci        MatchesRegex(
1424fd4e5da5Sopenharmony_ci            "Back-edges \\('.\\[%split\\]' -> '.\\[%split\\]'\\) can only be "
1425fd4e5da5Sopenharmony_ci            "formed between a block and a loop header.\n  %split = OpLabel\n"));
1426fd4e5da5Sopenharmony_ci  } else {
1427fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1428fd4e5da5Sopenharmony_ci  }
1429fd4e5da5Sopenharmony_ci}
1430fd4e5da5Sopenharmony_ci
1431fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, MultipleBackEdgeBlocksToLoopHeaderBad) {
1432fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1433fd4e5da5Sopenharmony_ci  Block entry("entry");
1434fd4e5da5Sopenharmony_ci  Block loop("loop", spv::Op::OpBranchConditional);
1435fd4e5da5Sopenharmony_ci  Block back0("back0");
1436fd4e5da5Sopenharmony_ci  Block back1("back1");
1437fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
1438fd4e5da5Sopenharmony_ci
1439fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1440fd4e5da5Sopenharmony_ci  if (is_shader) loop.SetBody("OpLoopMerge %merge %back0 None\n");
1441fd4e5da5Sopenharmony_ci
1442fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
1443fd4e5da5Sopenharmony_ci                    nameOps("loop", "back0", "back1") + types_consts() +
1444fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
1445fd4e5da5Sopenharmony_ci
1446fd4e5da5Sopenharmony_ci  str += entry >> loop;
1447fd4e5da5Sopenharmony_ci  str += loop >> std::vector<Block>({back0, back1});
1448fd4e5da5Sopenharmony_ci  str += back0 >> loop;
1449fd4e5da5Sopenharmony_ci  str += back1 >> loop;
1450fd4e5da5Sopenharmony_ci  str += merge;
1451fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1452fd4e5da5Sopenharmony_ci
1453fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1454fd4e5da5Sopenharmony_ci  if (is_shader) {
1455fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1456fd4e5da5Sopenharmony_ci    EXPECT_THAT(
1457fd4e5da5Sopenharmony_ci        getDiagnosticString(),
1458fd4e5da5Sopenharmony_ci        MatchesRegex(
1459fd4e5da5Sopenharmony_ci            "Loop header '.\\[%loop\\]' is targeted by 2 back-edge blocks but "
1460fd4e5da5Sopenharmony_ci            "the standard requires exactly one\n  %loop = OpLabel\n"))
1461fd4e5da5Sopenharmony_ci        << str;
1462fd4e5da5Sopenharmony_ci  } else {
1463fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1464fd4e5da5Sopenharmony_ci  }
1465fd4e5da5Sopenharmony_ci}
1466fd4e5da5Sopenharmony_ci
1467fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, ContinueTargetMustBePostDominatedByBackEdge) {
1468fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1469fd4e5da5Sopenharmony_ci  Block entry("entry");
1470fd4e5da5Sopenharmony_ci  Block loop("loop", spv::Op::OpBranchConditional);
1471fd4e5da5Sopenharmony_ci  Block cheader("cheader", spv::Op::OpBranchConditional);
1472fd4e5da5Sopenharmony_ci  Block be_block("be_block");
1473fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
1474fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
1475fd4e5da5Sopenharmony_ci
1476fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1477fd4e5da5Sopenharmony_ci  if (is_shader) loop.SetBody("OpLoopMerge %merge %cheader None\n");
1478fd4e5da5Sopenharmony_ci
1479fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
1480fd4e5da5Sopenharmony_ci                    nameOps("cheader", "be_block") + types_consts() +
1481fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
1482fd4e5da5Sopenharmony_ci
1483fd4e5da5Sopenharmony_ci  str += entry >> loop;
1484fd4e5da5Sopenharmony_ci  str += loop >> std::vector<Block>({cheader, merge});
1485fd4e5da5Sopenharmony_ci  str += cheader >> std::vector<Block>({exit, be_block});
1486fd4e5da5Sopenharmony_ci  str += exit;  //  Branches out of a continue construct
1487fd4e5da5Sopenharmony_ci  str += be_block >> loop;
1488fd4e5da5Sopenharmony_ci  str += merge;
1489fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1490fd4e5da5Sopenharmony_ci
1491fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1492fd4e5da5Sopenharmony_ci  if (is_shader) {
1493fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1494fd4e5da5Sopenharmony_ci    EXPECT_THAT(
1495fd4e5da5Sopenharmony_ci        getDiagnosticString(),
1496fd4e5da5Sopenharmony_ci        MatchesRegex(
1497fd4e5da5Sopenharmony_ci            "The continue construct with the continue target "
1498fd4e5da5Sopenharmony_ci            "'.\\[%cheader\\]' is not structurally post dominated by the "
1499fd4e5da5Sopenharmony_ci            "back-edge block '.\\[%be_block\\]'\n"
1500fd4e5da5Sopenharmony_ci            "  %be_block = OpLabel\n"));
1501fd4e5da5Sopenharmony_ci  } else {
1502fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1503fd4e5da5Sopenharmony_ci  }
1504fd4e5da5Sopenharmony_ci}
1505fd4e5da5Sopenharmony_ci
1506fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BranchOutOfConstructToMergeBad) {
1507fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1508fd4e5da5Sopenharmony_ci  Block entry("entry");
1509fd4e5da5Sopenharmony_ci  Block loop("loop", spv::Op::OpBranchConditional);
1510fd4e5da5Sopenharmony_ci  Block cont("cont", spv::Op::OpBranchConditional);
1511fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
1512fd4e5da5Sopenharmony_ci
1513fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1514fd4e5da5Sopenharmony_ci  if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
1515fd4e5da5Sopenharmony_ci
1516fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) + nameOps("cont", "loop") +
1517fd4e5da5Sopenharmony_ci                    types_consts() +
1518fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
1519fd4e5da5Sopenharmony_ci
1520fd4e5da5Sopenharmony_ci  str += entry >> loop;
1521fd4e5da5Sopenharmony_ci  str += loop >> std::vector<Block>({cont, merge});
1522fd4e5da5Sopenharmony_ci  str += cont >> std::vector<Block>({loop, merge});
1523fd4e5da5Sopenharmony_ci  str += merge;
1524fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1525fd4e5da5Sopenharmony_ci
1526fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1527fd4e5da5Sopenharmony_ci  if (is_shader) {
1528fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1529fd4e5da5Sopenharmony_ci    EXPECT_THAT(
1530fd4e5da5Sopenharmony_ci        getDiagnosticString(),
1531fd4e5da5Sopenharmony_ci        MatchesRegex("The continue construct with the continue target "
1532fd4e5da5Sopenharmony_ci                     "'.\\[%loop\\]' is not structurally post dominated by the "
1533fd4e5da5Sopenharmony_ci                     "back-edge block '.\\[%cont\\]'\n"
1534fd4e5da5Sopenharmony_ci                     "  %cont = OpLabel\n"))
1535fd4e5da5Sopenharmony_ci        << str;
1536fd4e5da5Sopenharmony_ci  } else {
1537fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1538fd4e5da5Sopenharmony_ci  }
1539fd4e5da5Sopenharmony_ci}
1540fd4e5da5Sopenharmony_ci
1541fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, BranchOutOfConstructBad) {
1542fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1543fd4e5da5Sopenharmony_ci  Block entry("entry");
1544fd4e5da5Sopenharmony_ci  Block loop("loop", spv::Op::OpBranchConditional);
1545fd4e5da5Sopenharmony_ci  Block cont("cont", spv::Op::OpBranchConditional);
1546fd4e5da5Sopenharmony_ci  Block merge("merge");
1547fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
1548fd4e5da5Sopenharmony_ci
1549fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1550fd4e5da5Sopenharmony_ci  if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
1551fd4e5da5Sopenharmony_ci
1552fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) + nameOps("cont", "loop") +
1553fd4e5da5Sopenharmony_ci                    types_consts() +
1554fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
1555fd4e5da5Sopenharmony_ci
1556fd4e5da5Sopenharmony_ci  str += entry >> loop;
1557fd4e5da5Sopenharmony_ci  str += loop >> std::vector<Block>({cont, merge});
1558fd4e5da5Sopenharmony_ci  str += cont >> std::vector<Block>({loop, exit});
1559fd4e5da5Sopenharmony_ci  str += merge >> exit;
1560fd4e5da5Sopenharmony_ci  str += exit;
1561fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1562fd4e5da5Sopenharmony_ci
1563fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1564fd4e5da5Sopenharmony_ci  if (is_shader) {
1565fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1566fd4e5da5Sopenharmony_ci    EXPECT_THAT(
1567fd4e5da5Sopenharmony_ci        getDiagnosticString(),
1568fd4e5da5Sopenharmony_ci        MatchesRegex("The continue construct with the continue target "
1569fd4e5da5Sopenharmony_ci                     "'.\\[%loop\\]' is not structurally post dominated by the "
1570fd4e5da5Sopenharmony_ci                     "back-edge block '.\\[%cont\\]'\n"
1571fd4e5da5Sopenharmony_ci                     "  %cont = OpLabel\n"));
1572fd4e5da5Sopenharmony_ci  } else {
1573fd4e5da5Sopenharmony_ci    ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1574fd4e5da5Sopenharmony_ci  }
1575fd4e5da5Sopenharmony_ci}
1576fd4e5da5Sopenharmony_ci
1577fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, OpSwitchToUnreachableBlock) {
1578fd4e5da5Sopenharmony_ci  Block entry("entry", spv::Op::OpSwitch);
1579fd4e5da5Sopenharmony_ci  Block case0("case0");
1580fd4e5da5Sopenharmony_ci  Block case1("case1");
1581fd4e5da5Sopenharmony_ci  Block case2("case2");
1582fd4e5da5Sopenharmony_ci  Block def("default", spv::Op::OpUnreachable);
1583fd4e5da5Sopenharmony_ci  Block phi("phi", spv::Op::OpReturn);
1584fd4e5da5Sopenharmony_ci
1585fd4e5da5Sopenharmony_ci  std::string str = R"(
1586fd4e5da5Sopenharmony_ciOpCapability Shader
1587fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
1588fd4e5da5Sopenharmony_ciOpEntryPoint GLCompute %main "main" %id
1589fd4e5da5Sopenharmony_ciOpExecutionMode %main LocalSize 1 1 1
1590fd4e5da5Sopenharmony_ciOpSource GLSL 430
1591fd4e5da5Sopenharmony_ciOpName %main "main"
1592fd4e5da5Sopenharmony_ciOpDecorate %id BuiltIn GlobalInvocationId
1593fd4e5da5Sopenharmony_ci%void      = OpTypeVoid
1594fd4e5da5Sopenharmony_ci%voidf     = OpTypeFunction %void
1595fd4e5da5Sopenharmony_ci%u32       = OpTypeInt 32 0
1596fd4e5da5Sopenharmony_ci%f32       = OpTypeFloat 32
1597fd4e5da5Sopenharmony_ci%uvec3     = OpTypeVector %u32 3
1598fd4e5da5Sopenharmony_ci%fvec3     = OpTypeVector %f32 3
1599fd4e5da5Sopenharmony_ci%uvec3ptr  = OpTypePointer Input %uvec3
1600fd4e5da5Sopenharmony_ci%id        = OpVariable %uvec3ptr Input
1601fd4e5da5Sopenharmony_ci%one       = OpConstant %u32 1
1602fd4e5da5Sopenharmony_ci%three     = OpConstant %u32 3
1603fd4e5da5Sopenharmony_ci%main      = OpFunction %void None %voidf
1604fd4e5da5Sopenharmony_ci)";
1605fd4e5da5Sopenharmony_ci
1606fd4e5da5Sopenharmony_ci  entry.SetBody(
1607fd4e5da5Sopenharmony_ci      "%idval    = OpLoad %uvec3 %id\n"
1608fd4e5da5Sopenharmony_ci      "%x        = OpCompositeExtract %u32 %idval 0\n"
1609fd4e5da5Sopenharmony_ci      "%selector = OpUMod %u32 %x %three\n"
1610fd4e5da5Sopenharmony_ci      "OpSelectionMerge %phi None\n");
1611fd4e5da5Sopenharmony_ci  str += entry >> std::vector<Block>({def, case0, case1, case2});
1612fd4e5da5Sopenharmony_ci  str += case1 >> phi;
1613fd4e5da5Sopenharmony_ci  str += def;
1614fd4e5da5Sopenharmony_ci  str += phi;
1615fd4e5da5Sopenharmony_ci  str += case0 >> phi;
1616fd4e5da5Sopenharmony_ci  str += case2 >> phi;
1617fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1618fd4e5da5Sopenharmony_ci
1619fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1620fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
1621fd4e5da5Sopenharmony_ci}
1622fd4e5da5Sopenharmony_ci
1623fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) {
1624fd4e5da5Sopenharmony_ci  std::string str = R"(
1625fd4e5da5Sopenharmony_ci           OpCapability Shader
1626fd4e5da5Sopenharmony_ci           OpMemoryModel Logical GLSL450
1627fd4e5da5Sopenharmony_ci           OpEntryPoint Fragment %main "main"
1628fd4e5da5Sopenharmony_ci           OpExecutionMode %main OriginUpperLeft
1629fd4e5da5Sopenharmony_ci           OpName %loop "loop"
1630fd4e5da5Sopenharmony_ci%voidt   = OpTypeVoid
1631fd4e5da5Sopenharmony_ci%funct   = OpTypeFunction %voidt
1632fd4e5da5Sopenharmony_ci%main    = OpFunction %voidt None %funct
1633fd4e5da5Sopenharmony_ci%loop    = OpLabel
1634fd4e5da5Sopenharmony_ci           OpLoopMerge %exit %loop None
1635fd4e5da5Sopenharmony_ci           OpBranch %exit
1636fd4e5da5Sopenharmony_ci%exit    = OpLabel
1637fd4e5da5Sopenharmony_ci           OpReturn
1638fd4e5da5Sopenharmony_ci           OpFunctionEnd
1639fd4e5da5Sopenharmony_ci)";
1640fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1641fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1642fd4e5da5Sopenharmony_ci  EXPECT_THAT(
1643fd4e5da5Sopenharmony_ci      getDiagnosticString(),
1644fd4e5da5Sopenharmony_ci      MatchesRegex("Loop header '.\\[%loop\\]' is targeted by "
1645fd4e5da5Sopenharmony_ci                   "0 back-edge blocks but the standard requires exactly "
1646fd4e5da5Sopenharmony_ci                   "one\n  %loop = OpLabel\n"));
1647fd4e5da5Sopenharmony_ci}
1648fd4e5da5Sopenharmony_ci
1649fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopWithBackEdgeFromUnreachableContinueConstructGood) {
1650fd4e5da5Sopenharmony_ci  std::string str = R"(
1651fd4e5da5Sopenharmony_ci           OpCapability Shader
1652fd4e5da5Sopenharmony_ci           OpMemoryModel Logical GLSL450
1653fd4e5da5Sopenharmony_ci           OpEntryPoint Fragment %main "main"
1654fd4e5da5Sopenharmony_ci           OpExecutionMode %main OriginUpperLeft
1655fd4e5da5Sopenharmony_ci           OpName %loop "loop"
1656fd4e5da5Sopenharmony_ci%voidt   = OpTypeVoid
1657fd4e5da5Sopenharmony_ci%funct   = OpTypeFunction %voidt
1658fd4e5da5Sopenharmony_ci%floatt  = OpTypeFloat 32
1659fd4e5da5Sopenharmony_ci%boolt   = OpTypeBool
1660fd4e5da5Sopenharmony_ci%one     = OpConstant %floatt 1
1661fd4e5da5Sopenharmony_ci%two     = OpConstant %floatt 2
1662fd4e5da5Sopenharmony_ci%main    = OpFunction %voidt None %funct
1663fd4e5da5Sopenharmony_ci%entry   = OpLabel
1664fd4e5da5Sopenharmony_ci           OpBranch %loop
1665fd4e5da5Sopenharmony_ci%loop    = OpLabel
1666fd4e5da5Sopenharmony_ci           OpLoopMerge %exit %cont None
1667fd4e5da5Sopenharmony_ci           OpBranch %16
1668fd4e5da5Sopenharmony_ci%16      = OpLabel
1669fd4e5da5Sopenharmony_ci%cond    = OpFOrdLessThan %boolt %one %two
1670fd4e5da5Sopenharmony_ci           OpBranchConditional %cond %body %exit
1671fd4e5da5Sopenharmony_ci%body    = OpLabel
1672fd4e5da5Sopenharmony_ci           OpReturn
1673fd4e5da5Sopenharmony_ci%cont    = OpLabel   ; Reachable only from OpLoopMerge ContinueTarget parameter
1674fd4e5da5Sopenharmony_ci           OpBranch %loop ; Should be considered a back-edge
1675fd4e5da5Sopenharmony_ci%exit    = OpLabel
1676fd4e5da5Sopenharmony_ci           OpReturn
1677fd4e5da5Sopenharmony_ci           OpFunctionEnd
1678fd4e5da5Sopenharmony_ci)";
1679fd4e5da5Sopenharmony_ci
1680fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1681fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
1682fd4e5da5Sopenharmony_ci}
1683fd4e5da5Sopenharmony_ci
1684fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG,
1685fd4e5da5Sopenharmony_ci       NestedConstructWithUnreachableMergeBlockBranchingToOuterMergeBlock) {
1686fd4e5da5Sopenharmony_ci  // Test for https://github.com/KhronosGroup/SPIRV-Tools/issues/297
1687fd4e5da5Sopenharmony_ci  // The nested construct has an unreachable merge block.  In the
1688fd4e5da5Sopenharmony_ci  // augmented CFG that merge block
1689fd4e5da5Sopenharmony_ci  // we still determine that the
1690fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1691fd4e5da5Sopenharmony_ci  Block entry("entry", spv::Op::OpBranchConditional);
1692fd4e5da5Sopenharmony_ci  Block inner_head("inner_head", spv::Op::OpBranchConditional);
1693fd4e5da5Sopenharmony_ci  Block inner_true("inner_true", spv::Op::OpReturn);
1694fd4e5da5Sopenharmony_ci  Block inner_false("inner_false", spv::Op::OpReturn);
1695fd4e5da5Sopenharmony_ci  Block inner_merge("inner_merge");
1696fd4e5da5Sopenharmony_ci  Block exit("exit", spv::Op::OpReturn);
1697fd4e5da5Sopenharmony_ci
1698fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1699fd4e5da5Sopenharmony_ci  if (is_shader) {
1700fd4e5da5Sopenharmony_ci    entry.AppendBody("OpSelectionMerge %exit None\n");
1701fd4e5da5Sopenharmony_ci    inner_head.SetBody("OpSelectionMerge %inner_merge None\n");
1702fd4e5da5Sopenharmony_ci  }
1703fd4e5da5Sopenharmony_ci
1704fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
1705fd4e5da5Sopenharmony_ci                    nameOps("entry", "inner_merge", "exit") + types_consts() +
1706fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
1707fd4e5da5Sopenharmony_ci
1708fd4e5da5Sopenharmony_ci  str += entry >> std::vector<Block>({inner_head, exit});
1709fd4e5da5Sopenharmony_ci  str += inner_head >> std::vector<Block>({inner_true, inner_false});
1710fd4e5da5Sopenharmony_ci  str += inner_true;
1711fd4e5da5Sopenharmony_ci  str += inner_false;
1712fd4e5da5Sopenharmony_ci  str += inner_merge >> exit;
1713fd4e5da5Sopenharmony_ci  str += exit;
1714fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1715fd4e5da5Sopenharmony_ci
1716fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1717fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
1718fd4e5da5Sopenharmony_ci}
1719fd4e5da5Sopenharmony_ci
1720fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) {
1721fd4e5da5Sopenharmony_ci  // The continue construct cannot be the merge target of a nested selection
1722fd4e5da5Sopenharmony_ci  // because the loop construct must contain "if_merge" because it contains
1723fd4e5da5Sopenharmony_ci  // "if_head".
1724fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1725fd4e5da5Sopenharmony_ci  Block entry("entry");
1726fd4e5da5Sopenharmony_ci  Block loop("loop");
1727fd4e5da5Sopenharmony_ci  Block if_head("if_head", spv::Op::OpBranchConditional);
1728fd4e5da5Sopenharmony_ci  Block if_true("if_true");
1729fd4e5da5Sopenharmony_ci  Block if_merge("if_merge", spv::Op::OpBranchConditional);
1730fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
1731fd4e5da5Sopenharmony_ci
1732fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1733fd4e5da5Sopenharmony_ci  if (is_shader) {
1734fd4e5da5Sopenharmony_ci    loop.SetBody("OpLoopMerge %merge %if_merge None\n");
1735fd4e5da5Sopenharmony_ci    if_head.SetBody("OpSelectionMerge %if_merge None\n");
1736fd4e5da5Sopenharmony_ci  }
1737fd4e5da5Sopenharmony_ci
1738fd4e5da5Sopenharmony_ci  std::string str =
1739fd4e5da5Sopenharmony_ci      GetDefaultHeader(GetParam()) +
1740fd4e5da5Sopenharmony_ci      nameOps("entry", "loop", "if_head", "if_true", "if_merge", "merge") +
1741fd4e5da5Sopenharmony_ci      types_consts() + "%func    = OpFunction %voidt None %funct\n";
1742fd4e5da5Sopenharmony_ci
1743fd4e5da5Sopenharmony_ci  str += entry >> loop;
1744fd4e5da5Sopenharmony_ci  str += loop >> if_head;
1745fd4e5da5Sopenharmony_ci  str += if_head >> std::vector<Block>({if_true, if_merge});
1746fd4e5da5Sopenharmony_ci  str += if_true >> if_merge;
1747fd4e5da5Sopenharmony_ci  str += if_merge >> std::vector<Block>({loop, merge});
1748fd4e5da5Sopenharmony_ci  str += merge;
1749fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1750fd4e5da5Sopenharmony_ci
1751fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1752fd4e5da5Sopenharmony_ci  if (is_shader) {
1753fd4e5da5Sopenharmony_ci    EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1754fd4e5da5Sopenharmony_ci    EXPECT_THAT(
1755fd4e5da5Sopenharmony_ci        getDiagnosticString(),
1756fd4e5da5Sopenharmony_ci        HasSubstr(
1757fd4e5da5Sopenharmony_ci            "Header block '3[%if_head]' is contained in the loop construct "
1758fd4e5da5Sopenharmony_ci            "headed "
1759fd4e5da5Sopenharmony_ci            "by '2[%loop]', but its merge block '5[%if_merge]' is not"));
1760fd4e5da5Sopenharmony_ci  } else {
1761fd4e5da5Sopenharmony_ci    EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
1762fd4e5da5Sopenharmony_ci  }
1763fd4e5da5Sopenharmony_ci}
1764fd4e5da5Sopenharmony_ci
1765fd4e5da5Sopenharmony_ciTEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) {
1766fd4e5da5Sopenharmony_ci  // This test case ensures we allow both branches of a loop latch block
1767fd4e5da5Sopenharmony_ci  // to go back to the loop header.  It still counts as a single back edge.
1768fd4e5da5Sopenharmony_ci  bool is_shader = GetParam() == spv::Capability::Shader;
1769fd4e5da5Sopenharmony_ci  Block entry("entry");
1770fd4e5da5Sopenharmony_ci  Block loop("loop", spv::Op::OpBranchConditional);
1771fd4e5da5Sopenharmony_ci  Block latch("latch", spv::Op::OpBranchConditional);
1772fd4e5da5Sopenharmony_ci  Block merge("merge", spv::Op::OpReturn);
1773fd4e5da5Sopenharmony_ci
1774fd4e5da5Sopenharmony_ci  entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
1775fd4e5da5Sopenharmony_ci  if (is_shader) {
1776fd4e5da5Sopenharmony_ci    loop.SetBody("OpLoopMerge %merge %latch None\n");
1777fd4e5da5Sopenharmony_ci  }
1778fd4e5da5Sopenharmony_ci
1779fd4e5da5Sopenharmony_ci  std::string str = GetDefaultHeader(GetParam()) +
1780fd4e5da5Sopenharmony_ci                    nameOps("entry", "loop", "latch", "merge") +
1781fd4e5da5Sopenharmony_ci                    types_consts() +
1782fd4e5da5Sopenharmony_ci                    "%func    = OpFunction %voidt None %funct\n";
1783fd4e5da5Sopenharmony_ci
1784fd4e5da5Sopenharmony_ci  str += entry >> loop;
1785fd4e5da5Sopenharmony_ci  str += loop >> std::vector<Block>({latch, merge});
1786fd4e5da5Sopenharmony_ci  str += latch >> std::vector<Block>({loop, loop});  // This is the key
1787fd4e5da5Sopenharmony_ci  str += merge;
1788fd4e5da5Sopenharmony_ci  str += "OpFunctionEnd";
1789fd4e5da5Sopenharmony_ci
1790fd4e5da5Sopenharmony_ci  CompileSuccessfully(str);
1791fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions())
1792fd4e5da5Sopenharmony_ci      << str << getDiagnosticString();
1793fd4e5da5Sopenharmony_ci}
1794fd4e5da5Sopenharmony_ci
1795fd4e5da5Sopenharmony_ci// Unit test to check the case where a basic block is the entry block of 2
1796fd4e5da5Sopenharmony_ci// different constructs. In this case, the basic block is the entry block of a
1797fd4e5da5Sopenharmony_ci// continue construct as well as a selection construct. See issue# 517 for more
1798fd4e5da5Sopenharmony_ci// details.
1799fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BasicBlockIsEntryBlockOfTwoConstructsGood) {
1800fd4e5da5Sopenharmony_ci  std::string spirv = R"(
1801fd4e5da5Sopenharmony_ci               OpCapability Shader
1802fd4e5da5Sopenharmony_ci               OpCapability Linkage
1803fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
1804fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
1805fd4e5da5Sopenharmony_ci       %bool = OpTypeBool
1806fd4e5da5Sopenharmony_ci        %int = OpTypeInt 32 1
1807fd4e5da5Sopenharmony_ci  %void_func = OpTypeFunction %void
1808fd4e5da5Sopenharmony_ci      %int_0 = OpConstant %int 0
1809fd4e5da5Sopenharmony_ci    %testfun = OpFunction %void None %void_func
1810fd4e5da5Sopenharmony_ci    %label_1 = OpLabel
1811fd4e5da5Sopenharmony_ci               OpBranch %start
1812fd4e5da5Sopenharmony_ci      %start = OpLabel
1813fd4e5da5Sopenharmony_ci       %cond = OpSLessThan %bool %int_0 %int_0
1814fd4e5da5Sopenharmony_ci       ;
1815fd4e5da5Sopenharmony_ci       ; Note: In this case, the "target" block is both the entry block of
1816fd4e5da5Sopenharmony_ci       ;       the continue construct of the loop as well as the entry block of
1817fd4e5da5Sopenharmony_ci       ;       the selection construct.
1818fd4e5da5Sopenharmony_ci       ;
1819fd4e5da5Sopenharmony_ci               OpLoopMerge %loop_merge %target None
1820fd4e5da5Sopenharmony_ci               OpBranchConditional %cond %target %loop_merge
1821fd4e5da5Sopenharmony_ci %loop_merge = OpLabel
1822fd4e5da5Sopenharmony_ci               OpReturn
1823fd4e5da5Sopenharmony_ci     %target = OpLabel
1824fd4e5da5Sopenharmony_ci               OpSelectionMerge %selection_merge None
1825fd4e5da5Sopenharmony_ci               OpBranchConditional %cond %do_stuff %do_other_stuff
1826fd4e5da5Sopenharmony_ci     %do_other_stuff = OpLabel
1827fd4e5da5Sopenharmony_ci               OpBranch %selection_merge
1828fd4e5da5Sopenharmony_ci     %selection_merge = OpLabel
1829fd4e5da5Sopenharmony_ci               OpBranch %start
1830fd4e5da5Sopenharmony_ci         %do_stuff = OpLabel
1831fd4e5da5Sopenharmony_ci               OpBranch %selection_merge
1832fd4e5da5Sopenharmony_ci               OpFunctionEnd
1833fd4e5da5Sopenharmony_ci  )";
1834fd4e5da5Sopenharmony_ci  CompileSuccessfully(spirv);
1835fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
1836fd4e5da5Sopenharmony_ci}
1837fd4e5da5Sopenharmony_ci
1838fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, OpReturnInNonVoidFunc) {
1839fd4e5da5Sopenharmony_ci  std::string spirv = R"(
1840fd4e5da5Sopenharmony_ci               OpCapability Shader
1841fd4e5da5Sopenharmony_ci               OpCapability Linkage
1842fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
1843fd4e5da5Sopenharmony_ci        %int = OpTypeInt 32 1
1844fd4e5da5Sopenharmony_ci   %int_func = OpTypeFunction %int
1845fd4e5da5Sopenharmony_ci    %testfun = OpFunction %int None %int_func
1846fd4e5da5Sopenharmony_ci    %label_1 = OpLabel
1847fd4e5da5Sopenharmony_ci               OpReturn
1848fd4e5da5Sopenharmony_ci               OpFunctionEnd
1849fd4e5da5Sopenharmony_ci  )";
1850fd4e5da5Sopenharmony_ci  CompileSuccessfully(spirv);
1851fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1852fd4e5da5Sopenharmony_ci  EXPECT_THAT(
1853fd4e5da5Sopenharmony_ci      getDiagnosticString(),
1854fd4e5da5Sopenharmony_ci      HasSubstr(
1855fd4e5da5Sopenharmony_ci          "OpReturn can only be called from a function with void return type.\n"
1856fd4e5da5Sopenharmony_ci          "  OpReturn"));
1857fd4e5da5Sopenharmony_ci}
1858fd4e5da5Sopenharmony_ci
1859fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, StructuredCFGBranchIntoSelectionBody) {
1860fd4e5da5Sopenharmony_ci  std::string spirv = R"(
1861fd4e5da5Sopenharmony_ciOpCapability Shader
1862fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
1863fd4e5da5Sopenharmony_ciOpEntryPoint Fragment %func "func"
1864fd4e5da5Sopenharmony_ciOpExecutionMode %func OriginUpperLeft
1865fd4e5da5Sopenharmony_ci%void = OpTypeVoid
1866fd4e5da5Sopenharmony_ci%bool = OpTypeBool
1867fd4e5da5Sopenharmony_ci%true = OpConstantTrue %bool
1868fd4e5da5Sopenharmony_ci%functy = OpTypeFunction %void
1869fd4e5da5Sopenharmony_ci%func = OpFunction %void None %functy
1870fd4e5da5Sopenharmony_ci%entry = OpLabel
1871fd4e5da5Sopenharmony_ciOpSelectionMerge %merge None
1872fd4e5da5Sopenharmony_ciOpBranchConditional %true %then %merge
1873fd4e5da5Sopenharmony_ci%merge = OpLabel
1874fd4e5da5Sopenharmony_ciOpBranch %then
1875fd4e5da5Sopenharmony_ci%then = OpLabel
1876fd4e5da5Sopenharmony_ciOpReturn
1877fd4e5da5Sopenharmony_ciOpFunctionEnd
1878fd4e5da5Sopenharmony_ci)";
1879fd4e5da5Sopenharmony_ci
1880fd4e5da5Sopenharmony_ci  CompileSuccessfully(spirv);
1881fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1882fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
1883fd4e5da5Sopenharmony_ci              HasSubstr("branches to the selection construct, but not to the "
1884fd4e5da5Sopenharmony_ci                        "selection header <ID> 6\n  %7 = OpLabel"));
1885fd4e5da5Sopenharmony_ci}
1886fd4e5da5Sopenharmony_ci
1887fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, SwitchDefaultOnly) {
1888fd4e5da5Sopenharmony_ci  std::string text = R"(
1889fd4e5da5Sopenharmony_ciOpCapability Shader
1890fd4e5da5Sopenharmony_ciOpCapability Linkage
1891fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
1892fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
1893fd4e5da5Sopenharmony_ci%2 = OpTypeInt 32 0
1894fd4e5da5Sopenharmony_ci%3 = OpConstant %2 0
1895fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
1896fd4e5da5Sopenharmony_ci%5 = OpFunction %1 None %4
1897fd4e5da5Sopenharmony_ci%6 = OpLabel
1898fd4e5da5Sopenharmony_ciOpSelectionMerge %7 None
1899fd4e5da5Sopenharmony_ciOpSwitch %3 %7
1900fd4e5da5Sopenharmony_ci%7 = OpLabel
1901fd4e5da5Sopenharmony_ciOpReturn
1902fd4e5da5Sopenharmony_ciOpFunctionEnd
1903fd4e5da5Sopenharmony_ci)";
1904fd4e5da5Sopenharmony_ci
1905fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
1906fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1907fd4e5da5Sopenharmony_ci}
1908fd4e5da5Sopenharmony_ci
1909fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, SwitchSingleCase) {
1910fd4e5da5Sopenharmony_ci  std::string text = R"(
1911fd4e5da5Sopenharmony_ciOpCapability Shader
1912fd4e5da5Sopenharmony_ciOpCapability Linkage
1913fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
1914fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
1915fd4e5da5Sopenharmony_ci%2 = OpTypeInt 32 0
1916fd4e5da5Sopenharmony_ci%3 = OpConstant %2 0
1917fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
1918fd4e5da5Sopenharmony_ci%5 = OpFunction %1 None %4
1919fd4e5da5Sopenharmony_ci%6 = OpLabel
1920fd4e5da5Sopenharmony_ciOpSelectionMerge %7 None
1921fd4e5da5Sopenharmony_ciOpSwitch %3 %7 0 %8
1922fd4e5da5Sopenharmony_ci%8 = OpLabel
1923fd4e5da5Sopenharmony_ciOpBranch %7
1924fd4e5da5Sopenharmony_ci%7 = OpLabel
1925fd4e5da5Sopenharmony_ciOpReturn
1926fd4e5da5Sopenharmony_ciOpFunctionEnd
1927fd4e5da5Sopenharmony_ci)";
1928fd4e5da5Sopenharmony_ci
1929fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
1930fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1931fd4e5da5Sopenharmony_ci}
1932fd4e5da5Sopenharmony_ci
1933fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MultipleFallThroughBlocks) {
1934fd4e5da5Sopenharmony_ci  std::string text = R"(
1935fd4e5da5Sopenharmony_ciOpCapability Shader
1936fd4e5da5Sopenharmony_ciOpCapability Linkage
1937fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
1938fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
1939fd4e5da5Sopenharmony_ci%2 = OpTypeInt 32 0
1940fd4e5da5Sopenharmony_ci%3 = OpConstant %2 0
1941fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
1942fd4e5da5Sopenharmony_ci%5 = OpTypeBool
1943fd4e5da5Sopenharmony_ci%6 = OpConstantTrue %5
1944fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %4
1945fd4e5da5Sopenharmony_ci%8 = OpLabel
1946fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
1947fd4e5da5Sopenharmony_ciOpSwitch %3 %10 0 %11 1 %12
1948fd4e5da5Sopenharmony_ci%10 = OpLabel
1949fd4e5da5Sopenharmony_ciOpBranchConditional %6 %11 %12
1950fd4e5da5Sopenharmony_ci%11 = OpLabel
1951fd4e5da5Sopenharmony_ciOpBranch %9
1952fd4e5da5Sopenharmony_ci%12 = OpLabel
1953fd4e5da5Sopenharmony_ciOpBranch %9
1954fd4e5da5Sopenharmony_ci%9 = OpLabel
1955fd4e5da5Sopenharmony_ciOpReturn
1956fd4e5da5Sopenharmony_ciOpFunctionEnd
1957fd4e5da5Sopenharmony_ci)";
1958fd4e5da5Sopenharmony_ci
1959fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
1960fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1961fd4e5da5Sopenharmony_ci  EXPECT_THAT(
1962fd4e5da5Sopenharmony_ci      getDiagnosticString(),
1963fd4e5da5Sopenharmony_ci      HasSubstr(
1964fd4e5da5Sopenharmony_ci          "Case construct that targets '10[%10]' has branches to multiple "
1965fd4e5da5Sopenharmony_ci          "other "
1966fd4e5da5Sopenharmony_ci          "case construct targets '12[%12]' and '11[%11]'\n  %10 = OpLabel"));
1967fd4e5da5Sopenharmony_ci}
1968fd4e5da5Sopenharmony_ci
1969fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MultipleFallThroughToDefault) {
1970fd4e5da5Sopenharmony_ci  std::string text = R"(
1971fd4e5da5Sopenharmony_ciOpCapability Shader
1972fd4e5da5Sopenharmony_ciOpCapability Linkage
1973fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
1974fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
1975fd4e5da5Sopenharmony_ci%2 = OpTypeInt 32 0
1976fd4e5da5Sopenharmony_ci%3 = OpConstant %2 0
1977fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
1978fd4e5da5Sopenharmony_ci%5 = OpTypeBool
1979fd4e5da5Sopenharmony_ci%6 = OpConstantTrue %5
1980fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %4
1981fd4e5da5Sopenharmony_ci%8 = OpLabel
1982fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
1983fd4e5da5Sopenharmony_ciOpSwitch %3 %10 0 %11 1 %12
1984fd4e5da5Sopenharmony_ci%10 = OpLabel
1985fd4e5da5Sopenharmony_ciOpBranch %9
1986fd4e5da5Sopenharmony_ci%11 = OpLabel
1987fd4e5da5Sopenharmony_ciOpBranch %10
1988fd4e5da5Sopenharmony_ci%12 = OpLabel
1989fd4e5da5Sopenharmony_ciOpBranch %10
1990fd4e5da5Sopenharmony_ci%9 = OpLabel
1991fd4e5da5Sopenharmony_ciOpReturn
1992fd4e5da5Sopenharmony_ciOpFunctionEnd
1993fd4e5da5Sopenharmony_ci)";
1994fd4e5da5Sopenharmony_ci
1995fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
1996fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1997fd4e5da5Sopenharmony_ci  EXPECT_THAT(
1998fd4e5da5Sopenharmony_ci      getDiagnosticString(),
1999fd4e5da5Sopenharmony_ci      HasSubstr("Multiple case constructs have branches to the case construct "
2000fd4e5da5Sopenharmony_ci                "that targets '10[%10]'\n  %10 = OpLabel"));
2001fd4e5da5Sopenharmony_ci}
2002fd4e5da5Sopenharmony_ci
2003fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MultipleFallThroughToNonDefault) {
2004fd4e5da5Sopenharmony_ci  std::string text = R"(
2005fd4e5da5Sopenharmony_ciOpCapability Shader
2006fd4e5da5Sopenharmony_ciOpCapability Linkage
2007fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2008fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
2009fd4e5da5Sopenharmony_ci%2 = OpTypeInt 32 0
2010fd4e5da5Sopenharmony_ci%3 = OpConstant %2 0
2011fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
2012fd4e5da5Sopenharmony_ci%5 = OpTypeBool
2013fd4e5da5Sopenharmony_ci%6 = OpConstantTrue %5
2014fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %4
2015fd4e5da5Sopenharmony_ci%8 = OpLabel
2016fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
2017fd4e5da5Sopenharmony_ciOpSwitch %3 %10 0 %11 1 %12
2018fd4e5da5Sopenharmony_ci%10 = OpLabel
2019fd4e5da5Sopenharmony_ciOpBranch %12
2020fd4e5da5Sopenharmony_ci%11 = OpLabel
2021fd4e5da5Sopenharmony_ciOpBranch %12
2022fd4e5da5Sopenharmony_ci%12 = OpLabel
2023fd4e5da5Sopenharmony_ciOpBranch %9
2024fd4e5da5Sopenharmony_ci%9 = OpLabel
2025fd4e5da5Sopenharmony_ciOpReturn
2026fd4e5da5Sopenharmony_ciOpFunctionEnd
2027fd4e5da5Sopenharmony_ci)";
2028fd4e5da5Sopenharmony_ci
2029fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2030fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2031fd4e5da5Sopenharmony_ci  EXPECT_THAT(
2032fd4e5da5Sopenharmony_ci      getDiagnosticString(),
2033fd4e5da5Sopenharmony_ci      HasSubstr("Multiple case constructs have branches to the case construct "
2034fd4e5da5Sopenharmony_ci                "that targets '12[%12]'\n  %12 = OpLabel"));
2035fd4e5da5Sopenharmony_ci}
2036fd4e5da5Sopenharmony_ci
2037fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, DuplicateTargetWithFallThrough) {
2038fd4e5da5Sopenharmony_ci  std::string text = R"(
2039fd4e5da5Sopenharmony_ciOpCapability Shader
2040fd4e5da5Sopenharmony_ciOpCapability Linkage
2041fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2042fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
2043fd4e5da5Sopenharmony_ci%2 = OpTypeInt 32 0
2044fd4e5da5Sopenharmony_ci%3 = OpConstant %2 0
2045fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
2046fd4e5da5Sopenharmony_ci%5 = OpTypeBool
2047fd4e5da5Sopenharmony_ci%6 = OpConstantTrue %5
2048fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %4
2049fd4e5da5Sopenharmony_ci%8 = OpLabel
2050fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
2051fd4e5da5Sopenharmony_ciOpSwitch %3 %10 0 %10 1 %11
2052fd4e5da5Sopenharmony_ci%10 = OpLabel
2053fd4e5da5Sopenharmony_ciOpBranch %11
2054fd4e5da5Sopenharmony_ci%11 = OpLabel
2055fd4e5da5Sopenharmony_ciOpBranch %9
2056fd4e5da5Sopenharmony_ci%9 = OpLabel
2057fd4e5da5Sopenharmony_ciOpReturn
2058fd4e5da5Sopenharmony_ciOpFunctionEnd
2059fd4e5da5Sopenharmony_ci)";
2060fd4e5da5Sopenharmony_ci
2061fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2062fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
2063fd4e5da5Sopenharmony_ci}
2064fd4e5da5Sopenharmony_ci
2065fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, OpSwitchTargetCannotBeOuterLoopMergeBlock) {
2066fd4e5da5Sopenharmony_ci  std::string text = R"(
2067fd4e5da5Sopenharmony_ciOpCapability Shader
2068fd4e5da5Sopenharmony_ciOpCapability Linkage
2069fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2070fd4e5da5Sopenharmony_ci
2071fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
2072fd4e5da5Sopenharmony_ci%2 = OpTypeFunction %1
2073fd4e5da5Sopenharmony_ci%3 = OpTypeBool
2074fd4e5da5Sopenharmony_ci%4 = OpUndef %3
2075fd4e5da5Sopenharmony_ci%5 = OpTypeInt 32 0
2076fd4e5da5Sopenharmony_ci%6 = OpConstant %5 0
2077fd4e5da5Sopenharmony_ci
2078fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %2
2079fd4e5da5Sopenharmony_ci
2080fd4e5da5Sopenharmony_ci%8 = OpLabel
2081fd4e5da5Sopenharmony_ciOpBranch %9
2082fd4e5da5Sopenharmony_ci
2083fd4e5da5Sopenharmony_ci%9 = OpLabel
2084fd4e5da5Sopenharmony_ciOpLoopMerge %10 %11 None
2085fd4e5da5Sopenharmony_ciOpBranch %12
2086fd4e5da5Sopenharmony_ci
2087fd4e5da5Sopenharmony_ci%12 = OpLabel
2088fd4e5da5Sopenharmony_ciOpSelectionMerge %13 None
2089fd4e5da5Sopenharmony_ciOpSwitch %6 %13 0 %10 1 %14
2090fd4e5da5Sopenharmony_ci
2091fd4e5da5Sopenharmony_ci%14 = OpLabel
2092fd4e5da5Sopenharmony_ciOpBranch %13
2093fd4e5da5Sopenharmony_ci
2094fd4e5da5Sopenharmony_ci%13 = OpLabel
2095fd4e5da5Sopenharmony_ciOpBranch %11
2096fd4e5da5Sopenharmony_ci
2097fd4e5da5Sopenharmony_ci%11 = OpLabel
2098fd4e5da5Sopenharmony_ciOpBranch %9
2099fd4e5da5Sopenharmony_ci
2100fd4e5da5Sopenharmony_ci%10 = OpLabel
2101fd4e5da5Sopenharmony_ciOpReturn
2102fd4e5da5Sopenharmony_ci
2103fd4e5da5Sopenharmony_ciOpFunctionEnd
2104fd4e5da5Sopenharmony_ci)";
2105fd4e5da5Sopenharmony_ci
2106fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2107fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2108fd4e5da5Sopenharmony_ci  EXPECT_THAT(
2109fd4e5da5Sopenharmony_ci      getDiagnosticString(),
2110fd4e5da5Sopenharmony_ci      HasSubstr(
2111fd4e5da5Sopenharmony_ci          "Switch header '12[%12]' does not structurally dominate its case construct '10[%10]'\n"
2112fd4e5da5Sopenharmony_ci          "  %12 = OpLabel"));
2113fd4e5da5Sopenharmony_ci}
2114fd4e5da5Sopenharmony_ci
2115fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, OpSwitchTargetCannotBeOuterLoopContinueBlock) {
2116fd4e5da5Sopenharmony_ci  std::string text = R"(
2117fd4e5da5Sopenharmony_ciOpCapability Shader
2118fd4e5da5Sopenharmony_ciOpCapability Linkage
2119fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2120fd4e5da5Sopenharmony_ci
2121fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
2122fd4e5da5Sopenharmony_ci%2 = OpTypeFunction %1
2123fd4e5da5Sopenharmony_ci%3 = OpTypeBool
2124fd4e5da5Sopenharmony_ci%4 = OpUndef %3
2125fd4e5da5Sopenharmony_ci%5 = OpTypeInt 32 0
2126fd4e5da5Sopenharmony_ci%6 = OpConstant %5 0
2127fd4e5da5Sopenharmony_ci
2128fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %2
2129fd4e5da5Sopenharmony_ci
2130fd4e5da5Sopenharmony_ci%8 = OpLabel
2131fd4e5da5Sopenharmony_ciOpBranch %9
2132fd4e5da5Sopenharmony_ci
2133fd4e5da5Sopenharmony_ci%9 = OpLabel
2134fd4e5da5Sopenharmony_ciOpLoopMerge %10 %11 None
2135fd4e5da5Sopenharmony_ciOpBranch %12
2136fd4e5da5Sopenharmony_ci
2137fd4e5da5Sopenharmony_ci%12 = OpLabel
2138fd4e5da5Sopenharmony_ciOpSelectionMerge %13 None
2139fd4e5da5Sopenharmony_ciOpSwitch %6 %13 0 %11 1 %14
2140fd4e5da5Sopenharmony_ci
2141fd4e5da5Sopenharmony_ci%14 = OpLabel
2142fd4e5da5Sopenharmony_ciOpBranch %13
2143fd4e5da5Sopenharmony_ci
2144fd4e5da5Sopenharmony_ci%13 = OpLabel
2145fd4e5da5Sopenharmony_ciOpBranch %11
2146fd4e5da5Sopenharmony_ci
2147fd4e5da5Sopenharmony_ci%11 = OpLabel
2148fd4e5da5Sopenharmony_ciOpBranch %9
2149fd4e5da5Sopenharmony_ci
2150fd4e5da5Sopenharmony_ci%10 = OpLabel
2151fd4e5da5Sopenharmony_ciOpReturn
2152fd4e5da5Sopenharmony_ci
2153fd4e5da5Sopenharmony_ciOpFunctionEnd
2154fd4e5da5Sopenharmony_ci)";
2155fd4e5da5Sopenharmony_ci
2156fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2157fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2158fd4e5da5Sopenharmony_ci  EXPECT_THAT(
2159fd4e5da5Sopenharmony_ci      getDiagnosticString(),
2160fd4e5da5Sopenharmony_ci      HasSubstr(
2161fd4e5da5Sopenharmony_ci          "Switch header '12[%12]' does not structurally dominate its case construct '11[%11]'\n"
2162fd4e5da5Sopenharmony_ci          "  %12 = OpLabel"));
2163fd4e5da5Sopenharmony_ci}
2164fd4e5da5Sopenharmony_ci
2165fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, WrongOperandList) {
2166fd4e5da5Sopenharmony_ci  std::string text = R"(
2167fd4e5da5Sopenharmony_ciOpCapability Shader
2168fd4e5da5Sopenharmony_ciOpCapability Linkage
2169fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2170fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
2171fd4e5da5Sopenharmony_ci%2 = OpTypeInt 32 0
2172fd4e5da5Sopenharmony_ci%3 = OpConstant %2 0
2173fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
2174fd4e5da5Sopenharmony_ci%5 = OpTypeBool
2175fd4e5da5Sopenharmony_ci%6 = OpConstantTrue %5
2176fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %4
2177fd4e5da5Sopenharmony_ci%8 = OpLabel
2178fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
2179fd4e5da5Sopenharmony_ciOpSwitch %3 %10 0 %11 1 %12
2180fd4e5da5Sopenharmony_ci%10 = OpLabel
2181fd4e5da5Sopenharmony_ciOpBranch %9
2182fd4e5da5Sopenharmony_ci%12 = OpLabel
2183fd4e5da5Sopenharmony_ciOpBranch %11
2184fd4e5da5Sopenharmony_ci%11 = OpLabel
2185fd4e5da5Sopenharmony_ciOpBranch %9
2186fd4e5da5Sopenharmony_ci%9 = OpLabel
2187fd4e5da5Sopenharmony_ciOpReturn
2188fd4e5da5Sopenharmony_ciOpFunctionEnd
2189fd4e5da5Sopenharmony_ci)";
2190fd4e5da5Sopenharmony_ci
2191fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2192fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2193fd4e5da5Sopenharmony_ci  EXPECT_THAT(
2194fd4e5da5Sopenharmony_ci      getDiagnosticString(),
2195fd4e5da5Sopenharmony_ci      HasSubstr(
2196fd4e5da5Sopenharmony_ci          "Case construct that targets '12[%12]' has branches to the case "
2197fd4e5da5Sopenharmony_ci          "construct that targets '11[%11]', but does not immediately "
2198fd4e5da5Sopenharmony_ci          "precede it in the OpSwitch's target list\n"
2199fd4e5da5Sopenharmony_ci          "  OpSwitch %uint_0 %10 0 %11 1 %12"));
2200fd4e5da5Sopenharmony_ci}
2201fd4e5da5Sopenharmony_ci
2202fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, WrongOperandListThroughDefault) {
2203fd4e5da5Sopenharmony_ci  std::string text = R"(
2204fd4e5da5Sopenharmony_ciOpCapability Shader
2205fd4e5da5Sopenharmony_ciOpCapability Linkage
2206fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2207fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
2208fd4e5da5Sopenharmony_ci%2 = OpTypeInt 32 0
2209fd4e5da5Sopenharmony_ci%3 = OpConstant %2 0
2210fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
2211fd4e5da5Sopenharmony_ci%5 = OpTypeBool
2212fd4e5da5Sopenharmony_ci%6 = OpConstantTrue %5
2213fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %4
2214fd4e5da5Sopenharmony_ci%8 = OpLabel
2215fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
2216fd4e5da5Sopenharmony_ciOpSwitch %3 %10 0 %11 1 %12
2217fd4e5da5Sopenharmony_ci%10 = OpLabel
2218fd4e5da5Sopenharmony_ciOpBranch %11
2219fd4e5da5Sopenharmony_ci%12 = OpLabel
2220fd4e5da5Sopenharmony_ciOpBranch %10
2221fd4e5da5Sopenharmony_ci%11 = OpLabel
2222fd4e5da5Sopenharmony_ciOpBranch %9
2223fd4e5da5Sopenharmony_ci%9 = OpLabel
2224fd4e5da5Sopenharmony_ciOpReturn
2225fd4e5da5Sopenharmony_ciOpFunctionEnd
2226fd4e5da5Sopenharmony_ci)";
2227fd4e5da5Sopenharmony_ci
2228fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2229fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2230fd4e5da5Sopenharmony_ci  EXPECT_THAT(
2231fd4e5da5Sopenharmony_ci      getDiagnosticString(),
2232fd4e5da5Sopenharmony_ci      HasSubstr(
2233fd4e5da5Sopenharmony_ci          "Case construct that targets '12[%12]' has branches to the case "
2234fd4e5da5Sopenharmony_ci          "construct that targets '11[%11]', but does not immediately "
2235fd4e5da5Sopenharmony_ci          "precede it in the OpSwitch's target list\n"
2236fd4e5da5Sopenharmony_ci          "  OpSwitch %uint_0 %10 0 %11 1 %12"));
2237fd4e5da5Sopenharmony_ci}
2238fd4e5da5Sopenharmony_ci
2239fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, WrongOperandListNotLast) {
2240fd4e5da5Sopenharmony_ci  std::string text = R"(
2241fd4e5da5Sopenharmony_ciOpCapability Shader
2242fd4e5da5Sopenharmony_ciOpCapability Linkage
2243fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2244fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
2245fd4e5da5Sopenharmony_ci%2 = OpTypeInt 32 0
2246fd4e5da5Sopenharmony_ci%3 = OpConstant %2 0
2247fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
2248fd4e5da5Sopenharmony_ci%5 = OpTypeBool
2249fd4e5da5Sopenharmony_ci%6 = OpConstantTrue %5
2250fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %4
2251fd4e5da5Sopenharmony_ci%8 = OpLabel
2252fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
2253fd4e5da5Sopenharmony_ciOpSwitch %3 %10 0 %11 1 %12 2 %13
2254fd4e5da5Sopenharmony_ci%10 = OpLabel
2255fd4e5da5Sopenharmony_ciOpBranch %9
2256fd4e5da5Sopenharmony_ci%12 = OpLabel
2257fd4e5da5Sopenharmony_ciOpBranch %11
2258fd4e5da5Sopenharmony_ci%11 = OpLabel
2259fd4e5da5Sopenharmony_ciOpBranch %9
2260fd4e5da5Sopenharmony_ci%13 = OpLabel
2261fd4e5da5Sopenharmony_ciOpBranch %9
2262fd4e5da5Sopenharmony_ci%9 = OpLabel
2263fd4e5da5Sopenharmony_ciOpReturn
2264fd4e5da5Sopenharmony_ciOpFunctionEnd
2265fd4e5da5Sopenharmony_ci)";
2266fd4e5da5Sopenharmony_ci
2267fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2268fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2269fd4e5da5Sopenharmony_ci  EXPECT_THAT(
2270fd4e5da5Sopenharmony_ci      getDiagnosticString(),
2271fd4e5da5Sopenharmony_ci      HasSubstr(
2272fd4e5da5Sopenharmony_ci          "Case construct that targets '12[%12]' has branches to the case "
2273fd4e5da5Sopenharmony_ci          "construct that targets '11[%11]', but does not immediately "
2274fd4e5da5Sopenharmony_ci          "precede it in the OpSwitch's target list\n"
2275fd4e5da5Sopenharmony_ci          "  OpSwitch %uint_0 %10 0 %11 1 %12 2 %13"));
2276fd4e5da5Sopenharmony_ci}
2277fd4e5da5Sopenharmony_ci
2278fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, GoodUnreachableSwitch) {
2279fd4e5da5Sopenharmony_ci  const std::string text = R"(
2280fd4e5da5Sopenharmony_ciOpCapability Shader
2281fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2282fd4e5da5Sopenharmony_ciOpEntryPoint Fragment %2 "main"
2283fd4e5da5Sopenharmony_ciOpExecutionMode %2 OriginUpperLeft
2284fd4e5da5Sopenharmony_ci%3 = OpTypeVoid
2285fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %3
2286fd4e5da5Sopenharmony_ci%5 = OpTypeBool
2287fd4e5da5Sopenharmony_ci%6 = OpConstantTrue %5
2288fd4e5da5Sopenharmony_ci%7 = OpTypeInt 32 1
2289fd4e5da5Sopenharmony_ci%9 = OpConstant %7 0
2290fd4e5da5Sopenharmony_ci%2 = OpFunction %3 None %4
2291fd4e5da5Sopenharmony_ci%10 = OpLabel
2292fd4e5da5Sopenharmony_ciOpSelectionMerge %11 None
2293fd4e5da5Sopenharmony_ciOpBranchConditional %6 %12 %13
2294fd4e5da5Sopenharmony_ci%12 = OpLabel
2295fd4e5da5Sopenharmony_ciOpReturn
2296fd4e5da5Sopenharmony_ci%13 = OpLabel
2297fd4e5da5Sopenharmony_ciOpReturn
2298fd4e5da5Sopenharmony_ci%11 = OpLabel
2299fd4e5da5Sopenharmony_ciOpSelectionMerge %14 None
2300fd4e5da5Sopenharmony_ciOpSwitch %9 %14 0 %15
2301fd4e5da5Sopenharmony_ci%15 = OpLabel
2302fd4e5da5Sopenharmony_ciOpBranch %14
2303fd4e5da5Sopenharmony_ci%14 = OpLabel
2304fd4e5da5Sopenharmony_ciOpReturn
2305fd4e5da5Sopenharmony_ciOpFunctionEnd
2306fd4e5da5Sopenharmony_ci)";
2307fd4e5da5Sopenharmony_ci
2308fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2309fd4e5da5Sopenharmony_ci  EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
2310fd4e5da5Sopenharmony_ci}
2311fd4e5da5Sopenharmony_ci
2312fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, InvalidCaseExit) {
2313fd4e5da5Sopenharmony_ci  const std::string text = R"(
2314fd4e5da5Sopenharmony_ciOpCapability Shader
2315fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2316fd4e5da5Sopenharmony_ciOpEntryPoint Fragment %1 "func"
2317fd4e5da5Sopenharmony_ciOpExecutionMode %1 OriginUpperLeft
2318fd4e5da5Sopenharmony_ci%2 = OpTypeVoid
2319fd4e5da5Sopenharmony_ci%3 = OpTypeInt 32 0
2320fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %2
2321fd4e5da5Sopenharmony_ci%5 = OpConstant %3 0
2322fd4e5da5Sopenharmony_ci%1 = OpFunction %2 None %4
2323fd4e5da5Sopenharmony_ci%6 = OpLabel
2324fd4e5da5Sopenharmony_ciOpSelectionMerge %7 None
2325fd4e5da5Sopenharmony_ciOpSwitch %5 %7 0 %8 1 %9
2326fd4e5da5Sopenharmony_ci%8 = OpLabel
2327fd4e5da5Sopenharmony_ciOpBranch %10
2328fd4e5da5Sopenharmony_ci%9 = OpLabel
2329fd4e5da5Sopenharmony_ciOpBranch %10
2330fd4e5da5Sopenharmony_ci%10 = OpLabel
2331fd4e5da5Sopenharmony_ciOpReturn
2332fd4e5da5Sopenharmony_ci%7 = OpLabel
2333fd4e5da5Sopenharmony_ciOpReturn
2334fd4e5da5Sopenharmony_ciOpFunctionEnd
2335fd4e5da5Sopenharmony_ci)";
2336fd4e5da5Sopenharmony_ci
2337fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2338fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2339fd4e5da5Sopenharmony_ci  EXPECT_THAT(
2340fd4e5da5Sopenharmony_ci      getDiagnosticString(),
2341fd4e5da5Sopenharmony_ci      HasSubstr("Case construct that targets '8[%8]' has invalid branch "
2342fd4e5da5Sopenharmony_ci                "to block '10[%10]' (not another case construct, "
2343fd4e5da5Sopenharmony_ci                "corresponding merge, outer loop merge or outer loop "
2344fd4e5da5Sopenharmony_ci                "continue)"));
2345fd4e5da5Sopenharmony_ci}
2346fd4e5da5Sopenharmony_ci
2347fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, GoodCaseExitsToOuterConstructs) {
2348fd4e5da5Sopenharmony_ci  const std::string text = R"(
2349fd4e5da5Sopenharmony_ciOpCapability Shader
2350fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2351fd4e5da5Sopenharmony_ciOpEntryPoint Fragment %func "func"
2352fd4e5da5Sopenharmony_ciOpExecutionMode %func OriginUpperLeft
2353fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2354fd4e5da5Sopenharmony_ci%bool = OpTypeBool
2355fd4e5da5Sopenharmony_ci%true = OpConstantTrue %bool
2356fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
2357fd4e5da5Sopenharmony_ci%int0 = OpConstant %int 0
2358fd4e5da5Sopenharmony_ci%func_ty = OpTypeFunction %void
2359fd4e5da5Sopenharmony_ci%func = OpFunction %void None %func_ty
2360fd4e5da5Sopenharmony_ci%1 = OpLabel
2361fd4e5da5Sopenharmony_ciOpBranch %2
2362fd4e5da5Sopenharmony_ci%2 = OpLabel
2363fd4e5da5Sopenharmony_ciOpLoopMerge %7 %6 None
2364fd4e5da5Sopenharmony_ciOpBranch %3
2365fd4e5da5Sopenharmony_ci%3 = OpLabel
2366fd4e5da5Sopenharmony_ciOpSelectionMerge %5 None
2367fd4e5da5Sopenharmony_ciOpSwitch %int0 %5 0 %4
2368fd4e5da5Sopenharmony_ci%4 = OpLabel
2369fd4e5da5Sopenharmony_ciOpBranchConditional %true %6 %7
2370fd4e5da5Sopenharmony_ci%5 = OpLabel
2371fd4e5da5Sopenharmony_ciOpBranchConditional %true %6 %7
2372fd4e5da5Sopenharmony_ci%6 = OpLabel
2373fd4e5da5Sopenharmony_ciOpBranch %2
2374fd4e5da5Sopenharmony_ci%7 = OpLabel
2375fd4e5da5Sopenharmony_ciOpReturn
2376fd4e5da5Sopenharmony_ciOpFunctionEnd
2377fd4e5da5Sopenharmony_ci)";
2378fd4e5da5Sopenharmony_ci
2379fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2380fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
2381fd4e5da5Sopenharmony_ci}
2382fd4e5da5Sopenharmony_ci
2383fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, SwitchCaseOrderingBad1) {
2384fd4e5da5Sopenharmony_ci  const std::string text = R"(
2385fd4e5da5Sopenharmony_ciOpCapability Shader
2386fd4e5da5Sopenharmony_ciOpCapability Linkage
2387fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2388fd4e5da5Sopenharmony_ciOpName %default "default"
2389fd4e5da5Sopenharmony_ciOpName %other "other"
2390fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2391fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
2392fd4e5da5Sopenharmony_ci%undef = OpUndef %int
2393fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2394fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2395fd4e5da5Sopenharmony_ci%entry = OpLabel
2396fd4e5da5Sopenharmony_ciOpSelectionMerge %merge None
2397fd4e5da5Sopenharmony_ciOpSwitch %undef %default 0 %other 1 %default
2398fd4e5da5Sopenharmony_ci%default = OpLabel
2399fd4e5da5Sopenharmony_ciOpBranch %other
2400fd4e5da5Sopenharmony_ci%other = OpLabel
2401fd4e5da5Sopenharmony_ciOpBranch %merge
2402fd4e5da5Sopenharmony_ci%merge = OpLabel
2403fd4e5da5Sopenharmony_ciOpReturn
2404fd4e5da5Sopenharmony_ciOpFunctionEnd
2405fd4e5da5Sopenharmony_ci)";
2406fd4e5da5Sopenharmony_ci
2407fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2408fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2409fd4e5da5Sopenharmony_ci  EXPECT_THAT(
2410fd4e5da5Sopenharmony_ci      getDiagnosticString(),
2411fd4e5da5Sopenharmony_ci      HasSubstr("Case construct that targets '1[%default]' has branches to the "
2412fd4e5da5Sopenharmony_ci                "case construct that targets '2[%other]', but does not "
2413fd4e5da5Sopenharmony_ci                "immediately precede it in the OpSwitch's target list"));
2414fd4e5da5Sopenharmony_ci}
2415fd4e5da5Sopenharmony_ci
2416fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, SwitchCaseOrderingBad2) {
2417fd4e5da5Sopenharmony_ci  const std::string text = R"(
2418fd4e5da5Sopenharmony_ciOpCapability Shader
2419fd4e5da5Sopenharmony_ciOpCapability Linkage
2420fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2421fd4e5da5Sopenharmony_ciOpName %default "default"
2422fd4e5da5Sopenharmony_ciOpName %other "other"
2423fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2424fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
2425fd4e5da5Sopenharmony_ci%undef = OpUndef %int
2426fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2427fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2428fd4e5da5Sopenharmony_ci%entry = OpLabel
2429fd4e5da5Sopenharmony_ciOpSelectionMerge %merge None
2430fd4e5da5Sopenharmony_ciOpSwitch %undef %default 0 %default 1 %other
2431fd4e5da5Sopenharmony_ci%other = OpLabel
2432fd4e5da5Sopenharmony_ciOpBranch %default
2433fd4e5da5Sopenharmony_ci%default = OpLabel
2434fd4e5da5Sopenharmony_ciOpBranch %merge
2435fd4e5da5Sopenharmony_ci%merge = OpLabel
2436fd4e5da5Sopenharmony_ciOpReturn
2437fd4e5da5Sopenharmony_ciOpFunctionEnd
2438fd4e5da5Sopenharmony_ci)";
2439fd4e5da5Sopenharmony_ci
2440fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2441fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2442fd4e5da5Sopenharmony_ci  EXPECT_THAT(
2443fd4e5da5Sopenharmony_ci      getDiagnosticString(),
2444fd4e5da5Sopenharmony_ci      HasSubstr("Case construct that targets '2[%other]' has branches to the "
2445fd4e5da5Sopenharmony_ci                "case construct that targets '1[%default]', but does not "
2446fd4e5da5Sopenharmony_ci                "immediately precede it in the OpSwitch's target list"));
2447fd4e5da5Sopenharmony_ci}
2448fd4e5da5Sopenharmony_ci
2449fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, SwitchMultipleDefaultWithFallThroughGood) {
2450fd4e5da5Sopenharmony_ci  const std::string text = R"(
2451fd4e5da5Sopenharmony_ciOpCapability Shader
2452fd4e5da5Sopenharmony_ciOpCapability Linkage
2453fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2454fd4e5da5Sopenharmony_ciOpName %first "first"
2455fd4e5da5Sopenharmony_ciOpName %second "second"
2456fd4e5da5Sopenharmony_ciOpName %third "third"
2457fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2458fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
2459fd4e5da5Sopenharmony_ci%undef = OpUndef %int
2460fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2461fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2462fd4e5da5Sopenharmony_ci%entry = OpLabel
2463fd4e5da5Sopenharmony_ciOpSelectionMerge %merge None
2464fd4e5da5Sopenharmony_ciOpSwitch %undef %second 0 %first 1 %second 2 %third
2465fd4e5da5Sopenharmony_ci%first = OpLabel
2466fd4e5da5Sopenharmony_ciOpBranch %second
2467fd4e5da5Sopenharmony_ci%second = OpLabel
2468fd4e5da5Sopenharmony_ciOpBranch %third
2469fd4e5da5Sopenharmony_ci%third = OpLabel
2470fd4e5da5Sopenharmony_ciOpBranch %merge
2471fd4e5da5Sopenharmony_ci%merge = OpLabel
2472fd4e5da5Sopenharmony_ciOpReturn
2473fd4e5da5Sopenharmony_ciOpFunctionEnd
2474fd4e5da5Sopenharmony_ci)";
2475fd4e5da5Sopenharmony_ci
2476fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2477fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2478fd4e5da5Sopenharmony_ci}
2479fd4e5da5Sopenharmony_ci
2480fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, SwitchMultipleDefaultWithFallThroughBad) {
2481fd4e5da5Sopenharmony_ci  const std::string text = R"(
2482fd4e5da5Sopenharmony_ciOpCapability Shader
2483fd4e5da5Sopenharmony_ciOpCapability Linkage
2484fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2485fd4e5da5Sopenharmony_ciOpName %first "first"
2486fd4e5da5Sopenharmony_ciOpName %second "second"
2487fd4e5da5Sopenharmony_ciOpName %third "third"
2488fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2489fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
2490fd4e5da5Sopenharmony_ci%undef = OpUndef %int
2491fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2492fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2493fd4e5da5Sopenharmony_ci%entry = OpLabel
2494fd4e5da5Sopenharmony_ciOpSelectionMerge %merge None
2495fd4e5da5Sopenharmony_ciOpSwitch %undef %second 0 %second 1 %first 2 %third
2496fd4e5da5Sopenharmony_ci%first = OpLabel
2497fd4e5da5Sopenharmony_ciOpBranch %second
2498fd4e5da5Sopenharmony_ci%second = OpLabel
2499fd4e5da5Sopenharmony_ciOpBranch %third
2500fd4e5da5Sopenharmony_ci%third = OpLabel
2501fd4e5da5Sopenharmony_ciOpBranch %merge
2502fd4e5da5Sopenharmony_ci%merge = OpLabel
2503fd4e5da5Sopenharmony_ciOpReturn
2504fd4e5da5Sopenharmony_ciOpFunctionEnd
2505fd4e5da5Sopenharmony_ci)";
2506fd4e5da5Sopenharmony_ci
2507fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2508fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2509fd4e5da5Sopenharmony_ci}
2510fd4e5da5Sopenharmony_ci
2511fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, GoodUnreachableSelection) {
2512fd4e5da5Sopenharmony_ci  const std::string text = R"(
2513fd4e5da5Sopenharmony_ciOpCapability Shader
2514fd4e5da5Sopenharmony_ci%1 = OpExtInstImport "GLSL.std.450"
2515fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2516fd4e5da5Sopenharmony_ciOpEntryPoint Fragment %main "main"
2517fd4e5da5Sopenharmony_ciOpExecutionMode %main OriginUpperLeft
2518fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2519fd4e5da5Sopenharmony_ci%8 = OpTypeFunction %void
2520fd4e5da5Sopenharmony_ci%bool = OpTypeBool
2521fd4e5da5Sopenharmony_ci%false = OpConstantFalse %bool
2522fd4e5da5Sopenharmony_ci%main = OpFunction %void None %8
2523fd4e5da5Sopenharmony_ci%15 = OpLabel
2524fd4e5da5Sopenharmony_ciOpBranch %16
2525fd4e5da5Sopenharmony_ci%16 = OpLabel
2526fd4e5da5Sopenharmony_ciOpLoopMerge %17 %18 None
2527fd4e5da5Sopenharmony_ciOpBranch %19
2528fd4e5da5Sopenharmony_ci%19 = OpLabel
2529fd4e5da5Sopenharmony_ciOpBranchConditional %false %21 %17
2530fd4e5da5Sopenharmony_ci%21 = OpLabel
2531fd4e5da5Sopenharmony_ciOpSelectionMerge %22 None
2532fd4e5da5Sopenharmony_ciOpBranchConditional %false %23 %22
2533fd4e5da5Sopenharmony_ci%23 = OpLabel
2534fd4e5da5Sopenharmony_ciOpBranch %24
2535fd4e5da5Sopenharmony_ci%24 = OpLabel
2536fd4e5da5Sopenharmony_ciOpLoopMerge %25 %26 None
2537fd4e5da5Sopenharmony_ciOpBranch %27
2538fd4e5da5Sopenharmony_ci%27 = OpLabel
2539fd4e5da5Sopenharmony_ciOpReturn
2540fd4e5da5Sopenharmony_ci%26 = OpLabel
2541fd4e5da5Sopenharmony_ciOpBranchConditional %false %24 %25
2542fd4e5da5Sopenharmony_ci%25 = OpLabel
2543fd4e5da5Sopenharmony_ciOpSelectionMerge %28 None
2544fd4e5da5Sopenharmony_ciOpBranchConditional %false %18 %28
2545fd4e5da5Sopenharmony_ci%28 = OpLabel
2546fd4e5da5Sopenharmony_ciOpBranch %22
2547fd4e5da5Sopenharmony_ci%22 = OpLabel
2548fd4e5da5Sopenharmony_ciOpBranch %18
2549fd4e5da5Sopenharmony_ci%18 = OpLabel
2550fd4e5da5Sopenharmony_ciOpBranch %16
2551fd4e5da5Sopenharmony_ci%17 = OpLabel
2552fd4e5da5Sopenharmony_ciOpReturn
2553fd4e5da5Sopenharmony_ciOpFunctionEnd
2554fd4e5da5Sopenharmony_ci)";
2555fd4e5da5Sopenharmony_ci
2556fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2557fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
2558fd4e5da5Sopenharmony_ci}
2559fd4e5da5Sopenharmony_ci
2560fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ShaderWithPhiPtr) {
2561fd4e5da5Sopenharmony_ci  const std::string text = R"(
2562fd4e5da5Sopenharmony_ci               OpCapability Shader
2563fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
2564fd4e5da5Sopenharmony_ci               OpEntryPoint GLCompute %1 "main"
2565fd4e5da5Sopenharmony_ci               OpExecutionMode %1 LocalSize 1 1 1
2566fd4e5da5Sopenharmony_ci               OpSource HLSL 600
2567fd4e5da5Sopenharmony_ci       %bool = OpTypeBool
2568fd4e5da5Sopenharmony_ci%_ptr_Function_bool = OpTypePointer Function %bool
2569fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
2570fd4e5da5Sopenharmony_ci          %5 = OpTypeFunction %void
2571fd4e5da5Sopenharmony_ci          %1 = OpFunction %void None %5
2572fd4e5da5Sopenharmony_ci          %6 = OpLabel
2573fd4e5da5Sopenharmony_ci          %7 = OpVariable %_ptr_Function_bool Function
2574fd4e5da5Sopenharmony_ci          %8 = OpVariable %_ptr_Function_bool Function
2575fd4e5da5Sopenharmony_ci          %9 = OpUndef %bool
2576fd4e5da5Sopenharmony_ci               OpSelectionMerge %10 None
2577fd4e5da5Sopenharmony_ci               OpBranchConditional %9 %11 %10
2578fd4e5da5Sopenharmony_ci         %11 = OpLabel
2579fd4e5da5Sopenharmony_ci               OpBranch %10
2580fd4e5da5Sopenharmony_ci         %10 = OpLabel
2581fd4e5da5Sopenharmony_ci         %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11
2582fd4e5da5Sopenharmony_ci               OpReturn
2583fd4e5da5Sopenharmony_ci               OpFunctionEnd
2584fd4e5da5Sopenharmony_ci)";
2585fd4e5da5Sopenharmony_ci
2586fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2587fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
2588fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
2589fd4e5da5Sopenharmony_ci              HasSubstr("Using pointers with OpPhi requires capability "
2590fd4e5da5Sopenharmony_ci                        "VariablePointers or VariablePointersStorageBuffer"));
2591fd4e5da5Sopenharmony_ci}
2592fd4e5da5Sopenharmony_ci
2593fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, VarPtrShaderWithPhiPtr) {
2594fd4e5da5Sopenharmony_ci  const std::string text = R"(
2595fd4e5da5Sopenharmony_ci               OpCapability Shader
2596fd4e5da5Sopenharmony_ci               OpCapability VariablePointers
2597fd4e5da5Sopenharmony_ci               OpExtension "SPV_KHR_variable_pointers"
2598fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
2599fd4e5da5Sopenharmony_ci               OpEntryPoint GLCompute %1 "main"
2600fd4e5da5Sopenharmony_ci               OpExecutionMode %1 LocalSize 1 1 1
2601fd4e5da5Sopenharmony_ci               OpSource HLSL 600
2602fd4e5da5Sopenharmony_ci       %bool = OpTypeBool
2603fd4e5da5Sopenharmony_ci%_ptr_Function_bool = OpTypePointer Function %bool
2604fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
2605fd4e5da5Sopenharmony_ci          %5 = OpTypeFunction %void
2606fd4e5da5Sopenharmony_ci          %1 = OpFunction %void None %5
2607fd4e5da5Sopenharmony_ci          %6 = OpLabel
2608fd4e5da5Sopenharmony_ci          %7 = OpVariable %_ptr_Function_bool Function
2609fd4e5da5Sopenharmony_ci          %8 = OpVariable %_ptr_Function_bool Function
2610fd4e5da5Sopenharmony_ci          %9 = OpUndef %bool
2611fd4e5da5Sopenharmony_ci               OpSelectionMerge %10 None
2612fd4e5da5Sopenharmony_ci               OpBranchConditional %9 %11 %10
2613fd4e5da5Sopenharmony_ci         %11 = OpLabel
2614fd4e5da5Sopenharmony_ci               OpBranch %10
2615fd4e5da5Sopenharmony_ci         %10 = OpLabel
2616fd4e5da5Sopenharmony_ci         %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11
2617fd4e5da5Sopenharmony_ci               OpReturn
2618fd4e5da5Sopenharmony_ci               OpFunctionEnd
2619fd4e5da5Sopenharmony_ci)";
2620fd4e5da5Sopenharmony_ci
2621fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2622fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2623fd4e5da5Sopenharmony_ci}
2624fd4e5da5Sopenharmony_ci
2625fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, VarPtrStgBufShaderWithPhiStgBufPtr) {
2626fd4e5da5Sopenharmony_ci  const std::string text = R"(
2627fd4e5da5Sopenharmony_ci               OpCapability Shader
2628fd4e5da5Sopenharmony_ci               OpCapability VariablePointersStorageBuffer
2629fd4e5da5Sopenharmony_ci               OpExtension "SPV_KHR_variable_pointers"
2630fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
2631fd4e5da5Sopenharmony_ci               OpEntryPoint GLCompute %1 "main"
2632fd4e5da5Sopenharmony_ci               OpExecutionMode %1 LocalSize 1 1 1
2633fd4e5da5Sopenharmony_ci               OpSource HLSL 600
2634fd4e5da5Sopenharmony_ci       %bool = OpTypeBool
2635fd4e5da5Sopenharmony_ci       %float = OpTypeFloat 32
2636fd4e5da5Sopenharmony_ci%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
2637fd4e5da5Sopenharmony_ci          %7 = OpVariable %_ptr_StorageBuffer_float StorageBuffer
2638fd4e5da5Sopenharmony_ci          %8 = OpVariable %_ptr_StorageBuffer_float StorageBuffer
2639fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
2640fd4e5da5Sopenharmony_ci          %5 = OpTypeFunction %void
2641fd4e5da5Sopenharmony_ci          %1 = OpFunction %void None %5
2642fd4e5da5Sopenharmony_ci          %6 = OpLabel
2643fd4e5da5Sopenharmony_ci          %9 = OpUndef %bool
2644fd4e5da5Sopenharmony_ci               OpSelectionMerge %10 None
2645fd4e5da5Sopenharmony_ci               OpBranchConditional %9 %11 %10
2646fd4e5da5Sopenharmony_ci         %11 = OpLabel
2647fd4e5da5Sopenharmony_ci               OpBranch %10
2648fd4e5da5Sopenharmony_ci         %10 = OpLabel
2649fd4e5da5Sopenharmony_ci         %12 = OpPhi %_ptr_StorageBuffer_float %7 %6 %8 %11
2650fd4e5da5Sopenharmony_ci               OpReturn
2651fd4e5da5Sopenharmony_ci               OpFunctionEnd
2652fd4e5da5Sopenharmony_ci)";
2653fd4e5da5Sopenharmony_ci
2654fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2655fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2656fd4e5da5Sopenharmony_ci}
2657fd4e5da5Sopenharmony_ci
2658fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, KernelWithPhiPtr) {
2659fd4e5da5Sopenharmony_ci  const std::string text = R"(
2660fd4e5da5Sopenharmony_ci               OpCapability Kernel
2661fd4e5da5Sopenharmony_ci               OpCapability Addresses
2662fd4e5da5Sopenharmony_ci               OpMemoryModel Physical32 OpenCL
2663fd4e5da5Sopenharmony_ci               OpEntryPoint Kernel %1 "main"
2664fd4e5da5Sopenharmony_ci               OpExecutionMode %1 LocalSize 1 1 1
2665fd4e5da5Sopenharmony_ci               OpSource HLSL 600
2666fd4e5da5Sopenharmony_ci       %bool = OpTypeBool
2667fd4e5da5Sopenharmony_ci%_ptr_Function_bool = OpTypePointer Function %bool
2668fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
2669fd4e5da5Sopenharmony_ci          %5 = OpTypeFunction %void
2670fd4e5da5Sopenharmony_ci          %1 = OpFunction %void None %5
2671fd4e5da5Sopenharmony_ci          %6 = OpLabel
2672fd4e5da5Sopenharmony_ci          %7 = OpVariable %_ptr_Function_bool Function
2673fd4e5da5Sopenharmony_ci          %8 = OpVariable %_ptr_Function_bool Function
2674fd4e5da5Sopenharmony_ci          %9 = OpUndef %bool
2675fd4e5da5Sopenharmony_ci               OpSelectionMerge %10 None
2676fd4e5da5Sopenharmony_ci               OpBranchConditional %9 %11 %10
2677fd4e5da5Sopenharmony_ci         %11 = OpLabel
2678fd4e5da5Sopenharmony_ci               OpBranch %10
2679fd4e5da5Sopenharmony_ci         %10 = OpLabel
2680fd4e5da5Sopenharmony_ci         %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11
2681fd4e5da5Sopenharmony_ci               OpReturn
2682fd4e5da5Sopenharmony_ci               OpFunctionEnd
2683fd4e5da5Sopenharmony_ci)";
2684fd4e5da5Sopenharmony_ci
2685fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2686fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2687fd4e5da5Sopenharmony_ci}
2688fd4e5da5Sopenharmony_ci
2689fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, SwitchTargetMustBeLabel) {
2690fd4e5da5Sopenharmony_ci  const std::string text = R"(
2691fd4e5da5Sopenharmony_ci               OpCapability Shader
2692fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
2693fd4e5da5Sopenharmony_ci               OpEntryPoint GLCompute %1 "foo"
2694fd4e5da5Sopenharmony_ci       %uint = OpTypeInt 32 0
2695fd4e5da5Sopenharmony_ci     %uint_0 = OpConstant %uint 0
2696fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
2697fd4e5da5Sopenharmony_ci          %5 = OpTypeFunction %void
2698fd4e5da5Sopenharmony_ci          %1 = OpFunction %void None %5
2699fd4e5da5Sopenharmony_ci          %6 = OpLabel
2700fd4e5da5Sopenharmony_ci          %7 = OpCopyObject %uint %uint_0
2701fd4e5da5Sopenharmony_ci               OpSelectionMerge %8 None
2702fd4e5da5Sopenharmony_ci               OpSwitch %uint_0 %8 0 %7
2703fd4e5da5Sopenharmony_ci          %8 = OpLabel
2704fd4e5da5Sopenharmony_ci               OpReturn
2705fd4e5da5Sopenharmony_ci               OpFunctionEnd
2706fd4e5da5Sopenharmony_ci)";
2707fd4e5da5Sopenharmony_ci
2708fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2709fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
2710fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
2711fd4e5da5Sopenharmony_ci              HasSubstr("'Target Label' operands for OpSwitch must "
2712fd4e5da5Sopenharmony_ci                        "be IDs of an OpLabel instruction"));
2713fd4e5da5Sopenharmony_ci}
2714fd4e5da5Sopenharmony_ci
2715fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BranchTargetMustBeLabel) {
2716fd4e5da5Sopenharmony_ci  const std::string text = R"(
2717fd4e5da5Sopenharmony_ci               OpCapability Shader
2718fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
2719fd4e5da5Sopenharmony_ci               OpEntryPoint GLCompute %1 "foo"
2720fd4e5da5Sopenharmony_ci       %uint = OpTypeInt 32 0
2721fd4e5da5Sopenharmony_ci     %uint_0 = OpConstant %uint 0
2722fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
2723fd4e5da5Sopenharmony_ci          %5 = OpTypeFunction %void
2724fd4e5da5Sopenharmony_ci          %1 = OpFunction %void None %5
2725fd4e5da5Sopenharmony_ci          %2 = OpLabel
2726fd4e5da5Sopenharmony_ci          %7 = OpCopyObject %uint %uint_0
2727fd4e5da5Sopenharmony_ci               OpBranch %7
2728fd4e5da5Sopenharmony_ci          %8 = OpLabel
2729fd4e5da5Sopenharmony_ci               OpReturn
2730fd4e5da5Sopenharmony_ci               OpFunctionEnd
2731fd4e5da5Sopenharmony_ci)";
2732fd4e5da5Sopenharmony_ci
2733fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2734fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
2735fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
2736fd4e5da5Sopenharmony_ci              HasSubstr("'Target Label' operands for OpBranch must "
2737fd4e5da5Sopenharmony_ci                        "be the ID of an OpLabel instruction"));
2738fd4e5da5Sopenharmony_ci}
2739fd4e5da5Sopenharmony_ci
2740fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ReachableOpUnreachableOneBlock) {
2741fd4e5da5Sopenharmony_ci  const std::string text = R"(
2742fd4e5da5Sopenharmony_ciOpCapability Shader
2743fd4e5da5Sopenharmony_ciOpCapability Linkage
2744fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2745fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2746fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2747fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2748fd4e5da5Sopenharmony_ci%entry = OpLabel
2749fd4e5da5Sopenharmony_ciOpUnreachable
2750fd4e5da5Sopenharmony_ciOpFunctionEnd
2751fd4e5da5Sopenharmony_ci)";
2752fd4e5da5Sopenharmony_ci
2753fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2754fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2755fd4e5da5Sopenharmony_ci}
2756fd4e5da5Sopenharmony_ci
2757fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ReachableOpUnreachableOpBranch) {
2758fd4e5da5Sopenharmony_ci  const std::string text = R"(
2759fd4e5da5Sopenharmony_ciOpCapability Shader
2760fd4e5da5Sopenharmony_ciOpCapability Linkage
2761fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2762fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2763fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2764fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2765fd4e5da5Sopenharmony_ci%entry = OpLabel
2766fd4e5da5Sopenharmony_ciOpBranch %block
2767fd4e5da5Sopenharmony_ci%block = OpLabel
2768fd4e5da5Sopenharmony_ciOpUnreachable
2769fd4e5da5Sopenharmony_ciOpFunctionEnd
2770fd4e5da5Sopenharmony_ci)";
2771fd4e5da5Sopenharmony_ci
2772fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2773fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2774fd4e5da5Sopenharmony_ci}
2775fd4e5da5Sopenharmony_ci
2776fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ReachableOpUnreachableOpBranchConditional) {
2777fd4e5da5Sopenharmony_ci  const std::string text = R"(
2778fd4e5da5Sopenharmony_ciOpCapability Shader
2779fd4e5da5Sopenharmony_ciOpCapability Linkage
2780fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2781fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2782fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2783fd4e5da5Sopenharmony_ci%bool = OpTypeBool
2784fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
2785fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2786fd4e5da5Sopenharmony_ci%entry = OpLabel
2787fd4e5da5Sopenharmony_ciOpSelectionMerge %block None
2788fd4e5da5Sopenharmony_ciOpBranchConditional %undef %block %unreachable
2789fd4e5da5Sopenharmony_ci%block = OpLabel
2790fd4e5da5Sopenharmony_ciOpReturn
2791fd4e5da5Sopenharmony_ci%unreachable = OpLabel
2792fd4e5da5Sopenharmony_ciOpUnreachable
2793fd4e5da5Sopenharmony_ciOpFunctionEnd
2794fd4e5da5Sopenharmony_ci)";
2795fd4e5da5Sopenharmony_ci
2796fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2797fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2798fd4e5da5Sopenharmony_ci}
2799fd4e5da5Sopenharmony_ci
2800fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ReachableOpUnreachableOpSwitch) {
2801fd4e5da5Sopenharmony_ci  const std::string text = R"(
2802fd4e5da5Sopenharmony_ciOpCapability Shader
2803fd4e5da5Sopenharmony_ciOpCapability Linkage
2804fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2805fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2806fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2807fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
2808fd4e5da5Sopenharmony_ci%undef = OpUndef %int
2809fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2810fd4e5da5Sopenharmony_ci%entry = OpLabel
2811fd4e5da5Sopenharmony_ciOpSelectionMerge %block1 None
2812fd4e5da5Sopenharmony_ciOpSwitch %undef %block1 0 %unreachable 1 %block2
2813fd4e5da5Sopenharmony_ci%block1 = OpLabel
2814fd4e5da5Sopenharmony_ciOpReturn
2815fd4e5da5Sopenharmony_ci%unreachable = OpLabel
2816fd4e5da5Sopenharmony_ciOpUnreachable
2817fd4e5da5Sopenharmony_ci%block2 = OpLabel
2818fd4e5da5Sopenharmony_ciOpReturn
2819fd4e5da5Sopenharmony_ciOpFunctionEnd
2820fd4e5da5Sopenharmony_ci)";
2821fd4e5da5Sopenharmony_ci
2822fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2823fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2824fd4e5da5Sopenharmony_ci}
2825fd4e5da5Sopenharmony_ci
2826fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ReachableOpUnreachableLoop) {
2827fd4e5da5Sopenharmony_ci  const std::string text = R"(
2828fd4e5da5Sopenharmony_ciOpCapability Shader
2829fd4e5da5Sopenharmony_ciOpCapability Linkage
2830fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2831fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2832fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2833fd4e5da5Sopenharmony_ci%bool = OpTypeBool
2834fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
2835fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2836fd4e5da5Sopenharmony_ci%entry = OpLabel
2837fd4e5da5Sopenharmony_ciOpBranch %loop
2838fd4e5da5Sopenharmony_ci%loop = OpLabel
2839fd4e5da5Sopenharmony_ciOpLoopMerge %unreachable %loop None
2840fd4e5da5Sopenharmony_ciOpBranchConditional %undef %loop %unreachable
2841fd4e5da5Sopenharmony_ci%unreachable = OpLabel
2842fd4e5da5Sopenharmony_ciOpUnreachable
2843fd4e5da5Sopenharmony_ciOpFunctionEnd
2844fd4e5da5Sopenharmony_ci)";
2845fd4e5da5Sopenharmony_ci
2846fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2847fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2848fd4e5da5Sopenharmony_ci}
2849fd4e5da5Sopenharmony_ci
2850fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, UnreachableLoopBadBackedge) {
2851fd4e5da5Sopenharmony_ci  const std::string text = R"(
2852fd4e5da5Sopenharmony_ciOpCapability Shader
2853fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2854fd4e5da5Sopenharmony_ciOpEntryPoint Fragment %2 "main"
2855fd4e5da5Sopenharmony_ciOpExecutionMode %2 OriginUpperLeft
2856fd4e5da5Sopenharmony_ci%4 = OpTypeVoid
2857fd4e5da5Sopenharmony_ci%5 = OpTypeFunction %4
2858fd4e5da5Sopenharmony_ci%8 = OpTypeBool
2859fd4e5da5Sopenharmony_ci%13 = OpConstantTrue %8
2860fd4e5da5Sopenharmony_ci%2 = OpFunction %4 None %5
2861fd4e5da5Sopenharmony_ci%14 = OpLabel
2862fd4e5da5Sopenharmony_ciOpSelectionMerge %15 None
2863fd4e5da5Sopenharmony_ciOpBranchConditional %13 %15 %15
2864fd4e5da5Sopenharmony_ci%16 = OpLabel
2865fd4e5da5Sopenharmony_ciOpLoopMerge %17 %18 None
2866fd4e5da5Sopenharmony_ciOpBranch %17
2867fd4e5da5Sopenharmony_ci%18 = OpLabel
2868fd4e5da5Sopenharmony_ciOpBranch %17
2869fd4e5da5Sopenharmony_ci%17 = OpLabel
2870fd4e5da5Sopenharmony_ciOpBranch %15
2871fd4e5da5Sopenharmony_ci%15 = OpLabel
2872fd4e5da5Sopenharmony_ciOpReturn
2873fd4e5da5Sopenharmony_ciOpFunctionEnd
2874fd4e5da5Sopenharmony_ci)";
2875fd4e5da5Sopenharmony_ci
2876fd4e5da5Sopenharmony_ci  // The back-edge in this test is bad, but the validator fails to identify it
2877fd4e5da5Sopenharmony_ci  // because it is in an entirely unreachable section of code. Prior to #2488
2878fd4e5da5Sopenharmony_ci  // this code failed an assert in Construct::blocks().
2879fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2880fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2881fd4e5da5Sopenharmony_ci}
2882fd4e5da5Sopenharmony_ci
2883fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, OneContinueTwoBackedges) {
2884fd4e5da5Sopenharmony_ci  const std::string text = R"(
2885fd4e5da5Sopenharmony_ciOpCapability Shader
2886fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2887fd4e5da5Sopenharmony_ciOpEntryPoint GLCompute %1 "main"
2888fd4e5da5Sopenharmony_ciOpExecutionMode %1 LocalSize 1 1 1
2889fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2890fd4e5da5Sopenharmony_ci%bool = OpTypeBool
2891fd4e5da5Sopenharmony_ci%true = OpConstantTrue %bool
2892fd4e5da5Sopenharmony_ci%5 = OpTypeFunction %void
2893fd4e5da5Sopenharmony_ci%1 = OpFunction %void None %5
2894fd4e5da5Sopenharmony_ci%6 = OpLabel
2895fd4e5da5Sopenharmony_ciOpBranch %7
2896fd4e5da5Sopenharmony_ci%7 = OpLabel
2897fd4e5da5Sopenharmony_ciOpLoopMerge %8 %9 None
2898fd4e5da5Sopenharmony_ciOpBranch %10
2899fd4e5da5Sopenharmony_ci%10 = OpLabel
2900fd4e5da5Sopenharmony_ciOpLoopMerge %11 %9 None
2901fd4e5da5Sopenharmony_ciOpBranchConditional %true %11 %9
2902fd4e5da5Sopenharmony_ci%9 = OpLabel
2903fd4e5da5Sopenharmony_ciOpBranchConditional %true %10 %7
2904fd4e5da5Sopenharmony_ci%11 = OpLabel
2905fd4e5da5Sopenharmony_ciOpBranch %8
2906fd4e5da5Sopenharmony_ci%8 = OpLabel
2907fd4e5da5Sopenharmony_ciOpReturn
2908fd4e5da5Sopenharmony_ciOpFunctionEnd
2909fd4e5da5Sopenharmony_ci)";
2910fd4e5da5Sopenharmony_ci
2911fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2912fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2913fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
2914fd4e5da5Sopenharmony_ci              HasSubstr("Back-edges ('10[%10]' -> '9[%9]') can only be formed "
2915fd4e5da5Sopenharmony_ci                        "between a block and a loop header"));
2916fd4e5da5Sopenharmony_ci}
2917fd4e5da5Sopenharmony_ci
2918fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopMergeMergeBlockNotLabel) {
2919fd4e5da5Sopenharmony_ci  const std::string text = R"(
2920fd4e5da5Sopenharmony_ciOpCapability Shader
2921fd4e5da5Sopenharmony_ciOpCapability Linkage
2922fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2923fd4e5da5Sopenharmony_ciOpName %undef "undef"
2924fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2925fd4e5da5Sopenharmony_ci%bool = OpTypeBool
2926fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
2927fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2928fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2929fd4e5da5Sopenharmony_ci%1 = OpLabel
2930fd4e5da5Sopenharmony_ciOpLoopMerge %undef %2 None
2931fd4e5da5Sopenharmony_ciOpBranchConditional %undef %2 %2
2932fd4e5da5Sopenharmony_ci%2 = OpLabel
2933fd4e5da5Sopenharmony_ciOpReturn
2934fd4e5da5Sopenharmony_ciOpFunctionEnd
2935fd4e5da5Sopenharmony_ci)";
2936fd4e5da5Sopenharmony_ci
2937fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2938fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
2939fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
2940fd4e5da5Sopenharmony_ci              HasSubstr("Merge Block '1[%undef]' must be an OpLabel"));
2941fd4e5da5Sopenharmony_ci}
2942fd4e5da5Sopenharmony_ci
2943fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopMergeContinueTargetNotLabel) {
2944fd4e5da5Sopenharmony_ci  const std::string text = R"(
2945fd4e5da5Sopenharmony_ciOpCapability Shader
2946fd4e5da5Sopenharmony_ciOpCapability Linkage
2947fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2948fd4e5da5Sopenharmony_ciOpName %undef "undef"
2949fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2950fd4e5da5Sopenharmony_ci%bool = OpTypeBool
2951fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
2952fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2953fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2954fd4e5da5Sopenharmony_ci%1 = OpLabel
2955fd4e5da5Sopenharmony_ciOpLoopMerge %2 %undef None
2956fd4e5da5Sopenharmony_ciOpBranchConditional %undef %2 %2
2957fd4e5da5Sopenharmony_ci%2 = OpLabel
2958fd4e5da5Sopenharmony_ciOpReturn
2959fd4e5da5Sopenharmony_ciOpFunctionEnd
2960fd4e5da5Sopenharmony_ci)";
2961fd4e5da5Sopenharmony_ci
2962fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2963fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
2964fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
2965fd4e5da5Sopenharmony_ci              HasSubstr("Continue Target '1[%undef]' must be an OpLabel"));
2966fd4e5da5Sopenharmony_ci}
2967fd4e5da5Sopenharmony_ci
2968fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopMergeMergeBlockContinueTargetSameLabel) {
2969fd4e5da5Sopenharmony_ci  const std::string text = R"(
2970fd4e5da5Sopenharmony_ciOpCapability Shader
2971fd4e5da5Sopenharmony_ciOpCapability Linkage
2972fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2973fd4e5da5Sopenharmony_ciOpName %undef "undef"
2974fd4e5da5Sopenharmony_ci%void = OpTypeVoid
2975fd4e5da5Sopenharmony_ci%bool = OpTypeBool
2976fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
2977fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
2978fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
2979fd4e5da5Sopenharmony_ci%1 = OpLabel
2980fd4e5da5Sopenharmony_ciOpLoopMerge %2 %2 None
2981fd4e5da5Sopenharmony_ciOpBranchConditional %undef %2 %2
2982fd4e5da5Sopenharmony_ci%2 = OpLabel
2983fd4e5da5Sopenharmony_ciOpReturn
2984fd4e5da5Sopenharmony_ciOpFunctionEnd
2985fd4e5da5Sopenharmony_ci)";
2986fd4e5da5Sopenharmony_ci
2987fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
2988fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
2989fd4e5da5Sopenharmony_ci  EXPECT_THAT(
2990fd4e5da5Sopenharmony_ci      getDiagnosticString(),
2991fd4e5da5Sopenharmony_ci      HasSubstr("Merge Block and Continue Target must be different ids"));
2992fd4e5da5Sopenharmony_ci}
2993fd4e5da5Sopenharmony_ci
2994fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopMergeUnrollAndDontUnroll) {
2995fd4e5da5Sopenharmony_ci  const std::string text = R"(
2996fd4e5da5Sopenharmony_ciOpCapability Shader
2997fd4e5da5Sopenharmony_ciOpCapability Linkage
2998fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
2999fd4e5da5Sopenharmony_ciOpName %undef "undef"
3000fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3001fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3002fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3003fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3004fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3005fd4e5da5Sopenharmony_ci%5 = OpLabel
3006fd4e5da5Sopenharmony_ciOpBranch %1
3007fd4e5da5Sopenharmony_ci%1 = OpLabel
3008fd4e5da5Sopenharmony_ciOpLoopMerge %2 %3 Unroll|DontUnroll
3009fd4e5da5Sopenharmony_ciOpBranchConditional %undef %2 %3
3010fd4e5da5Sopenharmony_ci%3 = OpLabel
3011fd4e5da5Sopenharmony_ciOpBranch %1
3012fd4e5da5Sopenharmony_ci%2 = OpLabel
3013fd4e5da5Sopenharmony_ciOpReturn
3014fd4e5da5Sopenharmony_ciOpFunctionEnd
3015fd4e5da5Sopenharmony_ci)";
3016fd4e5da5Sopenharmony_ci
3017fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3018fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
3019fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3020fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3021fd4e5da5Sopenharmony_ci      HasSubstr(
3022fd4e5da5Sopenharmony_ci          "Unroll and DontUnroll loop controls must not both be specified"));
3023fd4e5da5Sopenharmony_ci}
3024fd4e5da5Sopenharmony_ci
3025fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopMergePeelCountAndDontUnroll) {
3026fd4e5da5Sopenharmony_ci  const std::string text = R"(
3027fd4e5da5Sopenharmony_ciOpCapability Shader
3028fd4e5da5Sopenharmony_ciOpCapability Linkage
3029fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3030fd4e5da5Sopenharmony_ciOpName %undef "undef"
3031fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3032fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3033fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3034fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3035fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3036fd4e5da5Sopenharmony_ci%5 = OpLabel
3037fd4e5da5Sopenharmony_ciOpBranch %1
3038fd4e5da5Sopenharmony_ci%1 = OpLabel
3039fd4e5da5Sopenharmony_ciOpLoopMerge %2 %3 DontUnroll|PeelCount 1
3040fd4e5da5Sopenharmony_ciOpBranchConditional %undef %2 %3
3041fd4e5da5Sopenharmony_ci%3 = OpLabel
3042fd4e5da5Sopenharmony_ciOpBranch %1
3043fd4e5da5Sopenharmony_ci%2 = OpLabel
3044fd4e5da5Sopenharmony_ciOpReturn
3045fd4e5da5Sopenharmony_ciOpFunctionEnd
3046fd4e5da5Sopenharmony_ci)";
3047fd4e5da5Sopenharmony_ci
3048fd4e5da5Sopenharmony_ci  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
3049fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
3050fd4e5da5Sopenharmony_ci            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
3051fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3052fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3053fd4e5da5Sopenharmony_ci      HasSubstr(
3054fd4e5da5Sopenharmony_ci          "PeelCount and DontUnroll loop controls must not both be specified"));
3055fd4e5da5Sopenharmony_ci}
3056fd4e5da5Sopenharmony_ci
3057fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopMergePartialCountAndDontUnroll) {
3058fd4e5da5Sopenharmony_ci  const std::string text = R"(
3059fd4e5da5Sopenharmony_ciOpCapability Shader
3060fd4e5da5Sopenharmony_ciOpCapability Linkage
3061fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3062fd4e5da5Sopenharmony_ciOpName %undef "undef"
3063fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3064fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3065fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3066fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3067fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3068fd4e5da5Sopenharmony_ci%5 = OpLabel
3069fd4e5da5Sopenharmony_ciOpBranch %1
3070fd4e5da5Sopenharmony_ci%1 = OpLabel
3071fd4e5da5Sopenharmony_ciOpLoopMerge %2 %3 DontUnroll|PartialCount 1
3072fd4e5da5Sopenharmony_ciOpBranchConditional %undef %2 %3
3073fd4e5da5Sopenharmony_ci%3 = OpLabel
3074fd4e5da5Sopenharmony_ciOpBranch %1
3075fd4e5da5Sopenharmony_ci%2 = OpLabel
3076fd4e5da5Sopenharmony_ciOpReturn
3077fd4e5da5Sopenharmony_ciOpFunctionEnd
3078fd4e5da5Sopenharmony_ci)";
3079fd4e5da5Sopenharmony_ci
3080fd4e5da5Sopenharmony_ci  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
3081fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
3082fd4e5da5Sopenharmony_ci            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
3083fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
3084fd4e5da5Sopenharmony_ci              HasSubstr("PartialCount and DontUnroll loop controls must not "
3085fd4e5da5Sopenharmony_ci                        "both be specified"));
3086fd4e5da5Sopenharmony_ci}
3087fd4e5da5Sopenharmony_ci
3088fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopMergeIterationMultipleZero) {
3089fd4e5da5Sopenharmony_ci  const std::string text = R"(
3090fd4e5da5Sopenharmony_ciOpCapability Shader
3091fd4e5da5Sopenharmony_ciOpCapability Linkage
3092fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3093fd4e5da5Sopenharmony_ciOpName %undef "undef"
3094fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3095fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3096fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3097fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3098fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3099fd4e5da5Sopenharmony_ci%5 = OpLabel
3100fd4e5da5Sopenharmony_ciOpBranch %1
3101fd4e5da5Sopenharmony_ci%1 = OpLabel
3102fd4e5da5Sopenharmony_ciOpLoopMerge %2 %3 IterationMultiple 0
3103fd4e5da5Sopenharmony_ciOpBranchConditional %undef %2 %3
3104fd4e5da5Sopenharmony_ci%3 = OpLabel
3105fd4e5da5Sopenharmony_ciOpBranch %1
3106fd4e5da5Sopenharmony_ci%2 = OpLabel
3107fd4e5da5Sopenharmony_ciOpReturn
3108fd4e5da5Sopenharmony_ciOpFunctionEnd
3109fd4e5da5Sopenharmony_ci)";
3110fd4e5da5Sopenharmony_ci
3111fd4e5da5Sopenharmony_ci  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
3112fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
3113fd4e5da5Sopenharmony_ci            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
3114fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3115fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3116fd4e5da5Sopenharmony_ci      HasSubstr(
3117fd4e5da5Sopenharmony_ci          "IterationMultiple loop control operand must be greater than zero"));
3118fd4e5da5Sopenharmony_ci}
3119fd4e5da5Sopenharmony_ci
3120fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopMergeIterationMultipleZeroMoreOperands) {
3121fd4e5da5Sopenharmony_ci  const std::string text = R"(
3122fd4e5da5Sopenharmony_ciOpCapability Shader
3123fd4e5da5Sopenharmony_ciOpCapability Linkage
3124fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3125fd4e5da5Sopenharmony_ciOpName %undef "undef"
3126fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3127fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3128fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3129fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3130fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3131fd4e5da5Sopenharmony_ci%5 = OpLabel
3132fd4e5da5Sopenharmony_ciOpBranch %1
3133fd4e5da5Sopenharmony_ci%1 = OpLabel
3134fd4e5da5Sopenharmony_ciOpLoopMerge %2 %3 MaxIterations|IterationMultiple 4 0
3135fd4e5da5Sopenharmony_ciOpBranchConditional %undef %2 %3
3136fd4e5da5Sopenharmony_ci%3 = OpLabel
3137fd4e5da5Sopenharmony_ciOpBranch %1
3138fd4e5da5Sopenharmony_ci%2 = OpLabel
3139fd4e5da5Sopenharmony_ciOpReturn
3140fd4e5da5Sopenharmony_ciOpFunctionEnd
3141fd4e5da5Sopenharmony_ci)";
3142fd4e5da5Sopenharmony_ci
3143fd4e5da5Sopenharmony_ci  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
3144fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
3145fd4e5da5Sopenharmony_ci            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
3146fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3147fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3148fd4e5da5Sopenharmony_ci      HasSubstr(
3149fd4e5da5Sopenharmony_ci          "IterationMultiple loop control operand must be greater than zero"));
3150fd4e5da5Sopenharmony_ci}
3151fd4e5da5Sopenharmony_ci
3152fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopMergeTargetsHeader) {
3153fd4e5da5Sopenharmony_ci  const std::string text = R"(
3154fd4e5da5Sopenharmony_ciOpCapability Shader
3155fd4e5da5Sopenharmony_ciOpCapability Linkage
3156fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3157fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3158fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3159fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3160fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3161fd4e5da5Sopenharmony_ci%fn = OpFunction %void None %void_fn
3162fd4e5da5Sopenharmony_ci%entry = OpLabel
3163fd4e5da5Sopenharmony_ciOpBranch %loop
3164fd4e5da5Sopenharmony_ci%loop = OpLabel
3165fd4e5da5Sopenharmony_ciOpLoopMerge %loop %continue None
3166fd4e5da5Sopenharmony_ciOpBranch %body
3167fd4e5da5Sopenharmony_ci%continue = OpLabel
3168fd4e5da5Sopenharmony_ciOpBranch %loop
3169fd4e5da5Sopenharmony_ci%body = OpLabel
3170fd4e5da5Sopenharmony_ciOpReturn
3171fd4e5da5Sopenharmony_ciOpFunctionEnd
3172fd4e5da5Sopenharmony_ci)";
3173fd4e5da5Sopenharmony_ci
3174fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3175fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
3176fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3177fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3178fd4e5da5Sopenharmony_ci      HasSubstr("Merge Block may not be the block containing the OpLoopMerge"));
3179fd4e5da5Sopenharmony_ci}
3180fd4e5da5Sopenharmony_ci
3181fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, InvalidSelectionExit) {
3182fd4e5da5Sopenharmony_ci  const std::string text = R"(
3183fd4e5da5Sopenharmony_ciOpCapability Shader
3184fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3185fd4e5da5Sopenharmony_ciOpEntryPoint Fragment %1 "main"
3186fd4e5da5Sopenharmony_ciOpExecutionMode %1 OriginUpperLeft
3187fd4e5da5Sopenharmony_ci%2 = OpTypeVoid
3188fd4e5da5Sopenharmony_ci%3 = OpTypeBool
3189fd4e5da5Sopenharmony_ci%4 = OpConstantTrue %3
3190fd4e5da5Sopenharmony_ci%5 = OpTypeFunction %2
3191fd4e5da5Sopenharmony_ci%1 = OpFunction %2 None %5
3192fd4e5da5Sopenharmony_ci%6 = OpLabel
3193fd4e5da5Sopenharmony_ciOpSelectionMerge %7 None
3194fd4e5da5Sopenharmony_ciOpBranchConditional %4 %7 %8
3195fd4e5da5Sopenharmony_ci%8 = OpLabel
3196fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
3197fd4e5da5Sopenharmony_ciOpBranchConditional %4 %10 %9
3198fd4e5da5Sopenharmony_ci%10 = OpLabel
3199fd4e5da5Sopenharmony_ciOpBranch %7
3200fd4e5da5Sopenharmony_ci%9 = OpLabel
3201fd4e5da5Sopenharmony_ciOpBranch %7
3202fd4e5da5Sopenharmony_ci%7 = OpLabel
3203fd4e5da5Sopenharmony_ciOpReturn
3204fd4e5da5Sopenharmony_ciOpFunctionEnd
3205fd4e5da5Sopenharmony_ci)";
3206fd4e5da5Sopenharmony_ci
3207fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3208fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3209fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3210fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3211fd4e5da5Sopenharmony_ci      HasSubstr("block <ID> '10[%10]' exits the selection headed by <ID> "
3212fd4e5da5Sopenharmony_ci                "'8[%8]', but not via a structured exit"));
3213fd4e5da5Sopenharmony_ci}
3214fd4e5da5Sopenharmony_ci
3215fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, InvalidLoopExit) {
3216fd4e5da5Sopenharmony_ci  const std::string text = R"(
3217fd4e5da5Sopenharmony_ciOpCapability Shader
3218fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3219fd4e5da5Sopenharmony_ciOpEntryPoint Fragment %1 "main"
3220fd4e5da5Sopenharmony_ciOpExecutionMode %1 OriginUpperLeft
3221fd4e5da5Sopenharmony_ci%2 = OpTypeVoid
3222fd4e5da5Sopenharmony_ci%3 = OpTypeBool
3223fd4e5da5Sopenharmony_ci%4 = OpConstantTrue %3
3224fd4e5da5Sopenharmony_ci%5 = OpTypeFunction %2
3225fd4e5da5Sopenharmony_ci%1 = OpFunction %2 None %5
3226fd4e5da5Sopenharmony_ci%6 = OpLabel
3227fd4e5da5Sopenharmony_ciOpSelectionMerge %7 None
3228fd4e5da5Sopenharmony_ciOpBranchConditional %4 %7 %8
3229fd4e5da5Sopenharmony_ci%8 = OpLabel
3230fd4e5da5Sopenharmony_ciOpLoopMerge %9 %10 None
3231fd4e5da5Sopenharmony_ciOpBranchConditional %4 %9 %11
3232fd4e5da5Sopenharmony_ci%11 = OpLabel
3233fd4e5da5Sopenharmony_ciOpBranchConditional %4 %7 %10
3234fd4e5da5Sopenharmony_ci%10 = OpLabel
3235fd4e5da5Sopenharmony_ciOpBranch %8
3236fd4e5da5Sopenharmony_ci%9 = OpLabel
3237fd4e5da5Sopenharmony_ciOpBranch %7
3238fd4e5da5Sopenharmony_ci%7 = OpLabel
3239fd4e5da5Sopenharmony_ciOpReturn
3240fd4e5da5Sopenharmony_ciOpFunctionEnd
3241fd4e5da5Sopenharmony_ci)";
3242fd4e5da5Sopenharmony_ci
3243fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3244fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3245fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
3246fd4e5da5Sopenharmony_ci              HasSubstr("block <ID> '11[%11]' exits the loop headed by <ID> "
3247fd4e5da5Sopenharmony_ci                        "'8[%8]', but not via a structured exit"));
3248fd4e5da5Sopenharmony_ci}
3249fd4e5da5Sopenharmony_ci
3250fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, InvalidContinueExit) {
3251fd4e5da5Sopenharmony_ci  const std::string text = R"(
3252fd4e5da5Sopenharmony_ciOpCapability Shader
3253fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3254fd4e5da5Sopenharmony_ciOpEntryPoint Fragment %1 "main"
3255fd4e5da5Sopenharmony_ciOpExecutionMode %1 OriginUpperLeft
3256fd4e5da5Sopenharmony_ci%2 = OpTypeVoid
3257fd4e5da5Sopenharmony_ci%3 = OpTypeBool
3258fd4e5da5Sopenharmony_ci%4 = OpConstantTrue %3
3259fd4e5da5Sopenharmony_ci%5 = OpTypeFunction %2
3260fd4e5da5Sopenharmony_ci%1 = OpFunction %2 None %5
3261fd4e5da5Sopenharmony_ci%6 = OpLabel
3262fd4e5da5Sopenharmony_ciOpSelectionMerge %7 None
3263fd4e5da5Sopenharmony_ciOpBranchConditional %4 %7 %8
3264fd4e5da5Sopenharmony_ci%8 = OpLabel
3265fd4e5da5Sopenharmony_ciOpLoopMerge %9 %10 None
3266fd4e5da5Sopenharmony_ciOpBranchConditional %4 %9 %10
3267fd4e5da5Sopenharmony_ci%10 = OpLabel
3268fd4e5da5Sopenharmony_ciOpBranch %11
3269fd4e5da5Sopenharmony_ci%11 = OpLabel
3270fd4e5da5Sopenharmony_ciOpBranchConditional %4 %8 %7
3271fd4e5da5Sopenharmony_ci%9 = OpLabel
3272fd4e5da5Sopenharmony_ciOpBranch %7
3273fd4e5da5Sopenharmony_ci%7 = OpLabel
3274fd4e5da5Sopenharmony_ciOpReturn
3275fd4e5da5Sopenharmony_ciOpFunctionEnd
3276fd4e5da5Sopenharmony_ci)";
3277fd4e5da5Sopenharmony_ci
3278fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3279fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3280fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3281fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3282fd4e5da5Sopenharmony_ci      HasSubstr("block <ID> '11[%11]' exits the continue headed by <ID> "
3283fd4e5da5Sopenharmony_ci                "'10[%10]', but not via a structured exit"));
3284fd4e5da5Sopenharmony_ci}
3285fd4e5da5Sopenharmony_ci
3286fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, InvalidSelectionExitBackedge) {
3287fd4e5da5Sopenharmony_ci  const std::string text = R"(
3288fd4e5da5Sopenharmony_ciOpCapability Shader
3289fd4e5da5Sopenharmony_ciOpCapability Linkage
3290fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3291fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
3292fd4e5da5Sopenharmony_ci%2 = OpTypeBool
3293fd4e5da5Sopenharmony_ci%3 = OpUndef %2
3294fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
3295fd4e5da5Sopenharmony_ci%5 = OpFunction %1 None %4
3296fd4e5da5Sopenharmony_ci%6 = OpLabel
3297fd4e5da5Sopenharmony_ciOpBranch %7
3298fd4e5da5Sopenharmony_ci%7 = OpLabel
3299fd4e5da5Sopenharmony_ciOpLoopMerge %8 %9 None
3300fd4e5da5Sopenharmony_ciOpBranchConditional %3 %8 %9
3301fd4e5da5Sopenharmony_ci%9 = OpLabel
3302fd4e5da5Sopenharmony_ciOpSelectionMerge %10 None
3303fd4e5da5Sopenharmony_ciOpBranchConditional %3 %11 %12
3304fd4e5da5Sopenharmony_ci%11 = OpLabel
3305fd4e5da5Sopenharmony_ciOpBranch %13
3306fd4e5da5Sopenharmony_ci%12 = OpLabel
3307fd4e5da5Sopenharmony_ciOpBranch %13
3308fd4e5da5Sopenharmony_ci%13 = OpLabel
3309fd4e5da5Sopenharmony_ciOpBranch %7
3310fd4e5da5Sopenharmony_ci%10 = OpLabel
3311fd4e5da5Sopenharmony_ciOpUnreachable
3312fd4e5da5Sopenharmony_ci%8 = OpLabel
3313fd4e5da5Sopenharmony_ciOpReturn
3314fd4e5da5Sopenharmony_ciOpFunctionEnd
3315fd4e5da5Sopenharmony_ci)";
3316fd4e5da5Sopenharmony_ci
3317fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3318fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3319fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3320fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3321fd4e5da5Sopenharmony_ci      HasSubstr(
3322fd4e5da5Sopenharmony_ci          "The continue construct with the continue target '9[%9]' is not "
3323fd4e5da5Sopenharmony_ci          "structurally post dominated by the back-edge block '13[%13]'"));
3324fd4e5da5Sopenharmony_ci}
3325fd4e5da5Sopenharmony_ci
3326fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BreakFromSwitch) {
3327fd4e5da5Sopenharmony_ci  const std::string text = R"(
3328fd4e5da5Sopenharmony_ciOpCapability Shader
3329fd4e5da5Sopenharmony_ciOpCapability Linkage
3330fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3331fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
3332fd4e5da5Sopenharmony_ci%2 = OpTypeBool
3333fd4e5da5Sopenharmony_ci%3 = OpTypeInt 32 0
3334fd4e5da5Sopenharmony_ci%4 = OpUndef %2
3335fd4e5da5Sopenharmony_ci%5 = OpUndef %3
3336fd4e5da5Sopenharmony_ci%6 = OpTypeFunction %1
3337fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %6
3338fd4e5da5Sopenharmony_ci%8 = OpLabel
3339fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
3340fd4e5da5Sopenharmony_ciOpSwitch %5 %9 0 %10
3341fd4e5da5Sopenharmony_ci%10 = OpLabel
3342fd4e5da5Sopenharmony_ciOpSelectionMerge %11 None
3343fd4e5da5Sopenharmony_ciOpBranchConditional %4 %11 %12
3344fd4e5da5Sopenharmony_ci%12 = OpLabel
3345fd4e5da5Sopenharmony_ciOpBranch %9
3346fd4e5da5Sopenharmony_ci%11 = OpLabel
3347fd4e5da5Sopenharmony_ciOpBranch %9
3348fd4e5da5Sopenharmony_ci%9 = OpLabel
3349fd4e5da5Sopenharmony_ciOpReturn
3350fd4e5da5Sopenharmony_ciOpFunctionEnd
3351fd4e5da5Sopenharmony_ci)";
3352fd4e5da5Sopenharmony_ci
3353fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3354fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3355fd4e5da5Sopenharmony_ci}
3356fd4e5da5Sopenharmony_ci
3357fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, InvalidBreakFromSwitch) {
3358fd4e5da5Sopenharmony_ci  const std::string text = R"(
3359fd4e5da5Sopenharmony_ciOpCapability Shader
3360fd4e5da5Sopenharmony_ciOpCapability Linkage
3361fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3362fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
3363fd4e5da5Sopenharmony_ci%2 = OpTypeBool
3364fd4e5da5Sopenharmony_ci%3 = OpTypeInt 32 0
3365fd4e5da5Sopenharmony_ci%4 = OpUndef %2
3366fd4e5da5Sopenharmony_ci%5 = OpUndef %3
3367fd4e5da5Sopenharmony_ci%6 = OpTypeFunction %1
3368fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %6
3369fd4e5da5Sopenharmony_ci%8 = OpLabel
3370fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
3371fd4e5da5Sopenharmony_ciOpSwitch %5 %9 0 %10
3372fd4e5da5Sopenharmony_ci%10 = OpLabel
3373fd4e5da5Sopenharmony_ciOpSelectionMerge %11 None
3374fd4e5da5Sopenharmony_ciOpSwitch %5 %11 0 %12
3375fd4e5da5Sopenharmony_ci%12 = OpLabel
3376fd4e5da5Sopenharmony_ciOpBranch %9
3377fd4e5da5Sopenharmony_ci%11 = OpLabel
3378fd4e5da5Sopenharmony_ciOpBranch %9
3379fd4e5da5Sopenharmony_ci%9 = OpLabel
3380fd4e5da5Sopenharmony_ciOpReturn
3381fd4e5da5Sopenharmony_ciOpFunctionEnd
3382fd4e5da5Sopenharmony_ci)";
3383fd4e5da5Sopenharmony_ci
3384fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3385fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3386fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3387fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3388fd4e5da5Sopenharmony_ci      HasSubstr("block <ID> '12[%12]' exits the selection headed by <ID> "
3389fd4e5da5Sopenharmony_ci                "'10[%10]', but not via a structured exit"));
3390fd4e5da5Sopenharmony_ci}
3391fd4e5da5Sopenharmony_ci
3392fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BreakToOuterSwitch) {
3393fd4e5da5Sopenharmony_ci  const std::string text = R"(
3394fd4e5da5Sopenharmony_ciOpCapability Shader
3395fd4e5da5Sopenharmony_ciOpCapability Linkage
3396fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3397fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
3398fd4e5da5Sopenharmony_ci%2 = OpTypeBool
3399fd4e5da5Sopenharmony_ci%3 = OpTypeInt 32 0
3400fd4e5da5Sopenharmony_ci%4 = OpUndef %2
3401fd4e5da5Sopenharmony_ci%5 = OpUndef %3
3402fd4e5da5Sopenharmony_ci%6 = OpTypeFunction %1
3403fd4e5da5Sopenharmony_ci%7 = OpFunction %1 None %6
3404fd4e5da5Sopenharmony_ci%8 = OpLabel
3405fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
3406fd4e5da5Sopenharmony_ciOpSwitch %5 %9 0 %10
3407fd4e5da5Sopenharmony_ci%10 = OpLabel
3408fd4e5da5Sopenharmony_ciOpSelectionMerge %11 None
3409fd4e5da5Sopenharmony_ciOpSwitch %5 %11 0 %12
3410fd4e5da5Sopenharmony_ci%12 = OpLabel
3411fd4e5da5Sopenharmony_ciOpSelectionMerge %13 None
3412fd4e5da5Sopenharmony_ciOpBranchConditional %4 %13 %14
3413fd4e5da5Sopenharmony_ci%14 = OpLabel
3414fd4e5da5Sopenharmony_ciOpBranch %9
3415fd4e5da5Sopenharmony_ci%13 = OpLabel
3416fd4e5da5Sopenharmony_ciOpBranch %11
3417fd4e5da5Sopenharmony_ci%11 = OpLabel
3418fd4e5da5Sopenharmony_ciOpBranch %9
3419fd4e5da5Sopenharmony_ci%9 = OpLabel
3420fd4e5da5Sopenharmony_ciOpReturn
3421fd4e5da5Sopenharmony_ciOpFunctionEnd
3422fd4e5da5Sopenharmony_ci)";
3423fd4e5da5Sopenharmony_ci
3424fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3425fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3426fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3427fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3428fd4e5da5Sopenharmony_ci      HasSubstr("block <ID> '14[%14]' exits the selection headed by <ID> "
3429fd4e5da5Sopenharmony_ci                "'10[%10]', but not via a structured exit"));
3430fd4e5da5Sopenharmony_ci}
3431fd4e5da5Sopenharmony_ci
3432fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BreakToOuterLoop) {
3433fd4e5da5Sopenharmony_ci  const std::string text = R"(
3434fd4e5da5Sopenharmony_ciOpCapability Shader
3435fd4e5da5Sopenharmony_ciOpCapability Linkage
3436fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3437fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
3438fd4e5da5Sopenharmony_ci%2 = OpTypeBool
3439fd4e5da5Sopenharmony_ci%3 = OpUndef %2
3440fd4e5da5Sopenharmony_ci%4 = OpTypeFunction %1
3441fd4e5da5Sopenharmony_ci%5 = OpFunction %1 None %4
3442fd4e5da5Sopenharmony_ci%6 = OpLabel
3443fd4e5da5Sopenharmony_ciOpBranch %7
3444fd4e5da5Sopenharmony_ci%7 = OpLabel
3445fd4e5da5Sopenharmony_ciOpLoopMerge %8 %9 None
3446fd4e5da5Sopenharmony_ciOpBranch %10
3447fd4e5da5Sopenharmony_ci%10 = OpLabel
3448fd4e5da5Sopenharmony_ciOpLoopMerge %11 %12 None
3449fd4e5da5Sopenharmony_ciOpBranch %13
3450fd4e5da5Sopenharmony_ci%13 = OpLabel
3451fd4e5da5Sopenharmony_ciOpSelectionMerge %14 None
3452fd4e5da5Sopenharmony_ciOpBranchConditional %3 %14 %15
3453fd4e5da5Sopenharmony_ci%15 = OpLabel
3454fd4e5da5Sopenharmony_ciOpBranch %8
3455fd4e5da5Sopenharmony_ci%14 = OpLabel
3456fd4e5da5Sopenharmony_ciOpBranch %12
3457fd4e5da5Sopenharmony_ci%12 = OpLabel
3458fd4e5da5Sopenharmony_ciOpBranchConditional %3 %10 %11
3459fd4e5da5Sopenharmony_ci%11 = OpLabel
3460fd4e5da5Sopenharmony_ciOpBranch %9
3461fd4e5da5Sopenharmony_ci%9 = OpLabel
3462fd4e5da5Sopenharmony_ciOpBranchConditional %3 %7 %8
3463fd4e5da5Sopenharmony_ci%8 = OpLabel
3464fd4e5da5Sopenharmony_ciOpReturn
3465fd4e5da5Sopenharmony_ciOpFunctionEnd
3466fd4e5da5Sopenharmony_ci)";
3467fd4e5da5Sopenharmony_ci
3468fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3469fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3470fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
3471fd4e5da5Sopenharmony_ci              HasSubstr("block <ID> '15[%15]' exits the loop headed by <ID> "
3472fd4e5da5Sopenharmony_ci                        "'10[%10]', but not via a structured exit"));
3473fd4e5da5Sopenharmony_ci}
3474fd4e5da5Sopenharmony_ci
3475fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ContinueFromNestedSelection) {
3476fd4e5da5Sopenharmony_ci  const std::string text = R"(
3477fd4e5da5Sopenharmony_ciOpCapability Shader
3478fd4e5da5Sopenharmony_ciOpCapability Linkage
3479fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3480fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3481fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3482fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3483fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3484fd4e5da5Sopenharmony_ci%4 = OpFunction %void None %void_fn
3485fd4e5da5Sopenharmony_ci%5 = OpLabel
3486fd4e5da5Sopenharmony_ciOpBranch %48
3487fd4e5da5Sopenharmony_ci%48 = OpLabel
3488fd4e5da5Sopenharmony_ciOpLoopMerge %47 %50 None
3489fd4e5da5Sopenharmony_ciOpBranch %10
3490fd4e5da5Sopenharmony_ci%10 = OpLabel
3491fd4e5da5Sopenharmony_ciOpLoopMerge %12 %37 None
3492fd4e5da5Sopenharmony_ciOpBranchConditional %undef %11 %12
3493fd4e5da5Sopenharmony_ci%11 = OpLabel
3494fd4e5da5Sopenharmony_ciOpSelectionMerge %31 None
3495fd4e5da5Sopenharmony_ciOpBranchConditional %undef %30 %31
3496fd4e5da5Sopenharmony_ci%30 = OpLabel
3497fd4e5da5Sopenharmony_ciOpSelectionMerge %38 None
3498fd4e5da5Sopenharmony_ciOpBranchConditional %undef %36 %38
3499fd4e5da5Sopenharmony_ci%36 = OpLabel
3500fd4e5da5Sopenharmony_ciOpBranch %38
3501fd4e5da5Sopenharmony_ci%38 = OpLabel
3502fd4e5da5Sopenharmony_ciOpBranch %37
3503fd4e5da5Sopenharmony_ci%37 = OpLabel
3504fd4e5da5Sopenharmony_ciOpBranch %10
3505fd4e5da5Sopenharmony_ci%31 = OpLabel
3506fd4e5da5Sopenharmony_ciOpBranch %12
3507fd4e5da5Sopenharmony_ci%12 = OpLabel
3508fd4e5da5Sopenharmony_ciOpSelectionMerge %55 None
3509fd4e5da5Sopenharmony_ciOpBranchConditional %undef %47 %55
3510fd4e5da5Sopenharmony_ci%55 = OpLabel
3511fd4e5da5Sopenharmony_ciOpBranch %47
3512fd4e5da5Sopenharmony_ci%50 = OpLabel
3513fd4e5da5Sopenharmony_ciOpBranch %48
3514fd4e5da5Sopenharmony_ci%47 = OpLabel
3515fd4e5da5Sopenharmony_ciOpReturn
3516fd4e5da5Sopenharmony_ciOpFunctionEnd
3517fd4e5da5Sopenharmony_ci)";
3518fd4e5da5Sopenharmony_ci
3519fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3520fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3521fd4e5da5Sopenharmony_ci}
3522fd4e5da5Sopenharmony_ci
3523fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeConditionalBranchBad) {
3524fd4e5da5Sopenharmony_ci  const std::string text = R"(
3525fd4e5da5Sopenharmony_ciOpCapability Shader
3526fd4e5da5Sopenharmony_ciOpCapability Linkage
3527fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3528fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3529fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3530fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3531fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3532fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3533fd4e5da5Sopenharmony_ci%entry = OpLabel
3534fd4e5da5Sopenharmony_ciOpBranchConditional %undef %then %else
3535fd4e5da5Sopenharmony_ci%then = OpLabel
3536fd4e5da5Sopenharmony_ciOpReturn
3537fd4e5da5Sopenharmony_ci%else = OpLabel
3538fd4e5da5Sopenharmony_ciOpReturn
3539fd4e5da5Sopenharmony_ciOpFunctionEnd
3540fd4e5da5Sopenharmony_ci)";
3541fd4e5da5Sopenharmony_ci
3542fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3543fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3544fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
3545fd4e5da5Sopenharmony_ci}
3546fd4e5da5Sopenharmony_ci
3547fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, LoopConditionalBranchWithoutExitBad) {
3548fd4e5da5Sopenharmony_ci  const std::string text = R"(
3549fd4e5da5Sopenharmony_ciOpCapability Shader
3550fd4e5da5Sopenharmony_ciOpCapability Linkage
3551fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3552fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3553fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3554fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3555fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3556fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3557fd4e5da5Sopenharmony_ci%entry = OpLabel
3558fd4e5da5Sopenharmony_ciOpBranch %loop
3559fd4e5da5Sopenharmony_ci%loop = OpLabel
3560fd4e5da5Sopenharmony_ciOpLoopMerge %exit %continue None
3561fd4e5da5Sopenharmony_ciOpBranchConditional %undef %then %else
3562fd4e5da5Sopenharmony_ci%then = OpLabel
3563fd4e5da5Sopenharmony_ciOpBranch %continue
3564fd4e5da5Sopenharmony_ci%else = OpLabel
3565fd4e5da5Sopenharmony_ciOpBranch %exit
3566fd4e5da5Sopenharmony_ci%continue = OpLabel
3567fd4e5da5Sopenharmony_ciOpBranch %loop
3568fd4e5da5Sopenharmony_ci%exit = OpLabel
3569fd4e5da5Sopenharmony_ciOpReturn
3570fd4e5da5Sopenharmony_ciOpFunctionEnd
3571fd4e5da5Sopenharmony_ci)";
3572fd4e5da5Sopenharmony_ci
3573fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3574fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3575fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
3576fd4e5da5Sopenharmony_ci}
3577fd4e5da5Sopenharmony_ci
3578fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeSwitchBad) {
3579fd4e5da5Sopenharmony_ci  const std::string text = R"(
3580fd4e5da5Sopenharmony_ciOpCapability Shader
3581fd4e5da5Sopenharmony_ciOpCapability Linkage
3582fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3583fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3584fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3585fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
3586fd4e5da5Sopenharmony_ci%undef = OpUndef %int
3587fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3588fd4e5da5Sopenharmony_ci%entry = OpLabel
3589fd4e5da5Sopenharmony_ciOpSwitch %undef %then 0 %else
3590fd4e5da5Sopenharmony_ci%then = OpLabel
3591fd4e5da5Sopenharmony_ciOpReturn
3592fd4e5da5Sopenharmony_ci%else = OpLabel
3593fd4e5da5Sopenharmony_ciOpReturn
3594fd4e5da5Sopenharmony_ciOpFunctionEnd
3595fd4e5da5Sopenharmony_ci)";
3596fd4e5da5Sopenharmony_ci
3597fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3598fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3599fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3600fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3601fd4e5da5Sopenharmony_ci      HasSubstr(
3602fd4e5da5Sopenharmony_ci          "OpSwitch must be preceded by an OpSelectionMerge instruction"));
3603fd4e5da5Sopenharmony_ci}
3604fd4e5da5Sopenharmony_ci
3605fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeSwitchBad2) {
3606fd4e5da5Sopenharmony_ci  const std::string text = R"(
3607fd4e5da5Sopenharmony_ciOpCapability Shader
3608fd4e5da5Sopenharmony_ciOpCapability Linkage
3609fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3610fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3611fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3612fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
3613fd4e5da5Sopenharmony_ci%undef = OpUndef %int
3614fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3615fd4e5da5Sopenharmony_ci%entry = OpLabel
3616fd4e5da5Sopenharmony_ciOpSwitch %undef %then 0 %then 1 %then 2 %else
3617fd4e5da5Sopenharmony_ci%then = OpLabel
3618fd4e5da5Sopenharmony_ciOpReturn
3619fd4e5da5Sopenharmony_ci%else = OpLabel
3620fd4e5da5Sopenharmony_ciOpReturn
3621fd4e5da5Sopenharmony_ciOpFunctionEnd
3622fd4e5da5Sopenharmony_ci)";
3623fd4e5da5Sopenharmony_ci
3624fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3625fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3626fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3627fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3628fd4e5da5Sopenharmony_ci      HasSubstr(
3629fd4e5da5Sopenharmony_ci          "OpSwitch must be preceded by an OpSelectionMerge instruction"));
3630fd4e5da5Sopenharmony_ci}
3631fd4e5da5Sopenharmony_ci
3632fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) {
3633fd4e5da5Sopenharmony_ci  const std::string text = R"(
3634fd4e5da5Sopenharmony_ciOpCapability Shader
3635fd4e5da5Sopenharmony_ciOpCapability Linkage
3636fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3637fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3638fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3639fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3640fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3641fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3642fd4e5da5Sopenharmony_ci%entry = OpLabel
3643fd4e5da5Sopenharmony_ciOpSelectionMerge %b3 None
3644fd4e5da5Sopenharmony_ciOpBranchConditional %undef %b1 %b2
3645fd4e5da5Sopenharmony_ci%b1 = OpLabel
3646fd4e5da5Sopenharmony_ciOpBranchConditional %undef %b2 %b3
3647fd4e5da5Sopenharmony_ci%b2 = OpLabel
3648fd4e5da5Sopenharmony_ciOpBranch %b3
3649fd4e5da5Sopenharmony_ci%b3 = OpLabel
3650fd4e5da5Sopenharmony_ciOpReturn
3651fd4e5da5Sopenharmony_ciOpFunctionEnd
3652fd4e5da5Sopenharmony_ci)";
3653fd4e5da5Sopenharmony_ci
3654fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3655fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3656fd4e5da5Sopenharmony_ci}
3657fd4e5da5Sopenharmony_ci
3658fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeSameTargetConditionalBranchGood) {
3659fd4e5da5Sopenharmony_ci  const std::string text = R"(
3660fd4e5da5Sopenharmony_ciOpCapability Shader
3661fd4e5da5Sopenharmony_ciOpCapability Linkage
3662fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3663fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3664fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3665fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3666fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3667fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3668fd4e5da5Sopenharmony_ci%entry = OpLabel
3669fd4e5da5Sopenharmony_ciOpBranchConditional %undef %then %then
3670fd4e5da5Sopenharmony_ci%then = OpLabel
3671fd4e5da5Sopenharmony_ciOpReturn
3672fd4e5da5Sopenharmony_ciOpFunctionEnd
3673fd4e5da5Sopenharmony_ci)";
3674fd4e5da5Sopenharmony_ci
3675fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3676fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3677fd4e5da5Sopenharmony_ci}
3678fd4e5da5Sopenharmony_ci
3679fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeOneTargetSwitchBad) {
3680fd4e5da5Sopenharmony_ci  const std::string text = R"(
3681fd4e5da5Sopenharmony_ciOpCapability Shader
3682fd4e5da5Sopenharmony_ciOpCapability Linkage
3683fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3684fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3685fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3686fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
3687fd4e5da5Sopenharmony_ci%undef = OpUndef %int
3688fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3689fd4e5da5Sopenharmony_ci%entry = OpLabel
3690fd4e5da5Sopenharmony_ciOpSwitch %undef %then 0 %then 1 %then
3691fd4e5da5Sopenharmony_ci%then = OpLabel
3692fd4e5da5Sopenharmony_ciOpReturn
3693fd4e5da5Sopenharmony_ciOpFunctionEnd
3694fd4e5da5Sopenharmony_ci)";
3695fd4e5da5Sopenharmony_ci
3696fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3697fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3698fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3699fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3700fd4e5da5Sopenharmony_ci      HasSubstr(
3701fd4e5da5Sopenharmony_ci          "OpSwitch must be preceded by an OpSelectionMerge instruction"));
3702fd4e5da5Sopenharmony_ci}
3703fd4e5da5Sopenharmony_ci
3704fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchBad) {
3705fd4e5da5Sopenharmony_ci  const std::string text = R"(
3706fd4e5da5Sopenharmony_ciOpCapability Shader
3707fd4e5da5Sopenharmony_ciOpCapability Linkage
3708fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3709fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3710fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3711fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
3712fd4e5da5Sopenharmony_ci%undef_int = OpUndef %int
3713fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3714fd4e5da5Sopenharmony_ci%undef_bool = OpUndef %bool
3715fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3716fd4e5da5Sopenharmony_ci%entry = OpLabel
3717fd4e5da5Sopenharmony_ciOpSelectionMerge %merge None
3718fd4e5da5Sopenharmony_ciOpBranchConditional %undef_bool %merge %b1
3719fd4e5da5Sopenharmony_ci%b1 = OpLabel
3720fd4e5da5Sopenharmony_ciOpSwitch %undef_int %b2 0 %b2 1 %merge 2 %b2
3721fd4e5da5Sopenharmony_ci%b2 = OpLabel
3722fd4e5da5Sopenharmony_ciOpBranch %merge
3723fd4e5da5Sopenharmony_ci%merge = OpLabel
3724fd4e5da5Sopenharmony_ciOpReturn
3725fd4e5da5Sopenharmony_ciOpFunctionEnd
3726fd4e5da5Sopenharmony_ci)";
3727fd4e5da5Sopenharmony_ci
3728fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3729fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3730fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3731fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3732fd4e5da5Sopenharmony_ci      HasSubstr(
3733fd4e5da5Sopenharmony_ci          "OpSwitch must be preceded by an OpSelectionMerge instruction"));
3734fd4e5da5Sopenharmony_ci}
3735fd4e5da5Sopenharmony_ci
3736fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeLoopBreakGood) {
3737fd4e5da5Sopenharmony_ci  const std::string text = R"(
3738fd4e5da5Sopenharmony_ciOpCapability Shader
3739fd4e5da5Sopenharmony_ciOpCapability Linkage
3740fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3741fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3742fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3743fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3744fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3745fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3746fd4e5da5Sopenharmony_ci%entry = OpLabel
3747fd4e5da5Sopenharmony_ciOpBranch %loop
3748fd4e5da5Sopenharmony_ci%loop = OpLabel
3749fd4e5da5Sopenharmony_ciOpLoopMerge %exit %continue None
3750fd4e5da5Sopenharmony_ciOpBranch %body
3751fd4e5da5Sopenharmony_ci%body = OpLabel
3752fd4e5da5Sopenharmony_ciOpBranchConditional %undef %body2 %exit
3753fd4e5da5Sopenharmony_ci%body2 = OpLabel
3754fd4e5da5Sopenharmony_ciOpBranch %continue
3755fd4e5da5Sopenharmony_ci%continue = OpLabel
3756fd4e5da5Sopenharmony_ciOpBranch %loop
3757fd4e5da5Sopenharmony_ci%exit = OpLabel
3758fd4e5da5Sopenharmony_ciOpReturn
3759fd4e5da5Sopenharmony_ciOpFunctionEnd
3760fd4e5da5Sopenharmony_ci)";
3761fd4e5da5Sopenharmony_ci
3762fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3763fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3764fd4e5da5Sopenharmony_ci}
3765fd4e5da5Sopenharmony_ci
3766fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeLoopContinueGood) {
3767fd4e5da5Sopenharmony_ci  const std::string text = R"(
3768fd4e5da5Sopenharmony_ciOpCapability Shader
3769fd4e5da5Sopenharmony_ciOpCapability Linkage
3770fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3771fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3772fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3773fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3774fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3775fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3776fd4e5da5Sopenharmony_ci%entry = OpLabel
3777fd4e5da5Sopenharmony_ciOpBranch %loop
3778fd4e5da5Sopenharmony_ci%loop = OpLabel
3779fd4e5da5Sopenharmony_ciOpLoopMerge %exit %continue None
3780fd4e5da5Sopenharmony_ciOpBranch %body
3781fd4e5da5Sopenharmony_ci%body = OpLabel
3782fd4e5da5Sopenharmony_ciOpBranchConditional %undef %body2 %continue
3783fd4e5da5Sopenharmony_ci%body2 = OpLabel
3784fd4e5da5Sopenharmony_ciOpBranch %continue
3785fd4e5da5Sopenharmony_ci%continue = OpLabel
3786fd4e5da5Sopenharmony_ciOpBranch %loop
3787fd4e5da5Sopenharmony_ci%exit = OpLabel
3788fd4e5da5Sopenharmony_ciOpReturn
3789fd4e5da5Sopenharmony_ciOpFunctionEnd
3790fd4e5da5Sopenharmony_ci)";
3791fd4e5da5Sopenharmony_ci
3792fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3793fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3794fd4e5da5Sopenharmony_ci}
3795fd4e5da5Sopenharmony_ci
3796fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeSwitchBreakGood) {
3797fd4e5da5Sopenharmony_ci  const std::string text = R"(
3798fd4e5da5Sopenharmony_ciOpCapability Shader
3799fd4e5da5Sopenharmony_ciOpCapability Linkage
3800fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3801fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3802fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3803fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3804fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3805fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
3806fd4e5da5Sopenharmony_ci%int_0 = OpConstant %int 0
3807fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3808fd4e5da5Sopenharmony_ci%entry = OpLabel
3809fd4e5da5Sopenharmony_ciOpSelectionMerge %merge None
3810fd4e5da5Sopenharmony_ciOpSwitch %int_0 %merge 1 %b1
3811fd4e5da5Sopenharmony_ci%b1 = OpLabel
3812fd4e5da5Sopenharmony_ciOpBranchConditional %undef %merge %b2
3813fd4e5da5Sopenharmony_ci%b2 = OpLabel
3814fd4e5da5Sopenharmony_ciOpBranch %merge
3815fd4e5da5Sopenharmony_ci%merge = OpLabel
3816fd4e5da5Sopenharmony_ciOpReturn
3817fd4e5da5Sopenharmony_ciOpFunctionEnd
3818fd4e5da5Sopenharmony_ci)";
3819fd4e5da5Sopenharmony_ci
3820fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3821fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3822fd4e5da5Sopenharmony_ci}
3823fd4e5da5Sopenharmony_ci
3824fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeSwitchFallThroughGood) {
3825fd4e5da5Sopenharmony_ci  const std::string text = R"(
3826fd4e5da5Sopenharmony_ciOpCapability Shader
3827fd4e5da5Sopenharmony_ciOpCapability Linkage
3828fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3829fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3830fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3831fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3832fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3833fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
3834fd4e5da5Sopenharmony_ci%int_0 = OpConstant %int 0
3835fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3836fd4e5da5Sopenharmony_ci%entry = OpLabel
3837fd4e5da5Sopenharmony_ciOpSelectionMerge %merge None
3838fd4e5da5Sopenharmony_ciOpSwitch %int_0 %b1 1 %b2
3839fd4e5da5Sopenharmony_ci%b1 = OpLabel
3840fd4e5da5Sopenharmony_ciOpBranchConditional %undef %b3 %b2
3841fd4e5da5Sopenharmony_ci%b2 = OpLabel
3842fd4e5da5Sopenharmony_ciOpBranch %merge
3843fd4e5da5Sopenharmony_ci%b3 = OpLabel
3844fd4e5da5Sopenharmony_ciOpBranch %merge
3845fd4e5da5Sopenharmony_ci%merge = OpLabel
3846fd4e5da5Sopenharmony_ciOpReturn
3847fd4e5da5Sopenharmony_ciOpFunctionEnd
3848fd4e5da5Sopenharmony_ci)";
3849fd4e5da5Sopenharmony_ci
3850fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3851fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3852fd4e5da5Sopenharmony_ci}
3853fd4e5da5Sopenharmony_ci
3854fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeInALoopBad) {
3855fd4e5da5Sopenharmony_ci  const std::string text = R"(
3856fd4e5da5Sopenharmony_ciOpCapability Shader
3857fd4e5da5Sopenharmony_ciOpCapability Linkage
3858fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3859fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3860fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3861fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3862fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3863fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3864fd4e5da5Sopenharmony_ci%entry = OpLabel
3865fd4e5da5Sopenharmony_ciOpBranch %loop
3866fd4e5da5Sopenharmony_ci%loop = OpLabel
3867fd4e5da5Sopenharmony_ciOpLoopMerge %exit %continue None
3868fd4e5da5Sopenharmony_ciOpBranch %body
3869fd4e5da5Sopenharmony_ci%body = OpLabel
3870fd4e5da5Sopenharmony_ciOpBranchConditional %undef %b1 %b2
3871fd4e5da5Sopenharmony_ci%b1 = OpLabel
3872fd4e5da5Sopenharmony_ciOpBranch %exit
3873fd4e5da5Sopenharmony_ci%b2 = OpLabel
3874fd4e5da5Sopenharmony_ciOpBranch %continue
3875fd4e5da5Sopenharmony_ci%continue = OpLabel
3876fd4e5da5Sopenharmony_ciOpBranch %loop
3877fd4e5da5Sopenharmony_ci%exit = OpLabel
3878fd4e5da5Sopenharmony_ciOpReturn
3879fd4e5da5Sopenharmony_ciOpFunctionEnd
3880fd4e5da5Sopenharmony_ci)";
3881fd4e5da5Sopenharmony_ci
3882fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3883fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3884fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
3885fd4e5da5Sopenharmony_ci}
3886fd4e5da5Sopenharmony_ci
3887fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, MissingMergeCrissCrossBad) {
3888fd4e5da5Sopenharmony_ci  const std::string text = R"(
3889fd4e5da5Sopenharmony_ciOpCapability Shader
3890fd4e5da5Sopenharmony_ciOpCapability Linkage
3891fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3892fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3893fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3894fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3895fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3896fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3897fd4e5da5Sopenharmony_ci%entry = OpLabel
3898fd4e5da5Sopenharmony_ciOpSelectionMerge %merge None
3899fd4e5da5Sopenharmony_ciOpBranchConditional %undef %b1 %b2
3900fd4e5da5Sopenharmony_ci%b1 = OpLabel
3901fd4e5da5Sopenharmony_ciOpBranchConditional %undef %b3 %b4
3902fd4e5da5Sopenharmony_ci%b2 = OpLabel
3903fd4e5da5Sopenharmony_ciOpBranchConditional %undef %b3 %b4
3904fd4e5da5Sopenharmony_ci%b3 = OpLabel
3905fd4e5da5Sopenharmony_ciOpBranch %merge
3906fd4e5da5Sopenharmony_ci%b4 = OpLabel
3907fd4e5da5Sopenharmony_ciOpBranch %merge
3908fd4e5da5Sopenharmony_ci%merge = OpLabel
3909fd4e5da5Sopenharmony_ciOpReturn
3910fd4e5da5Sopenharmony_ciOpFunctionEnd
3911fd4e5da5Sopenharmony_ci)";
3912fd4e5da5Sopenharmony_ci
3913fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3914fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3915fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
3916fd4e5da5Sopenharmony_ci}
3917fd4e5da5Sopenharmony_ci
3918fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ContinueCannotBeSelectionMergeTarget) {
3919fd4e5da5Sopenharmony_ci  const std::string text = R"(
3920fd4e5da5Sopenharmony_ciOpCapability Shader
3921fd4e5da5Sopenharmony_ciOpCapability Linkage
3922fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3923fd4e5da5Sopenharmony_ciOpName %loop "loop"
3924fd4e5da5Sopenharmony_ciOpName %continue "continue"
3925fd4e5da5Sopenharmony_ciOpName %body "body"
3926fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3927fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3928fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3929fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3930fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3931fd4e5da5Sopenharmony_ci%entry = OpLabel
3932fd4e5da5Sopenharmony_ciOpBranch %loop
3933fd4e5da5Sopenharmony_ci%loop = OpLabel
3934fd4e5da5Sopenharmony_ciOpLoopMerge %exit %continue None
3935fd4e5da5Sopenharmony_ciOpBranch %body
3936fd4e5da5Sopenharmony_ci%body = OpLabel
3937fd4e5da5Sopenharmony_ciOpSelectionMerge %continue None
3938fd4e5da5Sopenharmony_ciOpBranchConditional %undef %exit %continue
3939fd4e5da5Sopenharmony_ci%continue = OpLabel
3940fd4e5da5Sopenharmony_ciOpBranch %loop
3941fd4e5da5Sopenharmony_ci%exit = OpLabel
3942fd4e5da5Sopenharmony_ciOpReturn
3943fd4e5da5Sopenharmony_ciOpFunctionEnd
3944fd4e5da5Sopenharmony_ci)";
3945fd4e5da5Sopenharmony_ci
3946fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3947fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3948fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3949fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3950fd4e5da5Sopenharmony_ci      HasSubstr("Header block '3[%body]' is contained in the loop construct "
3951fd4e5da5Sopenharmony_ci                "headed by "
3952fd4e5da5Sopenharmony_ci                "'1[%loop]', but its merge block '2[%continue]' is not"));
3953fd4e5da5Sopenharmony_ci}
3954fd4e5da5Sopenharmony_ci
3955fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) {
3956fd4e5da5Sopenharmony_ci  const std::string text = R"(
3957fd4e5da5Sopenharmony_ciOpCapability Shader
3958fd4e5da5Sopenharmony_ciOpCapability Linkage
3959fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3960fd4e5da5Sopenharmony_ciOpName %loop "loop"
3961fd4e5da5Sopenharmony_ciOpName %continue "continue"
3962fd4e5da5Sopenharmony_ciOpName %inner "inner"
3963fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3964fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
3965fd4e5da5Sopenharmony_ci%bool = OpTypeBool
3966fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
3967fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
3968fd4e5da5Sopenharmony_ci%entry = OpLabel
3969fd4e5da5Sopenharmony_ciOpBranch %loop
3970fd4e5da5Sopenharmony_ci%loop = OpLabel
3971fd4e5da5Sopenharmony_ciOpLoopMerge %exit %continue None
3972fd4e5da5Sopenharmony_ciOpBranchConditional %undef %exit %inner
3973fd4e5da5Sopenharmony_ci%inner = OpLabel
3974fd4e5da5Sopenharmony_ciOpLoopMerge %continue %inner None
3975fd4e5da5Sopenharmony_ciOpBranchConditional %undef %inner %continue
3976fd4e5da5Sopenharmony_ci%continue = OpLabel
3977fd4e5da5Sopenharmony_ciOpBranch %loop
3978fd4e5da5Sopenharmony_ci%exit = OpLabel
3979fd4e5da5Sopenharmony_ciOpReturn
3980fd4e5da5Sopenharmony_ciOpFunctionEnd
3981fd4e5da5Sopenharmony_ci)";
3982fd4e5da5Sopenharmony_ci
3983fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
3984fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3985fd4e5da5Sopenharmony_ci  EXPECT_THAT(
3986fd4e5da5Sopenharmony_ci      getDiagnosticString(),
3987fd4e5da5Sopenharmony_ci      HasSubstr("Header block '3[%inner]' is contained in the loop construct "
3988fd4e5da5Sopenharmony_ci                "headed by "
3989fd4e5da5Sopenharmony_ci                "'1[%loop]', but its merge block '2[%continue]' is not"));
3990fd4e5da5Sopenharmony_ci}
3991fd4e5da5Sopenharmony_ci
3992fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge) {
3993fd4e5da5Sopenharmony_ci  const std::string text = R"(
3994fd4e5da5Sopenharmony_ciOpCapability Shader
3995fd4e5da5Sopenharmony_ciOpCapability Linkage
3996fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
3997fd4e5da5Sopenharmony_ci%void = OpTypeVoid
3998fd4e5da5Sopenharmony_ci%2 = OpTypeFunction %void
3999fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 1
4000fd4e5da5Sopenharmony_ci%4 = OpUndef %int
4001fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4002fd4e5da5Sopenharmony_ci%6 = OpUndef %bool
4003fd4e5da5Sopenharmony_ci%7 = OpFunction %void None %2
4004fd4e5da5Sopenharmony_ci%8 = OpLabel
4005fd4e5da5Sopenharmony_ciOpSelectionMerge %9 None
4006fd4e5da5Sopenharmony_ciOpSwitch %4 %10 0 %11
4007fd4e5da5Sopenharmony_ci%10 = OpLabel
4008fd4e5da5Sopenharmony_ciOpBranch %9
4009fd4e5da5Sopenharmony_ci%11 = OpLabel
4010fd4e5da5Sopenharmony_ciOpBranch %12
4011fd4e5da5Sopenharmony_ci%12 = OpLabel
4012fd4e5da5Sopenharmony_ciOpLoopMerge %13 %14 None
4013fd4e5da5Sopenharmony_ciOpBranch %15
4014fd4e5da5Sopenharmony_ci%15 = OpLabel
4015fd4e5da5Sopenharmony_ciOpSelectionMerge %16 None
4016fd4e5da5Sopenharmony_ciOpSwitch %4 %17 1 %18 2 %19
4017fd4e5da5Sopenharmony_ci%17 = OpLabel
4018fd4e5da5Sopenharmony_ciOpBranch %16
4019fd4e5da5Sopenharmony_ci%18 = OpLabel
4020fd4e5da5Sopenharmony_ciOpBranch %14
4021fd4e5da5Sopenharmony_ci%19 = OpLabel
4022fd4e5da5Sopenharmony_ciOpBranch %16
4023fd4e5da5Sopenharmony_ci%16 = OpLabel
4024fd4e5da5Sopenharmony_ciOpBranch %14
4025fd4e5da5Sopenharmony_ci%14 = OpLabel
4026fd4e5da5Sopenharmony_ciOpBranchConditional %6 %12 %13
4027fd4e5da5Sopenharmony_ci%13 = OpLabel
4028fd4e5da5Sopenharmony_ciOpSelectionMerge %20 None
4029fd4e5da5Sopenharmony_ciOpBranchConditional %6 %21 %20
4030fd4e5da5Sopenharmony_ci%21 = OpLabel
4031fd4e5da5Sopenharmony_ciOpBranch %9
4032fd4e5da5Sopenharmony_ci%20 = OpLabel
4033fd4e5da5Sopenharmony_ciOpBranch %10
4034fd4e5da5Sopenharmony_ci%9 = OpLabel
4035fd4e5da5Sopenharmony_ciOpReturn
4036fd4e5da5Sopenharmony_ciOpFunctionEnd
4037fd4e5da5Sopenharmony_ci)";
4038fd4e5da5Sopenharmony_ci
4039fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4040fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
4041fd4e5da5Sopenharmony_ci}
4042fd4e5da5Sopenharmony_ci
4043fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge2) {
4044fd4e5da5Sopenharmony_ci  const std::string text = R"(
4045fd4e5da5Sopenharmony_ci               OpCapability Shader
4046fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
4047fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
4048fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %2 "main"
4049fd4e5da5Sopenharmony_ci               OpExecutionMode %2 OriginUpperLeft
4050fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
4051fd4e5da5Sopenharmony_ci          %4 = OpTypeFunction %void
4052fd4e5da5Sopenharmony_ci        %int = OpTypeInt 32 1
4053fd4e5da5Sopenharmony_ci          %6 = OpUndef %int
4054fd4e5da5Sopenharmony_ci       %bool = OpTypeBool
4055fd4e5da5Sopenharmony_ci          %8 = OpUndef %bool
4056fd4e5da5Sopenharmony_ci          %2 = OpFunction %void None %4
4057fd4e5da5Sopenharmony_ci          %9 = OpLabel
4058fd4e5da5Sopenharmony_ci               OpSelectionMerge %10 None
4059fd4e5da5Sopenharmony_ci               OpSwitch %6 %11 0 %12
4060fd4e5da5Sopenharmony_ci         %11 = OpLabel
4061fd4e5da5Sopenharmony_ci               OpBranch %10
4062fd4e5da5Sopenharmony_ci         %12 = OpLabel
4063fd4e5da5Sopenharmony_ci               OpBranch %13
4064fd4e5da5Sopenharmony_ci         %13 = OpLabel
4065fd4e5da5Sopenharmony_ci               OpLoopMerge %14 %15 None
4066fd4e5da5Sopenharmony_ci               OpBranch %16
4067fd4e5da5Sopenharmony_ci         %16 = OpLabel
4068fd4e5da5Sopenharmony_ci               OpSelectionMerge %17 None
4069fd4e5da5Sopenharmony_ci               OpSwitch %6 %18 1 %19 2 %20
4070fd4e5da5Sopenharmony_ci         %18 = OpLabel
4071fd4e5da5Sopenharmony_ci               OpBranch %17
4072fd4e5da5Sopenharmony_ci         %19 = OpLabel
4073fd4e5da5Sopenharmony_ci               OpBranch %15
4074fd4e5da5Sopenharmony_ci         %20 = OpLabel
4075fd4e5da5Sopenharmony_ci               OpBranch %17
4076fd4e5da5Sopenharmony_ci         %17 = OpLabel
4077fd4e5da5Sopenharmony_ci               OpBranch %15
4078fd4e5da5Sopenharmony_ci         %15 = OpLabel
4079fd4e5da5Sopenharmony_ci               OpBranchConditional %8 %13 %14
4080fd4e5da5Sopenharmony_ci         %14 = OpLabel
4081fd4e5da5Sopenharmony_ci               OpSelectionMerge %21 None
4082fd4e5da5Sopenharmony_ci               OpBranchConditional %8 %22 %21
4083fd4e5da5Sopenharmony_ci         %22 = OpLabel
4084fd4e5da5Sopenharmony_ci               OpSelectionMerge %23 None
4085fd4e5da5Sopenharmony_ci               OpBranchConditional %8 %24 %23
4086fd4e5da5Sopenharmony_ci         %24 = OpLabel
4087fd4e5da5Sopenharmony_ci               OpBranch %10
4088fd4e5da5Sopenharmony_ci         %23 = OpLabel
4089fd4e5da5Sopenharmony_ci               OpBranch %21
4090fd4e5da5Sopenharmony_ci         %21 = OpLabel
4091fd4e5da5Sopenharmony_ci               OpBranch %11
4092fd4e5da5Sopenharmony_ci         %10 = OpLabel
4093fd4e5da5Sopenharmony_ci               OpReturn
4094fd4e5da5Sopenharmony_ci               OpFunctionEnd
4095fd4e5da5Sopenharmony_ci)";
4096fd4e5da5Sopenharmony_ci
4097fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4098fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
4099fd4e5da5Sopenharmony_ci}
4100fd4e5da5Sopenharmony_ci
4101fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, PhiResultInvalidSampler) {
4102fd4e5da5Sopenharmony_ci  const std::string text = R"(
4103fd4e5da5Sopenharmony_ciOpCapability Shader
4104fd4e5da5Sopenharmony_ciOpCapability Linkage
4105fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4106fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4107fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4108fd4e5da5Sopenharmony_ci%f32 = OpTypeFloat 32
4109fd4e5da5Sopenharmony_ci%sampler = OpTypeSampler
4110fd4e5da5Sopenharmony_ci%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
4111fd4e5da5Sopenharmony_ci%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
4112fd4e5da5Sopenharmony_ci%undef_bool = OpUndef %bool
4113fd4e5da5Sopenharmony_ci%undef_sampler = OpUndef %sampler
4114fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4115fd4e5da5Sopenharmony_ci%fn = OpFunction %void None %void_fn
4116fd4e5da5Sopenharmony_ci%entry = OpLabel
4117fd4e5da5Sopenharmony_ci%ld_sampler = OpLoad %sampler %sampler_var
4118fd4e5da5Sopenharmony_ciOpBranch %loop
4119fd4e5da5Sopenharmony_ci%loop = OpLabel
4120fd4e5da5Sopenharmony_ci%phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop
4121fd4e5da5Sopenharmony_ciOpLoopMerge %exit %loop None
4122fd4e5da5Sopenharmony_ciOpBranchConditional %undef_bool %exit %loop
4123fd4e5da5Sopenharmony_ci%exit = OpLabel
4124fd4e5da5Sopenharmony_ciOpReturn
4125fd4e5da5Sopenharmony_ciOpFunctionEnd
4126fd4e5da5Sopenharmony_ci)";
4127fd4e5da5Sopenharmony_ci
4128fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4129fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
4130fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4131fd4e5da5Sopenharmony_ci              HasSubstr("Result type cannot be OpTypeSampler"));
4132fd4e5da5Sopenharmony_ci}
4133fd4e5da5Sopenharmony_ci
4134fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, PhiResultInvalidImage) {
4135fd4e5da5Sopenharmony_ci  const std::string text = R"(
4136fd4e5da5Sopenharmony_ciOpCapability Shader
4137fd4e5da5Sopenharmony_ciOpCapability Linkage
4138fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4139fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4140fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4141fd4e5da5Sopenharmony_ci%f32 = OpTypeFloat 32
4142fd4e5da5Sopenharmony_ci%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
4143fd4e5da5Sopenharmony_ci%ptr_uc_image = OpTypePointer UniformConstant %image
4144fd4e5da5Sopenharmony_ci%image_var = OpVariable %ptr_uc_image UniformConstant
4145fd4e5da5Sopenharmony_ci%undef_bool = OpUndef %bool
4146fd4e5da5Sopenharmony_ci%undef_image = OpUndef %image
4147fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4148fd4e5da5Sopenharmony_ci%fn = OpFunction %void None %void_fn
4149fd4e5da5Sopenharmony_ci%entry = OpLabel
4150fd4e5da5Sopenharmony_ci%ld_image = OpLoad %image %image_var
4151fd4e5da5Sopenharmony_ciOpBranch %loop
4152fd4e5da5Sopenharmony_ci%loop = OpLabel
4153fd4e5da5Sopenharmony_ci%phi = OpPhi %image %undef_image %entry %ld_image %loop
4154fd4e5da5Sopenharmony_ciOpLoopMerge %exit %loop None
4155fd4e5da5Sopenharmony_ciOpBranchConditional %undef_bool %exit %loop
4156fd4e5da5Sopenharmony_ci%exit = OpLabel
4157fd4e5da5Sopenharmony_ciOpReturn
4158fd4e5da5Sopenharmony_ciOpFunctionEnd
4159fd4e5da5Sopenharmony_ci)";
4160fd4e5da5Sopenharmony_ci
4161fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4162fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
4163fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4164fd4e5da5Sopenharmony_ci              HasSubstr("Result type cannot be OpTypeImage"));
4165fd4e5da5Sopenharmony_ci}
4166fd4e5da5Sopenharmony_ci
4167fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, PhiResultInvalidSampledImage) {
4168fd4e5da5Sopenharmony_ci  const std::string text = R"(
4169fd4e5da5Sopenharmony_ciOpCapability Shader
4170fd4e5da5Sopenharmony_ciOpCapability Linkage
4171fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4172fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4173fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4174fd4e5da5Sopenharmony_ci%f32 = OpTypeFloat 32
4175fd4e5da5Sopenharmony_ci%sampler = OpTypeSampler
4176fd4e5da5Sopenharmony_ci%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
4177fd4e5da5Sopenharmony_ci%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
4178fd4e5da5Sopenharmony_ci%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
4179fd4e5da5Sopenharmony_ci%ptr_uc_image = OpTypePointer UniformConstant %image
4180fd4e5da5Sopenharmony_ci%image_var = OpVariable %ptr_uc_image UniformConstant
4181fd4e5da5Sopenharmony_ci%sampled_image = OpTypeSampledImage %image
4182fd4e5da5Sopenharmony_ci%undef_bool = OpUndef %bool
4183fd4e5da5Sopenharmony_ci%undef_sampled_image = OpUndef %sampled_image
4184fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4185fd4e5da5Sopenharmony_ci%fn = OpFunction %void None %void_fn
4186fd4e5da5Sopenharmony_ci%entry = OpLabel
4187fd4e5da5Sopenharmony_ci%ld_image = OpLoad %image %image_var
4188fd4e5da5Sopenharmony_ci%ld_sampler = OpLoad %sampler %sampler_var
4189fd4e5da5Sopenharmony_ciOpBranch %loop
4190fd4e5da5Sopenharmony_ci%loop = OpLabel
4191fd4e5da5Sopenharmony_ci%phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop
4192fd4e5da5Sopenharmony_ci%sample = OpSampledImage %sampled_image %ld_image %ld_sampler
4193fd4e5da5Sopenharmony_ciOpLoopMerge %exit %loop None
4194fd4e5da5Sopenharmony_ciOpBranchConditional %undef_bool %exit %loop
4195fd4e5da5Sopenharmony_ci%exit = OpLabel
4196fd4e5da5Sopenharmony_ciOpReturn
4197fd4e5da5Sopenharmony_ciOpFunctionEnd
4198fd4e5da5Sopenharmony_ci)";
4199fd4e5da5Sopenharmony_ci
4200fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4201fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
4202fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4203fd4e5da5Sopenharmony_ci              HasSubstr("Result type cannot be OpTypeSampledImage"));
4204fd4e5da5Sopenharmony_ci}
4205fd4e5da5Sopenharmony_ci
4206fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, PhiResultValidPreLegalizationSampler) {
4207fd4e5da5Sopenharmony_ci  const std::string text = R"(
4208fd4e5da5Sopenharmony_ciOpCapability Shader
4209fd4e5da5Sopenharmony_ciOpCapability Linkage
4210fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4211fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4212fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4213fd4e5da5Sopenharmony_ci%f32 = OpTypeFloat 32
4214fd4e5da5Sopenharmony_ci%sampler = OpTypeSampler
4215fd4e5da5Sopenharmony_ci%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
4216fd4e5da5Sopenharmony_ci%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
4217fd4e5da5Sopenharmony_ci%undef_bool = OpUndef %bool
4218fd4e5da5Sopenharmony_ci%undef_sampler = OpUndef %sampler
4219fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4220fd4e5da5Sopenharmony_ci%fn = OpFunction %void None %void_fn
4221fd4e5da5Sopenharmony_ci%entry = OpLabel
4222fd4e5da5Sopenharmony_ci%ld_sampler = OpLoad %sampler %sampler_var
4223fd4e5da5Sopenharmony_ciOpBranch %loop
4224fd4e5da5Sopenharmony_ci%loop = OpLabel
4225fd4e5da5Sopenharmony_ci%phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop
4226fd4e5da5Sopenharmony_ciOpLoopMerge %exit %loop None
4227fd4e5da5Sopenharmony_ciOpBranchConditional %undef_bool %exit %loop
4228fd4e5da5Sopenharmony_ci%exit = OpLabel
4229fd4e5da5Sopenharmony_ciOpReturn
4230fd4e5da5Sopenharmony_ciOpFunctionEnd
4231fd4e5da5Sopenharmony_ci)";
4232fd4e5da5Sopenharmony_ci
4233fd4e5da5Sopenharmony_ci  options_->before_hlsl_legalization = true;
4234fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4235fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
4236fd4e5da5Sopenharmony_ci}
4237fd4e5da5Sopenharmony_ci
4238fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, PhiResultValidPreLegalizationImage) {
4239fd4e5da5Sopenharmony_ci  const std::string text = R"(
4240fd4e5da5Sopenharmony_ciOpCapability Shader
4241fd4e5da5Sopenharmony_ciOpCapability Linkage
4242fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4243fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4244fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4245fd4e5da5Sopenharmony_ci%f32 = OpTypeFloat 32
4246fd4e5da5Sopenharmony_ci%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
4247fd4e5da5Sopenharmony_ci%ptr_uc_image = OpTypePointer UniformConstant %image
4248fd4e5da5Sopenharmony_ci%image_var = OpVariable %ptr_uc_image UniformConstant
4249fd4e5da5Sopenharmony_ci%undef_bool = OpUndef %bool
4250fd4e5da5Sopenharmony_ci%undef_image = OpUndef %image
4251fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4252fd4e5da5Sopenharmony_ci%fn = OpFunction %void None %void_fn
4253fd4e5da5Sopenharmony_ci%entry = OpLabel
4254fd4e5da5Sopenharmony_ci%ld_image = OpLoad %image %image_var
4255fd4e5da5Sopenharmony_ciOpBranch %loop
4256fd4e5da5Sopenharmony_ci%loop = OpLabel
4257fd4e5da5Sopenharmony_ci%phi = OpPhi %image %undef_image %entry %ld_image %loop
4258fd4e5da5Sopenharmony_ciOpLoopMerge %exit %loop None
4259fd4e5da5Sopenharmony_ciOpBranchConditional %undef_bool %exit %loop
4260fd4e5da5Sopenharmony_ci%exit = OpLabel
4261fd4e5da5Sopenharmony_ciOpReturn
4262fd4e5da5Sopenharmony_ciOpFunctionEnd
4263fd4e5da5Sopenharmony_ci)";
4264fd4e5da5Sopenharmony_ci
4265fd4e5da5Sopenharmony_ci  options_->before_hlsl_legalization = true;
4266fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4267fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
4268fd4e5da5Sopenharmony_ci}
4269fd4e5da5Sopenharmony_ci
4270fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, PhiResultValidPreLegalizationSampledImage) {
4271fd4e5da5Sopenharmony_ci  const std::string text = R"(
4272fd4e5da5Sopenharmony_ciOpCapability Shader
4273fd4e5da5Sopenharmony_ciOpCapability Linkage
4274fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4275fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4276fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4277fd4e5da5Sopenharmony_ci%f32 = OpTypeFloat 32
4278fd4e5da5Sopenharmony_ci%sampler = OpTypeSampler
4279fd4e5da5Sopenharmony_ci%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
4280fd4e5da5Sopenharmony_ci%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
4281fd4e5da5Sopenharmony_ci%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
4282fd4e5da5Sopenharmony_ci%ptr_uc_image = OpTypePointer UniformConstant %image
4283fd4e5da5Sopenharmony_ci%image_var = OpVariable %ptr_uc_image UniformConstant
4284fd4e5da5Sopenharmony_ci%sampled_image = OpTypeSampledImage %image
4285fd4e5da5Sopenharmony_ci%undef_bool = OpUndef %bool
4286fd4e5da5Sopenharmony_ci%undef_sampled_image = OpUndef %sampled_image
4287fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4288fd4e5da5Sopenharmony_ci%fn = OpFunction %void None %void_fn
4289fd4e5da5Sopenharmony_ci%entry = OpLabel
4290fd4e5da5Sopenharmony_ci%ld_image = OpLoad %image %image_var
4291fd4e5da5Sopenharmony_ci%ld_sampler = OpLoad %sampler %sampler_var
4292fd4e5da5Sopenharmony_ciOpBranch %loop
4293fd4e5da5Sopenharmony_ci%loop = OpLabel
4294fd4e5da5Sopenharmony_ci%phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop
4295fd4e5da5Sopenharmony_ci%sample = OpSampledImage %sampled_image %ld_image %ld_sampler
4296fd4e5da5Sopenharmony_ciOpLoopMerge %exit %loop None
4297fd4e5da5Sopenharmony_ciOpBranchConditional %undef_bool %exit %loop
4298fd4e5da5Sopenharmony_ci%exit = OpLabel
4299fd4e5da5Sopenharmony_ciOpReturn
4300fd4e5da5Sopenharmony_ciOpFunctionEnd
4301fd4e5da5Sopenharmony_ci)";
4302fd4e5da5Sopenharmony_ci
4303fd4e5da5Sopenharmony_ci  options_->before_hlsl_legalization = true;
4304fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4305fd4e5da5Sopenharmony_ci  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
4306fd4e5da5Sopenharmony_ci}
4307fd4e5da5Sopenharmony_ci
4308fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, StructuredSelections_RegisterBothTrueAndFalse) {
4309fd4e5da5Sopenharmony_ci  // In this test, we try to make a case where the false branches
4310fd4e5da5Sopenharmony_ci  // to %20 and %60 from blocks %10 and %50 must be registered
4311fd4e5da5Sopenharmony_ci  // during the validity check for sturctured selections.
4312fd4e5da5Sopenharmony_ci  // However, an error is caught earlier in the flow, that the
4313fd4e5da5Sopenharmony_ci  // branches from %100 to %20 and %60 violate dominance.
4314fd4e5da5Sopenharmony_ci  const std::string text = R"(
4315fd4e5da5Sopenharmony_ci    OpCapability Shader
4316fd4e5da5Sopenharmony_ci    OpMemoryModel Logical Simple
4317fd4e5da5Sopenharmony_ci    OpEntryPoint Fragment %main "main"
4318fd4e5da5Sopenharmony_ci    OpExecutionMode %main OriginUpperLeft
4319fd4e5da5Sopenharmony_ci
4320fd4e5da5Sopenharmony_ci    %void    = OpTypeVoid
4321fd4e5da5Sopenharmony_ci    %void_fn = OpTypeFunction %void
4322fd4e5da5Sopenharmony_ci
4323fd4e5da5Sopenharmony_ci    %bool = OpTypeBool
4324fd4e5da5Sopenharmony_ci    %cond = OpUndef %bool
4325fd4e5da5Sopenharmony_ci
4326fd4e5da5Sopenharmony_ci    %main = OpFunction %void None %void_fn
4327fd4e5da5Sopenharmony_ci
4328fd4e5da5Sopenharmony_ci    %1 = OpLabel
4329fd4e5da5Sopenharmony_ci    OpSelectionMerge %999 None
4330fd4e5da5Sopenharmony_ci    OpBranchConditional %cond %10 %100
4331fd4e5da5Sopenharmony_ci
4332fd4e5da5Sopenharmony_ci    %10 = OpLabel
4333fd4e5da5Sopenharmony_ci    OpSelectionMerge %30 None  ; force registration of %30
4334fd4e5da5Sopenharmony_ci    OpBranchConditional %cond %30 %20 ; %20 should be registered too
4335fd4e5da5Sopenharmony_ci
4336fd4e5da5Sopenharmony_ci    %20 = OpLabel
4337fd4e5da5Sopenharmony_ci    OpBranch %30
4338fd4e5da5Sopenharmony_ci
4339fd4e5da5Sopenharmony_ci    %30 = OpLabel ; merge for first if
4340fd4e5da5Sopenharmony_ci    OpBranch %50
4341fd4e5da5Sopenharmony_ci
4342fd4e5da5Sopenharmony_ci
4343fd4e5da5Sopenharmony_ci    %50 = OpLabel
4344fd4e5da5Sopenharmony_ci    OpSelectionMerge %70 None  ; force registration of %70
4345fd4e5da5Sopenharmony_ci    OpBranchConditional %cond %70 %60 ; %60 should be registered
4346fd4e5da5Sopenharmony_ci
4347fd4e5da5Sopenharmony_ci    %60 = OpLabel
4348fd4e5da5Sopenharmony_ci    OpBranch %70
4349fd4e5da5Sopenharmony_ci
4350fd4e5da5Sopenharmony_ci    %70 = OpLabel ; merge for second if
4351fd4e5da5Sopenharmony_ci    OpBranch %999
4352fd4e5da5Sopenharmony_ci
4353fd4e5da5Sopenharmony_ci    %100 = OpLabel
4354fd4e5da5Sopenharmony_ci    OpBranchConditional %cond %20 %60 ; should require a merge
4355fd4e5da5Sopenharmony_ci
4356fd4e5da5Sopenharmony_ci    %999 = OpLabel
4357fd4e5da5Sopenharmony_ci    OpReturn
4358fd4e5da5Sopenharmony_ci
4359fd4e5da5Sopenharmony_ci    OpFunctionEnd
4360fd4e5da5Sopenharmony_ci)";
4361fd4e5da5Sopenharmony_ci
4362fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4363fd4e5da5Sopenharmony_ci  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
4364fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4365fd4e5da5Sopenharmony_ci              HasSubstr("The selection construct with the selection header "
4366fd4e5da5Sopenharmony_ci                        "'8[%8]' does not structurally dominate the merge "
4367fd4e5da5Sopenharmony_ci                        "block '10[%10]'\n"));
4368fd4e5da5Sopenharmony_ci}
4369fd4e5da5Sopenharmony_ci
4370fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, UnreachableIsStaticallyReachable) {
4371fd4e5da5Sopenharmony_ci  const std::string text = R"(
4372fd4e5da5Sopenharmony_ciOpCapability Shader
4373fd4e5da5Sopenharmony_ciOpCapability Linkage
4374fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4375fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
4376fd4e5da5Sopenharmony_ci%2 = OpTypeFunction %1
4377fd4e5da5Sopenharmony_ci%3 = OpFunction %1 None %2
4378fd4e5da5Sopenharmony_ci%4 = OpLabel
4379fd4e5da5Sopenharmony_ciOpBranch %5
4380fd4e5da5Sopenharmony_ci%5 = OpLabel
4381fd4e5da5Sopenharmony_ciOpUnreachable
4382fd4e5da5Sopenharmony_ciOpFunctionEnd
4383fd4e5da5Sopenharmony_ci)";
4384fd4e5da5Sopenharmony_ci
4385fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4386fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
4387fd4e5da5Sopenharmony_ci
4388fd4e5da5Sopenharmony_ci  auto f = vstate_->function(3);
4389fd4e5da5Sopenharmony_ci  auto entry = f->GetBlock(4).first;
4390fd4e5da5Sopenharmony_ci  ASSERT_TRUE(entry->reachable());
4391fd4e5da5Sopenharmony_ci  auto end = f->GetBlock(5).first;
4392fd4e5da5Sopenharmony_ci  ASSERT_TRUE(end->reachable());
4393fd4e5da5Sopenharmony_ci}
4394fd4e5da5Sopenharmony_ci
4395fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BlockOrderDoesNotAffectReachability) {
4396fd4e5da5Sopenharmony_ci  const std::string text = R"(
4397fd4e5da5Sopenharmony_ciOpCapability Shader
4398fd4e5da5Sopenharmony_ciOpCapability Linkage
4399fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4400fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
4401fd4e5da5Sopenharmony_ci%2 = OpTypeFunction %1
4402fd4e5da5Sopenharmony_ci%3 = OpTypeBool
4403fd4e5da5Sopenharmony_ci%4 = OpUndef %3
4404fd4e5da5Sopenharmony_ci%5 = OpFunction %1 None %2
4405fd4e5da5Sopenharmony_ci%6 = OpLabel
4406fd4e5da5Sopenharmony_ciOpBranch %7
4407fd4e5da5Sopenharmony_ci%7 = OpLabel
4408fd4e5da5Sopenharmony_ciOpSelectionMerge %8 None
4409fd4e5da5Sopenharmony_ciOpBranchConditional %4 %9 %10
4410fd4e5da5Sopenharmony_ci%8 = OpLabel
4411fd4e5da5Sopenharmony_ciOpReturn
4412fd4e5da5Sopenharmony_ci%9 = OpLabel
4413fd4e5da5Sopenharmony_ciOpBranch %8
4414fd4e5da5Sopenharmony_ci%10 = OpLabel
4415fd4e5da5Sopenharmony_ciOpBranch %8
4416fd4e5da5Sopenharmony_ci%11 = OpLabel
4417fd4e5da5Sopenharmony_ciOpUnreachable
4418fd4e5da5Sopenharmony_ciOpFunctionEnd
4419fd4e5da5Sopenharmony_ci)";
4420fd4e5da5Sopenharmony_ci
4421fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4422fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
4423fd4e5da5Sopenharmony_ci
4424fd4e5da5Sopenharmony_ci  auto f = vstate_->function(5);
4425fd4e5da5Sopenharmony_ci  auto b6 = f->GetBlock(6).first;
4426fd4e5da5Sopenharmony_ci  auto b7 = f->GetBlock(7).first;
4427fd4e5da5Sopenharmony_ci  auto b8 = f->GetBlock(8).first;
4428fd4e5da5Sopenharmony_ci  auto b9 = f->GetBlock(9).first;
4429fd4e5da5Sopenharmony_ci  auto b10 = f->GetBlock(10).first;
4430fd4e5da5Sopenharmony_ci  auto b11 = f->GetBlock(11).first;
4431fd4e5da5Sopenharmony_ci
4432fd4e5da5Sopenharmony_ci  ASSERT_TRUE(b6->reachable());
4433fd4e5da5Sopenharmony_ci  ASSERT_TRUE(b7->reachable());
4434fd4e5da5Sopenharmony_ci  ASSERT_TRUE(b8->reachable());
4435fd4e5da5Sopenharmony_ci  ASSERT_TRUE(b9->reachable());
4436fd4e5da5Sopenharmony_ci  ASSERT_TRUE(b10->reachable());
4437fd4e5da5Sopenharmony_ci  ASSERT_FALSE(b11->reachable());
4438fd4e5da5Sopenharmony_ci}
4439fd4e5da5Sopenharmony_ci
4440fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, PhiInstructionWithDuplicateIncomingEdges) {
4441fd4e5da5Sopenharmony_ci  const std::string text = R"(
4442fd4e5da5Sopenharmony_ci               OpCapability Shader
4443fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
4444fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
4445fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %4 "main"
4446fd4e5da5Sopenharmony_ci               OpExecutionMode %4 OriginUpperLeft
4447fd4e5da5Sopenharmony_ci               OpSource ESSL 320
4448fd4e5da5Sopenharmony_ci          %2 = OpTypeVoid
4449fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %2
4450fd4e5da5Sopenharmony_ci          %6 = OpTypeBool
4451fd4e5da5Sopenharmony_ci          %7 = OpConstantTrue %6
4452fd4e5da5Sopenharmony_ci          %4 = OpFunction %2 None %3
4453fd4e5da5Sopenharmony_ci          %5 = OpLabel
4454fd4e5da5Sopenharmony_ci               OpSelectionMerge %10 None
4455fd4e5da5Sopenharmony_ci               OpBranchConditional %7 %8 %9
4456fd4e5da5Sopenharmony_ci          %8 = OpLabel
4457fd4e5da5Sopenharmony_ci               OpBranch %10
4458fd4e5da5Sopenharmony_ci          %9 = OpLabel
4459fd4e5da5Sopenharmony_ci	       OpBranch %10
4460fd4e5da5Sopenharmony_ci         %10 = OpLabel
4461fd4e5da5Sopenharmony_ci	 %11 = OpPhi %6 %7 %8 %7 %8
4462fd4e5da5Sopenharmony_ci               OpReturn
4463fd4e5da5Sopenharmony_ci               OpFunctionEnd
4464fd4e5da5Sopenharmony_ci)";
4465fd4e5da5Sopenharmony_ci
4466fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4467fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
4468fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4469fd4e5da5Sopenharmony_ci              HasSubstr("OpPhi references incoming basic block <id> "));
4470fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(), HasSubstr("multiple times."));
4471fd4e5da5Sopenharmony_ci}
4472fd4e5da5Sopenharmony_ci
4473fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, PhiOnVoid) {
4474fd4e5da5Sopenharmony_ci  const std::string text = R"(
4475fd4e5da5Sopenharmony_ci               OpCapability Shader
4476fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
4477fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
4478fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %4 "main"
4479fd4e5da5Sopenharmony_ci               OpExecutionMode %4 OriginUpperLeft
4480fd4e5da5Sopenharmony_ci               OpSource ESSL 320
4481fd4e5da5Sopenharmony_ci               OpName %4 "main"
4482fd4e5da5Sopenharmony_ci               OpName %6 "foo("
4483fd4e5da5Sopenharmony_ci          %2 = OpTypeVoid
4484fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %2
4485fd4e5da5Sopenharmony_ci          %4 = OpFunction %2 None %3
4486fd4e5da5Sopenharmony_ci          %5 = OpLabel
4487fd4e5da5Sopenharmony_ci          %8 = OpFunctionCall %2 %6
4488fd4e5da5Sopenharmony_ci               OpBranch %20
4489fd4e5da5Sopenharmony_ci         %20 = OpLabel
4490fd4e5da5Sopenharmony_ci         %21 = OpPhi %2 %8 %20
4491fd4e5da5Sopenharmony_ci               OpReturn
4492fd4e5da5Sopenharmony_ci               OpFunctionEnd
4493fd4e5da5Sopenharmony_ci          %6 = OpFunction %2 None %3
4494fd4e5da5Sopenharmony_ci          %7 = OpLabel
4495fd4e5da5Sopenharmony_ci               OpReturn
4496fd4e5da5Sopenharmony_ci               OpFunctionEnd
4497fd4e5da5Sopenharmony_ci)";
4498fd4e5da5Sopenharmony_ci
4499fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4500fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
4501fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4502fd4e5da5Sopenharmony_ci              HasSubstr("OpPhi must not have void result type"));
4503fd4e5da5Sopenharmony_ci}
4504fd4e5da5Sopenharmony_ci
4505fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, InvalidExitSingleBlockLoop) {
4506fd4e5da5Sopenharmony_ci  const std::string text = R"(
4507fd4e5da5Sopenharmony_ciOpCapability Shader
4508fd4e5da5Sopenharmony_ciOpCapability Linkage
4509fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4510fd4e5da5Sopenharmony_ciOpName %5 "BAD"
4511fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4512fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4513fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
4514fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4515fd4e5da5Sopenharmony_ci%fn = OpFunction %void None %void_fn
4516fd4e5da5Sopenharmony_ci%1 = OpLabel
4517fd4e5da5Sopenharmony_ciOpBranch %2
4518fd4e5da5Sopenharmony_ci%2 = OpLabel
4519fd4e5da5Sopenharmony_ciOpLoopMerge %3 %4 None
4520fd4e5da5Sopenharmony_ciOpBranchConditional %undef %3 %5
4521fd4e5da5Sopenharmony_ci%5 = OpLabel
4522fd4e5da5Sopenharmony_ciOpLoopMerge %6 %5 None
4523fd4e5da5Sopenharmony_ciOpBranchConditional %undef %5 %4
4524fd4e5da5Sopenharmony_ci%6 = OpLabel
4525fd4e5da5Sopenharmony_ciOpReturn
4526fd4e5da5Sopenharmony_ci%4 = OpLabel
4527fd4e5da5Sopenharmony_ciOpBranch %2
4528fd4e5da5Sopenharmony_ci%3 = OpLabel
4529fd4e5da5Sopenharmony_ciOpReturn
4530fd4e5da5Sopenharmony_ciOpFunctionEnd
4531fd4e5da5Sopenharmony_ci)";
4532fd4e5da5Sopenharmony_ci
4533fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4534fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
4535fd4e5da5Sopenharmony_ci  EXPECT_THAT(
4536fd4e5da5Sopenharmony_ci      getDiagnosticString(),
4537fd4e5da5Sopenharmony_ci      HasSubstr("block <ID> '1[%BAD]' exits the continue headed by <ID> "
4538fd4e5da5Sopenharmony_ci                "'1[%BAD]', but not via a structured exit"));
4539fd4e5da5Sopenharmony_ci}
4540fd4e5da5Sopenharmony_ci
4541fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, SwitchSelectorNotAnInt) {
4542fd4e5da5Sopenharmony_ci  const std::string spirv = R"(
4543fd4e5da5Sopenharmony_ciOpCapability Shader
4544fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4545fd4e5da5Sopenharmony_ciOpEntryPoint GLCompute %main "main"
4546fd4e5da5Sopenharmony_ciOpExecutionMode %main LocalSize 1 1 1
4547fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4548fd4e5da5Sopenharmony_ci%float = OpTypeFloat 32
4549fd4e5da5Sopenharmony_ci%float_1 = OpConstant %float 1
4550fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4551fd4e5da5Sopenharmony_ci%main = OpFunction %void None %void_fn
4552fd4e5da5Sopenharmony_ci%entry = OpLabel
4553fd4e5da5Sopenharmony_ciOpSelectionMerge %default None
4554fd4e5da5Sopenharmony_ciOpSwitch %float_1 %default
4555fd4e5da5Sopenharmony_ci%default = OpLabel
4556fd4e5da5Sopenharmony_ciOpReturn
4557fd4e5da5Sopenharmony_ciOpFunctionEnd
4558fd4e5da5Sopenharmony_ci)";
4559fd4e5da5Sopenharmony_ci
4560fd4e5da5Sopenharmony_ci  CompileSuccessfully(spirv);
4561fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
4562fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4563fd4e5da5Sopenharmony_ci              HasSubstr("Selector type must be OpTypeInt"));
4564fd4e5da5Sopenharmony_ci}
4565fd4e5da5Sopenharmony_ci
4566fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, SwitchDefaultNotALabel) {
4567fd4e5da5Sopenharmony_ci  const std::string spirv = R"(
4568fd4e5da5Sopenharmony_ciOpCapability Shader
4569fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4570fd4e5da5Sopenharmony_ciOpEntryPoint GLCompute %main "main"
4571fd4e5da5Sopenharmony_ciOpExecutionMode %main LocalSize 1 1 1
4572fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4573fd4e5da5Sopenharmony_ci%int = OpTypeInt 32 0
4574fd4e5da5Sopenharmony_ci%int_1 = OpConstant %int 1
4575fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4576fd4e5da5Sopenharmony_ci%main = OpFunction %void None %void_fn
4577fd4e5da5Sopenharmony_ci%entry = OpLabel
4578fd4e5da5Sopenharmony_ciOpSelectionMerge %default None
4579fd4e5da5Sopenharmony_ciOpSwitch %int_1 %int_1
4580fd4e5da5Sopenharmony_ci%default = OpLabel
4581fd4e5da5Sopenharmony_ciOpReturn
4582fd4e5da5Sopenharmony_ciOpFunctionEnd
4583fd4e5da5Sopenharmony_ci)";
4584fd4e5da5Sopenharmony_ci
4585fd4e5da5Sopenharmony_ci  CompileSuccessfully(spirv);
4586fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
4587fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4588fd4e5da5Sopenharmony_ci              HasSubstr("Default must be an OpLabel instruction"));
4589fd4e5da5Sopenharmony_ci}
4590fd4e5da5Sopenharmony_ci
4591fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BlockDepthRecursion) {
4592fd4e5da5Sopenharmony_ci  const std::string text = R"(
4593fd4e5da5Sopenharmony_ciOpCapability Shader
4594fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4595fd4e5da5Sopenharmony_ciOpEntryPoint GLCompute %main "main"
4596fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4597fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4598fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
4599fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4600fd4e5da5Sopenharmony_ci%main = OpFunction %void None %void_fn
4601fd4e5da5Sopenharmony_ci%1 = OpLabel
4602fd4e5da5Sopenharmony_ciOpBranch %2
4603fd4e5da5Sopenharmony_ci%2 = OpLabel
4604fd4e5da5Sopenharmony_ciOpLoopMerge %3 %4 None
4605fd4e5da5Sopenharmony_ciOpBranchConditional %undef %3 %4
4606fd4e5da5Sopenharmony_ci%4 = OpLabel
4607fd4e5da5Sopenharmony_ciOpBranch %2
4608fd4e5da5Sopenharmony_ci%3 = OpLabel
4609fd4e5da5Sopenharmony_ciOpBranch %5
4610fd4e5da5Sopenharmony_ci%5 = OpLabel
4611fd4e5da5Sopenharmony_ciOpSelectionMerge %2 None
4612fd4e5da5Sopenharmony_ciOpBranchConditional %undef %6 %7
4613fd4e5da5Sopenharmony_ci%6 = OpLabel
4614fd4e5da5Sopenharmony_ciOpReturn
4615fd4e5da5Sopenharmony_ci%7 = OpLabel
4616fd4e5da5Sopenharmony_ciOpReturn
4617fd4e5da5Sopenharmony_ciOpFunctionEnd
4618fd4e5da5Sopenharmony_ci)";
4619fd4e5da5Sopenharmony_ci
4620fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4621fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
4622fd4e5da5Sopenharmony_ci}
4623fd4e5da5Sopenharmony_ci
4624fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BadStructuredExitBackwardsMerge) {
4625fd4e5da5Sopenharmony_ci  const std::string spirv = R"(
4626fd4e5da5Sopenharmony_ciOpCapability Shader
4627fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4628fd4e5da5Sopenharmony_ciOpEntryPoint GLCompute %main "main"
4629fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4630fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4631fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
4632fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4633fd4e5da5Sopenharmony_ci%main = OpFunction %void None %void_fn
4634fd4e5da5Sopenharmony_ci%1 = OpLabel
4635fd4e5da5Sopenharmony_ciOpBranch %2
4636fd4e5da5Sopenharmony_ci%2 = OpLabel
4637fd4e5da5Sopenharmony_ciOpLoopMerge %4 %5 None
4638fd4e5da5Sopenharmony_ciOpBranchConditional %undef %4 %6
4639fd4e5da5Sopenharmony_ci%6 = OpLabel
4640fd4e5da5Sopenharmony_ciOpSelectionMerge %7 None
4641fd4e5da5Sopenharmony_ciOpBranchConditional %undef %8 %9
4642fd4e5da5Sopenharmony_ci%7 = OpLabel
4643fd4e5da5Sopenharmony_ciOpReturn
4644fd4e5da5Sopenharmony_ci%8 = OpLabel
4645fd4e5da5Sopenharmony_ciOpBranch %5
4646fd4e5da5Sopenharmony_ci%9 = OpLabel
4647fd4e5da5Sopenharmony_ciOpSelectionMerge %6 None
4648fd4e5da5Sopenharmony_ciOpBranchConditional %undef %5 %5
4649fd4e5da5Sopenharmony_ci%5 = OpLabel
4650fd4e5da5Sopenharmony_ciOpBranch %2
4651fd4e5da5Sopenharmony_ci%4 = OpLabel
4652fd4e5da5Sopenharmony_ciOpReturn
4653fd4e5da5Sopenharmony_ciOpFunctionEnd
4654fd4e5da5Sopenharmony_ci)";
4655fd4e5da5Sopenharmony_ci
4656fd4e5da5Sopenharmony_ci  CompileSuccessfully(spirv);
4657fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
4658fd4e5da5Sopenharmony_ci}
4659fd4e5da5Sopenharmony_ci
4660fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BranchConditionalDifferentTargetsPre1p6) {
4661fd4e5da5Sopenharmony_ci  const std::string text = R"(
4662fd4e5da5Sopenharmony_ciOpCapability Shader
4663fd4e5da5Sopenharmony_ciOpCapability Linkage
4664fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4665fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4666fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4667fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
4668fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4669fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
4670fd4e5da5Sopenharmony_ci%entry = OpLabel
4671fd4e5da5Sopenharmony_ciOpBranchConditional %undef %target %target
4672fd4e5da5Sopenharmony_ci%target = OpLabel
4673fd4e5da5Sopenharmony_ciOpReturn
4674fd4e5da5Sopenharmony_ciOpFunctionEnd
4675fd4e5da5Sopenharmony_ci)";
4676fd4e5da5Sopenharmony_ci
4677fd4e5da5Sopenharmony_ci  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_5);
4678fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
4679fd4e5da5Sopenharmony_ci}
4680fd4e5da5Sopenharmony_ci
4681fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BranchConditionalDifferentTargetsPost1p6) {
4682fd4e5da5Sopenharmony_ci  const std::string text = R"(
4683fd4e5da5Sopenharmony_ciOpCapability Shader
4684fd4e5da5Sopenharmony_ciOpCapability Linkage
4685fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4686fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4687fd4e5da5Sopenharmony_ci%bool = OpTypeBool
4688fd4e5da5Sopenharmony_ci%undef = OpUndef %bool
4689fd4e5da5Sopenharmony_ci%void_fn = OpTypeFunction %void
4690fd4e5da5Sopenharmony_ci%func = OpFunction %void None %void_fn
4691fd4e5da5Sopenharmony_ci%entry = OpLabel
4692fd4e5da5Sopenharmony_ciOpBranchConditional %undef %target %target
4693fd4e5da5Sopenharmony_ci%target = OpLabel
4694fd4e5da5Sopenharmony_ciOpReturn
4695fd4e5da5Sopenharmony_ciOpFunctionEnd
4696fd4e5da5Sopenharmony_ci)";
4697fd4e5da5Sopenharmony_ci
4698fd4e5da5Sopenharmony_ci  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6);
4699fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
4700fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4701fd4e5da5Sopenharmony_ci              HasSubstr("In SPIR-V 1.6 or later, True Label and False Label "
4702fd4e5da5Sopenharmony_ci                        "must be different labels"));
4703fd4e5da5Sopenharmony_ci}
4704fd4e5da5Sopenharmony_ci
4705fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BadBackEdgeUnreachableContinue) {
4706fd4e5da5Sopenharmony_ci  const std::string text = R"(
4707fd4e5da5Sopenharmony_ciOpCapability Shader
4708fd4e5da5Sopenharmony_ciOpCapability Linkage
4709fd4e5da5Sopenharmony_ciOpMemoryModel Logical GLSL450
4710fd4e5da5Sopenharmony_ci%1 = OpTypeVoid
4711fd4e5da5Sopenharmony_ci%2 = OpTypeFunction %1
4712fd4e5da5Sopenharmony_ci%3 = OpFunction %1 None %2
4713fd4e5da5Sopenharmony_ci%4 = OpLabel
4714fd4e5da5Sopenharmony_ciOpBranch %5
4715fd4e5da5Sopenharmony_ci%5 = OpLabel
4716fd4e5da5Sopenharmony_ciOpLoopMerge %6 %7 None
4717fd4e5da5Sopenharmony_ciOpBranch %8
4718fd4e5da5Sopenharmony_ci%8 = OpLabel
4719fd4e5da5Sopenharmony_ciOpBranch %5
4720fd4e5da5Sopenharmony_ci%7 = OpLabel
4721fd4e5da5Sopenharmony_ciOpUnreachable
4722fd4e5da5Sopenharmony_ci%6 = OpLabel
4723fd4e5da5Sopenharmony_ciOpUnreachable
4724fd4e5da5Sopenharmony_ciOpFunctionEnd
4725fd4e5da5Sopenharmony_ci)";
4726fd4e5da5Sopenharmony_ci
4727fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4728fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
4729fd4e5da5Sopenharmony_ci  EXPECT_THAT(
4730fd4e5da5Sopenharmony_ci      getDiagnosticString(),
4731fd4e5da5Sopenharmony_ci      HasSubstr("The continue construct with the continue target '7[%7]' "
4732fd4e5da5Sopenharmony_ci                "does not structurally dominate the back-edge block '8[%8]'"));
4733fd4e5da5Sopenharmony_ci}
4734fd4e5da5Sopenharmony_ci
4735fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BadLoop) {
4736fd4e5da5Sopenharmony_ci  const std::string text = R"(
4737fd4e5da5Sopenharmony_ciOpCapability Shader
4738fd4e5da5Sopenharmony_ciOpMemoryModel Logical Simple
4739fd4e5da5Sopenharmony_ciOpEntryPoint Fragment %2 "           "
4740fd4e5da5Sopenharmony_ciOpExecutionMode %2 OriginUpperLeft
4741fd4e5da5Sopenharmony_ciOpName %49 "loop"
4742fd4e5da5Sopenharmony_ci%void = OpTypeVoid
4743fd4e5da5Sopenharmony_ci%12 = OpTypeFunction %void
4744fd4e5da5Sopenharmony_ci%2 = OpFunction %void None %12
4745fd4e5da5Sopenharmony_ci%33 = OpLabel
4746fd4e5da5Sopenharmony_ciOpBranch %49
4747fd4e5da5Sopenharmony_ci%50 = OpLabel
4748fd4e5da5Sopenharmony_ciOpBranch %49
4749fd4e5da5Sopenharmony_ci%49 = OpLabel
4750fd4e5da5Sopenharmony_ciOpLoopMerge %33 %50 Unroll
4751fd4e5da5Sopenharmony_ciOpBranch %49
4752fd4e5da5Sopenharmony_ciOpFunctionEnd
4753fd4e5da5Sopenharmony_ci)";
4754fd4e5da5Sopenharmony_ci
4755fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4756fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
4757fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4758fd4e5da5Sopenharmony_ci              HasSubstr("Loop header '2[%loop]' is targeted by 2 back-edge "
4759fd4e5da5Sopenharmony_ci                        "blocks but the standard requires exactly one"));
4760fd4e5da5Sopenharmony_ci}
4761fd4e5da5Sopenharmony_ci
4762fd4e5da5Sopenharmony_ciTEST_F(ValidateCFG, BadSwitch) {
4763fd4e5da5Sopenharmony_ci  const std::string text = R"(
4764fd4e5da5Sopenharmony_ci               OpCapability StorageImageExtendedFormats
4765fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
4766fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %2 "blah" %58
4767fd4e5da5Sopenharmony_ci               OpExecutionMode %2 OriginUpperLeft
4768fd4e5da5Sopenharmony_ci               OpName %BAD "BAD"
4769fd4e5da5Sopenharmony_ci         %11 = OpTypeVoid
4770fd4e5da5Sopenharmony_ci         %12 = OpTypeFunction %11
4771fd4e5da5Sopenharmony_ci         %19 = OpTypeInt 32 1
4772fd4e5da5Sopenharmony_ci         %21 = OpConstant %19 555758549
4773fd4e5da5Sopenharmony_ci          %2 = OpFunction %11 None %12
4774fd4e5da5Sopenharmony_ci          %4 = OpLabel
4775fd4e5da5Sopenharmony_ci               OpBranch %33
4776fd4e5da5Sopenharmony_ci         %33 = OpLabel
4777fd4e5da5Sopenharmony_ci               OpLoopMerge %34 %35 None
4778fd4e5da5Sopenharmony_ci               OpBranch %55
4779fd4e5da5Sopenharmony_ci        %BAD = OpLabel
4780fd4e5da5Sopenharmony_ci               OpSelectionMerge %53 None
4781fd4e5da5Sopenharmony_ci               OpSwitch %21 %34 196153896 %53 20856160 %34 33570306 %34 593494531 %52
4782fd4e5da5Sopenharmony_ci         %55 = OpLabel
4783fd4e5da5Sopenharmony_ci               OpLoopMerge %52 %58 DontUnroll
4784fd4e5da5Sopenharmony_ci               OpBranch %35
4785fd4e5da5Sopenharmony_ci         %58 = OpLabel
4786fd4e5da5Sopenharmony_ci               OpSelectionMerge %58 None
4787fd4e5da5Sopenharmony_ci               OpSwitch %21 %52 178168 %55 608223677 %34 604111047 %34 -553516825 %34 -106432813 %BAD 6946864 %55 1257373689 %55 973090296 %35 -113180668 %55 537002232 %BAD 13762553 %BAD 1030172152 %35 -553516825 %55 -262137 %35 -1091822332 %BAD 131320 %52 131321 %35 131320 %52 131321 %35 -1091822332 %BAD
4788fd4e5da5Sopenharmony_ci         %53 = OpLabel
4789fd4e5da5Sopenharmony_ci               OpBranch %35
4790fd4e5da5Sopenharmony_ci         %52 = OpLabel
4791fd4e5da5Sopenharmony_ci               OpBranch %34
4792fd4e5da5Sopenharmony_ci         %35 = OpLabel
4793fd4e5da5Sopenharmony_ci               OpBranch %33
4794fd4e5da5Sopenharmony_ci         %34 = OpLabel
4795fd4e5da5Sopenharmony_ci               OpKill
4796fd4e5da5Sopenharmony_ci               OpFunctionEnd
4797fd4e5da5Sopenharmony_ci)";
4798fd4e5da5Sopenharmony_ci
4799fd4e5da5Sopenharmony_ci  CompileSuccessfully(text);
4800fd4e5da5Sopenharmony_ci  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
4801fd4e5da5Sopenharmony_ci  EXPECT_THAT(getDiagnosticString(),
4802fd4e5da5Sopenharmony_ci              HasSubstr("exits the selection headed by <ID> '3[%BAD]', but not "
4803fd4e5da5Sopenharmony_ci                        "via a structured exit"));
4804fd4e5da5Sopenharmony_ci}
4805fd4e5da5Sopenharmony_ci
4806fd4e5da5Sopenharmony_ci}  // namespace
4807fd4e5da5Sopenharmony_ci}  // namespace val
4808fd4e5da5Sopenharmony_ci}  // namespace spvtools
4809