summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2021-07-25 11:39:04 -0700
committerGravatar GitHub2021-07-25 11:39:04 -0700
commit98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f (patch)
tree816faa96c2c4d291825063433331a8ea4b3d08f1 /src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
parentMerge pull request #6699 from lat9nq/common-threads (diff)
parentshader: Support out of bound local memory reads and immediate writes (diff)
downloadyuzu-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/spirv/emit_spirv_integer.cpp')
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
new file mode 100644
index 000000000..3501d7495
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
@@ -0,0 +1,270 @@
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 "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7
8namespace Shader::Backend::SPIRV {
9namespace {
10void SetZeroFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
11 IR::Inst* const zero{inst->GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
12 if (!zero) {
13 return;
14 }
15 zero->SetDefinition(ctx.OpIEqual(ctx.U1, result, ctx.u32_zero_value));
16 zero->Invalidate();
17}
18
19void SetSignFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
20 IR::Inst* const sign{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
21 if (!sign) {
22 return;
23 }
24 sign->SetDefinition(ctx.OpSLessThan(ctx.U1, result, ctx.u32_zero_value));
25 sign->Invalidate();
26}
27} // Anonymous namespace
28
29Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
30 Id result{};
31 if (IR::Inst* const carry{inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
32 const Id carry_type{ctx.TypeStruct(ctx.U32[1], ctx.U32[1])};
33 const Id carry_result{ctx.OpIAddCarry(carry_type, a, b)};
34 result = ctx.OpCompositeExtract(ctx.U32[1], carry_result, 0U);
35
36 const Id carry_value{ctx.OpCompositeExtract(ctx.U32[1], carry_result, 1U)};
37 carry->SetDefinition(ctx.OpINotEqual(ctx.U1, carry_value, ctx.u32_zero_value));
38 carry->Invalidate();
39 } else {
40 result = ctx.OpIAdd(ctx.U32[1], a, b);
41 }
42 SetZeroFlag(ctx, inst, result);
43 SetSignFlag(ctx, inst, result);
44 if (IR::Inst * overflow{inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) {
45 // https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c
46 constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())};
47 const Id is_positive{ctx.OpSGreaterThanEqual(ctx.U1, a, ctx.u32_zero_value)};
48 const Id sub_a{ctx.OpISub(ctx.U32[1], ctx.Const(s32_max), a)};
49
50 const Id positive_test{ctx.OpSGreaterThan(ctx.U1, b, sub_a)};
51 const Id negative_test{ctx.OpSLessThan(ctx.U1, b, sub_a)};
52 const Id carry_flag{ctx.OpSelect(ctx.U1, is_positive, positive_test, negative_test)};
53 overflow->SetDefinition(carry_flag);
54 overflow->Invalidate();
55 }
56 return result;
57}
58
59Id EmitIAdd64(EmitContext& ctx, Id a, Id b) {
60 return ctx.OpIAdd(ctx.U64, a, b);
61}
62
63Id EmitISub32(EmitContext& ctx, Id a, Id b) {
64 return ctx.OpISub(ctx.U32[1], a, b);
65}
66
67Id EmitISub64(EmitContext& ctx, Id a, Id b) {
68 return ctx.OpISub(ctx.U64, a, b);
69}
70
71Id EmitIMul32(EmitContext& ctx, Id a, Id b) {
72 return ctx.OpIMul(ctx.U32[1], a, b);
73}
74
75Id EmitINeg32(EmitContext& ctx, Id value) {
76 return ctx.OpSNegate(ctx.U32[1], value);
77}
78
79Id EmitINeg64(EmitContext& ctx, Id value) {
80 return ctx.OpSNegate(ctx.U64, value);
81}
82
83Id EmitIAbs32(EmitContext& ctx, Id value) {
84 return ctx.OpSAbs(ctx.U32[1], value);
85}
86
87Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) {
88 return ctx.OpShiftLeftLogical(ctx.U32[1], base, shift);
89}
90
91Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift) {
92 return ctx.OpShiftLeftLogical(ctx.U64, base, shift);
93}
94
95Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift) {
96 return ctx.OpShiftRightLogical(ctx.U32[1], base, shift);
97}
98
99Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift) {
100 return ctx.OpShiftRightLogical(ctx.U64, base, shift);
101}
102
103Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift) {
104 return ctx.OpShiftRightArithmetic(ctx.U32[1], base, shift);
105}
106
107Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift) {
108 return ctx.OpShiftRightArithmetic(ctx.U64, base, shift);
109}
110
111Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
112 const Id result{ctx.OpBitwiseAnd(ctx.U32[1], a, b)};
113 SetZeroFlag(ctx, inst, result);
114 SetSignFlag(ctx, inst, result);
115 return result;
116}
117
118Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
119 const Id result{ctx.OpBitwiseOr(ctx.U32[1], a, b)};
120 SetZeroFlag(ctx, inst, result);
121 SetSignFlag(ctx, inst, result);
122 return result;
123}
124
125Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
126 const Id result{ctx.OpBitwiseXor(ctx.U32[1], a, b)};
127 SetZeroFlag(ctx, inst, result);
128 SetSignFlag(ctx, inst, result);
129 return result;
130}
131
132Id EmitBitFieldInsert(EmitContext& ctx, Id base, Id insert, Id offset, Id count) {
133 return ctx.OpBitFieldInsert(ctx.U32[1], base, insert, offset, count);
134}
135
136Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
137 const Id result{ctx.OpBitFieldSExtract(ctx.U32[1], base, offset, count)};
138 SetZeroFlag(ctx, inst, result);
139 SetSignFlag(ctx, inst, result);
140 return result;
141}
142
143Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
144 const Id result{ctx.OpBitFieldUExtract(ctx.U32[1], base, offset, count)};
145 SetZeroFlag(ctx, inst, result);
146 SetSignFlag(ctx, inst, result);
147 return result;
148}
149
150Id EmitBitReverse32(EmitContext& ctx, Id value) {
151 return ctx.OpBitReverse(ctx.U32[1], value);
152}
153
154Id EmitBitCount32(EmitContext& ctx, Id value) {
155 return ctx.OpBitCount(ctx.U32[1], value);
156}
157
158Id EmitBitwiseNot32(EmitContext& ctx, Id value) {
159 return ctx.OpNot(ctx.U32[1], value);
160}
161
162Id EmitFindSMsb32(EmitContext& ctx, Id value) {
163 return ctx.OpFindSMsb(ctx.U32[1], value);
164}
165
166Id EmitFindUMsb32(EmitContext& ctx, Id value) {
167 return ctx.OpFindUMsb(ctx.U32[1], value);
168}
169
170Id EmitSMin32(EmitContext& ctx, Id a, Id b) {
171 const bool is_broken{ctx.profile.has_broken_signed_operations};
172 if (is_broken) {
173 a = ctx.OpBitcast(ctx.S32[1], a);
174 b = ctx.OpBitcast(ctx.S32[1], b);
175 }
176 const Id result{ctx.OpSMin(ctx.U32[1], a, b)};
177 return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
178}
179
180Id EmitUMin32(EmitContext& ctx, Id a, Id b) {
181 return ctx.OpUMin(ctx.U32[1], a, b);
182}
183
184Id EmitSMax32(EmitContext& ctx, Id a, Id b) {
185 const bool is_broken{ctx.profile.has_broken_signed_operations};
186 if (is_broken) {
187 a = ctx.OpBitcast(ctx.S32[1], a);
188 b = ctx.OpBitcast(ctx.S32[1], b);
189 }
190 const Id result{ctx.OpSMax(ctx.U32[1], a, b)};
191 return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
192}
193
194Id EmitUMax32(EmitContext& ctx, Id a, Id b) {
195 return ctx.OpUMax(ctx.U32[1], a, b);
196}
197
198Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
199 Id result{};
200 if (ctx.profile.has_broken_signed_operations || ctx.profile.has_broken_spirv_clamp) {
201 value = ctx.OpBitcast(ctx.S32[1], value);
202 min = ctx.OpBitcast(ctx.S32[1], min);
203 max = ctx.OpBitcast(ctx.S32[1], max);
204 if (ctx.profile.has_broken_spirv_clamp) {
205 result = ctx.OpSMax(ctx.S32[1], ctx.OpSMin(ctx.S32[1], value, max), min);
206 } else {
207 result = ctx.OpSClamp(ctx.S32[1], value, min, max);
208 }
209 result = ctx.OpBitcast(ctx.U32[1], result);
210 } else {
211 result = ctx.OpSClamp(ctx.U32[1], value, min, max);
212 }
213 SetZeroFlag(ctx, inst, result);
214 SetSignFlag(ctx, inst, result);
215 return result;
216}
217
218Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
219 Id result{};
220 if (ctx.profile.has_broken_spirv_clamp) {
221 result = ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], value, max), min);
222 } else {
223 result = ctx.OpUClamp(ctx.U32[1], value, min, max);
224 }
225 SetZeroFlag(ctx, inst, result);
226 SetSignFlag(ctx, inst, result);
227 return result;
228}
229
230Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) {
231 return ctx.OpSLessThan(ctx.U1, lhs, rhs);
232}
233
234Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs) {
235 return ctx.OpULessThan(ctx.U1, lhs, rhs);
236}
237
238Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs) {
239 return ctx.OpIEqual(ctx.U1, lhs, rhs);
240}
241
242Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
243 return ctx.OpSLessThanEqual(ctx.U1, lhs, rhs);
244}
245
246Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
247 return ctx.OpULessThanEqual(ctx.U1, lhs, rhs);
248}
249
250Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
251 return ctx.OpSGreaterThan(ctx.U1, lhs, rhs);
252}
253
254Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
255 return ctx.OpUGreaterThan(ctx.U1, lhs, rhs);
256}
257
258Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs) {
259 return ctx.OpINotEqual(ctx.U1, lhs, rhs);
260}
261
262Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
263 return ctx.OpSGreaterThanEqual(ctx.U1, lhs, rhs);
264}
265
266Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
267 return ctx.OpUGreaterThanEqual(ctx.U1, lhs, rhs);
268}
269
270} // namespace Shader::Backend::SPIRV