From fc93bc2abde0b54a0a495f9b28a76fd34b47f320 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 4 Apr 2021 03:04:48 -0300
Subject: [PATCH] shader: Implement BAR and fix memory barriers

---
 .../backend/spirv/emit_spirv.h                |  1 +
 .../backend/spirv/emit_spirv_barriers.cpp     | 15 ++++-
 .../frontend/ir/ir_emitter.cpp                |  4 ++
 .../frontend/ir/ir_emitter.h                  |  1 +
 .../frontend/ir/microinstruction.cpp          |  4 ++
 src/shader_recompiler/frontend/ir/opcodes.inc |  1 +
 .../translate/impl/barrier_operations.cpp     | 58 ++++++++++++++++++-
 7 files changed, 79 insertions(+), 5 deletions(-)

diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index e066ba87d5..032b0b2f9d 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -28,6 +28,7 @@ void EmitSelectionMerge(EmitContext& ctx, Id merge_label);
 void EmitReturn(EmitContext& ctx);
 void EmitUnreachable(EmitContext& ctx);
 void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label);
+void EmitBarrier(EmitContext& ctx);
 void EmitMemoryBarrierWorkgroupLevel(EmitContext& ctx);
 void EmitMemoryBarrierDeviceLevel(EmitContext& ctx);
 void EmitMemoryBarrierSystemLevel(EmitContext& ctx);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
index 18f512319b..74f523d0ff 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
@@ -8,16 +8,25 @@
 namespace Shader::Backend::SPIRV {
 namespace {
 void EmitMemoryBarrierImpl(EmitContext& ctx, spv::Scope scope) {
-    const auto semantics =
+    const auto semantics{
         spv::MemorySemanticsMask::AcquireRelease | spv::MemorySemanticsMask::UniformMemory |
         spv::MemorySemanticsMask::WorkgroupMemory | spv::MemorySemanticsMask::AtomicCounterMemory |
-        spv::MemorySemanticsMask::ImageMemory;
+        spv::MemorySemanticsMask::ImageMemory};
     ctx.OpMemoryBarrier(ctx.Constant(ctx.U32[1], static_cast<u32>(scope)),
                         ctx.Constant(ctx.U32[1], static_cast<u32>(semantics)));
 }
-
 } // Anonymous namespace
 
+void EmitBarrier(EmitContext& ctx) {
+    const auto execution{spv::Scope::Workgroup};
+    const auto memory{spv::Scope::Workgroup};
+    const auto memory_semantics{spv::MemorySemanticsMask::AcquireRelease |
+                                spv::MemorySemanticsMask::WorkgroupMemory};
+    ctx.OpControlBarrier(ctx.Constant(ctx.U32[1], static_cast<u32>(execution)),
+                         ctx.Constant(ctx.U32[1], static_cast<u32>(memory)),
+                         ctx.Constant(ctx.U32[1], static_cast<u32>(memory_semantics)));
+}
+
 void EmitMemoryBarrierWorkgroupLevel(EmitContext& ctx) {
     EmitMemoryBarrierImpl(ctx, spv::Scope::Workgroup);
 }
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index dbd38a28b3..246c3b9ef1 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -82,6 +82,10 @@ void IREmitter::SelectionMerge(Block* merge_block) {
     Inst(Opcode::SelectionMerge, merge_block);
 }
 
+void IREmitter::Barrier() {
+    Inst(Opcode::Barrier);
+}
+
 void IREmitter::MemoryBarrier(MemoryScope scope) {
     switch (scope) {
     case MemoryScope::Workgroup:
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 81a57fefea..1b00c548d0 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -128,6 +128,7 @@ public:
     [[nodiscard]] Value Select(const U1& condition, const Value& true_value,
                                const Value& false_value);
 
+    [[nodiscard]] void Barrier();
     [[nodiscard]] void MemoryBarrier(MemoryScope scope);
 
     template <typename Dest, typename Source>
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 074c71d533..481202d94b 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -57,6 +57,10 @@ bool Inst::MayHaveSideEffects() const noexcept {
     case Opcode::Return:
     case Opcode::Unreachable:
     case Opcode::DemoteToHelperInvocation:
+    case Opcode::Barrier:
+    case Opcode::MemoryBarrierWorkgroupLevel:
+    case Opcode::MemoryBarrierDeviceLevel:
+    case Opcode::MemoryBarrierSystemLevel:
     case Opcode::Prologue:
     case Opcode::Epilogue:
     case Opcode::SetAttribute:
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 734f5328be..dcd54bcf79 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -17,6 +17,7 @@ OPCODE(Unreachable,                                         Void,
 OPCODE(DemoteToHelperInvocation,                            Void,           Label,                                                                          )
 
 // Barriers
+OPCODE(Barrier,                                             Void,                                                                                           )
 OPCODE(MemoryBarrierWorkgroupLevel,                         Void,                                                                                           )
 OPCODE(MemoryBarrierDeviceLevel,                            Void,                                                                                           )
 OPCODE(MemoryBarrierSystemLevel,                            Void,                                                                                           )
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp
index 26d5e276bc..2a2a294dfa 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp
@@ -38,6 +38,7 @@ void TranslatorVisitor::MEMBAR(u64 inst) {
         u64 raw;
         BitField<8, 2, LocalScope> scope;
     } membar{inst};
+
     ir.MemoryBarrier(LocalScopeToMemoryScope(membar.scope));
 }
 
@@ -45,8 +46,61 @@ void TranslatorVisitor::DEPBAR() {
     // DEPBAR is a no-op
 }
 
-void TranslatorVisitor::BAR(u64) {
-    throw NotImplementedException("Instruction {} is not implemented", Opcode::BAR);
+void TranslatorVisitor::BAR(u64 insn) {
+    enum class Mode {
+        RedPopc,
+        Scan,
+        RedAnd,
+        RedOr,
+        Sync,
+        Arrive,
+    };
+    union {
+        u64 raw;
+        BitField<43, 1, u64> is_a_imm;
+        BitField<44, 1, u64> is_b_imm;
+        BitField<8, 8, u64> imm_a;
+        BitField<20, 12, u64> imm_b;
+        BitField<42, 1, u64> neg_pred;
+        BitField<39, 3, IR::Pred> pred;
+    } const bar{insn};
+
+    const Mode mode{[insn] {
+        switch (insn & 0x0000009B00000000ULL) {
+        case 0x0000000200000000ULL:
+            return Mode::RedPopc;
+        case 0x0000000300000000ULL:
+            return Mode::Scan;
+        case 0x0000000A00000000ULL:
+            return Mode::RedAnd;
+        case 0x0000001200000000ULL:
+            return Mode::RedOr;
+        case 0x0000008000000000ULL:
+            return Mode::Sync;
+        case 0x0000008100000000ULL:
+            return Mode::Arrive;
+        }
+        throw NotImplementedException("Invalid encoding");
+    }()};
+    if (mode != Mode::Sync) {
+        throw NotImplementedException("BAR mode {}", mode);
+    }
+    if (bar.is_a_imm == 0) {
+        throw NotImplementedException("Non-immediate input A");
+    }
+    if (bar.imm_a != 0) {
+        throw NotImplementedException("Non-zero input A");
+    }
+    if (bar.is_b_imm == 0) {
+        throw NotImplementedException("Non-immediate input B");
+    }
+    if (bar.imm_b != 0) {
+        throw NotImplementedException("Non-zero input B");
+    }
+    if (bar.pred != IR::Pred::PT && bar.neg_pred != 0) {
+        throw NotImplementedException("Non-true input predicate");
+    }
+    ir.Barrier();
 }
 
 } // namespace Shader::Maxwell