ir_builder.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. // Copyright (c) 2018 Google Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "source/opt/ir_builder.h"
  15. #include <memory>
  16. #include <vector>
  17. #include "effcee/effcee.h"
  18. #include "gtest/gtest.h"
  19. #include "source/opt/basic_block.h"
  20. #include "source/opt/build_module.h"
  21. #include "source/opt/instruction.h"
  22. #include "source/opt/type_manager.h"
  23. #include "spirv-tools/libspirv.hpp"
  24. namespace spvtools {
  25. namespace opt {
  26. namespace {
  27. using Analysis = IRContext::Analysis;
  28. using IRBuilderTest = ::testing::Test;
  29. bool Validate(const std::vector<uint32_t>& bin) {
  30. spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
  31. spv_context spvContext = spvContextCreate(target_env);
  32. spv_diagnostic diagnostic = nullptr;
  33. spv_const_binary_t binary = {bin.data(), bin.size()};
  34. spv_result_t error = spvValidate(spvContext, &binary, &diagnostic);
  35. if (error != 0) spvDiagnosticPrint(diagnostic);
  36. spvDiagnosticDestroy(diagnostic);
  37. spvContextDestroy(spvContext);
  38. return error == 0;
  39. }
  40. void Match(const std::string& original, IRContext* context,
  41. bool do_validation = true) {
  42. std::vector<uint32_t> bin;
  43. context->module()->ToBinary(&bin, true);
  44. if (do_validation) {
  45. EXPECT_TRUE(Validate(bin));
  46. }
  47. std::string assembly;
  48. SpirvTools tools(SPV_ENV_UNIVERSAL_1_2);
  49. EXPECT_TRUE(
  50. tools.Disassemble(bin, &assembly, SpirvTools::kDefaultDisassembleOption))
  51. << "Disassembling failed for shader:\n"
  52. << assembly << std::endl;
  53. auto match_result = effcee::Match(assembly, original);
  54. EXPECT_EQ(effcee::Result::Status::Ok, match_result.status())
  55. << match_result.message() << "\nChecking result:\n"
  56. << assembly;
  57. }
  58. TEST_F(IRBuilderTest, TestInsnAddition) {
  59. const std::string text = R"(
  60. ; CHECK: %18 = OpLabel
  61. ; CHECK: OpPhi %int %int_0 %14
  62. ; CHECK: OpPhi %bool %16 %14
  63. ; CHECK: OpBranch %17
  64. OpCapability Shader
  65. %1 = OpExtInstImport "GLSL.std.450"
  66. OpMemoryModel Logical GLSL450
  67. OpEntryPoint Fragment %2 "main" %3
  68. OpExecutionMode %2 OriginUpperLeft
  69. OpSource GLSL 330
  70. OpName %2 "main"
  71. OpName %4 "i"
  72. OpName %3 "c"
  73. OpDecorate %3 Location 0
  74. %5 = OpTypeVoid
  75. %6 = OpTypeFunction %5
  76. %7 = OpTypeInt 32 1
  77. %8 = OpTypePointer Function %7
  78. %9 = OpConstant %7 0
  79. %10 = OpTypeBool
  80. %11 = OpTypeFloat 32
  81. %12 = OpTypeVector %11 4
  82. %13 = OpTypePointer Output %12
  83. %3 = OpVariable %13 Output
  84. %2 = OpFunction %5 None %6
  85. %14 = OpLabel
  86. %4 = OpVariable %8 Function
  87. OpStore %4 %9
  88. %15 = OpLoad %7 %4
  89. %16 = OpINotEqual %10 %15 %9
  90. OpSelectionMerge %17 None
  91. OpBranchConditional %16 %18 %17
  92. %18 = OpLabel
  93. OpBranch %17
  94. %17 = OpLabel
  95. OpReturn
  96. OpFunctionEnd
  97. )";
  98. {
  99. std::unique_ptr<IRContext> context =
  100. BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
  101. BasicBlock* bb = context->cfg()->block(18);
  102. // Build managers.
  103. context->get_def_use_mgr();
  104. context->get_instr_block(nullptr);
  105. InstructionBuilder builder(context.get(), &*bb->begin());
  106. Instruction* phi1 = builder.AddPhi(7, {9, 14});
  107. Instruction* phi2 = builder.AddPhi(10, {16, 14});
  108. // Make sure the InstructionBuilder did not update the def/use manager.
  109. EXPECT_EQ(context->get_def_use_mgr()->GetDef(phi1->result_id()), nullptr);
  110. EXPECT_EQ(context->get_def_use_mgr()->GetDef(phi2->result_id()), nullptr);
  111. EXPECT_EQ(context->get_instr_block(phi1), nullptr);
  112. EXPECT_EQ(context->get_instr_block(phi2), nullptr);
  113. Match(text, context.get());
  114. }
  115. {
  116. std::unique_ptr<IRContext> context =
  117. BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
  118. // Build managers.
  119. context->get_def_use_mgr();
  120. context->get_instr_block(nullptr);
  121. BasicBlock* bb = context->cfg()->block(18);
  122. InstructionBuilder builder(
  123. context.get(), &*bb->begin(),
  124. IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
  125. Instruction* phi1 = builder.AddPhi(7, {9, 14});
  126. Instruction* phi2 = builder.AddPhi(10, {16, 14});
  127. // Make sure InstructionBuilder updated the def/use manager
  128. EXPECT_NE(context->get_def_use_mgr()->GetDef(phi1->result_id()), nullptr);
  129. EXPECT_NE(context->get_def_use_mgr()->GetDef(phi2->result_id()), nullptr);
  130. EXPECT_NE(context->get_instr_block(phi1), nullptr);
  131. EXPECT_NE(context->get_instr_block(phi2), nullptr);
  132. Match(text, context.get());
  133. }
  134. }
  135. TEST_F(IRBuilderTest, TestCondBranchAddition) {
  136. const std::string text = R"(
  137. ; CHECK: %main = OpFunction %void None %6
  138. ; CHECK-NEXT: %15 = OpLabel
  139. ; CHECK-NEXT: OpSelectionMerge %13 None
  140. ; CHECK-NEXT: OpBranchConditional %true %14 %13
  141. ; CHECK-NEXT: %14 = OpLabel
  142. ; CHECK-NEXT: OpBranch %13
  143. ; CHECK-NEXT: %13 = OpLabel
  144. ; CHECK-NEXT: OpReturn
  145. OpCapability Shader
  146. %1 = OpExtInstImport "GLSL.std.450"
  147. OpMemoryModel Logical GLSL450
  148. OpEntryPoint Fragment %2 "main" %3
  149. OpExecutionMode %2 OriginUpperLeft
  150. OpSource GLSL 330
  151. OpName %2 "main"
  152. OpName %4 "i"
  153. OpName %3 "c"
  154. OpDecorate %3 Location 0
  155. %5 = OpTypeVoid
  156. %6 = OpTypeFunction %5
  157. %7 = OpTypeBool
  158. %8 = OpTypePointer Private %7
  159. %9 = OpConstantTrue %7
  160. %10 = OpTypeFloat 32
  161. %11 = OpTypeVector %10 4
  162. %12 = OpTypePointer Output %11
  163. %3 = OpVariable %12 Output
  164. %4 = OpVariable %8 Private
  165. %2 = OpFunction %5 None %6
  166. %13 = OpLabel
  167. OpReturn
  168. OpFunctionEnd
  169. )";
  170. {
  171. std::unique_ptr<IRContext> context =
  172. BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
  173. Function& fn = *context->module()->begin();
  174. BasicBlock& bb_merge = *fn.begin();
  175. // TODO(1841): Handle id overflow.
  176. fn.begin().InsertBefore(std::unique_ptr<BasicBlock>(
  177. new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
  178. context.get(), spv::Op::OpLabel, 0, context->TakeNextId(), {})))));
  179. BasicBlock& bb_true = *fn.begin();
  180. {
  181. InstructionBuilder builder(context.get(), &*bb_true.begin());
  182. builder.AddBranch(bb_merge.id());
  183. }
  184. // TODO(1841): Handle id overflow.
  185. fn.begin().InsertBefore(std::unique_ptr<BasicBlock>(
  186. new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
  187. context.get(), spv::Op::OpLabel, 0, context->TakeNextId(), {})))));
  188. BasicBlock& bb_cond = *fn.begin();
  189. InstructionBuilder builder(context.get(), &bb_cond);
  190. // This also test consecutive instruction insertion: merge selection +
  191. // branch.
  192. builder.AddConditionalBranch(9, bb_true.id(), bb_merge.id(), bb_merge.id());
  193. Match(text, context.get());
  194. }
  195. }
  196. TEST_F(IRBuilderTest, AddSelect) {
  197. const std::string text = R"(
  198. ; CHECK: [[bool:%\w+]] = OpTypeBool
  199. ; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
  200. ; CHECK: [[true:%\w+]] = OpConstantTrue [[bool]]
  201. ; CHECK: [[u0:%\w+]] = OpConstant [[uint]] 0
  202. ; CHECK: [[u1:%\w+]] = OpConstant [[uint]] 1
  203. ; CHECK: OpSelect [[uint]] [[true]] [[u0]] [[u1]]
  204. OpCapability Kernel
  205. OpCapability Linkage
  206. OpMemoryModel Logical OpenCL
  207. %1 = OpTypeVoid
  208. %2 = OpTypeBool
  209. %3 = OpTypeInt 32 0
  210. %4 = OpConstantTrue %2
  211. %5 = OpConstant %3 0
  212. %6 = OpConstant %3 1
  213. %7 = OpTypeFunction %1
  214. %8 = OpFunction %1 None %7
  215. %9 = OpLabel
  216. OpReturn
  217. OpFunctionEnd
  218. )";
  219. std::unique_ptr<IRContext> context =
  220. BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
  221. EXPECT_NE(nullptr, context);
  222. InstructionBuilder builder(context.get(),
  223. &*context->module()->begin()->begin()->begin());
  224. EXPECT_NE(nullptr, builder.AddSelect(3u, 4u, 5u, 6u));
  225. Match(text, context.get());
  226. }
  227. TEST_F(IRBuilderTest, AddVariable) {
  228. // Use Private beacuse its' enun is 7 which is higher
  229. // than the ID limit.
  230. const std::string text = R"(
  231. ; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
  232. ; CHECK: [[ptr:%\w+]] = OpTypePointer Private [[uint]]
  233. ; CHECK: [[var:%\w+]] = OpVariable [[ptr]] Private
  234. ; CHECK: OpTypeFloat
  235. OpCapability Kernel
  236. OpCapability VectorComputeINTEL
  237. OpCapability Linkage
  238. OpExtension "SPV_INTEL_vector_compute"
  239. OpMemoryModel Logical OpenCL
  240. %1 = OpTypeInt 32 0
  241. %2 = OpTypePointer Private %1
  242. %3 = OpTypeFloat 32
  243. )";
  244. std::unique_ptr<IRContext> context =
  245. BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
  246. EXPECT_NE(nullptr, context) << text;
  247. auto* float_ty = context->get_def_use_mgr()->GetDef(3);
  248. InstructionBuilder builder(context.get(), float_ty);
  249. auto* var = builder.AddVariable(2u, uint32_t(spv::StorageClass::Private));
  250. EXPECT_NE(nullptr, var);
  251. context->get_def_use_mgr()->AnalyzeInstDefUse(var); // should not assert
  252. Match(text, context.get());
  253. }
  254. TEST_F(IRBuilderTest, AddCompositeConstruct) {
  255. const std::string text = R"(
  256. ; CHECK: [[uint:%\w+]] = OpTypeInt
  257. ; CHECK: [[u0:%\w+]] = OpConstant [[uint]] 0
  258. ; CHECK: [[u1:%\w+]] = OpConstant [[uint]] 1
  259. ; CHECK: [[struct:%\w+]] = OpTypeStruct [[uint]] [[uint]] [[uint]] [[uint]]
  260. ; CHECK: OpCompositeConstruct [[struct]] [[u0]] [[u1]] [[u1]] [[u0]]
  261. OpCapability Kernel
  262. OpCapability Linkage
  263. OpMemoryModel Logical OpenCL
  264. %1 = OpTypeVoid
  265. %2 = OpTypeInt 32 0
  266. %3 = OpConstant %2 0
  267. %4 = OpConstant %2 1
  268. %5 = OpTypeStruct %2 %2 %2 %2
  269. %6 = OpTypeFunction %1
  270. %7 = OpFunction %1 None %6
  271. %8 = OpLabel
  272. OpReturn
  273. OpFunctionEnd
  274. )";
  275. std::unique_ptr<IRContext> context =
  276. BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
  277. EXPECT_NE(nullptr, context);
  278. InstructionBuilder builder(context.get(),
  279. &*context->module()->begin()->begin()->begin());
  280. std::vector<uint32_t> ids = {3u, 4u, 4u, 3u};
  281. EXPECT_NE(nullptr, builder.AddCompositeConstruct(5u, ids));
  282. Match(text, context.get());
  283. }
  284. TEST_F(IRBuilderTest, ConstantAdder) {
  285. const std::string text = R"(
  286. ; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
  287. ; CHECK: OpConstant [[uint]] 13
  288. ; CHECK: [[sint:%\w+]] = OpTypeInt 32 1
  289. ; CHECK: OpConstant [[sint]] -1
  290. ; CHECK: OpConstant [[uint]] 1
  291. ; CHECK: OpConstant [[sint]] 34
  292. ; CHECK: OpConstant [[uint]] 0
  293. ; CHECK: OpConstant [[sint]] 0
  294. OpCapability Shader
  295. OpCapability Linkage
  296. OpMemoryModel Logical GLSL450
  297. %1 = OpTypeVoid
  298. %2 = OpTypeFunction %1
  299. %3 = OpFunction %1 None %2
  300. %4 = OpLabel
  301. OpReturn
  302. OpFunctionEnd
  303. )";
  304. std::unique_ptr<IRContext> context =
  305. BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
  306. EXPECT_NE(nullptr, context);
  307. InstructionBuilder builder(context.get(),
  308. &*context->module()->begin()->begin()->begin());
  309. EXPECT_NE(nullptr, builder.GetUintConstant(13));
  310. EXPECT_NE(nullptr, builder.GetSintConstant(-1));
  311. // Try adding the same constants again to make sure they aren't added.
  312. EXPECT_NE(nullptr, builder.GetUintConstant(13));
  313. EXPECT_NE(nullptr, builder.GetSintConstant(-1));
  314. // Try adding different constants to make sure the type is reused.
  315. EXPECT_NE(nullptr, builder.GetUintConstant(1));
  316. EXPECT_NE(nullptr, builder.GetSintConstant(34));
  317. // Try adding 0 as both signed and unsigned.
  318. EXPECT_NE(nullptr, builder.GetUintConstant(0));
  319. EXPECT_NE(nullptr, builder.GetSintConstant(0));
  320. Match(text, context.get());
  321. }
  322. TEST_F(IRBuilderTest, ConstantAdderTypeAlreadyExists) {
  323. const std::string text = R"(
  324. ; CHECK: OpConstant %uint 13
  325. ; CHECK: OpConstant %int -1
  326. ; CHECK: OpConstant %uint 1
  327. ; CHECK: OpConstant %int 34
  328. ; CHECK: OpConstant %uint 0
  329. ; CHECK: OpConstant %int 0
  330. OpCapability Shader
  331. OpCapability Linkage
  332. OpMemoryModel Logical GLSL450
  333. %1 = OpTypeVoid
  334. %uint = OpTypeInt 32 0
  335. %int = OpTypeInt 32 1
  336. %4 = OpTypeFunction %1
  337. %5 = OpFunction %1 None %4
  338. %6 = OpLabel
  339. OpReturn
  340. OpFunctionEnd
  341. )";
  342. std::unique_ptr<IRContext> context =
  343. BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
  344. EXPECT_NE(nullptr, context);
  345. InstructionBuilder builder(context.get(),
  346. &*context->module()->begin()->begin()->begin());
  347. Instruction* const_1 = builder.GetUintConstant(13);
  348. Instruction* const_2 = builder.GetSintConstant(-1);
  349. EXPECT_NE(nullptr, const_1);
  350. EXPECT_NE(nullptr, const_2);
  351. // Try adding the same constants again to make sure they aren't added.
  352. EXPECT_EQ(const_1, builder.GetUintConstant(13));
  353. EXPECT_EQ(const_2, builder.GetSintConstant(-1));
  354. Instruction* const_3 = builder.GetUintConstant(1);
  355. Instruction* const_4 = builder.GetSintConstant(34);
  356. // Try adding different constants to make sure the type is reused.
  357. EXPECT_NE(nullptr, const_3);
  358. EXPECT_NE(nullptr, const_4);
  359. Instruction* const_5 = builder.GetUintConstant(0);
  360. Instruction* const_6 = builder.GetSintConstant(0);
  361. // Try adding 0 as both signed and unsigned.
  362. EXPECT_NE(nullptr, const_5);
  363. EXPECT_NE(nullptr, const_6);
  364. // They have the same value but different types so should be unique.
  365. EXPECT_NE(const_5, const_6);
  366. // Check the types are correct.
  367. uint32_t type_id_unsigned = const_1->GetSingleWordOperand(0);
  368. uint32_t type_id_signed = const_2->GetSingleWordOperand(0);
  369. EXPECT_NE(type_id_unsigned, type_id_signed);
  370. EXPECT_EQ(const_3->GetSingleWordOperand(0), type_id_unsigned);
  371. EXPECT_EQ(const_5->GetSingleWordOperand(0), type_id_unsigned);
  372. EXPECT_EQ(const_4->GetSingleWordOperand(0), type_id_signed);
  373. EXPECT_EQ(const_6->GetSingleWordOperand(0), type_id_signed);
  374. Match(text, context.get());
  375. }
  376. TEST_F(IRBuilderTest, AccelerationStructureNV) {
  377. const std::string text = R"(
  378. ; CHECK: OpTypeAccelerationStructureKHR
  379. OpCapability Shader
  380. OpCapability RayTracingNV
  381. OpExtension "SPV_NV_ray_tracing"
  382. OpMemoryModel Logical GLSL450
  383. OpEntryPoint Fragment %8 "main"
  384. OpExecutionMode %8 OriginUpperLeft
  385. %1 = OpTypeVoid
  386. %2 = OpTypeBool
  387. %3 = OpTypeAccelerationStructureNV
  388. %7 = OpTypeFunction %1
  389. %8 = OpFunction %1 None %7
  390. %9 = OpLabel
  391. OpReturn
  392. OpFunctionEnd
  393. )";
  394. std::unique_ptr<IRContext> context =
  395. BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
  396. EXPECT_NE(nullptr, context);
  397. InstructionBuilder builder(context.get(),
  398. &*context->module()->begin()->begin()->begin());
  399. Match(text, context.get());
  400. }
  401. } // namespace
  402. } // namespace opt
  403. } // namespace spvtools