ConstantFolding.cs
  1  using Ryujinx.Common.Utilities;
  2  using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  3  using System;
  4  using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  5  
  6  namespace Ryujinx.Graphics.Shader.Translation.Optimizations
  7  {
  8      static class ConstantFolding
  9      {
 10          public static void RunPass(ResourceManager resourceManager, Operation operation)
 11          {
 12              if (!AreAllSourcesConstant(operation))
 13              {
 14                  return;
 15              }
 16  
 17              switch (operation.Inst)
 18              {
 19                  case Instruction.Add:
 20                      EvaluateBinary(operation, (x, y) => x + y);
 21                      break;
 22  
 23                  case Instruction.BitCount:
 24                      EvaluateUnary(operation, (x) => BitCount(x));
 25                      break;
 26  
 27                  case Instruction.BitwiseAnd:
 28                      EvaluateBinary(operation, (x, y) => x & y);
 29                      break;
 30  
 31                  case Instruction.BitwiseExclusiveOr:
 32                      EvaluateBinary(operation, (x, y) => x ^ y);
 33                      break;
 34  
 35                  case Instruction.BitwiseNot:
 36                      EvaluateUnary(operation, (x) => ~x);
 37                      break;
 38  
 39                  case Instruction.BitwiseOr:
 40                      EvaluateBinary(operation, (x, y) => x | y);
 41                      break;
 42  
 43                  case Instruction.BitfieldExtractS32:
 44                      BitfieldExtractS32(operation);
 45                      break;
 46  
 47                  case Instruction.BitfieldExtractU32:
 48                      BitfieldExtractU32(operation);
 49                      break;
 50  
 51                  case Instruction.Clamp:
 52                      EvaluateTernary(operation, (x, y, z) => Math.Clamp(x, y, z));
 53                      break;
 54  
 55                  case Instruction.ClampU32:
 56                      EvaluateTernary(operation, (x, y, z) => (int)Math.Clamp((uint)x, (uint)y, (uint)z));
 57                      break;
 58  
 59                  case Instruction.CompareEqual:
 60                      EvaluateBinary(operation, (x, y) => x == y);
 61                      break;
 62  
 63                  case Instruction.CompareGreater:
 64                      EvaluateBinary(operation, (x, y) => x > y);
 65                      break;
 66  
 67                  case Instruction.CompareGreaterOrEqual:
 68                      EvaluateBinary(operation, (x, y) => x >= y);
 69                      break;
 70  
 71                  case Instruction.CompareGreaterOrEqualU32:
 72                      EvaluateBinary(operation, (x, y) => (uint)x >= (uint)y);
 73                      break;
 74  
 75                  case Instruction.CompareGreaterU32:
 76                      EvaluateBinary(operation, (x, y) => (uint)x > (uint)y);
 77                      break;
 78  
 79                  case Instruction.CompareLess:
 80                      EvaluateBinary(operation, (x, y) => x < y);
 81                      break;
 82  
 83                  case Instruction.CompareLessOrEqual:
 84                      EvaluateBinary(operation, (x, y) => x <= y);
 85                      break;
 86  
 87                  case Instruction.CompareLessOrEqualU32:
 88                      EvaluateBinary(operation, (x, y) => (uint)x <= (uint)y);
 89                      break;
 90  
 91                  case Instruction.CompareLessU32:
 92                      EvaluateBinary(operation, (x, y) => (uint)x < (uint)y);
 93                      break;
 94  
 95                  case Instruction.CompareNotEqual:
 96                      EvaluateBinary(operation, (x, y) => x != y);
 97                      break;
 98  
 99                  case Instruction.Divide:
100                      EvaluateBinary(operation, (x, y) => y != 0 ? x / y : 0);
101                      break;
102  
103                  case Instruction.FP32 | Instruction.Add:
104                      EvaluateFPBinary(operation, (x, y) => x + y);
105                      break;
106  
107                  case Instruction.FP32 | Instruction.Clamp:
108                      EvaluateFPTernary(operation, (x, y, z) => Math.Clamp(x, y, z));
109                      break;
110  
111                  case Instruction.FP32 | Instruction.CompareEqual:
112                      EvaluateFPBinary(operation, (x, y) => x == y);
113                      break;
114  
115                  case Instruction.FP32 | Instruction.CompareGreater:
116                      EvaluateFPBinary(operation, (x, y) => x > y);
117                      break;
118  
119                  case Instruction.FP32 | Instruction.CompareGreaterOrEqual:
120                      EvaluateFPBinary(operation, (x, y) => x >= y);
121                      break;
122  
123                  case Instruction.FP32 | Instruction.CompareLess:
124                      EvaluateFPBinary(operation, (x, y) => x < y);
125                      break;
126  
127                  case Instruction.FP32 | Instruction.CompareLessOrEqual:
128                      EvaluateFPBinary(operation, (x, y) => x <= y);
129                      break;
130  
131                  case Instruction.FP32 | Instruction.CompareNotEqual:
132                      EvaluateFPBinary(operation, (x, y) => x != y);
133                      break;
134  
135                  case Instruction.FP32 | Instruction.Divide:
136                      EvaluateFPBinary(operation, (x, y) => x / y);
137                      break;
138  
139                  case Instruction.FP32 | Instruction.Multiply:
140                      EvaluateFPBinary(operation, (x, y) => x * y);
141                      break;
142  
143                  case Instruction.FP32 | Instruction.Negate:
144                      EvaluateFPUnary(operation, (x) => -x);
145                      break;
146  
147                  case Instruction.FP32 | Instruction.Subtract:
148                      EvaluateFPBinary(operation, (x, y) => x - y);
149                      break;
150  
151                  case Instruction.IsNan:
152                      EvaluateFPUnary(operation, (x) => float.IsNaN(x));
153                      break;
154  
155                  case Instruction.Load:
156                      if (operation.StorageKind == StorageKind.ConstantBuffer && operation.SourcesCount == 4)
157                      {
158                          int binding = operation.GetSource(0).Value;
159                          int fieldIndex = operation.GetSource(1).Value;
160  
161                          if (resourceManager.TryGetConstantBufferSlot(binding, out int cbufSlot) && fieldIndex == 0)
162                          {
163                              int vecIndex = operation.GetSource(2).Value;
164                              int elemIndex = operation.GetSource(3).Value;
165                              int cbufOffset = vecIndex * 4 + elemIndex;
166  
167                              operation.TurnIntoCopy(Cbuf(cbufSlot, cbufOffset));
168                          }
169                      }
170                      break;
171  
172                  case Instruction.Maximum:
173                      EvaluateBinary(operation, (x, y) => Math.Max(x, y));
174                      break;
175  
176                  case Instruction.MaximumU32:
177                      EvaluateBinary(operation, (x, y) => (int)Math.Max((uint)x, (uint)y));
178                      break;
179  
180                  case Instruction.Minimum:
181                      EvaluateBinary(operation, (x, y) => Math.Min(x, y));
182                      break;
183  
184                  case Instruction.MinimumU32:
185                      EvaluateBinary(operation, (x, y) => (int)Math.Min((uint)x, (uint)y));
186                      break;
187  
188                  case Instruction.Multiply:
189                      EvaluateBinary(operation, (x, y) => x * y);
190                      break;
191  
192                  case Instruction.Negate:
193                      EvaluateUnary(operation, (x) => -x);
194                      break;
195  
196                  case Instruction.ShiftLeft:
197                      EvaluateBinary(operation, (x, y) => x << y);
198                      break;
199  
200                  case Instruction.ShiftRightS32:
201                      EvaluateBinary(operation, (x, y) => x >> y);
202                      break;
203  
204                  case Instruction.ShiftRightU32:
205                      EvaluateBinary(operation, (x, y) => (int)((uint)x >> y));
206                      break;
207  
208                  case Instruction.Subtract:
209                      EvaluateBinary(operation, (x, y) => x - y);
210                      break;
211  
212                  case Instruction.UnpackHalf2x16:
213                      UnpackHalf2x16(operation);
214                      break;
215              }
216          }
217  
218          private static bool AreAllSourcesConstant(Operation operation)
219          {
220              for (int index = 0; index < operation.SourcesCount; index++)
221              {
222                  if (operation.GetSource(index).Type != OperandType.Constant)
223                  {
224                      return false;
225                  }
226              }
227  
228              return true;
229          }
230  
231          private static int BitCount(int value)
232          {
233              int count = 0;
234  
235              for (int bit = 0; bit < 32; bit++)
236              {
237                  if (value.Extract(bit))
238                  {
239                      count++;
240                  }
241              }
242  
243              return count;
244          }
245  
246          private static void BitfieldExtractS32(Operation operation)
247          {
248              int value = GetBitfieldExtractValue(operation);
249  
250              int shift = 32 - operation.GetSource(2).Value;
251  
252              value = (value << shift) >> shift;
253  
254              operation.TurnIntoCopy(Const(value));
255          }
256  
257          private static void BitfieldExtractU32(Operation operation)
258          {
259              operation.TurnIntoCopy(Const(GetBitfieldExtractValue(operation)));
260          }
261  
262          private static int GetBitfieldExtractValue(Operation operation)
263          {
264              int value = operation.GetSource(0).Value;
265              int lsb = operation.GetSource(1).Value;
266              int length = operation.GetSource(2).Value;
267  
268              return value.Extract(lsb, length);
269          }
270  
271          private static void UnpackHalf2x16(Operation operation)
272          {
273              int value = operation.GetSource(0).Value;
274  
275              value = (value >> operation.Index * 16) & 0xffff;
276  
277              operation.TurnIntoCopy(ConstF((float)BitConverter.UInt16BitsToHalf((ushort)value)));
278          }
279  
280          private static void EvaluateUnary(Operation operation, Func<int, int> op)
281          {
282              int x = operation.GetSource(0).Value;
283  
284              operation.TurnIntoCopy(Const(op(x)));
285          }
286  
287          private static void EvaluateFPUnary(Operation operation, Func<float, float> op)
288          {
289              float x = operation.GetSource(0).AsFloat();
290  
291              operation.TurnIntoCopy(ConstF(op(x)));
292          }
293  
294          private static void EvaluateFPUnary(Operation operation, Func<float, bool> op)
295          {
296              float x = operation.GetSource(0).AsFloat();
297  
298              operation.TurnIntoCopy(Const(op(x) ? IrConsts.True : IrConsts.False));
299          }
300  
301          private static void EvaluateBinary(Operation operation, Func<int, int, int> op)
302          {
303              int x = operation.GetSource(0).Value;
304              int y = operation.GetSource(1).Value;
305  
306              operation.TurnIntoCopy(Const(op(x, y)));
307          }
308  
309          private static void EvaluateBinary(Operation operation, Func<int, int, bool> op)
310          {
311              int x = operation.GetSource(0).Value;
312              int y = operation.GetSource(1).Value;
313  
314              operation.TurnIntoCopy(Const(op(x, y) ? IrConsts.True : IrConsts.False));
315          }
316  
317          private static void EvaluateFPBinary(Operation operation, Func<float, float, float> op)
318          {
319              float x = operation.GetSource(0).AsFloat();
320              float y = operation.GetSource(1).AsFloat();
321  
322              operation.TurnIntoCopy(ConstF(op(x, y)));
323          }
324  
325          private static void EvaluateFPBinary(Operation operation, Func<float, float, bool> op)
326          {
327              float x = operation.GetSource(0).AsFloat();
328              float y = operation.GetSource(1).AsFloat();
329  
330              operation.TurnIntoCopy(Const(op(x, y) ? IrConsts.True : IrConsts.False));
331          }
332  
333          private static void EvaluateTernary(Operation operation, Func<int, int, int, int> op)
334          {
335              int x = operation.GetSource(0).Value;
336              int y = operation.GetSource(1).Value;
337              int z = operation.GetSource(2).Value;
338  
339              operation.TurnIntoCopy(Const(op(x, y, z)));
340          }
341  
342          private static void EvaluateFPTernary(Operation operation, Func<float, float, float, float> op)
343          {
344              float x = operation.GetSource(0).AsFloat();
345              float y = operation.GetSource(1).AsFloat();
346              float z = operation.GetSource(2).AsFloat();
347  
348              operation.TurnIntoCopy(ConstF(op(x, y, z)));
349          }
350      }
351  }