emit_glasm.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <ranges>
  5. #include <string>
  6. #include <tuple>
  7. #include "shader_recompiler/backend/bindings.h"
  8. #include "shader_recompiler/backend/glasm/emit_context.h"
  9. #include "shader_recompiler/backend/glasm/emit_glasm.h"
  10. #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
  11. #include "shader_recompiler/frontend/ir/ir_emitter.h"
  12. #include "shader_recompiler/frontend/ir/program.h"
  13. #include "shader_recompiler/profile.h"
  14. namespace Shader::Backend::GLASM {
  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. template <typename T>
  26. struct Identity {
  27. Identity(T data_) : data{data_} {}
  28. T Extract() {
  29. return data;
  30. }
  31. T data;
  32. };
  33. template <bool scalar>
  34. class RegWrapper {
  35. public:
  36. RegWrapper(EmitContext& ctx, const IR::Value& ir_value) : reg_alloc{ctx.reg_alloc} {
  37. const Value value{reg_alloc.Peek(ir_value)};
  38. if (value.type == Type::Register) {
  39. inst = ir_value.InstRecursive();
  40. reg = Register{value};
  41. } else {
  42. const bool is_long{value.type == Type::F64 || value.type == Type::U64};
  43. reg = is_long ? reg_alloc.AllocLongReg() : reg_alloc.AllocReg();
  44. }
  45. switch (value.type) {
  46. case Type::Register:
  47. case Type::Void:
  48. break;
  49. case Type::U32:
  50. ctx.Add("MOV.U {}.x,{};", reg, value.imm_u32);
  51. break;
  52. case Type::S32:
  53. ctx.Add("MOV.S {}.x,{};", reg, value.imm_s32);
  54. break;
  55. case Type::F32:
  56. ctx.Add("MOV.F {}.x,{};", reg, value.imm_f32);
  57. break;
  58. case Type::U64:
  59. ctx.Add("MOV.U64 {}.x,{};", reg, value.imm_u64);
  60. break;
  61. case Type::F64:
  62. ctx.Add("MOV.F64 {}.x,{};", reg, value.imm_f64);
  63. break;
  64. }
  65. }
  66. auto Extract() {
  67. if (inst) {
  68. reg_alloc.Unref(*inst);
  69. } else {
  70. reg_alloc.FreeReg(reg);
  71. }
  72. return std::conditional_t<scalar, ScalarRegister, Register>{Value{reg}};
  73. }
  74. private:
  75. RegAlloc& reg_alloc;
  76. IR::Inst* inst{};
  77. Register reg{};
  78. };
  79. template <typename ArgType>
  80. class ValueWrapper {
  81. public:
  82. ValueWrapper(EmitContext& ctx, const IR::Value& ir_value_)
  83. : reg_alloc{ctx.reg_alloc}, ir_value{ir_value_}, value{reg_alloc.Peek(ir_value)} {}
  84. ArgType Extract() {
  85. if (!ir_value.IsImmediate()) {
  86. reg_alloc.Unref(*ir_value.InstRecursive());
  87. }
  88. return value;
  89. }
  90. private:
  91. RegAlloc& reg_alloc;
  92. const IR::Value& ir_value;
  93. ArgType value;
  94. };
  95. template <typename ArgType>
  96. auto Arg(EmitContext& ctx, const IR::Value& arg) {
  97. if constexpr (std::is_same_v<ArgType, Register>) {
  98. return RegWrapper<false>{ctx, arg};
  99. } else if constexpr (std::is_same_v<ArgType, ScalarRegister>) {
  100. return RegWrapper<true>{ctx, arg};
  101. } else if constexpr (std::is_base_of_v<Value, ArgType>) {
  102. return ValueWrapper<ArgType>{ctx, arg};
  103. } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
  104. return Identity<const IR::Value&>{arg};
  105. } else if constexpr (std::is_same_v<ArgType, u32>) {
  106. return Identity{arg.U32()};
  107. } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
  108. return Identity{arg.Attribute()};
  109. } else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
  110. return Identity{arg.Patch()};
  111. } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
  112. return Identity{arg.Reg()};
  113. }
  114. }
  115. template <auto func, bool is_first_arg_inst>
  116. struct InvokeCall {
  117. template <typename... Args>
  118. InvokeCall(EmitContext& ctx, IR::Inst* inst, Args&&... args) {
  119. if constexpr (is_first_arg_inst) {
  120. func(ctx, *inst, args.Extract()...);
  121. } else {
  122. func(ctx, args.Extract()...);
  123. }
  124. }
  125. };
  126. template <auto func, bool is_first_arg_inst, size_t... I>
  127. void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
  128. using Traits = FuncTraits<decltype(func)>;
  129. if constexpr (is_first_arg_inst) {
  130. InvokeCall<func, is_first_arg_inst>{
  131. ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...};
  132. } else {
  133. InvokeCall<func, is_first_arg_inst>{
  134. ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...};
  135. }
  136. }
  137. template <auto func>
  138. void Invoke(EmitContext& ctx, IR::Inst* inst) {
  139. using Traits = FuncTraits<decltype(func)>;
  140. static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
  141. if constexpr (Traits::NUM_ARGS == 1) {
  142. Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
  143. } else {
  144. using FirstArgType = typename Traits::template ArgType<1>;
  145. static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
  146. using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
  147. Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
  148. }
  149. }
  150. void EmitInst(EmitContext& ctx, IR::Inst* inst) {
  151. switch (inst->GetOpcode()) {
  152. #define OPCODE(name, result_type, ...) \
  153. case IR::Opcode::name: \
  154. return Invoke<&Emit##name>(ctx, inst);
  155. #include "shader_recompiler/frontend/ir/opcodes.inc"
  156. #undef OPCODE
  157. }
  158. throw LogicError("Invalid opcode {}", inst->GetOpcode());
  159. }
  160. void Precolor(EmitContext& ctx, const IR::Program& program) {
  161. for (IR::Block* const block : program.blocks) {
  162. for (IR::Inst& phi : block->Instructions() | std::views::take_while(IR::IsPhi)) {
  163. switch (phi.Arg(0).Type()) {
  164. case IR::Type::U1:
  165. case IR::Type::U32:
  166. case IR::Type::F32:
  167. ctx.reg_alloc.Define(phi);
  168. break;
  169. case IR::Type::U64:
  170. case IR::Type::F64:
  171. ctx.reg_alloc.LongDefine(phi);
  172. break;
  173. default:
  174. throw NotImplementedException("Phi node type {}", phi.Type());
  175. }
  176. const size_t num_args{phi.NumArgs()};
  177. for (size_t i = 0; i < num_args; ++i) {
  178. IR::IREmitter{*phi.PhiBlock(i)}.PhiMove(phi, phi.Arg(i));
  179. }
  180. // Add reference to the phi node on the phi predecessor to avoid overwritting it
  181. for (size_t i = 0; i < num_args; ++i) {
  182. IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
  183. }
  184. }
  185. }
  186. }
  187. void EmitCode(EmitContext& ctx, const IR::Program& program) {
  188. const auto eval{
  189. [&](const IR::U1& cond) { return ScalarS32{ctx.reg_alloc.Consume(IR::Value{cond})}; }};
  190. for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
  191. switch (node.type) {
  192. case IR::AbstractSyntaxNode::Type::Block:
  193. for (IR::Inst& inst : node.data.block->Instructions()) {
  194. EmitInst(ctx, &inst);
  195. }
  196. break;
  197. case IR::AbstractSyntaxNode::Type::If:
  198. ctx.Add("MOV.S.CC RC,{};"
  199. "IF NE.x;",
  200. eval(node.data.if_node.cond));
  201. break;
  202. case IR::AbstractSyntaxNode::Type::EndIf:
  203. ctx.Add("ENDIF;");
  204. break;
  205. case IR::AbstractSyntaxNode::Type::Loop:
  206. ctx.Add("REP;");
  207. break;
  208. case IR::AbstractSyntaxNode::Type::Repeat:
  209. if (node.data.repeat.cond.IsImmediate()) {
  210. if (node.data.repeat.cond.U1()) {
  211. ctx.Add("ENDREP;");
  212. } else {
  213. ctx.Add("BRK;"
  214. "ENDREP;");
  215. }
  216. } else {
  217. ctx.Add("MOV.S.CC RC,{};"
  218. "BRK (EQ.x);"
  219. "ENDREP;",
  220. eval(node.data.repeat.cond));
  221. }
  222. break;
  223. case IR::AbstractSyntaxNode::Type::Break:
  224. if (node.data.break_node.cond.IsImmediate()) {
  225. if (node.data.break_node.cond.U1()) {
  226. ctx.Add("BRK;");
  227. }
  228. } else {
  229. ctx.Add("MOV.S.CC RC,{};"
  230. "BRK (NE.x);",
  231. eval(node.data.break_node.cond));
  232. }
  233. break;
  234. case IR::AbstractSyntaxNode::Type::Return:
  235. case IR::AbstractSyntaxNode::Type::Unreachable:
  236. ctx.Add("RET;");
  237. break;
  238. }
  239. }
  240. }
  241. void SetupOptions(const IR::Program& program, const Profile& profile, std::string& header) {
  242. const Info& info{program.info};
  243. const Stage stage{program.stage};
  244. // TODO: Track the shared atomic ops
  245. header += "OPTION NV_internal;"
  246. "OPTION NV_shader_storage_buffer;"
  247. "OPTION NV_gpu_program_fp64;"
  248. "OPTION NV_bindless_texture;"
  249. "OPTION ARB_derivative_control;"
  250. "OPTION EXT_shader_image_load_formatted;";
  251. if (info.uses_int64_bit_atomics) {
  252. header += "OPTION NV_shader_atomic_int64;";
  253. }
  254. if (info.uses_atomic_f32_add) {
  255. header += "OPTION NV_shader_atomic_float;";
  256. }
  257. if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
  258. header += "OPTION NV_shader_atomic_fp16_vector;";
  259. }
  260. if (info.uses_subgroup_invocation_id || info.uses_subgroup_mask || info.uses_subgroup_vote ||
  261. info.uses_fswzadd) {
  262. header += "OPTION NV_shader_thread_group;";
  263. }
  264. if (info.uses_subgroup_shuffles) {
  265. header += "OPTION NV_shader_thread_shuffle;";
  266. }
  267. if (info.uses_sparse_residency) {
  268. header += "OPTION EXT_sparse_texture2;";
  269. }
  270. if ((info.stores_viewport_index || info.stores_layer) && stage != Stage::Geometry) {
  271. if (profile.support_viewport_index_layer_non_geometry) {
  272. header += "OPTION NV_viewport_array2;";
  273. }
  274. }
  275. const auto non_zero_frag_colors{info.stores_frag_color | std::views::drop(1)};
  276. if (std::ranges::find(non_zero_frag_colors, true) != non_zero_frag_colors.end()) {
  277. header += "OPTION ARB_draw_buffers;";
  278. }
  279. }
  280. std::string_view StageHeader(Stage stage) {
  281. switch (stage) {
  282. case Stage::VertexA:
  283. case Stage::VertexB:
  284. return "!!NVvp5.0\n";
  285. case Stage::TessellationControl:
  286. return "!!NVtcp5.0\n";
  287. case Stage::TessellationEval:
  288. return "!!NVtep5.0\n";
  289. case Stage::Geometry:
  290. return "!!NVgp5.0\n";
  291. case Stage::Fragment:
  292. return "!!NVfp5.0\n";
  293. case Stage::Compute:
  294. return "!!NVcp5.0\n";
  295. }
  296. throw InvalidArgument("Invalid stage {}", stage);
  297. }
  298. std::string_view InputPrimitive(InputTopology topology) {
  299. switch (topology) {
  300. case InputTopology::Points:
  301. return "POINTS";
  302. case InputTopology::Lines:
  303. return "LINES";
  304. case InputTopology::LinesAdjacency:
  305. return "LINESS_ADJACENCY";
  306. case InputTopology::Triangles:
  307. return "TRIANGLES";
  308. case InputTopology::TrianglesAdjacency:
  309. return "TRIANGLES_ADJACENCY";
  310. }
  311. throw InvalidArgument("Invalid input topology {}", topology);
  312. }
  313. std::string_view OutputPrimitive(OutputTopology topology) {
  314. switch (topology) {
  315. case OutputTopology::PointList:
  316. return "POINTS";
  317. case OutputTopology::LineStrip:
  318. return "LINE_STRIP";
  319. case OutputTopology::TriangleStrip:
  320. return "TRIANGLE_STRIP";
  321. }
  322. throw InvalidArgument("Invalid output topology {}", topology);
  323. }
  324. std::string_view GetTessMode(TessPrimitive primitive) {
  325. switch (primitive) {
  326. case TessPrimitive::Triangles:
  327. return "TRIANGLES";
  328. case TessPrimitive::Quads:
  329. return "QUADS";
  330. case TessPrimitive::Isolines:
  331. return "ISOLINES";
  332. }
  333. throw InvalidArgument("Invalid tessellation primitive {}", primitive);
  334. }
  335. std::string_view GetTessSpacing(TessSpacing spacing) {
  336. switch (spacing) {
  337. case TessSpacing::Equal:
  338. return "EQUAL";
  339. case TessSpacing::FractionalOdd:
  340. return "FRACTIONAL_ODD";
  341. case TessSpacing::FractionalEven:
  342. return "FRACTIONAL_EVEN";
  343. }
  344. throw InvalidArgument("Invalid tessellation spacing {}", spacing);
  345. }
  346. } // Anonymous namespace
  347. std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
  348. Bindings& bindings) {
  349. EmitContext ctx{program, bindings, profile, runtime_info};
  350. Precolor(ctx, program);
  351. EmitCode(ctx, program);
  352. std::string header{StageHeader(program.stage)};
  353. SetupOptions(program, profile, header);
  354. switch (program.stage) {
  355. case Stage::TessellationControl:
  356. header += fmt::format("VERTICES_OUT {};", program.invocations);
  357. break;
  358. case Stage::TessellationEval:
  359. header += fmt::format("TESS_MODE {};"
  360. "TESS_SPACING {};"
  361. "TESS_VERTEX_ORDER {};",
  362. GetTessMode(runtime_info.tess_primitive),
  363. GetTessSpacing(runtime_info.tess_spacing),
  364. runtime_info.tess_clockwise ? "CW" : "CCW");
  365. break;
  366. case Stage::Geometry:
  367. header += fmt::format("PRIMITIVE_IN {};"
  368. "PRIMITIVE_OUT {};"
  369. "VERTICES_OUT {};",
  370. InputPrimitive(runtime_info.input_topology),
  371. OutputPrimitive(program.output_topology), program.output_vertices);
  372. break;
  373. case Stage::Compute:
  374. header += fmt::format("GROUP_SIZE {} {} {};", program.workgroup_size[0],
  375. program.workgroup_size[1], program.workgroup_size[2]);
  376. break;
  377. default:
  378. break;
  379. }
  380. if (program.shared_memory_size > 0) {
  381. header += fmt::format("SHARED_MEMORY {};", program.shared_memory_size);
  382. header += fmt::format("SHARED shared_mem[]={{program.sharedmem}};");
  383. }
  384. header += "TEMP ";
  385. for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
  386. header += fmt::format("R{},", index);
  387. }
  388. if (program.local_memory_size > 0) {
  389. header += fmt::format("lmem[{}],", program.local_memory_size);
  390. }
  391. if (program.info.uses_fswzadd) {
  392. header += "FSWZA[4],FSWZB[4],";
  393. }
  394. header += "RC;"
  395. "LONG TEMP ";
  396. for (size_t index = 0; index < ctx.reg_alloc.NumUsedLongRegisters(); ++index) {
  397. header += fmt::format("D{},", index);
  398. }
  399. header += "DC;";
  400. if (program.info.uses_fswzadd) {
  401. header += "MOV.F FSWZA[0],-1;"
  402. "MOV.F FSWZA[1],1;"
  403. "MOV.F FSWZA[2],-1;"
  404. "MOV.F FSWZA[3],0;"
  405. "MOV.F FSWZB[0],-1;"
  406. "MOV.F FSWZB[1],-1;"
  407. "MOV.F FSWZB[2],1;"
  408. "MOV.F FSWZB[3],-1;";
  409. }
  410. ctx.code.insert(0, header);
  411. ctx.code += "END";
  412. return ctx.code;
  413. }
  414. } // namespace Shader::Backend::GLASM