From 33f843993aec87c2ea371d1213c3145c7e61f63f Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Mon, 9 Dec 2024 17:02:05 -0600 Subject: [PATCH] RegAlloc: Use DiagnosticInfo to report register allocation failures Improve the non-fatal cases to use DiagnosticInfo, which will now provide a location. The allocators attempt to report different errors if it happens to see inline assembly is involved (this detection is quite unreliable) using srcloc instead of dbgloc. For now, leave this behavior unchanged. I think reporting the full location and context function would be more useful. --- llvm/include/llvm/IR/DiagnosticInfo.h | 27 +++++++ llvm/lib/CodeGen/RegAllocBase.cpp | 22 +++--- llvm/lib/CodeGen/RegAllocFast.cpp | 20 ++++-- llvm/lib/IR/DiagnosticInfo.cpp | 18 +++++ .../CodeGen/AArch64/arm64-anyregcc-crash.ll | 2 +- .../AMDGPU/illegal-eviction-assert.mir | 2 +- llvm/test/CodeGen/AMDGPU/issue48473.mir | 2 +- ...ut-of-registers-error-all-regs-reserved.ll | 21 ++++++ .../AMDGPU/ran-out-of-registers-errors.ll | 70 +++++++++++++++++++ .../remaining-virtual-register-operands.ll | 2 +- .../CodeGen/PowerPC/ppc64-anyregcc-crash.ll | 2 +- llvm/test/CodeGen/X86/anyregcc-crash.ll | 2 +- 12 files changed, 169 insertions(+), 21 deletions(-) create mode 100644 llvm/test/CodeGen/AMDGPU/ran-out-of-registers-error-all-regs-reserved.ll create mode 100644 llvm/test/CodeGen/AMDGPU/ran-out-of-registers-errors.ll diff --git a/llvm/include/llvm/IR/DiagnosticInfo.h b/llvm/include/llvm/IR/DiagnosticInfo.h index 4c34c39683e56..0d582cc17967e 100644 --- a/llvm/include/llvm/IR/DiagnosticInfo.h +++ b/llvm/include/llvm/IR/DiagnosticInfo.h @@ -61,6 +61,7 @@ enum DiagnosticKind { DK_Generic, DK_GenericWithLoc, DK_InlineAsm, + DK_RegAllocFailure, DK_ResourceLimit, DK_StackSize, DK_Linker, @@ -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 { diff --git a/llvm/lib/CodeGen/RegAllocBase.cpp b/llvm/lib/CodeGen/RegAllocBase.cpp index e9fcff5c8ccbd..6300f6b8bf6d1 100644 --- a/llvm/lib/CodeGen/RegAllocBase.cpp +++ b/llvm/lib/CodeGen/RegAllocBase.cpp @@ -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" @@ -124,17 +125,20 @@ void RegAllocBase::allocatePhysRegs() { const TargetRegisterClass *RC = MRI->getRegClass(VirtReg->reg()); ArrayRef 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. diff --git a/llvm/lib/CodeGen/RegAllocFast.cpp b/llvm/lib/CodeGen/RegAllocFast.cpp index cd1e6263d7a43..65a27d564d1e3 100644 --- a/llvm/lib/CodeGen/RegAllocFast.cpp +++ b/llvm/lib/CodeGen/RegAllocFast.cpp @@ -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(); @@ -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(); + else + LR.PhysReg = 0; return; } @@ -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 " diff --git a/llvm/lib/IR/DiagnosticInfo.cpp b/llvm/lib/IR/DiagnosticInfo.cpp index eb91f49a524ac..0e526ada4b405 100644 --- a/llvm/lib/IR/DiagnosticInfo.cpp +++ b/llvm/lib/IR/DiagnosticInfo.cpp @@ -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) diff --git a/llvm/test/CodeGen/AArch64/arm64-anyregcc-crash.ll b/llvm/test/CodeGen/AArch64/arm64-anyregcc-crash.ll index b4fb9b613233d..ad8b027f8b760 100644 --- a/llvm/test/CodeGen/AArch64/arm64-anyregcc-crash.ll +++ b/llvm/test/CodeGen/AArch64/arm64-anyregcc-crash.ll @@ -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: :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, diff --git a/llvm/test/CodeGen/AMDGPU/illegal-eviction-assert.mir b/llvm/test/CodeGen/AMDGPU/illegal-eviction-assert.mir index 40089ed82b5db..99c27fa0bc95c 100644 --- a/llvm/test/CodeGen/AMDGPU/illegal-eviction-assert.mir +++ b/llvm/test/CodeGen/AMDGPU/illegal-eviction-assert.mir @@ -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: :0:0: ran out of registers during register allocation --- | define void @foo() #0 { diff --git a/llvm/test/CodeGen/AMDGPU/issue48473.mir b/llvm/test/CodeGen/AMDGPU/issue48473.mir index 5c202d9928ab7..e272bd3480383 100644 --- a/llvm/test/CodeGen/AMDGPU/issue48473.mir +++ b/llvm/test/CodeGen/AMDGPU/issue48473.mir @@ -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: :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 diff --git a/llvm/test/CodeGen/AMDGPU/ran-out-of-registers-error-all-regs-reserved.ll b/llvm/test/CodeGen/AMDGPU/ran-out-of-registers-error-all-regs-reserved.ll new file mode 100644 index 0000000000000..3e3e201040015 --- /dev/null +++ b/llvm/test/CodeGen/AMDGPU/ran-out-of-registers-error-all-regs-reserved.ll @@ -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" } diff --git a/llvm/test/CodeGen/AMDGPU/ran-out-of-registers-errors.ll b/llvm/test/CodeGen/AMDGPU/ran-out-of-registers-errors.ll new file mode 100644 index 0000000000000..3d150fe90d5ce --- /dev/null +++ b/llvm/test/CodeGen/AMDGPU/ran-out-of-registers-errors.ll @@ -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: :0:0: ran out of registers during register allocation in function 'ran_out_of_registers_general' +; BASIC: error: :0:0: ran out of registers during register allocation in function 'ran_out_of_registers_general' +; FAST: error: :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: :0:0: ran out of registers during register allocation in function '@0' +; FAST: error: :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} diff --git a/llvm/test/CodeGen/AMDGPU/remaining-virtual-register-operands.ll b/llvm/test/CodeGen/AMDGPU/remaining-virtual-register-operands.ll index 04e995b6f343e..8e3054cceb85b 100644 --- a/llvm/test/CodeGen/AMDGPU/remaining-virtual-register-operands.ll +++ b/llvm/test/CodeGen/AMDGPU/remaining-virtual-register-operands.ll @@ -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: :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}"() diff --git a/llvm/test/CodeGen/PowerPC/ppc64-anyregcc-crash.ll b/llvm/test/CodeGen/PowerPC/ppc64-anyregcc-crash.ll index a7d372c5e61a2..e4d96678b2877 100644 --- a/llvm/test/CodeGen/PowerPC/ppc64-anyregcc-crash.ll +++ b/llvm/test/CodeGen/PowerPC/ppc64-anyregcc-crash.ll @@ -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: :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, diff --git a/llvm/test/CodeGen/X86/anyregcc-crash.ll b/llvm/test/CodeGen/X86/anyregcc-crash.ll index d5d6ebe751c0d..3b65babea23eb 100644 --- a/llvm/test/CodeGen/X86/anyregcc-crash.ll +++ b/llvm/test/CodeGen/X86/anyregcc-crash.ll @@ -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: :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) {