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 }