emit_glasm.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <string>
  5. #include <tuple>
  6. #include "shader_recompiler/backend/bindings.h"
  7. #include "shader_recompiler/backend/glasm/emit_context.h"
  8. #include "shader_recompiler/backend/glasm/emit_glasm.h"
  9. #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
  10. #include "shader_recompiler/frontend/ir/program.h"
  11. #include "shader_recompiler/profile.h"
  12. namespace Shader::Backend::GLASM {
  13. namespace {
  14. template <class Func>
  15. struct FuncTraits {};
  16. template <class ReturnType_, class... Args>
  17. struct FuncTraits<ReturnType_ (*)(Args...)> {
  18. using ReturnType = ReturnType_;
  19. static constexpr size_t NUM_ARGS = sizeof...(Args);
  20. template <size_t I>
  21. using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
  22. };
  23. template <typename T>
  24. struct Identity {
  25. Identity(const T& data_) : data{data_} {}
  26. const T& Extract() {
  27. return data;
  28. }
  29. T data;
  30. };
  31. template <bool scalar>
  32. struct RegWrapper {
  33. RegWrapper(EmitContext& ctx, Value value)
  34. : reg_alloc{ctx.reg_alloc}, allocated{value.type != Type::Register} {
  35. reg = allocated ? reg_alloc.AllocReg() : Register{value};
  36. switch (value.type) {
  37. case Type::Register:
  38. break;
  39. case Type::U32:
  40. ctx.Add("MOV.U {}.x,{};", reg, value.imm_u32);
  41. break;
  42. case Type::S32:
  43. ctx.Add("MOV.S {}.x,{};", reg, value.imm_s32);
  44. break;
  45. case Type::F32:
  46. ctx.Add("MOV.F {}.x,{};", reg, value.imm_f32);
  47. break;
  48. }
  49. }
  50. ~RegWrapper() {
  51. if (allocated) {
  52. reg_alloc.FreeReg(reg);
  53. }
  54. }
  55. auto Extract() {
  56. return std::conditional_t<scalar, ScalarRegister, Register>{Value{reg}};
  57. }
  58. RegAlloc& reg_alloc;
  59. Register reg{};
  60. bool allocated{};
  61. };
  62. template <typename ArgType>
  63. auto Arg(EmitContext& ctx, const IR::Value& arg) {
  64. if constexpr (std::is_same_v<ArgType, Register>) {
  65. return RegWrapper<false>{ctx, ctx.reg_alloc.Consume(arg)};
  66. } else if constexpr (std::is_same_v<ArgType, ScalarRegister>) {
  67. return RegWrapper<true>{ctx, ctx.reg_alloc.Consume(arg)};
  68. } else if constexpr (std::is_base_of_v<Value, ArgType>) {
  69. return Identity{ArgType{ctx.reg_alloc.Consume(arg)}};
  70. } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
  71. return Identity{arg};
  72. } else if constexpr (std::is_same_v<ArgType, u32>) {
  73. return Identity{arg.U32()};
  74. } else if constexpr (std::is_same_v<ArgType, IR::Block*>) {
  75. return Identity{arg.Label()};
  76. } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
  77. return Identity{arg.Attribute()};
  78. } else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
  79. return Identity{arg.Patch()};
  80. } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
  81. return Identity{arg.Reg()};
  82. }
  83. }
  84. template <auto func, bool is_first_arg_inst, typename... Args>
  85. void InvokeCall(EmitContext& ctx, IR::Inst* inst, Args&&... args) {
  86. if constexpr (is_first_arg_inst) {
  87. func(ctx, *inst, std::forward<Args>(args.Extract())...);
  88. } else {
  89. func(ctx, std::forward<Args>(args.Extract())...);
  90. }
  91. }
  92. template <auto func, bool is_first_arg_inst, size_t... I>
  93. void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
  94. using Traits = FuncTraits<decltype(func)>;
  95. if constexpr (is_first_arg_inst) {
  96. func(ctx, *inst,
  97. Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I)).Extract()...);
  98. } else {
  99. func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I)).Extract()...);
  100. }
  101. }
  102. template <auto func>
  103. void Invoke(EmitContext& ctx, IR::Inst* inst) {
  104. using Traits = FuncTraits<decltype(func)>;
  105. static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
  106. if constexpr (Traits::NUM_ARGS == 1) {
  107. Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
  108. } else {
  109. using FirstArgType = typename Traits::template ArgType<1>;
  110. static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
  111. using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
  112. Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
  113. }
  114. }
  115. void EmitInst(EmitContext& ctx, IR::Inst* inst) {
  116. switch (inst->GetOpcode()) {
  117. #define OPCODE(name, result_type, ...) \
  118. case IR::Opcode::name: \
  119. return Invoke<&Emit##name>(ctx, inst);
  120. #include "shader_recompiler/frontend/ir/opcodes.inc"
  121. #undef OPCODE
  122. }
  123. throw LogicError("Invalid opcode {}", inst->GetOpcode());
  124. }
  125. } // Anonymous namespace
  126. std::string EmitGLASM(const Profile&, IR::Program& program, Bindings&) {
  127. EmitContext ctx{program};
  128. for (IR::Block* const block : program.blocks) {
  129. for (IR::Inst& inst : block->Instructions()) {
  130. EmitInst(ctx, &inst);
  131. }
  132. }
  133. std::string header = "!!NVcp5.0\n"
  134. "OPTION NV_internal;";
  135. switch (program.stage) {
  136. case Stage::Compute:
  137. header += fmt::format("GROUP_SIZE {} {} {};", program.workgroup_size[0],
  138. program.workgroup_size[1], program.workgroup_size[2]);
  139. break;
  140. default:
  141. break;
  142. }
  143. header += "TEMP ";
  144. for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
  145. header += fmt::format("R{},", index);
  146. }
  147. header += "RC;";
  148. if (!program.info.storage_buffers_descriptors.empty()) {
  149. header += "LONG TEMP LC;";
  150. }
  151. ctx.code.insert(0, header);
  152. ctx.code += "END";
  153. return ctx.code;
  154. }
  155. } // namespace Shader::Backend::GLASM