emit_spirv.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <span>
  4. #include <tuple>
  5. #include <type_traits>
  6. #include <utility>
  7. #include <vector>
  8. #include "common/settings.h"
  9. #include "shader_recompiler/backend/spirv/emit_spirv.h"
  10. #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
  11. #include "shader_recompiler/backend/spirv/spirv_emit_context.h"
  12. #include "shader_recompiler/frontend/ir/basic_block.h"
  13. #include "shader_recompiler/frontend/ir/program.h"
  14. namespace Shader::Backend::SPIRV {
  15. namespace {
  16. template <class Func>
  17. struct FuncTraits {};
  18. template <class ReturnType_, class... Args>
  19. struct FuncTraits<ReturnType_ (*)(Args...)> {
  20. using ReturnType = ReturnType_;
  21. static constexpr size_t NUM_ARGS = sizeof...(Args);
  22. template <size_t I>
  23. using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
  24. };
  25. #ifdef _MSC_VER
  26. #pragma warning(push)
  27. #pragma warning(disable : 4702) // Ignore unreachable code warning
  28. #endif
  29. template <auto func, typename... Args>
  30. void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
  31. inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
  32. }
  33. #ifdef _MSC_VER
  34. #pragma warning(pop)
  35. #endif
  36. template <typename ArgType>
  37. ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
  38. if constexpr (std::is_same_v<ArgType, Id>) {
  39. return ctx.Def(arg);
  40. } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
  41. return arg;
  42. } else if constexpr (std::is_same_v<ArgType, u32>) {
  43. return arg.U32();
  44. } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
  45. return arg.Attribute();
  46. } else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
  47. return arg.Patch();
  48. } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
  49. return arg.Reg();
  50. }
  51. }
  52. template <auto func, bool is_first_arg_inst, size_t... I>
  53. void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
  54. using Traits = FuncTraits<decltype(func)>;
  55. if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
  56. if constexpr (is_first_arg_inst) {
  57. SetDefinition<func>(
  58. ctx, inst, inst,
  59. Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
  60. } else {
  61. SetDefinition<func>(
  62. ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
  63. }
  64. } else {
  65. if constexpr (is_first_arg_inst) {
  66. func(ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
  67. } else {
  68. func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
  69. }
  70. }
  71. }
  72. template <auto func>
  73. void Invoke(EmitContext& ctx, IR::Inst* inst) {
  74. using Traits = FuncTraits<decltype(func)>;
  75. static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
  76. if constexpr (Traits::NUM_ARGS == 1) {
  77. Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
  78. } else {
  79. using FirstArgType = typename Traits::template ArgType<1>;
  80. static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst*>;
  81. using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
  82. Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
  83. }
  84. }
  85. void EmitInst(EmitContext& ctx, IR::Inst* inst) {
  86. switch (inst->GetOpcode()) {
  87. #define OPCODE(name, result_type, ...) \
  88. case IR::Opcode::name: \
  89. return Invoke<&Emit##name>(ctx, inst);
  90. #include "shader_recompiler/frontend/ir/opcodes.inc"
  91. #undef OPCODE
  92. }
  93. throw LogicError("Invalid opcode {}", inst->GetOpcode());
  94. }
  95. Id TypeId(const EmitContext& ctx, IR::Type type) {
  96. switch (type) {
  97. case IR::Type::U1:
  98. return ctx.U1;
  99. case IR::Type::U32:
  100. return ctx.U32[1];
  101. default:
  102. throw NotImplementedException("Phi node type {}", type);
  103. }
  104. }
  105. void Traverse(EmitContext& ctx, IR::Program& program) {
  106. IR::Block* current_block{};
  107. for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
  108. switch (node.type) {
  109. case IR::AbstractSyntaxNode::Type::Block: {
  110. const Id label{node.data.block->Definition<Id>()};
  111. if (current_block) {
  112. ctx.OpBranch(label);
  113. }
  114. current_block = node.data.block;
  115. ctx.AddLabel(label);
  116. for (IR::Inst& inst : node.data.block->Instructions()) {
  117. EmitInst(ctx, &inst);
  118. }
  119. break;
  120. }
  121. case IR::AbstractSyntaxNode::Type::If: {
  122. const Id if_label{node.data.if_node.body->Definition<Id>()};
  123. const Id endif_label{node.data.if_node.merge->Definition<Id>()};
  124. ctx.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone);
  125. ctx.OpBranchConditional(ctx.Def(node.data.if_node.cond), if_label, endif_label);
  126. break;
  127. }
  128. case IR::AbstractSyntaxNode::Type::Loop: {
  129. const Id body_label{node.data.loop.body->Definition<Id>()};
  130. const Id continue_label{node.data.loop.continue_block->Definition<Id>()};
  131. const Id endloop_label{node.data.loop.merge->Definition<Id>()};
  132. ctx.OpLoopMerge(endloop_label, continue_label, spv::LoopControlMask::MaskNone);
  133. ctx.OpBranch(body_label);
  134. break;
  135. }
  136. case IR::AbstractSyntaxNode::Type::Break: {
  137. const Id break_label{node.data.break_node.merge->Definition<Id>()};
  138. const Id skip_label{node.data.break_node.skip->Definition<Id>()};
  139. ctx.OpBranchConditional(ctx.Def(node.data.break_node.cond), break_label, skip_label);
  140. break;
  141. }
  142. case IR::AbstractSyntaxNode::Type::EndIf:
  143. if (current_block) {
  144. ctx.OpBranch(node.data.end_if.merge->Definition<Id>());
  145. }
  146. break;
  147. case IR::AbstractSyntaxNode::Type::Repeat: {
  148. Id cond{ctx.Def(node.data.repeat.cond)};
  149. if (!Settings::values.disable_shader_loop_safety_checks) {
  150. const Id pointer_type{ctx.TypePointer(spv::StorageClass::Private, ctx.U32[1])};
  151. const Id safety_counter{ctx.AddGlobalVariable(
  152. pointer_type, spv::StorageClass::Private, ctx.Const(0x2000u))};
  153. if (ctx.profile.supported_spirv >= 0x00010400) {
  154. ctx.interfaces.push_back(safety_counter);
  155. }
  156. const Id old_counter{ctx.OpLoad(ctx.U32[1], safety_counter)};
  157. const Id new_counter{ctx.OpISub(ctx.U32[1], old_counter, ctx.Const(1u))};
  158. ctx.OpStore(safety_counter, new_counter);
  159. const Id safety_cond{
  160. ctx.OpSGreaterThanEqual(ctx.U1, new_counter, ctx.u32_zero_value)};
  161. cond = ctx.OpLogicalAnd(ctx.U1, cond, safety_cond);
  162. }
  163. const Id loop_header_label{node.data.repeat.loop_header->Definition<Id>()};
  164. const Id merge_label{node.data.repeat.merge->Definition<Id>()};
  165. ctx.OpBranchConditional(cond, loop_header_label, merge_label);
  166. break;
  167. }
  168. case IR::AbstractSyntaxNode::Type::Return:
  169. ctx.OpReturn();
  170. break;
  171. case IR::AbstractSyntaxNode::Type::Unreachable:
  172. ctx.OpUnreachable();
  173. break;
  174. }
  175. if (node.type != IR::AbstractSyntaxNode::Type::Block) {
  176. current_block = nullptr;
  177. }
  178. }
  179. }
  180. Id DefineMain(EmitContext& ctx, IR::Program& program) {
  181. const Id void_function{ctx.TypeFunction(ctx.void_id)};
  182. const Id main{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)};
  183. for (IR::Block* const block : program.blocks) {
  184. block->SetDefinition(ctx.OpLabel());
  185. }
  186. Traverse(ctx, program);
  187. ctx.OpFunctionEnd();
  188. return main;
  189. }
  190. spv::ExecutionMode ExecutionMode(TessPrimitive primitive) {
  191. switch (primitive) {
  192. case TessPrimitive::Isolines:
  193. return spv::ExecutionMode::Isolines;
  194. case TessPrimitive::Triangles:
  195. return spv::ExecutionMode::Triangles;
  196. case TessPrimitive::Quads:
  197. return spv::ExecutionMode::Quads;
  198. }
  199. throw InvalidArgument("Tessellation primitive {}", primitive);
  200. }
  201. spv::ExecutionMode ExecutionMode(TessSpacing spacing) {
  202. switch (spacing) {
  203. case TessSpacing::Equal:
  204. return spv::ExecutionMode::SpacingEqual;
  205. case TessSpacing::FractionalOdd:
  206. return spv::ExecutionMode::SpacingFractionalOdd;
  207. case TessSpacing::FractionalEven:
  208. return spv::ExecutionMode::SpacingFractionalEven;
  209. }
  210. throw InvalidArgument("Tessellation spacing {}", spacing);
  211. }
  212. void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
  213. const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size());
  214. spv::ExecutionModel execution_model{};
  215. switch (program.stage) {
  216. case Stage::Compute: {
  217. const std::array<u32, 3> workgroup_size{program.workgroup_size};
  218. execution_model = spv::ExecutionModel::GLCompute;
  219. ctx.AddExecutionMode(main, spv::ExecutionMode::LocalSize, workgroup_size[0],
  220. workgroup_size[1], workgroup_size[2]);
  221. break;
  222. }
  223. case Stage::VertexB:
  224. execution_model = spv::ExecutionModel::Vertex;
  225. break;
  226. case Stage::TessellationControl:
  227. execution_model = spv::ExecutionModel::TessellationControl;
  228. ctx.AddCapability(spv::Capability::Tessellation);
  229. ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.invocations);
  230. break;
  231. case Stage::TessellationEval:
  232. execution_model = spv::ExecutionModel::TessellationEvaluation;
  233. ctx.AddCapability(spv::Capability::Tessellation);
  234. ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_primitive));
  235. ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_spacing));
  236. ctx.AddExecutionMode(main, ctx.runtime_info.tess_clockwise
  237. ? spv::ExecutionMode::VertexOrderCw
  238. : spv::ExecutionMode::VertexOrderCcw);
  239. break;
  240. case Stage::Geometry:
  241. execution_model = spv::ExecutionModel::Geometry;
  242. ctx.AddCapability(spv::Capability::Geometry);
  243. ctx.AddCapability(spv::Capability::GeometryStreams);
  244. switch (ctx.runtime_info.input_topology) {
  245. case InputTopology::Points:
  246. ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints);
  247. break;
  248. case InputTopology::Lines:
  249. ctx.AddExecutionMode(main, spv::ExecutionMode::InputLines);
  250. break;
  251. case InputTopology::LinesAdjacency:
  252. ctx.AddExecutionMode(main, spv::ExecutionMode::InputLinesAdjacency);
  253. break;
  254. case InputTopology::Triangles:
  255. ctx.AddExecutionMode(main, spv::ExecutionMode::Triangles);
  256. break;
  257. case InputTopology::TrianglesAdjacency:
  258. ctx.AddExecutionMode(main, spv::ExecutionMode::InputTrianglesAdjacency);
  259. break;
  260. }
  261. switch (program.output_topology) {
  262. case OutputTopology::PointList:
  263. ctx.AddExecutionMode(main, spv::ExecutionMode::OutputPoints);
  264. break;
  265. case OutputTopology::LineStrip:
  266. ctx.AddExecutionMode(main, spv::ExecutionMode::OutputLineStrip);
  267. break;
  268. case OutputTopology::TriangleStrip:
  269. ctx.AddExecutionMode(main, spv::ExecutionMode::OutputTriangleStrip);
  270. break;
  271. }
  272. if (program.info.stores[IR::Attribute::PointSize]) {
  273. ctx.AddCapability(spv::Capability::GeometryPointSize);
  274. }
  275. ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.output_vertices);
  276. ctx.AddExecutionMode(main, spv::ExecutionMode::Invocations, program.invocations);
  277. if (program.is_geometry_passthrough) {
  278. if (ctx.profile.support_geometry_shader_passthrough) {
  279. ctx.AddExtension("SPV_NV_geometry_shader_passthrough");
  280. ctx.AddCapability(spv::Capability::GeometryShaderPassthroughNV);
  281. } else {
  282. LOG_WARNING(Shader_SPIRV, "Geometry shader passthrough used with no support");
  283. }
  284. }
  285. break;
  286. case Stage::Fragment:
  287. execution_model = spv::ExecutionModel::Fragment;
  288. if (ctx.profile.lower_left_origin_mode) {
  289. ctx.AddExecutionMode(main, spv::ExecutionMode::OriginLowerLeft);
  290. } else {
  291. ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
  292. }
  293. if (program.info.stores_frag_depth) {
  294. ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing);
  295. }
  296. if (ctx.runtime_info.force_early_z) {
  297. ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests);
  298. }
  299. break;
  300. default:
  301. throw NotImplementedException("Stage {}", program.stage);
  302. }
  303. ctx.AddEntryPoint(execution_model, main, "main", interfaces);
  304. }
  305. void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx,
  306. Id main_func) {
  307. const Info& info{program.info};
  308. if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) {
  309. LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader");
  310. } else if (info.uses_fp32_denorms_flush) {
  311. if (profile.support_fp32_denorm_flush) {
  312. ctx.AddCapability(spv::Capability::DenormFlushToZero);
  313. ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U);
  314. } else {
  315. // Drivers will most likely flush denorms by default, no need to warn
  316. }
  317. } else if (info.uses_fp32_denorms_preserve) {
  318. if (profile.support_fp32_denorm_preserve) {
  319. ctx.AddCapability(spv::Capability::DenormPreserve);
  320. ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 32U);
  321. } else {
  322. LOG_DEBUG(Shader_SPIRV, "Fp32 denorm preserve used in shader without host support");
  323. }
  324. }
  325. if (!profile.support_separate_denorm_behavior || profile.has_broken_fp16_float_controls) {
  326. // No separate denorm behavior
  327. return;
  328. }
  329. if (info.uses_fp16_denorms_flush && info.uses_fp16_denorms_preserve) {
  330. LOG_DEBUG(Shader_SPIRV, "Fp16 denorm flush and preserve on the same shader");
  331. } else if (info.uses_fp16_denorms_flush) {
  332. if (profile.support_fp16_denorm_flush) {
  333. ctx.AddCapability(spv::Capability::DenormFlushToZero);
  334. ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 16U);
  335. } else {
  336. // Same as fp32, no need to warn as most drivers will flush by default
  337. }
  338. } else if (info.uses_fp16_denorms_preserve) {
  339. if (profile.support_fp16_denorm_preserve) {
  340. ctx.AddCapability(spv::Capability::DenormPreserve);
  341. ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 16U);
  342. } else {
  343. LOG_DEBUG(Shader_SPIRV, "Fp16 denorm preserve used in shader without host support");
  344. }
  345. }
  346. }
  347. void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& program,
  348. EmitContext& ctx, Id main_func) {
  349. if (profile.has_broken_fp16_float_controls && program.info.uses_fp16) {
  350. return;
  351. }
  352. if (program.info.uses_fp16 && profile.support_fp16_signed_zero_nan_preserve) {
  353. ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
  354. ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 16U);
  355. }
  356. if (profile.support_fp32_signed_zero_nan_preserve) {
  357. ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
  358. ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 32U);
  359. }
  360. if (program.info.uses_fp64 && profile.support_fp64_signed_zero_nan_preserve) {
  361. ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
  362. ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 64U);
  363. }
  364. }
  365. void SetupTransformFeedbackCapabilities(EmitContext& ctx, Id main_func) {
  366. if (ctx.runtime_info.xfb_count == 0) {
  367. return;
  368. }
  369. ctx.AddCapability(spv::Capability::TransformFeedback);
  370. ctx.AddExecutionMode(main_func, spv::ExecutionMode::Xfb);
  371. }
  372. void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ctx) {
  373. if (info.uses_sampled_1d) {
  374. ctx.AddCapability(spv::Capability::Sampled1D);
  375. }
  376. if (info.uses_sparse_residency) {
  377. ctx.AddCapability(spv::Capability::SparseResidency);
  378. }
  379. if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) {
  380. if (profile.supported_spirv < 0x00010600) {
  381. ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
  382. }
  383. ctx.AddCapability(spv::Capability::DemoteToHelperInvocation);
  384. }
  385. if (info.stores[IR::Attribute::ViewportIndex] && profile.support_multi_viewport) {
  386. ctx.AddCapability(spv::Capability::MultiViewport);
  387. }
  388. if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
  389. ctx.AddExtension("SPV_NV_viewport_array2");
  390. ctx.AddCapability(spv::Capability::ShaderViewportMaskNV);
  391. }
  392. if (info.stores[IR::Attribute::Layer] || info.stores[IR::Attribute::ViewportIndex]) {
  393. if (profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
  394. ctx.AddExtension("SPV_EXT_shader_viewport_index_layer");
  395. ctx.AddCapability(spv::Capability::ShaderViewportIndexLayerEXT);
  396. }
  397. }
  398. if (!profile.support_vertex_instance_id &&
  399. (info.loads[IR::Attribute::InstanceId] || info.loads[IR::Attribute::VertexId])) {
  400. ctx.AddExtension("SPV_KHR_shader_draw_parameters");
  401. ctx.AddCapability(spv::Capability::DrawParameters);
  402. }
  403. if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
  404. info.uses_subgroup_shuffles) &&
  405. profile.support_vote) {
  406. ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
  407. ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
  408. if (!profile.warp_size_potentially_larger_than_guest) {
  409. // vote ops are only used when not taking the long path
  410. ctx.AddCapability(spv::Capability::GroupNonUniformVote);
  411. }
  412. }
  413. if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
  414. ctx.AddCapability(spv::Capability::Int64Atomics);
  415. }
  416. if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) {
  417. ctx.AddCapability(spv::Capability::StorageImageReadWithoutFormat);
  418. }
  419. if (info.uses_typeless_image_writes) {
  420. ctx.AddCapability(spv::Capability::StorageImageWriteWithoutFormat);
  421. }
  422. if (info.uses_image_buffers) {
  423. ctx.AddCapability(spv::Capability::ImageBuffer);
  424. }
  425. if (info.uses_sample_id) {
  426. ctx.AddCapability(spv::Capability::SampleRateShading);
  427. }
  428. if (info.uses_derivatives) {
  429. ctx.AddCapability(spv::Capability::DerivativeControl);
  430. }
  431. // TODO: Track this usage
  432. ctx.AddCapability(spv::Capability::ImageGatherExtended);
  433. ctx.AddCapability(spv::Capability::ImageQuery);
  434. ctx.AddCapability(spv::Capability::SampledBuffer);
  435. }
  436. void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
  437. auto inst{program.blocks.front()->begin()};
  438. size_t block_index{0};
  439. ctx.PatchDeferredPhi([&](size_t phi_arg) {
  440. if (phi_arg == 0) {
  441. ++inst;
  442. if (inst == program.blocks[block_index]->end() ||
  443. inst->GetOpcode() != IR::Opcode::Phi) {
  444. do {
  445. ++block_index;
  446. inst = program.blocks[block_index]->begin();
  447. } while (inst->GetOpcode() != IR::Opcode::Phi);
  448. }
  449. }
  450. return ctx.Def(inst->Arg(phi_arg));
  451. });
  452. }
  453. } // Anonymous namespace
  454. std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
  455. IR::Program& program, Bindings& bindings) {
  456. EmitContext ctx{profile, runtime_info, program, bindings};
  457. const Id main{DefineMain(ctx, program)};
  458. DefineEntryPoint(program, ctx, main);
  459. if (profile.support_float_controls) {
  460. ctx.AddExtension("SPV_KHR_float_controls");
  461. SetupDenormControl(profile, program, ctx, main);
  462. SetupSignedNanCapabilities(profile, program, ctx, main);
  463. }
  464. SetupCapabilities(profile, program.info, ctx);
  465. SetupTransformFeedbackCapabilities(ctx, main);
  466. PatchPhiNodes(program, ctx);
  467. return ctx.Assemble();
  468. }
  469. Id EmitPhi(EmitContext& ctx, IR::Inst* inst) {
  470. const size_t num_args{inst->NumArgs()};
  471. boost::container::small_vector<Id, 32> blocks;
  472. blocks.reserve(num_args);
  473. for (size_t index = 0; index < num_args; ++index) {
  474. blocks.push_back(inst->PhiBlock(index)->Definition<Id>());
  475. }
  476. // The type of a phi instruction is stored in its flags
  477. const Id result_type{TypeId(ctx, inst->Flags<IR::Type>())};
  478. return ctx.DeferredOpPhi(result_type, std::span(blocks.data(), blocks.size()));
  479. }
  480. void EmitVoid(EmitContext&) {}
  481. Id EmitIdentity(EmitContext& ctx, const IR::Value& value) {
  482. const Id id{ctx.Def(value)};
  483. if (!Sirit::ValidId(id)) {
  484. throw NotImplementedException("Forward identity declaration");
  485. }
  486. return id;
  487. }
  488. Id EmitConditionRef(EmitContext& ctx, const IR::Value& value) {
  489. const Id id{ctx.Def(value)};
  490. if (!Sirit::ValidId(id)) {
  491. throw NotImplementedException("Forward identity declaration");
  492. }
  493. return id;
  494. }
  495. void EmitReference(EmitContext&) {}
  496. void EmitPhiMove(EmitContext&) {
  497. throw LogicError("Unreachable instruction");
  498. }
  499. void EmitGetZeroFromOp(EmitContext&) {
  500. throw LogicError("Unreachable instruction");
  501. }
  502. void EmitGetSignFromOp(EmitContext&) {
  503. throw LogicError("Unreachable instruction");
  504. }
  505. void EmitGetCarryFromOp(EmitContext&) {
  506. throw LogicError("Unreachable instruction");
  507. }
  508. void EmitGetOverflowFromOp(EmitContext&) {
  509. throw LogicError("Unreachable instruction");
  510. }
  511. void EmitGetSparseFromOp(EmitContext&) {
  512. throw LogicError("Unreachable instruction");
  513. }
  514. void EmitGetInBoundsFromOp(EmitContext&) {
  515. throw LogicError("Unreachable instruction");
  516. }
  517. } // namespace Shader::Backend::SPIRV