shader: Add support for forward declarations
This commit is contained in:
		| @@ -64,31 +64,49 @@ EmitSPIRV::EmitSPIRV(IR::Program& program) { | ||||
|     std::system("spirv-cross shader.spv"); | ||||
| } | ||||
|  | ||||
| template <auto method, typename... Args> | ||||
| static void SetDefinition(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst, Args... args) { | ||||
|     const Id forward_id{inst->Definition<Id>()}; | ||||
|     const bool has_forward_id{Sirit::ValidId(forward_id)}; | ||||
|     Id current_id{}; | ||||
|     if (has_forward_id) { | ||||
|         current_id = ctx.ExchangeCurrentId(forward_id); | ||||
|     } | ||||
|     const Id new_id{(emit.*method)(ctx, std::forward<Args>(args)...)}; | ||||
|     if (has_forward_id) { | ||||
|         ctx.ExchangeCurrentId(current_id); | ||||
|     } else { | ||||
|         inst->SetDefinition<Id>(new_id); | ||||
|     } | ||||
| } | ||||
|  | ||||
| template <auto method> | ||||
| static void Invoke(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst) { | ||||
|     using M = decltype(method); | ||||
|     using std::is_invocable_r_v; | ||||
|     if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&>) { | ||||
|         ctx.Define(inst, (emit.*method)(ctx)); | ||||
|         SetDefinition<method>(emit, ctx, inst); | ||||
|     } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id>) { | ||||
|         ctx.Define(inst, (emit.*method)(ctx, ctx.Def(inst->Arg(0)))); | ||||
|         SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0))); | ||||
|     } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id, Id>) { | ||||
|         ctx.Define(inst, (emit.*method)(ctx, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)))); | ||||
|         SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1))); | ||||
|     } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id, Id, Id>) { | ||||
|         ctx.Define(inst, (emit.*method)(ctx, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)), | ||||
|                                         ctx.Def(inst->Arg(2)))); | ||||
|         SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)), | ||||
|                               ctx.Def(inst->Arg(2))); | ||||
|     } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, IR::Inst*>) { | ||||
|         SetDefinition<method>(emit, ctx, inst, inst); | ||||
|     } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, IR::Inst*, Id, Id>) { | ||||
|         ctx.Define(inst, (emit.*method)(ctx, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)))); | ||||
|         SetDefinition<method>(emit, ctx, inst, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1))); | ||||
|     } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, IR::Inst*, Id, Id, Id>) { | ||||
|         ctx.Define(inst, (emit.*method)(ctx, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)), | ||||
|                                         ctx.Def(inst->Arg(2)))); | ||||
|         SetDefinition<method>(emit, ctx, inst, inst, ctx.Def(inst->Arg(0)), ctx.Def(inst->Arg(1)), | ||||
|                               ctx.Def(inst->Arg(2))); | ||||
|     } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, Id, u32>) { | ||||
|         ctx.Define(inst, (emit.*method)(ctx, ctx.Def(inst->Arg(0)), inst->Arg(1).U32())); | ||||
|         SetDefinition<method>(emit, ctx, inst, ctx.Def(inst->Arg(0)), inst->Arg(1).U32()); | ||||
|     } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, const IR::Value&>) { | ||||
|         ctx.Define(inst, (emit.*method)(ctx, inst->Arg(0))); | ||||
|         SetDefinition<method>(emit, ctx, inst, inst->Arg(0)); | ||||
|     } else if constexpr (is_invocable_r_v<Id, M, EmitSPIRV&, EmitContext&, const IR::Value&, | ||||
|                                           const IR::Value&>) { | ||||
|         ctx.Define(inst, (emit.*method)(ctx, inst->Arg(0), inst->Arg(1))); | ||||
|         SetDefinition<method>(emit, ctx, inst, inst->Arg(0), inst->Arg(1)); | ||||
|     } else if constexpr (is_invocable_r_v<void, M, EmitSPIRV&, EmitContext&, IR::Inst*>) { | ||||
|         (emit.*method)(ctx, inst); | ||||
|     } else if constexpr (is_invocable_r_v<void, M, EmitSPIRV&, EmitContext&>) { | ||||
| @@ -122,11 +140,28 @@ static Id TypeId(const EmitContext& ctx, IR::Type type) { | ||||
|  | ||||
| Id EmitSPIRV::EmitPhi(EmitContext& ctx, IR::Inst* inst) { | ||||
|     const size_t num_args{inst->NumArgs()}; | ||||
|     boost::container::small_vector<Id, 64> operands; | ||||
|     boost::container::small_vector<Id, 32> operands; | ||||
|     operands.reserve(num_args * 2); | ||||
|     for (size_t index = 0; index < num_args; ++index) { | ||||
|         // Phi nodes can have forward declarations, if an argument is not defined provide a forward | ||||
|         // declaration of it. Invoke will take care of giving it the right definition when it's | ||||
|         // actually defined. | ||||
|         const IR::Value arg{inst->Arg(index)}; | ||||
|         Id def{}; | ||||
|         if (arg.IsImmediate()) { | ||||
|             // Let the context handle immediate definitions, as it already knows how | ||||
|             def = ctx.Def(arg); | ||||
|         } else { | ||||
|             IR::Inst* const arg_inst{arg.Inst()}; | ||||
|             def = arg_inst->Definition<Id>(); | ||||
|             if (!Sirit::ValidId(def)) { | ||||
|                 // If it hasn't been defined, get a forward declaration | ||||
|                 def = ctx.ForwardDeclarationId(); | ||||
|                 arg_inst->SetDefinition<Id>(def); | ||||
|             } | ||||
|         } | ||||
|         IR::Block* const phi_block{inst->PhiBlock(index)}; | ||||
|         operands.push_back(ctx.Def(inst->Arg(index))); | ||||
|         operands.push_back(def); | ||||
|         operands.push_back(ctx.BlockLabel(phi_block)); | ||||
|     } | ||||
|     const Id result_type{TypeId(ctx, inst->Arg(0).Type())}; | ||||
|   | ||||
| @@ -6,8 +6,6 @@ | ||||
|  | ||||
| #include <sirit/sirit.h> | ||||
|  | ||||
| #include <boost/container/flat_map.hpp> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "shader_recompiler/frontend/ir/microinstruction.h" | ||||
| #include "shader_recompiler/frontend/ir/program.h" | ||||
| @@ -16,37 +14,6 @@ namespace Shader::Backend::SPIRV { | ||||
|  | ||||
| using Sirit::Id; | ||||
|  | ||||
| class DefMap { | ||||
| public: | ||||
|     void Define(IR::Inst* inst, Id def_id) { | ||||
|         const InstInfo info{.use_count{inst->UseCount()}, .def_id{def_id}}; | ||||
|         const auto it{map.insert(map.end(), std::make_pair(inst, info))}; | ||||
|         if (it == map.end()) { | ||||
|             throw LogicError("Defining already defined instruction"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] Id Consume(IR::Inst* inst) { | ||||
|         const auto it{map.find(inst)}; | ||||
|         if (it == map.end()) { | ||||
|             throw LogicError("Consuming undefined instruction"); | ||||
|         } | ||||
|         const Id def_id{it->second.def_id}; | ||||
|         if (--it->second.use_count == 0) { | ||||
|             map.erase(it); | ||||
|         } | ||||
|         return def_id; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     struct InstInfo { | ||||
|         int use_count; | ||||
|         Id def_id; | ||||
|     }; | ||||
|  | ||||
|     boost::container::flat_map<IR::Inst*, InstInfo> map; | ||||
| }; | ||||
|  | ||||
| class VectorTypes { | ||||
| public: | ||||
|     void Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { | ||||
| @@ -76,7 +43,7 @@ public: | ||||
|  | ||||
|     [[nodiscard]] Id Def(const IR::Value& value) { | ||||
|         if (!value.IsImmediate()) { | ||||
|             return def_map.Consume(value.Inst()); | ||||
|             return value.Inst()->Definition<Id>(); | ||||
|         } | ||||
|         switch (value.Type()) { | ||||
|         case IR::Type::U1: | ||||
| @@ -90,10 +57,6 @@ public: | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void Define(IR::Inst* inst, Id def_id) { | ||||
|         def_map.Define(inst, def_id); | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] Id BlockLabel(IR::Block* block) const { | ||||
|         const auto it{std::ranges::lower_bound(block_label_map, block, {}, | ||||
|                                                &std::pair<IR::Block*, Id>::first)}; | ||||
| @@ -117,7 +80,6 @@ public: | ||||
|     Id local_invocation_id{}; | ||||
|  | ||||
| private: | ||||
|     DefMap def_map; | ||||
|     std::vector<std::pair<IR::Block*, Id>> block_label_map; | ||||
| }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user