ir_builder.cpp 14 KB

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