From 85cce78583bc2232428a8fb39e43182877c8d5ad Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 17 Feb 2021 00:59:28 -0300
Subject: shader: Primitive Vulkan integration

---
 src/shader_recompiler/CMakeLists.txt               |  13 +-
 .../backend/spirv/emit_context.cpp                 |   2 +
 src/shader_recompiler/backend/spirv/emit_spirv.cpp | 117 +++---
 src/shader_recompiler/backend/spirv/emit_spirv.h   | 419 ++++++++++-----------
 .../spirv/emit_spirv_bitwise_conversion.cpp        |  24 +-
 .../backend/spirv/emit_spirv_composite.cpp         |  48 +--
 .../backend/spirv/emit_spirv_context_get_set.cpp   |  42 +--
 .../backend/spirv/emit_spirv_control_flow.cpp      |  10 +-
 .../backend/spirv/emit_spirv_floating_point.cpp    |  92 ++---
 .../backend/spirv/emit_spirv_integer.cpp           |  60 +--
 .../backend/spirv/emit_spirv_logical.cpp           |  40 +-
 .../backend/spirv/emit_spirv_memory.cpp            |  56 +--
 .../backend/spirv/emit_spirv_select.cpp            |   8 +-
 .../backend/spirv/emit_spirv_undefined.cpp         |  10 +-
 src/shader_recompiler/environment.h                |   6 +-
 src/shader_recompiler/file_environment.cpp         |   6 +-
 src/shader_recompiler/file_environment.h           |   4 +-
 src/shader_recompiler/frontend/ir/basic_block.cpp  |   2 +
 src/shader_recompiler/frontend/ir/post_order.cpp   |   2 +-
 src/shader_recompiler/frontend/maxwell/program.cpp |   2 +-
 .../frontend/maxwell/translate/impl/impl.cpp       |   8 +
 .../frontend/maxwell/translate/impl/impl.h         |   1 +
 .../maxwell/translate/impl/move_register.cpp       |  35 +-
 .../maxwell/translate/impl/not_implemented.cpp     |   4 -
 src/shader_recompiler/main.cpp                     |   2 +-
 src/shader_recompiler/profile.h                    |  13 +
 src/shader_recompiler/recompiler.cpp               |  27 ++
 src/shader_recompiler/recompiler.h                 |  18 +
 28 files changed, 573 insertions(+), 498 deletions(-)
 create mode 100644 src/shader_recompiler/profile.h
 create mode 100644 src/shader_recompiler/recompiler.cpp
 create mode 100644 src/shader_recompiler/recompiler.h

(limited to 'src/shader_recompiler')

diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 84be94a8d..b56bdd3d9 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -1,4 +1,4 @@
-add_executable(shader_recompiler
+add_library(shader_recompiler STATIC
     backend/spirv/emit_context.cpp
     backend/spirv/emit_context.h
     backend/spirv/emit_spirv.cpp
@@ -85,13 +85,19 @@ add_executable(shader_recompiler
     ir_opt/passes.h
     ir_opt/ssa_rewrite_pass.cpp
     ir_opt/verification_pass.cpp
-    main.cpp
     object_pool.h
+    profile.h
+    recompiler.cpp
+    recompiler.h
     shader_info.h
 )
 
-target_include_directories(video_core PRIVATE sirit)
+target_include_directories(shader_recompiler PRIVATE sirit)
 target_link_libraries(shader_recompiler PRIVATE fmt::fmt sirit)
+target_link_libraries(shader_recompiler INTERFACE fmt::fmt sirit)
+
+add_executable(shader_util main.cpp)
+target_link_libraries(shader_util PRIVATE shader_recompiler)
 
 if (MSVC)
     target_compile_options(shader_recompiler PRIVATE
@@ -121,3 +127,4 @@ else()
 endif()
 
 create_target_directory_groups(shader_recompiler)
+create_target_directory_groups(shader_util)
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 1c985aff8..770067d98 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -115,6 +115,7 @@ void EmitContext::DefineConstantBuffers(const Info& info) {
     for (const Info::ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) {
         const Id id{AddGlobalVariable(uniform_type, spv::StorageClass::Uniform)};
         Decorate(id, spv::Decoration::Binding, binding);
+        Decorate(id, spv::Decoration::DescriptorSet, 0U);
         Name(id, fmt::format("c{}", desc.index));
         std::fill_n(cbufs.data() + desc.index, desc.count, id);
         binding += desc.count;
@@ -143,6 +144,7 @@ void EmitContext::DefineStorageBuffers(const Info& info) {
     for (const Info::StorageBufferDescriptor& desc : info.storage_buffers_descriptors) {
         const Id id{AddGlobalVariable(storage_type, spv::StorageClass::StorageBuffer)};
         Decorate(id, spv::Decoration::Binding, binding);
+        Decorate(id, spv::Decoration::DescriptorSet, 0U);
         Name(id, fmt::format("ssbo{}", binding));
         std::fill_n(ssbos.data() + binding, desc.count, id);
         binding += desc.count;
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 55018332e..d59718435 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -2,8 +2,11 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
-#include <numeric>
+#include <span>
+#include <tuple>
 #include <type_traits>
+#include <utility>
+#include <vector>
 
 #include "shader_recompiler/backend/spirv/emit_spirv.h"
 #include "shader_recompiler/frontend/ir/basic_block.h"
@@ -14,10 +17,10 @@
 namespace Shader::Backend::SPIRV {
 namespace {
 template <class Func>
-struct FuncTraits : FuncTraits<decltype(&Func::operator())> {};
+struct FuncTraits : FuncTraits<Func> {};
 
-template <class ClassType, class ReturnType_, class... Args>
-struct FuncTraits<ReturnType_ (ClassType::*)(Args...)> {
+template <class ReturnType_, class... Args>
+struct FuncTraits<ReturnType_ (*)(Args...)> {
     using ReturnType = ReturnType_;
 
     static constexpr size_t NUM_ARGS = sizeof...(Args);
@@ -26,15 +29,15 @@ struct FuncTraits<ReturnType_ (ClassType::*)(Args...)> {
     using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
 };
 
-template <auto method, typename... Args>
-void SetDefinition(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst, Args... args) {
+template <auto func, typename... Args>
+void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
     const Id forward_id{inst->Definition<Id>()};
     const bool has_forward_id{Sirit::ValidId(forward_id)};
     Id current_id{};
     if (has_forward_id) {
         current_id = ctx.ExchangeCurrentId(forward_id);
     }
-    const Id new_id{(emit.*method)(ctx, std::forward<Args>(args)...)};
+    const Id new_id{func(ctx, std::forward<Args>(args)...)};
     if (has_forward_id) {
         ctx.ExchangeCurrentId(current_id);
     } else {
@@ -55,42 +58,62 @@ ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
     }
 }
 
-template <auto method, bool is_first_arg_inst, size_t... I>
-void Invoke(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
-    using Traits = FuncTraits<decltype(method)>;
+template <auto func, bool is_first_arg_inst, size_t... I>
+void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
+    using Traits = FuncTraits<decltype(func)>;
     if constexpr (std::is_same_v<Traits::ReturnType, Id>) {
         if constexpr (is_first_arg_inst) {
-            SetDefinition<method>(emit, ctx, inst, inst,
-                                  Arg<Traits::ArgType<I + 2>>(ctx, inst->Arg(I))...);
+            SetDefinition<func>(ctx, inst, inst, Arg<Traits::ArgType<I + 2>>(ctx, inst->Arg(I))...);
         } else {
-            SetDefinition<method>(emit, ctx, inst,
-                                  Arg<Traits::ArgType<I + 1>>(ctx, inst->Arg(I))...);
+            SetDefinition<func>(ctx, inst, Arg<Traits::ArgType<I + 1>>(ctx, inst->Arg(I))...);
         }
     } else {
         if constexpr (is_first_arg_inst) {
-            (emit.*method)(ctx, inst, Arg<Traits::ArgType<I + 2>>(ctx, inst->Arg(I))...);
+            func(ctx, inst, Arg<Traits::ArgType<I + 2>>(ctx, inst->Arg(I))...);
         } else {
-            (emit.*method)(ctx, Arg<Traits::ArgType<I + 1>>(ctx, inst->Arg(I))...);
+            func(ctx, Arg<Traits::ArgType<I + 1>>(ctx, inst->Arg(I))...);
         }
     }
 }
 
-template <auto method>
-void Invoke(EmitSPIRV& emit, EmitContext& ctx, IR::Inst* inst) {
-    using Traits = FuncTraits<decltype(method)>;
+template <auto func>
+void Invoke(EmitContext& ctx, IR::Inst* inst) {
+    using Traits = FuncTraits<decltype(func)>;
     static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
     if constexpr (Traits::NUM_ARGS == 1) {
-        Invoke<method, false>(emit, ctx, inst, std::make_index_sequence<0>{});
+        Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
     } else {
         using FirstArgType = typename Traits::template ArgType<1>;
         static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst*>;
         using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
-        Invoke<method, is_first_arg_inst>(emit, ctx, inst, Indices{});
+        Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
+    }
+}
+
+void EmitInst(EmitContext& ctx, IR::Inst* inst) {
+    switch (inst->Opcode()) {
+#define OPCODE(name, result_type, ...)                                                             \
+    case IR::Opcode::name:                                                                         \
+        return Invoke<&Emit##name>(ctx, inst);
+#include "shader_recompiler/frontend/ir/opcodes.inc"
+#undef OPCODE
+    }
+    throw LogicError("Invalid opcode {}", inst->Opcode());
+}
+
+Id TypeId(const EmitContext& ctx, IR::Type type) {
+    switch (type) {
+    case IR::Type::U1:
+        return ctx.U1;
+    case IR::Type::U32:
+        return ctx.U32[1];
+    default:
+        throw NotImplementedException("Phi node type {}", type);
     }
 }
 } // Anonymous namespace
 
-EmitSPIRV::EmitSPIRV(IR::Program& program) {
+std::vector<u32> EmitSPIRV(Environment& env, IR::Program& program) {
     EmitContext ctx{program};
     const Id void_function{ctx.TypeFunction(ctx.void_id)};
     // FIXME: Forward declare functions (needs sirit support)
@@ -112,43 +135,17 @@ EmitSPIRV::EmitSPIRV(IR::Program& program) {
     if (program.info.uses_local_invocation_id) {
         interfaces.push_back(ctx.local_invocation_id);
     }
-
     const std::span interfaces_span(interfaces.data(), interfaces.size());
-    ctx.AddEntryPoint(spv::ExecutionModel::Fragment, func, "main", interfaces_span);
-    ctx.AddExecutionMode(func, spv::ExecutionMode::OriginUpperLeft);
-
-    std::vector<u32> result{ctx.Assemble()};
-    std::FILE* file{std::fopen("D:\\shader.spv", "wb")};
-    std::fwrite(result.data(), sizeof(u32), result.size(), file);
-    std::fclose(file);
-    std::system("spirv-dis D:\\shader.spv") == 0 &&
-        std::system("spirv-val --uniform-buffer-standard-layout D:\\shader.spv") == 0 &&
-        std::system("spirv-cross -V D:\\shader.spv") == 0;
-}
+    ctx.AddEntryPoint(spv::ExecutionModel::GLCompute, func, "main", interfaces_span);
 
-void EmitSPIRV::EmitInst(EmitContext& ctx, IR::Inst* inst) {
-    switch (inst->Opcode()) {
-#define OPCODE(name, result_type, ...)                                                             \
-    case IR::Opcode::name:                                                                         \
-        return Invoke<&EmitSPIRV::Emit##name>(*this, ctx, inst);
-#include "shader_recompiler/frontend/ir/opcodes.inc"
-#undef OPCODE
-    }
-    throw LogicError("Invalid opcode {}", inst->Opcode());
-}
+    const std::array<u32, 3> workgroup_size{env.WorkgroupSize()};
+    ctx.AddExecutionMode(func, spv::ExecutionMode::LocalSize, workgroup_size[0], workgroup_size[1],
+                         workgroup_size[2]);
 
-static Id TypeId(const EmitContext& ctx, IR::Type type) {
-    switch (type) {
-    case IR::Type::U1:
-        return ctx.U1;
-    case IR::Type::U32:
-        return ctx.U32[1];
-    default:
-        throw NotImplementedException("Phi node type {}", type);
-    }
+    return ctx.Assemble();
 }
 
-Id EmitSPIRV::EmitPhi(EmitContext& ctx, IR::Inst* inst) {
+Id EmitPhi(EmitContext& ctx, IR::Inst* inst) {
     const size_t num_args{inst->NumArgs()};
     boost::container::small_vector<Id, 32> operands;
     operands.reserve(num_args * 2);
@@ -178,25 +175,25 @@ Id EmitSPIRV::EmitPhi(EmitContext& ctx, IR::Inst* inst) {
     return ctx.OpPhi(result_type, std::span(operands.data(), operands.size()));
 }
 
-void EmitSPIRV::EmitVoid(EmitContext&) {}
+void EmitVoid(EmitContext&) {}
 
-Id EmitSPIRV::EmitIdentity(EmitContext& ctx, const IR::Value& value) {
+Id EmitIdentity(EmitContext& ctx, const IR::Value& value) {
     return ctx.Def(value);
 }
 
-void EmitSPIRV::EmitGetZeroFromOp(EmitContext&) {
+void EmitGetZeroFromOp(EmitContext&) {
     throw LogicError("Unreachable instruction");
 }
 
-void EmitSPIRV::EmitGetSignFromOp(EmitContext&) {
+void EmitGetSignFromOp(EmitContext&) {
     throw LogicError("Unreachable instruction");
 }
 
-void EmitSPIRV::EmitGetCarryFromOp(EmitContext&) {
+void EmitGetCarryFromOp(EmitContext&) {
     throw LogicError("Unreachable instruction");
 }
 
-void EmitSPIRV::EmitGetOverflowFromOp(EmitContext&) {
+void EmitGetOverflowFromOp(EmitContext&) {
     throw LogicError("Unreachable instruction");
 }
 
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 8bde82613..5813f51ff 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -8,223 +8,218 @@
 
 #include "common/common_types.h"
 #include "shader_recompiler/backend/spirv/emit_context.h"
+#include "shader_recompiler/environment.h"
 #include "shader_recompiler/frontend/ir/microinstruction.h"
 #include "shader_recompiler/frontend/ir/program.h"
 
 namespace Shader::Backend::SPIRV {
 
-class EmitSPIRV {
-public:
-    explicit EmitSPIRV(IR::Program& program);
+[[nodiscard]] std::vector<u32> EmitSPIRV(Environment& env, IR::Program& program);
 
-private:
-    void EmitInst(EmitContext& ctx, IR::Inst* inst);
-
-    // Microinstruction emitters
-    Id EmitPhi(EmitContext& ctx, IR::Inst* inst);
-    void EmitVoid(EmitContext& ctx);
-    Id EmitIdentity(EmitContext& ctx, const IR::Value& value);
-    void EmitBranch(EmitContext& ctx, IR::Block* label);
-    void EmitBranchConditional(EmitContext& ctx, Id condition, IR::Block* true_label,
-                               IR::Block* false_label);
-    void EmitLoopMerge(EmitContext& ctx, IR::Block* merge_label, IR::Block* continue_label);
-    void EmitSelectionMerge(EmitContext& ctx, IR::Block* merge_label);
-    void EmitReturn(EmitContext& ctx);
-    void EmitGetRegister(EmitContext& ctx);
-    void EmitSetRegister(EmitContext& ctx);
-    void EmitGetPred(EmitContext& ctx);
-    void EmitSetPred(EmitContext& ctx);
-    void EmitSetGotoVariable(EmitContext& ctx);
-    void EmitGetGotoVariable(EmitContext& ctx);
-    Id EmitGetCbuf(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
-    void EmitGetAttribute(EmitContext& ctx);
-    void EmitSetAttribute(EmitContext& ctx);
-    void EmitGetAttributeIndexed(EmitContext& ctx);
-    void EmitSetAttributeIndexed(EmitContext& ctx);
-    void EmitGetZFlag(EmitContext& ctx);
-    void EmitGetSFlag(EmitContext& ctx);
-    void EmitGetCFlag(EmitContext& ctx);
-    void EmitGetOFlag(EmitContext& ctx);
-    void EmitSetZFlag(EmitContext& ctx);
-    void EmitSetSFlag(EmitContext& ctx);
-    void EmitSetCFlag(EmitContext& ctx);
-    void EmitSetOFlag(EmitContext& ctx);
-    Id EmitWorkgroupId(EmitContext& ctx);
-    Id EmitLocalInvocationId(EmitContext& ctx);
-    Id EmitUndefU1(EmitContext& ctx);
-    Id EmitUndefU8(EmitContext& ctx);
-    Id EmitUndefU16(EmitContext& ctx);
-    Id EmitUndefU32(EmitContext& ctx);
-    Id EmitUndefU64(EmitContext& ctx);
-    void EmitLoadGlobalU8(EmitContext& ctx);
-    void EmitLoadGlobalS8(EmitContext& ctx);
-    void EmitLoadGlobalU16(EmitContext& ctx);
-    void EmitLoadGlobalS16(EmitContext& ctx);
-    void EmitLoadGlobal32(EmitContext& ctx);
-    void EmitLoadGlobal64(EmitContext& ctx);
-    void EmitLoadGlobal128(EmitContext& ctx);
-    void EmitWriteGlobalU8(EmitContext& ctx);
-    void EmitWriteGlobalS8(EmitContext& ctx);
-    void EmitWriteGlobalU16(EmitContext& ctx);
-    void EmitWriteGlobalS16(EmitContext& ctx);
-    void EmitWriteGlobal32(EmitContext& ctx);
-    void EmitWriteGlobal64(EmitContext& ctx);
-    void EmitWriteGlobal128(EmitContext& ctx);
-    void EmitLoadStorageU8(EmitContext& ctx);
-    void EmitLoadStorageS8(EmitContext& ctx);
-    void EmitLoadStorageU16(EmitContext& ctx);
-    void EmitLoadStorageS16(EmitContext& ctx);
-    Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
-    void EmitLoadStorage64(EmitContext& ctx);
-    void EmitLoadStorage128(EmitContext& ctx);
-    void EmitWriteStorageU8(EmitContext& ctx);
-    void EmitWriteStorageS8(EmitContext& ctx);
-    void EmitWriteStorageU16(EmitContext& ctx);
-    void EmitWriteStorageS16(EmitContext& ctx);
-    void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
-                            Id value);
-    void EmitWriteStorage64(EmitContext& ctx);
-    void EmitWriteStorage128(EmitContext& ctx);
-    void EmitCompositeConstructU32x2(EmitContext& ctx);
-    void EmitCompositeConstructU32x3(EmitContext& ctx);
-    void EmitCompositeConstructU32x4(EmitContext& ctx);
-    void EmitCompositeExtractU32x2(EmitContext& ctx);
-    Id EmitCompositeExtractU32x3(EmitContext& ctx, Id vector, u32 index);
-    void EmitCompositeExtractU32x4(EmitContext& ctx);
-    void EmitCompositeConstructF16x2(EmitContext& ctx);
-    void EmitCompositeConstructF16x3(EmitContext& ctx);
-    void EmitCompositeConstructF16x4(EmitContext& ctx);
-    void EmitCompositeExtractF16x2(EmitContext& ctx);
-    void EmitCompositeExtractF16x3(EmitContext& ctx);
-    void EmitCompositeExtractF16x4(EmitContext& ctx);
-    void EmitCompositeConstructF32x2(EmitContext& ctx);
-    void EmitCompositeConstructF32x3(EmitContext& ctx);
-    void EmitCompositeConstructF32x4(EmitContext& ctx);
-    void EmitCompositeExtractF32x2(EmitContext& ctx);
-    void EmitCompositeExtractF32x3(EmitContext& ctx);
-    void EmitCompositeExtractF32x4(EmitContext& ctx);
-    void EmitCompositeConstructF64x2(EmitContext& ctx);
-    void EmitCompositeConstructF64x3(EmitContext& ctx);
-    void EmitCompositeConstructF64x4(EmitContext& ctx);
-    void EmitCompositeExtractF64x2(EmitContext& ctx);
-    void EmitCompositeExtractF64x3(EmitContext& ctx);
-    void EmitCompositeExtractF64x4(EmitContext& ctx);
-    void EmitSelect8(EmitContext& ctx);
-    void EmitSelect16(EmitContext& ctx);
-    void EmitSelect32(EmitContext& ctx);
-    void EmitSelect64(EmitContext& ctx);
-    void EmitBitCastU16F16(EmitContext& ctx);
-    Id EmitBitCastU32F32(EmitContext& ctx, Id value);
-    void EmitBitCastU64F64(EmitContext& ctx);
-    void EmitBitCastF16U16(EmitContext& ctx);
-    Id EmitBitCastF32U32(EmitContext& ctx, Id value);
-    void EmitBitCastF64U64(EmitContext& ctx);
-    void EmitPackUint2x32(EmitContext& ctx);
-    void EmitUnpackUint2x32(EmitContext& ctx);
-    void EmitPackFloat2x16(EmitContext& ctx);
-    void EmitUnpackFloat2x16(EmitContext& ctx);
-    void EmitPackDouble2x32(EmitContext& ctx);
-    void EmitUnpackDouble2x32(EmitContext& ctx);
-    void EmitGetZeroFromOp(EmitContext& ctx);
-    void EmitGetSignFromOp(EmitContext& ctx);
-    void EmitGetCarryFromOp(EmitContext& ctx);
-    void EmitGetOverflowFromOp(EmitContext& ctx);
-    void EmitFPAbs16(EmitContext& ctx);
-    void EmitFPAbs32(EmitContext& ctx);
-    void EmitFPAbs64(EmitContext& ctx);
-    Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
-    Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
-    Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
-    Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
-    Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
-    Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
-    void EmitFPMax32(EmitContext& ctx);
-    void EmitFPMax64(EmitContext& ctx);
-    void EmitFPMin32(EmitContext& ctx);
-    void EmitFPMin64(EmitContext& ctx);
-    Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
-    Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
-    Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
-    void EmitFPNeg16(EmitContext& ctx);
-    void EmitFPNeg32(EmitContext& ctx);
-    void EmitFPNeg64(EmitContext& ctx);
-    void EmitFPRecip32(EmitContext& ctx);
-    void EmitFPRecip64(EmitContext& ctx);
-    void EmitFPRecipSqrt32(EmitContext& ctx);
-    void EmitFPRecipSqrt64(EmitContext& ctx);
-    void EmitFPSqrt(EmitContext& ctx);
-    void EmitFPSin(EmitContext& ctx);
-    void EmitFPSinNotReduced(EmitContext& ctx);
-    void EmitFPExp2(EmitContext& ctx);
-    void EmitFPExp2NotReduced(EmitContext& ctx);
-    void EmitFPCos(EmitContext& ctx);
-    void EmitFPCosNotReduced(EmitContext& ctx);
-    void EmitFPLog2(EmitContext& ctx);
-    void EmitFPSaturate16(EmitContext& ctx);
-    void EmitFPSaturate32(EmitContext& ctx);
-    void EmitFPSaturate64(EmitContext& ctx);
-    void EmitFPRoundEven16(EmitContext& ctx);
-    void EmitFPRoundEven32(EmitContext& ctx);
-    void EmitFPRoundEven64(EmitContext& ctx);
-    void EmitFPFloor16(EmitContext& ctx);
-    void EmitFPFloor32(EmitContext& ctx);
-    void EmitFPFloor64(EmitContext& ctx);
-    void EmitFPCeil16(EmitContext& ctx);
-    void EmitFPCeil32(EmitContext& ctx);
-    void EmitFPCeil64(EmitContext& ctx);
-    void EmitFPTrunc16(EmitContext& ctx);
-    void EmitFPTrunc32(EmitContext& ctx);
-    void EmitFPTrunc64(EmitContext& ctx);
-    Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
-    void EmitIAdd64(EmitContext& ctx);
-    Id EmitISub32(EmitContext& ctx, Id a, Id b);
-    void EmitISub64(EmitContext& ctx);
-    Id EmitIMul32(EmitContext& ctx, Id a, Id b);
-    void EmitINeg32(EmitContext& ctx);
-    void EmitIAbs32(EmitContext& ctx);
-    Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift);
-    void EmitShiftRightLogical32(EmitContext& ctx);
-    void EmitShiftRightArithmetic32(EmitContext& ctx);
-    void EmitBitwiseAnd32(EmitContext& ctx);
-    void EmitBitwiseOr32(EmitContext& ctx);
-    void EmitBitwiseXor32(EmitContext& ctx);
-    void EmitBitFieldInsert(EmitContext& ctx);
-    void EmitBitFieldSExtract(EmitContext& ctx);
-    Id EmitBitFieldUExtract(EmitContext& ctx, Id base, Id offset, Id count);
-    Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs);
-    void EmitULessThan(EmitContext& ctx);
-    void EmitIEqual(EmitContext& ctx);
-    void EmitSLessThanEqual(EmitContext& ctx);
-    void EmitULessThanEqual(EmitContext& ctx);
-    Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs);
-    void EmitUGreaterThan(EmitContext& ctx);
-    void EmitINotEqual(EmitContext& ctx);
-    void EmitSGreaterThanEqual(EmitContext& ctx);
-    Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
-    void EmitLogicalOr(EmitContext& ctx);
-    void EmitLogicalAnd(EmitContext& ctx);
-    void EmitLogicalXor(EmitContext& ctx);
-    void EmitLogicalNot(EmitContext& ctx);
-    void EmitConvertS16F16(EmitContext& ctx);
-    void EmitConvertS16F32(EmitContext& ctx);
-    void EmitConvertS16F64(EmitContext& ctx);
-    void EmitConvertS32F16(EmitContext& ctx);
-    void EmitConvertS32F32(EmitContext& ctx);
-    void EmitConvertS32F64(EmitContext& ctx);
-    void EmitConvertS64F16(EmitContext& ctx);
-    void EmitConvertS64F32(EmitContext& ctx);
-    void EmitConvertS64F64(EmitContext& ctx);
-    void EmitConvertU16F16(EmitContext& ctx);
-    void EmitConvertU16F32(EmitContext& ctx);
-    void EmitConvertU16F64(EmitContext& ctx);
-    void EmitConvertU32F16(EmitContext& ctx);
-    void EmitConvertU32F32(EmitContext& ctx);
-    void EmitConvertU32F64(EmitContext& ctx);
-    void EmitConvertU64F16(EmitContext& ctx);
-    void EmitConvertU64F32(EmitContext& ctx);
-    void EmitConvertU64F64(EmitContext& ctx);
-    void EmitConvertU64U32(EmitContext& ctx);
-    void EmitConvertU32U64(EmitContext& ctx);
-};
+// Microinstruction emitters
+Id EmitPhi(EmitContext& ctx, IR::Inst* inst);
+void EmitVoid(EmitContext& ctx);
+Id EmitIdentity(EmitContext& ctx, const IR::Value& value);
+void EmitBranch(EmitContext& ctx, IR::Block* label);
+void EmitBranchConditional(EmitContext& ctx, Id condition, IR::Block* true_label,
+                           IR::Block* false_label);
+void EmitLoopMerge(EmitContext& ctx, IR::Block* merge_label, IR::Block* continue_label);
+void EmitSelectionMerge(EmitContext& ctx, IR::Block* merge_label);
+void EmitReturn(EmitContext& ctx);
+void EmitGetRegister(EmitContext& ctx);
+void EmitSetRegister(EmitContext& ctx);
+void EmitGetPred(EmitContext& ctx);
+void EmitSetPred(EmitContext& ctx);
+void EmitSetGotoVariable(EmitContext& ctx);
+void EmitGetGotoVariable(EmitContext& ctx);
+Id EmitGetCbuf(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
+void EmitGetAttribute(EmitContext& ctx);
+void EmitSetAttribute(EmitContext& ctx);
+void EmitGetAttributeIndexed(EmitContext& ctx);
+void EmitSetAttributeIndexed(EmitContext& ctx);
+void EmitGetZFlag(EmitContext& ctx);
+void EmitGetSFlag(EmitContext& ctx);
+void EmitGetCFlag(EmitContext& ctx);
+void EmitGetOFlag(EmitContext& ctx);
+void EmitSetZFlag(EmitContext& ctx);
+void EmitSetSFlag(EmitContext& ctx);
+void EmitSetCFlag(EmitContext& ctx);
+void EmitSetOFlag(EmitContext& ctx);
+Id EmitWorkgroupId(EmitContext& ctx);
+Id EmitLocalInvocationId(EmitContext& ctx);
+Id EmitUndefU1(EmitContext& ctx);
+Id EmitUndefU8(EmitContext& ctx);
+Id EmitUndefU16(EmitContext& ctx);
+Id EmitUndefU32(EmitContext& ctx);
+Id EmitUndefU64(EmitContext& ctx);
+void EmitLoadGlobalU8(EmitContext& ctx);
+void EmitLoadGlobalS8(EmitContext& ctx);
+void EmitLoadGlobalU16(EmitContext& ctx);
+void EmitLoadGlobalS16(EmitContext& ctx);
+void EmitLoadGlobal32(EmitContext& ctx);
+void EmitLoadGlobal64(EmitContext& ctx);
+void EmitLoadGlobal128(EmitContext& ctx);
+void EmitWriteGlobalU8(EmitContext& ctx);
+void EmitWriteGlobalS8(EmitContext& ctx);
+void EmitWriteGlobalU16(EmitContext& ctx);
+void EmitWriteGlobalS16(EmitContext& ctx);
+void EmitWriteGlobal32(EmitContext& ctx);
+void EmitWriteGlobal64(EmitContext& ctx);
+void EmitWriteGlobal128(EmitContext& ctx);
+void EmitLoadStorageU8(EmitContext& ctx);
+void EmitLoadStorageS8(EmitContext& ctx);
+void EmitLoadStorageU16(EmitContext& ctx);
+void EmitLoadStorageS16(EmitContext& ctx);
+Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
+void EmitLoadStorage64(EmitContext& ctx);
+void EmitLoadStorage128(EmitContext& ctx);
+void EmitWriteStorageU8(EmitContext& ctx);
+void EmitWriteStorageS8(EmitContext& ctx);
+void EmitWriteStorageU16(EmitContext& ctx);
+void EmitWriteStorageS16(EmitContext& ctx);
+void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
+                        Id value);
+void EmitWriteStorage64(EmitContext& ctx);
+void EmitWriteStorage128(EmitContext& ctx);
+void EmitCompositeConstructU32x2(EmitContext& ctx);
+void EmitCompositeConstructU32x3(EmitContext& ctx);
+void EmitCompositeConstructU32x4(EmitContext& ctx);
+void EmitCompositeExtractU32x2(EmitContext& ctx);
+Id EmitCompositeExtractU32x3(EmitContext& ctx, Id vector, u32 index);
+void EmitCompositeExtractU32x4(EmitContext& ctx);
+void EmitCompositeConstructF16x2(EmitContext& ctx);
+void EmitCompositeConstructF16x3(EmitContext& ctx);
+void EmitCompositeConstructF16x4(EmitContext& ctx);
+void EmitCompositeExtractF16x2(EmitContext& ctx);
+void EmitCompositeExtractF16x3(EmitContext& ctx);
+void EmitCompositeExtractF16x4(EmitContext& ctx);
+void EmitCompositeConstructF32x2(EmitContext& ctx);
+void EmitCompositeConstructF32x3(EmitContext& ctx);
+void EmitCompositeConstructF32x4(EmitContext& ctx);
+void EmitCompositeExtractF32x2(EmitContext& ctx);
+void EmitCompositeExtractF32x3(EmitContext& ctx);
+void EmitCompositeExtractF32x4(EmitContext& ctx);
+void EmitCompositeConstructF64x2(EmitContext& ctx);
+void EmitCompositeConstructF64x3(EmitContext& ctx);
+void EmitCompositeConstructF64x4(EmitContext& ctx);
+void EmitCompositeExtractF64x2(EmitContext& ctx);
+void EmitCompositeExtractF64x3(EmitContext& ctx);
+void EmitCompositeExtractF64x4(EmitContext& ctx);
+void EmitSelect8(EmitContext& ctx);
+void EmitSelect16(EmitContext& ctx);
+void EmitSelect32(EmitContext& ctx);
+void EmitSelect64(EmitContext& ctx);
+void EmitBitCastU16F16(EmitContext& ctx);
+Id EmitBitCastU32F32(EmitContext& ctx, Id value);
+void EmitBitCastU64F64(EmitContext& ctx);
+void EmitBitCastF16U16(EmitContext& ctx);
+Id EmitBitCastF32U32(EmitContext& ctx, Id value);
+void EmitBitCastF64U64(EmitContext& ctx);
+void EmitPackUint2x32(EmitContext& ctx);
+void EmitUnpackUint2x32(EmitContext& ctx);
+void EmitPackFloat2x16(EmitContext& ctx);
+void EmitUnpackFloat2x16(EmitContext& ctx);
+void EmitPackDouble2x32(EmitContext& ctx);
+void EmitUnpackDouble2x32(EmitContext& ctx);
+void EmitGetZeroFromOp(EmitContext& ctx);
+void EmitGetSignFromOp(EmitContext& ctx);
+void EmitGetCarryFromOp(EmitContext& ctx);
+void EmitGetOverflowFromOp(EmitContext& ctx);
+void EmitFPAbs16(EmitContext& ctx);
+void EmitFPAbs32(EmitContext& ctx);
+void EmitFPAbs64(EmitContext& ctx);
+Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
+Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
+Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
+Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
+Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
+Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
+void EmitFPMax32(EmitContext& ctx);
+void EmitFPMax64(EmitContext& ctx);
+void EmitFPMin32(EmitContext& ctx);
+void EmitFPMin64(EmitContext& ctx);
+Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
+Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
+Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
+void EmitFPNeg16(EmitContext& ctx);
+void EmitFPNeg32(EmitContext& ctx);
+void EmitFPNeg64(EmitContext& ctx);
+void EmitFPRecip32(EmitContext& ctx);
+void EmitFPRecip64(EmitContext& ctx);
+void EmitFPRecipSqrt32(EmitContext& ctx);
+void EmitFPRecipSqrt64(EmitContext& ctx);
+void EmitFPSqrt(EmitContext& ctx);
+void EmitFPSin(EmitContext& ctx);
+void EmitFPSinNotReduced(EmitContext& ctx);
+void EmitFPExp2(EmitContext& ctx);
+void EmitFPExp2NotReduced(EmitContext& ctx);
+void EmitFPCos(EmitContext& ctx);
+void EmitFPCosNotReduced(EmitContext& ctx);
+void EmitFPLog2(EmitContext& ctx);
+void EmitFPSaturate16(EmitContext& ctx);
+void EmitFPSaturate32(EmitContext& ctx);
+void EmitFPSaturate64(EmitContext& ctx);
+void EmitFPRoundEven16(EmitContext& ctx);
+void EmitFPRoundEven32(EmitContext& ctx);
+void EmitFPRoundEven64(EmitContext& ctx);
+void EmitFPFloor16(EmitContext& ctx);
+void EmitFPFloor32(EmitContext& ctx);
+void EmitFPFloor64(EmitContext& ctx);
+void EmitFPCeil16(EmitContext& ctx);
+void EmitFPCeil32(EmitContext& ctx);
+void EmitFPCeil64(EmitContext& ctx);
+void EmitFPTrunc16(EmitContext& ctx);
+void EmitFPTrunc32(EmitContext& ctx);
+void EmitFPTrunc64(EmitContext& ctx);
+Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
+void EmitIAdd64(EmitContext& ctx);
+Id EmitISub32(EmitContext& ctx, Id a, Id b);
+void EmitISub64(EmitContext& ctx);
+Id EmitIMul32(EmitContext& ctx, Id a, Id b);
+void EmitINeg32(EmitContext& ctx);
+void EmitIAbs32(EmitContext& ctx);
+Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift);
+void EmitShiftRightLogical32(EmitContext& ctx);
+void EmitShiftRightArithmetic32(EmitContext& ctx);
+void EmitBitwiseAnd32(EmitContext& ctx);
+void EmitBitwiseOr32(EmitContext& ctx);
+void EmitBitwiseXor32(EmitContext& ctx);
+void EmitBitFieldInsert(EmitContext& ctx);
+void EmitBitFieldSExtract(EmitContext& ctx);
+Id EmitBitFieldUExtract(EmitContext& ctx, Id base, Id offset, Id count);
+Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs);
+void EmitULessThan(EmitContext& ctx);
+void EmitIEqual(EmitContext& ctx);
+void EmitSLessThanEqual(EmitContext& ctx);
+void EmitULessThanEqual(EmitContext& ctx);
+Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs);
+void EmitUGreaterThan(EmitContext& ctx);
+void EmitINotEqual(EmitContext& ctx);
+void EmitSGreaterThanEqual(EmitContext& ctx);
+Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
+void EmitLogicalOr(EmitContext& ctx);
+void EmitLogicalAnd(EmitContext& ctx);
+void EmitLogicalXor(EmitContext& ctx);
+void EmitLogicalNot(EmitContext& ctx);
+void EmitConvertS16F16(EmitContext& ctx);
+void EmitConvertS16F32(EmitContext& ctx);
+void EmitConvertS16F64(EmitContext& ctx);
+void EmitConvertS32F16(EmitContext& ctx);
+void EmitConvertS32F32(EmitContext& ctx);
+void EmitConvertS32F64(EmitContext& ctx);
+void EmitConvertS64F16(EmitContext& ctx);
+void EmitConvertS64F32(EmitContext& ctx);
+void EmitConvertS64F64(EmitContext& ctx);
+void EmitConvertU16F16(EmitContext& ctx);
+void EmitConvertU16F32(EmitContext& ctx);
+void EmitConvertU16F64(EmitContext& ctx);
+void EmitConvertU32F16(EmitContext& ctx);
+void EmitConvertU32F32(EmitContext& ctx);
+void EmitConvertU32F64(EmitContext& ctx);
+void EmitConvertU64F16(EmitContext& ctx);
+void EmitConvertU64F32(EmitContext& ctx);
+void EmitConvertU64F64(EmitContext& ctx);
+void EmitConvertU64U32(EmitContext& ctx);
+void EmitConvertU32U64(EmitContext& ctx);
 
 } // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
index af82df99c..49c200498 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
@@ -6,51 +6,51 @@
 
 namespace Shader::Backend::SPIRV {
 
-void EmitSPIRV::EmitBitCastU16F16(EmitContext&) {
+void EmitBitCastU16F16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitBitCastU32F32(EmitContext& ctx, Id value) {
+Id EmitBitCastU32F32(EmitContext& ctx, Id value) {
     return ctx.OpBitcast(ctx.U32[1], value);
 }
 
-void EmitSPIRV::EmitBitCastU64F64(EmitContext&) {
+void EmitBitCastU64F64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitBitCastF16U16(EmitContext&) {
+void EmitBitCastF16U16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitBitCastF32U32(EmitContext& ctx, Id value) {
+Id EmitBitCastF32U32(EmitContext& ctx, Id value) {
     return ctx.OpBitcast(ctx.F32[1], value);
 }
 
-void EmitSPIRV::EmitBitCastF64U64(EmitContext&) {
+void EmitBitCastF64U64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitPackUint2x32(EmitContext&) {
+void EmitPackUint2x32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitUnpackUint2x32(EmitContext&) {
+void EmitUnpackUint2x32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitPackFloat2x16(EmitContext&) {
+void EmitPackFloat2x16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitUnpackFloat2x16(EmitContext&) {
+void EmitUnpackFloat2x16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitPackDouble2x32(EmitContext&) {
+void EmitPackDouble2x32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitUnpackDouble2x32(EmitContext&) {
+void EmitUnpackDouble2x32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
index a7374c89d..348e4796d 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
@@ -6,99 +6,99 @@
 
 namespace Shader::Backend::SPIRV {
 
-void EmitSPIRV::EmitCompositeConstructU32x2(EmitContext&) {
+void EmitCompositeConstructU32x2(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructU32x3(EmitContext&) {
+void EmitCompositeConstructU32x3(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructU32x4(EmitContext&) {
+void EmitCompositeConstructU32x4(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeExtractU32x2(EmitContext&) {
+void EmitCompositeExtractU32x2(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitCompositeExtractU32x3(EmitContext& ctx, Id vector, u32 index) {
+Id EmitCompositeExtractU32x3(EmitContext& ctx, Id vector, u32 index) {
     return ctx.OpCompositeExtract(ctx.U32[1], vector, index);
 }
 
-void EmitSPIRV::EmitCompositeExtractU32x4(EmitContext&) {
+void EmitCompositeExtractU32x4(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructF16x2(EmitContext&) {
+void EmitCompositeConstructF16x2(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructF16x3(EmitContext&) {
+void EmitCompositeConstructF16x3(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructF16x4(EmitContext&) {
+void EmitCompositeConstructF16x4(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeExtractF16x2(EmitContext&) {
+void EmitCompositeExtractF16x2(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeExtractF16x3(EmitContext&) {
+void EmitCompositeExtractF16x3(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeExtractF16x4(EmitContext&) {
+void EmitCompositeExtractF16x4(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructF32x2(EmitContext&) {
+void EmitCompositeConstructF32x2(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructF32x3(EmitContext&) {
+void EmitCompositeConstructF32x3(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructF32x4(EmitContext&) {
+void EmitCompositeConstructF32x4(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeExtractF32x2(EmitContext&) {
+void EmitCompositeExtractF32x2(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeExtractF32x3(EmitContext&) {
+void EmitCompositeExtractF32x3(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeExtractF32x4(EmitContext&) {
+void EmitCompositeExtractF32x4(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructF64x2(EmitContext&) {
+void EmitCompositeConstructF64x2(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructF64x3(EmitContext&) {
+void EmitCompositeConstructF64x3(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeConstructF64x4(EmitContext&) {
+void EmitCompositeConstructF64x4(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeExtractF64x2(EmitContext&) {
+void EmitCompositeExtractF64x2(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeExtractF64x3(EmitContext&) {
+void EmitCompositeExtractF64x3(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitCompositeExtractF64x4(EmitContext&) {
+void EmitCompositeExtractF64x4(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index f4c9970eb..eb9c01c5a 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -6,31 +6,31 @@
 
 namespace Shader::Backend::SPIRV {
 
-void EmitSPIRV::EmitGetRegister(EmitContext&) {
+void EmitGetRegister(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSetRegister(EmitContext&) {
+void EmitSetRegister(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitGetPred(EmitContext&) {
+void EmitGetPred(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSetPred(EmitContext&) {
+void EmitSetPred(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSetGotoVariable(EmitContext&) {
+void EmitSetGotoVariable(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitGetGotoVariable(EmitContext&) {
+void EmitGetGotoVariable(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitGetCbuf(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
+Id EmitGetCbuf(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
     if (!binding.IsImmediate()) {
         throw NotImplementedException("Constant buffer indexing");
     }
@@ -43,59 +43,59 @@ Id EmitSPIRV::EmitGetCbuf(EmitContext& ctx, const IR::Value& binding, const IR::
     return ctx.OpLoad(ctx.U32[1], access_chain);
 }
 
-void EmitSPIRV::EmitGetAttribute(EmitContext&) {
+void EmitGetAttribute(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSetAttribute(EmitContext&) {
+void EmitSetAttribute(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitGetAttributeIndexed(EmitContext&) {
+void EmitGetAttributeIndexed(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSetAttributeIndexed(EmitContext&) {
+void EmitSetAttributeIndexed(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitGetZFlag(EmitContext&) {
+void EmitGetZFlag(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitGetSFlag(EmitContext&) {
+void EmitGetSFlag(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitGetCFlag(EmitContext&) {
+void EmitGetCFlag(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitGetOFlag(EmitContext&) {
+void EmitGetOFlag(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSetZFlag(EmitContext&) {
+void EmitSetZFlag(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSetSFlag(EmitContext&) {
+void EmitSetSFlag(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSetCFlag(EmitContext&) {
+void EmitSetCFlag(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSetOFlag(EmitContext&) {
+void EmitSetOFlag(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitWorkgroupId(EmitContext& ctx) {
+Id EmitWorkgroupId(EmitContext& ctx) {
     return ctx.OpLoad(ctx.U32[3], ctx.workgroup_id);
 }
 
-Id EmitSPIRV::EmitLocalInvocationId(EmitContext& ctx) {
+Id EmitLocalInvocationId(EmitContext& ctx) {
     return ctx.OpLoad(ctx.U32[3], ctx.local_invocation_id);
 }
 
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 549c1907a..6c4199664 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
@@ -6,25 +6,25 @@
 
 namespace Shader::Backend::SPIRV {
 
-void EmitSPIRV::EmitBranch(EmitContext& ctx, IR::Block* label) {
+void EmitBranch(EmitContext& ctx, IR::Block* label) {
     ctx.OpBranch(label->Definition<Id>());
 }
 
-void EmitSPIRV::EmitBranchConditional(EmitContext& ctx, Id condition, IR::Block* true_label,
+void EmitBranchConditional(EmitContext& ctx, Id condition, IR::Block* true_label,
                                       IR::Block* false_label) {
     ctx.OpBranchConditional(condition, true_label->Definition<Id>(), false_label->Definition<Id>());
 }
 
-void EmitSPIRV::EmitLoopMerge(EmitContext& ctx, IR::Block* merge_label, IR::Block* continue_label) {
+void EmitLoopMerge(EmitContext& ctx, IR::Block* merge_label, IR::Block* continue_label) {
     ctx.OpLoopMerge(merge_label->Definition<Id>(), continue_label->Definition<Id>(),
                     spv::LoopControlMask::MaskNone);
 }
 
-void EmitSPIRV::EmitSelectionMerge(EmitContext& ctx, IR::Block* merge_label) {
+void EmitSelectionMerge(EmitContext& ctx, IR::Block* merge_label) {
     ctx.OpSelectionMerge(merge_label->Definition<Id>(), spv::SelectionControlMask::MaskNone);
 }
 
-void EmitSPIRV::EmitReturn(EmitContext& ctx) {
+void EmitReturn(EmitContext& ctx) {
     ctx.OpReturn();
 }
 
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
index c9bc121f8..d24fbb353 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
@@ -33,187 +33,187 @@ Id Decorate(EmitContext& ctx, IR::Inst* inst, Id op) {
 
 } // Anonymous namespace
 
-void EmitSPIRV::EmitFPAbs16(EmitContext&) {
+void EmitFPAbs16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPAbs32(EmitContext&) {
+void EmitFPAbs32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPAbs64(EmitContext&) {
+void EmitFPAbs64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
+Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
     return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b));
 }
 
-Id EmitSPIRV::EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
+Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
     return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b));
 }
 
-Id EmitSPIRV::EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
+Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
     return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b));
 }
 
-Id EmitSPIRV::EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
+Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
     return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c));
 }
 
-Id EmitSPIRV::EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
+Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
     return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c));
 }
 
-Id EmitSPIRV::EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
+Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
     return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c));
 }
 
-void EmitSPIRV::EmitFPMax32(EmitContext&) {
+void EmitFPMax32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPMax64(EmitContext&) {
+void EmitFPMax64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPMin32(EmitContext&) {
+void EmitFPMin32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPMin64(EmitContext&) {
+void EmitFPMin64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
+Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
     return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b));
 }
 
-Id EmitSPIRV::EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
+Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
     return Decorate(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b));
 }
 
-Id EmitSPIRV::EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
+Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
     return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b));
 }
 
-void EmitSPIRV::EmitFPNeg16(EmitContext&) {
+void EmitFPNeg16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPNeg32(EmitContext&) {
+void EmitFPNeg32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPNeg64(EmitContext&) {
+void EmitFPNeg64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPRecip32(EmitContext&) {
+void EmitFPRecip32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPRecip64(EmitContext&) {
+void EmitFPRecip64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPRecipSqrt32(EmitContext&) {
+void EmitFPRecipSqrt32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPRecipSqrt64(EmitContext&) {
+void EmitFPRecipSqrt64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPSqrt(EmitContext&) {
+void EmitFPSqrt(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPSin(EmitContext&) {
+void EmitFPSin(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPSinNotReduced(EmitContext&) {
+void EmitFPSinNotReduced(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPExp2(EmitContext&) {
+void EmitFPExp2(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPExp2NotReduced(EmitContext&) {
+void EmitFPExp2NotReduced(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPCos(EmitContext&) {
+void EmitFPCos(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPCosNotReduced(EmitContext&) {
+void EmitFPCosNotReduced(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPLog2(EmitContext&) {
+void EmitFPLog2(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPSaturate16(EmitContext&) {
+void EmitFPSaturate16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPSaturate32(EmitContext&) {
+void EmitFPSaturate32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPSaturate64(EmitContext&) {
+void EmitFPSaturate64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPRoundEven16(EmitContext&) {
+void EmitFPRoundEven16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPRoundEven32(EmitContext&) {
+void EmitFPRoundEven32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPRoundEven64(EmitContext&) {
+void EmitFPRoundEven64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPFloor16(EmitContext&) {
+void EmitFPFloor16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPFloor32(EmitContext&) {
+void EmitFPFloor32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPFloor64(EmitContext&) {
+void EmitFPFloor64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPCeil16(EmitContext&) {
+void EmitFPCeil16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPCeil32(EmitContext&) {
+void EmitFPCeil32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPCeil64(EmitContext&) {
+void EmitFPCeil64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPTrunc16(EmitContext&) {
+void EmitFPTrunc16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPTrunc32(EmitContext&) {
+void EmitFPTrunc32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitFPTrunc64(EmitContext&) {
+void EmitFPTrunc64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
index 32af94a73..a1d16b81e 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
@@ -6,126 +6,126 @@
 
 namespace Shader::Backend::SPIRV {
 
-Id EmitSPIRV::EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
+Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
     if (inst->HasAssociatedPseudoOperation()) {
         throw NotImplementedException("Pseudo-operations on IAdd32");
     }
     return ctx.OpIAdd(ctx.U32[1], a, b);
 }
 
-void EmitSPIRV::EmitIAdd64(EmitContext&) {
+void EmitIAdd64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitISub32(EmitContext& ctx, Id a, Id b) {
+Id EmitISub32(EmitContext& ctx, Id a, Id b) {
     return ctx.OpISub(ctx.U32[1], a, b);
 }
 
-void EmitSPIRV::EmitISub64(EmitContext&) {
+void EmitISub64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitIMul32(EmitContext& ctx, Id a, Id b) {
+Id EmitIMul32(EmitContext& ctx, Id a, Id b) {
     return ctx.OpIMul(ctx.U32[1], a, b);
 }
 
-void EmitSPIRV::EmitINeg32(EmitContext&) {
+void EmitINeg32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitIAbs32(EmitContext&) {
+void EmitIAbs32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) {
+Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) {
     return ctx.OpShiftLeftLogical(ctx.U32[1], base, shift);
 }
 
-void EmitSPIRV::EmitShiftRightLogical32(EmitContext&) {
+void EmitShiftRightLogical32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitShiftRightArithmetic32(EmitContext&) {
+void EmitShiftRightArithmetic32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitBitwiseAnd32(EmitContext&) {
+void EmitBitwiseAnd32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitBitwiseOr32(EmitContext&) {
+void EmitBitwiseOr32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitBitwiseXor32(EmitContext&) {
+void EmitBitwiseXor32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitBitFieldInsert(EmitContext&) {
+void EmitBitFieldInsert(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitBitFieldSExtract(EmitContext&) {
+void EmitBitFieldSExtract(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitBitFieldUExtract(EmitContext& ctx, Id base, Id offset, Id count) {
+Id EmitBitFieldUExtract(EmitContext& ctx, Id base, Id offset, Id count) {
     return ctx.OpBitFieldUExtract(ctx.U32[1], base, offset, count);
 }
 
-Id EmitSPIRV::EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) {
+Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) {
     return ctx.OpSLessThan(ctx.U1, lhs, rhs);
 }
 
-void EmitSPIRV::EmitULessThan(EmitContext&) {
+void EmitULessThan(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitIEqual(EmitContext&) {
+void EmitIEqual(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSLessThanEqual(EmitContext&) {
+void EmitSLessThanEqual(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitULessThanEqual(EmitContext&) {
+void EmitULessThanEqual(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
+Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
     return ctx.OpSGreaterThan(ctx.U1, lhs, rhs);
 }
 
-void EmitSPIRV::EmitUGreaterThan(EmitContext&) {
+void EmitUGreaterThan(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitINotEqual(EmitContext&) {
+void EmitINotEqual(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSGreaterThanEqual(EmitContext&) {
+void EmitSGreaterThanEqual(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
+Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
     return ctx.OpUGreaterThanEqual(ctx.U1, lhs, rhs);
 }
 
-void EmitSPIRV::EmitLogicalOr(EmitContext&) {
+void EmitLogicalOr(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLogicalAnd(EmitContext&) {
+void EmitLogicalAnd(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLogicalXor(EmitContext&) {
+void EmitLogicalXor(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLogicalNot(EmitContext&) {
+void EmitLogicalNot(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
index 7b43c4ed8..ff2f4fb74 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
@@ -6,83 +6,83 @@
 
 namespace Shader::Backend::SPIRV {
 
-void EmitSPIRV::EmitConvertS16F16(EmitContext&) {
+void EmitConvertS16F16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertS16F32(EmitContext&) {
+void EmitConvertS16F32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertS16F64(EmitContext&) {
+void EmitConvertS16F64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertS32F16(EmitContext&) {
+void EmitConvertS32F16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertS32F32(EmitContext&) {
+void EmitConvertS32F32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertS32F64(EmitContext&) {
+void EmitConvertS32F64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertS64F16(EmitContext&) {
+void EmitConvertS64F16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertS64F32(EmitContext&) {
+void EmitConvertS64F32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertS64F64(EmitContext&) {
+void EmitConvertS64F64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU16F16(EmitContext&) {
+void EmitConvertU16F16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU16F32(EmitContext&) {
+void EmitConvertU16F32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU16F64(EmitContext&) {
+void EmitConvertU16F64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU32F16(EmitContext&) {
+void EmitConvertU32F16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU32F32(EmitContext&) {
+void EmitConvertU32F32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU32F64(EmitContext&) {
+void EmitConvertU32F64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU64F16(EmitContext&) {
+void EmitConvertU64F16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU64F32(EmitContext&) {
+void EmitConvertU64F32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU64F64(EmitContext&) {
+void EmitConvertU64F64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU64U32(EmitContext&) {
+void EmitConvertU64U32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitConvertU32U64(EmitContext&) {
+void EmitConvertU32U64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
index 5769a3c95..77d698ffd 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
@@ -22,79 +22,79 @@ static Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element
     return ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
 }
 
-void EmitSPIRV::EmitLoadGlobalU8(EmitContext&) {
+void EmitLoadGlobalU8(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadGlobalS8(EmitContext&) {
+void EmitLoadGlobalS8(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadGlobalU16(EmitContext&) {
+void EmitLoadGlobalU16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadGlobalS16(EmitContext&) {
+void EmitLoadGlobalS16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadGlobal32(EmitContext&) {
+void EmitLoadGlobal32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadGlobal64(EmitContext&) {
+void EmitLoadGlobal64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadGlobal128(EmitContext&) {
+void EmitLoadGlobal128(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteGlobalU8(EmitContext&) {
+void EmitWriteGlobalU8(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteGlobalS8(EmitContext&) {
+void EmitWriteGlobalS8(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteGlobalU16(EmitContext&) {
+void EmitWriteGlobalU16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteGlobalS16(EmitContext&) {
+void EmitWriteGlobalS16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteGlobal32(EmitContext&) {
+void EmitWriteGlobal32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteGlobal64(EmitContext&) {
+void EmitWriteGlobal64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteGlobal128(EmitContext&) {
+void EmitWriteGlobal128(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadStorageU8(EmitContext&) {
+void EmitLoadStorageU8(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadStorageS8(EmitContext&) {
+void EmitLoadStorageS8(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadStorageU16(EmitContext&) {
+void EmitLoadStorageU16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadStorageS16(EmitContext&) {
+void EmitLoadStorageS16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding,
+Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding,
                                 const IR::Value& offset) {
     if (!binding.IsImmediate()) {
         throw NotImplementedException("Dynamic storage buffer indexing");
@@ -105,31 +105,31 @@ Id EmitSPIRV::EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding,
     return ctx.OpLoad(ctx.U32[1], pointer);
 }
 
-void EmitSPIRV::EmitLoadStorage64(EmitContext&) {
+void EmitLoadStorage64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitLoadStorage128(EmitContext&) {
+void EmitLoadStorage128(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteStorageU8(EmitContext&) {
+void EmitWriteStorageU8(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteStorageS8(EmitContext&) {
+void EmitWriteStorageS8(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteStorageU16(EmitContext&) {
+void EmitWriteStorageU16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteStorageS16(EmitContext&) {
+void EmitWriteStorageS16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding,
+void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding,
                                    const IR::Value& offset, Id value) {
     if (!binding.IsImmediate()) {
         throw NotImplementedException("Dynamic storage buffer indexing");
@@ -140,11 +140,11 @@ void EmitSPIRV::EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding,
     ctx.OpStore(pointer, value);
 }
 
-void EmitSPIRV::EmitWriteStorage64(EmitContext&) {
+void EmitWriteStorage64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitWriteStorage128(EmitContext&) {
+void EmitWriteStorage128(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
index 40a856f72..8d5062724 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
@@ -6,19 +6,19 @@
 
 namespace Shader::Backend::SPIRV {
 
-void EmitSPIRV::EmitSelect8(EmitContext&) {
+void EmitSelect8(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSelect16(EmitContext&) {
+void EmitSelect16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSelect32(EmitContext&) {
+void EmitSelect32(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-void EmitSPIRV::EmitSelect64(EmitContext&) {
+void EmitSelect64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
index c1ed8f281..19b06dbe4 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
@@ -6,23 +6,23 @@
 
 namespace Shader::Backend::SPIRV {
 
-Id EmitSPIRV::EmitUndefU1(EmitContext& ctx) {
+Id EmitUndefU1(EmitContext& ctx) {
     return ctx.OpUndef(ctx.U1);
 }
 
-Id EmitSPIRV::EmitUndefU8(EmitContext&) {
+Id EmitUndefU8(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitUndefU16(EmitContext&) {
+Id EmitUndefU16(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
-Id EmitSPIRV::EmitUndefU32(EmitContext& ctx) {
+Id EmitUndefU32(EmitContext& ctx) {
     return ctx.OpUndef(ctx.U32[1]);
 }
 
-Id EmitSPIRV::EmitUndefU64(EmitContext&) {
+Id EmitUndefU64(EmitContext&) {
     throw NotImplementedException("SPIR-V Instruction");
 }
 
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index f6230e817..0ba681fb9 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -1,5 +1,7 @@
 #pragma once
 
+#include <array>
+
 #include "common/common_types.h"
 
 namespace Shader {
@@ -8,7 +10,9 @@ class Environment {
 public:
     virtual ~Environment() = default;
 
-    [[nodiscard]] virtual u64 ReadInstruction(u32 address) const = 0;
+    [[nodiscard]] virtual u64 ReadInstruction(u32 address) = 0;
+
+    [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() = 0;
 };
 
 } // namespace Shader
diff --git a/src/shader_recompiler/file_environment.cpp b/src/shader_recompiler/file_environment.cpp
index b34bf462b..5127523f9 100644
--- a/src/shader_recompiler/file_environment.cpp
+++ b/src/shader_recompiler/file_environment.cpp
@@ -29,7 +29,7 @@ FileEnvironment::FileEnvironment(const char* path) {
 
 FileEnvironment::~FileEnvironment() = default;
 
-u64 FileEnvironment::ReadInstruction(u32 offset) const {
+u64 FileEnvironment::ReadInstruction(u32 offset) {
     if (offset % 8 != 0) {
         throw InvalidArgument("offset={} is not aligned to 8", offset);
     }
@@ -39,4 +39,8 @@ u64 FileEnvironment::ReadInstruction(u32 offset) const {
     return data[offset / 8];
 }
 
+std::array<u32, 3> FileEnvironment::WorkgroupSize() {
+    return {1, 1, 1};
+}
+
 } // namespace Shader
diff --git a/src/shader_recompiler/file_environment.h b/src/shader_recompiler/file_environment.h
index c294bc6fa..b8c4bbadd 100644
--- a/src/shader_recompiler/file_environment.h
+++ b/src/shader_recompiler/file_environment.h
@@ -12,7 +12,9 @@ public:
     explicit FileEnvironment(const char* path);
     ~FileEnvironment() override;
 
-    u64 ReadInstruction(u32 offset) const override;
+    u64 ReadInstruction(u32 offset) override;
+
+    std::array<u32, 3> WorkgroupSize() override;
 
 private:
     std::vector<u64> data;
diff --git a/src/shader_recompiler/frontend/ir/basic_block.cpp b/src/shader_recompiler/frontend/ir/basic_block.cpp
index 5ae91dd7d..ec029dfd6 100644
--- a/src/shader_recompiler/frontend/ir/basic_block.cpp
+++ b/src/shader_recompiler/frontend/ir/basic_block.cpp
@@ -127,6 +127,8 @@ static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_ind
         return fmt::format("#{}", arg.U32());
     case Type::U64:
         return fmt::format("#{}", arg.U64());
+    case Type::F32:
+        return fmt::format("#{}", arg.F32());
     case Type::Reg:
         return fmt::format("{}", arg.Reg());
     case Type::Pred:
diff --git a/src/shader_recompiler/frontend/ir/post_order.cpp b/src/shader_recompiler/frontend/ir/post_order.cpp
index a48b8dec5..8709a2ea1 100644
--- a/src/shader_recompiler/frontend/ir/post_order.cpp
+++ b/src/shader_recompiler/frontend/ir/post_order.cpp
@@ -28,7 +28,7 @@ BlockList PostOrder(const BlockList& blocks) {
         if (!visited.insert(branch).second) {
             return false;
         }
-        // Calling push_back twice is faster than insert on msvc
+        // Calling push_back twice is faster than insert on MSVC
         block_stack.push_back(block);
         block_stack.push_back(branch);
         return true;
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp
index 8331d576c..8c44ebb29 100644
--- a/src/shader_recompiler/frontend/maxwell/program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/program.cpp
@@ -69,7 +69,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
         Optimization::VerificationPass(function);
     }
     Optimization::CollectShaderInfoPass(program);
-    //*/
+    fmt::print(stdout, "{}\n", IR::DumpProgram(program));
     return program;
 }
 
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
index 3c9eaddd9..079e3497f 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
@@ -24,6 +24,14 @@ void TranslatorVisitor::F(IR::Reg dest_reg, const IR::F32& value) {
     X(dest_reg, ir.BitCast<IR::U32>(value));
 }
 
+IR::U32 TranslatorVisitor::GetReg8(u64 insn) {
+    union {
+        u64 raw;
+        BitField<8, 8, IR::Reg> index;
+    } const reg{insn};
+    return X(reg.index);
+}
+
 IR::U32 TranslatorVisitor::GetReg20(u64 insn) {
     union {
         u64 raw;
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
index b701605d7..8bd468244 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
@@ -301,6 +301,7 @@ public:
     void X(IR::Reg dest_reg, const IR::U32& value);
     void F(IR::Reg dest_reg, const IR::F32& value);
 
+    [[nodiscard]] IR::U32 GetReg8(u64 insn);
     [[nodiscard]] IR::U32 GetReg20(u64 insn);
     [[nodiscard]] IR::U32 GetReg39(u64 insn);
     [[nodiscard]] IR::F32 GetReg20F(u64 insn);
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_register.cpp
index 1f83d1068..c3c4b9abd 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_register.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_register.cpp
@@ -10,36 +10,35 @@
 
 namespace Shader::Maxwell {
 namespace {
-union MOV {
-    u64 raw;
-    BitField<0, 8, IR::Reg> dest_reg;
-    BitField<20, 8, IR::Reg> src_reg;
-    BitField<39, 4, u64> mask;
-};
-
-void CheckMask(MOV mov) {
-    if (mov.mask != 0xf) {
+void MOV(TranslatorVisitor& v, u64 insn, const IR::U32& src, bool is_mov32i = false) {
+    union {
+        u64 raw;
+        BitField<0, 8, IR::Reg> dest_reg;
+        BitField<39, 4, u64> mask;
+        BitField<12, 4, u64> mov32i_mask;
+    } const mov{insn};
+
+    if ((is_mov32i ? mov.mov32i_mask : mov.mask) != 0xf) {
         throw NotImplementedException("Non-full move mask");
     }
+    v.X(mov.dest_reg, src);
 }
 } // Anonymous namespace
 
 void TranslatorVisitor::MOV_reg(u64 insn) {
-    const MOV mov{insn};
-    CheckMask(mov);
-    X(mov.dest_reg, X(mov.src_reg));
+    MOV(*this, insn, GetReg8(insn));
 }
 
 void TranslatorVisitor::MOV_cbuf(u64 insn) {
-    const MOV mov{insn};
-    CheckMask(mov);
-    X(mov.dest_reg, GetCbuf(insn));
+    MOV(*this, insn, GetCbuf(insn));
 }
 
 void TranslatorVisitor::MOV_imm(u64 insn) {
-    const MOV mov{insn};
-    CheckMask(mov);
-    X(mov.dest_reg, GetImm20(insn));
+    MOV(*this, insn, GetImm20(insn));
+}
+
+void TranslatorVisitor::MOV32I(u64 insn) {
+    MOV(*this, insn, GetImm32(insn), true);
 }
 
 } // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
index 1bb160acb..6b2a1356b 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
@@ -617,10 +617,6 @@ void TranslatorVisitor::MEMBAR(u64) {
     ThrowNotImplemented(Opcode::MEMBAR);
 }
 
-void TranslatorVisitor::MOV32I(u64) {
-    ThrowNotImplemented(Opcode::MOV32I);
-}
-
 void TranslatorVisitor::NOP(u64) {
     ThrowNotImplemented(Opcode::NOP);
 }
diff --git a/src/shader_recompiler/main.cpp b/src/shader_recompiler/main.cpp
index 1610bb34e..050a37f18 100644
--- a/src/shader_recompiler/main.cpp
+++ b/src/shader_recompiler/main.cpp
@@ -76,5 +76,5 @@ int main() {
     fmt::print(stdout, "{}\n", cfg.Dot());
     IR::Program program{TranslateProgram(inst_pool, block_pool, env, cfg)};
     fmt::print(stdout, "{}\n", IR::DumpProgram(program));
-    Backend::SPIRV::EmitSPIRV spirv{program};
+    void(Backend::SPIRV::EmitSPIRV(env, program));
 }
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
new file mode 100644
index 000000000..c96d783b7
--- /dev/null
+++ b/src/shader_recompiler/profile.h
@@ -0,0 +1,13 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Shader {
+
+struct Profile {
+    bool unified_descriptor_binding;
+};
+
+} // namespace Shader
diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp
new file mode 100644
index 000000000..b25081e39
--- /dev/null
+++ b/src/shader_recompiler/recompiler.cpp
@@ -0,0 +1,27 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <vector>
+
+#include "common/common_types.h"
+#include "shader_recompiler/backend/spirv/emit_spirv.h"
+#include "shader_recompiler/environment.h"
+#include "shader_recompiler/frontend/maxwell/control_flow.h"
+#include "shader_recompiler/frontend/maxwell/program.h"
+#include "shader_recompiler/object_pool.h"
+#include "shader_recompiler/recompiler.h"
+
+namespace Shader {
+
+std::pair<Info, std::vector<u32>> RecompileSPIRV(Environment& env, u32 start_address) {
+    ObjectPool<Maxwell::Flow::Block> flow_block_pool;
+    ObjectPool<IR::Inst> inst_pool;
+    ObjectPool<IR::Block> block_pool;
+
+    Maxwell::Flow::CFG cfg{env, flow_block_pool, start_address};
+    IR::Program program{Maxwell::TranslateProgram(inst_pool, block_pool, env, cfg)};
+    return {std::move(program.info), Backend::SPIRV::EmitSPIRV(env, program)};
+}
+
+} // namespace Shader
diff --git a/src/shader_recompiler/recompiler.h b/src/shader_recompiler/recompiler.h
new file mode 100644
index 000000000..4cb973878
--- /dev/null
+++ b/src/shader_recompiler/recompiler.h
@@ -0,0 +1,18 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <utility>
+#include <vector>
+
+#include "common/common_types.h"
+#include "shader_recompiler/environment.h"
+#include "shader_recompiler/shader_info.h"
+
+namespace Shader {
+
+[[nodiscard]] std::pair<Info, std::vector<u32>> RecompileSPIRV(Environment& env, u32 start_address);
+
+} // namespace Shader
-- 
cgit v1.2.3