From f92921a6d118aa9c6acdb3ecaa3cd61a19fe341e Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Thu, 15 Jun 2023 17:31:53 -0300
Subject: [PATCH] Implement Load/Store Local/Shared and Atomic shared using new
 instructions (#5241)

* Implement Load/Store Local/Shared and Atomic shared using new instructions

* Remove now unused code

* Fix base offset register overwrite

* Fix missing storage buffer set index when generating GLSL for Vulkan

* Shader cache version bump

* Remove more unused code

* Some PR feedback
---
 .../Shader/DiskCache/DiskCacheHostStorage.cs  |   2 +-
 .../CodeGen/Glsl/Declarations.cs              |  75 +++-----
 .../CodeGen/Glsl/DefaultNames.cs              |   3 -
 .../AtomicMinMaxS32Shared.glsl                |  21 ---
 .../HelperFunctions/HelperFunctionNames.cs    |   8 -
 .../HelperFunctions/StoreSharedSmallInt.glsl  |  23 ---
 .../CodeGen/Glsl/Instructions/InstGen.cs      |  37 +---
 .../Glsl/Instructions/InstGenHelper.cs        |   8 -
 .../Glsl/Instructions/InstGenMemory.cs        |  86 ++-------
 .../CodeGen/Glsl/OperandManager.cs            |  15 +-
 .../CodeGen/Spirv/CodeGenContext.cs           |   5 +-
 .../CodeGen/Spirv/Declarations.cs             |  60 ++----
 .../CodeGen/Spirv/Instructions.cs             | 176 +++---------------
 .../Instructions/InstEmitMemory.cs            | 109 +++++------
 .../IntermediateRepresentation/Instruction.cs |   6 -
 .../IntermediateRepresentation/StorageKind.cs |  11 +-
 .../Ryujinx.Graphics.Shader.csproj            |   2 -
 .../StructuredIr/HelperFunctionsMask.cs       |  18 +-
 .../StructuredIr/InstructionInfo.cs           |   6 -
 .../StructuredIr/MemoryDefinition.cs          |  18 ++
 .../StructuredIr/ShaderProperties.cs          |  22 +++
 .../StructuredIr/StructuredProgram.cs         |  11 --
 .../Translation/EmitterContextInsts.cs        |  40 +---
 .../Translation/HelperFunctionManager.cs      | 109 ++++++++++-
 .../Translation/HelperFunctionName.cs         |   4 +
 .../Optimizations/GlobalToStorage.cs          |  32 ++--
 .../Translation/ResourceManager.cs            |  25 ++-
 .../Translation/Rewriter.cs                   |  92 +++++++++
 .../Translation/ShaderConfig.cs               |  16 +-
 .../Translation/Translator.cs                 |   2 +-
 30 files changed, 475 insertions(+), 567 deletions(-)
 delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl
 delete mode 100644 src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl
 create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs

diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index 153a2e8c15..5cfbfd3864 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
         private const ushort FileFormatVersionMajor = 1;
         private const ushort FileFormatVersionMinor = 2;
         private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
-        private const uint CodeGenVersion = 5080;
+        private const uint CodeGenVersion = 5241;
 
         private const string SharedTocFileName = "shared.toc";
         private const string SharedDataFileName = "shared.data";
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index 958f1cef39..08e8eb1955 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -71,40 +71,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
             context.AppendLine($"const int {DefaultNames.UndefinedName} = 0;");
             context.AppendLine();
 
-            if (context.Config.Stage == ShaderStage.Compute)
-            {
-                int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4);
-
-                if (localMemorySize != 0)
-                {
-                    string localMemorySizeStr = NumberFormatter.FormatInt(localMemorySize);
-
-                    context.AppendLine($"uint {DefaultNames.LocalMemoryName}[{localMemorySizeStr}];");
-                    context.AppendLine();
-                }
-
-                int sharedMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeSharedMemorySize(), 4);
-
-                if (sharedMemorySize != 0)
-                {
-                    string sharedMemorySizeStr = NumberFormatter.FormatInt(sharedMemorySize);
-
-                    context.AppendLine($"shared uint {DefaultNames.SharedMemoryName}[{sharedMemorySizeStr}];");
-                    context.AppendLine();
-                }
-            }
-            else if (context.Config.LocalMemorySize != 0)
-            {
-                int localMemorySize = BitUtils.DivRoundUp(context.Config.LocalMemorySize, 4);
-
-                string localMemorySizeStr = NumberFormatter.FormatInt(localMemorySize);
-
-                context.AppendLine($"uint {DefaultNames.LocalMemoryName}[{localMemorySizeStr}];");
-                context.AppendLine();
-            }
-
             DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
             DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
+            DeclareMemories(context, context.Config.Properties.LocalMemories.Values, isShared: false);
+            DeclareMemories(context, context.Config.Properties.SharedMemories.Values, isShared: true);
 
             var textureDescriptors = context.Config.GetTextureDescriptors();
             if (textureDescriptors.Length != 0)
@@ -238,11 +208,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
                 context.AppendLine();
             }
 
-            if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0)
-            {
-                AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl");
-            }
-
             if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0)
             {
                 AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl");
@@ -273,11 +238,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
                 AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl");
             }
 
-            if ((info.HelperFunctionsMask & HelperFunctionsMask.StoreSharedSmallInt) != 0)
-            {
-                AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl");
-            }
-
             if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0)
             {
                 AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl");
@@ -358,7 +318,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
                     _ => "std430"
                 };
 
-                context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) {declType} _{buffer.Name}");
+                string set = string.Empty;
+
+                if (context.Config.Options.TargetApi == TargetApi.Vulkan)
+                {
+                    set = $"set = {buffer.Set}, ";
+                }
+
+                context.AppendLine($"layout ({set}binding = {buffer.Binding}, {layout}) {declType} _{buffer.Name}");
                 context.EnterScope();
 
                 foreach (StructureField field in buffer.Type.Fields)
@@ -391,6 +358,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
             }
         }
 
+        private static void DeclareMemories(CodeGenContext context, IEnumerable<MemoryDefinition> memories, bool isShared)
+        {
+            string prefix = isShared ? "shared " : string.Empty;
+
+            foreach (MemoryDefinition memory in memories)
+            {
+                string typeName = GetVarTypeName(context, memory.Type & ~AggregateType.Array);
+
+                if (memory.ArrayLength > 0)
+                {
+                    string arraySize = memory.ArrayLength.ToString(CultureInfo.InvariantCulture);
+
+                    context.AppendLine($"{prefix}{typeName} {memory.Name}[{arraySize}];");
+                }
+                else
+                {
+                    context.AppendLine($"{prefix}{typeName} {memory.Name}[];");
+                }
+            }
+        }
+
         private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
         {
             int arraySize = 0;
@@ -717,7 +705,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
             string code = EmbeddedResources.ReadAllText(filename);
 
             code = code.Replace("\t", CodeGenContext.Tab);
-            code = code.Replace("$SHARED_MEM$", DefaultNames.SharedMemoryName);
 
             if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot())
             {
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
index 5ee8259cfa..e909dcf046 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
@@ -11,9 +11,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
         public const string IAttributePrefix = "in_attr";
         public const string OAttributePrefix = "out_attr";
 
-        public const string LocalMemoryName  = "local_mem";
-        public const string SharedMemoryName = "shared_mem";
-
         public const string ArgumentNamePrefix = "a";
 
         public const string UndefinedName = "undef";
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl
deleted file mode 100644
index 82b76bccf2..0000000000
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl
+++ /dev/null
@@ -1,21 +0,0 @@
-int Helper_AtomicMaxS32(int offset, int value)
-{
-    uint oldValue, newValue;
-    do
-    {
-        oldValue = $SHARED_MEM$[offset];
-        newValue = uint(max(int(oldValue), value));
-    } while (atomicCompSwap($SHARED_MEM$[offset], oldValue, newValue) != oldValue);
-    return int(oldValue);
-}
-
-int Helper_AtomicMinS32(int offset, int value)
-{
-    uint oldValue, newValue;
-    do
-    {
-        oldValue = $SHARED_MEM$[offset];
-        newValue = uint(min(int(oldValue), value));
-    } while (atomicCompSwap($SHARED_MEM$[offset], oldValue, newValue) != oldValue);
-    return int(oldValue);
-}
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs
index 54f35b15aa..21c435475f 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs
@@ -2,9 +2,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 {
     static class HelperFunctionNames
     {
-        public static string AtomicMaxS32 = "Helper_AtomicMaxS32";
-        public static string AtomicMinS32 = "Helper_AtomicMinS32";
-
         public static string MultiplyHighS32 = "Helper_MultiplyHighS32";
         public static string MultiplyHighU32 = "Helper_MultiplyHighU32";
 
@@ -13,10 +10,5 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
         public static string ShuffleUp   = "Helper_ShuffleUp";
         public static string ShuffleXor  = "Helper_ShuffleXor";
         public static string SwizzleAdd  = "Helper_SwizzleAdd";
-
-        public static string StoreShared16  = "Helper_StoreShared16";
-        public static string StoreShared8   = "Helper_StoreShared8";
-        public static string StoreStorage16 = "Helper_StoreStorage16";
-        public static string StoreStorage8  = "Helper_StoreStorage8";
     }
 }
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl
deleted file mode 100644
index 2f57b5ff64..0000000000
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl
+++ /dev/null
@@ -1,23 +0,0 @@
-void Helper_StoreShared16(int offset, uint value)
-{
-    int wordOffset = offset >> 2;
-    int bitOffset = (offset & 3) * 8;
-    uint oldValue, newValue;
-    do
-    {
-        oldValue = $SHARED_MEM$[wordOffset];
-        newValue = bitfieldInsert(oldValue, value, bitOffset, 16);
-    } while (atomicCompSwap($SHARED_MEM$[wordOffset], oldValue, newValue) != oldValue);
-}
-
-void Helper_StoreShared8(int offset, uint value)
-{
-    int wordOffset = offset >> 2;
-    int bitOffset = (offset & 3) * 8;
-    uint oldValue, newValue;
-    do
-    {
-        oldValue = $SHARED_MEM$[wordOffset];
-        newValue = bitfieldInsert(oldValue, value, bitOffset, 8);
-    } while (atomicCompSwap($SHARED_MEM$[wordOffset], oldValue, newValue) != oldValue);
-}
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
index 01d8a6e7a7..b2577a999f 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
@@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
 
                 string args = string.Empty;
 
-                if (atomic && operation.StorageKind == StorageKind.StorageBuffer)
+                if (atomic && (operation.StorageKind == StorageKind.StorageBuffer || operation.StorageKind == StorageKind.SharedMemory))
                 {
                     args = GenerateLoadOrStore(context, operation, isStore: false);
 
@@ -81,23 +81,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
                         args += ", " + GetSoureExpr(context, operation.GetSource(argIndex), dstType);
                     }
                 }
-                else if (atomic && operation.StorageKind == StorageKind.SharedMemory)
-                {
-                    args = LoadShared(context, operation);
-
-                    // For shared memory access, the second argument is unused and should be ignored.
-                    // It is there to make both storage and shared access have the same number of arguments.
-                    // For storage, both inputs are consumed when the argument index is 0, so we should skip it here.
-
-                    for (int argIndex = 2; argIndex < arity; argIndex++)
-                    {
-                        args += ", ";
-
-                        AggregateType dstType = GetSrcVarType(inst, argIndex);
-
-                        args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
-                    }
-                }
                 else
                 {
                     for (int argIndex = 0; argIndex < arity; argIndex++)
@@ -179,12 +162,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
                     case Instruction.Load:
                         return Load(context, operation);
 
-                    case Instruction.LoadLocal:
-                        return LoadLocal(context, operation);
-
-                    case Instruction.LoadShared:
-                        return LoadShared(context, operation);
-
                     case Instruction.Lod:
                         return Lod(context, operation);
 
@@ -200,18 +177,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
                     case Instruction.Store:
                         return Store(context, operation);
 
-                    case Instruction.StoreLocal:
-                        return StoreLocal(context, operation);
-
-                    case Instruction.StoreShared:
-                        return StoreShared(context, operation);
-
-                    case Instruction.StoreShared16:
-                        return StoreShared16(context, operation);
-
-                    case Instruction.StoreShared8:
-                        return StoreShared8(context, operation);
-
                     case Instruction.TextureSample:
                         return TextureSample(context, operation);
 
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
index f42d98986e..8b0b744ad8 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
@@ -17,9 +17,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
             Add(Instruction.AtomicAdd,                InstType.AtomicBinary,   "atomicAdd");
             Add(Instruction.AtomicAnd,                InstType.AtomicBinary,   "atomicAnd");
             Add(Instruction.AtomicCompareAndSwap,     InstType.AtomicTernary,  "atomicCompSwap");
-            Add(Instruction.AtomicMaxS32,             InstType.CallTernary,    HelperFunctionNames.AtomicMaxS32);
             Add(Instruction.AtomicMaxU32,             InstType.AtomicBinary,   "atomicMax");
-            Add(Instruction.AtomicMinS32,             InstType.CallTernary,    HelperFunctionNames.AtomicMinS32);
             Add(Instruction.AtomicMinU32,             InstType.AtomicBinary,   "atomicMin");
             Add(Instruction.AtomicOr,                 InstType.AtomicBinary,   "atomicOr");
             Add(Instruction.AtomicSwap,               InstType.AtomicBinary,   "atomicExchange");
@@ -83,8 +81,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
             Add(Instruction.ImageAtomic,              InstType.Special);
             Add(Instruction.IsNan,                    InstType.CallUnary,      "isnan");
             Add(Instruction.Load,                     InstType.Special);
-            Add(Instruction.LoadLocal,                InstType.Special);
-            Add(Instruction.LoadShared,               InstType.Special);
             Add(Instruction.Lod,                      InstType.Special);
             Add(Instruction.LogarithmB2,              InstType.CallUnary,      "log2");
             Add(Instruction.LogicalAnd,               InstType.OpBinaryCom,    "&&",              9);
@@ -118,10 +114,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
             Add(Instruction.Sine,                     InstType.CallUnary,      "sin");
             Add(Instruction.SquareRoot,               InstType.CallUnary,      "sqrt");
             Add(Instruction.Store,                    InstType.Special);
-            Add(Instruction.StoreLocal,               InstType.Special);
-            Add(Instruction.StoreShared,              InstType.Special);
-            Add(Instruction.StoreShared16,            InstType.Special);
-            Add(Instruction.StoreShared8,             InstType.Special);
             Add(Instruction.Subtract,                 InstType.OpBinary,       "-",               2);
             Add(Instruction.SwizzleAdd,               InstType.CallTernary,    HelperFunctionNames.SwizzleAdd);
             Add(Instruction.TextureSample,            InstType.Special);
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
index c8084d9ddc..99376ffb23 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
@@ -191,25 +191,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
             return GenerateLoadOrStore(context, operation, isStore: false);
         }
 
-        public static string LoadLocal(CodeGenContext context, AstOperation operation)
-        {
-            return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
-        }
-
-        public static string LoadShared(CodeGenContext context, AstOperation operation)
-        {
-            return LoadLocalOrShared(context, operation, DefaultNames.SharedMemoryName);
-        }
-
-        private static string LoadLocalOrShared(CodeGenContext context, AstOperation operation, string arrayName)
-        {
-            IAstNode src1 = operation.GetSource(0);
-
-            string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
-
-            return $"{arrayName}[{offsetExpr}]";
-        }
-
         public static string Lod(CodeGenContext context, AstOperation operation)
         {
             AstTextureOperation texOp = (AstTextureOperation)operation;
@@ -263,58 +244,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
             return GenerateLoadOrStore(context, operation, isStore: true);
         }
 
-        public static string StoreLocal(CodeGenContext context, AstOperation operation)
-        {
-            return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
-        }
-
-        public static string StoreShared(CodeGenContext context, AstOperation operation)
-        {
-            return StoreLocalOrShared(context, operation, DefaultNames.SharedMemoryName);
-        }
-
-        private static string StoreLocalOrShared(CodeGenContext context, AstOperation operation, string arrayName)
-        {
-            IAstNode src1 = operation.GetSource(0);
-            IAstNode src2 = operation.GetSource(1);
-
-            string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
-
-            AggregateType srcType = OperandManager.GetNodeDestType(context, src2);
-
-            string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32);
-
-            return $"{arrayName}[{offsetExpr}] = {src}";
-        }
-
-        public static string StoreShared16(CodeGenContext context, AstOperation operation)
-        {
-            IAstNode src1 = operation.GetSource(0);
-            IAstNode src2 = operation.GetSource(1);
-
-            string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
-
-            AggregateType srcType = OperandManager.GetNodeDestType(context, src2);
-
-            string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32);
-
-            return $"{HelperFunctionNames.StoreShared16}({offsetExpr}, {src})";
-        }
-
-        public static string StoreShared8(CodeGenContext context, AstOperation operation)
-        {
-            IAstNode src1 = operation.GetSource(0);
-            IAstNode src2 = operation.GetSource(1);
-
-            string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
-
-            AggregateType srcType = OperandManager.GetNodeDestType(context, src2);
-
-            string src = TypeConversion.ReinterpretCast(context, src2, srcType, AggregateType.U32);
-
-            return $"{HelperFunctionNames.StoreShared8}({offsetExpr}, {src})";
-        }
-
         public static string TextureSample(CodeGenContext context, AstOperation operation)
         {
             AstTextureOperation texOp = (AstTextureOperation)operation;
@@ -675,6 +604,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
                     varType = field.Type;
                     break;
 
+                case StorageKind.LocalMemory:
+                case StorageKind.SharedMemory:
+                    if (!(operation.GetSource(srcIndex++) is AstOperand bindingId) || bindingId.Type != OperandType.Constant)
+                    {
+                        throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
+                    }
+
+                    MemoryDefinition memory = storageKind == StorageKind.LocalMemory
+                        ? context.Config.Properties.LocalMemories[bindingId.Value]
+                        : context.Config.Properties.SharedMemories[bindingId.Value];
+
+                    varName = memory.Name;
+                    varType = memory.Type;
+                    break;
+
                 case StorageKind.Input:
                 case StorageKind.InputPerPatch:
                 case StorageKind.Output:
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
index 4fd1d17c47..4f6ca642ca 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
@@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 
             if (node is AstOperation operation)
             {
-                if (operation.Inst == Instruction.Load)
+                if (operation.Inst == Instruction.Load || operation.Inst.IsAtomic())
                 {
                     switch (operation.StorageKind)
                     {
@@ -136,6 +136,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 
                             return field.Type & AggregateType.ElementTypeMask;
 
+                        case StorageKind.LocalMemory:
+                        case StorageKind.SharedMemory:
+                            if (!(operation.GetSource(0) is AstOperand bindingId) || bindingId.Type != OperandType.Constant)
+                            {
+                                throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
+                            }
+
+                            MemoryDefinition memory = operation.StorageKind == StorageKind.LocalMemory
+                                ? context.Config.Properties.LocalMemories[bindingId.Value]
+                                : context.Config.Properties.SharedMemories[bindingId.Value];
+
+                            return memory.Type & AggregateType.ElementTypeMask;
+
                         case StorageKind.Input:
                         case StorageKind.InputPerPatch:
                         case StorageKind.Output:
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
index 1f5167e667..a4daaa67e7 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
@@ -25,8 +25,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 
         public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>();
         public Dictionary<int, Instruction> StorageBuffers { get; } = new Dictionary<int, Instruction>();
-        public Instruction LocalMemory { get; set; }
-        public Instruction SharedMemory { get; set; }
+        public Dictionary<int, Instruction> LocalMemories { get; } = new Dictionary<int, Instruction>();
+        public Dictionary<int, Instruction> SharedMemories { get; } = new Dictionary<int, Instruction>();
         public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>();
         public Dictionary<TextureMeta, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<TextureMeta, (Instruction, Instruction, Instruction)>();
         public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>();
@@ -35,7 +35,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
         public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>();
         public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>();
 
-        public Instruction CoordTemp { get; set; }
         public StructuredFunction CurrentFunction { get; set; }
         private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
         private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>();
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
index eb2db514d3..59acea4f62 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
@@ -6,7 +6,6 @@ using Spv.Generator;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
-using System.Linq;
 using System.Numerics;
 using static Spv.Specification;
 using SpvInstruction = Spv.Generator.Instruction;
@@ -44,13 +43,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
                 context.AddLocalVariable(spvLocal);
                 context.DeclareLocal(local, spvLocal);
             }
-
-            var ivector2Type = context.TypeVector(context.TypeS32(), 2);
-            var coordTempPointerType = context.TypePointer(StorageClass.Function, ivector2Type);
-            var coordTemp = context.Variable(coordTempPointerType, StorageClass.Function);
-
-            context.AddLocalVariable(coordTemp);
-            context.CoordTemp = coordTemp;
         }
 
         public static void DeclareLocalForArgs(CodeGenContext context, List<StructuredFunction> functions)
@@ -77,54 +69,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 
         public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
         {
-            if (context.Config.Stage == ShaderStage.Compute)
-            {
-                int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4);
-
-                if (localMemorySize != 0)
-                {
-                    DeclareLocalMemory(context, localMemorySize);
-                }
-
-                int sharedMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeSharedMemorySize(), 4);
-
-                if (sharedMemorySize != 0)
-                {
-                    DeclareSharedMemory(context, sharedMemorySize);
-                }
-            }
-            else if (context.Config.LocalMemorySize != 0)
-            {
-                int localMemorySize = BitUtils.DivRoundUp(context.Config.LocalMemorySize, 4);
-                DeclareLocalMemory(context, localMemorySize);
-            }
-
             DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
             DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
+            DeclareMemories(context, context.Config.Properties.LocalMemories, context.LocalMemories, StorageClass.Private);
+            DeclareMemories(context, context.Config.Properties.SharedMemories, context.SharedMemories, StorageClass.Workgroup);
             DeclareSamplers(context, context.Config.GetTextureDescriptors());
             DeclareImages(context, context.Config.GetImageDescriptors());
             DeclareInputsAndOutputs(context, info);
         }
 
-        private static void DeclareLocalMemory(CodeGenContext context, int size)
+        private static void DeclareMemories(
+            CodeGenContext context,
+            IReadOnlyDictionary<int, MemoryDefinition> memories,
+            Dictionary<int, SpvInstruction> dict,
+            StorageClass storage)
         {
-            context.LocalMemory = DeclareMemory(context, StorageClass.Private, size);
-        }
+            foreach ((int id, MemoryDefinition memory) in memories)
+            {
+                var pointerType = context.TypePointer(storage, context.GetType(memory.Type, memory.ArrayLength));
+                var variable = context.Variable(pointerType, storage);
 
-        private static void DeclareSharedMemory(CodeGenContext context, int size)
-        {
-            context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size);
-        }
+                context.AddGlobalVariable(variable);
 
-        private static SpvInstruction DeclareMemory(CodeGenContext context, StorageClass storage, int size)
-        {
-            var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size));
-            var pointerType = context.TypePointer(storage, arrayType);
-            var variable = context.Variable(pointerType, storage);
-
-            context.AddGlobalVariable(variable);
-
-            return variable;
+                dict.Add(id, variable);
+            }
         }
 
         private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
index 6c11575250..b451f7a48d 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
@@ -97,8 +97,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             Add(Instruction.ImageStore,               GenerateImageStore);
             Add(Instruction.IsNan,                    GenerateIsNan);
             Add(Instruction.Load,                     GenerateLoad);
-            Add(Instruction.LoadLocal,                GenerateLoadLocal);
-            Add(Instruction.LoadShared,               GenerateLoadShared);
             Add(Instruction.Lod,                      GenerateLod);
             Add(Instruction.LogarithmB2,              GenerateLogarithmB2);
             Add(Instruction.LogicalAnd,               GenerateLogicalAnd);
@@ -132,10 +130,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             Add(Instruction.Sine,                     GenerateSine);
             Add(Instruction.SquareRoot,               GenerateSquareRoot);
             Add(Instruction.Store,                    GenerateStore);
-            Add(Instruction.StoreLocal,               GenerateStoreLocal);
-            Add(Instruction.StoreShared,              GenerateStoreShared);
-            Add(Instruction.StoreShared16,            GenerateStoreShared16);
-            Add(Instruction.StoreShared8,             GenerateStoreShared8);
             Add(Instruction.Subtract,                 GenerateSubtract);
             Add(Instruction.SwizzleAdd,               GenerateSwizzleAdd);
             Add(Instruction.TextureSample,            GenerateTextureSample);
@@ -871,30 +865,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             return GenerateLoadOrStore(context, operation, isStore: false);
         }
 
-        private static OperationResult GenerateLoadLocal(CodeGenContext context, AstOperation operation)
-        {
-            return GenerateLoadLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory);
-        }
-
-        private static OperationResult GenerateLoadShared(CodeGenContext context, AstOperation operation)
-        {
-            return GenerateLoadLocalOrShared(context, operation, StorageClass.Workgroup, context.SharedMemory);
-        }
-
-        private static OperationResult GenerateLoadLocalOrShared(
-            CodeGenContext context,
-            AstOperation operation,
-            StorageClass storageClass,
-            SpvInstruction memory)
-        {
-            var offset = context.Get(AggregateType.S32, operation.GetSource(0));
-
-            var elemPointer = context.AccessChain(context.TypePointer(storageClass, context.TypeU32()), memory, offset);
-            var value = context.Load(context.TypeU32(), elemPointer);
-
-            return new OperationResult(AggregateType.U32, value);
-        }
-
         private static OperationResult GenerateLod(CodeGenContext context, AstOperation operation)
         {
             AstTextureOperation texOp = (AstTextureOperation)operation;
@@ -1268,45 +1238,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             return GenerateLoadOrStore(context, operation, isStore: true);
         }
 
-        private static OperationResult GenerateStoreLocal(CodeGenContext context, AstOperation operation)
-        {
-            return GenerateStoreLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory);
-        }
-
-        private static OperationResult GenerateStoreShared(CodeGenContext context, AstOperation operation)
-        {
-            return GenerateStoreLocalOrShared(context, operation, StorageClass.Workgroup, context.SharedMemory);
-        }
-
-        private static OperationResult GenerateStoreLocalOrShared(
-            CodeGenContext context,
-            AstOperation operation,
-            StorageClass storageClass,
-            SpvInstruction memory)
-        {
-            var offset = context.Get(AggregateType.S32, operation.GetSource(0));
-            var value = context.Get(AggregateType.U32, operation.GetSource(1));
-
-            var elemPointer = context.AccessChain(context.TypePointer(storageClass, context.TypeU32()), memory, offset);
-            context.Store(elemPointer, value);
-
-            return OperationResult.Invalid;
-        }
-
-        private static OperationResult GenerateStoreShared16(CodeGenContext context, AstOperation operation)
-        {
-            GenerateStoreSharedSmallInt(context, operation, 16);
-
-            return OperationResult.Invalid;
-        }
-
-        private static OperationResult GenerateStoreShared8(CodeGenContext context, AstOperation operation)
-        {
-            GenerateStoreSharedSmallInt(context, operation, 8);
-
-            return OperationResult.Invalid;
-        }
-
         private static OperationResult GenerateSubtract(CodeGenContext context, AstOperation operation)
         {
             return GenerateBinary(context, operation, context.Delegates.FSub, context.Delegates.ISub);
@@ -1827,55 +1758,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             AstOperation operation,
             Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitU)
         {
-            var value = context.GetU32(operation.GetSource(operation.SourcesCount - 1));
+            SpvInstruction elemPointer = GetStoragePointer(context, operation, out AggregateType varType);
 
-            SpvInstruction elemPointer;
-
-            if (operation.StorageKind == StorageKind.StorageBuffer)
-            {
-                elemPointer = GetStoragePointer(context, operation, out _);
-            }
-            else if (operation.StorageKind == StorageKind.SharedMemory)
-            {
-                var offset = context.GetU32(operation.GetSource(0));
-                elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset);
-            }
-            else
-            {
-                throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\".");
-            }
+            var value = context.Get(varType, operation.GetSource(operation.SourcesCount - 1));
 
             var one = context.Constant(context.TypeU32(), 1);
             var zero = context.Constant(context.TypeU32(), 0);
 
-            return new OperationResult(AggregateType.U32, emitU(context.TypeU32(), elemPointer, one, zero, value));
+            return new OperationResult(varType, emitU(context.GetType(varType), elemPointer, one, zero, value));
         }
 
         private static OperationResult GenerateAtomicMemoryCas(CodeGenContext context, AstOperation operation)
         {
-            var value0 = context.GetU32(operation.GetSource(operation.SourcesCount - 2));
-            var value1 = context.GetU32(operation.GetSource(operation.SourcesCount - 1));
+            SpvInstruction elemPointer = GetStoragePointer(context, operation, out AggregateType varType);
 
-            SpvInstruction elemPointer;
-
-            if (operation.StorageKind == StorageKind.StorageBuffer)
-            {
-                elemPointer = GetStoragePointer(context, operation, out _);
-            }
-            else if (operation.StorageKind == StorageKind.SharedMemory)
-            {
-                var offset = context.GetU32(operation.GetSource(0));
-                elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), context.SharedMemory, offset);
-            }
-            else
-            {
-                throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\".");
-            }
+            var value0 = context.Get(varType, operation.GetSource(operation.SourcesCount - 2));
+            var value1 = context.Get(varType, operation.GetSource(operation.SourcesCount - 1));
 
             var one = context.Constant(context.TypeU32(), 1);
             var zero = context.Constant(context.TypeU32(), 0);
 
-            return new OperationResult(AggregateType.U32, context.AtomicCompareExchange(context.TypeU32(), elemPointer, one, zero, zero, value1, value0));
+            return new OperationResult(varType, context.AtomicCompareExchange(context.GetType(varType), elemPointer, one, zero, zero, value1, value0));
         }
 
         private static OperationResult GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
@@ -1928,6 +1831,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
                         : context.StorageBuffers[bindingIndex.Value];
                     break;
 
+                case StorageKind.LocalMemory:
+                case StorageKind.SharedMemory:
+                    if (!(operation.GetSource(srcIndex++) is AstOperand bindingId) || bindingId.Type != OperandType.Constant)
+                    {
+                        throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
+                    }
+
+                    if (storageKind == StorageKind.LocalMemory)
+                    {
+                        storageClass = StorageClass.Private;
+                        varType = context.Config.Properties.LocalMemories[bindingId.Value].Type & AggregateType.ElementTypeMask;
+                        baseObj = context.LocalMemories[bindingId.Value];
+                    }
+                    else
+                    {
+                        storageClass = StorageClass.Workgroup;
+                        varType = context.Config.Properties.SharedMemories[bindingId.Value].Type & AggregateType.ElementTypeMask;
+                        baseObj = context.SharedMemories[bindingId.Value];
+                    }
+                    break;
+
                 case StorageKind.Input:
                 case StorageKind.InputPerPatch:
                 case StorageKind.Output:
@@ -2048,50 +1972,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             return context.Load(context.GetType(varType), context.Inputs[ioDefinition]);
         }
 
-        private static void GenerateStoreSharedSmallInt(CodeGenContext context, AstOperation operation, int bitSize)
-        {
-            var offset = context.Get(AggregateType.U32, operation.GetSource(0));
-            var value = context.Get(AggregateType.U32, operation.GetSource(1));
-
-            var wordOffset = context.ShiftRightLogical(context.TypeU32(), offset, context.Constant(context.TypeU32(), 2));
-            var bitOffset = context.BitwiseAnd(context.TypeU32(), offset, context.Constant(context.TypeU32(), 3));
-            bitOffset = context.ShiftLeftLogical(context.TypeU32(), bitOffset, context.Constant(context.TypeU32(), 3));
-
-            var memory = context.SharedMemory;
-
-            var elemPointer = context.AccessChain(context.TypePointer(StorageClass.Workgroup, context.TypeU32()), memory, wordOffset);
-
-            GenerateStoreSmallInt(context, elemPointer, bitOffset, value, bitSize);
-        }
-
-        private static void GenerateStoreSmallInt(
-            CodeGenContext context,
-            SpvInstruction elemPointer,
-            SpvInstruction bitOffset,
-            SpvInstruction value,
-            int bitSize)
-        {
-            var loopStart = context.Label();
-            var loopEnd = context.Label();
-
-            context.Branch(loopStart);
-            context.AddLabel(loopStart);
-
-            var oldValue = context.Load(context.TypeU32(), elemPointer);
-            var newValue = context.BitFieldInsert(context.TypeU32(), oldValue, value, bitOffset, context.Constant(context.TypeU32(), bitSize));
-
-            var one = context.Constant(context.TypeU32(), 1);
-            var zero = context.Constant(context.TypeU32(), 0);
-
-            var result = context.AtomicCompareExchange(context.TypeU32(), elemPointer, one, zero, zero, newValue, oldValue);
-            var failed = context.INotEqual(context.TypeBool(), result, oldValue);
-
-            context.LoopMerge(loopEnd, loopStart, LoopControlMask.MaskNone);
-            context.BranchConditional(failed, loopStart, loopEnd);
-
-            context.AddLabel(loopEnd);
-        }
-
         private static OperationResult GetZeroOperationResult(
             CodeGenContext context,
             AstTextureOperation texOp,
diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
index 9aa7382005..99d7bec97a 100644
--- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
+++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
@@ -10,12 +10,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
 {
     static partial class InstEmit
     {
-        private enum MemoryRegion
-        {
-            Local,
-            Shared
-        }
-
         public static void Atom(EmitterContext context)
         {
             InstAtom op = context.GetOp<InstAtom>();
@@ -51,7 +45,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 _ => AtomSize.U32
             };
 
-            Operand res = EmitAtomicOp(context, StorageKind.SharedMemory, op.AtomOp, size, offset, Const(0), value);
+            Operand id = Const(context.Config.ResourceManager.SharedMemoryId);
+            Operand res = EmitAtomicOp(context, StorageKind.SharedMemory, op.AtomOp, size, id, offset, value);
 
             context.Copy(GetDest(op.Dest), res);
         }
@@ -114,14 +109,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
         {
             InstLdl op = context.GetOp<InstLdl>();
 
-            EmitLoad(context, MemoryRegion.Local, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
+            EmitLoad(context, StorageKind.LocalMemory, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
         }
 
         public static void Lds(EmitterContext context)
         {
             InstLds op = context.GetOp<InstLds>();
 
-            EmitLoad(context, MemoryRegion.Shared, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
+            EmitLoad(context, StorageKind.SharedMemory, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
         }
 
         public static void Red(EmitterContext context)
@@ -144,14 +139,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
         {
             InstStl op = context.GetOp<InstStl>();
 
-            EmitStore(context, MemoryRegion.Local, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
+            EmitStore(context, StorageKind.LocalMemory, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
         }
 
         public static void Sts(EmitterContext context)
         {
             InstSts op = context.GetOp<InstSts>();
 
-            EmitStore(context, MemoryRegion.Shared, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
+            EmitStore(context, StorageKind.SharedMemory, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
         }
 
         private static Operand EmitLoadConstant(EmitterContext context, Operand slot, Operand offset)
@@ -192,8 +187,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
             StorageKind storageKind,
             AtomOp op,
             AtomSize type,
-            Operand addrLow,
-            Operand addrHigh,
+            Operand e0,
+            Operand e1,
             Operand value)
         {
             Operand res = Const(0);
@@ -203,7 +198,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 case AtomOp.Add:
                     if (type == AtomSize.S32 || type == AtomSize.U32)
                     {
-                        res = context.AtomicAdd(storageKind, addrLow, addrHigh, value);
+                        res = context.AtomicAdd(storageKind, e0, e1, value);
                     }
                     else
                     {
@@ -213,7 +208,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 case AtomOp.And:
                     if (type == AtomSize.S32 || type == AtomSize.U32)
                     {
-                        res = context.AtomicAnd(storageKind, addrLow, addrHigh, value);
+                        res = context.AtomicAnd(storageKind, e0, e1, value);
                     }
                     else
                     {
@@ -223,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 case AtomOp.Xor:
                     if (type == AtomSize.S32 || type == AtomSize.U32)
                     {
-                        res = context.AtomicXor(storageKind, addrLow, addrHigh, value);
+                        res = context.AtomicXor(storageKind, e0, e1, value);
                     }
                     else
                     {
@@ -233,7 +228,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 case AtomOp.Or:
                     if (type == AtomSize.S32 || type == AtomSize.U32)
                     {
-                        res = context.AtomicOr(storageKind, addrLow, addrHigh, value);
+                        res = context.AtomicOr(storageKind, e0, e1, value);
                     }
                     else
                     {
@@ -243,11 +238,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 case AtomOp.Max:
                     if (type == AtomSize.S32)
                     {
-                        res = context.AtomicMaxS32(storageKind, addrLow, addrHigh, value);
+                        res = context.AtomicMaxS32(storageKind, e0, e1, value);
                     }
                     else if (type == AtomSize.U32)
                     {
-                        res = context.AtomicMaxU32(storageKind, addrLow, addrHigh, value);
+                        res = context.AtomicMaxU32(storageKind, e0, e1, value);
                     }
                     else
                     {
@@ -257,11 +252,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 case AtomOp.Min:
                     if (type == AtomSize.S32)
                     {
-                        res = context.AtomicMinS32(storageKind, addrLow, addrHigh, value);
+                        res = context.AtomicMinS32(storageKind, e0, e1, value);
                     }
                     else if (type == AtomSize.U32)
                     {
-                        res = context.AtomicMinU32(storageKind, addrLow, addrHigh, value);
+                        res = context.AtomicMinU32(storageKind, e0, e1, value);
                     }
                     else
                     {
@@ -275,7 +270,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
 
         private static void EmitLoad(
             EmitterContext context,
-            MemoryRegion region,
+            StorageKind storageKind,
             LsSize2 size,
             Operand srcA,
             int rd,
@@ -287,19 +282,19 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 return;
             }
 
+            int id = storageKind == StorageKind.LocalMemory
+                ? context.Config.ResourceManager.LocalMemoryId
+                : context.Config.ResourceManager.SharedMemoryId;
             bool isSmallInt = size < LsSize2.B32;
 
-            int count = 1;
-
-            switch (size)
+            int count = size switch
             {
-                case LsSize2.B64: count = 2; break;
-                case LsSize2.B128: count = 4; break;
-            }
+                LsSize2.B64 => 2,
+                LsSize2.B128 => 4,
+                _ => 1
+            };
 
-            Operand baseOffset = context.IAdd(srcA, Const(offset));
-            Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2)); // Word offset = byte offset / 4 (one word = 4 bytes).
-            Operand bitOffset = GetBitOffset(context, baseOffset);
+            Operand baseOffset = context.Copy(srcA);
 
             for (int index = 0; index < count; index++)
             {
@@ -310,14 +305,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
                     break;
                 }
 
-                Operand elemOffset = context.IAdd(wordOffset, Const(index));
-                Operand value = null;
-
-                switch (region)
-                {
-                    case MemoryRegion.Local: value = context.LoadLocal(elemOffset); break;
-                    case MemoryRegion.Shared: value = context.LoadShared(elemOffset); break;
-                }
+                Operand byteOffset = context.IAdd(baseOffset, Const(offset + index * 4));
+                Operand wordOffset = context.ShiftRightU32(byteOffset, Const(2)); // Word offset = byte offset / 4 (one word = 4 bytes).
+                Operand bitOffset = GetBitOffset(context, byteOffset);
+                Operand value = context.Load(storageKind, id, wordOffset);
 
                 if (isSmallInt)
                 {
@@ -360,7 +351,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
 
         private static void EmitStore(
             EmitterContext context,
-            MemoryRegion region,
+            StorageKind storageKind,
             LsSize2 size,
             Operand srcA,
             int rd,
@@ -372,52 +363,54 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 return;
             }
 
+            int id = storageKind == StorageKind.LocalMemory
+                ? context.Config.ResourceManager.LocalMemoryId
+                : context.Config.ResourceManager.SharedMemoryId;
             bool isSmallInt = size < LsSize2.B32;
 
-            int count = 1;
-
-            switch (size)
+            int count = size switch
             {
-                case LsSize2.B64: count = 2; break;
-                case LsSize2.B128: count = 4; break;
-            }
+                LsSize2.B64 => 2,
+                LsSize2.B128 => 4,
+                _ => 1
+            };
 
-            Operand baseOffset = context.IAdd(srcA, Const(offset));
-            Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2));
-            Operand bitOffset = GetBitOffset(context, baseOffset);
+            Operand baseOffset = context.Copy(srcA);
 
             for (int index = 0; index < count; index++)
             {
                 bool isRz = rd + index >= RegisterConsts.RegisterZeroIndex;
 
                 Operand value = Register(isRz ? rd : rd + index, RegisterType.Gpr);
-                Operand elemOffset = context.IAdd(wordOffset, Const(index));
+                Operand byteOffset = context.IAdd(baseOffset, Const(offset + index * 4));
+                Operand wordOffset = context.ShiftRightU32(byteOffset, Const(2));
+                Operand bitOffset = GetBitOffset(context, byteOffset);
 
-                if (isSmallInt && region == MemoryRegion.Local)
+                if (isSmallInt && storageKind == StorageKind.LocalMemory)
                 {
-                    Operand word = context.LoadLocal(elemOffset);
+                    Operand word = context.Load(storageKind, id, wordOffset);
 
                     value = InsertSmallInt(context, (LsSize)size, bitOffset, word, value);
                 }
 
-                if (region == MemoryRegion.Local)
+                if (storageKind == StorageKind.LocalMemory)
                 {
-                    context.StoreLocal(elemOffset, value);
+                    context.Store(storageKind, id, wordOffset, value);
                 }
-                else if (region == MemoryRegion.Shared)
+                else if (storageKind == StorageKind.SharedMemory)
                 {
                     switch (size)
                     {
                         case LsSize2.U8:
                         case LsSize2.S8:
-                            context.StoreShared8(baseOffset, value);
+                            context.Store(StorageKind.SharedMemory8, id, byteOffset, value);
                             break;
                         case LsSize2.U16:
                         case LsSize2.S16:
-                            context.StoreShared16(baseOffset, value);
+                            context.Store(StorageKind.SharedMemory16, id, byteOffset, value);
                             break;
                         default:
-                            context.StoreShared(elemOffset, value);
+                            context.Store(storageKind, id, wordOffset, value);
                             break;
                     }
                 }
diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
index aecb672495..de41a2cf79 100644
--- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
+++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
@@ -79,8 +79,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
         ImageAtomic,
         IsNan,
         Load,
-        LoadLocal,
-        LoadShared,
         Lod,
         LogarithmB2,
         LogicalAnd,
@@ -115,10 +113,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
         Sine,
         SquareRoot,
         Store,
-        StoreLocal,
-        StoreShared,
-        StoreShared16,
-        StoreShared8,
         Subtract,
         SwizzleAdd,
         TextureSample,
diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs
index 2b5dd1dec9..20576a454c 100644
--- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs
+++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/StorageKind.cs
@@ -11,12 +11,13 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
         StorageBuffer,
         LocalMemory,
         SharedMemory,
+        SharedMemory8, // TODO: Remove this and store type as a field on the Operation class itself.
+        SharedMemory16, // TODO: Remove this and store type as a field on the Operation class itself.
         GlobalMemory,
-        // TODO: Remove those and store type as a field on the Operation class itself.
-        GlobalMemoryS8,
-        GlobalMemoryS16,
-        GlobalMemoryU8,
-        GlobalMemoryU16
+        GlobalMemoryS8, // TODO: Remove this and store type as a field on the Operation class itself.
+        GlobalMemoryS16, // TODO: Remove this and store type as a field on the Operation class itself.
+        GlobalMemoryU8, // TODO: Remove this and store type as a field on the Operation class itself.
+        GlobalMemoryU16 // TODO: Remove this and store type as a field on the Operation class itself.
     }
 
     static class StorageKindExtensions
diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj
index 86de2e755e..b1f1fb9633 100644
--- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj
+++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj
@@ -10,14 +10,12 @@
   </ItemGroup>
 
   <ItemGroup>
-    <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Shared.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighS32.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleDown.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleXor.glsl" />
-    <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
   </ItemGroup>
 
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs
index c348b5d93d..ed910f96dd 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs
@@ -5,15 +5,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
     [Flags]
     enum HelperFunctionsMask
     {
-        AtomicMinMaxS32Shared  = 1 << 0,
-        MultiplyHighS32        = 1 << 2,
-        MultiplyHighU32        = 1 << 3,
-        Shuffle                = 1 << 4,
-        ShuffleDown            = 1 << 5,
-        ShuffleUp              = 1 << 6,
-        ShuffleXor             = 1 << 7,
-        StoreSharedSmallInt    = 1 << 8,
-        SwizzleAdd             = 1 << 10,
-        FSI                    = 1 << 11
+        MultiplyHighS32 = 1 << 2,
+        MultiplyHighU32 = 1 << 3,
+        Shuffle         = 1 << 4,
+        ShuffleDown     = 1 << 5,
+        ShuffleUp       = 1 << 6,
+        ShuffleXor      = 1 << 7,
+        SwizzleAdd      = 1 << 10,
+        FSI             = 1 << 11
     }
 }
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
index 6e2013501e..b08478ad3a 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
@@ -90,8 +90,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
             Add(Instruction.ImageAtomic,              AggregateType.S32);
             Add(Instruction.IsNan,                    AggregateType.Bool,   AggregateType.Scalar);
             Add(Instruction.Load,                     AggregateType.FP32);
-            Add(Instruction.LoadLocal,                AggregateType.U32,    AggregateType.S32);
-            Add(Instruction.LoadShared,               AggregateType.U32,    AggregateType.S32);
             Add(Instruction.Lod,                      AggregateType.FP32);
             Add(Instruction.LogarithmB2,              AggregateType.Scalar, AggregateType.Scalar);
             Add(Instruction.LogicalAnd,               AggregateType.Bool,   AggregateType.Bool,    AggregateType.Bool);
@@ -121,10 +119,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
             Add(Instruction.Sine,                     AggregateType.Scalar, AggregateType.Scalar);
             Add(Instruction.SquareRoot,               AggregateType.Scalar, AggregateType.Scalar);
             Add(Instruction.Store,                    AggregateType.Void);
-            Add(Instruction.StoreLocal,               AggregateType.Void,   AggregateType.S32,     AggregateType.U32);
-            Add(Instruction.StoreShared,              AggregateType.Void,   AggregateType.S32,     AggregateType.U32);
-            Add(Instruction.StoreShared16,            AggregateType.Void,   AggregateType.S32,     AggregateType.U32);
-            Add(Instruction.StoreShared8,             AggregateType.Void,   AggregateType.S32,     AggregateType.U32);
             Add(Instruction.Subtract,                 AggregateType.Scalar, AggregateType.Scalar,  AggregateType.Scalar);
             Add(Instruction.SwizzleAdd,               AggregateType.FP32,   AggregateType.FP32,    AggregateType.FP32,    AggregateType.S32);
             Add(Instruction.TextureSample,            AggregateType.FP32);
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs
new file mode 100644
index 0000000000..c0bb750e74
--- /dev/null
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/MemoryDefinition.cs
@@ -0,0 +1,18 @@
+using Ryujinx.Graphics.Shader.Translation;
+
+namespace Ryujinx.Graphics.Shader.StructuredIr
+{
+    readonly struct MemoryDefinition
+    {
+        public string Name { get; }
+        public AggregateType Type { get; }
+        public int ArrayLength { get; }
+
+        public MemoryDefinition(string name, AggregateType type, int arrayLength = 1)
+        {
+            Name = name;
+            Type = type;
+            ArrayLength = arrayLength;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs
index 157c5937dd..c6132ef8cc 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs
@@ -6,14 +6,20 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
     {
         private readonly Dictionary<int, BufferDefinition> _constantBuffers;
         private readonly Dictionary<int, BufferDefinition> _storageBuffers;
+        private readonly Dictionary<int, MemoryDefinition> _localMemories;
+        private readonly Dictionary<int, MemoryDefinition> _sharedMemories;
 
         public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers;
         public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers;
+        public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories;
+        public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories;
 
         public ShaderProperties()
         {
             _constantBuffers = new Dictionary<int, BufferDefinition>();
             _storageBuffers = new Dictionary<int, BufferDefinition>();
+            _localMemories = new Dictionary<int, MemoryDefinition>();
+            _sharedMemories = new Dictionary<int, MemoryDefinition>();
         }
 
         public void AddConstantBuffer(int binding, BufferDefinition definition)
@@ -25,5 +31,21 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
         {
             _storageBuffers[binding] = definition;
         }
+
+        public int AddLocalMemory(MemoryDefinition definition)
+        {
+            int id = _localMemories.Count;
+            _localMemories.Add(id, definition);
+
+            return id;
+        }
+
+        public int AddSharedMemory(MemoryDefinition definition)
+        {
+            int id = _sharedMemories.Count;
+            _sharedMemories.Add(id, definition);
+
+            return id;
+        }
     }
 }
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
index a8f1327665..9d12a73cd6 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
@@ -274,13 +274,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
             // decide which helper functions are needed on the final generated code.
             switch (operation.Inst)
             {
-                case Instruction.AtomicMaxS32:
-                case Instruction.AtomicMinS32:
-                    if (operation.StorageKind == StorageKind.SharedMemory)
-                    {
-                        context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
-                    }
-                    break;
                 case Instruction.MultiplyHighS32:
                     context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
                     break;
@@ -299,10 +292,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
                 case Instruction.ShuffleXor:
                     context.Info.HelperFunctionsMask |= HelperFunctionsMask.ShuffleXor;
                     break;
-                case Instruction.StoreShared16:
-                case Instruction.StoreShared8:
-                    context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreSharedSmallInt;
-                    break;
                 case Instruction.SwizzleAdd:
                     context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd;
                     break;
diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
index be0cba8090..0ba26107c3 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
@@ -67,6 +67,11 @@ namespace Ryujinx.Graphics.Shader.Translation
             return context.Add(Instruction.AtomicAnd, storageKind, Local(), Const(binding), e0, e1, value);
         }
 
+        public static Operand AtomicCompareAndSwap(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand compare, Operand value)
+        {
+            return context.Add(Instruction.AtomicCompareAndSwap, storageKind, Local(), Const(binding), e0, compare, value);
+        }
+
         public static Operand AtomicCompareAndSwap(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand compare, Operand value)
         {
             return context.Add(Instruction.AtomicCompareAndSwap, storageKind, Local(), Const(binding), e0, e1, compare, value);
@@ -661,16 +666,6 @@ namespace Ryujinx.Graphics.Shader.Translation
                 : context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex);
         }
 
-        public static Operand LoadLocal(this EmitterContext context, Operand a)
-        {
-            return context.Add(Instruction.LoadLocal, Local(), a);
-        }
-
-        public static Operand LoadShared(this EmitterContext context, Operand a)
-        {
-            return context.Add(Instruction.LoadShared, Local(), a);
-        }
-
         public static Operand MemoryBarrier(this EmitterContext context)
         {
             return context.Add(Instruction.MemoryBarrier);
@@ -753,6 +748,11 @@ namespace Ryujinx.Graphics.Shader.Translation
             return context.Add(Instruction.Store, storageKind, null, e0, e1, value);
         }
 
+        public static Operand Store(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand value)
+        {
+            return context.Add(Instruction.Store, storageKind, null, Const(binding), e0, value);
+        }
+
         public static Operand Store(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
         {
             return context.Add(Instruction.Store, storageKind, null, Const(binding), e0, e1, value);
@@ -797,26 +797,6 @@ namespace Ryujinx.Graphics.Shader.Translation
                 : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value);
         }
 
-        public static Operand StoreLocal(this EmitterContext context, Operand a, Operand b)
-        {
-            return context.Add(Instruction.StoreLocal, null, a, b);
-        }
-
-        public static Operand StoreShared(this EmitterContext context, Operand a, Operand b)
-        {
-            return context.Add(Instruction.StoreShared, null, a, b);
-        }
-
-        public static Operand StoreShared16(this EmitterContext context, Operand a, Operand b)
-        {
-            return context.Add(Instruction.StoreShared16, null, a, b);
-        }
-
-        public static Operand StoreShared8(this EmitterContext context, Operand a, Operand b)
-        {
-            return context.Add(Instruction.StoreShared8, null, a, b);
-        }
-
         public static Operand UnpackDouble2x32High(this EmitterContext context, Operand a)
         {
             return UnpackDouble2x32(context, a, 1);
diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs
index 6958b86f2c..51a396821d 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs
@@ -9,13 +9,13 @@ namespace Ryujinx.Graphics.Shader.Translation
     class HelperFunctionManager
     {
         private readonly List<Function> _functionList;
-        private readonly Dictionary<HelperFunctionName, int> _functionIds;
+        private readonly Dictionary<int, int> _functionIds;
         private readonly ShaderStage _stage;
 
         public HelperFunctionManager(List<Function> functionList, ShaderStage stage)
         {
             _functionList = functionList;
-            _functionIds = new Dictionary<HelperFunctionName, int>();
+            _functionIds = new Dictionary<int, int>();
             _stage = stage;
         }
 
@@ -29,14 +29,30 @@ namespace Ryujinx.Graphics.Shader.Translation
 
         public int GetOrCreateFunctionId(HelperFunctionName functionName)
         {
-            if (_functionIds.TryGetValue(functionName, out int functionId))
+            if (_functionIds.TryGetValue((int)functionName, out int functionId))
             {
                 return functionId;
             }
 
             Function function = GenerateFunction(functionName);
             functionId = AddFunction(function);
-            _functionIds.Add(functionName, functionId);
+            _functionIds.Add((int)functionName, functionId);
+
+            return functionId;
+        }
+
+        public int GetOrCreateFunctionId(HelperFunctionName functionName, int id)
+        {
+            int key = (int)functionName | (id << 16);
+
+            if (_functionIds.TryGetValue(key, out int functionId))
+            {
+                return functionId;
+            }
+
+            Function function = GenerateFunction(functionName, id);
+            functionId = AddFunction(function);
+            _functionIds.Add(key, functionId);
 
             return functionId;
         }
@@ -140,6 +156,67 @@ namespace Ryujinx.Graphics.Shader.Translation
             return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "ConvertFloatToDouble", false, 1, 2);
         }
 
+        private static Function GenerateFunction(HelperFunctionName functionName, int id)
+        {
+            return functionName switch
+            {
+                HelperFunctionName.SharedAtomicMaxS32 => GenerateSharedAtomicSigned(id, isMin: false),
+                HelperFunctionName.SharedAtomicMinS32 => GenerateSharedAtomicSigned(id, isMin: true),
+                HelperFunctionName.SharedStore8 => GenerateSharedStore8(id),
+                HelperFunctionName.SharedStore16 => GenerateSharedStore16(id),
+                _ => throw new ArgumentException($"Invalid function name {functionName}")
+            };
+        }
+
+        private static Function GenerateSharedAtomicSigned(int id, bool isMin)
+        {
+            EmitterContext context = new EmitterContext();
+
+            Operand wordOffset = Argument(0);
+            Operand value = Argument(1);
+
+            Operand result = GenerateSharedAtomicCasLoop(context, wordOffset, id, (memValue) =>
+            {
+                return isMin
+                    ? context.IMinimumS32(memValue, value)
+                    : context.IMaximumS32(memValue, value);
+            });
+
+            context.Return(result);
+
+            return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, $"SharedAtomic{(isMin ? "Min" : "Max")}_{id}", true, 2, 0);
+        }
+
+        private static Function GenerateSharedStore8(int id)
+        {
+            return GenerateSharedStore(id, 8);
+        }
+
+        private static Function GenerateSharedStore16(int id)
+        {
+            return GenerateSharedStore(id, 16);
+        }
+
+        private static Function GenerateSharedStore(int id, int bitSize)
+        {
+            EmitterContext context = new EmitterContext();
+
+            Operand offset = Argument(0);
+            Operand value = Argument(1);
+
+            Operand wordOffset = context.ShiftRightU32(offset, Const(2));
+            Operand bitOffset = GetBitOffset(context, offset);
+
+            GenerateSharedAtomicCasLoop(context, wordOffset, id, (memValue) =>
+            {
+                return context.BitfieldInsert(memValue, value, bitOffset, Const(bitSize));
+            });
+
+            context.Return();
+
+            return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, $"SharedStore{bitSize}_{id}", false, 2, 0);
+        }
+
         private Function GenerateTexelFetchScaleFunction()
         {
             EmitterContext context = new EmitterContext();
@@ -226,5 +303,29 @@ namespace Ryujinx.Graphics.Shader.Translation
                     return context.IAdd(Const(1), index);
             }
         }
+
+        public static Operand GetBitOffset(EmitterContext context, Operand offset)
+        {
+            return context.ShiftLeft(context.BitwiseAnd(offset, Const(3)), Const(3));
+        }
+
+        private static Operand GenerateSharedAtomicCasLoop(EmitterContext context, Operand wordOffset, int id, Func<Operand, Operand> opCallback)
+        {
+            Operand lblLoopHead = Label();
+
+            context.MarkLabel(lblLoopHead);
+
+            Operand oldValue = context.Load(StorageKind.SharedMemory, id, wordOffset);
+            Operand newValue = opCallback(oldValue);
+
+            Operand casResult = context.AtomicCompareAndSwap(StorageKind.SharedMemory, id, wordOffset, oldValue, newValue);
+
+            Operand casFail = context.ICompareNotEqual(casResult, oldValue);
+
+            context.BranchIfTrue(lblLoopHead, casFail);
+
+            return oldValue;
+        }
+
     }
 }
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs
index 8c37c34c7b..984f2d0478 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/HelperFunctionName.cs
@@ -4,6 +4,10 @@ namespace Ryujinx.Graphics.Shader.Translation
     {
         ConvertDoubleToFloat,
         ConvertFloatToDouble,
+        SharedAtomicMaxS32,
+        SharedAtomicMinS32,
+        SharedStore8,
+        SharedStore16,
         TexelFetchScale,
         TextureSizeUnscale
     }
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
index 14904b2607..9d260c678c 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
@@ -244,7 +244,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
                             node = nextNode;
                         }
                     }
-                    else if (operation.Inst == Instruction.StoreShared || operation.Inst == Instruction.StoreLocal)
+                    else if (operation.Inst == Instruction.Store &&
+                        (operation.StorageKind == StorageKind.SharedMemory ||
+                        operation.StorageKind == StorageKind.LocalMemory))
                     {
                         // The NVIDIA compiler can sometimes use shared or local memory as temporary
                         // storage to place the base address and size on, so we need
@@ -874,7 +876,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
 
                 if (bitSize < 32)
                 {
-                    Operand bitOffset = GetBitOffset(context, offset);
+                    Operand bitOffset = HelperFunctionManager.GetBitOffset(context, offset);
 
                     GenerateAtomicCasLoop(context, wordOffset, binding, (memValue) =>
                     {
@@ -892,7 +894,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
 
                 if (IsSmallInt(storageKind))
                 {
-                    Operand bitOffset = GetBitOffset(context, offset);
+                    Operand bitOffset = HelperFunctionManager.GetBitOffset(context, offset);
 
                     switch (storageKind)
                     {
@@ -921,11 +923,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
             return true;
         }
 
-        private static Operand GetBitOffset(EmitterContext context, Operand offset)
-        {
-            return context.ShiftLeft(context.BitwiseAnd(offset, Const(3)), Const(3));
-        }
-
         private static Operand GenerateAtomicCasLoop(EmitterContext context, Operand wordOffset, int binding, Func<Operand, Operand> opCallback)
         {
             Operand lblLoopHead = Label();
@@ -1070,15 +1067,18 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
         {
             baseOffset = null;
 
-            if (operation.Inst == Instruction.LoadShared || operation.Inst == Instruction.StoreShared)
+            if (operation.Inst == Instruction.Load || operation.Inst == Instruction.Store)
             {
-                type = LsMemoryType.Shared;
-                return TryGetSharedMemoryOffsets(operation, out baseOffset, out constOffset);
-            }
-            else if (operation.Inst == Instruction.LoadLocal || operation.Inst == Instruction.StoreLocal)
-            {
-                type = LsMemoryType.Local;
-                return TryGetLocalMemoryOffset(operation, out constOffset);
+                if (operation.StorageKind == StorageKind.SharedMemory)
+                {
+                    type = LsMemoryType.Shared;
+                    return TryGetSharedMemoryOffsets(operation, out baseOffset, out constOffset);
+                }
+                else if (operation.StorageKind == StorageKind.LocalMemory)
+                {
+                    type = LsMemoryType.Local;
+                    return TryGetLocalMemoryOffset(operation, out constOffset);
+                }
             }
 
             type = default;
diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
index 2d19a5a700..c58e4828b4 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Common;
 using Ryujinx.Graphics.Shader.StructuredIr;
 using System;
 using System.Collections.Generic;
@@ -22,9 +23,12 @@ namespace Ryujinx.Graphics.Shader.Translation
 
         private readonly HashSet<int> _usedConstantBufferBindings;
 
+        public int LocalMemoryId { get; }
+        public int SharedMemoryId { get; }
+
         public ShaderProperties Properties => _properties;
 
-        public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties)
+        public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties, int localMemorySize)
         {
             _gpuAccessor = gpuAccessor;
             _properties = properties;
@@ -41,6 +45,25 @@ namespace Ryujinx.Graphics.Shader.Translation
             _usedConstantBufferBindings = new HashSet<int>();
 
             properties.AddConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType()));
+
+            LocalMemoryId = -1;
+            SharedMemoryId = -1;
+
+            if (localMemorySize != 0)
+            {
+                var lmem = new MemoryDefinition("local_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(localMemorySize, sizeof(uint)));
+
+                LocalMemoryId = properties.AddLocalMemory(lmem);
+            }
+
+            int sharedMemorySize = stage == ShaderStage.Compute ? gpuAccessor.QueryComputeSharedMemorySize() : 0;
+
+            if (sharedMemorySize != 0)
+            {
+                var smem = new MemoryDefinition("shared_memory", AggregateType.Array | AggregateType.U32,  BitUtils.DivRoundUp(sharedMemorySize, sizeof(uint)));
+
+                SharedMemoryId = properties.AddSharedMemory(smem);
+            }
         }
 
         public int GetConstantBufferBinding(int slot)
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
index baa88251ba..f5a524a0f9 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
@@ -1,6 +1,8 @@
 using Ryujinx.Graphics.Shader.IntermediateRepresentation;
 using Ryujinx.Graphics.Shader.StructuredIr;
+using Ryujinx.Graphics.Shader.Translation.Optimizations;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Linq;
 
 using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
@@ -70,6 +72,15 @@ namespace Ryujinx.Graphics.Shader.Translation
                             }
                         }
                     }
+                    else
+                    {
+                        node = InsertSharedStoreSmallInt(hfm, node);
+
+                        if (config.Options.TargetLanguage != TargetLanguage.Spirv)
+                        {
+                            node = InsertSharedAtomicSigned(hfm, node);
+                        }
+                    }
                 }
             }
         }
@@ -171,6 +182,87 @@ namespace Ryujinx.Graphics.Shader.Translation
             operation.TurnIntoCopy(result);
         }
 
+        private static LinkedListNode<INode> InsertSharedStoreSmallInt(HelperFunctionManager hfm, LinkedListNode<INode> node)
+        {
+            Operation operation = (Operation)node.Value;
+            HelperFunctionName name;
+
+            if (operation.StorageKind == StorageKind.SharedMemory8)
+            {
+                name = HelperFunctionName.SharedStore8;
+            }
+            else if (operation.StorageKind == StorageKind.SharedMemory16)
+            {
+                name = HelperFunctionName.SharedStore16;
+            }
+            else
+            {
+                return node;
+            }
+
+            if (operation.Inst != Instruction.Store)
+            {
+                return node;
+            }
+
+            Operand memoryId = operation.GetSource(0);
+            Operand byteOffset = operation.GetSource(1);
+            Operand value = operation.GetSource(2);
+
+            Debug.Assert(memoryId.Type == OperandType.Constant);
+
+            int functionId = hfm.GetOrCreateFunctionId(name, memoryId.Value);
+
+            Operand[] callArgs = new Operand[] { Const(functionId), byteOffset, value };
+
+            LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, (Operand)null, callArgs));
+
+            Utils.DeleteNode(node, operation);
+
+            return newNode;
+        }
+
+        private static LinkedListNode<INode> InsertSharedAtomicSigned(HelperFunctionManager hfm, LinkedListNode<INode> node)
+        {
+            Operation operation = (Operation)node.Value;
+            HelperFunctionName name;
+
+            if (operation.Inst == Instruction.AtomicMaxS32)
+            {
+                name = HelperFunctionName.SharedAtomicMaxS32;
+            }
+            else if (operation.Inst == Instruction.AtomicMinS32)
+            {
+                name = HelperFunctionName.SharedAtomicMinS32;
+            }
+            else
+            {
+                return node;
+            }
+
+            if (operation.StorageKind != StorageKind.SharedMemory)
+            {
+                return node;
+            }
+
+            Operand result = operation.Dest;
+            Operand memoryId = operation.GetSource(0);
+            Operand byteOffset = operation.GetSource(1);
+            Operand value = operation.GetSource(2);
+
+            Debug.Assert(memoryId.Type == OperandType.Constant);
+
+            int functionId = hfm.GetOrCreateFunctionId(name, memoryId.Value);
+
+            Operand[] callArgs = new Operand[] { Const(functionId), byteOffset, value };
+
+            LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, result, callArgs));
+
+            Utils.DeleteNode(node, operation);
+
+            return newNode;
+        }
+
         private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config)
         {
             TextureOperation texOp = (TextureOperation)node.Value;
diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
index 534bda70e3..fa1250022a 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
@@ -124,7 +124,7 @@ namespace Ryujinx.Graphics.Shader.Translation
         private TextureDescriptor[] _cachedTextureDescriptors;
         private TextureDescriptor[] _cachedImageDescriptors;
 
-        public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options)
+        public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options, int localMemorySize)
         {
             Stage       = stage;
             GpuAccessor = gpuAccessor;
@@ -143,7 +143,7 @@ namespace Ryujinx.Graphics.Shader.Translation
             _usedTextures = new Dictionary<TextureInfo, TextureMeta>();
             _usedImages   = new Dictionary<TextureInfo, TextureMeta>();
 
-            ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties());
+            ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties(), localMemorySize);
 
             if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled())
             {
@@ -176,14 +176,17 @@ namespace Ryujinx.Graphics.Shader.Translation
             OutputTopology outputTopology,
             int maxOutputVertices,
             IGpuAccessor gpuAccessor,
-            TranslationOptions options) : this(stage, gpuAccessor, options)
+            TranslationOptions options) : this(stage, gpuAccessor, options, 0)
         {
             ThreadsPerInputPrimitive = 1;
             OutputTopology           = outputTopology;
             MaxOutputVertices        = maxOutputVertices;
         }
 
-        public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options)
+        public ShaderConfig(
+            ShaderHeader header,
+            IGpuAccessor gpuAccessor,
+            TranslationOptions options) : this(header.Stage, gpuAccessor, options, GetLocalMemorySize(header))
         {
             GpPassthrough            = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
             ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive;
@@ -197,6 +200,11 @@ namespace Ryujinx.Graphics.Shader.Translation
             LastInVertexPipeline     = header.Stage < ShaderStage.Fragment;
         }
 
+        private static int GetLocalMemorySize(ShaderHeader header)
+        {
+            return header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize + (header.ShaderLocalMemoryCrsSize / ThreadsPerWarp);
+        }
+
         private void EnsureTransformFeedbackInitialized()
         {
             if (HasTransformFeedbackOutputs() && _transformFeedbackOutputs == null)
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs
index c0212a5bc9..b44d6daaa9 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs
@@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Shader.Translation
 
             if (options.Flags.HasFlag(TranslationFlags.Compute))
             {
-                config = new ShaderConfig(ShaderStage.Compute, gpuAccessor, options);
+                config = new ShaderConfig(ShaderStage.Compute, gpuAccessor, options, gpuAccessor.QueryComputeLocalMemorySize());
 
                 program = Decoder.Decode(config, address);
             }