[WebAssembly] Track frame registers through VReg and local allocation
This change has 2 components:
Target-independent: add a method getDwarfFrameBase to TargetFrameLowering. It
describes how the Dwarf frame base will be encoded. That can be a register (the
default), the CFA (which replaces NVPTX-specific logic in DwarfCompileUnit), or
a DW_OP_WASM_location descriptr.
WebAssembly: Allow WebAssemblyFunctionInfo::getFrameRegister to return the
correct virtual register instead of FP32/SP32 after WebAssemblyReplacePhysRegs
has run. Make WebAssemblyExplicitLocals store the local it allocates for the
frame register. Use this local information to implement getDwarfFrameBase
The result is that the DW_AT_frame_base attribute is correctly encoded for each
subprogram, and each param and local variable has a correct DW_AT_location that
uses DW_OP_fbreg to refer to the frame base.
This is a reland of rG3a05c3969c18 with fixes for the expensive-checks
and Windows builds
Differential Revision: https://reviews.llvm.org/D71681
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 1fbffc1..69a39d5 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -400,15 +400,33 @@
// Only include DW_AT_frame_base in full debug info
if (!includeMinimalInlineScopes()) {
- if (Asm->MF->getTarget().getTargetTriple().isNVPTX()) {
+ const TargetFrameLowering *TFI = Asm->MF->getSubtarget().getFrameLowering();
+ TargetFrameLowering::DwarfFrameBase FrameBase =
+ TFI->getDwarfFrameBase(*Asm->MF);
+ switch (FrameBase.Kind) {
+ case TargetFrameLowering::DwarfFrameBase::Register: {
+ if (Register::isPhysicalRegister(FrameBase.Location.Reg)) {
+ MachineLocation Location(FrameBase.Location.Reg);
+ addAddress(*SPDie, dwarf::DW_AT_frame_base, Location);
+ }
+ break;
+ }
+ case TargetFrameLowering::DwarfFrameBase::CFA: {
DIELoc *Loc = new (DIEValueAllocator) DIELoc;
addUInt(*Loc, dwarf::DW_FORM_data1, dwarf::DW_OP_call_frame_cfa);
addBlock(*SPDie, dwarf::DW_AT_frame_base, Loc);
- } else {
- const TargetRegisterInfo *RI = Asm->MF->getSubtarget().getRegisterInfo();
- MachineLocation Location(RI->getFrameRegister(*Asm->MF));
- if (Register::isPhysicalRegister(Location.getReg()))
- addAddress(*SPDie, dwarf::DW_AT_frame_base, Location);
+ break;
+ }
+ case TargetFrameLowering::DwarfFrameBase::WasmFrameBase: {
+ DIELoc *Loc = new (DIEValueAllocator) DIELoc;
+ DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);
+ DIExpressionCursor Cursor({});
+ DwarfExpr.addWasmLocation(FrameBase.Location.WasmLoc.Kind,
+ FrameBase.Location.WasmLoc.Index);
+ DwarfExpr.addExpression(std::move(Cursor));
+ addBlock(*SPDie, dwarf::DW_AT_frame_base, DwarfExpr.finalize());
+ break;
+ }
}
}
diff --git a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
index bc59be8..9d9bc26 100644
--- a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
+++ b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
@@ -154,3 +154,9 @@
const {
llvm_unreachable("getInitialCFARegister() not implemented!");
}
+
+TargetFrameLowering::DwarfFrameBase
+TargetFrameLowering::getDwarfFrameBase(const MachineFunction &MF) const {
+ const TargetRegisterInfo *RI = MF.getSubtarget().getRegisterInfo();
+ return DwarfFrameBase{DwarfFrameBase::Register, {RI->getFrameRegister(MF)}};
+}
diff --git a/llvm/lib/Target/NVPTX/NVPTXFrameLowering.cpp b/llvm/lib/Target/NVPTX/NVPTXFrameLowering.cpp
index d26912f..2aad490 100644
--- a/llvm/lib/Target/NVPTX/NVPTXFrameLowering.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXFrameLowering.cpp
@@ -83,3 +83,8 @@
// ADJCALLSTACKUP instructions.
return MBB.erase(I);
}
+
+TargetFrameLowering::DwarfFrameBase
+NVPTXFrameLowering::getDwarfFrameBase(const MachineFunction &MF) const {
+ return {DwarfFrameBase::CFA, {0}};
+}
diff --git a/llvm/lib/Target/NVPTX/NVPTXFrameLowering.h b/llvm/lib/Target/NVPTX/NVPTXFrameLowering.h
index 40269f5..2e58eb7 100644
--- a/llvm/lib/Target/NVPTX/NVPTXFrameLowering.h
+++ b/llvm/lib/Target/NVPTX/NVPTXFrameLowering.h
@@ -30,6 +30,7 @@
MachineBasicBlock::iterator
eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator I) const override;
+ DwarfFrameBase getDwarfFrameBase(const MachineFunction &MF) const override;
};
} // End llvm namespace
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
index acbd4c9..1196b5d 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
@@ -72,10 +72,15 @@
/// Return a local id number for the given register, assigning it a new one
/// if it doesn't yet have one.
static unsigned getLocalId(DenseMap<unsigned, unsigned> &Reg2Local,
- unsigned &CurLocal, unsigned Reg) {
+ WebAssemblyFunctionInfo &MFI, unsigned &CurLocal,
+ unsigned Reg) {
auto P = Reg2Local.insert(std::make_pair(Reg, CurLocal));
- if (P.second)
+ if (P.second) {
+ // Mark the local allocated for the frame base vreg.
+ if (MFI.isFrameBaseVirtual() && Reg == MFI.getFrameBaseVreg())
+ MFI.setFrameBaseLocal(CurLocal);
++CurLocal;
+ }
return P.first->second;
}
@@ -244,7 +249,7 @@
// Stackify the input if it isn't stackified yet.
if (!MFI.isVRegStackified(OldReg)) {
- unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
+ unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);
Register NewReg = MRI.createVirtualRegister(RC);
unsigned Opc = getLocalGetOpcode(RC);
BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg)
@@ -255,7 +260,7 @@
// Replace the TEE with a LOCAL_TEE.
unsigned LocalId =
- getLocalId(Reg2Local, CurLocal, MI.getOperand(1).getReg());
+ getLocalId(Reg2Local, MFI, CurLocal, MI.getOperand(1).getReg());
unsigned Opc = getLocalTeeOpcode(RC);
BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc),
MI.getOperand(0).getReg())
@@ -291,7 +296,7 @@
// After the drop instruction, this reg operand will not be used
Drop->getOperand(0).setIsKill();
} else {
- unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
+ unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);
unsigned Opc = getLocalSetOpcode(RC);
WebAssemblyDebugValueManager(&MI).replaceWithLocal(LocalId);
@@ -323,7 +328,7 @@
// immediates.
if (MO.isDef()) {
assert(MI.isInlineAsm());
- unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
+ unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);
// If this register operand is tied to another operand, we can't
// change it to an immediate. Untie it first.
MI.untieRegOperand(MI.getOperandNo(&MO));
@@ -341,7 +346,7 @@
// Our contract with inline asm register operands is to provide local
// indices as immediates.
if (MI.isInlineAsm()) {
- unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
+ unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);
// Untie it first if this reg operand is tied to another operand.
MI.untieRegOperand(MI.getOperandNo(&MO));
MO.ChangeToImmediate(LocalId);
@@ -349,7 +354,7 @@
}
// Insert a local.get.
- unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
+ unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);
const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
Register NewReg = MRI.createVirtualRegister(RC);
unsigned Opc = getLocalGetOpcode(RC);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
index 71eeebf..9da7f53 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
@@ -19,6 +19,7 @@
#include "WebAssemblyFrameLowering.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssembly.h"
#include "WebAssemblyInstrInfo.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
@@ -259,3 +260,20 @@
writeSPToGlobal(SPReg, MF, MBB, InsertPt, DL);
}
+
+TargetFrameLowering::DwarfFrameBase
+WebAssemblyFrameLowering::getDwarfFrameBase(const MachineFunction &MF) const {
+ DwarfFrameBase Loc;
+ Loc.Kind = DwarfFrameBase::WasmFrameBase;
+ const WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
+ if (needsSP(MF)) {
+ unsigned LocalNum = MFI.getFrameBaseLocal();
+ Loc.Location.WasmLoc = {WebAssembly::TI_LOCAL_START, LocalNum};
+ } else {
+ // TODO: This should work on a breakpoint at a function with no frame,
+ // but probably won't work for traversing up the stack.
+ // TODO: This needs a relocation for correct __stack_pointer
+ Loc.Location.WasmLoc = {WebAssembly::TI_GLOBAL_START, 0};
+ }
+ return Loc;
+}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h
index fdc0f56..42fe8e5 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h
@@ -44,6 +44,7 @@
bool hasFP(const MachineFunction &MF) const override;
bool hasReservedCallFrame(const MachineFunction &MF) const override;
+ DwarfFrameBase getDwarfFrameBase(const MachineFunction &MF) const override;
bool needsPrologForEH(const MachineFunction &MF) const;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h
index 16e2f43..f2bbdb8 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h
@@ -55,6 +55,12 @@
// A virtual register holding the base pointer for functions that have
// overaligned values on the user stack.
unsigned BasePtrVreg = -1U;
+ // A virtual register holding the frame base. This is either FP or SP
+ // after it has been replaced by a vreg
+ unsigned FrameBaseVreg = -1U;
+ // The local holding the frame base. This is either FP or SP
+ // after WebAssemblyExplicitLocals
+ unsigned FrameBaseLocal = -1U;
// Function properties.
bool CFGStackified = false;
@@ -90,6 +96,18 @@
assert(BasePtrVreg != -1U && "Base ptr vreg hasn't been set");
return BasePtrVreg;
}
+ void setFrameBaseVreg(unsigned Reg) { FrameBaseVreg = Reg; }
+ unsigned getFrameBaseVreg() const {
+ assert(FrameBaseVreg != -1U && "Frame base vreg hasn't been set");
+ return FrameBaseVreg;
+ }
+ // Return true if the frame base physreg has been replaced by a virtual reg.
+ bool isFrameBaseVirtual() const { return FrameBaseVreg != -1U; }
+ void setFrameBaseLocal(unsigned Local) { FrameBaseLocal = Local; }
+ unsigned getFrameBaseLocal() const {
+ assert(FrameBaseLocal != -1U && "Frame base local hasn't been set");
+ return FrameBaseLocal;
+ }
void setBasePointerVreg(unsigned Reg) { BasePtrVreg = Reg; }
static const unsigned UnusedReg = -1u;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp
index 0bd3079..a2da0ea 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp
@@ -20,6 +20,7 @@
//===----------------------------------------------------------------------===//
#include "WebAssembly.h"
+#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
@@ -82,10 +83,22 @@
SmallVector<LiveInterval *, 4> SplitLIs;
for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) {
unsigned Reg = Register::index2VirtReg(I);
+ auto &TRI = *MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
+
if (MRI.reg_nodbg_empty(Reg))
continue;
LIS.splitSeparateComponents(LIS.getInterval(Reg), SplitLIs);
+ if (Reg == TRI.getFrameRegister(MF) && SplitLIs.size() > 0) {
+ // The live interval for the frame register was split, resulting in a new
+ // VReg. For now we only support debug info output for a single frame base
+ // value for the function, so just use the last one. It will certainly be
+ // wrong for some part of the function, but until we are able to track
+ // values through live-range splitting and stackification, it will have to
+ // do.
+ MF.getInfo<WebAssemblyFunctionInfo>()->setFrameBaseVreg(
+ SplitLIs.back()->reg);
+ }
SplitLIs.clear();
}
@@ -103,5 +116,5 @@
}
}
- return false;
+ return true;
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp
index 789a025..205fb6e 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp
@@ -132,6 +132,10 @@
Register
WebAssemblyRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
+ // If the PReg has been replaced by a VReg, return that.
+ const auto &MFI = MF.getInfo<WebAssemblyFunctionInfo>();
+ if (MFI->isFrameBaseVirtual())
+ return MFI->getFrameBaseVreg();
static const unsigned Regs[2][2] = {
/* !isArch64Bit isArch64Bit */
/* !hasFP */ {WebAssembly::SP32, WebAssembly::SP64},
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp
index 5eafd6c..9f5d6b2 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp
@@ -67,7 +67,7 @@
});
MachineRegisterInfo &MRI = MF.getRegInfo();
- const auto &TRI = *MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
+ auto &TRI = *MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
bool Changed = false;
assert(!mustPreserveAnalysisID(LiveIntervalsID) &&
@@ -88,8 +88,18 @@
for (auto I = MRI.reg_begin(PReg), E = MRI.reg_end(); I != E;) {
MachineOperand &MO = *I++;
if (!MO.isImplicit()) {
- if (VReg == WebAssembly::NoRegister)
+ if (VReg == WebAssembly::NoRegister) {
VReg = MRI.createVirtualRegister(RC);
+ if (PReg == TRI.getFrameRegister(MF)) {
+ auto FI = MF.getInfo<WebAssemblyFunctionInfo>();
+ assert(!FI->isFrameBaseVirtual());
+ FI->setFrameBaseVreg(VReg);
+ LLVM_DEBUG({
+ dbgs() << "replacing preg " << PReg << " with " << VReg << " ("
+ << Register::virtReg2Index(VReg) << ")\n";
+ });
+ }
+ }
MO.setReg(VReg);
if (MO.getParent()->isDebugValue())
MO.setIsDebug();