emit_spirv.cpp 22 KB

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