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