/ src / ARMeilleure / Instructions / InstEmitSimdMemory32.cs
InstEmitSimdMemory32.cs
  1  using ARMeilleure.Decoders;
  2  using ARMeilleure.IntermediateRepresentation;
  3  using ARMeilleure.State;
  4  using ARMeilleure.Translation;
  5  
  6  using static ARMeilleure.Instructions.InstEmitHelper;
  7  using static ARMeilleure.Instructions.InstEmitMemoryHelper;
  8  using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  9  
 10  namespace ARMeilleure.Instructions
 11  {
 12      static partial class InstEmit32
 13      {
 14          public static void Vld1(ArmEmitterContext context)
 15          {
 16              EmitVStoreOrLoadN(context, 1, true);
 17          }
 18  
 19          public static void Vld2(ArmEmitterContext context)
 20          {
 21              EmitVStoreOrLoadN(context, 2, true);
 22          }
 23  
 24          public static void Vld3(ArmEmitterContext context)
 25          {
 26              EmitVStoreOrLoadN(context, 3, true);
 27          }
 28  
 29          public static void Vld4(ArmEmitterContext context)
 30          {
 31              EmitVStoreOrLoadN(context, 4, true);
 32          }
 33  
 34          public static void Vst1(ArmEmitterContext context)
 35          {
 36              EmitVStoreOrLoadN(context, 1, false);
 37          }
 38  
 39          public static void Vst2(ArmEmitterContext context)
 40          {
 41              EmitVStoreOrLoadN(context, 2, false);
 42          }
 43  
 44          public static void Vst3(ArmEmitterContext context)
 45          {
 46              EmitVStoreOrLoadN(context, 3, false);
 47          }
 48  
 49          public static void Vst4(ArmEmitterContext context)
 50          {
 51              EmitVStoreOrLoadN(context, 4, false);
 52          }
 53  
 54          public static void EmitVStoreOrLoadN(ArmEmitterContext context, int count, bool load)
 55          {
 56              if (context.CurrOp is OpCode32SimdMemSingle)
 57              {
 58                  OpCode32SimdMemSingle op = (OpCode32SimdMemSingle)context.CurrOp;
 59  
 60                  int eBytes = 1 << op.Size;
 61  
 62                  Operand n = context.Copy(GetIntA32(context, op.Rn));
 63  
 64                  // TODO: Check alignment.
 65                  int offset = 0;
 66                  int d = op.Vd;
 67  
 68                  for (int i = 0; i < count; i++)
 69                  {
 70                      // Accesses an element from a double simd register.
 71                      Operand address = context.Add(n, Const(offset));
 72                      if (eBytes == 8)
 73                      {
 74                          if (load)
 75                          {
 76                              EmitDVectorLoad(context, address, d);
 77                          }
 78                          else
 79                          {
 80                              EmitDVectorStore(context, address, d);
 81                          }
 82                      }
 83                      else
 84                      {
 85                          int index = ((d & 1) << (3 - op.Size)) + op.Index;
 86                          if (load)
 87                          {
 88                              if (op.Replicate)
 89                              {
 90                                  var regs = (count > 1) ? 1 : op.Increment;
 91                                  for (int reg = 0; reg < regs; reg++)
 92                                  {
 93                                      int dreg = reg + d;
 94                                      int rIndex = ((dreg & 1) << (3 - op.Size));
 95                                      int limit = rIndex + (1 << (3 - op.Size));
 96  
 97                                      while (rIndex < limit)
 98                                      {
 99                                          EmitLoadSimd(context, address, GetVecA32(dreg >> 1), dreg >> 1, rIndex++, op.Size);
100                                      }
101                                  }
102                              }
103                              else
104                              {
105                                  EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size);
106                              }
107                          }
108                          else
109                          {
110                              EmitStoreSimd(context, address, d >> 1, index, op.Size);
111                          }
112                      }
113                      offset += eBytes;
114                      d += op.Increment;
115                  }
116  
117                  if (op.WBack)
118                  {
119                      if (op.RegisterIndex)
120                      {
121                          Operand m = GetIntA32(context, op.Rm);
122                          SetIntA32(context, op.Rn, context.Add(n, m));
123                      }
124                      else
125                      {
126                          SetIntA32(context, op.Rn, context.Add(n, Const(count * eBytes)));
127                      }
128                  }
129              }
130              else
131              {
132                  OpCode32SimdMemPair op = (OpCode32SimdMemPair)context.CurrOp;
133  
134                  int increment = count > 1 ? op.Increment : 1;
135                  int eBytes = 1 << op.Size;
136  
137                  Operand n = context.Copy(GetIntA32(context, op.Rn));
138                  int offset = 0;
139                  int d = op.Vd;
140  
141                  for (int reg = 0; reg < op.Regs; reg++)
142                  {
143                      for (int elem = 0; elem < op.Elems; elem++)
144                      {
145                          int elemD = d + reg;
146                          for (int i = 0; i < count; i++)
147                          {
148                              // Accesses an element from a double simd register,
149                              // add ebytes for each element.
150                              Operand address = context.Add(n, Const(offset));
151                              int index = ((elemD & 1) << (3 - op.Size)) + elem;
152                              if (eBytes == 8)
153                              {
154                                  if (load)
155                                  {
156                                      EmitDVectorLoad(context, address, elemD);
157                                  }
158                                  else
159                                  {
160                                      EmitDVectorStore(context, address, elemD);
161                                  }
162                              }
163                              else
164                              {
165                                  if (load)
166                                  {
167                                      EmitLoadSimd(context, address, GetVecA32(elemD >> 1), elemD >> 1, index, op.Size);
168                                  }
169                                  else
170                                  {
171                                      EmitStoreSimd(context, address, elemD >> 1, index, op.Size);
172                                  }
173                              }
174  
175                              offset += eBytes;
176                              elemD += increment;
177                          }
178                      }
179                  }
180  
181                  if (op.WBack)
182                  {
183                      if (op.RegisterIndex)
184                      {
185                          Operand m = GetIntA32(context, op.Rm);
186                          SetIntA32(context, op.Rn, context.Add(n, m));
187                      }
188                      else
189                      {
190                          SetIntA32(context, op.Rn, context.Add(n, Const(count * 8 * op.Regs)));
191                      }
192                  }
193              }
194          }
195  
196          public static void Vldm(ArmEmitterContext context)
197          {
198              OpCode32SimdMemMult op = (OpCode32SimdMemMult)context.CurrOp;
199  
200              Operand n = context.Copy(GetIntA32(context, op.Rn));
201  
202              Operand baseAddress = context.Add(n, Const(op.Offset));
203  
204              bool writeBack = op.PostOffset != 0;
205  
206              if (writeBack)
207              {
208                  SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
209              }
210  
211              int range = op.RegisterRange;
212  
213              int sReg = (op.DoubleWidth) ? (op.Vd << 1) : op.Vd;
214              int offset = 0;
215              int byteSize = 4;
216  
217              for (int num = 0; num < range; num++, sReg++)
218              {
219                  Operand address = context.Add(baseAddress, Const(offset));
220                  Operand vec = GetVecA32(sReg >> 2);
221  
222                  EmitLoadSimd(context, address, vec, sReg >> 2, sReg & 3, WordSizeLog2);
223                  offset += byteSize;
224              }
225          }
226  
227          public static void Vstm(ArmEmitterContext context)
228          {
229              OpCode32SimdMemMult op = (OpCode32SimdMemMult)context.CurrOp;
230  
231              Operand n = context.Copy(GetIntA32(context, op.Rn));
232  
233              Operand baseAddress = context.Add(n, Const(op.Offset));
234  
235              bool writeBack = op.PostOffset != 0;
236  
237              if (writeBack)
238              {
239                  SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
240              }
241  
242              int offset = 0;
243  
244              int range = op.RegisterRange;
245              int sReg = (op.DoubleWidth) ? (op.Vd << 1) : op.Vd;
246              int byteSize = 4;
247  
248              for (int num = 0; num < range; num++, sReg++)
249              {
250                  Operand address = context.Add(baseAddress, Const(offset));
251  
252                  EmitStoreSimd(context, address, sReg >> 2, sReg & 3, WordSizeLog2);
253  
254                  offset += byteSize;
255              }
256          }
257  
258          public static void Vldr(ArmEmitterContext context)
259          {
260              EmitVLoadOrStore(context, AccessType.Load);
261          }
262  
263          public static void Vstr(ArmEmitterContext context)
264          {
265              EmitVLoadOrStore(context, AccessType.Store);
266          }
267  
268          private static void EmitDVectorStore(ArmEmitterContext context, Operand address, int vecD)
269          {
270              int vecQ = vecD >> 1;
271              int vecSElem = (vecD & 1) << 1;
272              Operand lblBigEndian = Label();
273              Operand lblEnd = Label();
274  
275              context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
276  
277              EmitStoreSimd(context, address, vecQ, vecSElem, WordSizeLog2);
278              EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem | 1, WordSizeLog2);
279  
280              context.Branch(lblEnd);
281  
282              context.MarkLabel(lblBigEndian);
283  
284              EmitStoreSimd(context, address, vecQ, vecSElem | 1, WordSizeLog2);
285              EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem, WordSizeLog2);
286  
287              context.MarkLabel(lblEnd);
288          }
289  
290          private static void EmitDVectorLoad(ArmEmitterContext context, Operand address, int vecD)
291          {
292              int vecQ = vecD >> 1;
293              int vecSElem = (vecD & 1) << 1;
294              Operand vec = GetVecA32(vecQ);
295  
296              Operand lblBigEndian = Label();
297              Operand lblEnd = Label();
298  
299              context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
300  
301              EmitLoadSimd(context, address, vec, vecQ, vecSElem, WordSizeLog2);
302              EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem | 1, WordSizeLog2);
303  
304              context.Branch(lblEnd);
305  
306              context.MarkLabel(lblBigEndian);
307  
308              EmitLoadSimd(context, address, vec, vecQ, vecSElem | 1, WordSizeLog2);
309              EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem, WordSizeLog2);
310  
311              context.MarkLabel(lblEnd);
312          }
313  
314          private static void EmitVLoadOrStore(ArmEmitterContext context, AccessType accType)
315          {
316              OpCode32SimdMemImm op = (OpCode32SimdMemImm)context.CurrOp;
317  
318              Operand n = context.Copy(GetIntA32(context, op.Rn));
319              Operand m = GetMemM(context, setCarry: false);
320  
321              Operand address = op.Add
322                  ? context.Add(n, m)
323                  : context.Subtract(n, m);
324  
325              int size = op.Size;
326  
327              if ((accType & AccessType.Load) != 0)
328              {
329                  if (size == DWordSizeLog2)
330                  {
331                      EmitDVectorLoad(context, address, op.Vd);
332                  }
333                  else
334                  {
335                      Operand vec = GetVecA32(op.Vd >> 2);
336                      EmitLoadSimd(context, address, vec, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
337                  }
338              }
339              else
340              {
341                  if (size == DWordSizeLog2)
342                  {
343                      EmitDVectorStore(context, address, op.Vd);
344                  }
345                  else
346                  {
347                      EmitStoreSimd(context, address, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
348                  }
349              }
350          }
351      }
352  }