-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[NFC][X86] Format Floating Point Stackifier Pass #167910
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
boomanaiden154
merged 1 commit into
main
from
users/boomanaiden154/nfcx86-format-floating-point-stackifier-pass
Nov 13, 2025
Merged
[NFC][X86] Format Floating Point Stackifier Pass #167910
boomanaiden154
merged 1 commit into
main
from
users/boomanaiden154/nfcx86-format-floating-point-stackifier-pass
Nov 13, 2025
+497
−499
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Created using spr 1.3.7
Member
|
@llvm/pr-subscribers-backend-x86 Author: Aiden Grossman (boomanaiden154) ChangesIn preparation for porting to the NewPM. Patch is 53.10 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167910.diff 1 Files Affected:
diff --git a/llvm/lib/Target/X86/X86FloatingPoint.cpp b/llvm/lib/Target/X86/X86FloatingPoint.cpp
index 3f06d21a23b88..bc9d354cd8e1a 100644
--- a/llvm/lib/Target/X86/X86FloatingPoint.cpp
+++ b/llvm/lib/Target/X86/X86FloatingPoint.cpp
@@ -51,255 +51,254 @@ using namespace llvm;
#define DEBUG_TYPE "x86-codegen"
STATISTIC(NumFXCH, "Number of fxch instructions inserted");
-STATISTIC(NumFP , "Number of floating point instructions");
+STATISTIC(NumFP, "Number of floating point instructions");
namespace {
- const unsigned ScratchFPReg = 7;
-
- struct FPS : public MachineFunctionPass {
- static char ID;
- FPS() : MachineFunctionPass(ID) {}
-
- void getAnalysisUsage(AnalysisUsage &AU) const override {
- AU.setPreservesCFG();
- AU.addRequired<EdgeBundlesWrapperLegacy>();
- AU.addPreservedID(MachineLoopInfoID);
- AU.addPreservedID(MachineDominatorsID);
- MachineFunctionPass::getAnalysisUsage(AU);
- }
+const unsigned ScratchFPReg = 7;
+
+struct FPS : public MachineFunctionPass {
+ static char ID;
+ FPS() : MachineFunctionPass(ID) {}
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesCFG();
+ AU.addRequired<EdgeBundlesWrapperLegacy>();
+ AU.addPreservedID(MachineLoopInfoID);
+ AU.addPreservedID(MachineDominatorsID);
+ MachineFunctionPass::getAnalysisUsage(AU);
+ }
- bool runOnMachineFunction(MachineFunction &MF) override;
+ bool runOnMachineFunction(MachineFunction &MF) override;
- MachineFunctionProperties getRequiredProperties() const override {
- return MachineFunctionProperties().setNoVRegs();
- }
+ MachineFunctionProperties getRequiredProperties() const override {
+ return MachineFunctionProperties().setNoVRegs();
+ }
- StringRef getPassName() const override { return "X86 FP Stackifier"; }
+ StringRef getPassName() const override { return "X86 FP Stackifier"; }
- private:
- const TargetInstrInfo *TII = nullptr; // Machine instruction info.
+private:
+ const TargetInstrInfo *TII = nullptr; // Machine instruction info.
- // Two CFG edges are related if they leave the same block, or enter the same
- // block. The transitive closure of an edge under this relation is a
- // LiveBundle. It represents a set of CFG edges where the live FP stack
- // registers must be allocated identically in the x87 stack.
- //
- // A LiveBundle is usually all the edges leaving a block, or all the edges
- // entering a block, but it can contain more edges if critical edges are
- // present.
- //
- // The set of live FP registers in a LiveBundle is calculated by bundleCFG,
- // but the exact mapping of FP registers to stack slots is fixed later.
- struct LiveBundle {
- // Bit mask of live FP registers. Bit 0 = FP0, bit 1 = FP1, &c.
- unsigned Mask = 0;
-
- // Number of pre-assigned live registers in FixStack. This is 0 when the
- // stack order has not yet been fixed.
- unsigned FixCount = 0;
-
- // Assigned stack order for live-in registers.
- // FixStack[i] == getStackEntry(i) for all i < FixCount.
- unsigned char FixStack[8];
-
- LiveBundle() = default;
-
- // Have the live registers been assigned a stack order yet?
- bool isFixed() const { return !Mask || FixCount; }
- };
-
- // Numbered LiveBundle structs. LiveBundles[0] is used for all CFG edges
- // with no live FP registers.
- SmallVector<LiveBundle, 8> LiveBundles;
-
- // The edge bundle analysis provides indices into the LiveBundles vector.
- EdgeBundles *Bundles = nullptr;
-
- // Return a bitmask of FP registers in block's live-in list.
- static unsigned calcLiveInMask(MachineBasicBlock *MBB, bool RemoveFPs) {
- unsigned Mask = 0;
- for (MachineBasicBlock::livein_iterator I = MBB->livein_begin();
- I != MBB->livein_end(); ) {
- MCPhysReg Reg = I->PhysReg;
- static_assert(X86::FP6 - X86::FP0 == 6, "sequential regnums");
- if (Reg >= X86::FP0 && Reg <= X86::FP6) {
- Mask |= 1 << (Reg - X86::FP0);
- if (RemoveFPs) {
- I = MBB->removeLiveIn(I);
- continue;
- }
+ // Two CFG edges are related if they leave the same block, or enter the same
+ // block. The transitive closure of an edge under this relation is a
+ // LiveBundle. It represents a set of CFG edges where the live FP stack
+ // registers must be allocated identically in the x87 stack.
+ //
+ // A LiveBundle is usually all the edges leaving a block, or all the edges
+ // entering a block, but it can contain more edges if critical edges are
+ // present.
+ //
+ // The set of live FP registers in a LiveBundle is calculated by bundleCFG,
+ // but the exact mapping of FP registers to stack slots is fixed later.
+ struct LiveBundle {
+ // Bit mask of live FP registers. Bit 0 = FP0, bit 1 = FP1, &c.
+ unsigned Mask = 0;
+
+ // Number of pre-assigned live registers in FixStack. This is 0 when the
+ // stack order has not yet been fixed.
+ unsigned FixCount = 0;
+
+ // Assigned stack order for live-in registers.
+ // FixStack[i] == getStackEntry(i) for all i < FixCount.
+ unsigned char FixStack[8];
+
+ LiveBundle() = default;
+
+ // Have the live registers been assigned a stack order yet?
+ bool isFixed() const { return !Mask || FixCount; }
+ };
+
+ // Numbered LiveBundle structs. LiveBundles[0] is used for all CFG edges
+ // with no live FP registers.
+ SmallVector<LiveBundle, 8> LiveBundles;
+
+ // The edge bundle analysis provides indices into the LiveBundles vector.
+ EdgeBundles *Bundles = nullptr;
+
+ // Return a bitmask of FP registers in block's live-in list.
+ static unsigned calcLiveInMask(MachineBasicBlock *MBB, bool RemoveFPs) {
+ unsigned Mask = 0;
+ for (MachineBasicBlock::livein_iterator I = MBB->livein_begin();
+ I != MBB->livein_end();) {
+ MCPhysReg Reg = I->PhysReg;
+ static_assert(X86::FP6 - X86::FP0 == 6, "sequential regnums");
+ if (Reg >= X86::FP0 && Reg <= X86::FP6) {
+ Mask |= 1 << (Reg - X86::FP0);
+ if (RemoveFPs) {
+ I = MBB->removeLiveIn(I);
+ continue;
}
- ++I;
}
- return Mask;
+ ++I;
}
+ return Mask;
+ }
- // Partition all the CFG edges into LiveBundles.
- void bundleCFGRecomputeKillFlags(MachineFunction &MF);
+ // Partition all the CFG edges into LiveBundles.
+ void bundleCFGRecomputeKillFlags(MachineFunction &MF);
- MachineBasicBlock *MBB = nullptr; // Current basic block
+ MachineBasicBlock *MBB = nullptr; // Current basic block
- // The hardware keeps track of how many FP registers are live, so we have
- // to model that exactly. Usually, each live register corresponds to an
- // FP<n> register, but when dealing with calls, returns, and inline
- // assembly, it is sometimes necessary to have live scratch registers.
- unsigned Stack[8] = {}; // FP<n> Registers in each stack slot...
- unsigned StackTop = 0; // The current top of the FP stack.
+ // The hardware keeps track of how many FP registers are live, so we have
+ // to model that exactly. Usually, each live register corresponds to an
+ // FP<n> register, but when dealing with calls, returns, and inline
+ // assembly, it is sometimes necessary to have live scratch registers.
+ unsigned Stack[8] = {}; // FP<n> Registers in each stack slot...
+ unsigned StackTop = 0; // The current top of the FP stack.
- enum {
- NumFPRegs = 8 // Including scratch pseudo-registers.
- };
+ enum {
+ NumFPRegs = 8 // Including scratch pseudo-registers.
+ };
- // For each live FP<n> register, point to its Stack[] entry.
- // The first entries correspond to FP0-FP6, the rest are scratch registers
- // used when we need slightly different live registers than what the
- // register allocator thinks.
- unsigned RegMap[NumFPRegs] = {};
+ // For each live FP<n> register, point to its Stack[] entry.
+ // The first entries correspond to FP0-FP6, the rest are scratch registers
+ // used when we need slightly different live registers than what the
+ // register allocator thinks.
+ unsigned RegMap[NumFPRegs] = {};
- // Set up our stack model to match the incoming registers to MBB.
- void setupBlockStack();
+ // Set up our stack model to match the incoming registers to MBB.
+ void setupBlockStack();
- // Shuffle live registers to match the expectations of successor blocks.
- void finishBlockStack();
+ // Shuffle live registers to match the expectations of successor blocks.
+ void finishBlockStack();
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- void dumpStack() const {
- dbgs() << "Stack contents:";
- for (unsigned i = 0; i != StackTop; ++i) {
- dbgs() << " FP" << Stack[i];
- assert(RegMap[Stack[i]] == i && "Stack[] doesn't match RegMap[]!");
- }
+ void dumpStack() const {
+ dbgs() << "Stack contents:";
+ for (unsigned i = 0; i != StackTop; ++i) {
+ dbgs() << " FP" << Stack[i];
+ assert(RegMap[Stack[i]] == i && "Stack[] doesn't match RegMap[]!");
}
+ }
#endif
- /// getSlot - Return the stack slot number a particular register number is
- /// in.
- unsigned getSlot(unsigned RegNo) const {
- assert(RegNo < NumFPRegs && "Regno out of range!");
- return RegMap[RegNo];
- }
+ /// getSlot - Return the stack slot number a particular register number is
+ /// in.
+ unsigned getSlot(unsigned RegNo) const {
+ assert(RegNo < NumFPRegs && "Regno out of range!");
+ return RegMap[RegNo];
+ }
- /// isLive - Is RegNo currently live in the stack?
- bool isLive(unsigned RegNo) const {
- unsigned Slot = getSlot(RegNo);
- return Slot < StackTop && Stack[Slot] == RegNo;
- }
+ /// isLive - Is RegNo currently live in the stack?
+ bool isLive(unsigned RegNo) const {
+ unsigned Slot = getSlot(RegNo);
+ return Slot < StackTop && Stack[Slot] == RegNo;
+ }
- /// getStackEntry - Return the X86::FP<n> register in register ST(i).
- unsigned getStackEntry(unsigned STi) const {
- if (STi >= StackTop)
- report_fatal_error("Access past stack top!");
- return Stack[StackTop-1-STi];
- }
+ /// getStackEntry - Return the X86::FP<n> register in register ST(i).
+ unsigned getStackEntry(unsigned STi) const {
+ if (STi >= StackTop)
+ report_fatal_error("Access past stack top!");
+ return Stack[StackTop - 1 - STi];
+ }
- /// getSTReg - Return the X86::ST(i) register which contains the specified
- /// FP<RegNo> register.
- unsigned getSTReg(unsigned RegNo) const {
- return StackTop - 1 - getSlot(RegNo) + X86::ST0;
- }
+ /// getSTReg - Return the X86::ST(i) register which contains the specified
+ /// FP<RegNo> register.
+ unsigned getSTReg(unsigned RegNo) const {
+ return StackTop - 1 - getSlot(RegNo) + X86::ST0;
+ }
- // pushReg - Push the specified FP<n> register onto the stack.
- void pushReg(unsigned Reg) {
- assert(Reg < NumFPRegs && "Register number out of range!");
- if (StackTop >= 8)
- report_fatal_error("Stack overflow!");
- Stack[StackTop] = Reg;
- RegMap[Reg] = StackTop++;
- }
+ // pushReg - Push the specified FP<n> register onto the stack.
+ void pushReg(unsigned Reg) {
+ assert(Reg < NumFPRegs && "Register number out of range!");
+ if (StackTop >= 8)
+ report_fatal_error("Stack overflow!");
+ Stack[StackTop] = Reg;
+ RegMap[Reg] = StackTop++;
+ }
- // popReg - Pop a register from the stack.
- void popReg() {
- if (StackTop == 0)
- report_fatal_error("Cannot pop empty stack!");
- RegMap[Stack[--StackTop]] = ~0; // Update state
- }
+ // popReg - Pop a register from the stack.
+ void popReg() {
+ if (StackTop == 0)
+ report_fatal_error("Cannot pop empty stack!");
+ RegMap[Stack[--StackTop]] = ~0; // Update state
+ }
- bool isAtTop(unsigned RegNo) const { return getSlot(RegNo) == StackTop-1; }
- void moveToTop(unsigned RegNo, MachineBasicBlock::iterator I) {
- DebugLoc dl = I == MBB->end() ? DebugLoc() : I->getDebugLoc();
- if (isAtTop(RegNo)) return;
+ bool isAtTop(unsigned RegNo) const { return getSlot(RegNo) == StackTop - 1; }
+ void moveToTop(unsigned RegNo, MachineBasicBlock::iterator I) {
+ DebugLoc dl = I == MBB->end() ? DebugLoc() : I->getDebugLoc();
+ if (isAtTop(RegNo))
+ return;
- unsigned STReg = getSTReg(RegNo);
- unsigned RegOnTop = getStackEntry(0);
+ unsigned STReg = getSTReg(RegNo);
+ unsigned RegOnTop = getStackEntry(0);
- // Swap the slots the regs are in.
- std::swap(RegMap[RegNo], RegMap[RegOnTop]);
+ // Swap the slots the regs are in.
+ std::swap(RegMap[RegNo], RegMap[RegOnTop]);
- // Swap stack slot contents.
- if (RegMap[RegOnTop] >= StackTop)
- report_fatal_error("Access past stack top!");
- std::swap(Stack[RegMap[RegOnTop]], Stack[StackTop-1]);
+ // Swap stack slot contents.
+ if (RegMap[RegOnTop] >= StackTop)
+ report_fatal_error("Access past stack top!");
+ std::swap(Stack[RegMap[RegOnTop]], Stack[StackTop - 1]);
- // Emit an fxch to update the runtime processors version of the state.
- BuildMI(*MBB, I, dl, TII->get(X86::XCH_F)).addReg(STReg);
- ++NumFXCH;
- }
+ // Emit an fxch to update the runtime processors version of the state.
+ BuildMI(*MBB, I, dl, TII->get(X86::XCH_F)).addReg(STReg);
+ ++NumFXCH;
+ }
- void duplicateToTop(unsigned RegNo, unsigned AsReg,
- MachineBasicBlock::iterator I) {
- DebugLoc dl = I == MBB->end() ? DebugLoc() : I->getDebugLoc();
- unsigned STReg = getSTReg(RegNo);
- pushReg(AsReg); // New register on top of stack
+ void duplicateToTop(unsigned RegNo, unsigned AsReg,
+ MachineBasicBlock::iterator I) {
+ DebugLoc dl = I == MBB->end() ? DebugLoc() : I->getDebugLoc();
+ unsigned STReg = getSTReg(RegNo);
+ pushReg(AsReg); // New register on top of stack
- BuildMI(*MBB, I, dl, TII->get(X86::LD_Frr)).addReg(STReg);
- }
+ BuildMI(*MBB, I, dl, TII->get(X86::LD_Frr)).addReg(STReg);
+ }
- /// popStackAfter - Pop the current value off of the top of the FP stack
- /// after the specified instruction.
- void popStackAfter(MachineBasicBlock::iterator &I);
-
- /// freeStackSlotAfter - Free the specified register from the register
- /// stack, so that it is no longer in a register. If the register is
- /// currently at the top of the stack, we just pop the current instruction,
- /// otherwise we store the current top-of-stack into the specified slot,
- /// then pop the top of stack.
- void freeStackSlotAfter(MachineBasicBlock::iterator &I, unsigned Reg);
-
- /// freeStackSlotBefore - Just the pop, no folding. Return the inserted
- /// instruction.
- MachineBasicBlock::iterator
- freeStackSlotBefore(MachineBasicBlock::iterator I, unsigned FPRegNo);
-
- /// Adjust the live registers to be the set in Mask.
- void adjustLiveRegs(unsigned Mask, MachineBasicBlock::iterator I);
-
- /// Shuffle the top FixCount stack entries such that FP reg FixStack[0] is
- /// st(0), FP reg FixStack[1] is st(1) etc.
- void shuffleStackTop(const unsigned char *FixStack, unsigned FixCount,
- MachineBasicBlock::iterator I);
-
- bool processBasicBlock(MachineFunction &MF, MachineBasicBlock &MBB);
-
- void handleCall(MachineBasicBlock::iterator &I);
- void handleReturn(MachineBasicBlock::iterator &I);
- void handleZeroArgFP(MachineBasicBlock::iterator &I);
- void handleOneArgFP(MachineBasicBlock::iterator &I);
- void handleOneArgFPRW(MachineBasicBlock::iterator &I);
- void handleTwoArgFP(MachineBasicBlock::iterator &I);
- void handleCompareFP(MachineBasicBlock::iterator &I);
- void handleCondMovFP(MachineBasicBlock::iterator &I);
- void handleSpecialFP(MachineBasicBlock::iterator &I);
-
- // Check if a COPY instruction is using FP registers.
- static bool isFPCopy(MachineInstr &MI) {
- Register DstReg = MI.getOperand(0).getReg();
- Register SrcReg = MI.getOperand(1).getReg();
-
- return X86::RFP80RegClass.contains(DstReg) ||
- X86::RFP80RegClass.contains(SrcReg);
- }
+ /// popStackAfter - Pop the current value off of the top of the FP stack
+ /// after the specified instruction.
+ void popStackAfter(MachineBasicBlock::iterator &I);
+
+ /// freeStackSlotAfter - Free the specified register from the register
+ /// stack, so that it is no longer in a register. If the register is
+ /// currently at the top of the stack, we just pop the current instruction,
+ /// otherwise we store the current top-of-stack into the specified slot,
+ /// then pop the top of stack.
+ void freeStackSlotAfter(MachineBasicBlock::iterator &I, unsigned Reg);
+
+ /// freeStackSlotBefore - Just the pop, no folding. Return the inserted
+ /// instruction.
+ MachineBasicBlock::iterator freeStackSlotBefore(MachineBasicBlock::iterator I,
+ unsigned FPRegNo);
+
+ /// Adjust the live registers to be the set in Mask.
+ void adjustLiveRegs(unsigned Mask, MachineBasicBlock::iterator I);
+
+ /// Shuffle the top FixCount stack entries such that FP reg FixStack[0] is
+ /// st(0), FP reg FixStack[1] is st(1) etc.
+ void shuffleStackTop(const unsigned char *FixStack, unsigned FixCount,
+ MachineBasicBlock::iterator I);
+
+ bool processBasicBlock(MachineFunction &MF, MachineBasicBlock &MBB);
+
+ void handleCall(MachineBasicBlock::iterator &I);
+ void handleReturn(MachineBasicBlock::iterator &I);
+ void handleZeroArgFP(MachineBasicBlock::iterator &I);
+ void handleOneArgFP(MachineBasicBlock::iterator &I);
+ void handleOneArgFPRW(MachineBasicBlock::iterator &I);
+ void handleTwoArgFP(MachineBasicBlock::iterator &I);
+ void handleCompareFP(MachineBasicBlock::iterator &I);
+ void handleCondMovFP(MachineBasicBlock::iterator &I);
+ void handleSpecialFP(MachineBasicBlock::iterator &I);
+
+ // Check if a COPY instruction is using FP registers.
+ static bool isFPCopy(MachineInstr &MI) {
+ Register DstReg = MI.getOperand(0).getReg();
+ Register SrcReg = MI.getOperand(1).getReg();
+
+ return X86::RFP80RegClass.contains(DstReg) ||
+ X86::RFP80RegClass.contains(SrcReg);
+ }
- void setKillFlags(MachineBasicBlock &MBB) const;
- };
-}
+ void setKillFlags(MachineBasicBlock &MBB) const;
+};
+} // namespace
char FPS::ID = 0;
-INITIALIZE_PASS_BEGIN(FPS, DEBUG_TYPE, "X86 FP Stackifier",
- false, false)
+INITIALIZE_PASS_BEGIN(FPS, DEBUG_TYPE, "X86 FP Stackifier", false, false)
INITIALIZE_PASS_DEPENDENCY(EdgeBundlesWrapperLegacy)
-INITIALIZE_PASS_END(FPS, DEBUG_TYPE, "X86 FP Stackifier",
- false, false)
+INITIALIZE_PASS_END(FPS, DEBUG_TYPE, "X86 FP Stackifier", false, false)
FunctionPass *llvm::createX86FloatingPointStackifierPass() { return new FPS(); }
@@ -320,7 +319,8 @@ bool FPS::runOnMachineFunction(MachineFunction &MF) {
// function. If it is all integer, there is nothing for us to do!
bool FPIsUsed = false;
- static_assert(X86::FP6 == X86::FP0+6, "Register enums aren't sorted right!");
+ static_assert(X86::FP6 == X86::FP0 + 6,
+ "Register enums aren't sorted right!");
const MachineRegisterInfo &MRI = MF.getRegInfo();
for (unsigned i = 0; i <= 6; ++i)
if (!MRI.reg_nodbg_empty(X86::FP0 + i)) {
@@ -329,7 +329,8 @@ bool FPS::runOnMachineFunction(MachineFunction &MF) {
}
// Early exit.
- if (!FPIsUsed) return false;
+ if (!FPIsUsed)
+ return false;
Bundles = &getAnalysis<EdgeBundlesWrapperLegacy>().getEdgeBundles();
TII = MF.getSubtarget().getInstrInfo();
@@ -341,16 +342,17 @@ bool FPS::runOnMachineFunction(MachineFunction &MF) {
// Process the function in depth first order so that we process at least one
// of the predecessors for every reachable block in the function.
- df_iterator_default_set<MachineBasicBlock*> Processed;
+ df_iterator_default_set<MachineBasicBlock *> Processed;
MachineBasicBlock *Entry = &MF.front();
LiveBundle &Bundle =
- LiveBundles[Bundles->getBundle(Entry->getNumber(), false)];
+ LiveBundles[Bundles->ge...
[truncated]
|
arsenm
approved these changes
Nov 13, 2025
kazutakahirata
approved these changes
Nov 13, 2025
Contributor
kazutakahirata
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks!
boomanaiden154
added a commit
to boomanaiden154/llvm-project
that referenced
this pull request
Nov 13, 2025
In preparation for porting to the NewPM. Pull Request: llvm#167910
llvm-sync bot
pushed a commit
to arm/arm-toolchain
that referenced
this pull request
Nov 13, 2025
In preparation for porting to the NewPM. Reviewers: kazutakahirata, arsenm Reviewed By: kazutakahirata, arsenm Pull Request: llvm/llvm-project#167910
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
In preparation for porting to the NewPM.