LLVM 24.0.0git
SIMachineFunctionInfo.cpp
Go to the documentation of this file.
1//===- SIMachineFunctionInfo.cpp - SI Machine Function Info ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "AMDGPUSubtarget.h"
11#include "GCNSubtarget.h"
13#include "SIRegisterInfo.h"
21#include "llvm/IR/CallingConv.h"
23#include "llvm/IR/Function.h"
24#include <cassert>
25#include <optional>
26#include <vector>
27
28enum { MAX_LANES = 64 };
29
30using namespace llvm;
31
32// TODO -- delete this flag once we have more robust mechanisms to allocate the
33// optimal RC for Opc and Dest of MFMA. In particular, there are high RP cases
34// where it is better to produce the VGPR form (e.g. if there are VGPR users
35// of the MFMA result).
37 "amdgpu-mfma-vgpr-form",
38 cl::desc("Whether to force use VGPR for Opc and Dest of MFMA. If "
39 "unspecified, default to compiler heuristics"),
42
44 const SITargetLowering *TLI = STI->getTargetLowering();
45 return static_cast<const GCNTargetMachine &>(TLI->getTargetMachine());
46}
47
49
51 const GCNSubtarget *STI)
52 : AMDGPUMachineFunctionInfo(F, *STI), Mode(F, *STI),
53 GWSResourcePSV(getTM(STI)), UserSGPRInfo(F, *STI), WorkGroupIDX(false),
54 WorkGroupIDY(false), WorkGroupIDZ(false), WorkGroupInfo(false),
55 LDSKernelId(false), PrivateSegmentWaveByteOffset(false),
56 WorkItemIDX(false), WorkItemIDY(false), WorkItemIDZ(false),
57 ImplicitArgPtr(false), GITPtrHigh(0xffffffff), HighBitsOf32BitAddress(0),
58 IsWholeWaveFunction(F.getCallingConv() ==
59 CallingConv::AMDGPU_Gfx_WholeWave) {
60 const GCNSubtarget &ST = *STI;
61 FlatWorkGroupSizes = ST.getFlatWorkGroupSizes(F);
62 WavesPerEU = ST.getWavesPerEU(F);
63 MaxNumWorkGroups = AMDGPU::getMaxNumWorkGroups(F);
64 assert(MaxNumWorkGroups.size() == 3);
65
66 DynamicVGPRBlockSize = AMDGPU::getDynamicVGPRBlockSize(F);
67 Occupancy = ST.computeOccupancy(F, getLDSSize()).second;
68 CallingConv::ID CC = F.getCallingConv();
69
70 VRegFlags.reserve(1024);
71
72 const bool IsKernel = CC == CallingConv::AMDGPU_KERNEL ||
74
75 if (IsKernel) {
76 WorkGroupIDX = true;
77 WorkItemIDX = true;
78 } else if (CC == CallingConv::AMDGPU_PS) {
79 PSInputAddr = AMDGPU::getInitialPSInputAddr(F);
80 }
81
82 if (ST.hasGFX90AInsts()) {
83 // FIXME: Extract logic out of getMaxNumVectorRegs; we need to apply the
84 // allocation granule and clamping.
85 auto [MinNumAGPRAttr, MaxNumAGPRAttr] =
86 AMDGPU::getIntegerPairAttribute(F, "amdgpu-agpr-alloc", {~0u, ~0u},
87 /*OnlyFirstRequired=*/true);
88 MinNumAGPRs = MinNumAGPRAttr;
89 }
90
91 if (!isEntryFunction()) {
92 if (CC != CallingConv::AMDGPU_Gfx &&
95
96 FrameOffsetReg = AMDGPU::SGPR33;
97 StackPtrOffsetReg = AMDGPU::SGPR32;
98
99 if (!ST.hasFlatScratchEnabled()) {
100 // Non-entry functions have no special inputs for now, other registers
101 // required for scratch access.
102 ScratchRSrcReg = AMDGPU::isChainCC(CC)
103 ? AMDGPU::SGPR48_SGPR49_SGPR50_SGPR51
104 : AMDGPU::SGPR0_SGPR1_SGPR2_SGPR3;
105
106 ArgInfo.PrivateSegmentBuffer =
107 ArgDescriptor::createRegister(ScratchRSrcReg);
108 }
109
110 if (!F.hasFnAttribute("amdgpu-no-implicitarg-ptr") &&
112 ImplicitArgPtr = true;
113 } else {
114 ImplicitArgPtr = false;
116 std::max(ST.getAlignmentForImplicitArgPtr(), MaxKernArgAlign);
117 }
118
119 if (!AMDGPU::isGraphics(CC) ||
121 ST.hasArchitectedSGPRs())) {
122 if (IsKernel || !F.hasFnAttribute("amdgpu-no-workgroup-id-x") ||
123 !F.hasFnAttribute("amdgpu-no-cluster-id-x"))
124 WorkGroupIDX = true;
125
126 if (!F.hasFnAttribute("amdgpu-no-workgroup-id-y") ||
127 !F.hasFnAttribute("amdgpu-no-cluster-id-y"))
128 WorkGroupIDY = true;
129
130 if (!F.hasFnAttribute("amdgpu-no-workgroup-id-z") ||
131 !F.hasFnAttribute("amdgpu-no-cluster-id-z"))
132 WorkGroupIDZ = true;
133 }
134
135 if (!AMDGPU::isGraphics(CC)) {
136 if (IsKernel || !F.hasFnAttribute("amdgpu-no-workitem-id-x"))
137 WorkItemIDX = true;
138
139 if (!F.hasFnAttribute("amdgpu-no-workitem-id-y") &&
140 ST.getMaxWorkitemID(F, 1) != 0)
141 WorkItemIDY = true;
142
143 if (!F.hasFnAttribute("amdgpu-no-workitem-id-z") &&
144 ST.getMaxWorkitemID(F, 2) != 0)
145 WorkItemIDZ = true;
146
147 if (!IsKernel && !F.hasFnAttribute("amdgpu-no-lds-kernel-id"))
148 LDSKernelId = true;
149 }
150
151 if (isEntryFunction()) {
152 // X, XY, and XYZ are the only supported combinations, so make sure Y is
153 // enabled if Z is.
154 if (WorkItemIDZ)
155 WorkItemIDY = true;
156
157 if (!ST.hasArchitectedFlatScratch()) {
158 PrivateSegmentWaveByteOffset = true;
159
160 // HS and GS always have the scratch wave offset in SGPR5 on GFX9.
161 if (ST.getGeneration() >= AMDGPUSubtarget::GFX9 &&
163 ArgInfo.PrivateSegmentWaveByteOffset =
164 ArgDescriptor::createRegister(AMDGPU::SGPR5);
165 }
166 }
167
168 Attribute A = F.getFnAttribute("amdgpu-git-ptr-high");
169 StringRef S = A.getValueAsString();
170 if (!S.empty())
171 S.consumeInteger(0, GITPtrHigh);
172
173 A = F.getFnAttribute("amdgpu-32bit-address-high-bits");
174 S = A.getValueAsString();
175 if (!S.empty())
176 S.consumeInteger(0, HighBitsOf32BitAddress);
177
178 MaxMemoryClusterDWords = F.getFnAttributeAsParsedInteger(
179 "amdgpu-max-memory-cluster-dwords", DefaultMemoryClusterDWordsLimit);
180
181 // On GFX908, in order to guarantee copying between AGPRs, we need a scratch
182 // VGPR available at all times. For now, reserve highest available VGPR. After
183 // RA, shift it to the lowest available unused VGPR if the one exist.
184 if (ST.hasMAIInsts() && !ST.hasGFX90AInsts()) {
185 VGPRForAGPRCopy =
186 AMDGPU::VGPR_32RegClass.getRegister(ST.getMaxNumVGPRs(F) - 1);
187 }
188
189 ClusterDims = AMDGPU::ClusterDimsAttr::get(F);
190}
191
198
201 const GCNSubtarget& ST = MF.getSubtarget<GCNSubtarget>();
202 limitOccupancy(ST.getOccupancyWithWorkGroupSizes(MF).second);
203}
204
206 const SIRegisterInfo &TRI) {
207 ArgInfo.PrivateSegmentBuffer =
208 ArgDescriptor::createRegister(TRI.getMatchingSuperReg(
209 getNextUserSGPR(), AMDGPU::sub0, &AMDGPU::SGPR_128RegClass));
210 NumUserSGPRs += 4;
211 return ArgInfo.PrivateSegmentBuffer.getRegister();
212}
213
215 ArgInfo.DispatchPtr = ArgDescriptor::createRegister(TRI.getMatchingSuperReg(
216 getNextUserSGPR(), AMDGPU::sub0, &AMDGPU::SReg_64RegClass));
217 NumUserSGPRs += 2;
218 return ArgInfo.DispatchPtr.getRegister();
219}
220
222 ArgInfo.QueuePtr = ArgDescriptor::createRegister(TRI.getMatchingSuperReg(
223 getNextUserSGPR(), AMDGPU::sub0, &AMDGPU::SReg_64RegClass));
224 NumUserSGPRs += 2;
225 return ArgInfo.QueuePtr.getRegister();
226}
227
229 ArgInfo.KernargSegmentPtr
230 = ArgDescriptor::createRegister(TRI.getMatchingSuperReg(
231 getNextUserSGPR(), AMDGPU::sub0, &AMDGPU::SReg_64RegClass));
232 NumUserSGPRs += 2;
233 return ArgInfo.KernargSegmentPtr.getRegister();
234}
235
237 ArgInfo.DispatchID = ArgDescriptor::createRegister(TRI.getMatchingSuperReg(
238 getNextUserSGPR(), AMDGPU::sub0, &AMDGPU::SReg_64RegClass));
239 NumUserSGPRs += 2;
240 return ArgInfo.DispatchID.getRegister();
241}
242
244 ArgInfo.FlatScratchInit = ArgDescriptor::createRegister(TRI.getMatchingSuperReg(
245 getNextUserSGPR(), AMDGPU::sub0, &AMDGPU::SReg_64RegClass));
246 NumUserSGPRs += 2;
247 return ArgInfo.FlatScratchInit.getRegister();
248}
249
251 ArgInfo.PrivateSegmentSize = ArgDescriptor::createRegister(getNextUserSGPR());
252 NumUserSGPRs += 1;
253 return ArgInfo.PrivateSegmentSize.getRegister();
254}
255
257 ArgInfo.ImplicitBufferPtr = ArgDescriptor::createRegister(TRI.getMatchingSuperReg(
258 getNextUserSGPR(), AMDGPU::sub0, &AMDGPU::SReg_64RegClass));
259 NumUserSGPRs += 2;
260 return ArgInfo.ImplicitBufferPtr.getRegister();
261}
262
264 ArgInfo.LDSKernelId = ArgDescriptor::createRegister(getNextUserSGPR());
265 NumUserSGPRs += 1;
266 return ArgInfo.LDSKernelId.getRegister();
267}
268
270 const SIRegisterInfo &TRI, const TargetRegisterClass *RC,
271 unsigned AllocSizeDWord, int KernArgIdx, int PaddingSGPRs) {
272 auto [It, Inserted] = ArgInfo.PreloadKernArgs.try_emplace(KernArgIdx);
273 assert(Inserted && "Preload kernel argument allocated twice.");
274 NumUserSGPRs += PaddingSGPRs;
275 // If the available register tuples are aligned with the kernarg to be
276 // preloaded use that register, otherwise we need to use a set of SGPRs and
277 // merge them.
278 if (!ArgInfo.FirstKernArgPreloadReg)
279 ArgInfo.FirstKernArgPreloadReg = getNextUserSGPR();
280 Register PreloadReg =
281 TRI.getMatchingSuperReg(getNextUserSGPR(), AMDGPU::sub0, RC);
282 auto &Regs = It->second.Regs;
283 if (PreloadReg &&
284 (RC == &AMDGPU::SReg_32RegClass || RC == &AMDGPU::SReg_64RegClass)) {
285 Regs.push_back(PreloadReg);
286 NumUserSGPRs += AllocSizeDWord;
287 } else {
288 Regs.reserve(AllocSizeDWord);
289 for (unsigned I = 0; I < AllocSizeDWord; ++I) {
290 Regs.push_back(getNextUserSGPR());
291 NumUserSGPRs++;
292 }
293 }
294
295 // Track the actual number of SGPRs that HW will preload to.
296 UserSGPRInfo.allocKernargPreloadSGPRs(AllocSizeDWord + PaddingSGPRs);
297 return &Regs;
298}
299
301 uint64_t Size, Align Alignment) {
302 // Skip if it is an entry function or the register is already added.
303 if (isEntryFunction() || WWMSpills.count(VGPR))
304 return;
305
306 // Skip if this is a function with the amdgpu_cs_chain or
307 // amdgpu_cs_chain_preserve calling convention and this is a scratch register.
308 // We never need to allocate a spill for these because we don't even need to
309 // restore the inactive lanes for them (they're scratchier than the usual
310 // scratch registers). We only need to do this if we have calls to
311 // llvm.amdgcn.cs.chain (otherwise there's no one to save them for, since
312 // chain functions do not return) and the function did not contain a call to
313 // llvm.amdgcn.init.whole.wave (since in that case there are no inactive lanes
314 // when entering the function).
315 if (isChainFunction() &&
318 return;
319
320 WWMSpills.insert(std::make_pair(
321 VGPR, MF.getFrameInfo().CreateSpillStackObject(Size, Alignment)));
322}
323
324// Separate out the callee-saved and scratch registers.
326 MachineFunction &MF,
327 SmallVectorImpl<std::pair<Register, int>> &CalleeSavedRegs,
328 SmallVectorImpl<std::pair<Register, int>> &ScratchRegs) const {
329 const MCPhysReg *CSRegs = MF.getRegInfo().getCalleeSavedRegs();
330 for (auto &Reg : WWMSpills) {
331 if (isCalleeSavedReg(CSRegs, Reg.first))
332 CalleeSavedRegs.push_back(Reg);
333 else
334 ScratchRegs.push_back(Reg);
335 }
336}
337
339 MCPhysReg Reg) const {
340 for (unsigned I = 0; CSRegs[I]; ++I) {
341 if (CSRegs[I] == Reg)
342 return true;
343 }
344
345 return false;
346}
347
350 BitVector &SavedVGPRs) {
351 const SIRegisterInfo *TRI = MF.getSubtarget<GCNSubtarget>().getRegisterInfo();
353 for (unsigned I = 0, E = WWMVGPRs.size(); I < E; ++I) {
354 Register Reg = WWMVGPRs[I];
355 Register NewReg =
356 TRI->findUnusedRegister(MRI, &AMDGPU::VGPR_32RegClass, MF);
357 if (!NewReg || NewReg >= Reg)
358 break;
359
360 MRI.replaceRegWith(Reg, NewReg);
361
362 // Update various tables with the new VGPR.
363 WWMVGPRs[I] = NewReg;
364 WWMReservedRegs.remove(Reg);
365 WWMReservedRegs.insert(NewReg);
366 MRI.reserveReg(NewReg, TRI);
367
368 // Replace the register in SpillPhysVGPRs. This is needed to look for free
369 // lanes while spilling special SGPRs like FP, BP, etc. during PEI.
370 auto *RegItr = llvm::find(SpillPhysVGPRs, Reg);
371 if (RegItr != SpillPhysVGPRs.end()) {
372 unsigned Idx = std::distance(SpillPhysVGPRs.begin(), RegItr);
373 SpillPhysVGPRs[Idx] = NewReg;
374
375 // For replacing registers used in the CFI instructions.
376 MF.replaceFrameInstRegister(Reg, NewReg);
377 }
378
379 // The generic `determineCalleeSaves` might have set the old register if it
380 // is in the CSR range.
381 SavedVGPRs.reset(Reg);
382
383 for (MachineBasicBlock &MBB : MF) {
384 MBB.removeLiveIn(Reg);
385 MBB.sortUniqueLiveIns();
386 }
387
388 Reg = NewReg;
389 }
390}
391
392bool SIMachineFunctionInfo::allocateVirtualVGPRForSGPRSpills(
393 MachineFunction &MF, int FI, unsigned LaneIndex) {
395 Register LaneVGPR;
396 if (!LaneIndex) {
397 LaneVGPR = MRI.createVirtualRegister(&AMDGPU::VGPR_32RegClass);
398 SpillVGPRs.push_back(LaneVGPR);
399 } else {
400 LaneVGPR = SpillVGPRs.back();
401 }
402
403 SGPRSpillsToVirtualVGPRLanes[FI].emplace_back(LaneVGPR, LaneIndex);
404 return true;
405}
406
407bool SIMachineFunctionInfo::allocatePhysicalVGPRForSGPRSpills(
408 MachineFunction &MF, int FI, unsigned LaneIndex, bool IsPrologEpilog) {
409 const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
410 const SIRegisterInfo *TRI = ST.getRegisterInfo();
411 MachineRegisterInfo &MRI = MF.getRegInfo();
412 Register LaneVGPR;
413 if (!LaneIndex) {
414 // Find the highest available register if called before RA to ensure the
415 // lowest registers are available for allocation. The LaneVGPR, in that
416 // case, will be shifted back to the lowest range after VGPR allocation.
417 LaneVGPR = TRI->findUnusedRegister(MRI, &AMDGPU::VGPR_32RegClass, MF,
418 !IsPrologEpilog);
419 if (LaneVGPR == AMDGPU::NoRegister) {
420 // We have no VGPRs left for spilling SGPRs. Reset because we will not
421 // partially spill the SGPR to VGPRs.
422 SGPRSpillsToPhysicalVGPRLanes.erase(FI);
423 return false;
424 }
425
426 if (IsPrologEpilog)
427 allocateWWMSpill(MF, LaneVGPR);
428
429 reserveWWMRegister(LaneVGPR);
430 for (MachineBasicBlock &MBB : MF) {
431 MBB.addLiveIn(LaneVGPR);
433 }
434 SpillPhysVGPRs.push_back(LaneVGPR);
435 } else {
436 LaneVGPR = SpillPhysVGPRs.back();
437 }
438
439 SGPRSpillsToPhysicalVGPRLanes[FI].emplace_back(LaneVGPR, LaneIndex);
440 return true;
441}
442
444 MachineFunction &MF, int FI, bool SpillToPhysVGPRLane,
445 bool IsPrologEpilog) {
446 std::vector<SIRegisterInfo::SpilledReg> &SpillLanes =
447 SpillToPhysVGPRLane ? SGPRSpillsToPhysicalVGPRLanes[FI]
448 : SGPRSpillsToVirtualVGPRLanes[FI];
449
450 // This has already been allocated.
451 if (!SpillLanes.empty())
452 return true;
453
454 const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
455 MachineFrameInfo &FrameInfo = MF.getFrameInfo();
456 unsigned WaveSize = ST.getWavefrontSize();
457
458 unsigned Size = FrameInfo.getObjectSize(FI);
459 unsigned NumLanes = Size / 4;
460
461 if (NumLanes > WaveSize)
462 return false;
463
464 assert(Size >= 4 && "invalid sgpr spill size");
465 assert(ST.getRegisterInfo()->spillSGPRToVGPR() &&
466 "not spilling SGPRs to VGPRs");
467
468 unsigned &NumSpillLanes = SpillToPhysVGPRLane ? NumPhysicalVGPRSpillLanes
469 : NumVirtualVGPRSpillLanes;
470
471 for (unsigned I = 0; I < NumLanes; ++I, ++NumSpillLanes) {
472 unsigned LaneIndex = (NumSpillLanes % WaveSize);
473
474 bool Allocated = SpillToPhysVGPRLane
475 ? allocatePhysicalVGPRForSGPRSpills(MF, FI, LaneIndex,
476 IsPrologEpilog)
477 : allocateVirtualVGPRForSGPRSpills(MF, FI, LaneIndex);
478 if (!Allocated) {
479 NumSpillLanes -= I;
480 return false;
481 }
482 }
483
484 return true;
485}
486
487/// Reserve AGPRs or VGPRs to support spilling for FrameIndex \p FI.
488/// Either AGPR is spilled to VGPR to vice versa.
489/// Returns true if a \p FI can be eliminated completely.
491 int FI,
492 bool isAGPRtoVGPR) {
494 MachineFrameInfo &FrameInfo = MF.getFrameInfo();
495 const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
496
497 assert(ST.hasMAIInsts() && FrameInfo.isSpillSlotObjectIndex(FI));
498
499 auto &Spill = VGPRToAGPRSpills[FI];
500
501 // This has already been allocated.
502 if (!Spill.Lanes.empty())
503 return Spill.FullyAllocated;
504
505 unsigned Size = FrameInfo.getObjectSize(FI);
506 unsigned NumLanes = Size / 4;
507 Spill.Lanes.resize(NumLanes, AMDGPU::NoRegister);
508
509 const TargetRegisterClass &RC =
510 isAGPRtoVGPR ? AMDGPU::VGPR_32RegClass : AMDGPU::AGPR_32RegClass;
511 auto Regs = RC.getRegisters();
512
513 auto &SpillRegs = isAGPRtoVGPR ? SpillAGPR : SpillVGPR;
514 const SIRegisterInfo *TRI = ST.getRegisterInfo();
515 Spill.FullyAllocated = true;
516
517 // FIXME: Move allocation logic out of MachineFunctionInfo and initialize
518 // once.
519 BitVector OtherUsedRegs;
520 OtherUsedRegs.resize(TRI->getNumRegs());
521
522 const uint32_t *CSRMask =
523 TRI->getCallPreservedMask(MF, MF.getFunction().getCallingConv());
524 if (CSRMask)
525 OtherUsedRegs.setBitsInMask(CSRMask);
526
527 // TODO: Should include register tuples, but doesn't matter with current
528 // usage.
529 for (MCPhysReg Reg : SpillAGPR)
530 OtherUsedRegs.set(Reg);
531 for (MCPhysReg Reg : SpillVGPR)
532 OtherUsedRegs.set(Reg);
533
534 SmallVectorImpl<MCPhysReg>::const_iterator NextSpillReg = Regs.begin();
535 for (int I = NumLanes - 1; I >= 0; --I) {
536 NextSpillReg = std::find_if(
537 NextSpillReg, Regs.end(), [&MRI, &OtherUsedRegs](MCPhysReg Reg) {
538 return MRI.isAllocatable(Reg) && !MRI.isPhysRegUsed(Reg) &&
539 !OtherUsedRegs[Reg];
540 });
541
542 if (NextSpillReg == Regs.end()) { // Registers exhausted
543 Spill.FullyAllocated = false;
544 break;
545 }
546
547 OtherUsedRegs.set(*NextSpillReg);
548 SpillRegs.push_back(*NextSpillReg);
549 MRI.reserveReg(*NextSpillReg, TRI);
550 Spill.Lanes[I] = *NextSpillReg++;
551 }
552
553 return Spill.FullyAllocated;
554}
555
557 MachineFrameInfo &MFI, bool ResetSGPRSpillStackIDs) {
558 // Remove dead frame indices from function frame, however keep FP & BP since
559 // spills for them haven't been inserted yet. And also make sure to remove the
560 // frame indices from `SGPRSpillsToVirtualVGPRLanes` data structure,
561 // otherwise, it could result in an unexpected side effect and bug, in case of
562 // any re-mapping of freed frame indices by later pass(es) like "stack slot
563 // coloring".
564 for (auto &R : SGPRSpillsToVirtualVGPRLanes)
565 MFI.RemoveStackObject(R.first);
566 SGPRSpillsToVirtualVGPRLanes.clear();
567
568 // Remove the dead frame indices of CSR SGPRs which are spilled to physical
569 // VGPR lanes during SILowerSGPRSpills pass.
570 if (!ResetSGPRSpillStackIDs) {
571 for (auto &R : SGPRSpillsToPhysicalVGPRLanes)
572 MFI.RemoveStackObject(R.first);
573 SGPRSpillsToPhysicalVGPRLanes.clear();
574 }
575 bool HaveSGPRToMemory = false;
576
577 if (ResetSGPRSpillStackIDs) {
578 // All other SGPRs must be allocated on the default stack, so reset the
579 // stack ID.
580 for (int I = MFI.getObjectIndexBegin(), E = MFI.getObjectIndexEnd(); I != E;
581 ++I) {
585 HaveSGPRToMemory = true;
586 }
587 }
588 }
589 }
590
591 for (auto &R : VGPRToAGPRSpills) {
592 if (R.second.IsDead)
593 MFI.RemoveStackObject(R.first);
594 }
595
596 return HaveSGPRToMemory;
597}
598
600 const SIRegisterInfo &TRI) {
601 if (ScavengeFI)
602 return *ScavengeFI;
603
604 ScavengeFI =
605 MFI.CreateStackObject(TRI.getSpillSize(AMDGPU::SGPR_32RegClass),
606 TRI.getSpillAlign(AMDGPU::SGPR_32RegClass), false);
607 return *ScavengeFI;
608}
609
610MCPhysReg SIMachineFunctionInfo::getNextUserSGPR() const {
611 assert(NumSystemSGPRs == 0 && "System SGPRs must be added after user SGPRs");
612 return AMDGPU::SGPR0 + NumUserSGPRs;
613}
614
615MCPhysReg SIMachineFunctionInfo::getNextSystemSGPR() const {
616 return AMDGPU::SGPR0 + NumUserSGPRs + NumSystemSGPRs;
617}
618
619void SIMachineFunctionInfo::MRI_NoteNewVirtualRegister(Register Reg) {
620 VRegFlags.grow(Reg);
621}
622
623void SIMachineFunctionInfo::MRI_NoteCloneVirtualRegister(Register NewReg,
624 Register SrcReg) {
625 VRegFlags.grow(NewReg);
626 VRegFlags[NewReg] = VRegFlags[SrcReg];
627}
628
631 const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
632 if (!ST.isAmdPalOS())
633 return Register();
634 Register GitPtrLo = AMDGPU::SGPR0; // Low GIT address passed in
635 if (ST.hasMergedShaders()) {
636 switch (MF.getFunction().getCallingConv()) {
639 // Low GIT address is passed in s8 rather than s0 for an LS+HS or
640 // ES+GS merged shader on gfx9+.
641 GitPtrLo = AMDGPU::SGPR8;
642 return GitPtrLo;
643 default:
644 return GitPtrLo;
645 }
646 }
647 return GitPtrLo;
648}
649
651 const TargetRegisterInfo &TRI) {
653 {
654 raw_string_ostream OS(Dest.Value);
655 OS << printReg(Reg, &TRI);
656 }
657 return Dest;
658}
659
660static std::optional<yaml::SIArgumentInfo>
662 const TargetRegisterInfo &TRI) {
664
665 auto convertArg = [&](std::optional<yaml::SIArgument> &A,
666 const ArgDescriptor &Arg) {
667 if (!Arg)
668 return false;
669
670 // Create a register or stack argument.
672 if (Arg.isRegister()) {
674 OS << printReg(Arg.getRegister(), &TRI);
675 } else
676 SA.StackOffset = Arg.getStackOffset();
677 // Check and update the optional mask.
678 if (Arg.isMasked())
679 SA.Mask = Arg.getMask();
680
681 A = std::move(SA);
682 return true;
683 };
684
685 bool Any = false;
686 Any |= convertArg(AI.PrivateSegmentBuffer, ArgInfo.PrivateSegmentBuffer);
687 Any |= convertArg(AI.DispatchPtr, ArgInfo.DispatchPtr);
688 Any |= convertArg(AI.QueuePtr, ArgInfo.QueuePtr);
689 Any |= convertArg(AI.KernargSegmentPtr, ArgInfo.KernargSegmentPtr);
690 Any |= convertArg(AI.DispatchID, ArgInfo.DispatchID);
691 Any |= convertArg(AI.FlatScratchInit, ArgInfo.FlatScratchInit);
692 Any |= convertArg(AI.LDSKernelId, ArgInfo.LDSKernelId);
693 Any |= convertArg(AI.PrivateSegmentSize, ArgInfo.PrivateSegmentSize);
694 Any |= convertArg(AI.WorkGroupIDX, ArgInfo.WorkGroupIDX);
695 Any |= convertArg(AI.WorkGroupIDY, ArgInfo.WorkGroupIDY);
696 Any |= convertArg(AI.WorkGroupIDZ, ArgInfo.WorkGroupIDZ);
697 Any |= convertArg(AI.WorkGroupInfo, ArgInfo.WorkGroupInfo);
698 Any |= convertArg(AI.PrivateSegmentWaveByteOffset,
699 ArgInfo.PrivateSegmentWaveByteOffset);
700 Any |= convertArg(AI.ImplicitArgPtr, ArgInfo.ImplicitArgPtr);
701 Any |= convertArg(AI.ImplicitBufferPtr, ArgInfo.ImplicitBufferPtr);
702 Any |= convertArg(AI.WorkItemIDX, ArgInfo.WorkItemIDX);
703 Any |= convertArg(AI.WorkItemIDY, ArgInfo.WorkItemIDY);
704 Any |= convertArg(AI.WorkItemIDZ, ArgInfo.WorkItemIDZ);
705
706 // Write FirstKernArgPreloadReg separately, since it's a Register,
707 // not ArgDescriptor.
708 if (ArgInfo.FirstKernArgPreloadReg) {
709 Register Reg = ArgInfo.FirstKernArgPreloadReg;
710 assert(Reg.isPhysical() &&
711 "FirstKernArgPreloadReg must be a physical register");
712
715 OS << printReg(Reg, &TRI);
716
718 Any = true;
719 }
720
721 if (Any)
722 return AI;
723
724 return std::nullopt;
725}
726
729 const llvm::MachineFunction &MF)
730 : ExplicitKernArgSize(MFI.getExplicitKernArgSize()),
731 MaxKernArgAlign(MFI.getMaxKernArgAlign()), LDSSize(MFI.getLDSSize()),
732 GDSSize(MFI.getGDSSize()), DynLDSAlign(MFI.getDynLDSAlign()),
733 IsEntryFunction(MFI.isEntryFunction()), MemoryBound(MFI.isMemoryBound()),
734 WaveLimiter(MFI.needsWaveLimiter()),
735 HasSpilledSGPRs(MFI.hasSpilledSGPRs()),
736 HasSpilledVGPRs(MFI.hasSpilledVGPRs()),
737 HasNoWWMPoolSGPRSpillFallback(MFI.hasNoWWMPoolSGPRSpillFallback()),
738 NumWaveDispatchSGPRs(MFI.getNumWaveDispatchSGPRs()),
739 NumWaveDispatchVGPRs(MFI.getNumWaveDispatchVGPRs()),
740 HighBitsOf32BitAddress(MFI.get32BitAddressHighBits()),
741 Occupancy(MFI.getOccupancy()),
742 ScratchRSrcReg(regToString(MFI.getScratchRSrcReg(), TRI)),
743 FrameOffsetReg(regToString(MFI.getFrameOffsetReg(), TRI)),
744 StackPtrOffsetReg(regToString(MFI.getStackPtrOffsetReg(), TRI)),
745 BytesInStackArgArea(MFI.getBytesInStackArgArea()),
746 ReturnsVoid(MFI.returnsVoid()),
747 ArgInfo(convertArgumentInfo(MFI.getArgInfo(), TRI)),
748 PSInputAddr(MFI.getPSInputAddr()), PSInputEnable(MFI.getPSInputEnable()),
749 MaxMemoryClusterDWords(MFI.getMaxMemoryClusterDWords()),
750 Mode(MFI.getMode()), HasInitWholeWave(MFI.hasInitWholeWave()),
751 IsWholeWaveFunction(MFI.isWholeWaveFunction()),
752 DynamicVGPRBlockSize(MFI.getDynamicVGPRBlockSize()),
753 ScratchReservedForDynamicVGPRs(MFI.getScratchReservedForDynamicVGPRs()),
754 NumKernargPreloadSGPRs(MFI.getNumKernargPreloadedSGPRs()),
755 MinNumAGPRs(MFI.getMinNumAGPRs()) {
756 for (Register Reg : MFI.getSGPRSpillPhysVGPRs())
757 SpillPhysVGPRS.push_back(regToString(Reg, TRI));
758
759 for (Register Reg : MFI.getWWMReservedRegs())
760 WWMReservedRegs.push_back(regToString(Reg, TRI));
761
762 if (MFI.getLongBranchReservedReg())
764 if (MFI.getVGPRForAGPRCopy())
766
767 if (MFI.getSGPRForEXECCopy())
769
770 auto SFI = MFI.getOptionalScavengeFI();
771 if (SFI)
773}
774
778
780 const yaml::SIMachineFunctionInfo &YamlMFI, const MachineFunction &MF,
784 LDSSize = YamlMFI.LDSSize;
785 GDSSize = YamlMFI.GDSSize;
786 DynLDSAlign = YamlMFI.DynLDSAlign;
787 PSInputAddr = YamlMFI.PSInputAddr;
788 PSInputEnable = YamlMFI.PSInputEnable;
789 MaxMemoryClusterDWords = YamlMFI.MaxMemoryClusterDWords;
790 HighBitsOf32BitAddress = YamlMFI.HighBitsOf32BitAddress;
791 Occupancy = YamlMFI.Occupancy;
793 MemoryBound = YamlMFI.MemoryBound;
794 WaveLimiter = YamlMFI.WaveLimiter;
795 HasSpilledSGPRs = YamlMFI.HasSpilledSGPRs;
796 HasSpilledVGPRs = YamlMFI.HasSpilledVGPRs;
797 HasNoWWMPoolSGPRSpillFallback = YamlMFI.HasNoWWMPoolSGPRSpillFallback;
798 NumWaveDispatchSGPRs = YamlMFI.NumWaveDispatchSGPRs;
799 NumWaveDispatchVGPRs = YamlMFI.NumWaveDispatchVGPRs;
800 BytesInStackArgArea = YamlMFI.BytesInStackArgArea;
801 ReturnsVoid = YamlMFI.ReturnsVoid;
802 IsWholeWaveFunction = YamlMFI.IsWholeWaveFunction;
803 MinNumAGPRs = YamlMFI.MinNumAGPRs;
804 // This can also be set by the function attribute, MFI has higher precedence
805 // though.
806 if (YamlMFI.DynamicVGPRBlockSize != std::nullopt)
807 DynamicVGPRBlockSize = *YamlMFI.DynamicVGPRBlockSize;
808
809 UserSGPRInfo.allocKernargPreloadSGPRs(YamlMFI.NumKernargPreloadSGPRs);
810
811 if (YamlMFI.ScavengeFI) {
812 auto FIOrErr = YamlMFI.ScavengeFI->getFI(MF.getFrameInfo());
813 if (!FIOrErr) {
814 // Create a diagnostic for a the frame index.
815 const MemoryBuffer &Buffer =
816 *PFS.SM->getMemoryBuffer(PFS.SM->getMainFileID());
817
818 Error = SMDiagnostic(*PFS.SM, SMLoc(), Buffer.getBufferIdentifier(), 1, 1,
819 SourceMgr::DK_Error, toString(FIOrErr.takeError()),
820 "", {}, {});
821 SourceRange = YamlMFI.ScavengeFI->SourceRange;
822 return true;
823 }
824 ScavengeFI = *FIOrErr;
825 } else {
826 ScavengeFI = std::nullopt;
827 }
828 return false;
829}
830
832 auto [MinNumAGPR, MaxNumAGPR] =
833 AMDGPU::getIntegerPairAttribute(F, "amdgpu-agpr-alloc", {~0u, ~0u},
834 /*OnlyFirstRequired=*/true);
835 return MinNumAGPR != 0u;
836}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Provides AMDGPU specific target descriptions.
Base class for AMDGPU specific classes of TargetSubtarget.
MachineBasicBlock & MBB
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
AMD GCN specific subclass of TargetSubtarget.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
if(PassOpts->AAPipeline)
const GCNTargetMachine & getTM(const GCNSubtarget *STI)
static cl::opt< bool, true > MFMAVGPRFormOpt("amdgpu-mfma-vgpr-form", cl::desc("Whether to force use VGPR for Opc and Dest of MFMA. If " "unspecified, default to compiler heuristics"), cl::location(SIMachineFunctionInfo::MFMAVGPRForm), cl::init(true), cl::Hidden)
static std::optional< yaml::SIArgumentInfo > convertArgumentInfo(const AMDGPUFunctionArgInfo &ArgInfo, const TargetRegisterInfo &TRI)
static yaml::StringValue regToString(Register Reg, const TargetRegisterInfo &TRI)
Interface definition for SIRegisterInfo.
Align DynLDSAlign
Align for dynamic shared memory if any.
AMDGPUMachineFunctionInfo(const Function &F, const AMDGPUSubtarget &ST)
uint32_t LDSSize
Number of bytes in the LDS that are being used.
static ClusterDimsAttr get(const Function &F)
Functions, function parameters, and return types can have attributes to indicate how they should be t...
Definition Attributes.h:105
BitVector & reset()
Reset all bits in the bitvector.
Definition BitVector.h:409
void resize(unsigned N, bool t=false)
Grow or shrink the bitvector.
Definition BitVector.h:355
BitVector & set()
Set all bits in the bitvector.
Definition BitVector.h:366
void setBitsInMask(const uint32_t *Mask, unsigned MaskWords=~0u)
Add '1' bits from Mask to this vector.
Definition BitVector.h:742
void push_back(bool Val)
Definition BitVector.h:505
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
CallingConv::ID getCallingConv() const
getCallingConv()/setCallingConv(CC) - These method get and set the calling convention of this functio...
Definition Function.h:272
const SITargetLowering * getTargetLowering() const override
ArrayRef< MCPhysReg > getRegisters() const
LLVM_ABI void sortUniqueLiveIns()
Sorts and uniques the LiveIns vector.
void addLiveIn(MCRegister PhysReg, LaneBitmask LaneMask=LaneBitmask::getAll())
Adds the specified register as a live in.
The MachineFrameInfo class represents an abstract stack frame until prolog/epilog code is inserted.
LLVM_ABI int CreateStackObject(uint64_t Size, Align Alignment, bool isSpillSlot, const AllocaInst *Alloca=nullptr, uint8_t ID=0)
Create a new statically sized stack object, returning a nonnegative identifier to represent it.
void setStackID(int ObjectIdx, uint8_t ID)
bool hasTailCall() const
Returns true if the function contains a tail call.
LLVM_ABI int CreateSpillStackObject(uint64_t Size, Align Alignment, TargetStackID::Value StackID=TargetStackID::Default)
Create a new statically sized stack object that represents a spill slot, returning a nonnegative iden...
void RemoveStackObject(int ObjectIdx)
Remove or mark dead a statically sized stack object.
int getObjectIndexEnd() const
Return one past the maximum frame object index.
uint8_t getStackID(int ObjectIdx) const
int getObjectIndexBegin() const
Return the minimum frame object index.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineFrameInfo & getFrameInfo()
getFrameInfo - Return the frame info object for the current function.
void replaceFrameInstRegister(MCRegister From, MCRegister To)
Replace all references to register.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
Ty * cloneInfo(const Ty &Old)
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
LLVM_ABI const MCPhysReg * getCalleeSavedRegs() const
Returns list of callee saved registers.
void reserveReg(MCRegister PhysReg, const TargetRegisterInfo *TRI)
reserveReg – Mark a register as reserved so checks like isAllocatable will not suggest using it.
LLVM_ABI void replaceRegWith(Register FromReg, Register ToReg)
replaceRegWith - Replace all instances of FromReg with ToReg in the machine function.
This interface provides simple read-only access to a block of memory, and provides simple methods for...
virtual StringRef getBufferIdentifier() const
Return an identifier for this buffer, typically the filename it was read from.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
This class keeps track of the SPI_SP_INPUT_ADDR config register, which tells the hardware which inter...
bool initializeBaseYamlFields(const yaml::SIMachineFunctionInfo &YamlMFI, const MachineFunction &MF, PerFunctionMIParsingState &PFS, SMDiagnostic &Error, SMRange &SourceRange)
void shiftWwmVGPRsToLowestRange(MachineFunction &MF, SmallVectorImpl< Register > &WWMVGPRs, BitVector &SavedVGPRs)
Register addPrivateSegmentSize(const SIRegisterInfo &TRI)
void allocateWWMSpill(MachineFunction &MF, Register VGPR, uint64_t Size=4, Align Alignment=Align(4))
Register addDispatchPtr(const SIRegisterInfo &TRI)
Register addFlatScratchInit(const SIRegisterInfo &TRI)
ArrayRef< Register > getSGPRSpillPhysVGPRs() const
int getScavengeFI(MachineFrameInfo &MFI, const SIRegisterInfo &TRI)
Register addQueuePtr(const SIRegisterInfo &TRI)
SIMachineFunctionInfo(const SIMachineFunctionInfo &MFI)=default
Register getGITPtrLoReg(const MachineFunction &MF) const
bool allocateVGPRSpillToAGPR(MachineFunction &MF, int FI, bool isAGPRtoVGPR)
Reserve AGPRs or VGPRs to support spilling for FrameIndex FI.
void splitWWMSpillRegisters(MachineFunction &MF, SmallVectorImpl< std::pair< Register, int > > &CalleeSavedRegs, SmallVectorImpl< std::pair< Register, int > > &ScratchRegs) const
bool mayUseAGPRs(const Function &F) const
bool isCalleeSavedReg(const MCPhysReg *CSRegs, MCPhysReg Reg) const
bool allocateSGPRSpillToVGPRLane(MachineFunction &MF, int FI, bool SpillToPhysVGPRLane=false, bool IsPrologEpilog=false)
Register addKernargSegmentPtr(const SIRegisterInfo &TRI)
Register addDispatchID(const SIRegisterInfo &TRI)
bool removeDeadFrameIndices(MachineFrameInfo &MFI, bool ResetSGPRSpillStackIDs)
If ResetSGPRSpillStackIDs is true, reset the stack ID from sgpr-spill to the default stack.
MachineFunctionInfo * clone(BumpPtrAllocator &Allocator, MachineFunction &DestMF, const DenseMap< MachineBasicBlock *, MachineBasicBlock * > &Src2DstMBB) const override
Make a functionally equivalent copy of this MachineFunctionInfo in MF.
bool checkIndexInPrologEpilogSGPRSpills(int FI) const
Register addPrivateSegmentBuffer(const SIRegisterInfo &TRI)
const ReservedRegSet & getWWMReservedRegs() const
std::optional< int > getOptionalScavengeFI() const
Register addImplicitBufferPtr(const SIRegisterInfo &TRI)
void limitOccupancy(const MachineFunction &MF)
SmallVectorImpl< MCRegister > * addPreloadedKernArg(const SIRegisterInfo &TRI, const TargetRegisterClass *RC, unsigned AllocSizeDWord, int KernArgIdx, int PaddingSGPRs)
static bool isChainScratchRegister(Register VGPR)
Instances of this class encapsulate one diagnostic report, allowing printing to a raw_ostream as a ca...
Definition SourceMgr.h:308
Represents a location in source code.
Definition SMLoc.h:22
Represents a range in source code.
Definition SMLoc.h:47
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
typename SuperClass::const_iterator const_iterator
unsigned getMainFileID() const
Definition SourceMgr.h:151
const MemoryBuffer * getMemoryBuffer(unsigned i) const
Definition SourceMgr.h:144
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
bool consumeInteger(unsigned Radix, T &Result)
Parse the current string as an integer of the specified radix.
Definition StringRef.h:519
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
const TargetMachine & getTargetMachine() const
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
A raw_ostream that writes to an std::string.
unsigned getInitialPSInputAddr(const Function &F)
unsigned getDynamicVGPRBlockSize(const Function &F)
SmallVector< unsigned > getMaxNumWorkGroups(const Function &F)
LLVM_READNONE constexpr bool isChainCC(CallingConv::ID CC)
std::pair< unsigned, unsigned > getIntegerPairAttribute(const Function &F, StringRef Name, std::pair< unsigned, unsigned > Default, bool OnlyFirstRequired)
LLVM_READNONE constexpr bool isGraphics(CallingConv::ID CC)
CallingConv Namespace - This namespace contains an enum with a value for the well-known calling conve...
Definition CallingConv.h:21
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ AMDGPU_CS
Used for Mesa/AMDPAL compute shaders.
@ AMDGPU_KERNEL
Used for AMDGPU code object kernels.
@ AMDGPU_Gfx
Used for AMD graphics targets.
@ AMDGPU_HS
Used for Mesa/AMDPAL hull shaders (= tessellation control shaders).
@ AMDGPU_GS
Used for Mesa/AMDPAL geometry shaders.
@ AMDGPU_PS
Used for Mesa/AMDPAL pixel shaders.
@ SPIR_KERNEL
Used for SPIR kernel functions.
initializer< Ty > init(const Ty &Val)
LocationClass< Ty > location(Ty &L)
This is an optimization pass for GlobalISel generic memory operations.
auto find(R &&Range, const T &Val)
Provide wrappers to std::find which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1765
uint16_t MCPhysReg
An unsigned integer type large enough to represent all physical registers, but not necessarily virtua...
Definition MCRegister.h:21
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)
constexpr unsigned DefaultMemoryClusterDWordsLimit
Definition SIInstrInfo.h:42
BumpPtrAllocatorImpl<> BumpPtrAllocator
The standard BumpPtrAllocator which just uses the default template parameters.
Definition Allocator.h:390
LLVM_ABI Printable printReg(Register Reg, const TargetRegisterInfo *TRI=nullptr, unsigned SubIdx=0, const MachineRegisterInfo *MRI=nullptr)
Prints virtual and physical registers with or without a TRI instance.
MCRegisterClass TargetRegisterClass
Definition FastISel.h:58
static const AMDGPUFunctionArgInfo FixedABIFunctionInfo
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
static ArgDescriptor createRegister(Register Reg, unsigned Mask=~0u)
Helper struct shared between Function Specialization and SCCP Solver.
Definition SCCPSolver.h:42
MachineFunctionInfo - This class can be derived from and used by targets to hold private target-speci...
A serializaable representation of a reference to a stack object or fixed stack object.
This class should be specialized by any type that needs to be converted to/from a YAML mapping.
Definition YAMLTraits.h:63
std::optional< SIArgument > PrivateSegmentWaveByteOffset
std::optional< SIArgument > WorkGroupIDY
std::optional< SIArgument > FlatScratchInit
std::optional< SIArgument > DispatchPtr
std::optional< SIArgument > DispatchID
std::optional< SIArgument > WorkItemIDY
std::optional< SIArgument > WorkGroupIDX
std::optional< SIArgument > ImplicitArgPtr
std::optional< SIArgument > QueuePtr
std::optional< SIArgument > WorkGroupInfo
std::optional< SIArgument > LDSKernelId
std::optional< SIArgument > ImplicitBufferPtr
std::optional< SIArgument > WorkItemIDX
std::optional< SIArgument > KernargSegmentPtr
std::optional< SIArgument > WorkItemIDZ
std::optional< SIArgument > PrivateSegmentSize
std::optional< SIArgument > PrivateSegmentBuffer
std::optional< SIArgument > FirstKernArgPreloadReg
std::optional< SIArgument > WorkGroupIDZ
std::optional< unsigned > Mask
static SIArgument createArgument(bool IsReg)
SmallVector< StringValue > WWMReservedRegs
void mappingImpl(yaml::IO &YamlIO) override
std::optional< SIArgumentInfo > ArgInfo
std::optional< unsigned > DynamicVGPRBlockSize
SmallVector< StringValue, 2 > SpillPhysVGPRS
std::optional< FrameIndex > ScavengeFI
A wrapper around std::string which contains a source range that's being set during parsing.