diff options
| author | 2021-07-25 11:39:04 -0700 | |
|---|---|---|
| committer | 2021-07-25 11:39:04 -0700 | |
| commit | 98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f (patch) | |
| tree | 816faa96c2c4d291825063433331a8ea4b3d08f1 /src/shader_recompiler/backend/glasm/reg_alloc.cpp | |
| parent | Merge pull request #6699 from lat9nq/common-threads (diff) | |
| parent | shader: Support out of bound local memory reads and immediate writes (diff) | |
| download | yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.gz yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.xz yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.zip | |
Merge pull request #6585 from ameerj/hades
Shader Decompiler Rewrite
Diffstat (limited to 'src/shader_recompiler/backend/glasm/reg_alloc.cpp')
| -rw-r--r-- | src/shader_recompiler/backend/glasm/reg_alloc.cpp | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/src/shader_recompiler/backend/glasm/reg_alloc.cpp b/src/shader_recompiler/backend/glasm/reg_alloc.cpp new file mode 100644 index 000000000..4c046db6e --- /dev/null +++ b/src/shader_recompiler/backend/glasm/reg_alloc.cpp | |||
| @@ -0,0 +1,186 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include <fmt/format.h> | ||
| 8 | |||
| 9 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 10 | #include "shader_recompiler/backend/glasm/reg_alloc.h" | ||
| 11 | #include "shader_recompiler/exception.h" | ||
| 12 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 13 | |||
| 14 | namespace Shader::Backend::GLASM { | ||
| 15 | |||
| 16 | Register RegAlloc::Define(IR::Inst& inst) { | ||
| 17 | return Define(inst, false); | ||
| 18 | } | ||
| 19 | |||
| 20 | Register RegAlloc::LongDefine(IR::Inst& inst) { | ||
| 21 | return Define(inst, true); | ||
| 22 | } | ||
| 23 | |||
| 24 | Value RegAlloc::Peek(const IR::Value& value) { | ||
| 25 | if (value.IsImmediate()) { | ||
| 26 | return MakeImm(value); | ||
| 27 | } else { | ||
| 28 | return PeekInst(*value.Inst()); | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | Value RegAlloc::Consume(const IR::Value& value) { | ||
| 33 | if (value.IsImmediate()) { | ||
| 34 | return MakeImm(value); | ||
| 35 | } else { | ||
| 36 | return ConsumeInst(*value.Inst()); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | void RegAlloc::Unref(IR::Inst& inst) { | ||
| 41 | IR::Inst& value_inst{AliasInst(inst)}; | ||
| 42 | value_inst.DestructiveRemoveUsage(); | ||
| 43 | if (!value_inst.HasUses()) { | ||
| 44 | Free(value_inst.Definition<Id>()); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | Register RegAlloc::AllocReg() { | ||
| 49 | Register ret; | ||
| 50 | ret.type = Type::Register; | ||
| 51 | ret.id = Alloc(false); | ||
| 52 | return ret; | ||
| 53 | } | ||
| 54 | |||
| 55 | Register RegAlloc::AllocLongReg() { | ||
| 56 | Register ret; | ||
| 57 | ret.type = Type::Register; | ||
| 58 | ret.id = Alloc(true); | ||
| 59 | return ret; | ||
| 60 | } | ||
| 61 | |||
| 62 | void RegAlloc::FreeReg(Register reg) { | ||
| 63 | Free(reg.id); | ||
| 64 | } | ||
| 65 | |||
| 66 | Value RegAlloc::MakeImm(const IR::Value& value) { | ||
| 67 | Value ret; | ||
| 68 | switch (value.Type()) { | ||
| 69 | case IR::Type::Void: | ||
| 70 | ret.type = Type::Void; | ||
| 71 | break; | ||
| 72 | case IR::Type::U1: | ||
| 73 | ret.type = Type::U32; | ||
| 74 | ret.imm_u32 = value.U1() ? 0xffffffff : 0; | ||
| 75 | break; | ||
| 76 | case IR::Type::U32: | ||
| 77 | ret.type = Type::U32; | ||
| 78 | ret.imm_u32 = value.U32(); | ||
| 79 | break; | ||
| 80 | case IR::Type::F32: | ||
| 81 | ret.type = Type::U32; | ||
| 82 | ret.imm_u32 = Common::BitCast<u32>(value.F32()); | ||
| 83 | break; | ||
| 84 | case IR::Type::U64: | ||
| 85 | ret.type = Type::U64; | ||
| 86 | ret.imm_u64 = value.U64(); | ||
| 87 | break; | ||
| 88 | case IR::Type::F64: | ||
| 89 | ret.type = Type::U64; | ||
| 90 | ret.imm_u64 = Common::BitCast<u64>(value.F64()); | ||
| 91 | break; | ||
| 92 | default: | ||
| 93 | throw NotImplementedException("Immediate type {}", value.Type()); | ||
| 94 | } | ||
| 95 | return ret; | ||
| 96 | } | ||
| 97 | |||
| 98 | Register RegAlloc::Define(IR::Inst& inst, bool is_long) { | ||
| 99 | if (inst.HasUses()) { | ||
| 100 | inst.SetDefinition<Id>(Alloc(is_long)); | ||
| 101 | } else { | ||
| 102 | Id id{}; | ||
| 103 | id.is_long.Assign(is_long ? 1 : 0); | ||
| 104 | id.is_null.Assign(1); | ||
| 105 | inst.SetDefinition<Id>(id); | ||
| 106 | } | ||
| 107 | return Register{PeekInst(inst)}; | ||
| 108 | } | ||
| 109 | |||
| 110 | Value RegAlloc::PeekInst(IR::Inst& inst) { | ||
| 111 | Value ret; | ||
| 112 | ret.type = Type::Register; | ||
| 113 | ret.id = inst.Definition<Id>(); | ||
| 114 | return ret; | ||
| 115 | } | ||
| 116 | |||
| 117 | Value RegAlloc::ConsumeInst(IR::Inst& inst) { | ||
| 118 | Unref(inst); | ||
| 119 | return PeekInst(inst); | ||
| 120 | } | ||
| 121 | |||
| 122 | Id RegAlloc::Alloc(bool is_long) { | ||
| 123 | size_t& num_regs{is_long ? num_used_long_registers : num_used_registers}; | ||
| 124 | std::bitset<NUM_REGS>& use{is_long ? long_register_use : register_use}; | ||
| 125 | if (num_used_registers + num_used_long_registers < NUM_REGS) { | ||
| 126 | for (size_t reg = 0; reg < NUM_REGS; ++reg) { | ||
| 127 | if (use[reg]) { | ||
| 128 | continue; | ||
| 129 | } | ||
| 130 | num_regs = std::max(num_regs, reg + 1); | ||
| 131 | use[reg] = true; | ||
| 132 | Id ret{}; | ||
| 133 | ret.is_valid.Assign(1); | ||
| 134 | ret.is_long.Assign(is_long ? 1 : 0); | ||
| 135 | ret.is_spill.Assign(0); | ||
| 136 | ret.is_condition_code.Assign(0); | ||
| 137 | ret.is_null.Assign(0); | ||
| 138 | ret.index.Assign(static_cast<u32>(reg)); | ||
| 139 | return ret; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | throw NotImplementedException("Register spilling"); | ||
| 143 | } | ||
| 144 | |||
| 145 | void RegAlloc::Free(Id id) { | ||
| 146 | if (id.is_valid == 0) { | ||
| 147 | throw LogicError("Freeing invalid register"); | ||
| 148 | } | ||
| 149 | if (id.is_spill != 0) { | ||
| 150 | throw NotImplementedException("Free spill"); | ||
| 151 | } | ||
| 152 | if (id.is_long != 0) { | ||
| 153 | long_register_use[id.index] = false; | ||
| 154 | } else { | ||
| 155 | register_use[id.index] = false; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /*static*/ bool RegAlloc::IsAliased(const IR::Inst& inst) { | ||
| 160 | switch (inst.GetOpcode()) { | ||
| 161 | case IR::Opcode::Identity: | ||
| 162 | case IR::Opcode::BitCastU16F16: | ||
| 163 | case IR::Opcode::BitCastU32F32: | ||
| 164 | case IR::Opcode::BitCastU64F64: | ||
| 165 | case IR::Opcode::BitCastF16U16: | ||
| 166 | case IR::Opcode::BitCastF32U32: | ||
| 167 | case IR::Opcode::BitCastF64U64: | ||
| 168 | return true; | ||
| 169 | default: | ||
| 170 | return false; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | /*static*/ IR::Inst& RegAlloc::AliasInst(IR::Inst& inst) { | ||
| 175 | IR::Inst* it{&inst}; | ||
| 176 | while (IsAliased(*it)) { | ||
| 177 | const IR::Value arg{it->Arg(0)}; | ||
| 178 | if (arg.IsImmediate()) { | ||
| 179 | break; | ||
| 180 | } | ||
| 181 | it = arg.InstRecursive(); | ||
| 182 | } | ||
| 183 | return *it; | ||
| 184 | } | ||
| 185 | |||
| 186 | } // namespace Shader::Backend::GLASM | ||