From bf4dfb3ad4c6419216e8154c970f5577bc1bc475 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 4 Jun 2019 22:44:06 -0300
Subject: [PATCH] shader: Use shared_ptr to store nodes and move initialization
 to file

Instead of having a vector of unique_ptr stored in a vector and
returning star pointers to this, use shared_ptr. While changing
initialization code, move it to a separate file when possible.

This is a first step to allow code analysis and node generation beyond
the ShaderIR class.
---
 src/video_core/CMakeLists.txt                 |   2 +
 .../renderer_opengl/gl_shader_decompiler.cpp  |  62 +++++-----
 .../renderer_vulkan/vk_shader_decompiler.cpp  |  50 ++++----
 src/video_core/shader/decode.cpp              |   1 +
 src/video_core/shader/decode/arithmetic.cpp   |   1 +
 .../shader/decode/arithmetic_half.cpp         |   1 +
 .../decode/arithmetic_half_immediate.cpp      |   1 +
 .../shader/decode/arithmetic_immediate.cpp    |   1 +
 .../shader/decode/arithmetic_integer.cpp      |   1 +
 .../decode/arithmetic_integer_immediate.cpp   |   1 +
 src/video_core/shader/decode/bfe.cpp          |   1 +
 src/video_core/shader/decode/bfi.cpp          |   1 +
 src/video_core/shader/decode/conversion.cpp   |   1 +
 src/video_core/shader/decode/ffma.cpp         |   1 +
 src/video_core/shader/decode/float_set.cpp    |   1 +
 .../shader/decode/float_set_predicate.cpp     |   1 +
 src/video_core/shader/decode/half_set.cpp     |   1 +
 .../shader/decode/half_set_predicate.cpp      |   1 +
 src/video_core/shader/decode/hfma2.cpp        |   1 +
 src/video_core/shader/decode/integer_set.cpp  |   1 +
 .../shader/decode/integer_set_predicate.cpp   |   1 +
 src/video_core/shader/decode/memory.cpp       |   9 +-
 src/video_core/shader/decode/other.cpp        |   1 +
 .../shader/decode/predicate_set_predicate.cpp |   1 +
 .../shader/decode/predicate_set_register.cpp  |   1 +
 .../shader/decode/register_set_predicate.cpp  |   1 +
 src/video_core/shader/decode/shift.cpp        |   1 +
 src/video_core/shader/decode/texture.cpp      |   9 +-
 src/video_core/shader/decode/video.cpp        |   1 +
 src/video_core/shader/decode/xmad.cpp         |   1 +
 src/video_core/shader/node_helper.cpp         |  99 ++++++++++++++++
 src/video_core/shader/node_helper.h           |  60 ++++++++++
 src/video_core/shader/shader_ir.cpp           | 102 ++---------------
 src/video_core/shader/shader_ir.h             | 108 ++++--------------
 src/video_core/shader/track.cpp               |  18 +--
 35 files changed, 296 insertions(+), 248 deletions(-)
 create mode 100644 src/video_core/shader/node_helper.cpp
 create mode 100644 src/video_core/shader/node_helper.h

diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 1e010e4da6..fa80c38144 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -102,6 +102,8 @@ add_library(video_core STATIC
     shader/decode/xmad.cpp
     shader/decode/other.cpp
     shader/decode.cpp
+    shader/node_helper.cpp
+    shader/node_helper.h
     shader/shader_ir.cpp
     shader/shader_ir.h
     shader/track.cpp
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 29de5c9db8..f2d0722af6 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -123,8 +123,8 @@ bool IsPrecise(Operation operand) {
     return false;
 }
 
-bool IsPrecise(Node node) {
-    if (const auto operation = std::get_if<OperationNode>(node)) {
+bool IsPrecise(const Node& node) {
+    if (const auto operation = std::get_if<OperationNode>(&*node)) {
         return IsPrecise(*operation);
     }
     return false;
@@ -497,15 +497,15 @@ private:
     }
 
     void VisitBlock(const NodeBlock& bb) {
-        for (const Node node : bb) {
+        for (const auto& node : bb) {
             if (const std::string expr = Visit(node); !expr.empty()) {
                 code.AddLine(expr);
             }
         }
     }
 
-    std::string Visit(Node node) {
-        if (const auto operation = std::get_if<OperationNode>(node)) {
+    std::string Visit(const Node& node) {
+        if (const auto operation = std::get_if<OperationNode>(&*node)) {
             const auto operation_index = static_cast<std::size_t>(operation->GetCode());
             if (operation_index >= operation_decompilers.size()) {
                 UNREACHABLE_MSG("Out of bounds operation: {}", operation_index);
@@ -519,7 +519,7 @@ private:
             return (this->*decompiler)(*operation);
         }
 
-        if (const auto gpr = std::get_if<GprNode>(node)) {
+        if (const auto gpr = std::get_if<GprNode>(&*node)) {
             const u32 index = gpr->GetIndex();
             if (index == Register::ZeroIndex) {
                 return "0";
@@ -527,7 +527,7 @@ private:
             return GetRegister(index);
         }
 
-        if (const auto immediate = std::get_if<ImmediateNode>(node)) {
+        if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
             const u32 value = immediate->GetValue();
             if (value < 10) {
                 // For eyecandy avoid using hex numbers on single digits
@@ -536,7 +536,7 @@ private:
             return fmt::format("utof(0x{:x}u)", immediate->GetValue());
         }
 
-        if (const auto predicate = std::get_if<PredicateNode>(node)) {
+        if (const auto predicate = std::get_if<PredicateNode>(&*node)) {
             const auto value = [&]() -> std::string {
                 switch (const auto index = predicate->GetIndex(); index) {
                 case Tegra::Shader::Pred::UnusedIndex:
@@ -553,7 +553,7 @@ private:
             return value;
         }
 
-        if (const auto abuf = std::get_if<AbufNode>(node)) {
+        if (const auto abuf = std::get_if<AbufNode>(&*node)) {
             UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry,
                                  "Physical attributes in geometry shaders are not implemented");
             if (abuf->IsPhysicalBuffer()) {
@@ -563,9 +563,9 @@ private:
             return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
         }
 
-        if (const auto cbuf = std::get_if<CbufNode>(node)) {
+        if (const auto cbuf = std::get_if<CbufNode>(&*node)) {
             const Node offset = cbuf->GetOffset();
-            if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
+            if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
                 // Direct access
                 const u32 offset_imm = immediate->GetValue();
                 ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access");
@@ -601,22 +601,22 @@ private:
             UNREACHABLE_MSG("Unmanaged offset node type");
         }
 
-        if (const auto gmem = std::get_if<GmemNode>(node)) {
+        if (const auto gmem = std::get_if<GmemNode>(&*node)) {
             const std::string real = Visit(gmem->GetRealAddress());
             const std::string base = Visit(gmem->GetBaseAddress());
             const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
             return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
         }
 
-        if (const auto lmem = std::get_if<LmemNode>(node)) {
+        if (const auto lmem = std::get_if<LmemNode>(&*node)) {
             return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
         }
 
-        if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) {
+        if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
             return GetInternalFlag(internal_flag->GetFlag());
         }
 
-        if (const auto conditional = std::get_if<ConditionalNode>(node)) {
+        if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
             // It's invalid to call conditional on nested nodes, use an operation instead
             code.AddLine("if ({}) {{", Visit(conditional->GetCondition()));
             ++code.scope;
@@ -628,7 +628,7 @@ private:
             return {};
         }
 
-        if (const auto comment = std::get_if<CommentNode>(node)) {
+        if (const auto comment = std::get_if<CommentNode>(&*node)) {
             return "// " + comment->GetText();
         }
 
@@ -636,7 +636,7 @@ private:
         return {};
     }
 
-    std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) {
+    std::string ReadAttribute(Attribute::Index attribute, u32 element, const Node& buffer = {}) {
         const auto GeometryPass = [&](std::string_view name) {
             if (stage == ShaderStage::Geometry && buffer) {
                 // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
@@ -872,7 +872,7 @@ private:
         std::string expr = ", ";
         switch (type) {
         case Type::Int:
-            if (const auto immediate = std::get_if<ImmediateNode>(operand)) {
+            if (const auto immediate = std::get_if<ImmediateNode>(&*operand)) {
                 // Inline the string as an immediate integer in GLSL (some extra arguments are
                 // required to be constant)
                 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
@@ -904,7 +904,7 @@ private:
 
         for (std::size_t index = 0; index < aoffi.size(); ++index) {
             const auto operand{aoffi.at(index)};
-            if (const auto immediate = std::get_if<ImmediateNode>(operand)) {
+            if (const auto immediate = std::get_if<ImmediateNode>(&*operand)) {
                 // Inline the string as an immediate integer in GLSL (AOFFI arguments are required
                 // to be constant by the standard).
                 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
@@ -925,17 +925,17 @@ private:
     }
 
     std::string Assign(Operation operation) {
-        const Node dest = operation[0];
-        const Node src = operation[1];
+        const Node& dest = operation[0];
+        const Node& src = operation[1];
 
         std::string target;
-        if (const auto gpr = std::get_if<GprNode>(dest)) {
+        if (const auto gpr = std::get_if<GprNode>(&*dest)) {
             if (gpr->GetIndex() == Register::ZeroIndex) {
                 // Writing to Register::ZeroIndex is a no op
                 return {};
             }
             target = GetRegister(gpr->GetIndex());
-        } else if (const auto abuf = std::get_if<AbufNode>(dest)) {
+        } else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
             UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
 
             target = [&]() -> std::string {
@@ -957,9 +957,9 @@ private:
                     return "0";
                 }
             }();
-        } else if (const auto lmem = std::get_if<LmemNode>(dest)) {
+        } else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
             target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
-        } else if (const auto gmem = std::get_if<GmemNode>(dest)) {
+        } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
             const std::string real = Visit(gmem->GetRealAddress());
             const std::string base = Visit(gmem->GetBaseAddress());
             const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
@@ -1236,12 +1236,12 @@ private:
     }
 
     std::string LogicalAssign(Operation operation) {
-        const Node dest = operation[0];
-        const Node src = operation[1];
+        const Node& dest = operation[0];
+        const Node& src = operation[1];
 
         std::string target;
 
-        if (const auto pred = std::get_if<PredicateNode>(dest)) {
+        if (const auto pred = std::get_if<PredicateNode>(&*dest)) {
             ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment");
 
             const auto index = pred->GetIndex();
@@ -1252,7 +1252,7 @@ private:
                 return {};
             }
             target = GetPredicate(index);
-        } else if (const auto flag = std::get_if<InternalFlagNode>(dest)) {
+        } else if (const auto flag = std::get_if<InternalFlagNode>(&*dest)) {
             target = GetInternalFlag(flag->GetFlag());
         }
 
@@ -1429,7 +1429,7 @@ private:
     }
 
     std::string Branch(Operation operation) {
-        const auto target = std::get_if<ImmediateNode>(operation[0]);
+        const auto target = std::get_if<ImmediateNode>(&*operation[0]);
         UNIMPLEMENTED_IF(!target);
 
         code.AddLine("jmp_to = 0x{:x}u;", target->GetValue());
@@ -1438,7 +1438,7 @@ private:
     }
 
     std::string PushFlowStack(Operation operation) {
-        const auto target = std::get_if<ImmediateNode>(operation[0]);
+        const auto target = std::get_if<ImmediateNode>(&*operation[0]);
         UNIMPLEMENTED_IF(!target);
 
         code.AddLine("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue());
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index a85fcae5a4..547883425d 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -481,13 +481,13 @@ private:
     }
 
     void VisitBasicBlock(const NodeBlock& bb) {
-        for (const Node node : bb) {
+        for (const auto& node : bb) {
             static_cast<void>(Visit(node));
         }
     }
 
-    Id Visit(Node node) {
-        if (const auto operation = std::get_if<OperationNode>(node)) {
+    Id Visit(const Node& node) {
+        if (const auto operation = std::get_if<OperationNode>(&*node)) {
             const auto operation_index = static_cast<std::size_t>(operation->GetCode());
             const auto decompiler = operation_decompilers[operation_index];
             if (decompiler == nullptr) {
@@ -495,17 +495,17 @@ private:
             }
             return (this->*decompiler)(*operation);
 
-        } else if (const auto gpr = std::get_if<GprNode>(node)) {
+        } else if (const auto gpr = std::get_if<GprNode>(&*node)) {
             const u32 index = gpr->GetIndex();
             if (index == Register::ZeroIndex) {
                 return Constant(t_float, 0.0f);
             }
             return Emit(OpLoad(t_float, registers.at(index)));
 
-        } else if (const auto immediate = std::get_if<ImmediateNode>(node)) {
+        } else if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
             return BitcastTo<Type::Float>(Constant(t_uint, immediate->GetValue()));
 
-        } else if (const auto predicate = std::get_if<PredicateNode>(node)) {
+        } else if (const auto predicate = std::get_if<PredicateNode>(&*node)) {
             const auto value = [&]() -> Id {
                 switch (const auto index = predicate->GetIndex(); index) {
                 case Tegra::Shader::Pred::UnusedIndex:
@@ -521,7 +521,7 @@ private:
             }
             return value;
 
-        } else if (const auto abuf = std::get_if<AbufNode>(node)) {
+        } else if (const auto abuf = std::get_if<AbufNode>(&*node)) {
             const auto attribute = abuf->GetIndex();
             const auto element = abuf->GetElement();
 
@@ -571,8 +571,8 @@ private:
             }
             UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
 
-        } else if (const auto cbuf = std::get_if<CbufNode>(node)) {
-            const Node offset = cbuf->GetOffset();
+        } else if (const auto cbuf = std::get_if<CbufNode>(&*node)) {
+            const Node& offset = cbuf->GetOffset();
             const Id buffer_id = constant_buffers.at(cbuf->GetIndex());
 
             Id pointer{};
@@ -584,7 +584,7 @@ private:
             } else {
                 Id buffer_index{};
                 Id buffer_element{};
-                if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
+                if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
                     // Direct access
                     const u32 offset_imm = immediate->GetValue();
                     ASSERT(offset_imm % 4 == 0);
@@ -606,7 +606,7 @@ private:
             }
             return Emit(OpLoad(t_float, pointer));
 
-        } else if (const auto gmem = std::get_if<GmemNode>(node)) {
+        } else if (const auto gmem = std::get_if<GmemNode>(&*node)) {
             const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor());
             const Id real = BitcastTo<Type::Uint>(Visit(gmem->GetRealAddress()));
             const Id base = BitcastTo<Type::Uint>(Visit(gmem->GetBaseAddress()));
@@ -616,7 +616,7 @@ private:
             return Emit(OpLoad(t_float, Emit(OpAccessChain(t_gmem_float, gmem_buffer,
                                                            Constant(t_uint, 0u), offset))));
 
-        } else if (const auto conditional = std::get_if<ConditionalNode>(node)) {
+        } else if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
             // It's invalid to call conditional on nested nodes, use an operation instead
             const Id true_label = OpLabel();
             const Id skip_label = OpLabel();
@@ -631,7 +631,7 @@ private:
             Emit(skip_label);
             return {};
 
-        } else if (const auto comment = std::get_if<CommentNode>(node)) {
+        } else if (const auto comment = std::get_if<CommentNode>(&*node)) {
             Name(Emit(OpUndef(t_void)), comment->GetText());
             return {};
         }
@@ -699,18 +699,18 @@ private:
     }
 
     Id Assign(Operation operation) {
-        const Node dest = operation[0];
-        const Node src = operation[1];
+        const Node& dest = operation[0];
+        const Node& src = operation[1];
 
         Id target{};
-        if (const auto gpr = std::get_if<GprNode>(dest)) {
+        if (const auto gpr = std::get_if<GprNode>(&*dest)) {
             if (gpr->GetIndex() == Register::ZeroIndex) {
                 // Writing to Register::ZeroIndex is a no op
                 return {};
             }
             target = registers.at(gpr->GetIndex());
 
-        } else if (const auto abuf = std::get_if<AbufNode>(dest)) {
+        } else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
             target = [&]() -> Id {
                 switch (const auto attribute = abuf->GetIndex(); attribute) {
                 case Attribute::Index::Position:
@@ -735,7 +735,7 @@ private:
                 }
             }();
 
-        } else if (const auto lmem = std::get_if<LmemNode>(dest)) {
+        } else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
             Id address = BitcastTo<Type::Uint>(Visit(lmem->GetAddress()));
             address = Emit(OpUDiv(t_uint, address, Constant(t_uint, 4)));
             target = Emit(OpAccessChain(t_prv_float, local_memory, {address}));
@@ -781,11 +781,11 @@ private:
     }
 
     Id LogicalAssign(Operation operation) {
-        const Node dest = operation[0];
-        const Node src = operation[1];
+        const Node& dest = operation[0];
+        const Node& src = operation[1];
 
         Id target{};
-        if (const auto pred = std::get_if<PredicateNode>(dest)) {
+        if (const auto pred = std::get_if<PredicateNode>(&*dest)) {
             ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment");
 
             const auto index = pred->GetIndex();
@@ -797,7 +797,7 @@ private:
             }
             target = predicates.at(index);
 
-        } else if (const auto flag = std::get_if<InternalFlagNode>(dest)) {
+        } else if (const auto flag = std::get_if<InternalFlagNode>(&*dest)) {
             target = internal_flags.at(static_cast<u32>(flag->GetFlag()));
         }
 
@@ -883,7 +883,7 @@ private:
         } else {
             u32 component_value = 0;
             if (meta->component) {
-                const auto component = std::get_if<ImmediateNode>(meta->component);
+                const auto component = std::get_if<ImmediateNode>(&*meta->component);
                 ASSERT_MSG(component, "Component is not an immediate value");
                 component_value = component->GetValue();
             }
@@ -940,7 +940,7 @@ private:
     }
 
     Id Branch(Operation operation) {
-        const auto target = std::get_if<ImmediateNode>(operation[0]);
+        const auto target = std::get_if<ImmediateNode>(&*operation[0]);
         UNIMPLEMENTED_IF(!target);
 
         Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue())));
@@ -949,7 +949,7 @@ private:
     }
 
     Id PushFlowStack(Operation operation) {
-        const auto target = std::get_if<ImmediateNode>(operation[0]);
+        const auto target = std::get_if<ImmediateNode>(&*operation[0]);
         ASSERT(target);
 
         const Id current = Emit(OpLoad(t_uint, flow_stack_top));
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp
index 2da595c0d1..a0554c97e6 100644
--- a/src/video_core/shader/decode.cpp
+++ b/src/video_core/shader/decode.cpp
@@ -11,6 +11,7 @@
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
 #include "video_core/engines/shader_header.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp
index b4859bc1e5..87d8fecaa3 100644
--- a/src/video_core/shader/decode/arithmetic.cpp
+++ b/src/video_core/shader/decode/arithmetic.cpp
@@ -6,6 +6,7 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp
index 3a29c4a466..b06cbe4414 100644
--- a/src/video_core/shader/decode/arithmetic_half.cpp
+++ b/src/video_core/shader/decode/arithmetic_half.cpp
@@ -6,6 +6,7 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/arithmetic_half_immediate.cpp b/src/video_core/shader/decode/arithmetic_half_immediate.cpp
index 5341e460f7..7bcf38f233 100644
--- a/src/video_core/shader/decode/arithmetic_half_immediate.cpp
+++ b/src/video_core/shader/decode/arithmetic_half_immediate.cpp
@@ -6,6 +6,7 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/arithmetic_immediate.cpp b/src/video_core/shader/decode/arithmetic_immediate.cpp
index 3095f2fd4c..f1875967c7 100644
--- a/src/video_core/shader/decode/arithmetic_immediate.cpp
+++ b/src/video_core/shader/decode/arithmetic_immediate.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp
index 9fd4b273e6..c8c1a7f409 100644
--- a/src/video_core/shader/decode/arithmetic_integer.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/arithmetic_integer_immediate.cpp b/src/video_core/shader/decode/arithmetic_integer_immediate.cpp
index 679ac0d4e9..73880db0ec 100644
--- a/src/video_core/shader/decode/arithmetic_integer_immediate.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer_immediate.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/bfe.cpp b/src/video_core/shader/decode/bfe.cpp
index 1ae192c6a2..e02bcd0972 100644
--- a/src/video_core/shader/decode/bfe.cpp
+++ b/src/video_core/shader/decode/bfe.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/bfi.cpp b/src/video_core/shader/decode/bfi.cpp
index 0b12a0d081..8be1119df3 100644
--- a/src/video_core/shader/decode/bfi.cpp
+++ b/src/video_core/shader/decode/bfi.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/conversion.cpp b/src/video_core/shader/decode/conversion.cpp
index b5ec9a6f55..4221f0c58a 100644
--- a/src/video_core/shader/decode/conversion.cpp
+++ b/src/video_core/shader/decode/conversion.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/ffma.cpp b/src/video_core/shader/decode/ffma.cpp
index a1d04c6e55..29be25ca32 100644
--- a/src/video_core/shader/decode/ffma.cpp
+++ b/src/video_core/shader/decode/ffma.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/float_set.cpp b/src/video_core/shader/decode/float_set.cpp
index cc522f1dec..f5013e44a7 100644
--- a/src/video_core/shader/decode/float_set.cpp
+++ b/src/video_core/shader/decode/float_set.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/float_set_predicate.cpp b/src/video_core/shader/decode/float_set_predicate.cpp
index 9d2322a1d8..2323052b0c 100644
--- a/src/video_core/shader/decode/float_set_predicate.cpp
+++ b/src/video_core/shader/decode/float_set_predicate.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp
index 755f2ec44d..48ca7a4afe 100644
--- a/src/video_core/shader/decode/half_set.cpp
+++ b/src/video_core/shader/decode/half_set.cpp
@@ -8,6 +8,7 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/half_set_predicate.cpp b/src/video_core/shader/decode/half_set_predicate.cpp
index fba44d7141..d59d15bd8a 100644
--- a/src/video_core/shader/decode/half_set_predicate.cpp
+++ b/src/video_core/shader/decode/half_set_predicate.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/hfma2.cpp b/src/video_core/shader/decode/hfma2.cpp
index a425f9eb73..c3bcf1ae9d 100644
--- a/src/video_core/shader/decode/hfma2.cpp
+++ b/src/video_core/shader/decode/hfma2.cpp
@@ -7,6 +7,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/integer_set.cpp b/src/video_core/shader/decode/integer_set.cpp
index a4cdaf74de..46e3d59057 100644
--- a/src/video_core/shader/decode/integer_set.cpp
+++ b/src/video_core/shader/decode/integer_set.cpp
@@ -4,6 +4,7 @@
 
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/integer_set_predicate.cpp b/src/video_core/shader/decode/integer_set_predicate.cpp
index a6a1fb6327..dd20775d79 100644
--- a/src/video_core/shader/decode/integer_set_predicate.cpp
+++ b/src/video_core/shader/decode/integer_set_predicate.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index e6a010a7dd..80fc0ccfc1 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -10,6 +10,7 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
@@ -169,7 +170,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
             const Node it_offset = Immediate(i * 4);
             const Node real_address =
                 Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
-            const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
+            const Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
 
             SetTemporal(bb, i, gmem);
         }
@@ -262,7 +263,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
             const Node it_offset = Immediate(i * 4);
             const Node real_address =
                 Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
-            const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
+            const Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
 
             bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1)));
         }
@@ -298,9 +299,9 @@ std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeB
 
     const Node base_address{
         TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))};
-    const auto cbuf = std::get_if<CbufNode>(base_address);
+    const auto cbuf = std::get_if<CbufNode>(&*base_address);
     ASSERT(cbuf != nullptr);
-    const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
+    const auto cbuf_offset_imm = std::get_if<ImmediateNode>(&*cbuf->GetOffset());
     ASSERT(cbuf_offset_imm != nullptr);
     const auto cbuf_offset = cbuf_offset_imm->GetValue();
 
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp
index a6c1235730..6fc07f213c 100644
--- a/src/video_core/shader/decode/other.cpp
+++ b/src/video_core/shader/decode/other.cpp
@@ -6,6 +6,7 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/predicate_set_predicate.cpp b/src/video_core/shader/decode/predicate_set_predicate.cpp
index 71844c42b7..9290d22eb0 100644
--- a/src/video_core/shader/decode/predicate_set_predicate.cpp
+++ b/src/video_core/shader/decode/predicate_set_predicate.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/predicate_set_register.cpp b/src/video_core/shader/decode/predicate_set_register.cpp
index 387491bd38..febbfeb50b 100644
--- a/src/video_core/shader/decode/predicate_set_register.cpp
+++ b/src/video_core/shader/decode/predicate_set_register.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/register_set_predicate.cpp b/src/video_core/shader/decode/register_set_predicate.cpp
index f8659e48e9..e6c9d287ef 100644
--- a/src/video_core/shader/decode/register_set_predicate.cpp
+++ b/src/video_core/shader/decode/register_set_predicate.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/shift.cpp b/src/video_core/shader/decode/shift.cpp
index 44ae87ece5..2ac16eeb06 100644
--- a/src/video_core/shader/decode/shift.cpp
+++ b/src/video_core/shader/decode/shift.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index 5b033126d8..4a356dbd4d 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -11,6 +11,7 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
@@ -291,8 +292,8 @@ const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg,
     const Node sampler_register = GetRegister(reg);
     const Node base_sampler =
         TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
-    const auto cbuf = std::get_if<CbufNode>(base_sampler);
-    const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
+    const auto cbuf = std::get_if<CbufNode>(&*base_sampler);
+    const auto cbuf_offset_imm = std::get_if<ImmediateNode>(&*cbuf->GetOffset());
     ASSERT(cbuf_offset_imm != nullptr);
     const auto cbuf_offset = cbuf_offset_imm->GetValue();
     const auto cbuf_index = cbuf->GetIndex();
@@ -388,8 +389,8 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
                                Node array, Node depth_compare, u32 bias_offset,
                                std::vector<Node> aoffi,
                                std::optional<Tegra::Shader::Register> bindless_reg) {
-    const bool is_array = array;
-    const bool is_shadow = depth_compare;
+    const auto is_array = static_cast<bool>(array);
+    const auto is_shadow = static_cast<bool>(depth_compare);
     const bool is_bindless = bindless_reg.has_value();
 
     UNIMPLEMENTED_IF_MSG((texture_type == TextureType::Texture3D && (is_array || is_shadow)) ||
diff --git a/src/video_core/shader/decode/video.cpp b/src/video_core/shader/decode/video.cpp
index cb9ab72b12..97fc6f9b12 100644
--- a/src/video_core/shader/decode/video.cpp
+++ b/src/video_core/shader/decode/video.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp
index 04a7763982..93dee77d1c 100644
--- a/src/video_core/shader/decode/xmad.cpp
+++ b/src/video_core/shader/decode/xmad.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
diff --git a/src/video_core/shader/node_helper.cpp b/src/video_core/shader/node_helper.cpp
new file mode 100644
index 0000000000..6fccbbba3a
--- /dev/null
+++ b/src/video_core/shader/node_helper.cpp
@@ -0,0 +1,99 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include <vector>
+
+#include "common/common_types.h"
+#include "video_core/shader/node_helper.h"
+#include "video_core/shader/shader_ir.h"
+
+namespace VideoCommon::Shader {
+
+Node Conditional(Node condition, std::vector<Node> code) {
+    return MakeNode<ConditionalNode>(condition, std::move(code));
+}
+
+Node Comment(std::string text) {
+    return MakeNode<CommentNode>(std::move(text));
+}
+
+Node Immediate(u32 value) {
+    return MakeNode<ImmediateNode>(value);
+}
+
+Node Immediate(s32 value) {
+    return Immediate(static_cast<u32>(value));
+}
+
+Node Immediate(f32 value) {
+    u32 integral;
+    std::memcpy(&integral, &value, sizeof(u32));
+    return Immediate(integral);
+}
+
+OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed) {
+    if (is_signed) {
+        return operation_code;
+    }
+    switch (operation_code) {
+    case OperationCode::FCastInteger:
+        return OperationCode::FCastUInteger;
+    case OperationCode::IAdd:
+        return OperationCode::UAdd;
+    case OperationCode::IMul:
+        return OperationCode::UMul;
+    case OperationCode::IDiv:
+        return OperationCode::UDiv;
+    case OperationCode::IMin:
+        return OperationCode::UMin;
+    case OperationCode::IMax:
+        return OperationCode::UMax;
+    case OperationCode::ICastFloat:
+        return OperationCode::UCastFloat;
+    case OperationCode::ICastUnsigned:
+        return OperationCode::UCastSigned;
+    case OperationCode::ILogicalShiftLeft:
+        return OperationCode::ULogicalShiftLeft;
+    case OperationCode::ILogicalShiftRight:
+        return OperationCode::ULogicalShiftRight;
+    case OperationCode::IArithmeticShiftRight:
+        return OperationCode::UArithmeticShiftRight;
+    case OperationCode::IBitwiseAnd:
+        return OperationCode::UBitwiseAnd;
+    case OperationCode::IBitwiseOr:
+        return OperationCode::UBitwiseOr;
+    case OperationCode::IBitwiseXor:
+        return OperationCode::UBitwiseXor;
+    case OperationCode::IBitwiseNot:
+        return OperationCode::UBitwiseNot;
+    case OperationCode::IBitfieldInsert:
+        return OperationCode::UBitfieldInsert;
+    case OperationCode::IBitCount:
+        return OperationCode::UBitCount;
+    case OperationCode::LogicalILessThan:
+        return OperationCode::LogicalULessThan;
+    case OperationCode::LogicalIEqual:
+        return OperationCode::LogicalUEqual;
+    case OperationCode::LogicalILessEqual:
+        return OperationCode::LogicalULessEqual;
+    case OperationCode::LogicalIGreaterThan:
+        return OperationCode::LogicalUGreaterThan;
+    case OperationCode::LogicalINotEqual:
+        return OperationCode::LogicalUNotEqual;
+    case OperationCode::LogicalIGreaterEqual:
+        return OperationCode::LogicalUGreaterEqual;
+    case OperationCode::INegate:
+        UNREACHABLE_MSG("Can't negate an unsigned integer");
+        return {};
+    case OperationCode::IAbsolute:
+        UNREACHABLE_MSG("Can't apply absolute to an unsigned integer");
+        return {};
+    default:
+        UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast<u32>(operation_code));
+        return {};
+    }
+}
+
+} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/node_helper.h b/src/video_core/shader/node_helper.h
new file mode 100644
index 0000000000..70547a03db
--- /dev/null
+++ b/src/video_core/shader/node_helper.h
@@ -0,0 +1,60 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "common/common_types.h"
+#include "video_core/shader/shader_ir.h"
+
+namespace VideoCommon::Shader {
+
+/// Creates a conditional node
+Node Conditional(Node condition, std::vector<Node> code);
+
+/// Creates a commentary node
+Node Comment(std::string text);
+
+/// Creates an u32 immediate
+Node Immediate(u32 value);
+
+/// Creates a s32 immediate
+Node Immediate(s32 value);
+
+/// Creates a f32 immediate
+Node Immediate(f32 value);
+
+/// Converts an signed operation code to an unsigned operation code
+OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed);
+
+template <typename T, typename... Args>
+Node MakeNode(Args&&... args) {
+    static_assert(std::is_convertible_v<T, NodeData>);
+    return std::make_shared<NodeData>(T(std::forward<Args>(args)...));
+}
+
+template <typename... Args>
+Node Operation(OperationCode code, Args&&... args) {
+    if constexpr (sizeof...(args) == 0) {
+        return MakeNode<OperationNode>(code);
+    } else if constexpr (std::is_convertible_v<std::tuple_element_t<0, std::tuple<Args...>>,
+                                               Meta>) {
+        return MakeNode<OperationNode>(code, std::forward<Args>(args)...);
+    } else {
+        return MakeNode<OperationNode>(code, Meta{}, std::forward<Args>(args)...);
+    }
+}
+
+template <typename... Args>
+Node SignedOperation(OperationCode code, bool is_signed, Args&&... args) {
+    return Operation(SignedToUnsignedCode(code, is_signed), std::forward<Args>(args)...);
+}
+
+} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index 8a6ee5cf5d..11b545ccac 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -9,6 +9,7 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "video_core/engines/shader_bytecode.h"
+#include "video_core/shader/node_helper.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace VideoCommon::Shader {
@@ -28,30 +29,11 @@ ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset)
 
 ShaderIR::~ShaderIR() = default;
 
-Node ShaderIR::StoreNode(NodeData&& node_data) {
-    auto store = std::make_unique<NodeData>(node_data);
-    const Node node = store.get();
-    stored_nodes.push_back(std::move(store));
-    return node;
-}
-
-Node ShaderIR::Conditional(Node condition, std::vector<Node>&& code) {
-    return StoreNode(ConditionalNode(condition, std::move(code)));
-}
-
-Node ShaderIR::Comment(std::string text) {
-    return StoreNode(CommentNode(std::move(text)));
-}
-
-Node ShaderIR::Immediate(u32 value) {
-    return StoreNode(ImmediateNode(value));
-}
-
 Node ShaderIR::GetRegister(Register reg) {
     if (reg != Register::ZeroIndex) {
         used_registers.insert(static_cast<u32>(reg));
     }
-    return StoreNode(GprNode(reg));
+    return MakeNode<GprNode>(reg);
 }
 
 Node ShaderIR::GetImmediate19(Instruction instr) {
@@ -69,7 +51,7 @@ Node ShaderIR::GetConstBuffer(u64 index_, u64 offset_) {
     const auto [entry, is_new] = used_cbufs.try_emplace(index);
     entry->second.MarkAsUsed(offset);
 
-    return StoreNode(CbufNode(index, Immediate(offset)));
+    return MakeNode<CbufNode>(index, Immediate(offset));
 }
 
 Node ShaderIR::GetConstBufferIndirect(u64 index_, u64 offset_, Node node) {
@@ -80,7 +62,7 @@ Node ShaderIR::GetConstBufferIndirect(u64 index_, u64 offset_, Node node) {
     entry->second.MarkAsUsedIndirect();
 
     const Node final_offset = Operation(OperationCode::UAdd, NO_PRECISE, node, Immediate(offset));
-    return StoreNode(CbufNode(index, final_offset));
+    return MakeNode<CbufNode>(index, final_offset);
 }
 
 Node ShaderIR::GetPredicate(u64 pred_, bool negated) {
@@ -89,7 +71,7 @@ Node ShaderIR::GetPredicate(u64 pred_, bool negated) {
         used_predicates.insert(pred);
     }
 
-    return StoreNode(PredicateNode(pred, negated));
+    return MakeNode<PredicateNode>(pred, negated);
 }
 
 Node ShaderIR::GetPredicate(bool immediate) {
@@ -98,12 +80,12 @@ Node ShaderIR::GetPredicate(bool immediate) {
 
 Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffer) {
     used_input_attributes.emplace(index);
-    return StoreNode(AbufNode(index, static_cast<u32>(element), buffer));
+    return MakeNode<AbufNode>(index, static_cast<u32>(element), buffer);
 }
 
 Node ShaderIR::GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer) {
     uses_physical_attributes = true;
-    return StoreNode(AbufNode(GetRegister(physical_address), buffer));
+    return MakeNode<AbufNode>(GetRegister(physical_address), buffer);
 }
 
 Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) {
@@ -115,11 +97,11 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff
     }
     used_output_attributes.insert(index);
 
-    return StoreNode(AbufNode(index, static_cast<u32>(element), buffer));
+    return MakeNode<AbufNode>(index, static_cast<u32>(element), buffer);
 }
 
 Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) {
-    const Node node = StoreNode(InternalFlagNode(flag));
+    const Node node = MakeNode<InternalFlagNode>(flag);
     if (negated) {
         return Operation(OperationCode::LogicalNegate, node);
     }
@@ -127,7 +109,7 @@ Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) {
 }
 
 Node ShaderIR::GetLocalMemory(Node address) {
-    return StoreNode(LmemNode(address));
+    return MakeNode<LmemNode>(address);
 }
 
 Node ShaderIR::GetTemporal(u32 id) {
@@ -393,68 +375,4 @@ Node ShaderIR::BitfieldExtract(Node value, u32 offset, u32 bits) {
                      Immediate(bits));
 }
 
-/*static*/ OperationCode ShaderIR::SignedToUnsignedCode(OperationCode operation_code,
-                                                        bool is_signed) {
-    if (is_signed) {
-        return operation_code;
-    }
-    switch (operation_code) {
-    case OperationCode::FCastInteger:
-        return OperationCode::FCastUInteger;
-    case OperationCode::IAdd:
-        return OperationCode::UAdd;
-    case OperationCode::IMul:
-        return OperationCode::UMul;
-    case OperationCode::IDiv:
-        return OperationCode::UDiv;
-    case OperationCode::IMin:
-        return OperationCode::UMin;
-    case OperationCode::IMax:
-        return OperationCode::UMax;
-    case OperationCode::ICastFloat:
-        return OperationCode::UCastFloat;
-    case OperationCode::ICastUnsigned:
-        return OperationCode::UCastSigned;
-    case OperationCode::ILogicalShiftLeft:
-        return OperationCode::ULogicalShiftLeft;
-    case OperationCode::ILogicalShiftRight:
-        return OperationCode::ULogicalShiftRight;
-    case OperationCode::IArithmeticShiftRight:
-        return OperationCode::UArithmeticShiftRight;
-    case OperationCode::IBitwiseAnd:
-        return OperationCode::UBitwiseAnd;
-    case OperationCode::IBitwiseOr:
-        return OperationCode::UBitwiseOr;
-    case OperationCode::IBitwiseXor:
-        return OperationCode::UBitwiseXor;
-    case OperationCode::IBitwiseNot:
-        return OperationCode::UBitwiseNot;
-    case OperationCode::IBitfieldInsert:
-        return OperationCode::UBitfieldInsert;
-    case OperationCode::IBitCount:
-        return OperationCode::UBitCount;
-    case OperationCode::LogicalILessThan:
-        return OperationCode::LogicalULessThan;
-    case OperationCode::LogicalIEqual:
-        return OperationCode::LogicalUEqual;
-    case OperationCode::LogicalILessEqual:
-        return OperationCode::LogicalULessEqual;
-    case OperationCode::LogicalIGreaterThan:
-        return OperationCode::LogicalUGreaterThan;
-    case OperationCode::LogicalINotEqual:
-        return OperationCode::LogicalUNotEqual;
-    case OperationCode::LogicalIGreaterEqual:
-        return OperationCode::LogicalUGreaterEqual;
-    case OperationCode::INegate:
-        UNREACHABLE_MSG("Can't negate an unsigned integer");
-        return {};
-    case OperationCode::IAbsolute:
-        UNREACHABLE_MSG("Can't apply absolute to an unsigned integer");
-        return {};
-    default:
-        UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast<u32>(operation_code));
-        return {};
-    }
-}
-
 } // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index ff7472e30a..1b2745c238 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -38,7 +38,7 @@ using ProgramCode = std::vector<u64>;
 using NodeData =
     std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode, InternalFlagNode,
                  PredicateNode, AbufNode, CbufNode, LmemNode, GmemNode, CommentNode>;
-using Node = const NodeData*;
+using Node = std::shared_ptr<NodeData>;
 using Node4 = std::array<Node, 4>;
 using NodeBlock = std::vector<Node>;
 
@@ -342,23 +342,20 @@ using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>;
 /// Holds any kind of operation that can be done in the IR
 class OperationNode final {
 public:
-    explicit OperationNode(OperationCode code) : code{code} {}
+    explicit OperationNode(OperationCode code) : OperationNode(code, Meta{}) {}
 
-    explicit OperationNode(OperationCode code, Meta&& meta) : code{code}, meta{std::move(meta)} {}
+    explicit OperationNode(OperationCode code, Meta meta)
+        : OperationNode(code, meta, std::vector<Node>{}) {}
 
-    template <typename... T>
-    explicit OperationNode(OperationCode code, const T*... operands)
-        : OperationNode(code, {}, operands...) {}
+    explicit OperationNode(OperationCode code, std::vector<Node> operands)
+        : OperationNode(code, Meta{}, std::move(operands)) {}
 
-    template <typename... T>
-    explicit OperationNode(OperationCode code, Meta&& meta, const T*... operands_)
-        : code{code}, meta{std::move(meta)}, operands{operands_...} {}
+    explicit OperationNode(OperationCode code, Meta meta, std::vector<Node> operands)
+        : code{code}, meta{std::move(meta)}, operands{std::move(operands)} {}
 
-    explicit OperationNode(OperationCode code, Meta&& meta, std::vector<Node>&& operands)
-        : code{code}, meta{meta}, operands{std::move(operands)} {}
-
-    explicit OperationNode(OperationCode code, std::vector<Node>&& operands)
-        : code{code}, operands{std::move(operands)} {}
+    template <typename... Args>
+    explicit OperationNode(OperationCode code, Meta meta, Args&&... operands)
+        : code{code}, meta{std::move(meta)}, operands{operands...} {}
 
     OperationCode GetCode() const {
         return code;
@@ -372,13 +369,13 @@ public:
         return operands.size();
     }
 
-    Node operator[](std::size_t operand_index) const {
+    const Node& operator[](std::size_t operand_index) const {
         return operands.at(operand_index);
     }
 
 private:
-    const OperationCode code;
-    const Meta meta;
+    OperationCode code{};
+    Meta meta{};
     std::vector<Node> operands;
 };
 
@@ -463,13 +460,12 @@ private:
 class AbufNode final {
 public:
     // Initialize for standard attributes (index is explicit).
-    explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
-                                Node buffer = {})
-        : buffer{buffer}, index{index}, element{element} {}
+    explicit AbufNode(Tegra::Shader::Attribute::Index index, u32 element, Node buffer = {})
+        : buffer{std::move(buffer)}, index{index}, element{element} {}
 
     // Initialize for physical attributes (index is a variable value).
-    explicit constexpr AbufNode(Node physical_address, Node buffer = {})
-        : physical_address{physical_address}, buffer{buffer} {}
+    explicit AbufNode(Node physical_address, Node buffer = {})
+        : physical_address{physical_address}, buffer{std::move(buffer)} {}
 
     Tegra::Shader::Attribute::Index GetIndex() const {
         return index;
@@ -484,16 +480,16 @@ public:
     }
 
     bool IsPhysicalBuffer() const {
-        return physical_address != nullptr;
+        return static_cast<bool>(physical_address);
     }
 
-    Node GetPhysicalAddress() const {
+    const Node& GetPhysicalAddress() const {
         return physical_address;
     }
 
 private:
-    Node physical_address{};
-    Node buffer{};
+    Node physical_address;
+    Node buffer;
     Tegra::Shader::Attribute::Index index{};
     u32 element{};
 };
@@ -501,7 +497,7 @@ private:
 /// Constant buffer node, usually mapped to uniform buffers in GLSL
 class CbufNode final {
 public:
-    explicit constexpr CbufNode(u32 index, Node offset) : index{index}, offset{offset} {}
+    explicit CbufNode(u32 index, Node offset) : index{index}, offset{offset} {}
 
     u32 GetIndex() const {
         return index;
@@ -519,7 +515,7 @@ private:
 /// Local memory node
 class LmemNode final {
 public:
-    explicit constexpr LmemNode(Node address) : address{address} {}
+    explicit LmemNode(Node address) : address{address} {}
 
     Node GetAddress() const {
         return address;
@@ -532,8 +528,7 @@ private:
 /// Global memory node
 class GmemNode final {
 public:
-    explicit constexpr GmemNode(Node real_address, Node base_address,
-                                const GlobalMemoryBase& descriptor)
+    explicit GmemNode(Node real_address, Node base_address, const GlobalMemoryBase& descriptor)
         : real_address{real_address}, base_address{base_address}, descriptor{descriptor} {}
 
     Node GetRealAddress() const {
@@ -663,26 +658,6 @@ private:
     u32 DecodeXmad(NodeBlock& bb, u32 pc);
     u32 DecodeOther(NodeBlock& bb, u32 pc);
 
-    /// Internalizes node's data and returns a managed pointer to a clone of that node
-    Node StoreNode(NodeData&& node_data);
-
-    /// Creates a conditional node
-    Node Conditional(Node condition, std::vector<Node>&& code);
-    /// Creates a commentary
-    Node Comment(std::string text);
-    /// Creates an u32 immediate
-    Node Immediate(u32 value);
-    /// Creates a s32 immediate
-    Node Immediate(s32 value) {
-        return Immediate(static_cast<u32>(value));
-    }
-    /// Creates a f32 immediate
-    Node Immediate(f32 value) {
-        u32 integral;
-        std::memcpy(&integral, &value, sizeof(u32));
-        return Immediate(integral);
-    }
-
     /// Generates a node for a passed register.
     Node GetRegister(Tegra::Shader::Register reg);
     /// Generates a node representing a 19-bit immediate value
@@ -827,37 +802,6 @@ private:
     std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(
         NodeBlock& bb, Tegra::Shader::Instruction instr, bool is_write);
 
-    template <typename... T>
-    Node Operation(OperationCode code, const T*... operands) {
-        return StoreNode(OperationNode(code, operands...));
-    }
-
-    template <typename... T>
-    Node Operation(OperationCode code, Meta&& meta, const T*... operands) {
-        return StoreNode(OperationNode(code, std::move(meta), operands...));
-    }
-
-    Node Operation(OperationCode code, std::vector<Node>&& operands) {
-        return StoreNode(OperationNode(code, std::move(operands)));
-    }
-
-    Node Operation(OperationCode code, Meta&& meta, std::vector<Node>&& operands) {
-        return StoreNode(OperationNode(code, std::move(meta), std::move(operands)));
-    }
-
-    template <typename... T>
-    Node SignedOperation(OperationCode code, bool is_signed, const T*... operands) {
-        return StoreNode(OperationNode(SignedToUnsignedCode(code, is_signed), operands...));
-    }
-
-    template <typename... T>
-    Node SignedOperation(OperationCode code, bool is_signed, Meta&& meta, const T*... operands) {
-        return StoreNode(
-            OperationNode(SignedToUnsignedCode(code, is_signed), std::move(meta), operands...));
-    }
-
-    static OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed);
-
     const ProgramCode& program_code;
     const u32 main_offset;
 
@@ -868,8 +812,6 @@ private:
     std::map<u32, NodeBlock> basic_blocks;
     NodeBlock global_code;
 
-    std::vector<std::unique_ptr<NodeData>> stored_nodes;
-
     std::set<u32> used_registers;
     std::set<Tegra::Shader::Pred> used_predicates;
     std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index 19ede1eb94..fc957d980e 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -16,12 +16,12 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
                                    OperationCode operation_code) {
     for (; cursor >= 0; --cursor) {
         const Node node = code.at(cursor);
-        if (const auto operation = std::get_if<OperationNode>(node)) {
+        if (const auto operation = std::get_if<OperationNode>(&*node)) {
             if (operation->GetCode() == operation_code) {
                 return {node, cursor};
             }
         }
-        if (const auto conditional = std::get_if<ConditionalNode>(node)) {
+        if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
             const auto& conditional_code = conditional->GetCode();
             const auto [found, internal_cursor] = FindOperation(
                 conditional_code, static_cast<s64>(conditional_code.size() - 1), operation_code);
@@ -35,11 +35,11 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
 } // namespace
 
 Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const {
-    if (const auto cbuf = std::get_if<CbufNode>(tracked)) {
+    if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
         // Cbuf found, but it has to be immediate
         return std::holds_alternative<ImmediateNode>(*cbuf->GetOffset()) ? tracked : nullptr;
     }
-    if (const auto gpr = std::get_if<GprNode>(tracked)) {
+    if (const auto gpr = std::get_if<GprNode>(&*tracked)) {
         if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) {
             return nullptr;
         }
@@ -51,7 +51,7 @@ Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const
         }
         return TrackCbuf(source, code, new_cursor);
     }
-    if (const auto operation = std::get_if<OperationNode>(tracked)) {
+    if (const auto operation = std::get_if<OperationNode>(&*tracked)) {
         for (std::size_t i = 0; i < operation->GetOperandsCount(); ++i) {
             if (const auto found = TrackCbuf((*operation)[i], code, cursor)) {
                 // Cbuf found in operand
@@ -60,7 +60,7 @@ Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const
         }
         return nullptr;
     }
-    if (const auto conditional = std::get_if<ConditionalNode>(tracked)) {
+    if (const auto conditional = std::get_if<ConditionalNode>(&*tracked)) {
         const auto& conditional_code = conditional->GetCode();
         return TrackCbuf(tracked, conditional_code, static_cast<s64>(conditional_code.size()));
     }
@@ -75,7 +75,7 @@ std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code,
     if (!found) {
         return {};
     }
-    if (const auto immediate = std::get_if<ImmediateNode>(found)) {
+    if (const auto immediate = std::get_if<ImmediateNode>(&*found)) {
         return immediate->GetValue();
     }
     return {};
@@ -88,11 +88,11 @@ std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeB
         if (!found_node) {
             return {};
         }
-        const auto operation = std::get_if<OperationNode>(found_node);
+        const auto operation = std::get_if<OperationNode>(&*found_node);
         ASSERT(operation);
 
         const auto& target = (*operation)[0];
-        if (const auto gpr_target = std::get_if<GprNode>(target)) {
+        if (const auto gpr_target = std::get_if<GprNode>(&*target)) {
             if (gpr_target->GetIndex() == tracked->GetIndex()) {
                 return {(*operation)[1], new_cursor};
             }