forked from OSchip/llvm-project
[GlobalISel] Allow DBG_VALUE to use undefined vregs before LiveDebugValues.
Expanding on D109750. Since `DBG_VALUE` instructions have final register validity determined in `LDVImpl::handleDebugValue`, there is no apparent reason to immediately prune unused register operands as their defs are erased. Consequently, this renders `MachineInstr::eraseFromParentAndMarkDBGValuesForRemoval` moot; gaining a substantial performance improvement. The only necessary changes involve making relevant passes consider invalid DBG_VALUE vregs uses as valid. Reviewed By: MatzeB Differential Revision: https://reviews.llvm.org/D112852
This commit is contained in:
parent
1b44364714
commit
f108c7f59d
|
@ -1248,7 +1248,7 @@ private:
|
|||
for (auto *DeadMI : DeadInsts) {
|
||||
LLVM_DEBUG(dbgs() << *DeadMI << "Is dead, eagerly deleting\n");
|
||||
WrapperObserver.erasingInstr(*DeadMI);
|
||||
DeadMI->eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
DeadMI->eraseFromParent();
|
||||
}
|
||||
DeadInsts.clear();
|
||||
}
|
||||
|
|
|
@ -695,6 +695,7 @@ struct MachineFunction {
|
|||
bool TracksRegLiveness = false;
|
||||
bool HasWinCFI = false;
|
||||
bool FailsVerification = false;
|
||||
bool TracksDebugUserValues = false;
|
||||
std::vector<VirtualRegisterDefinition> VirtualRegisters;
|
||||
std::vector<MachineFunctionLiveIn> LiveIns;
|
||||
Optional<std::vector<FlowStringValue>> CalleeSavedRegisters;
|
||||
|
@ -724,6 +725,8 @@ template <> struct MappingTraits<MachineFunction> {
|
|||
YamlIO.mapOptional("tracksRegLiveness", MF.TracksRegLiveness, false);
|
||||
YamlIO.mapOptional("hasWinCFI", MF.HasWinCFI, false);
|
||||
YamlIO.mapOptional("failsVerification", MF.FailsVerification, false);
|
||||
YamlIO.mapOptional("tracksDebugUserValues", MF.TracksDebugUserValues,
|
||||
false);
|
||||
YamlIO.mapOptional("registers", MF.VirtualRegisters,
|
||||
std::vector<VirtualRegisterDefinition>());
|
||||
YamlIO.mapOptional("liveins", MF.LiveIns,
|
||||
|
|
|
@ -152,6 +152,12 @@ public:
|
|||
// FailsVerification: Means that the function is not expected to pass machine
|
||||
// verification. This can be set by passes that introduce known problems that
|
||||
// have not been fixed yet.
|
||||
// TracksDebugUserValues: Without this property enabled, debug instructions
|
||||
// such as DBG_VALUE are allowed to reference virtual registers even if those
|
||||
// registers do not have a definition. With the property enabled virtual
|
||||
// registers must only be used if they have a definition. This property
|
||||
// allows earlier passes in the pipeline to skip updates of `DBG_VALUE`
|
||||
// instructions to save compile time.
|
||||
enum class Property : unsigned {
|
||||
IsSSA,
|
||||
NoPHIs,
|
||||
|
@ -163,7 +169,8 @@ public:
|
|||
Selected,
|
||||
TiedOpsRewritten,
|
||||
FailsVerification,
|
||||
LastProperty = FailsVerification,
|
||||
TracksDebugUserValues,
|
||||
LastProperty = TracksDebugUserValues,
|
||||
};
|
||||
|
||||
bool hasProperty(Property P) const {
|
||||
|
|
|
@ -1173,12 +1173,6 @@ public:
|
|||
/// eraseFromBundle() to erase individual bundled instructions.
|
||||
void eraseFromParent();
|
||||
|
||||
/// Unlink 'this' from the containing basic block and delete it.
|
||||
///
|
||||
/// For all definitions mark their uses in DBG_VALUE nodes
|
||||
/// as undefined. Otherwise like eraseFromParent().
|
||||
void eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
|
||||
/// Unlink 'this' form its basic block and delete it.
|
||||
///
|
||||
/// If the instruction is part of a bundle, the other instructions in the
|
||||
|
|
|
@ -142,9 +142,9 @@ bool DeadMachineInstructionElim::eliminateDeadMI(MachineFunction &MF) {
|
|||
if (isDead(&MI)) {
|
||||
LLVM_DEBUG(dbgs() << "DeadMachineInstructionElim: DELETING: " << MI);
|
||||
// It is possible that some DBG_VALUE instructions refer to this
|
||||
// instruction. They get marked as undef and will be deleted
|
||||
// in the live debug variable analysis.
|
||||
MI.eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
// instruction. They will be deleted in the live debug variable
|
||||
// analysis.
|
||||
MI.eraseFromParent();
|
||||
AnyChanges = true;
|
||||
++NumDeletes;
|
||||
continue;
|
||||
|
|
|
@ -135,7 +135,7 @@ bool Combiner::combineMachineInstrs(MachineFunction &MF,
|
|||
// Erase dead insts before even adding to the list.
|
||||
if (isTriviallyDead(CurMI, *MRI)) {
|
||||
LLVM_DEBUG(dbgs() << CurMI << "Is dead; erasing.\n");
|
||||
CurMI.eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
CurMI.eraseFromParent();
|
||||
continue;
|
||||
}
|
||||
WorkList.deferred_insert(&CurMI);
|
||||
|
|
|
@ -1551,8 +1551,8 @@ void CombinerHelper::applyShiftOfShiftedLogic(MachineInstr &MI,
|
|||
Builder.buildInstr(MatchInfo.Logic->getOpcode(), {Dest}, {Shift1, Shift2});
|
||||
|
||||
// These were one use so it's safe to remove them.
|
||||
MatchInfo.Shift2->eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
MatchInfo.Logic->eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
MatchInfo.Shift2->eraseFromParent();
|
||||
MatchInfo.Logic->eraseFromParent();
|
||||
|
||||
MI.eraseFromParent();
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
|
|||
// If so, erase it.
|
||||
if (isTriviallyDead(MI, MRI)) {
|
||||
LLVM_DEBUG(dbgs() << "Is dead; erasing.\n");
|
||||
MI.eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
MI.eraseFromParent();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -255,8 +255,12 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
|
|||
MachineInstr *MI = nullptr;
|
||||
if (!MRI.def_empty(VReg))
|
||||
MI = &*MRI.def_instr_begin(VReg);
|
||||
else if (!MRI.use_empty(VReg))
|
||||
else if (!MRI.use_empty(VReg)) {
|
||||
MI = &*MRI.use_instr_begin(VReg);
|
||||
// Debug value instruction is permitted to use undefined vregs.
|
||||
if (MI->isDebugValue())
|
||||
continue;
|
||||
}
|
||||
if (!MI)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -1184,25 +1184,6 @@ bool llvm::shouldOptForSize(const MachineBasicBlock &MBB,
|
|||
llvm::shouldOptimizeForSize(MBB.getBasicBlock(), PSI, BFI);
|
||||
}
|
||||
|
||||
/// These artifacts generally don't have any debug users because they don't
|
||||
/// directly originate from IR instructions, but instead usually from
|
||||
/// legalization. Avoiding checking for debug users improves compile time.
|
||||
/// Note that truncates or extends aren't included because they have IR
|
||||
/// counterparts which can have debug users after translation.
|
||||
static bool shouldSkipDbgValueFor(MachineInstr &MI) {
|
||||
switch (MI.getOpcode()) {
|
||||
case TargetOpcode::G_UNMERGE_VALUES:
|
||||
case TargetOpcode::G_MERGE_VALUES:
|
||||
case TargetOpcode::G_CONCAT_VECTORS:
|
||||
case TargetOpcode::G_BUILD_VECTOR:
|
||||
case TargetOpcode::G_EXTRACT:
|
||||
case TargetOpcode::G_INSERT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void llvm::saveUsesAndErase(MachineInstr &MI, MachineRegisterInfo &MRI,
|
||||
LostDebugLocObserver *LocObserver,
|
||||
SmallInstListTy &DeadInstChain) {
|
||||
|
@ -1212,10 +1193,7 @@ void llvm::saveUsesAndErase(MachineInstr &MI, MachineRegisterInfo &MRI,
|
|||
}
|
||||
LLVM_DEBUG(dbgs() << MI << "Is dead; erasing.\n");
|
||||
DeadInstChain.remove(&MI);
|
||||
if (shouldSkipDbgValueFor(MI))
|
||||
MI.eraseFromParent();
|
||||
else
|
||||
MI.eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
MI.eraseFromParent();
|
||||
if (LocObserver)
|
||||
LocObserver->checkpoint(false);
|
||||
}
|
||||
|
|
|
@ -822,9 +822,6 @@ bool LDVImpl::handleDebugValue(MachineInstr &MI, SlotIndex Idx) {
|
|||
// register that hasn't been defined yet. If we do not remove those here, then
|
||||
// the re-insertion of the DBG_VALUE instruction after register allocation
|
||||
// will be incorrect.
|
||||
// TODO: If earlier passes are corrected to generate sane debug information
|
||||
// (and if the machine verifier is improved to catch this), then these checks
|
||||
// could be removed or replaced by asserts.
|
||||
bool Discard = false;
|
||||
for (const MachineOperand &Op : MI.debug_operands()) {
|
||||
if (Op.isReg() && Register::isVirtualRegister(Op.getReg())) {
|
||||
|
|
|
@ -56,6 +56,11 @@ private:
|
|||
bool runOnMachineFunction(MachineFunction &) override;
|
||||
void releaseMemory() override;
|
||||
void getAnalysisUsage(AnalysisUsage &) const override;
|
||||
|
||||
MachineFunctionProperties getSetProperties() const override {
|
||||
return MachineFunctionProperties().set(
|
||||
MachineFunctionProperties::Property::TracksDebugUserValues);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
|
|
@ -457,6 +457,9 @@ MIRParserImpl::initializeMachineFunction(const yaml::MachineFunction &YamlMF,
|
|||
if (YamlMF.FailsVerification)
|
||||
MF.getProperties().set(
|
||||
MachineFunctionProperties::Property::FailsVerification);
|
||||
if (YamlMF.TracksDebugUserValues)
|
||||
MF.getProperties().set(
|
||||
MachineFunctionProperties::Property::TracksDebugUserValues);
|
||||
|
||||
PerFunctionMIParsingState PFS(MF, SM, IRSlots, *Target);
|
||||
if (parseRegisterInfo(PFS, YamlMF))
|
||||
|
|
|
@ -219,6 +219,8 @@ void MIRPrinter::print(const MachineFunction &MF) {
|
|||
MachineFunctionProperties::Property::FailedISel);
|
||||
YamlMF.FailsVerification = MF.getProperties().hasProperty(
|
||||
MachineFunctionProperties::Property::FailsVerification);
|
||||
YamlMF.TracksDebugUserValues = MF.getProperties().hasProperty(
|
||||
MachineFunctionProperties::Property::TracksDebugUserValues);
|
||||
|
||||
convert(YamlMF, MF.getRegInfo(), MF.getSubtarget().getRegisterInfo());
|
||||
MachineModuleSlotTracker MST(&MF);
|
||||
|
|
|
@ -485,7 +485,7 @@ static void insertDeleteInstructions(MachineBasicBlock *MBB, MachineInstr &MI,
|
|||
MBB->insert((MachineBasicBlock::iterator)&MI, InstrPtr);
|
||||
|
||||
for (auto *InstrPtr : DelInstrs) {
|
||||
InstrPtr->eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
InstrPtr->eraseFromParent();
|
||||
// Erase all LiveRegs defined by the removed instruction
|
||||
for (auto I = RegUnits.begin(); I != RegUnits.end(); ) {
|
||||
if (I->MI == InstrPtr)
|
||||
|
|
|
@ -89,6 +89,7 @@ static cl::opt<unsigned> AlignAllFunctions(
|
|||
static const char *getPropertyName(MachineFunctionProperties::Property Prop) {
|
||||
using P = MachineFunctionProperties::Property;
|
||||
|
||||
// clang-format off
|
||||
switch(Prop) {
|
||||
case P::FailedISel: return "FailedISel";
|
||||
case P::IsSSA: return "IsSSA";
|
||||
|
@ -100,7 +101,9 @@ static const char *getPropertyName(MachineFunctionProperties::Property Prop) {
|
|||
case P::TracksLiveness: return "TracksLiveness";
|
||||
case P::TiedOpsRewritten: return "TiedOpsRewritten";
|
||||
case P::FailsVerification: return "FailsVerification";
|
||||
case P::TracksDebugUserValues: return "TracksDebugUserValues";
|
||||
}
|
||||
// clang-format on
|
||||
llvm_unreachable("Invalid machine function property");
|
||||
}
|
||||
|
||||
|
|
|
@ -682,26 +682,6 @@ void MachineInstr::eraseFromParent() {
|
|||
getParent()->erase(this);
|
||||
}
|
||||
|
||||
void MachineInstr::eraseFromParentAndMarkDBGValuesForRemoval() {
|
||||
assert(getParent() && "Not embedded in a basic block!");
|
||||
MachineBasicBlock *MBB = getParent();
|
||||
MachineFunction *MF = MBB->getParent();
|
||||
assert(MF && "Not embedded in a function!");
|
||||
|
||||
MachineInstr *MI = (MachineInstr *)this;
|
||||
MachineRegisterInfo &MRI = MF->getRegInfo();
|
||||
|
||||
for (const MachineOperand &MO : MI->operands()) {
|
||||
if (!MO.isReg() || !MO.isDef())
|
||||
continue;
|
||||
Register Reg = MO.getReg();
|
||||
if (!Reg.isVirtual())
|
||||
continue;
|
||||
MRI.markUsesInDebugValueAsUndef(Reg);
|
||||
}
|
||||
MI->eraseFromParent();
|
||||
}
|
||||
|
||||
void MachineInstr::eraseFromBundle() {
|
||||
assert(getParent() && "Not embedded in a basic block!");
|
||||
getParent()->erase_instr(this);
|
||||
|
|
|
@ -101,6 +101,7 @@ namespace {
|
|||
// Avoid querying the MachineFunctionProperties for each operand.
|
||||
bool isFunctionRegBankSelected;
|
||||
bool isFunctionSelected;
|
||||
bool isFunctionTracksDebugUserValues;
|
||||
|
||||
using RegVector = SmallVector<Register, 16>;
|
||||
using RegMaskVector = SmallVector<const uint32_t *, 4>;
|
||||
|
@ -384,6 +385,8 @@ unsigned MachineVerifier::verify(const MachineFunction &MF) {
|
|||
MachineFunctionProperties::Property::RegBankSelected);
|
||||
isFunctionSelected = MF.getProperties().hasProperty(
|
||||
MachineFunctionProperties::Property::Selected);
|
||||
isFunctionTracksDebugUserValues = MF.getProperties().hasProperty(
|
||||
MachineFunctionProperties::Property::TracksDebugUserValues);
|
||||
|
||||
LiveVars = nullptr;
|
||||
LiveInts = nullptr;
|
||||
|
@ -1980,41 +1983,50 @@ MachineVerifier::visitMachineOperand(const MachineOperand *MO, unsigned MONum) {
|
|||
if (MO->isUndef())
|
||||
report("Generic virtual register use cannot be undef", MO, MONum);
|
||||
|
||||
// If we're post-Select, we can't have gvregs anymore.
|
||||
if (isFunctionSelected) {
|
||||
report("Generic virtual register invalid in a Selected function",
|
||||
MO, MONum);
|
||||
return;
|
||||
// Debug value instruction is permitted to use undefined vregs.
|
||||
// This is a performance measure to skip the overhead of immediately
|
||||
// pruning unused debug operands. The final undef substitution occurs
|
||||
// when debug values are allocated in LDVImpl::handleDebugValue, so
|
||||
// these verifications always apply after this pass.
|
||||
if (isFunctionTracksDebugUserValues || !MO->isUse() ||
|
||||
!MI->isDebugValue() || !MRI->def_empty(Reg)) {
|
||||
// If we're post-Select, we can't have gvregs anymore.
|
||||
if (isFunctionSelected) {
|
||||
report("Generic virtual register invalid in a Selected function",
|
||||
MO, MONum);
|
||||
return;
|
||||
}
|
||||
|
||||
// The gvreg must have a type and it must not have a SubIdx.
|
||||
LLT Ty = MRI->getType(Reg);
|
||||
if (!Ty.isValid()) {
|
||||
report("Generic virtual register must have a valid type", MO,
|
||||
MONum);
|
||||
return;
|
||||
}
|
||||
|
||||
const RegisterBank *RegBank = MRI->getRegBankOrNull(Reg);
|
||||
|
||||
// If we're post-RegBankSelect, the gvreg must have a bank.
|
||||
if (!RegBank && isFunctionRegBankSelected) {
|
||||
report("Generic virtual register must have a bank in a "
|
||||
"RegBankSelected function",
|
||||
MO, MONum);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the register fits into its register bank if any.
|
||||
if (RegBank && Ty.isValid() &&
|
||||
RegBank->getSize() < Ty.getSizeInBits()) {
|
||||
report("Register bank is too small for virtual register", MO,
|
||||
MONum);
|
||||
errs() << "Register bank " << RegBank->getName() << " too small("
|
||||
<< RegBank->getSize() << ") to fit " << Ty.getSizeInBits()
|
||||
<< "-bits\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The gvreg must have a type and it must not have a SubIdx.
|
||||
LLT Ty = MRI->getType(Reg);
|
||||
if (!Ty.isValid()) {
|
||||
report("Generic virtual register must have a valid type", MO,
|
||||
MONum);
|
||||
return;
|
||||
}
|
||||
|
||||
const RegisterBank *RegBank = MRI->getRegBankOrNull(Reg);
|
||||
|
||||
// If we're post-RegBankSelect, the gvreg must have a bank.
|
||||
if (!RegBank && isFunctionRegBankSelected) {
|
||||
report("Generic virtual register must have a bank in a "
|
||||
"RegBankSelected function",
|
||||
MO, MONum);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the register fits into its register bank if any.
|
||||
if (RegBank && Ty.isValid() &&
|
||||
RegBank->getSize() < Ty.getSizeInBits()) {
|
||||
report("Register bank is too small for virtual register", MO,
|
||||
MONum);
|
||||
errs() << "Register bank " << RegBank->getName() << " too small("
|
||||
<< RegBank->getSize() << ") to fit " << Ty.getSizeInBits()
|
||||
<< "-bits\n";
|
||||
return;
|
||||
}
|
||||
if (SubIdx) {
|
||||
report("Generic virtual register does not allow subregister index", MO,
|
||||
MONum);
|
||||
|
|
|
@ -1620,7 +1620,7 @@ bool SIFoldOperands::tryFoldRegSequence(MachineInstr &MI) {
|
|||
// Erase the REG_SEQUENCE eagerly, unless we followed a chain of COPY users,
|
||||
// in which case we can erase them all later in runOnMachineFunction.
|
||||
if (MRI->use_nodbg_empty(MI.getOperand(0).getReg()))
|
||||
MI.eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
MI.eraseFromParent();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1821,7 +1821,7 @@ bool SIFoldOperands::runOnMachineFunction(MachineFunction &MF) {
|
|||
while (MRI->use_nodbg_empty(InstToErase->getOperand(0).getReg())) {
|
||||
auto &SrcOp = InstToErase->getOperand(1);
|
||||
auto SrcReg = SrcOp.isReg() ? SrcOp.getReg() : Register();
|
||||
InstToErase->eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
InstToErase->eraseFromParent();
|
||||
InstToErase = nullptr;
|
||||
if (!SrcReg || SrcReg.isPhysical())
|
||||
break;
|
||||
|
@ -1831,7 +1831,7 @@ bool SIFoldOperands::runOnMachineFunction(MachineFunction &MF) {
|
|||
}
|
||||
if (InstToErase && InstToErase->isRegSequence() &&
|
||||
MRI->use_nodbg_empty(InstToErase->getOperand(0).getReg()))
|
||||
InstToErase->eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
InstToErase->eraseFromParent();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -126,9 +126,9 @@ static void CombineCVTAToLocal(MachineInstr &Root) {
|
|||
|
||||
// Check if MRI has only one non dbg use, which is Root
|
||||
if (MRI.hasOneNonDBGUse(Prev.getOperand(0).getReg())) {
|
||||
Prev.eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
Prev.eraseFromParent();
|
||||
}
|
||||
Root.eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
Root.eraseFromParent();
|
||||
}
|
||||
|
||||
bool NVPTXPeephole::runOnMachineFunction(MachineFunction &MF) {
|
||||
|
@ -157,7 +157,7 @@ bool NVPTXPeephole::runOnMachineFunction(MachineFunction &MF) {
|
|||
const auto &MRI = MF.getRegInfo();
|
||||
if (MRI.use_empty(NRI->getFrameRegister(MF))) {
|
||||
if (auto MI = MRI.getUniqueVRegDef(NRI->getFrameRegister(MF))) {
|
||||
MI->eraseFromParentAndMarkDBGValuesForRemoval();
|
||||
MI->eraseFromParent();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ body: |
|
|||
liveins: $w0
|
||||
; CHECK-LABEL: name: test_dbg_value_dead
|
||||
; CHECK-NOT: COPY
|
||||
; CHECK: DBG_VALUE $noreg, $noreg, !7, !DIExpression(), debug-location !9
|
||||
; CHECK: DBG_VALUE %0:gpr, $noreg, !7, !DIExpression(), debug-location !9
|
||||
%0:gpr(s32) = COPY $w0
|
||||
DBG_VALUE %0(s32), $noreg, !7, !DIExpression(), debug-location !9
|
||||
...
|
||||
|
|
|
@ -310,7 +310,7 @@ body: |
|
|||
; CHECK-NEXT: [[AND:%[0-9]+]]:_(s32) = G_AND [[ADD]], [[C]]
|
||||
; CHECK-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
|
||||
; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), [[AND]](s32), [[C1]]
|
||||
; CHECK-NEXT: DBG_VALUE $noreg
|
||||
; CHECK-NEXT: DBG_VALUE {{%[0-9]+}}:_(s16)
|
||||
; CHECK-NEXT: G_BRCOND [[ICMP]](s1), %bb.2
|
||||
; CHECK-NEXT: G_BR %bb.1
|
||||
; CHECK-NEXT: {{ $}}
|
||||
|
|
|
@ -14,7 +14,7 @@ body: |
|
|||
|
||||
# GCN-LABEL: name: fold-imm-readfirstlane-dbgvalue{{$}}
|
||||
# GCN: %1:sreg_32_xm0 = S_MOV_B32 123
|
||||
# GCN: DBG_VALUE $noreg, 0, 0
|
||||
# GCN: DBG_VALUE %0:vgpr_32, 0, 0
|
||||
---
|
||||
name: fold-imm-readfirstlane-dbgvalue
|
||||
tracksRegLiveness: true
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# RUN: llc -verify-machineinstrs -run-pass=livedebugvars -o - %s | FileCheck %s
|
||||
# REQUIRES: aarch64-registered-target
|
||||
|
||||
--- |
|
||||
|
||||
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "aarch64--"
|
||||
define i64 @test() { ret i64 0 }
|
||||
|
||||
...
|
||||
|
||||
---
|
||||
name: test
|
||||
tracksRegLiveness: true
|
||||
registers:
|
||||
- { id: 0, class: _ }
|
||||
- { id: 1, class: gpr64 }
|
||||
body: |
|
||||
bb.0:
|
||||
; CHECK-LABEL: bb.0
|
||||
; CHECK-NEXT: %1:gpr64(s64) = G_CONSTANT i64 0
|
||||
; CHECK-NEXT: RET %1(s64)
|
||||
DBG_VALUE %0, $noreg, $noreg, $noreg, $noreg
|
||||
%1(s64) = G_CONSTANT i64 0
|
||||
RET %1
|
||||
...
|
|
@ -0,0 +1,57 @@
|
|||
; RUN: llc -mtriple=i386-linux-gnu -global-isel -verify-machineinstrs < %s -o - | FileCheck %s --check-prefix=ALL
|
||||
|
||||
; This file is the output of clang -g -O2
|
||||
; int test_dbg_trunc(unsigned long long a) { return a; }
|
||||
;
|
||||
; The intent of this check is to ensure the DBG_VALUE use of G_MERGE_VALUES is undef'd when the legalizer erases it.
|
||||
|
||||
; ModuleID = 'x86-calllowering-dbg-trunc.c'
|
||||
source_filename = "x86-calllowering-dbg-trunc.c"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i386"
|
||||
|
||||
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn
|
||||
define dso_local i32 @test_dbg_trunc(i64 %a) local_unnamed_addr #0 !dbg !9 {
|
||||
; ALL-LABEL: test_dbg_trunc:
|
||||
; ALL: # %bb.0: # %entry
|
||||
; ALL: pushl %ebp
|
||||
; ALL: movl %esp, %ebp
|
||||
; ALL: movl 8(%ebp), %eax
|
||||
; ALL: #DEBUG_VALUE: test_dbg_trunc:a <- undef
|
||||
; ALL: popl %ebp
|
||||
; ALL: retl
|
||||
entry:
|
||||
call void @llvm.dbg.value(metadata i64 %a, metadata !15, metadata !DIExpression()), !dbg !16
|
||||
%conv = trunc i64 %a to i32, !dbg !17
|
||||
ret i32 %conv, !dbg !18
|
||||
}
|
||||
|
||||
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
||||
declare void @llvm.dbg.value(metadata, metadata, metadata) #1
|
||||
|
||||
attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
|
||||
attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
|
||||
|
||||
!llvm.dbg.cu = !{!0}
|
||||
!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
|
||||
!llvm.ident = !{!8}
|
||||
|
||||
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 14.0.0 (https://github.com/llvm/llvm-project ...)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
|
||||
!1 = !DIFile(filename: "x86-calllowering-dbg-trunc.c", directory: "/tmp")
|
||||
!2 = !{i32 1, !"NumRegisterParameters", i32 0}
|
||||
!3 = !{i32 7, !"Dwarf Version", i32 4}
|
||||
!4 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
!5 = !{i32 1, !"wchar_size", i32 4}
|
||||
!6 = !{i32 7, !"uwtable", i32 1}
|
||||
!7 = !{i32 7, !"frame-pointer", i32 2}
|
||||
!8 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project ...)"}
|
||||
!9 = distinct !DISubprogram(name: "test_dbg_trunc", scope: !1, file: !1, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14)
|
||||
!10 = !DISubroutineType(types: !11)
|
||||
!11 = !{!12, !13}
|
||||
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
|
||||
!13 = !DIBasicType(name: "unsigned long long", size: 64, encoding: DW_ATE_unsigned)
|
||||
!14 = !{!15}
|
||||
!15 = !DILocalVariable(name: "a", arg: 1, scope: !9, file: !1, line: 1, type: !13)
|
||||
!16 = !DILocation(line: 0, scope: !9)
|
||||
!17 = !DILocation(line: 1, column: 51, scope: !9)
|
||||
!18 = !DILocation(line: 1, column: 44, scope: !9)
|
|
@ -0,0 +1,38 @@
|
|||
# RUN: not --crash llc -verify-machineinstrs -run-pass none -o /dev/null %s 2>&1 | FileCheck %s
|
||||
# REQUIRES: aarch64-registered-target
|
||||
|
||||
--- |
|
||||
|
||||
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "aarch64--"
|
||||
define void @regbankselected_notrack() { ret void }
|
||||
define void @regbankselected_track() { ret void }
|
||||
|
||||
...
|
||||
|
||||
---
|
||||
name: regbankselected_notrack
|
||||
regBankSelected: true
|
||||
tracksDebugUserValues: false
|
||||
registers:
|
||||
- { id: 0, class: _ }
|
||||
body: |
|
||||
bb.0:
|
||||
; CHECK-NOT: function: regbankselected_notrack
|
||||
DBG_VALUE %0(s64), $noreg, $noreg, $noreg, $noreg
|
||||
...
|
||||
|
||||
---
|
||||
name: regbankselected_track
|
||||
regBankSelected: true
|
||||
tracksDebugUserValues: true
|
||||
registers:
|
||||
- { id: 0, class: _ }
|
||||
body: |
|
||||
bb.0:
|
||||
; CHECK: *** Bad machine code: Generic virtual register must have a bank in a RegBankSelected function ***
|
||||
; CHECK: function: regbankselected_track
|
||||
; CHECK: instruction: DBG_VALUE %0:_
|
||||
; CHECK: operand 0: %0
|
||||
DBG_VALUE %0(s64), $noreg, $noreg, $noreg, $noreg
|
||||
...
|
|
@ -0,0 +1,40 @@
|
|||
# RUN: not --crash llc -verify-machineinstrs -run-pass none -o /dev/null %s 2>&1 | FileCheck %s
|
||||
# REQUIRES: aarch64-registered-target
|
||||
|
||||
--- |
|
||||
|
||||
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "aarch64--"
|
||||
define void @selected_notrack() { ret void }
|
||||
define void @selected_track() { ret void }
|
||||
|
||||
...
|
||||
|
||||
---
|
||||
name: selected_notrack
|
||||
regBankSelected: true
|
||||
selected: true
|
||||
tracksDebugUserValues: false
|
||||
registers:
|
||||
- { id: 0, class: _ }
|
||||
body: |
|
||||
bb.0:
|
||||
; CHECK-NOT: function: selected_notrack
|
||||
DBG_VALUE %0, $noreg, $noreg, $noreg, $noreg
|
||||
...
|
||||
|
||||
---
|
||||
name: selected_track
|
||||
regBankSelected: true
|
||||
selected: true
|
||||
tracksDebugUserValues: true
|
||||
registers:
|
||||
- { id: 0, class: _ }
|
||||
body: |
|
||||
bb.0:
|
||||
; CHECK: *** Bad machine code: Generic virtual register invalid in a Selected function ***
|
||||
; CHECK: function: selected_track
|
||||
; CHECK: instruction: DBG_VALUE %0:_
|
||||
; CHECK: operand 0: %0
|
||||
DBG_VALUE %0, $noreg, $noreg, $noreg, $noreg
|
||||
...
|
Loading…
Reference in New Issue