[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:
Jack Andersen 2021-12-05 14:55:20 -05:00
parent 1b44364714
commit f108c7f59d
26 changed files with 254 additions and 105 deletions

View File

@ -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();
}

View File

@ -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,

View File

@ -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 {

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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();
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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())) {

View File

@ -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

View File

@ -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))

View File

@ -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);

View File

@ -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)

View File

@ -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");
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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
...

View File

@ -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: {{ $}}

View File

@ -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

View File

@ -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
...

View File

@ -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)

View File

@ -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
...

View File

@ -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
...