/ src / ARMeilleure / CodeGen / X86 / PreAllocatorSystemV.cs
PreAllocatorSystemV.cs
  1  using ARMeilleure.IntermediateRepresentation;
  2  using ARMeilleure.Translation;
  3  using System;
  4  using System.Collections.Generic;
  5  using System.Diagnostics;
  6  using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  7  using static ARMeilleure.IntermediateRepresentation.Operation.Factory;
  8  
  9  namespace ARMeilleure.CodeGen.X86
 10  {
 11      class PreAllocatorSystemV : PreAllocator
 12      {
 13          public static void InsertCallCopies(IntrusiveList<Operation> nodes, Operation node)
 14          {
 15              Operand dest = node.Destination;
 16  
 17              List<Operand> sources = new()
 18              {
 19                  node.GetSource(0),
 20              };
 21  
 22              int argsCount = node.SourcesCount - 1;
 23  
 24              int intMax = CallingConvention.GetIntArgumentsOnRegsCount();
 25              int vecMax = CallingConvention.GetVecArgumentsOnRegsCount();
 26  
 27              int intCount = 0;
 28              int vecCount = 0;
 29  
 30              int stackOffset = 0;
 31  
 32              for (int index = 0; index < argsCount; index++)
 33              {
 34                  Operand source = node.GetSource(index + 1);
 35  
 36                  bool passOnReg;
 37  
 38                  if (source.Type.IsInteger())
 39                  {
 40                      passOnReg = intCount < intMax;
 41                  }
 42                  else if (source.Type == OperandType.V128)
 43                  {
 44                      passOnReg = intCount + 1 < intMax;
 45                  }
 46                  else
 47                  {
 48                      passOnReg = vecCount < vecMax;
 49                  }
 50  
 51                  if (source.Type == OperandType.V128 && passOnReg)
 52                  {
 53                      // V128 is a struct, we pass each half on a GPR if possible.
 54                      Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64);
 55                      Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64);
 56  
 57                      nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0)));
 58                      nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1)));
 59  
 60                      continue;
 61                  }
 62  
 63                  if (passOnReg)
 64                  {
 65                      Operand argReg = source.Type.IsInteger()
 66                          ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type)
 67                          : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type);
 68  
 69                      Operation copyOp = Operation(Instruction.Copy, argReg, source);
 70  
 71                      InsertConstantRegCopies(nodes, nodes.AddBefore(node, copyOp));
 72  
 73                      sources.Add(argReg);
 74                  }
 75                  else
 76                  {
 77                      Operand offset = Const(stackOffset);
 78  
 79                      Operation spillOp = Operation(Instruction.SpillArg, default, offset, source);
 80  
 81                      InsertConstantRegCopies(nodes, nodes.AddBefore(node, spillOp));
 82  
 83                      stackOffset += source.Type.GetSizeInBytes();
 84                  }
 85              }
 86  
 87              node.SetSources(sources.ToArray());
 88  
 89              if (dest != default)
 90              {
 91                  if (dest.Type == OperandType.V128)
 92                  {
 93                      Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64);
 94                      Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64);
 95  
 96                      Operation operation = node;
 97  
 98                      node = nodes.AddAfter(node, Operation(Instruction.VectorCreateScalar, dest, retLReg));
 99                      nodes.AddAfter(node, Operation(Instruction.VectorInsert, dest, dest, retHReg, Const(1)));
100  
101                      operation.Destination = default;
102                  }
103                  else
104                  {
105                      Operand retReg = dest.Type.IsInteger()
106                          ? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type)
107                          : Xmm(CallingConvention.GetVecReturnRegister(), dest.Type);
108  
109                      Operation copyOp = Operation(Instruction.Copy, dest, retReg);
110  
111                      nodes.AddAfter(node, copyOp);
112  
113                      node.Destination = retReg;
114                  }
115              }
116          }
117  
118          public static void InsertTailcallCopies(IntrusiveList<Operation> nodes, Operation node)
119          {
120              List<Operand> sources = new()
121              {
122                  node.GetSource(0),
123              };
124  
125              int argsCount = node.SourcesCount - 1;
126  
127              int intMax = CallingConvention.GetIntArgumentsOnRegsCount();
128              int vecMax = CallingConvention.GetVecArgumentsOnRegsCount();
129  
130              int intCount = 0;
131              int vecCount = 0;
132  
133              // Handle arguments passed on registers.
134              for (int index = 0; index < argsCount; index++)
135              {
136                  Operand source = node.GetSource(1 + index);
137  
138                  bool passOnReg;
139  
140                  if (source.Type.IsInteger())
141                  {
142                      passOnReg = intCount + 1 < intMax;
143                  }
144                  else
145                  {
146                      passOnReg = vecCount < vecMax;
147                  }
148  
149                  if (source.Type == OperandType.V128 && passOnReg)
150                  {
151                      // V128 is a struct, we pass each half on a GPR if possible.
152                      Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64);
153                      Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64);
154  
155                      nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0)));
156                      nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1)));
157  
158                      continue;
159                  }
160  
161                  if (passOnReg)
162                  {
163                      Operand argReg = source.Type.IsInteger()
164                          ? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type)
165                          : Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type);
166  
167                      Operation copyOp = Operation(Instruction.Copy, argReg, source);
168  
169                      InsertConstantRegCopies(nodes, nodes.AddBefore(node, copyOp));
170  
171                      sources.Add(argReg);
172                  }
173                  else
174                  {
175                      throw new NotImplementedException("Spilling is not currently supported for tail calls. (too many arguments)");
176                  }
177              }
178  
179              // The target address must be on the return registers, since we
180              // don't return anything and it is guaranteed to not be a
181              // callee saved register (which would be trashed on the epilogue).
182              Operand retReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64);
183  
184              Operation addrCopyOp = Operation(Instruction.Copy, retReg, node.GetSource(0));
185  
186              nodes.AddBefore(node, addrCopyOp);
187  
188              sources[0] = retReg;
189  
190              node.SetSources(sources.ToArray());
191          }
192  
193          public static Operation InsertLoadArgumentCopy(
194              CompilerContext cctx,
195              ref Span<Operation> buffer,
196              IntrusiveList<Operation> nodes,
197              Operand[] preservedArgs,
198              Operation node)
199          {
200              Operand source = node.GetSource(0);
201  
202              Debug.Assert(source.Kind == OperandKind.Constant, "Non-constant LoadArgument source kind.");
203  
204              int index = source.AsInt32();
205  
206              int intCount = 0;
207              int vecCount = 0;
208  
209              for (int cIndex = 0; cIndex < index; cIndex++)
210              {
211                  OperandType argType = cctx.FuncArgTypes[cIndex];
212  
213                  if (argType.IsInteger())
214                  {
215                      intCount++;
216                  }
217                  else if (argType == OperandType.V128)
218                  {
219                      intCount += 2;
220                  }
221                  else
222                  {
223                      vecCount++;
224                  }
225              }
226  
227              bool passOnReg;
228  
229              if (source.Type.IsInteger())
230              {
231                  passOnReg = intCount < CallingConvention.GetIntArgumentsOnRegsCount();
232              }
233              else if (source.Type == OperandType.V128)
234              {
235                  passOnReg = intCount + 1 < CallingConvention.GetIntArgumentsOnRegsCount();
236              }
237              else
238              {
239                  passOnReg = vecCount < CallingConvention.GetVecArgumentsOnRegsCount();
240              }
241  
242              if (passOnReg)
243              {
244                  Operand dest = node.Destination;
245  
246                  if (preservedArgs[index] == default)
247                  {
248                      if (dest.Type == OperandType.V128)
249                      {
250                          // V128 is a struct, we pass each half on a GPR if possible.
251                          Operand pArg = Local(OperandType.V128);
252  
253                          Operand argLReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount), OperandType.I64);
254                          Operand argHReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount + 1), OperandType.I64);
255  
256                          Operation copyL = Operation(Instruction.VectorCreateScalar, pArg, argLReg);
257                          Operation copyH = Operation(Instruction.VectorInsert, pArg, pArg, argHReg, Const(1));
258  
259                          cctx.Cfg.Entry.Operations.AddFirst(copyH);
260                          cctx.Cfg.Entry.Operations.AddFirst(copyL);
261  
262                          preservedArgs[index] = pArg;
263                      }
264                      else
265                      {
266                          Operand pArg = Local(dest.Type);
267  
268                          Operand argReg = dest.Type.IsInteger()
269                              ? Gpr(CallingConvention.GetIntArgumentRegister(intCount), dest.Type)
270                              : Xmm(CallingConvention.GetVecArgumentRegister(vecCount), dest.Type);
271  
272                          Operation copyOp = Operation(Instruction.Copy, pArg, argReg);
273  
274                          cctx.Cfg.Entry.Operations.AddFirst(copyOp);
275  
276                          preservedArgs[index] = pArg;
277                      }
278                  }
279  
280                  Operation nextNode;
281  
282                  if (dest.AssignmentsCount == 1)
283                  {
284                      // Let's propagate the argument if we can to avoid copies.
285                      PreAllocatorCommon.Propagate(ref buffer, dest, preservedArgs[index]);
286                      nextNode = node.ListNext;
287                  }
288                  else
289                  {
290                      Operation argCopyOp = Operation(Instruction.Copy, dest, preservedArgs[index]);
291                      nextNode = nodes.AddBefore(node, argCopyOp);
292                  }
293  
294                  Delete(nodes, node);
295                  return nextNode;
296              }
297              else
298              {
299                  // TODO: Pass on stack.
300                  return node;
301              }
302          }
303  
304          public static void InsertReturnCopy(IntrusiveList<Operation> nodes, Operation node)
305          {
306              if (node.SourcesCount == 0)
307              {
308                  return;
309              }
310  
311              Operand source = node.GetSource(0);
312  
313              if (source.Type == OperandType.V128)
314              {
315                  Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64);
316                  Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64);
317  
318                  nodes.AddBefore(node, Operation(Instruction.VectorExtract, retLReg, source, Const(0)));
319                  nodes.AddBefore(node, Operation(Instruction.VectorExtract, retHReg, source, Const(1)));
320              }
321              else
322              {
323                  Operand retReg = source.Type.IsInteger()
324                      ? Gpr(CallingConvention.GetIntReturnRegister(), source.Type)
325                      : Xmm(CallingConvention.GetVecReturnRegister(), source.Type);
326  
327                  Operation retCopyOp = Operation(Instruction.Copy, retReg, source);
328  
329                  nodes.AddBefore(node, retCopyOp);
330              }
331          }
332      }
333  }