diff options
| author | 2021-05-14 00:40:54 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:31 -0400 | |
| commit | d54d7de40e7295827b0e4e4026441b53d3fc9569 (patch) | |
| tree | 29b5074f851292dace7aeb5da7716675544b3735 /src/shader_recompiler | |
| parent | glasm: Implement Storage atomics (diff) | |
| download | yuzu-d54d7de40e7295827b0e4e4026441b53d3fc9569.tar.gz yuzu-d54d7de40e7295827b0e4e4026441b53d3fc9569.tar.xz yuzu-d54d7de40e7295827b0e4e4026441b53d3fc9569.zip | |
glasm: Rework control flow introducing a syntax list
This commit regresses VertexA shaders, their transformation pass has to
be adapted to the new control flow.
Diffstat (limited to '')
33 files changed, 437 insertions, 505 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index f829b8d32..0d55924a7 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt | |||
| @@ -51,6 +51,7 @@ add_library(shader_recompiler STATIC | |||
| 51 | backend/spirv/emit_spirv_warp.cpp | 51 | backend/spirv/emit_spirv_warp.cpp |
| 52 | environment.h | 52 | environment.h |
| 53 | exception.h | 53 | exception.h |
| 54 | frontend/ir/abstract_syntax_list.h | ||
| 54 | frontend/ir/attribute.cpp | 55 | frontend/ir/attribute.cpp |
| 55 | frontend/ir/attribute.h | 56 | frontend/ir/attribute.h |
| 56 | frontend/ir/basic_block.cpp | 57 | frontend/ir/basic_block.cpp |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp index 056d8cbf8..51ca83d18 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp | |||
| @@ -117,8 +117,6 @@ auto Arg(EmitContext& ctx, const IR::Value& arg) { | |||
| 117 | return Identity<const IR::Value&>{arg}; | 117 | return Identity<const IR::Value&>{arg}; |
| 118 | } else if constexpr (std::is_same_v<ArgType, u32>) { | 118 | } else if constexpr (std::is_same_v<ArgType, u32>) { |
| 119 | return Identity{arg.U32()}; | 119 | return Identity{arg.U32()}; |
| 120 | } else if constexpr (std::is_same_v<ArgType, IR::Block*>) { | ||
| 121 | return Identity{arg.Label()}; | ||
| 122 | } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) { | 120 | } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) { |
| 123 | return Identity{arg.Attribute()}; | 121 | return Identity{arg.Attribute()}; |
| 124 | } else if constexpr (std::is_same_v<ArgType, IR::Patch>) { | 122 | } else if constexpr (std::is_same_v<ArgType, IR::Patch>) { |
| @@ -177,6 +175,39 @@ void EmitInst(EmitContext& ctx, IR::Inst* inst) { | |||
| 177 | throw LogicError("Invalid opcode {}", inst->GetOpcode()); | 175 | throw LogicError("Invalid opcode {}", inst->GetOpcode()); |
| 178 | } | 176 | } |
| 179 | 177 | ||
| 178 | void EmitCode(EmitContext& ctx, const IR::Program& program) { | ||
| 179 | const auto eval{ | ||
| 180 | [&](const IR::U1& cond) { return ScalarS32{ctx.reg_alloc.Consume(IR::Value{cond})}; }}; | ||
| 181 | for (const IR::AbstractSyntaxNode& node : program.syntax_list) { | ||
| 182 | switch (node.type) { | ||
| 183 | case IR::AbstractSyntaxNode::Type::Block: | ||
| 184 | for (IR::Inst& inst : node.block->Instructions()) { | ||
| 185 | EmitInst(ctx, &inst); | ||
| 186 | } | ||
| 187 | break; | ||
| 188 | case IR::AbstractSyntaxNode::Type::If: | ||
| 189 | ctx.Add("MOV.S.CC RC,{};IF NE.x;", eval(node.if_node.cond)); | ||
| 190 | break; | ||
| 191 | case IR::AbstractSyntaxNode::Type::EndIf: | ||
| 192 | ctx.Add("ENDIF;"); | ||
| 193 | break; | ||
| 194 | case IR::AbstractSyntaxNode::Type::Loop: | ||
| 195 | ctx.Add("REP;"); | ||
| 196 | break; | ||
| 197 | case IR::AbstractSyntaxNode::Type::Repeat: | ||
| 198 | ctx.Add("MOV.S.CC RC,{};BRK NE.x;ENDREP;", eval(node.repeat.cond)); | ||
| 199 | break; | ||
| 200 | case IR::AbstractSyntaxNode::Type::Break: | ||
| 201 | ctx.Add("MOV.S.CC RC,{};BRK NE.x;", eval(node.repeat.cond)); | ||
| 202 | break; | ||
| 203 | case IR::AbstractSyntaxNode::Type::Return: | ||
| 204 | case IR::AbstractSyntaxNode::Type::Unreachable: | ||
| 205 | ctx.Add("RET;"); | ||
| 206 | break; | ||
| 207 | } | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 180 | void SetupOptions(std::string& header, Info info) { | 211 | void SetupOptions(std::string& header, Info info) { |
| 181 | if (info.uses_int64_bit_atomics) { | 212 | if (info.uses_int64_bit_atomics) { |
| 182 | header += "OPTION NV_shader_atomic_int64;"; | 213 | header += "OPTION NV_shader_atomic_int64;"; |
| @@ -201,11 +232,7 @@ void SetupOptions(std::string& header, Info info) { | |||
| 201 | 232 | ||
| 202 | std::string EmitGLASM(const Profile&, IR::Program& program, Bindings&) { | 233 | std::string EmitGLASM(const Profile&, IR::Program& program, Bindings&) { |
| 203 | EmitContext ctx{program}; | 234 | EmitContext ctx{program}; |
| 204 | for (IR::Block* const block : program.blocks) { | 235 | EmitCode(ctx, program); |
| 205 | for (IR::Inst& inst : block->Instructions()) { | ||
| 206 | EmitInst(ctx, &inst); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | std::string header = "!!NVcp5.0\n" | 236 | std::string header = "!!NVcp5.0\n" |
| 210 | "OPTION NV_internal;"; | 237 | "OPTION NV_internal;"; |
| 211 | SetupOptions(header, program.info); | 238 | SetupOptions(header, program.info); |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h index 8202354fe..0f7f16e6e 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h | |||
| @@ -22,13 +22,8 @@ class EmitContext; | |||
| 22 | void EmitPhi(EmitContext& ctx, IR::Inst& inst); | 22 | void EmitPhi(EmitContext& ctx, IR::Inst& inst); |
| 23 | void EmitVoid(EmitContext& ctx); | 23 | void EmitVoid(EmitContext& ctx); |
| 24 | void EmitIdentity(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); | 24 | void EmitIdentity(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); |
| 25 | void EmitBranch(EmitContext& ctx); | 25 | void EmitBranchConditionRef(EmitContext&); |
| 26 | void EmitBranchConditional(EmitContext& ctx); | ||
| 27 | void EmitLoopMerge(EmitContext& ctx); | ||
| 28 | void EmitSelectionMerge(EmitContext& ctx); | ||
| 29 | void EmitReturn(EmitContext& ctx); | ||
| 30 | void EmitJoin(EmitContext& ctx); | 26 | void EmitJoin(EmitContext& ctx); |
| 31 | void EmitUnreachable(EmitContext& ctx); | ||
| 32 | void EmitDemoteToHelperInvocation(EmitContext& ctx); | 27 | void EmitDemoteToHelperInvocation(EmitContext& ctx); |
| 33 | void EmitBarrier(EmitContext& ctx); | 28 | void EmitBarrier(EmitContext& ctx); |
| 34 | void EmitWorkgroupMemoryBarrier(EmitContext& ctx); | 29 | void EmitWorkgroupMemoryBarrier(EmitContext& ctx); |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp index 15fd23356..adcc0404b 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp | |||
| @@ -91,7 +91,8 @@ void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, Scalar | |||
| 91 | if (count.type != Type::Register && offset.type != Type::Register) { | 91 | if (count.type != Type::Register && offset.type != Type::Register) { |
| 92 | ctx.Add("BFI.S {},{{{},{},0,0}},{},{};", ret, count, offset, insert, base); | 92 | ctx.Add("BFI.S {},{{{},{},0,0}},{},{};", ret, count, offset, insert, base); |
| 93 | } else { | 93 | } else { |
| 94 | ctx.Add("MOV.S RC.x,{};MOV.U RC.y,{};" | 94 | ctx.Add("MOV.S RC.x,{};" |
| 95 | "MOV.S RC.y,{};" | ||
| 95 | "BFI.S {},RC,{},{};", | 96 | "BFI.S {},RC,{},{};", |
| 96 | count, offset, ret, insert, base); | 97 | count, offset, ret, insert, base); |
| 97 | } | 98 | } |
| @@ -103,7 +104,8 @@ void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, Scal | |||
| 103 | if (count.type != Type::Register && offset.type != Type::Register) { | 104 | if (count.type != Type::Register && offset.type != Type::Register) { |
| 104 | ctx.Add("BFE.S {},{{{},{},0,0}},{};", ret, count, offset, base); | 105 | ctx.Add("BFE.S {},{{{},{},0,0}},{};", ret, count, offset, base); |
| 105 | } else { | 106 | } else { |
| 106 | ctx.Add("MOV.S RC.x,{};MOV.U RC.y,{};" | 107 | ctx.Add("MOV.S RC.x,{};" |
| 108 | "MOV.S RC.y,{};" | ||
| 107 | "BFE.S {},RC,{};", | 109 | "BFE.S {},RC,{};", |
| 108 | count, offset, ret, base); | 110 | count, offset, ret, base); |
| 109 | } | 111 | } |
| @@ -115,7 +117,8 @@ void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, Scal | |||
| 115 | if (count.type != Type::Register && offset.type != Type::Register) { | 117 | if (count.type != Type::Register && offset.type != Type::Register) { |
| 116 | ctx.Add("BFE.U {},{{{},{},0,0}},{};", ret, count, offset, base); | 118 | ctx.Add("BFE.U {},{{{},{},0,0}},{};", ret, count, offset, base); |
| 117 | } else { | 119 | } else { |
| 118 | ctx.Add("MOV.U RC.x,{};MOV.U RC.y,{};" | 120 | ctx.Add("MOV.U RC.x,{};" |
| 121 | "MOV.U RC.y,{};" | ||
| 119 | "BFE.U {},RC,{};", | 122 | "BFE.U {},RC,{};", |
| 120 | count, offset, ret, base); | 123 | count, offset, ret, base); |
| 121 | } | 124 | } |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp index b40d09f8c..f37ad5587 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp | |||
| @@ -23,34 +23,12 @@ void EmitPhi(EmitContext& ctx, IR::Inst& inst) { | |||
| 23 | 23 | ||
| 24 | void EmitVoid(EmitContext&) {} | 24 | void EmitVoid(EmitContext&) {} |
| 25 | 25 | ||
| 26 | void EmitBranch(EmitContext& ctx) { | 26 | void EmitBranchConditionRef(EmitContext&) {} |
| 27 | NotImplemented(); | ||
| 28 | } | ||
| 29 | |||
| 30 | void EmitBranchConditional(EmitContext& ctx) { | ||
| 31 | NotImplemented(); | ||
| 32 | } | ||
| 33 | |||
| 34 | void EmitLoopMerge(EmitContext& ctx) { | ||
| 35 | NotImplemented(); | ||
| 36 | } | ||
| 37 | |||
| 38 | void EmitSelectionMerge(EmitContext& ctx) { | ||
| 39 | NotImplemented(); | ||
| 40 | } | ||
| 41 | |||
| 42 | void EmitReturn(EmitContext& ctx) { | ||
| 43 | ctx.Add("RET;"); | ||
| 44 | } | ||
| 45 | 27 | ||
| 46 | void EmitJoin(EmitContext& ctx) { | 28 | void EmitJoin(EmitContext& ctx) { |
| 47 | NotImplemented(); | 29 | NotImplemented(); |
| 48 | } | 30 | } |
| 49 | 31 | ||
| 50 | void EmitUnreachable(EmitContext& ctx) { | ||
| 51 | NotImplemented(); | ||
| 52 | } | ||
| 53 | |||
| 54 | void EmitDemoteToHelperInvocation(EmitContext& ctx) { | 32 | void EmitDemoteToHelperInvocation(EmitContext& ctx) { |
| 55 | NotImplemented(); | 33 | NotImplemented(); |
| 56 | } | 34 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp index 9759591bd..a98e08392 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp | |||
| @@ -463,7 +463,6 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program, Bindings | |||
| 463 | DefineImages(program.info, image_binding); | 463 | DefineImages(program.info, image_binding); |
| 464 | DefineAttributeMemAccess(program.info); | 464 | DefineAttributeMemAccess(program.info); |
| 465 | DefineGlobalMemoryFunctions(program.info); | 465 | DefineGlobalMemoryFunctions(program.info); |
| 466 | DefineLabels(program); | ||
| 467 | } | 466 | } |
| 468 | 467 | ||
| 469 | EmitContext::~EmitContext() = default; | 468 | EmitContext::~EmitContext() = default; |
| @@ -487,8 +486,6 @@ Id EmitContext::Def(const IR::Value& value) { | |||
| 487 | return Const(value.F32()); | 486 | return Const(value.F32()); |
| 488 | case IR::Type::F64: | 487 | case IR::Type::F64: |
| 489 | return Constant(F64[1], value.F64()); | 488 | return Constant(F64[1], value.F64()); |
| 490 | case IR::Type::Label: | ||
| 491 | return value.Label()->Definition<Id>(); | ||
| 492 | default: | 489 | default: |
| 493 | throw NotImplementedException("Immediate type {}", value.Type()); | 490 | throw NotImplementedException("Immediate type {}", value.Type()); |
| 494 | } | 491 | } |
| @@ -1139,12 +1136,6 @@ void EmitContext::DefineImages(const Info& info, u32& binding) { | |||
| 1139 | } | 1136 | } |
| 1140 | } | 1137 | } |
| 1141 | 1138 | ||
| 1142 | void EmitContext::DefineLabels(IR::Program& program) { | ||
| 1143 | for (IR::Block* const block : program.blocks) { | ||
| 1144 | block->SetDefinition(OpLabel()); | ||
| 1145 | } | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | void EmitContext::DefineInputs(const Info& info) { | 1139 | void EmitContext::DefineInputs(const Info& info) { |
| 1149 | if (info.uses_workgroup_id) { | 1140 | if (info.uses_workgroup_id) { |
| 1150 | workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId); | 1141 | workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId); |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h index 8b000f1ec..d2b79f6c1 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.h +++ b/src/shader_recompiler/backend/spirv/emit_context.h | |||
| @@ -296,7 +296,6 @@ private: | |||
| 296 | void DefineImages(const Info& info, u32& binding); | 296 | void DefineImages(const Info& info, u32& binding); |
| 297 | void DefineAttributeMemAccess(const Info& info); | 297 | void DefineAttributeMemAccess(const Info& info); |
| 298 | void DefineGlobalMemoryFunctions(const Info& info); | 298 | void DefineGlobalMemoryFunctions(const Info& info); |
| 299 | void DefineLabels(IR::Program& program); | ||
| 300 | 299 | ||
| 301 | void DefineInputs(const Info& info); | 300 | void DefineInputs(const Info& info); |
| 302 | void DefineOutputs(const IR::Program& program); | 301 | void DefineOutputs(const IR::Program& program); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 2dad87e87..c22edfec2 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp | |||
| @@ -41,8 +41,6 @@ ArgType Arg(EmitContext& ctx, const IR::Value& arg) { | |||
| 41 | return arg; | 41 | return arg; |
| 42 | } else if constexpr (std::is_same_v<ArgType, u32>) { | 42 | } else if constexpr (std::is_same_v<ArgType, u32>) { |
| 43 | return arg.U32(); | 43 | return arg.U32(); |
| 44 | } else if constexpr (std::is_same_v<ArgType, IR::Block*>) { | ||
| 45 | return arg.Label(); | ||
| 46 | } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) { | 44 | } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) { |
| 47 | return arg.Attribute(); | 45 | return arg.Attribute(); |
| 48 | } else if constexpr (std::is_same_v<ArgType, IR::Patch>) { | 46 | } else if constexpr (std::is_same_v<ArgType, IR::Patch>) { |
| @@ -109,15 +107,74 @@ Id TypeId(const EmitContext& ctx, IR::Type type) { | |||
| 109 | } | 107 | } |
| 110 | } | 108 | } |
| 111 | 109 | ||
| 110 | void Traverse(EmitContext& ctx, IR::Program& program) { | ||
| 111 | IR::Block* current_block{}; | ||
| 112 | for (const IR::AbstractSyntaxNode& node : program.syntax_list) { | ||
| 113 | switch (node.type) { | ||
| 114 | case IR::AbstractSyntaxNode::Type::Block: | ||
| 115 | const Id label{node.block->Definition<Id>()}; | ||
| 116 | if (current_block) { | ||
| 117 | ctx.OpBranch(label); | ||
| 118 | } | ||
| 119 | current_block = node.block; | ||
| 120 | ctx.AddLabel(label); | ||
| 121 | for (IR::Inst& inst : node.block->Instructions()) { | ||
| 122 | EmitInst(ctx, &inst); | ||
| 123 | } | ||
| 124 | break; | ||
| 125 | case IR::AbstractSyntaxNode::Type::If: { | ||
| 126 | const Id if_label{node.if_node.body->Definition<Id>()}; | ||
| 127 | const Id endif_label{node.if_node.merge->Definition<Id>()}; | ||
| 128 | ctx.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone); | ||
| 129 | ctx.OpBranchConditional(ctx.Def(node.if_node.cond), if_label, endif_label); | ||
| 130 | break; | ||
| 131 | } | ||
| 132 | case IR::AbstractSyntaxNode::Type::Loop: { | ||
| 133 | const Id body_label{node.loop.body->Definition<Id>()}; | ||
| 134 | const Id continue_label{node.loop.continue_block->Definition<Id>()}; | ||
| 135 | const Id endloop_label{node.loop.merge->Definition<Id>()}; | ||
| 136 | |||
| 137 | ctx.OpLoopMerge(endloop_label, continue_label, spv::LoopControlMask::MaskNone); | ||
| 138 | ctx.OpBranch(node.loop.body->Definition<Id>()); | ||
| 139 | break; | ||
| 140 | } | ||
| 141 | case IR::AbstractSyntaxNode::Type::Break: { | ||
| 142 | const Id break_label{node.break_node.merge->Definition<Id>()}; | ||
| 143 | const Id skip_label{node.break_node.skip->Definition<Id>()}; | ||
| 144 | ctx.OpBranchConditional(ctx.Def(node.break_node.cond), break_label, skip_label); | ||
| 145 | break; | ||
| 146 | } | ||
| 147 | case IR::AbstractSyntaxNode::Type::EndIf: | ||
| 148 | if (current_block) { | ||
| 149 | ctx.OpBranch(node.end_if.merge->Definition<Id>()); | ||
| 150 | } | ||
| 151 | break; | ||
| 152 | case IR::AbstractSyntaxNode::Type::Repeat: { | ||
| 153 | const Id loop_header_label{node.repeat.loop_header->Definition<Id>()}; | ||
| 154 | const Id merge_label{node.repeat.merge->Definition<Id>()}; | ||
| 155 | ctx.OpBranchConditional(ctx.Def(node.repeat.cond), loop_header_label, merge_label); | ||
| 156 | break; | ||
| 157 | } | ||
| 158 | case IR::AbstractSyntaxNode::Type::Return: | ||
| 159 | ctx.OpReturn(); | ||
| 160 | break; | ||
| 161 | case IR::AbstractSyntaxNode::Type::Unreachable: | ||
| 162 | ctx.OpUnreachable(); | ||
| 163 | break; | ||
| 164 | } | ||
| 165 | if (node.type != IR::AbstractSyntaxNode::Type::Block) { | ||
| 166 | current_block = nullptr; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 112 | Id DefineMain(EmitContext& ctx, IR::Program& program) { | 171 | Id DefineMain(EmitContext& ctx, IR::Program& program) { |
| 113 | const Id void_function{ctx.TypeFunction(ctx.void_id)}; | 172 | const Id void_function{ctx.TypeFunction(ctx.void_id)}; |
| 114 | const Id main{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)}; | 173 | const Id main{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)}; |
| 115 | for (IR::Block* const block : program.blocks) { | 174 | for (IR::Block* const block : program.blocks) { |
| 116 | ctx.AddLabel(block->Definition<Id>()); | 175 | block->SetDefinition(ctx.OpLabel()); |
| 117 | for (IR::Inst& inst : block->Instructions()) { | ||
| 118 | EmitInst(ctx, &inst); | ||
| 119 | } | ||
| 120 | } | 176 | } |
| 177 | Traverse(ctx, program); | ||
| 121 | ctx.OpFunctionEnd(); | 178 | ctx.OpFunctionEnd(); |
| 122 | return main; | 179 | return main; |
| 123 | } | 180 | } |
| @@ -411,6 +468,8 @@ Id EmitIdentity(EmitContext& ctx, const IR::Value& value) { | |||
| 411 | return id; | 468 | return id; |
| 412 | } | 469 | } |
| 413 | 470 | ||
| 471 | void EmitBranchConditionRef(EmitContext&) {} | ||
| 472 | |||
| 414 | void EmitGetZeroFromOp(EmitContext&) { | 473 | void EmitGetZeroFromOp(EmitContext&) { |
| 415 | throw LogicError("Unreachable instruction"); | 474 | throw LogicError("Unreachable instruction"); |
| 416 | } | 475 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp index 6154c46be..d33486f28 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp | |||
| @@ -7,40 +7,21 @@ | |||
| 7 | 7 | ||
| 8 | namespace Shader::Backend::SPIRV { | 8 | namespace Shader::Backend::SPIRV { |
| 9 | 9 | ||
| 10 | void EmitBranch(EmitContext& ctx, Id label) { | ||
| 11 | ctx.OpBranch(label); | ||
| 12 | } | ||
| 13 | |||
| 14 | void EmitBranchConditional(EmitContext& ctx, Id condition, Id true_label, Id false_label) { | ||
| 15 | ctx.OpBranchConditional(condition, true_label, false_label); | ||
| 16 | } | ||
| 17 | |||
| 18 | void EmitLoopMerge(EmitContext& ctx, Id merge_label, Id continue_label) { | ||
| 19 | ctx.OpLoopMerge(merge_label, continue_label, spv::LoopControlMask::MaskNone); | ||
| 20 | } | ||
| 21 | |||
| 22 | void EmitSelectionMerge(EmitContext& ctx, Id merge_label) { | ||
| 23 | ctx.OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone); | ||
| 24 | } | ||
| 25 | |||
| 26 | void EmitReturn(EmitContext& ctx) { | ||
| 27 | ctx.OpReturn(); | ||
| 28 | } | ||
| 29 | |||
| 30 | void EmitJoin(EmitContext&) { | 10 | void EmitJoin(EmitContext&) { |
| 31 | throw NotImplementedException("Join shouldn't be emitted"); | 11 | throw NotImplementedException("Join shouldn't be emitted"); |
| 32 | } | 12 | } |
| 33 | 13 | ||
| 34 | void EmitUnreachable(EmitContext& ctx) { | 14 | void EmitDemoteToHelperInvocation(EmitContext& ctx) { |
| 35 | ctx.OpUnreachable(); | ||
| 36 | } | ||
| 37 | |||
| 38 | void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label) { | ||
| 39 | if (ctx.profile.support_demote_to_helper_invocation) { | 15 | if (ctx.profile.support_demote_to_helper_invocation) { |
| 40 | ctx.OpDemoteToHelperInvocationEXT(); | 16 | ctx.OpDemoteToHelperInvocationEXT(); |
| 41 | ctx.OpBranch(continue_label); | ||
| 42 | } else { | 17 | } else { |
| 18 | const Id kill_label{ctx.OpLabel()}; | ||
| 19 | const Id impossible_label{ctx.OpLabel()}; | ||
| 20 | ctx.OpSelectionMerge(impossible_label, spv::SelectionControlMask::MaskNone); | ||
| 21 | ctx.OpBranchConditional(ctx.true_value, kill_label, impossible_label); | ||
| 22 | ctx.AddLabel(kill_label); | ||
| 43 | ctx.OpKill(); | 23 | ctx.OpKill(); |
| 24 | ctx.AddLabel(impossible_label); | ||
| 44 | } | 25 | } |
| 45 | } | 26 | } |
| 46 | 27 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index a1ca3f43d..2f4f6e59e 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h | |||
| @@ -23,14 +23,9 @@ class EmitContext; | |||
| 23 | Id EmitPhi(EmitContext& ctx, IR::Inst* inst); | 23 | Id EmitPhi(EmitContext& ctx, IR::Inst* inst); |
| 24 | void EmitVoid(EmitContext& ctx); | 24 | void EmitVoid(EmitContext& ctx); |
| 25 | Id EmitIdentity(EmitContext& ctx, const IR::Value& value); | 25 | Id EmitIdentity(EmitContext& ctx, const IR::Value& value); |
| 26 | void EmitBranch(EmitContext& ctx, Id label); | 26 | void EmitBranchConditionRef(EmitContext&); |
| 27 | void EmitBranchConditional(EmitContext& ctx, Id condition, Id true_label, Id false_label); | ||
| 28 | void EmitLoopMerge(EmitContext& ctx, Id merge_label, Id continue_label); | ||
| 29 | void EmitSelectionMerge(EmitContext& ctx, Id merge_label); | ||
| 30 | void EmitReturn(EmitContext& ctx); | ||
| 31 | void EmitJoin(EmitContext& ctx); | 27 | void EmitJoin(EmitContext& ctx); |
| 32 | void EmitUnreachable(EmitContext& ctx); | 28 | void EmitDemoteToHelperInvocation(EmitContext& ctx); |
| 33 | void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label); | ||
| 34 | void EmitBarrier(EmitContext& ctx); | 29 | void EmitBarrier(EmitContext& ctx); |
| 35 | void EmitWorkgroupMemoryBarrier(EmitContext& ctx); | 30 | void EmitWorkgroupMemoryBarrier(EmitContext& ctx); |
| 36 | void EmitDeviceMemoryBarrier(EmitContext& ctx); | 31 | void EmitDeviceMemoryBarrier(EmitContext& ctx); |
diff --git a/src/shader_recompiler/frontend/ir/abstract_syntax_list.h b/src/shader_recompiler/frontend/ir/abstract_syntax_list.h new file mode 100644 index 000000000..1366414c2 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/abstract_syntax_list.h | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 10 | |||
| 11 | namespace Shader::IR { | ||
| 12 | |||
| 13 | class Block; | ||
| 14 | |||
| 15 | struct AbstractSyntaxNode { | ||
| 16 | enum class Type { | ||
| 17 | Block, | ||
| 18 | If, | ||
| 19 | EndIf, | ||
| 20 | Loop, | ||
| 21 | Repeat, | ||
| 22 | Break, | ||
| 23 | Return, | ||
| 24 | Unreachable, | ||
| 25 | }; | ||
| 26 | Type type{}; | ||
| 27 | union { | ||
| 28 | Block* block{}; | ||
| 29 | struct { | ||
| 30 | U1 cond; | ||
| 31 | Block* body; | ||
| 32 | Block* merge; | ||
| 33 | } if_node; | ||
| 34 | struct { | ||
| 35 | Block* merge; | ||
| 36 | } end_if; | ||
| 37 | struct { | ||
| 38 | Block* body; | ||
| 39 | Block* continue_block; | ||
| 40 | Block* merge; | ||
| 41 | } loop; | ||
| 42 | struct { | ||
| 43 | U1 cond; | ||
| 44 | Block* loop_header; | ||
| 45 | Block* merge; | ||
| 46 | } repeat; | ||
| 47 | struct { | ||
| 48 | U1 cond; | ||
| 49 | Block* merge; | ||
| 50 | Block* skip; | ||
| 51 | } break_node; | ||
| 52 | }; | ||
| 53 | }; | ||
| 54 | using AbstractSyntaxList = std::vector<AbstractSyntaxNode>; | ||
| 55 | |||
| 56 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/basic_block.cpp b/src/shader_recompiler/frontend/ir/basic_block.cpp index f92fc2571..7c08b25ce 100644 --- a/src/shader_recompiler/frontend/ir/basic_block.cpp +++ b/src/shader_recompiler/frontend/ir/basic_block.cpp | |||
| @@ -14,10 +14,7 @@ | |||
| 14 | 14 | ||
| 15 | namespace Shader::IR { | 15 | namespace Shader::IR { |
| 16 | 16 | ||
| 17 | Block::Block(ObjectPool<Inst>& inst_pool_, u32 begin, u32 end) | 17 | Block::Block(ObjectPool<Inst>& inst_pool_) : inst_pool{&inst_pool_} {} |
| 18 | : inst_pool{&inst_pool_}, location_begin{begin}, location_end{end} {} | ||
| 19 | |||
| 20 | Block::Block(ObjectPool<Inst>& inst_pool_) : Block{inst_pool_, 0, 0} {} | ||
| 21 | 18 | ||
| 22 | Block::~Block() = default; | 19 | Block::~Block() = default; |
| 23 | 20 | ||
| @@ -40,39 +37,15 @@ Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op, | |||
| 40 | return result_it; | 37 | return result_it; |
| 41 | } | 38 | } |
| 42 | 39 | ||
| 43 | void Block::SetBranches(Condition cond, Block* branch_true_, Block* branch_false_) { | 40 | void Block::AddBranch(Block* block) { |
| 44 | branch_cond = cond; | 41 | if (std::ranges::find(imm_successors, block) != imm_successors.end()) { |
| 45 | branch_true = branch_true_; | 42 | throw LogicError("Successor already inserted"); |
| 46 | branch_false = branch_false_; | 43 | } |
| 47 | } | 44 | if (std::ranges::find(block->imm_predecessors, this) != block->imm_predecessors.end()) { |
| 48 | 45 | throw LogicError("Predecessor already inserted"); | |
| 49 | void Block::SetBranch(Block* branch) { | ||
| 50 | branch_cond = Condition{true}; | ||
| 51 | branch_true = branch; | ||
| 52 | } | ||
| 53 | |||
| 54 | void Block::SetReturn() { | ||
| 55 | branch_cond = Condition{true}; | ||
| 56 | branch_true = nullptr; | ||
| 57 | branch_false = nullptr; | ||
| 58 | } | ||
| 59 | |||
| 60 | bool Block::IsVirtual() const noexcept { | ||
| 61 | return location_begin == location_end; | ||
| 62 | } | ||
| 63 | |||
| 64 | u32 Block::LocationBegin() const noexcept { | ||
| 65 | return location_begin; | ||
| 66 | } | ||
| 67 | |||
| 68 | u32 Block::LocationEnd() const noexcept { | ||
| 69 | return location_end; | ||
| 70 | } | ||
| 71 | |||
| 72 | void Block::AddImmediatePredecessor(Block* block) { | ||
| 73 | if (std::ranges::find(imm_predecessors, block) == imm_predecessors.end()) { | ||
| 74 | imm_predecessors.push_back(block); | ||
| 75 | } | 46 | } |
| 47 | imm_successors.push_back(block); | ||
| 48 | block->imm_predecessors.push_back(this); | ||
| 76 | } | 49 | } |
| 77 | 50 | ||
| 78 | static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index, | 51 | static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index, |
| @@ -92,15 +65,11 @@ static size_t InstIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& in | |||
| 92 | return it->second; | 65 | return it->second; |
| 93 | } | 66 | } |
| 94 | 67 | ||
| 95 | static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_index, | 68 | static std::string ArgToIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index, |
| 96 | std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index, | ||
| 97 | const Value& arg) { | 69 | const Value& arg) { |
| 98 | if (arg.IsEmpty()) { | 70 | if (arg.IsEmpty()) { |
| 99 | return "<null>"; | 71 | return "<null>"; |
| 100 | } | 72 | } |
| 101 | if (arg.IsLabel()) { | ||
| 102 | return BlockToIndex(block_to_index, arg.Label()); | ||
| 103 | } | ||
| 104 | if (!arg.IsImmediate() || arg.IsIdentity()) { | 73 | if (!arg.IsImmediate() || arg.IsIdentity()) { |
| 105 | return fmt::format("%{}", InstIndex(inst_to_index, inst_index, arg.Inst())); | 74 | return fmt::format("%{}", InstIndex(inst_to_index, inst_index, arg.Inst())); |
| 106 | } | 75 | } |
| @@ -140,8 +109,7 @@ std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& | |||
| 140 | if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) { | 109 | if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) { |
| 141 | ret += fmt::format(" ${}", it->second); | 110 | ret += fmt::format(" ${}", it->second); |
| 142 | } | 111 | } |
| 143 | ret += fmt::format(": begin={:04x} end={:04x}\n", block.LocationBegin(), block.LocationEnd()); | 112 | ret += '\n'; |
| 144 | |||
| 145 | for (const Inst& inst : block) { | 113 | for (const Inst& inst : block) { |
| 146 | const Opcode op{inst.GetOpcode()}; | 114 | const Opcode op{inst.GetOpcode()}; |
| 147 | ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst)); | 115 | ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst)); |
| @@ -153,7 +121,7 @@ std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& | |||
| 153 | const size_t arg_count{inst.NumArgs()}; | 121 | const size_t arg_count{inst.NumArgs()}; |
| 154 | for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) { | 122 | for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) { |
| 155 | const Value arg{inst.Arg(arg_index)}; | 123 | const Value arg{inst.Arg(arg_index)}; |
| 156 | const std::string arg_str{ArgToIndex(block_to_index, inst_to_index, inst_index, arg)}; | 124 | const std::string arg_str{ArgToIndex(inst_to_index, inst_index, arg)}; |
| 157 | ret += arg_index != 0 ? ", " : " "; | 125 | ret += arg_index != 0 ? ", " : " "; |
| 158 | if (op == Opcode::Phi) { | 126 | if (op == Opcode::Phi) { |
| 159 | ret += fmt::format("[ {}, {} ]", arg_str, | 127 | ret += fmt::format("[ {}, {} ]", arg_str, |
diff --git a/src/shader_recompiler/frontend/ir/basic_block.h b/src/shader_recompiler/frontend/ir/basic_block.h index 0b0c97af6..7e134b4c7 100644 --- a/src/shader_recompiler/frontend/ir/basic_block.h +++ b/src/shader_recompiler/frontend/ir/basic_block.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <boost/intrusive/list.hpp> | 12 | #include <boost/intrusive/list.hpp> |
| 13 | 13 | ||
| 14 | #include "common/bit_cast.h" | 14 | #include "common/bit_cast.h" |
| 15 | #include "common/common_types.h" | ||
| 15 | #include "shader_recompiler/frontend/ir/condition.h" | 16 | #include "shader_recompiler/frontend/ir/condition.h" |
| 16 | #include "shader_recompiler/frontend/ir/value.h" | 17 | #include "shader_recompiler/frontend/ir/value.h" |
| 17 | #include "shader_recompiler/object_pool.h" | 18 | #include "shader_recompiler/object_pool.h" |
| @@ -27,7 +28,6 @@ public: | |||
| 27 | using reverse_iterator = InstructionList::reverse_iterator; | 28 | using reverse_iterator = InstructionList::reverse_iterator; |
| 28 | using const_reverse_iterator = InstructionList::const_reverse_iterator; | 29 | using const_reverse_iterator = InstructionList::const_reverse_iterator; |
| 29 | 30 | ||
| 30 | explicit Block(ObjectPool<Inst>& inst_pool_, u32 begin, u32 end); | ||
| 31 | explicit Block(ObjectPool<Inst>& inst_pool_); | 31 | explicit Block(ObjectPool<Inst>& inst_pool_); |
| 32 | ~Block(); | 32 | ~Block(); |
| 33 | 33 | ||
| @@ -44,22 +44,8 @@ public: | |||
| 44 | iterator PrependNewInst(iterator insertion_point, Opcode op, | 44 | iterator PrependNewInst(iterator insertion_point, Opcode op, |
| 45 | std::initializer_list<Value> args = {}, u32 flags = 0); | 45 | std::initializer_list<Value> args = {}, u32 flags = 0); |
| 46 | 46 | ||
| 47 | /// Set the branches to jump to when all instructions have executed. | 47 | /// Adds a new branch to this basic block. |
| 48 | void SetBranches(Condition cond, Block* branch_true, Block* branch_false); | 48 | void AddBranch(Block* block); |
| 49 | /// Set the branch to unconditionally jump to when all instructions have executed. | ||
| 50 | void SetBranch(Block* branch); | ||
| 51 | /// Mark the block as a return block. | ||
| 52 | void SetReturn(); | ||
| 53 | |||
| 54 | /// Returns true when the block does not implement any guest instructions directly. | ||
| 55 | [[nodiscard]] bool IsVirtual() const noexcept; | ||
| 56 | /// Gets the starting location of this basic block. | ||
| 57 | [[nodiscard]] u32 LocationBegin() const noexcept; | ||
| 58 | /// Gets the end location for this basic block. | ||
| 59 | [[nodiscard]] u32 LocationEnd() const noexcept; | ||
| 60 | |||
| 61 | /// Adds a new immediate predecessor to this basic block. | ||
| 62 | void AddImmediatePredecessor(Block* block); | ||
| 63 | 49 | ||
| 64 | /// Gets a mutable reference to the instruction list for this basic block. | 50 | /// Gets a mutable reference to the instruction list for this basic block. |
| 65 | [[nodiscard]] InstructionList& Instructions() noexcept { | 51 | [[nodiscard]] InstructionList& Instructions() noexcept { |
| @@ -71,9 +57,13 @@ public: | |||
| 71 | } | 57 | } |
| 72 | 58 | ||
| 73 | /// Gets an immutable span to the immediate predecessors. | 59 | /// Gets an immutable span to the immediate predecessors. |
| 74 | [[nodiscard]] std::span<Block* const> ImmediatePredecessors() const noexcept { | 60 | [[nodiscard]] std::span<Block* const> ImmPredecessors() const noexcept { |
| 75 | return imm_predecessors; | 61 | return imm_predecessors; |
| 76 | } | 62 | } |
| 63 | /// Gets an immutable span to the immediate successors. | ||
| 64 | [[nodiscard]] std::span<Block* const> ImmSuccessors() const noexcept { | ||
| 65 | return imm_successors; | ||
| 66 | } | ||
| 77 | 67 | ||
| 78 | /// Intrusively store the host definition of this instruction. | 68 | /// Intrusively store the host definition of this instruction. |
| 79 | template <typename DefinitionType> | 69 | template <typename DefinitionType> |
| @@ -87,19 +77,6 @@ public: | |||
| 87 | return Common::BitCast<DefinitionType>(definition); | 77 | return Common::BitCast<DefinitionType>(definition); |
| 88 | } | 78 | } |
| 89 | 79 | ||
| 90 | [[nodiscard]] Condition BranchCondition() const noexcept { | ||
| 91 | return branch_cond; | ||
| 92 | } | ||
| 93 | [[nodiscard]] bool IsTerminationBlock() const noexcept { | ||
| 94 | return !branch_true && !branch_false; | ||
| 95 | } | ||
| 96 | [[nodiscard]] Block* TrueBranch() const noexcept { | ||
| 97 | return branch_true; | ||
| 98 | } | ||
| 99 | [[nodiscard]] Block* FalseBranch() const noexcept { | ||
| 100 | return branch_false; | ||
| 101 | } | ||
| 102 | |||
| 103 | void SetSsaRegValue(IR::Reg reg, const Value& value) noexcept { | 80 | void SetSsaRegValue(IR::Reg reg, const Value& value) noexcept { |
| 104 | ssa_reg_values[RegIndex(reg)] = value; | 81 | ssa_reg_values[RegIndex(reg)] = value; |
| 105 | } | 82 | } |
| @@ -178,22 +155,14 @@ public: | |||
| 178 | private: | 155 | private: |
| 179 | /// Memory pool for instruction list | 156 | /// Memory pool for instruction list |
| 180 | ObjectPool<Inst>* inst_pool; | 157 | ObjectPool<Inst>* inst_pool; |
| 181 | /// Starting location of this block | ||
| 182 | u32 location_begin; | ||
| 183 | /// End location of this block | ||
| 184 | u32 location_end; | ||
| 185 | 158 | ||
| 186 | /// List of instructions in this block | 159 | /// List of instructions in this block |
| 187 | InstructionList instructions; | 160 | InstructionList instructions; |
| 188 | 161 | ||
| 189 | /// Condition to choose the branch to take | ||
| 190 | Condition branch_cond{true}; | ||
| 191 | /// Block to jump into when the branch condition evaluates as true | ||
| 192 | Block* branch_true{nullptr}; | ||
| 193 | /// Block to jump into when the branch condition evaluates as false | ||
| 194 | Block* branch_false{nullptr}; | ||
| 195 | /// Block immediate predecessors | 162 | /// Block immediate predecessors |
| 196 | std::vector<Block*> imm_predecessors; | 163 | std::vector<Block*> imm_predecessors; |
| 164 | /// Block immediate successors | ||
| 165 | std::vector<Block*> imm_successors; | ||
| 197 | 166 | ||
| 198 | /// Intrusively store the value of a register in the block. | 167 | /// Intrusively store the value of a register in the block. |
| 199 | std::array<Value, NUM_REGS> ssa_reg_values; | 168 | std::array<Value, NUM_REGS> ssa_reg_values; |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index ce6c9af07..eb45aa477 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp | |||
| @@ -61,25 +61,28 @@ F64 IREmitter::Imm64(f64 value) const { | |||
| 61 | return F64{Value{value}}; | 61 | return F64{Value{value}}; |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | void IREmitter::Branch(Block* label) { | 64 | void IREmitter::Prologue() { |
| 65 | label->AddImmediatePredecessor(block); | 65 | Inst(Opcode::Prologue); |
| 66 | block->SetBranch(label); | ||
| 67 | Inst(Opcode::Branch, label); | ||
| 68 | } | 66 | } |
| 69 | 67 | ||
| 70 | void IREmitter::BranchConditional(const U1& condition, Block* true_label, Block* false_label) { | 68 | void IREmitter::Epilogue() { |
| 71 | block->SetBranches(IR::Condition{true}, true_label, false_label); | 69 | Inst(Opcode::Epilogue); |
| 72 | true_label->AddImmediatePredecessor(block); | ||
| 73 | false_label->AddImmediatePredecessor(block); | ||
| 74 | Inst(Opcode::BranchConditional, condition, true_label, false_label); | ||
| 75 | } | 70 | } |
| 76 | 71 | ||
| 77 | void IREmitter::LoopMerge(Block* merge_block, Block* continue_target) { | 72 | void IREmitter::BranchConditionRef(const U1& cond) { |
| 78 | Inst(Opcode::LoopMerge, merge_block, continue_target); | 73 | Inst(Opcode::BranchConditionRef, cond); |
| 79 | } | 74 | } |
| 80 | 75 | ||
| 81 | void IREmitter::SelectionMerge(Block* merge_block) { | 76 | void IREmitter::DemoteToHelperInvocation() { |
| 82 | Inst(Opcode::SelectionMerge, merge_block); | 77 | Inst(Opcode::DemoteToHelperInvocation); |
| 78 | } | ||
| 79 | |||
| 80 | void IREmitter::EmitVertex(const U32& stream) { | ||
| 81 | Inst(Opcode::EmitVertex, stream); | ||
| 82 | } | ||
| 83 | |||
| 84 | void IREmitter::EndPrimitive(const U32& stream) { | ||
| 85 | Inst(Opcode::EndPrimitive, stream); | ||
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | void IREmitter::Barrier() { | 88 | void IREmitter::Barrier() { |
| @@ -94,37 +97,6 @@ void IREmitter::DeviceMemoryBarrier() { | |||
| 94 | Inst(Opcode::DeviceMemoryBarrier); | 97 | Inst(Opcode::DeviceMemoryBarrier); |
| 95 | } | 98 | } |
| 96 | 99 | ||
| 97 | void IREmitter::Return() { | ||
| 98 | block->SetReturn(); | ||
| 99 | Inst(Opcode::Return); | ||
| 100 | } | ||
| 101 | |||
| 102 | void IREmitter::Unreachable() { | ||
| 103 | Inst(Opcode::Unreachable); | ||
| 104 | } | ||
| 105 | |||
| 106 | void IREmitter::DemoteToHelperInvocation(Block* continue_label) { | ||
| 107 | block->SetBranch(continue_label); | ||
| 108 | continue_label->AddImmediatePredecessor(block); | ||
| 109 | Inst(Opcode::DemoteToHelperInvocation, continue_label); | ||
| 110 | } | ||
| 111 | |||
| 112 | void IREmitter::Prologue() { | ||
| 113 | Inst(Opcode::Prologue); | ||
| 114 | } | ||
| 115 | |||
| 116 | void IREmitter::Epilogue() { | ||
| 117 | Inst(Opcode::Epilogue); | ||
| 118 | } | ||
| 119 | |||
| 120 | void IREmitter::EmitVertex(const U32& stream) { | ||
| 121 | Inst(Opcode::EmitVertex, stream); | ||
| 122 | } | ||
| 123 | |||
| 124 | void IREmitter::EndPrimitive(const U32& stream) { | ||
| 125 | Inst(Opcode::EndPrimitive, stream); | ||
| 126 | } | ||
| 127 | |||
| 128 | U32 IREmitter::GetReg(IR::Reg reg) { | 100 | U32 IREmitter::GetReg(IR::Reg reg) { |
| 129 | return Inst<U32>(Opcode::GetRegister, reg); | 101 | return Inst<U32>(Opcode::GetRegister, reg); |
| 130 | } | 102 | } |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index fd41b7e89..7a83c33d3 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h | |||
| @@ -32,17 +32,10 @@ public: | |||
| 32 | [[nodiscard]] U64 Imm64(s64 value) const; | 32 | [[nodiscard]] U64 Imm64(s64 value) const; |
| 33 | [[nodiscard]] F64 Imm64(f64 value) const; | 33 | [[nodiscard]] F64 Imm64(f64 value) const; |
| 34 | 34 | ||
| 35 | void Branch(Block* label); | ||
| 36 | void BranchConditional(const U1& condition, Block* true_label, Block* false_label); | ||
| 37 | void LoopMerge(Block* merge_block, Block* continue_target); | ||
| 38 | void SelectionMerge(Block* merge_block); | ||
| 39 | void Return(); | ||
| 40 | void Unreachable(); | ||
| 41 | void DemoteToHelperInvocation(Block* continue_label); | ||
| 42 | |||
| 43 | void Prologue(); | 35 | void Prologue(); |
| 44 | void Epilogue(); | 36 | void Epilogue(); |
| 45 | 37 | void BranchConditionRef(const U1& cond); | |
| 38 | void DemoteToHelperInvocation(); | ||
| 46 | void EmitVertex(const U32& stream); | 39 | void EmitVertex(const U32& stream); |
| 47 | void EndPrimitive(const U32& stream); | 40 | void EndPrimitive(const U32& stream); |
| 48 | 41 | ||
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index 616ef17d4..364574240 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp | |||
| @@ -56,19 +56,14 @@ Inst::~Inst() { | |||
| 56 | 56 | ||
| 57 | bool Inst::MayHaveSideEffects() const noexcept { | 57 | bool Inst::MayHaveSideEffects() const noexcept { |
| 58 | switch (op) { | 58 | switch (op) { |
| 59 | case Opcode::Branch: | 59 | case Opcode::Prologue: |
| 60 | case Opcode::BranchConditional: | 60 | case Opcode::Epilogue: |
| 61 | case Opcode::LoopMerge: | 61 | case Opcode::BranchConditionRef: |
| 62 | case Opcode::SelectionMerge: | ||
| 63 | case Opcode::Return: | ||
| 64 | case Opcode::Join: | 62 | case Opcode::Join: |
| 65 | case Opcode::Unreachable: | ||
| 66 | case Opcode::DemoteToHelperInvocation: | 63 | case Opcode::DemoteToHelperInvocation: |
| 67 | case Opcode::Barrier: | 64 | case Opcode::Barrier: |
| 68 | case Opcode::WorkgroupMemoryBarrier: | 65 | case Opcode::WorkgroupMemoryBarrier: |
| 69 | case Opcode::DeviceMemoryBarrier: | 66 | case Opcode::DeviceMemoryBarrier: |
| 70 | case Opcode::Prologue: | ||
| 71 | case Opcode::Epilogue: | ||
| 72 | case Opcode::EmitVertex: | 67 | case Opcode::EmitVertex: |
| 73 | case Opcode::EndPrimitive: | 68 | case Opcode::EndPrimitive: |
| 74 | case Opcode::SetAttribute: | 69 | case Opcode::SetAttribute: |
diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h index 2b9c0ed8c..56b001902 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.h +++ b/src/shader_recompiler/frontend/ir/opcodes.h | |||
| @@ -30,7 +30,6 @@ struct OpcodeMeta { | |||
| 30 | // using enum Type; | 30 | // using enum Type; |
| 31 | constexpr Type Void{Type::Void}; | 31 | constexpr Type Void{Type::Void}; |
| 32 | constexpr Type Opaque{Type::Opaque}; | 32 | constexpr Type Opaque{Type::Opaque}; |
| 33 | constexpr Type Label{Type::Label}; | ||
| 34 | constexpr Type Reg{Type::Reg}; | 33 | constexpr Type Reg{Type::Reg}; |
| 35 | constexpr Type Pred{Type::Pred}; | 34 | constexpr Type Pred{Type::Pred}; |
| 36 | constexpr Type Attribute{Type::Attribute}; | 35 | constexpr Type Attribute{Type::Attribute}; |
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 9165421f8..75ddb6b6f 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc | |||
| @@ -7,27 +7,20 @@ OPCODE(Phi, Opaque, | |||
| 7 | OPCODE(Identity, Opaque, Opaque, ) | 7 | OPCODE(Identity, Opaque, Opaque, ) |
| 8 | OPCODE(Void, Void, ) | 8 | OPCODE(Void, Void, ) |
| 9 | 9 | ||
| 10 | // Control flow | 10 | // Special operations |
| 11 | OPCODE(Branch, Void, Label, ) | 11 | OPCODE(Prologue, Void, ) |
| 12 | OPCODE(BranchConditional, Void, U1, Label, Label, ) | 12 | OPCODE(Epilogue, Void, ) |
| 13 | OPCODE(LoopMerge, Void, Label, Label, ) | 13 | OPCODE(BranchConditionRef, Void, U1, ) |
| 14 | OPCODE(SelectionMerge, Void, Label, ) | ||
| 15 | OPCODE(Return, Void, ) | ||
| 16 | OPCODE(Join, Void, ) | 14 | OPCODE(Join, Void, ) |
| 17 | OPCODE(Unreachable, Void, ) | 15 | OPCODE(DemoteToHelperInvocation, Void, ) |
| 18 | OPCODE(DemoteToHelperInvocation, Void, Label, ) | 16 | OPCODE(EmitVertex, Void, U32, ) |
| 17 | OPCODE(EndPrimitive, Void, U32, ) | ||
| 19 | 18 | ||
| 20 | // Barriers | 19 | // Barriers |
| 21 | OPCODE(Barrier, Void, ) | 20 | OPCODE(Barrier, Void, ) |
| 22 | OPCODE(WorkgroupMemoryBarrier, Void, ) | 21 | OPCODE(WorkgroupMemoryBarrier, Void, ) |
| 23 | OPCODE(DeviceMemoryBarrier, Void, ) | 22 | OPCODE(DeviceMemoryBarrier, Void, ) |
| 24 | 23 | ||
| 25 | // Special operations | ||
| 26 | OPCODE(Prologue, Void, ) | ||
| 27 | OPCODE(Epilogue, Void, ) | ||
| 28 | OPCODE(EmitVertex, Void, U32, ) | ||
| 29 | OPCODE(EndPrimitive, Void, U32, ) | ||
| 30 | |||
| 31 | // Context getters/setters | 24 | // Context getters/setters |
| 32 | OPCODE(GetRegister, U32, Reg, ) | 25 | OPCODE(GetRegister, U32, Reg, ) |
| 33 | OPCODE(SetRegister, Void, Reg, U32, ) | 26 | OPCODE(SetRegister, Void, Reg, U32, ) |
diff --git a/src/shader_recompiler/frontend/ir/post_order.cpp b/src/shader_recompiler/frontend/ir/post_order.cpp index 8709a2ea1..1a28df7fb 100644 --- a/src/shader_recompiler/frontend/ir/post_order.cpp +++ b/src/shader_recompiler/frontend/ir/post_order.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | |||
| 5 | #include <boost/container/flat_set.hpp> | 7 | #include <boost/container/flat_set.hpp> |
| 6 | #include <boost/container/small_vector.hpp> | 8 | #include <boost/container/small_vector.hpp> |
| 7 | 9 | ||
| @@ -10,35 +12,31 @@ | |||
| 10 | 12 | ||
| 11 | namespace Shader::IR { | 13 | namespace Shader::IR { |
| 12 | 14 | ||
| 13 | BlockList PostOrder(const BlockList& blocks) { | 15 | BlockList PostOrder(const AbstractSyntaxNode& root) { |
| 14 | boost::container::small_vector<Block*, 16> block_stack; | 16 | boost::container::small_vector<Block*, 16> block_stack; |
| 15 | boost::container::flat_set<Block*> visited; | 17 | boost::container::flat_set<Block*> visited; |
| 16 | |||
| 17 | BlockList post_order_blocks; | 18 | BlockList post_order_blocks; |
| 18 | post_order_blocks.reserve(blocks.size()); | ||
| 19 | 19 | ||
| 20 | Block* const first_block{blocks.front()}; | 20 | if (root.type != AbstractSyntaxNode::Type::Block) { |
| 21 | throw LogicError("First node in abstract syntax list root is not a block"); | ||
| 22 | } | ||
| 23 | Block* const first_block{root.block}; | ||
| 21 | visited.insert(first_block); | 24 | visited.insert(first_block); |
| 22 | block_stack.push_back(first_block); | 25 | block_stack.push_back(first_block); |
| 23 | 26 | ||
| 24 | const auto visit_branch = [&](Block* block, Block* branch) { | ||
| 25 | if (!branch) { | ||
| 26 | return false; | ||
| 27 | } | ||
| 28 | if (!visited.insert(branch).second) { | ||
| 29 | return false; | ||
| 30 | } | ||
| 31 | // Calling push_back twice is faster than insert on MSVC | ||
| 32 | block_stack.push_back(block); | ||
| 33 | block_stack.push_back(branch); | ||
| 34 | return true; | ||
| 35 | }; | ||
| 36 | while (!block_stack.empty()) { | 27 | while (!block_stack.empty()) { |
| 37 | Block* const block{block_stack.back()}; | 28 | Block* const block{block_stack.back()}; |
| 29 | const auto visit{[&](Block* branch) { | ||
| 30 | if (!visited.insert(branch).second) { | ||
| 31 | return false; | ||
| 32 | } | ||
| 33 | // Calling push_back twice is faster than insert on MSVC | ||
| 34 | block_stack.push_back(block); | ||
| 35 | block_stack.push_back(branch); | ||
| 36 | return true; | ||
| 37 | }}; | ||
| 38 | block_stack.pop_back(); | 38 | block_stack.pop_back(); |
| 39 | 39 | if (std::ranges::none_of(block->ImmSuccessors(), visit)) { | |
| 40 | if (!visit_branch(block, block->TrueBranch()) && | ||
| 41 | !visit_branch(block, block->FalseBranch())) { | ||
| 42 | post_order_blocks.push_back(block); | 40 | post_order_blocks.push_back(block); |
| 43 | } | 41 | } |
| 44 | } | 42 | } |
diff --git a/src/shader_recompiler/frontend/ir/post_order.h b/src/shader_recompiler/frontend/ir/post_order.h index 30137ff57..58a0467a0 100644 --- a/src/shader_recompiler/frontend/ir/post_order.h +++ b/src/shader_recompiler/frontend/ir/post_order.h | |||
| @@ -5,9 +5,10 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/frontend/ir/basic_block.h" | 7 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 8 | #include "shader_recompiler/frontend/ir/abstract_syntax_list.h" | ||
| 8 | 9 | ||
| 9 | namespace Shader::IR { | 10 | namespace Shader::IR { |
| 10 | 11 | ||
| 11 | BlockList PostOrder(const BlockList& blocks); | 12 | BlockList PostOrder(const AbstractSyntaxNode& root); |
| 12 | 13 | ||
| 13 | } // namespace Shader::IR | 14 | } // namespace Shader::IR |
diff --git a/src/shader_recompiler/frontend/ir/program.h b/src/shader_recompiler/frontend/ir/program.h index 51e1a8c77..9ede5b48d 100644 --- a/src/shader_recompiler/frontend/ir/program.h +++ b/src/shader_recompiler/frontend/ir/program.h | |||
| @@ -7,8 +7,7 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | 9 | ||
| 10 | #include <boost/container/small_vector.hpp> | 10 | #include "shader_recompiler/frontend/ir/abstract_syntax_list.h" |
| 11 | |||
| 12 | #include "shader_recompiler/frontend/ir/basic_block.h" | 11 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 13 | #include "shader_recompiler/program_header.h" | 12 | #include "shader_recompiler/program_header.h" |
| 14 | #include "shader_recompiler/shader_info.h" | 13 | #include "shader_recompiler/shader_info.h" |
| @@ -17,6 +16,7 @@ | |||
| 17 | namespace Shader::IR { | 16 | namespace Shader::IR { |
| 18 | 17 | ||
| 19 | struct Program { | 18 | struct Program { |
| 19 | AbstractSyntaxList syntax_list; | ||
| 20 | BlockList blocks; | 20 | BlockList blocks; |
| 21 | BlockList post_order_blocks; | 21 | BlockList post_order_blocks; |
| 22 | Info info; | 22 | Info info; |
diff --git a/src/shader_recompiler/frontend/ir/type.h b/src/shader_recompiler/frontend/ir/type.h index 8b3b33852..294b230c4 100644 --- a/src/shader_recompiler/frontend/ir/type.h +++ b/src/shader_recompiler/frontend/ir/type.h | |||
| @@ -16,31 +16,30 @@ namespace Shader::IR { | |||
| 16 | enum class Type { | 16 | enum class Type { |
| 17 | Void = 0, | 17 | Void = 0, |
| 18 | Opaque = 1 << 0, | 18 | Opaque = 1 << 0, |
| 19 | Label = 1 << 1, | 19 | Reg = 1 << 1, |
| 20 | Reg = 1 << 2, | 20 | Pred = 1 << 2, |
| 21 | Pred = 1 << 3, | 21 | Attribute = 1 << 3, |
| 22 | Attribute = 1 << 4, | 22 | Patch = 1 << 4, |
| 23 | Patch = 1 << 5, | 23 | U1 = 1 << 5, |
| 24 | U1 = 1 << 6, | 24 | U8 = 1 << 6, |
| 25 | U8 = 1 << 7, | 25 | U16 = 1 << 7, |
| 26 | U16 = 1 << 8, | 26 | U32 = 1 << 8, |
| 27 | U32 = 1 << 9, | 27 | U64 = 1 << 9, |
| 28 | U64 = 1 << 10, | 28 | F16 = 1 << 10, |
| 29 | F16 = 1 << 11, | 29 | F32 = 1 << 11, |
| 30 | F32 = 1 << 12, | 30 | F64 = 1 << 12, |
| 31 | F64 = 1 << 13, | 31 | U32x2 = 1 << 13, |
| 32 | U32x2 = 1 << 14, | 32 | U32x3 = 1 << 14, |
| 33 | U32x3 = 1 << 15, | 33 | U32x4 = 1 << 15, |
| 34 | U32x4 = 1 << 16, | 34 | F16x2 = 1 << 16, |
| 35 | F16x2 = 1 << 17, | 35 | F16x3 = 1 << 17, |
| 36 | F16x3 = 1 << 18, | 36 | F16x4 = 1 << 18, |
| 37 | F16x4 = 1 << 19, | 37 | F32x2 = 1 << 19, |
| 38 | F32x2 = 1 << 20, | 38 | F32x3 = 1 << 20, |
| 39 | F32x3 = 1 << 21, | 39 | F32x4 = 1 << 21, |
| 40 | F32x4 = 1 << 22, | 40 | F64x2 = 1 << 22, |
| 41 | F64x2 = 1 << 23, | 41 | F64x3 = 1 << 23, |
| 42 | F64x3 = 1 << 24, | 42 | F64x4 = 1 << 24, |
| 43 | F64x4 = 1 << 25, | ||
| 44 | }; | 43 | }; |
| 45 | DECLARE_ENUM_FLAG_OPERATORS(Type) | 44 | DECLARE_ENUM_FLAG_OPERATORS(Type) |
| 46 | 45 | ||
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp index b962f170d..d365ea1bc 100644 --- a/src/shader_recompiler/frontend/ir/value.cpp +++ b/src/shader_recompiler/frontend/ir/value.cpp | |||
| @@ -9,8 +9,6 @@ namespace Shader::IR { | |||
| 9 | 9 | ||
| 10 | Value::Value(IR::Inst* value) noexcept : type{Type::Opaque}, inst{value} {} | 10 | Value::Value(IR::Inst* value) noexcept : type{Type::Opaque}, inst{value} {} |
| 11 | 11 | ||
| 12 | Value::Value(IR::Block* value) noexcept : type{Type::Label}, label{value} {} | ||
| 13 | |||
| 14 | Value::Value(IR::Reg value) noexcept : type{Type::Reg}, reg{value} {} | 12 | Value::Value(IR::Reg value) noexcept : type{Type::Reg}, reg{value} {} |
| 15 | 13 | ||
| 16 | Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {} | 14 | Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {} |
| @@ -33,10 +31,6 @@ Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} | |||
| 33 | 31 | ||
| 34 | Value::Value(f64 value) noexcept : type{Type::F64}, imm_f64{value} {} | 32 | Value::Value(f64 value) noexcept : type{Type::F64}, imm_f64{value} {} |
| 35 | 33 | ||
| 36 | bool Value::IsLabel() const noexcept { | ||
| 37 | return type == Type::Label; | ||
| 38 | } | ||
| 39 | |||
| 40 | IR::Type Value::Type() const noexcept { | 34 | IR::Type Value::Type() const noexcept { |
| 41 | if (IsPhi()) { | 35 | if (IsPhi()) { |
| 42 | // The type of a phi node is stored in its flags | 36 | // The type of a phi node is stored in its flags |
| @@ -60,8 +54,6 @@ bool Value::operator==(const Value& other) const { | |||
| 60 | return true; | 54 | return true; |
| 61 | case Type::Opaque: | 55 | case Type::Opaque: |
| 62 | return inst == other.inst; | 56 | return inst == other.inst; |
| 63 | case Type::Label: | ||
| 64 | return label == other.label; | ||
| 65 | case Type::Reg: | 57 | case Type::Reg: |
| 66 | return reg == other.reg; | 58 | return reg == other.reg; |
| 67 | case Type::Pred: | 59 | case Type::Pred: |
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index beaf149f3..2ce49f953 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h | |||
| @@ -37,7 +37,6 @@ class Value { | |||
| 37 | public: | 37 | public: |
| 38 | Value() noexcept = default; | 38 | Value() noexcept = default; |
| 39 | explicit Value(IR::Inst* value) noexcept; | 39 | explicit Value(IR::Inst* value) noexcept; |
| 40 | explicit Value(IR::Block* value) noexcept; | ||
| 41 | explicit Value(IR::Reg value) noexcept; | 40 | explicit Value(IR::Reg value) noexcept; |
| 42 | explicit Value(IR::Pred value) noexcept; | 41 | explicit Value(IR::Pred value) noexcept; |
| 43 | explicit Value(IR::Attribute value) noexcept; | 42 | explicit Value(IR::Attribute value) noexcept; |
| @@ -54,11 +53,9 @@ public: | |||
| 54 | [[nodiscard]] bool IsPhi() const noexcept; | 53 | [[nodiscard]] bool IsPhi() const noexcept; |
| 55 | [[nodiscard]] bool IsEmpty() const noexcept; | 54 | [[nodiscard]] bool IsEmpty() const noexcept; |
| 56 | [[nodiscard]] bool IsImmediate() const noexcept; | 55 | [[nodiscard]] bool IsImmediate() const noexcept; |
| 57 | [[nodiscard]] bool IsLabel() const noexcept; | ||
| 58 | [[nodiscard]] IR::Type Type() const noexcept; | 56 | [[nodiscard]] IR::Type Type() const noexcept; |
| 59 | 57 | ||
| 60 | [[nodiscard]] IR::Inst* Inst() const; | 58 | [[nodiscard]] IR::Inst* Inst() const; |
| 61 | [[nodiscard]] IR::Block* Label() const; | ||
| 62 | [[nodiscard]] IR::Inst* InstRecursive() const; | 59 | [[nodiscard]] IR::Inst* InstRecursive() const; |
| 63 | [[nodiscard]] IR::Value Resolve() const; | 60 | [[nodiscard]] IR::Value Resolve() const; |
| 64 | [[nodiscard]] IR::Reg Reg() const; | 61 | [[nodiscard]] IR::Reg Reg() const; |
| @@ -80,7 +77,6 @@ private: | |||
| 80 | IR::Type type{}; | 77 | IR::Type type{}; |
| 81 | union { | 78 | union { |
| 82 | IR::Inst* inst{}; | 79 | IR::Inst* inst{}; |
| 83 | IR::Block* label; | ||
| 84 | IR::Reg reg; | 80 | IR::Reg reg; |
| 85 | IR::Pred pred; | 81 | IR::Pred pred; |
| 86 | IR::Attribute attribute; | 82 | IR::Attribute attribute; |
| @@ -304,11 +300,6 @@ inline IR::Inst* Value::Inst() const { | |||
| 304 | return inst; | 300 | return inst; |
| 305 | } | 301 | } |
| 306 | 302 | ||
| 307 | inline IR::Block* Value::Label() const { | ||
| 308 | DEBUG_ASSERT(type == Type::Label); | ||
| 309 | return label; | ||
| 310 | } | ||
| 311 | |||
| 312 | inline IR::Inst* Value::InstRecursive() const { | 303 | inline IR::Inst* Value::InstRecursive() const { |
| 313 | DEBUG_ASSERT(type == Type::Opaque); | 304 | DEBUG_ASSERT(type == Type::Opaque); |
| 314 | if (IsIdentity()) { | 305 | if (IsIdentity()) { |
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp index 0d3f00699..017c4b8fd 100644 --- a/src/shader_recompiler/frontend/maxwell/program.cpp +++ b/src/shader_recompiler/frontend/maxwell/program.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <ranges> | ||
| 7 | #include <vector> | 8 | #include <vector> |
| 8 | 9 | ||
| 9 | #include "shader_recompiler/frontend/ir/basic_block.h" | 10 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| @@ -15,6 +16,16 @@ | |||
| 15 | 16 | ||
| 16 | namespace Shader::Maxwell { | 17 | namespace Shader::Maxwell { |
| 17 | namespace { | 18 | namespace { |
| 19 | IR::BlockList GenerateBlocks(const IR::AbstractSyntaxList& syntax_list) { | ||
| 20 | auto syntax_blocks{syntax_list | std::views::filter([](const auto& node) { | ||
| 21 | return node.type == IR::AbstractSyntaxNode::Type::Block; | ||
| 22 | })}; | ||
| 23 | IR::BlockList blocks(std::ranges::distance(syntax_blocks)); | ||
| 24 | std::ranges::transform(syntax_blocks, blocks.begin(), | ||
| 25 | [](const IR::AbstractSyntaxNode& node) { return node.block; }); | ||
| 26 | return blocks; | ||
| 27 | } | ||
| 28 | |||
| 18 | void RemoveUnreachableBlocks(IR::Program& program) { | 29 | void RemoveUnreachableBlocks(IR::Program& program) { |
| 19 | // Some blocks might be unreachable if a function call exists unconditionally | 30 | // Some blocks might be unreachable if a function call exists unconditionally |
| 20 | // If this happens the number of blocks and post order blocks will mismatch | 31 | // If this happens the number of blocks and post order blocks will mismatch |
| @@ -23,7 +34,7 @@ void RemoveUnreachableBlocks(IR::Program& program) { | |||
| 23 | } | 34 | } |
| 24 | const auto begin{program.blocks.begin() + 1}; | 35 | const auto begin{program.blocks.begin() + 1}; |
| 25 | const auto end{program.blocks.end()}; | 36 | const auto end{program.blocks.end()}; |
| 26 | const auto pred{[](IR::Block* block) { return block->ImmediatePredecessors().empty(); }}; | 37 | const auto pred{[](IR::Block* block) { return block->ImmPredecessors().empty(); }}; |
| 27 | program.blocks.erase(std::remove_if(begin, end, pred), end); | 38 | program.blocks.erase(std::remove_if(begin, end, pred), end); |
| 28 | } | 39 | } |
| 29 | 40 | ||
| @@ -110,8 +121,9 @@ void AddNVNStorageBuffers(IR::Program& program) { | |||
| 110 | IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, | 121 | IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, |
| 111 | Environment& env, Flow::CFG& cfg) { | 122 | Environment& env, Flow::CFG& cfg) { |
| 112 | IR::Program program; | 123 | IR::Program program; |
| 113 | program.blocks = VisitAST(inst_pool, block_pool, env, cfg); | 124 | program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg); |
| 114 | program.post_order_blocks = PostOrder(program.blocks); | 125 | program.blocks = GenerateBlocks(program.syntax_list); |
| 126 | program.post_order_blocks = PostOrder(program.syntax_list.front()); | ||
| 115 | program.stage = env.ShaderStage(); | 127 | program.stage = env.ShaderStage(); |
| 116 | program.local_memory_size = env.LocalMemorySize(); | 128 | program.local_memory_size = env.LocalMemorySize(); |
| 117 | switch (program.stage) { | 129 | switch (program.stage) { |
| @@ -159,9 +171,7 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b | |||
| 159 | Optimization::VertexATransformPass(vertex_a); | 171 | Optimization::VertexATransformPass(vertex_a); |
| 160 | Optimization::VertexBTransformPass(vertex_b); | 172 | Optimization::VertexBTransformPass(vertex_b); |
| 161 | std::swap(result.blocks, vertex_a.blocks); | 173 | std::swap(result.blocks, vertex_a.blocks); |
| 162 | for (IR::Block* block : vertex_b.blocks) { | 174 | result.blocks.insert(result.blocks.end(), vertex_b.blocks.begin(), vertex_b.blocks.end()); |
| 163 | result.blocks.push_back(block); | ||
| 164 | } | ||
| 165 | result.stage = Stage::VertexB; | 175 | result.stage = Stage::VertexB; |
| 166 | result.info = vertex_a.info; | 176 | result.info = vertex_a.info; |
| 167 | result.local_memory_size = std::max(vertex_a.local_memory_size, vertex_b.local_memory_size); | 177 | result.local_memory_size = std::max(vertex_a.local_memory_size, vertex_b.local_memory_size); |
| @@ -173,7 +183,7 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b | |||
| 173 | Optimization::JoinTextureInfo(result.info, vertex_b.info); | 183 | Optimization::JoinTextureInfo(result.info, vertex_b.info); |
| 174 | Optimization::JoinStorageInfo(result.info, vertex_b.info); | 184 | Optimization::JoinStorageInfo(result.info, vertex_b.info); |
| 175 | Optimization::DualVertexJoinPass(result); | 185 | Optimization::DualVertexJoinPass(result); |
| 176 | result.post_order_blocks = PostOrder(result.blocks); | 186 | result.post_order_blocks = PostOrder(result.syntax_list.front()); |
| 177 | Optimization::DeadCodeEliminationPass(result); | 187 | Optimization::DeadCodeEliminationPass(result); |
| 178 | Optimization::VerificationPass(result); | 188 | Optimization::VerificationPass(result); |
| 179 | Optimization::CollectShaderInfoPass(env_vertex_b, result); | 189 | Optimization::CollectShaderInfoPass(env_vertex_b, result); |
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp index cc5410c6d..e7e2e9c82 100644 --- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp | |||
| @@ -36,7 +36,6 @@ using Tree = boost::intrusive::list<Statement, | |||
| 36 | // Avoid linear complexity on splice, size is never called | 36 | // Avoid linear complexity on splice, size is never called |
| 37 | boost::intrusive::constant_time_size<false>>; | 37 | boost::intrusive::constant_time_size<false>>; |
| 38 | using Node = Tree::iterator; | 38 | using Node = Tree::iterator; |
| 39 | using ConstNode = Tree::const_iterator; | ||
| 40 | 39 | ||
| 41 | enum class StatementType { | 40 | enum class StatementType { |
| 42 | Code, | 41 | Code, |
| @@ -91,7 +90,8 @@ struct IndirectBranchCond {}; | |||
| 91 | #pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement | 90 | #pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement |
| 92 | #endif | 91 | #endif |
| 93 | struct Statement : ListBaseHook { | 92 | struct Statement : ListBaseHook { |
| 94 | Statement(IR::Block* code_, Statement* up_) : code{code_}, up{up_}, type{StatementType::Code} {} | 93 | Statement(const Flow::Block* block_, Statement* up_) |
| 94 | : block{block_}, up{up_}, type{StatementType::Code} {} | ||
| 95 | Statement(Goto, Statement* cond_, Node label_, Statement* up_) | 95 | Statement(Goto, Statement* cond_, Node label_, Statement* up_) |
| 96 | : label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {} | 96 | : label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {} |
| 97 | Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {} | 97 | Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {} |
| @@ -125,7 +125,7 @@ struct Statement : ListBaseHook { | |||
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | union { | 127 | union { |
| 128 | IR::Block* code; | 128 | const Flow::Block* block; |
| 129 | Node label; | 129 | Node label; |
| 130 | Tree children; | 130 | Tree children; |
| 131 | IR::Condition guest_cond; | 131 | IR::Condition guest_cond; |
| @@ -171,8 +171,8 @@ std::string DumpTree(const Tree& tree, u32 indentation = 0) { | |||
| 171 | switch (stmt->type) { | 171 | switch (stmt->type) { |
| 172 | case StatementType::Code: | 172 | case StatementType::Code: |
| 173 | ret += fmt::format("{} Block {:04x} -> {:04x} (0x{:016x});\n", indent, | 173 | ret += fmt::format("{} Block {:04x} -> {:04x} (0x{:016x});\n", indent, |
| 174 | stmt->code->LocationBegin(), stmt->code->LocationEnd(), | 174 | stmt->block->begin, stmt->block->end, |
| 175 | reinterpret_cast<uintptr_t>(stmt->code)); | 175 | reinterpret_cast<uintptr_t>(stmt->block)); |
| 176 | break; | 176 | break; |
| 177 | case StatementType::Goto: | 177 | case StatementType::Goto: |
| 178 | ret += fmt::format("{} if ({}) goto L{};\n", indent, DumpExpr(stmt->cond), | 178 | ret += fmt::format("{} if ({}) goto L{};\n", indent, DumpExpr(stmt->cond), |
| @@ -407,11 +407,7 @@ private: | |||
| 407 | }}; | 407 | }}; |
| 408 | root.push_front(make_reset_variable()); | 408 | root.push_front(make_reset_variable()); |
| 409 | root.insert(ip, make_reset_variable()); | 409 | root.insert(ip, make_reset_variable()); |
| 410 | 410 | root.insert(ip, *pool.Create(&block, &root_stmt)); | |
| 411 | const u32 begin_offset{block.begin.Offset()}; | ||
| 412 | const u32 end_offset{block.end.Offset()}; | ||
| 413 | IR::Block* const ir_block{block_pool.Create(inst_pool, begin_offset, end_offset)}; | ||
| 414 | root.insert(ip, *pool.Create(ir_block, &root_stmt)); | ||
| 415 | 411 | ||
| 416 | switch (block.end_class) { | 412 | switch (block.end_class) { |
| 417 | case Flow::EndClass::Branch: { | 413 | case Flow::EndClass::Branch: { |
| @@ -620,13 +616,13 @@ private: | |||
| 620 | Statement root_stmt{FunctionTag{}}; | 616 | Statement root_stmt{FunctionTag{}}; |
| 621 | }; | 617 | }; |
| 622 | 618 | ||
| 623 | IR::Block* TryFindForwardBlock(const Statement& stmt) { | 619 | [[nodiscard]] Statement* TryFindForwardBlock(Statement& stmt) { |
| 624 | const Tree& tree{stmt.up->children}; | 620 | Tree& tree{stmt.up->children}; |
| 625 | const ConstNode end{tree.cend()}; | 621 | const Node end{tree.end()}; |
| 626 | ConstNode forward_node{std::next(Tree::s_iterator_to(stmt))}; | 622 | Node forward_node{std::next(Tree::s_iterator_to(stmt))}; |
| 627 | while (forward_node != end && !HasChildren(forward_node->type)) { | 623 | while (forward_node != end && !HasChildren(forward_node->type)) { |
| 628 | if (forward_node->type == StatementType::Code) { | 624 | if (forward_node->type == StatementType::Code) { |
| 629 | return forward_node->code; | 625 | return &*forward_node; |
| 630 | } | 626 | } |
| 631 | ++forward_node; | 627 | ++forward_node; |
| 632 | } | 628 | } |
| @@ -654,21 +650,29 @@ class TranslatePass { | |||
| 654 | public: | 650 | public: |
| 655 | TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_, | 651 | TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_, |
| 656 | ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt, | 652 | ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt, |
| 657 | IR::BlockList& block_list_) | 653 | IR::AbstractSyntaxList& syntax_list_) |
| 658 | : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_}, | 654 | : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_}, |
| 659 | block_list{block_list_} { | 655 | syntax_list{syntax_list_} { |
| 660 | Visit(root_stmt, nullptr, nullptr); | 656 | Visit(root_stmt, nullptr, nullptr); |
| 661 | 657 | ||
| 662 | IR::Block& first_block{*block_list.front()}; | 658 | IR::Block& first_block{*syntax_list.front().block}; |
| 663 | IR::IREmitter ir{first_block, first_block.begin()}; | 659 | IR::IREmitter ir{first_block, first_block.begin()}; |
| 664 | ir.Prologue(); | 660 | ir.Prologue(); |
| 665 | } | 661 | } |
| 666 | 662 | ||
| 667 | private: | 663 | private: |
| 668 | void Visit(Statement& parent, IR::Block* continue_block, IR::Block* break_block) { | 664 | void Visit(Statement& parent, IR::Block* break_block, IR::Block* fallthrough_block) { |
| 665 | IR::Block* current_block{}; | ||
| 666 | const auto ensure_block{[&] { | ||
| 667 | if (current_block) { | ||
| 668 | return; | ||
| 669 | } | ||
| 670 | current_block = block_pool.Create(inst_pool); | ||
| 671 | auto& node{syntax_list.emplace_back()}; | ||
| 672 | node.type = IR::AbstractSyntaxNode::Type::Block; | ||
| 673 | node.block = current_block; | ||
| 674 | }}; | ||
| 669 | Tree& tree{parent.children}; | 675 | Tree& tree{parent.children}; |
| 670 | IR::Block* current_block{nullptr}; | ||
| 671 | |||
| 672 | for (auto it = tree.begin(); it != tree.end(); ++it) { | 676 | for (auto it = tree.begin(); it != tree.end(); ++it) { |
| 673 | Statement& stmt{*it}; | 677 | Statement& stmt{*it}; |
| 674 | switch (stmt.type) { | 678 | switch (stmt.type) { |
| @@ -676,124 +680,157 @@ private: | |||
| 676 | // Labels can be ignored | 680 | // Labels can be ignored |
| 677 | break; | 681 | break; |
| 678 | case StatementType::Code: { | 682 | case StatementType::Code: { |
| 679 | if (current_block && current_block != stmt.code) { | 683 | ensure_block(); |
| 680 | IR::IREmitter{*current_block}.Branch(stmt.code); | 684 | Translate(env, current_block, stmt.block->begin.Offset(), stmt.block->end.Offset()); |
| 681 | } | ||
| 682 | current_block = stmt.code; | ||
| 683 | Translate(env, stmt.code); | ||
| 684 | block_list.push_back(stmt.code); | ||
| 685 | break; | 685 | break; |
| 686 | } | 686 | } |
| 687 | case StatementType::SetVariable: { | 687 | case StatementType::SetVariable: { |
| 688 | if (!current_block) { | 688 | ensure_block(); |
| 689 | current_block = MergeBlock(parent, stmt); | ||
| 690 | } | ||
| 691 | IR::IREmitter ir{*current_block}; | 689 | IR::IREmitter ir{*current_block}; |
| 692 | ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); | 690 | ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); |
| 693 | break; | 691 | break; |
| 694 | } | 692 | } |
| 695 | case StatementType::SetIndirectBranchVariable: { | 693 | case StatementType::SetIndirectBranchVariable: { |
| 696 | if (!current_block) { | 694 | ensure_block(); |
| 697 | current_block = MergeBlock(parent, stmt); | ||
| 698 | } | ||
| 699 | IR::IREmitter ir{*current_block}; | 695 | IR::IREmitter ir{*current_block}; |
| 700 | IR::U32 address{ir.IAdd(ir.GetReg(stmt.branch_reg), ir.Imm32(stmt.branch_offset))}; | 696 | IR::U32 address{ir.IAdd(ir.GetReg(stmt.branch_reg), ir.Imm32(stmt.branch_offset))}; |
| 701 | ir.SetIndirectBranchVariable(address); | 697 | ir.SetIndirectBranchVariable(address); |
| 702 | break; | 698 | break; |
| 703 | } | 699 | } |
| 704 | case StatementType::If: { | 700 | case StatementType::If: { |
| 705 | if (!current_block) { | 701 | ensure_block(); |
| 706 | current_block = block_pool.Create(inst_pool); | ||
| 707 | block_list.push_back(current_block); | ||
| 708 | } | ||
| 709 | IR::Block* const merge_block{MergeBlock(parent, stmt)}; | 702 | IR::Block* const merge_block{MergeBlock(parent, stmt)}; |
| 710 | 703 | ||
| 711 | // Visit children | ||
| 712 | const size_t first_block_index{block_list.size()}; | ||
| 713 | Visit(stmt, merge_block, break_block); | ||
| 714 | |||
| 715 | // Implement if header block | 704 | // Implement if header block |
| 716 | IR::Block* const first_if_block{block_list.at(first_block_index)}; | ||
| 717 | IR::IREmitter ir{*current_block}; | 705 | IR::IREmitter ir{*current_block}; |
| 718 | const IR::U1 cond{VisitExpr(ir, *stmt.cond)}; | 706 | const IR::U1 cond{VisitExpr(ir, *stmt.cond)}; |
| 719 | ir.SelectionMerge(merge_block); | 707 | ir.BranchConditionRef(cond); |
| 720 | ir.BranchConditional(cond, first_if_block, merge_block); | ||
| 721 | 708 | ||
| 709 | const size_t if_node_index{syntax_list.size()}; | ||
| 710 | syntax_list.emplace_back(); | ||
| 711 | |||
| 712 | // Visit children | ||
| 713 | const size_t then_block_index{syntax_list.size()}; | ||
| 714 | Visit(stmt, break_block, merge_block); | ||
| 715 | |||
| 716 | IR::Block* const then_block{syntax_list.at(then_block_index).block}; | ||
| 717 | current_block->AddBranch(then_block); | ||
| 718 | current_block->AddBranch(merge_block); | ||
| 722 | current_block = merge_block; | 719 | current_block = merge_block; |
| 720 | |||
| 721 | auto& if_node{syntax_list[if_node_index]}; | ||
| 722 | if_node.type = IR::AbstractSyntaxNode::Type::If; | ||
| 723 | if_node.if_node.cond = cond; | ||
| 724 | if_node.if_node.body = then_block; | ||
| 725 | if_node.if_node.merge = merge_block; | ||
| 726 | |||
| 727 | auto& endif_node{syntax_list.emplace_back()}; | ||
| 728 | endif_node.type = IR::AbstractSyntaxNode::Type::EndIf; | ||
| 729 | endif_node.end_if.merge = merge_block; | ||
| 730 | |||
| 731 | auto& merge{syntax_list.emplace_back()}; | ||
| 732 | merge.type = IR::AbstractSyntaxNode::Type::Block; | ||
| 733 | merge.block = merge_block; | ||
| 723 | break; | 734 | break; |
| 724 | } | 735 | } |
| 725 | case StatementType::Loop: { | 736 | case StatementType::Loop: { |
| 726 | IR::Block* const loop_header_block{block_pool.Create(inst_pool)}; | 737 | IR::Block* const loop_header_block{block_pool.Create(inst_pool)}; |
| 727 | if (current_block) { | 738 | if (current_block) { |
| 728 | IR::IREmitter{*current_block}.Branch(loop_header_block); | 739 | current_block->AddBranch(loop_header_block); |
| 729 | } | 740 | } |
| 730 | block_list.push_back(loop_header_block); | 741 | auto& header_node{syntax_list.emplace_back()}; |
| 742 | header_node.type = IR::AbstractSyntaxNode::Type::Block; | ||
| 743 | header_node.block = loop_header_block; | ||
| 731 | 744 | ||
| 732 | IR::Block* const new_continue_block{block_pool.Create(inst_pool)}; | 745 | IR::Block* const continue_block{block_pool.Create(inst_pool)}; |
| 733 | IR::Block* const merge_block{MergeBlock(parent, stmt)}; | 746 | IR::Block* const merge_block{MergeBlock(parent, stmt)}; |
| 734 | 747 | ||
| 748 | const size_t loop_node_index{syntax_list.size()}; | ||
| 749 | syntax_list.emplace_back(); | ||
| 750 | |||
| 735 | // Visit children | 751 | // Visit children |
| 736 | const size_t first_block_index{block_list.size()}; | 752 | const size_t body_block_index{syntax_list.size()}; |
| 737 | Visit(stmt, new_continue_block, merge_block); | 753 | Visit(stmt, merge_block, continue_block); |
| 738 | 754 | ||
| 739 | // The continue block is located at the end of the loop | 755 | // The continue block is located at the end of the loop |
| 740 | block_list.push_back(new_continue_block); | 756 | IR::IREmitter ir{*continue_block}; |
| 757 | const IR::U1 cond{VisitExpr(ir, *stmt.cond)}; | ||
| 758 | ir.BranchConditionRef(cond); | ||
| 741 | 759 | ||
| 742 | // Implement loop header block | 760 | IR::Block* const body_block{syntax_list.at(body_block_index).block}; |
| 743 | IR::Block* const first_loop_block{block_list.at(first_block_index)}; | 761 | loop_header_block->AddBranch(body_block); |
| 744 | IR::IREmitter ir{*loop_header_block}; | ||
| 745 | ir.LoopMerge(merge_block, new_continue_block); | ||
| 746 | ir.Branch(first_loop_block); | ||
| 747 | 762 | ||
| 748 | // Implement continue block | 763 | continue_block->AddBranch(loop_header_block); |
| 749 | IR::IREmitter continue_ir{*new_continue_block}; | 764 | continue_block->AddBranch(merge_block); |
| 750 | const IR::U1 continue_cond{VisitExpr(continue_ir, *stmt.cond)}; | ||
| 751 | continue_ir.BranchConditional(continue_cond, ir.block, merge_block); | ||
| 752 | 765 | ||
| 753 | current_block = merge_block; | 766 | current_block = merge_block; |
| 767 | |||
| 768 | auto& loop{syntax_list[loop_node_index]}; | ||
| 769 | loop.type = IR::AbstractSyntaxNode::Type::Loop; | ||
| 770 | loop.loop.body = body_block; | ||
| 771 | loop.loop.continue_block = continue_block; | ||
| 772 | loop.loop.merge = merge_block; | ||
| 773 | |||
| 774 | auto& continue_block_node{syntax_list.emplace_back()}; | ||
| 775 | continue_block_node.type = IR::AbstractSyntaxNode::Type::Block; | ||
| 776 | continue_block_node.block = continue_block; | ||
| 777 | |||
| 778 | auto& repeat{syntax_list.emplace_back()}; | ||
| 779 | repeat.type = IR::AbstractSyntaxNode::Type::Repeat; | ||
| 780 | repeat.repeat.cond = cond; | ||
| 781 | repeat.repeat.loop_header = loop_header_block; | ||
| 782 | repeat.repeat.merge = merge_block; | ||
| 783 | |||
| 784 | auto& merge{syntax_list.emplace_back()}; | ||
| 785 | merge.type = IR::AbstractSyntaxNode::Type::Block; | ||
| 786 | merge.block = merge_block; | ||
| 754 | break; | 787 | break; |
| 755 | } | 788 | } |
| 756 | case StatementType::Break: { | 789 | case StatementType::Break: { |
| 757 | if (!current_block) { | 790 | ensure_block(); |
| 758 | current_block = block_pool.Create(inst_pool); | ||
| 759 | block_list.push_back(current_block); | ||
| 760 | } | ||
| 761 | IR::Block* const skip_block{MergeBlock(parent, stmt)}; | 791 | IR::Block* const skip_block{MergeBlock(parent, stmt)}; |
| 762 | 792 | ||
| 763 | IR::IREmitter ir{*current_block}; | 793 | IR::IREmitter ir{*current_block}; |
| 764 | ir.BranchConditional(VisitExpr(ir, *stmt.cond), break_block, skip_block); | 794 | const IR::U1 cond{VisitExpr(ir, *stmt.cond)}; |
| 765 | 795 | ir.BranchConditionRef(cond); | |
| 796 | current_block->AddBranch(break_block); | ||
| 797 | current_block->AddBranch(skip_block); | ||
| 766 | current_block = skip_block; | 798 | current_block = skip_block; |
| 799 | |||
| 800 | auto& break_node{syntax_list.emplace_back()}; | ||
| 801 | break_node.type = IR::AbstractSyntaxNode::Type::Break; | ||
| 802 | break_node.break_node.cond = cond; | ||
| 803 | break_node.break_node.merge = break_block; | ||
| 804 | break_node.break_node.skip = skip_block; | ||
| 805 | |||
| 806 | auto& merge{syntax_list.emplace_back()}; | ||
| 807 | merge.type = IR::AbstractSyntaxNode::Type::Block; | ||
| 808 | merge.block = skip_block; | ||
| 767 | break; | 809 | break; |
| 768 | } | 810 | } |
| 769 | case StatementType::Return: { | 811 | case StatementType::Return: { |
| 770 | if (!current_block) { | 812 | ensure_block(); |
| 771 | current_block = block_pool.Create(inst_pool); | 813 | IR::IREmitter{*current_block}.Epilogue(); |
| 772 | block_list.push_back(current_block); | ||
| 773 | } | ||
| 774 | IR::IREmitter ir{*current_block}; | ||
| 775 | ir.Epilogue(); | ||
| 776 | ir.Return(); | ||
| 777 | current_block = nullptr; | 814 | current_block = nullptr; |
| 815 | syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return; | ||
| 778 | break; | 816 | break; |
| 779 | } | 817 | } |
| 780 | case StatementType::Kill: { | 818 | case StatementType::Kill: { |
| 781 | if (!current_block) { | 819 | ensure_block(); |
| 782 | current_block = block_pool.Create(inst_pool); | ||
| 783 | block_list.push_back(current_block); | ||
| 784 | } | ||
| 785 | IR::Block* demote_block{MergeBlock(parent, stmt)}; | 820 | IR::Block* demote_block{MergeBlock(parent, stmt)}; |
| 786 | IR::IREmitter{*current_block}.DemoteToHelperInvocation(demote_block); | 821 | IR::IREmitter{*current_block}.DemoteToHelperInvocation(); |
| 822 | current_block->AddBranch(demote_block); | ||
| 787 | current_block = demote_block; | 823 | current_block = demote_block; |
| 824 | |||
| 825 | auto& merge{syntax_list.emplace_back()}; | ||
| 826 | merge.type = IR::AbstractSyntaxNode::Type::Block; | ||
| 827 | merge.block = demote_block; | ||
| 788 | break; | 828 | break; |
| 789 | } | 829 | } |
| 790 | case StatementType::Unreachable: { | 830 | case StatementType::Unreachable: { |
| 791 | if (!current_block) { | 831 | ensure_block(); |
| 792 | current_block = block_pool.Create(inst_pool); | ||
| 793 | block_list.push_back(current_block); | ||
| 794 | } | ||
| 795 | IR::IREmitter{*current_block}.Unreachable(); | ||
| 796 | current_block = nullptr; | 832 | current_block = nullptr; |
| 833 | syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Unreachable; | ||
| 797 | break; | 834 | break; |
| 798 | } | 835 | } |
| 799 | default: | 836 | default: |
| @@ -801,42 +838,42 @@ private: | |||
| 801 | } | 838 | } |
| 802 | } | 839 | } |
| 803 | if (current_block) { | 840 | if (current_block) { |
| 804 | IR::IREmitter ir{*current_block}; | 841 | if (fallthrough_block) { |
| 805 | if (continue_block) { | 842 | current_block->AddBranch(fallthrough_block); |
| 806 | ir.Branch(continue_block); | ||
| 807 | } else { | 843 | } else { |
| 808 | ir.Unreachable(); | 844 | syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Unreachable; |
| 809 | } | 845 | } |
| 810 | } | 846 | } |
| 811 | } | 847 | } |
| 812 | 848 | ||
| 813 | IR::Block* MergeBlock(Statement& parent, Statement& stmt) { | 849 | IR::Block* MergeBlock(Statement& parent, Statement& stmt) { |
| 814 | if (IR::Block* const block{TryFindForwardBlock(stmt)}) { | 850 | Statement* merge_stmt{TryFindForwardBlock(stmt)}; |
| 815 | return block; | 851 | if (!merge_stmt) { |
| 852 | // Create a merge block we can visit later | ||
| 853 | merge_stmt = stmt_pool.Create(&dummy_flow_block, &parent); | ||
| 854 | parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt); | ||
| 816 | } | 855 | } |
| 817 | // Create a merge block we can visit later | 856 | return block_pool.Create(inst_pool); |
| 818 | IR::Block* const block{block_pool.Create(inst_pool)}; | ||
| 819 | Statement* const merge_stmt{stmt_pool.Create(block, &parent)}; | ||
| 820 | parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt); | ||
| 821 | return block; | ||
| 822 | } | 857 | } |
| 823 | 858 | ||
| 824 | ObjectPool<Statement>& stmt_pool; | 859 | ObjectPool<Statement>& stmt_pool; |
| 825 | ObjectPool<IR::Inst>& inst_pool; | 860 | ObjectPool<IR::Inst>& inst_pool; |
| 826 | ObjectPool<IR::Block>& block_pool; | 861 | ObjectPool<IR::Block>& block_pool; |
| 827 | Environment& env; | 862 | Environment& env; |
| 828 | IR::BlockList& block_list; | 863 | IR::AbstractSyntaxList& syntax_list; |
| 864 | // TODO: Make this constexpr when std::vector is constexpr | ||
| 865 | const Flow::Block dummy_flow_block; | ||
| 829 | }; | 866 | }; |
| 830 | } // Anonymous namespace | 867 | } // Anonymous namespace |
| 831 | 868 | ||
| 832 | IR::BlockList VisitAST(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, | 869 | IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, |
| 833 | Environment& env, Flow::CFG& cfg) { | 870 | Environment& env, Flow::CFG& cfg) { |
| 834 | ObjectPool<Statement> stmt_pool{64}; | 871 | ObjectPool<Statement> stmt_pool{64}; |
| 835 | GotoPass goto_pass{cfg, inst_pool, block_pool, stmt_pool}; | 872 | GotoPass goto_pass{cfg, inst_pool, block_pool, stmt_pool}; |
| 836 | Statement& root{goto_pass.RootStatement()}; | 873 | Statement& root{goto_pass.RootStatement()}; |
| 837 | IR::BlockList block_list; | 874 | IR::AbstractSyntaxList syntax_list; |
| 838 | TranslatePass{inst_pool, block_pool, stmt_pool, env, root, block_list}; | 875 | TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list}; |
| 839 | return block_list; | 876 | return syntax_list; |
| 840 | } | 877 | } |
| 841 | 878 | ||
| 842 | } // namespace Shader::Maxwell | 879 | } // namespace Shader::Maxwell |
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.h b/src/shader_recompiler/frontend/maxwell/structured_control_flow.h index a6be12ba2..88b083649 100644 --- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.h +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.h | |||
| @@ -4,12 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <functional> | ||
| 8 | #include <span> | ||
| 9 | |||
| 10 | #include <boost/intrusive/list.hpp> | ||
| 11 | |||
| 12 | #include "shader_recompiler/environment.h" | 7 | #include "shader_recompiler/environment.h" |
| 8 | #include "shader_recompiler/frontend/ir/abstract_syntax_list.h" | ||
| 13 | #include "shader_recompiler/frontend/ir/basic_block.h" | 9 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 14 | #include "shader_recompiler/frontend/ir/value.h" | 10 | #include "shader_recompiler/frontend/ir/value.h" |
| 15 | #include "shader_recompiler/frontend/maxwell/control_flow.h" | 11 | #include "shader_recompiler/frontend/maxwell/control_flow.h" |
| @@ -17,8 +13,8 @@ | |||
| 17 | 13 | ||
| 18 | namespace Shader::Maxwell { | 14 | namespace Shader::Maxwell { |
| 19 | 15 | ||
| 20 | [[nodiscard]] IR::BlockList VisitAST(ObjectPool<IR::Inst>& inst_pool, | 16 | [[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, |
| 21 | ObjectPool<IR::Block>& block_pool, Environment& env, | 17 | ObjectPool<IR::Block>& block_pool, Environment& env, |
| 22 | Flow::CFG& cfg); | 18 | Flow::CFG& cfg); |
| 23 | 19 | ||
| 24 | } // namespace Shader::Maxwell | 20 | } // namespace Shader::Maxwell |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/translate.cpp b/src/shader_recompiler/frontend/maxwell/translate/translate.cpp index f1230f58f..0f4e7a251 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/translate.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/translate.cpp | |||
| @@ -23,13 +23,12 @@ static void Invoke(TranslatorVisitor& visitor, Location pc, u64 insn) { | |||
| 23 | } | 23 | } |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | void Translate(Environment& env, IR::Block* block) { | 26 | void Translate(Environment& env, IR::Block* block, u32 location_begin, u32 location_end) { |
| 27 | if (block->IsVirtual()) { | 27 | if (location_begin == location_end) { |
| 28 | return; | 28 | return; |
| 29 | } | 29 | } |
| 30 | TranslatorVisitor visitor{env, *block}; | 30 | TranslatorVisitor visitor{env, *block}; |
| 31 | const Location pc_end{block->LocationEnd()}; | 31 | for (Location pc = location_begin; pc != location_end; ++pc) { |
| 32 | for (Location pc = block->LocationBegin(); pc != pc_end; ++pc) { | ||
| 33 | const u64 insn{env.ReadInstruction(pc.Offset())}; | 32 | const u64 insn{env.ReadInstruction(pc.Offset())}; |
| 34 | const Opcode opcode{Decode(insn)}; | 33 | const Opcode opcode{Decode(insn)}; |
| 35 | switch (opcode) { | 34 | switch (opcode) { |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/translate.h b/src/shader_recompiler/frontend/maxwell/translate/translate.h index e1aa2e0f4..a3edd2e46 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/translate.h +++ b/src/shader_recompiler/frontend/maxwell/translate/translate.h | |||
| @@ -9,6 +9,6 @@ | |||
| 9 | 9 | ||
| 10 | namespace Shader::Maxwell { | 10 | namespace Shader::Maxwell { |
| 11 | 11 | ||
| 12 | void Translate(Environment& env, IR::Block* block); | 12 | void Translate(Environment& env, IR::Block* block, u32 location_begin, u32 location_end); |
| 13 | 13 | ||
| 14 | } // namespace Shader::Maxwell | 14 | } // namespace Shader::Maxwell |
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp index b1c45d13a..66f1391db 100644 --- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp | |||
| @@ -353,24 +353,6 @@ IR::Value EvalImmediates(const IR::Inst& inst, Func&& func, std::index_sequence< | |||
| 353 | return IR::Value{func(Arg<typename Traits::template ArgType<I>>(inst.Arg(I))...)}; | 353 | return IR::Value{func(Arg<typename Traits::template ArgType<I>>(inst.Arg(I))...)}; |
| 354 | } | 354 | } |
| 355 | 355 | ||
| 356 | void FoldBranchConditional(IR::Inst& inst) { | ||
| 357 | const IR::U1 cond{inst.Arg(0)}; | ||
| 358 | if (cond.IsImmediate()) { | ||
| 359 | // TODO: Convert to Branch | ||
| 360 | return; | ||
| 361 | } | ||
| 362 | const IR::Inst* cond_inst{cond.InstRecursive()}; | ||
| 363 | if (cond_inst->GetOpcode() == IR::Opcode::LogicalNot) { | ||
| 364 | const IR::Value true_label{inst.Arg(1)}; | ||
| 365 | const IR::Value false_label{inst.Arg(2)}; | ||
| 366 | // Remove negation on the conditional (take the parameter out of LogicalNot) and swap | ||
| 367 | // the branches | ||
| 368 | inst.SetArg(0, cond_inst->Arg(0)); | ||
| 369 | inst.SetArg(1, false_label); | ||
| 370 | inst.SetArg(2, true_label); | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | std::optional<IR::Value> FoldCompositeExtractImpl(IR::Value inst_value, IR::Opcode insert, | 356 | std::optional<IR::Value> FoldCompositeExtractImpl(IR::Value inst_value, IR::Opcode insert, |
| 375 | IR::Opcode construct, u32 first_index) { | 357 | IR::Opcode construct, u32 first_index) { |
| 376 | IR::Inst* const inst{inst_value.InstRecursive()}; | 358 | IR::Inst* const inst{inst_value.InstRecursive()}; |
| @@ -581,8 +563,6 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { | |||
| 581 | return (base & ~(~(~0u << bits) << offset)) | (insert << offset); | 563 | return (base & ~(~(~0u << bits) << offset)) | (insert << offset); |
| 582 | }); | 564 | }); |
| 583 | return; | 565 | return; |
| 584 | case IR::Opcode::BranchConditional: | ||
| 585 | return FoldBranchConditional(inst); | ||
| 586 | case IR::Opcode::CompositeExtractF32x2: | 566 | case IR::Opcode::CompositeExtractF32x2: |
| 587 | return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF32x2, | 567 | return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF32x2, |
| 588 | IR::Opcode::CompositeInsertF32x2); | 568 | IR::Opcode::CompositeInsertF32x2); |
diff --git a/src/shader_recompiler/ir_opt/dual_vertex_pass.cpp b/src/shader_recompiler/ir_opt/dual_vertex_pass.cpp index f2d7db0e6..b0a9f5258 100644 --- a/src/shader_recompiler/ir_opt/dual_vertex_pass.cpp +++ b/src/shader_recompiler/ir_opt/dual_vertex_pass.cpp | |||
| @@ -13,60 +13,16 @@ | |||
| 13 | 13 | ||
| 14 | namespace Shader::Optimization { | 14 | namespace Shader::Optimization { |
| 15 | 15 | ||
| 16 | void VertexATransformPass(IR::Program& program) { | 16 | void VertexATransformPass(IR::Program&) { |
| 17 | bool replaced_join{}; | 17 | throw NotImplementedException("VertexA pass"); |
| 18 | bool eliminated_epilogue{}; | ||
| 19 | for (IR::Block* const block : program.post_order_blocks) { | ||
| 20 | for (IR::Inst& inst : block->Instructions()) { | ||
| 21 | switch (inst.GetOpcode()) { | ||
| 22 | case IR::Opcode::Return: | ||
| 23 | inst.ReplaceOpcode(IR::Opcode::Join); | ||
| 24 | replaced_join = true; | ||
| 25 | break; | ||
| 26 | case IR::Opcode::Epilogue: | ||
| 27 | inst.Invalidate(); | ||
| 28 | eliminated_epilogue = true; | ||
| 29 | break; | ||
| 30 | default: | ||
| 31 | break; | ||
| 32 | } | ||
| 33 | if (replaced_join && eliminated_epilogue) { | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | } | 18 | } |
| 39 | 19 | ||
| 40 | void VertexBTransformPass(IR::Program& program) { | 20 | void VertexBTransformPass(IR::Program&) { |
| 41 | for (IR::Block* const block : program.blocks) { | 21 | throw NotImplementedException("VertexA pass"); |
| 42 | for (IR::Inst& inst : block->Instructions()) { | ||
| 43 | if (inst.GetOpcode() == IR::Opcode::Prologue) { | ||
| 44 | return inst.Invalidate(); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | 22 | } |
| 49 | 23 | ||
| 50 | void DualVertexJoinPass(IR::Program& program) { | 24 | void DualVertexJoinPass(IR::Program&) { |
| 51 | const auto& blocks = program.blocks; | 25 | throw NotImplementedException("VertexA pass"); |
| 52 | const s64 sub_size = static_cast<s64>(blocks.size()) - 1; | ||
| 53 | if (sub_size < 1) { | ||
| 54 | throw LogicError("Dual Vertex Join pass failed, expected atleast 2 blocks"); | ||
| 55 | } | ||
| 56 | for (s64 index = 0; index < sub_size; ++index) { | ||
| 57 | IR::Block* const current_block{blocks[index]}; | ||
| 58 | IR::Block* const next_block{blocks[index + 1]}; | ||
| 59 | for (IR::Inst& inst : current_block->Instructions()) { | ||
| 60 | if (inst.GetOpcode() == IR::Opcode::Join) { | ||
| 61 | IR::IREmitter ir{*current_block, IR::Block::InstructionList::s_iterator_to(inst)}; | ||
| 62 | ir.Branch(next_block); | ||
| 63 | inst.Invalidate(); | ||
| 64 | // Only 1 join should exist | ||
| 65 | return; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | throw LogicError("Dual Vertex Join pass failed, no join present"); | ||
| 70 | } | 26 | } |
| 71 | 27 | ||
| 72 | } // namespace Shader::Optimization | 28 | } // namespace Shader::Optimization |
diff --git a/src/shader_recompiler/ir_opt/identity_removal_pass.cpp b/src/shader_recompiler/ir_opt/identity_removal_pass.cpp index 6afbe24f7..e9b55f835 100644 --- a/src/shader_recompiler/ir_opt/identity_removal_pass.cpp +++ b/src/shader_recompiler/ir_opt/identity_removal_pass.cpp | |||
| @@ -12,7 +12,6 @@ namespace Shader::Optimization { | |||
| 12 | 12 | ||
| 13 | void IdentityRemovalPass(IR::Program& program) { | 13 | void IdentityRemovalPass(IR::Program& program) { |
| 14 | std::vector<IR::Inst*> to_invalidate; | 14 | std::vector<IR::Inst*> to_invalidate; |
| 15 | |||
| 16 | for (IR::Block* const block : program.blocks) { | 15 | for (IR::Block* const block : program.blocks) { |
| 17 | for (auto inst = block->begin(); inst != block->end();) { | 16 | for (auto inst = block->begin(); inst != block->end();) { |
| 18 | const size_t num_args{inst->NumArgs()}; | 17 | const size_t num_args{inst->NumArgs()}; |
diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp index a8064a5d0..26eb3a3ab 100644 --- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | |||
| @@ -202,7 +202,7 @@ public: | |||
| 202 | 202 | ||
| 203 | incomplete_phis[block].insert_or_assign(variable, phi); | 203 | incomplete_phis[block].insert_or_assign(variable, phi); |
| 204 | stack.back().result = IR::Value{&*phi}; | 204 | stack.back().result = IR::Value{&*phi}; |
| 205 | } else if (const std::span imm_preds{block->ImmediatePredecessors()}; | 205 | } else if (const std::span imm_preds = block->ImmPredecessors(); |
| 206 | imm_preds.size() == 1) { | 206 | imm_preds.size() == 1) { |
| 207 | // Optimize the common case of one predecessor: no phi needed | 207 | // Optimize the common case of one predecessor: no phi needed |
| 208 | stack.back().pc = Status::SetValue; | 208 | stack.back().pc = Status::SetValue; |
| @@ -257,7 +257,7 @@ public: | |||
| 257 | private: | 257 | private: |
| 258 | template <typename Type> | 258 | template <typename Type> |
| 259 | IR::Value AddPhiOperands(Type variable, IR::Inst& phi, IR::Block* block) { | 259 | IR::Value AddPhiOperands(Type variable, IR::Inst& phi, IR::Block* block) { |
| 260 | for (IR::Block* const imm_pred : block->ImmediatePredecessors()) { | 260 | for (IR::Block* const imm_pred : block->ImmPredecessors()) { |
| 261 | phi.AddPhiOperand(imm_pred, ReadVariable(variable, imm_pred)); | 261 | phi.AddPhiOperand(imm_pred, ReadVariable(variable, imm_pred)); |
| 262 | } | 262 | } |
| 263 | return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable)); | 263 | return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable)); |