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 'src/shader_recompiler')
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)); |