Skip to content

RegAlloc: Use DiagnosticInfo to report register allocation failures #119492

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
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions llvm/include/llvm/IR/DiagnosticInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ enum DiagnosticKind {
DK_Generic,
DK_GenericWithLoc,
DK_InlineAsm,
DK_RegAllocFailure,
DK_ResourceLimit,
DK_StackSize,
DK_Linker,
Expand Down Expand Up @@ -399,6 +400,32 @@ class DiagnosticInfoGenericWithLoc : public DiagnosticInfoWithLocationBase {
}
};

class DiagnosticInfoRegAllocFailure : public DiagnosticInfoWithLocationBase {
private:
/// Message to be reported.
const Twine &MsgStr;

public:
/// \p MsgStr is the message to be reported to the frontend.
/// This class does not copy \p MsgStr, therefore the reference must be valid
/// for the whole life time of the Diagnostic.
DiagnosticInfoRegAllocFailure(const Twine &MsgStr, const Function &Fn,
const DiagnosticLocation &DL,
DiagnosticSeverity Severity = DS_Error);

DiagnosticInfoRegAllocFailure(const Twine &MsgStr, const Function &Fn,
DiagnosticSeverity Severity = DS_Error);

const Twine &getMsgStr() const { return MsgStr; }

/// \see DiagnosticInfo::print.
void print(DiagnosticPrinter &DP) const override;

static bool classof(const DiagnosticInfo *DI) {
return DI->getKind() == DK_RegAllocFailure;
}
};

/// Diagnostic information for stack size etc. reporting.
/// This is basically a function and a size.
class DiagnosticInfoResourceLimit : public DiagnosticInfoWithLocationBase {
Expand Down
22 changes: 13 additions & 9 deletions llvm/lib/CodeGen/RegAllocBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "llvm/CodeGen/Spiller.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/VirtRegMap.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
Expand Down Expand Up @@ -124,17 +125,20 @@ void RegAllocBase::allocatePhysRegs() {

const TargetRegisterClass *RC = MRI->getRegClass(VirtReg->reg());
ArrayRef<MCPhysReg> AllocOrder = RegClassInfo.getOrder(RC);
if (AllocOrder.empty())
if (AllocOrder.empty()) {
report_fatal_error("no registers from class available to allocate");
else if (MI && MI->isInlineAsm()) {
MI->emitInlineAsmError(
"inline assembly requires more registers than available");
} else if (MI) {
LLVMContext &Context =
MI->getParent()->getParent()->getFunction().getContext();
Context.emitError("ran out of registers during register allocation");
} else {
report_fatal_error("ran out of registers during register allocation");
if (MI && MI->isInlineAsm()) {
MI->emitInlineAsmError(
"inline assembly requires more registers than available");
} else {
const Function &Fn = VRM->getMachineFunction().getFunction();
LLVMContext &Context = Fn.getContext();
DiagnosticInfoRegAllocFailure DI(
"ran out of registers during register allocation", Fn,
MI ? MI->getDebugLoc() : DiagnosticLocation());
Context.diagnose(DI);
}
}

// Keep going after reporting the error.
Expand Down
20 changes: 14 additions & 6 deletions llvm/lib/CodeGen/RegAllocFast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ void RegAllocFastImpl::reloadAtBegin(MachineBasicBlock &MBB) {
getMBBBeginInsertionPoint(MBB, PrologLiveIns);
for (const LiveReg &LR : LiveVirtRegs) {
MCPhysReg PhysReg = LR.PhysReg;
if (PhysReg == 0)
if (PhysReg == 0 || LR.Error)
continue;

MCRegister FirstUnit = *TRI->regunits(PhysReg).begin();
Expand Down Expand Up @@ -963,14 +963,22 @@ void RegAllocFastImpl::allocVirtReg(MachineInstr &MI, LiveReg &LR,
if (!BestReg) {
// Nothing we can do: Report an error and keep going with an invalid
// allocation.
if (MI.isInlineAsm())
if (MI.isInlineAsm()) {
MI.emitInlineAsmError(
"inline assembly requires more registers than available");
else
MI.emitInlineAsmError("ran out of registers during register allocation");
} else {
const Function &Fn = MBB->getParent()->getFunction();
DiagnosticInfoRegAllocFailure DI(
"ran out of registers during register allocation", Fn,
MI.getDebugLoc());
Fn.getContext().diagnose(DI);
}

LR.Error = true;
LR.PhysReg = 0;
if (!AllocationOrder.empty())
LR.PhysReg = AllocationOrder.front();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LR.PhysReg = 0 makes more sense to me (the original code).
Why are you still trying to assign something at this point?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LR.PhysReg is used as a lack of assignment marker in non-error situations. We need a definitive registers assignment. This will get cleaned up more in a pending PR

else
LR.PhysReg = 0;
return;
}

Expand Down Expand Up @@ -1076,7 +1084,7 @@ bool RegAllocFastImpl::defineVirtReg(MachineInstr &MI, unsigned OpNum,
return setPhysReg(MI, MO, *AllocationOrder.begin());
}
} else {
assert(!isRegUsedInInstr(LRI->PhysReg, LookAtPhysRegUses) &&
assert((!isRegUsedInInstr(LRI->PhysReg, LookAtPhysRegUses) || LRI->Error) &&
"TODO: preassign mismatch");
LLVM_DEBUG(dbgs() << "In def of " << printReg(VirtReg, TRI)
<< " use existing assignment to "
Expand Down
18 changes: 18 additions & 0 deletions llvm/lib/IR/DiagnosticInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,24 @@ void DiagnosticInfoInlineAsm::print(DiagnosticPrinter &DP) const {
DP << " at line " << getLocCookie();
}

DiagnosticInfoRegAllocFailure::DiagnosticInfoRegAllocFailure(
const Twine &MsgStr, const Function &Fn, const DiagnosticLocation &DL,
DiagnosticSeverity Severity)
: DiagnosticInfoWithLocationBase(DK_RegAllocFailure, Severity, Fn,
DL.isValid() ? DL : Fn.getSubprogram()),
MsgStr(MsgStr) {}

DiagnosticInfoRegAllocFailure::DiagnosticInfoRegAllocFailure(
const Twine &MsgStr, const Function &Fn, DiagnosticSeverity Severity)
: DiagnosticInfoWithLocationBase(DK_RegAllocFailure, Severity, Fn,
Fn.getSubprogram()),
MsgStr(MsgStr) {}

void DiagnosticInfoRegAllocFailure::print(DiagnosticPrinter &DP) const {
DP << getLocationStr() << ": " << MsgStr << " in function '" << getFunction()
<< '\'';
}

DiagnosticInfoResourceLimit::DiagnosticInfoResourceLimit(
const Function &Fn, const char *ResourceName, uint64_t ResourceSize,
uint64_t ResourceLimit, DiagnosticSeverity Severity, DiagnosticKind Kind)
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/AArch64/arm64-anyregcc-crash.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
;
; Check that misuse of anyregcc results in a compile time error.

; CHECK: error: ran out of registers during register allocation
; CHECK: error: <unknown>:0:0: ran out of registers during register allocation
define i64 @anyreglimit(i64 %v1, i64 %v2, i64 %v3, i64 %v4, i64 %v5, i64 %v6, i64 %v7, i64 %v8,
i64 %v9, i64 %v10, i64 %v11, i64 %v12, i64 %v13, i64 %v14, i64 %v15, i64 %v16,
i64 %v17, i64 %v18, i64 %v19, i64 %v20, i64 %v21, i64 %v22, i64 %v23, i64 %v24,
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/AMDGPU/illegal-eviction-assert.mir
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# check was inconsistent with a later assertion when the eviction was
# performed.

# ERR: error: ran out of registers during register allocation
# ERR: error: <unknown>:0:0: ran out of registers during register allocation

--- |
define void @foo() #0 {
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/AMDGPU/issue48473.mir
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# RUN: FileCheck -check-prefix=ERR %s < %t.err

# ERR: error: register allocation failed: maximum depth for recoloring reached. Use -fexhaustive-register-search to skip cutoffs
# ERR-NEXT: error: ran out of registers during register allocation
# ERR-NEXT: error: <unknown>:0:0: ran out of registers during register allocation

# This testcase used to fail with an "overlapping insert" assertion
# when trying to roll back an unsucessful recoloring of %25. One of
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
; RUN: not --crash llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx908 -vgpr-regalloc=greedy -filetype=null %s 2>&1 | FileCheck %s
; RUN: not --crash llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx908 -vgpr-regalloc=basic -filetype=null %s 2>&1 | FileCheck %s

; TODO: Check regalloc fast when it doesn't assert after failing.

; CHECK: LLVM ERROR: no registers from class available to allocate

declare <32 x i32> @llvm.amdgcn.mfma.i32.32x32x4i8(i32, i32, <32 x i32>, i32 immarg, i32 immarg, i32 immarg)

define <32 x i32> @no_registers_from_class_available_to_allocate(<32 x i32> %arg) #0 {
%ret = call <32 x i32> @llvm.amdgcn.mfma.i32.32x32x4i8(i32 1, i32 2, <32 x i32> %arg, i32 1, i32 2, i32 3)
ret <32 x i32> %ret
}

; FIXME: Special case in fast RA, asserts. Also asserts in greedy
; define void @no_registers_from_class_available_to_allocate_undef_asm() #0 {
; call void asm sideeffect "; use $0", "v"(<32 x i32> poison)
; ret void
; }

attributes #0 = { "amdgpu-waves-per-eu"="10,10" }
70 changes: 70 additions & 0 deletions llvm/test/CodeGen/AMDGPU/ran-out-of-registers-errors.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
; RUN: not llc -mtriple=amdgcn-amd-amdhsa -stress-regalloc=1 -vgpr-regalloc=greedy -filetype=null %s 2>&1 | FileCheck -check-prefixes=CHECK,GREEDY -implicit-check-not=error %s
; RUN: not llc -mtriple=amdgcn-amd-amdhsa -stress-regalloc=1 -vgpr-regalloc=basic -filetype=null %s 2>&1 | FileCheck -implicit-check-not=error -check-prefixes=CHECK,BASIC %s
; RUN: not llc -mtriple=amdgcn-amd-amdhsa -stress-regalloc=1 -vgpr-regalloc=fast -filetype=null %s 2>&1 | FileCheck -implicit-check-not=error -check-prefixes=CHECK,FAST %s
; RUN: opt -passes=debugify -o %t.bc %s
; RUN: not llc -mtriple=amdgcn-amd-amdhsa -stress-regalloc=1 -vgpr-regalloc=greedy -filetype=null %t.bc 2>&1 | FileCheck -implicit-check-not=error -check-prefixes=DBGINFO-CHECK,DBGINFO-GREEDY %s
; RUN: not llc -mtriple=amdgcn-amd-amdhsa -stress-regalloc=1 -vgpr-regalloc=basic -filetype=null %t.bc 2>&1 | FileCheck -implicit-check-not=error -check-prefixes=DBGINFO-CHECK,DBGINFO-BASIC %s

; FIXME: Asserts when using -O2 + -vgpr-regalloc=fast
; RUN: not llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx908 -stress-regalloc=1 -O0 -filetype=null %t.bc 2>&1 | FileCheck -implicit-check-not=error -check-prefixes=DBGINFO-CHECK,DBGINFO-FAST %s

; TODO: Should we fix emitting multiple errors sometimes in basic and fast?


; CHECK: error: <unknown>:0:0: ran out of registers during register allocation in function 'ran_out_of_registers_general'
; BASIC: error: <unknown>:0:0: ran out of registers during register allocation in function 'ran_out_of_registers_general'
; FAST: error: <unknown>:0:0: ran out of registers during register allocation in function 'ran_out_of_registers_general'

; DBGINFO-GREEDY: error: {{.*}}:3:1: ran out of registers during register allocation in function 'ran_out_of_registers_general'

; DBGINFO-BASIC: error: {{.*}}:1:1: ran out of registers during register allocation in function 'ran_out_of_registers_general'
; DBGINFO-BASIC: error: {{.*}}:3:1: ran out of registers during register allocation in function 'ran_out_of_registers_general'

; DBGINFO-FAST: error: {{.*}}:3:1: ran out of registers during register allocation in function 'ran_out_of_registers_general'
; DBGINFO-FAST: error: {{.*}}:1:0: ran out of registers during register allocation in function 'ran_out_of_registers_general'
define i32 @ran_out_of_registers_general(ptr addrspace(1) %ptr) #0 {
%ld0 = load volatile i32, ptr addrspace(1) %ptr
%ld1 = load volatile i32, ptr addrspace(1) %ptr
%add = add i32 %ld0, %ld1
ret i32 %add
}

; CHECK: error: inline assembly requires more registers than available at line 23
; DBGINFO-CHECK: error: inline assembly requires more registers than available at line 23
define void @ran_out_of_registers_asm_def() #0 {
%asm = call { i32, i32 } asm sideeffect "; def $0 $1", "=v,=v"(), !srcloc !0
ret void
}

; CHECK: error: inline assembly requires more registers than available at line 23
; DBGINFO-CHECK: error: inline assembly requires more registers than available at line 23
define void @ran_out_of_registers_asm_use() #0 {
call void asm sideeffect "; def $0 $1", "v,v"(i32 0, i32 1), !srcloc !0
ret void
}

; Test error in anonymous function.

; GREEDY: error: inline assembly requires more registers than available at line 23
; BASIC: error: inline assembly requires more registers than available at line 23

; FAST: error: <unknown>:0:0: ran out of registers during register allocation in function '@0'
; FAST: error: <unknown>:0:0: ran out of registers during register allocation in function '@0'


; DBGINFO-GREEDY: error: inline assembly requires more registers than available at line 23
; DBGINFO-BASIC: error: inline assembly requires more registers than available at line 23

; DBGINFO-FAST: error: {{.*}}:12:1: ran out of registers during register allocation in function '@0'
; DBGINFO-FAST: error: {{.*}}:9:0: ran out of registers during register allocation in function '@0'
define i32 @0(ptr addrspace(1) %ptr) #0 {
%asm = call { i32, i32 } asm sideeffect "; def $0 $1 use $2", "=v,=v,v"(ptr addrspace(1) %ptr), !srcloc !0
%elt0 = extractvalue { i32, i32 } %asm, 0
%elt1 = extractvalue { i32, i32 } %asm, 1
%add = add i32 %elt0, %elt1
ret i32 %add
}

attributes #0 = { "target-cpu"="gfx908" }

!0 = !{i32 23}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
; This happens due to when register allocator is out of registers
; it takes the first avialable register.

; CHECK: error: ran out of registers during register allocation
; CHECK: error: <unknown>:0:0: ran out of registers during register allocation
; CHECK: Bad machine code: Using an undefined physical register
define amdgpu_kernel void @alloc_failure_with_split_vregs(float %v0, float %v1) #0 {
%agpr0 = call float asm sideeffect "; def $0", "=${a0}"()
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/PowerPC/ppc64-anyregcc-crash.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
;
; Check that misuse of anyregcc results in a compile time error.

; CHECK: error: ran out of registers during register allocation
; CHECK: error: <unknown>:0:0: ran out of registers during register allocation
define i64 @anyreglimit(i64 %v1, i64 %v2, i64 %v3, i64 %v4, i64 %v5, i64 %v6, i64 %v7, i64 %v8,
i64 %v9, i64 %v10, i64 %v11, i64 %v12, i64 %v13, i64 %v14, i64 %v15, i64 %v16,
i64 %v17, i64 %v18, i64 %v19, i64 %v20, i64 %v21, i64 %v22, i64 %v23, i64 %v24,
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/X86/anyregcc-crash.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
;
; Check that misuse of anyregcc results in a compile time error.

; CHECK: error: ran out of registers during register allocation
; CHECK: error: <unknown>:0:0: ran out of registers during register allocation
define i64 @anyreglimit(i64 %v1, i64 %v2, i64 %v3, i64 %v4, i64 %v5, i64 %v6,
i64 %v7, i64 %v8, i64 %v9, i64 %v10, i64 %v11, i64 %v12,
i64 %v13, i64 %v14, i64 %v15, i64 %v16) {
Expand Down
Loading