diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index b8ab70bd9e386..b05956b674d18 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -31,6 +31,7 @@ #include "llvm/CodeGen/StackMaps.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Module.h" +#include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/ErrorHandling.h" @@ -3526,6 +3527,27 @@ RISCVInstrInfo::getOutliningCandidateInfo( Candidate.getMF()->getSubtarget().hasStdExtZca() ? 2 : 4; unsigned CallOverhead = 0, FrameOverhead = 0; + // Count the number of CFI instructions in the candidate, if present. + unsigned CFICount = 0; + for (auto &I : Candidate) { + if (I.isCFIInstruction()) + CFICount++; + } + + // Ensure CFI coverage matches: comparing the number of CFIs in the candidate + // with the total number of CFIs in the parent function for each candidate. + // Outlining only a subset of a function’s CFIs would split the unwind state + // across two code regions and lead to incorrect address offsets between the + // outlined body and the remaining code. To preserve correct unwind info, we + // only outline when all CFIs in the function can be outlined together. + for (outliner::Candidate &C : RepeatedSequenceLocs) { + std::vector CFIInstructions = + C.getMF()->getFrameInstructions(); + + if (CFICount > 0 && CFICount != CFIInstructions.size()) + return std::nullopt; + } + MachineOutlinerConstructionID MOCI = MachineOutlinerDefault; if (Candidate.back().isReturn()) { MOCI = MachineOutlinerTailCall; @@ -3541,6 +3563,11 @@ RISCVInstrInfo::getOutliningCandidateInfo( FrameOverhead = InstrSizeCExt; } + // If we have CFI instructions, we can only outline if the outlined section + // can be a tail call. + if (MOCI != MachineOutlinerTailCall && CFICount > 0) + return std::nullopt; + for (auto &C : RepeatedSequenceLocs) C.setCallInfo(MOCI, CallOverhead); @@ -3562,13 +3589,11 @@ RISCVInstrInfo::getOutliningTypeImpl(const MachineModuleInfo &MMI, MBB->getParent()->getSubtarget().getRegisterInfo(); const auto &F = MI.getMF()->getFunction(); - // We can manually strip out CFI instructions later. + // We can only outline CFI instructions if we will tail call the outlined + // function, or fix up the CFI offsets. Currently, CFI instructions are + // outlined only if in a tail call. if (MI.isCFIInstruction()) - // If current function has exception handling code, we can't outline & - // strip these CFI instructions since it may break .eh_frame section - // needed in unwinding. - return F.needsUnwindTableEntry() ? outliner::InstrType::Illegal - : outliner::InstrType::Invisible; + return outliner::InstrType::Legal; if (cannotInsertTailCall(*MBB) && (MI.isReturn() || isMIModifiesReg(MI, TRI, RISCV::X5))) @@ -3595,21 +3620,6 @@ void RISCVInstrInfo::buildOutlinedFrame( MachineBasicBlock &MBB, MachineFunction &MF, const outliner::OutlinedFunction &OF) const { - // Strip out any CFI instructions - bool Changed = true; - while (Changed) { - Changed = false; - auto I = MBB.begin(); - auto E = MBB.end(); - for (; I != E; ++I) { - if (I->isCFIInstruction()) { - I->removeFromParent(); - Changed = true; - break; - } - } - } - if (OF.FrameConstructionID == MachineOutlinerTailCall) return; diff --git a/llvm/test/CodeGen/RISCV/machine-outliner-cfi.mir b/llvm/test/CodeGen/RISCV/machine-outliner-cfi.mir index 2acb1d43e01ea..78d242b5a28b9 100644 --- a/llvm/test/CodeGen/RISCV/machine-outliner-cfi.mir +++ b/llvm/test/CodeGen/RISCV/machine-outliner-cfi.mir @@ -3,27 +3,33 @@ # RUN: llc -mtriple=riscv64 -x mir -run-pass=machine-outliner -simplify-mir -verify-machineinstrs < %s \ # RUN: | FileCheck -check-prefixes=OUTLINED,RV64I-MO %s -# CFIs are invisible (they can be outlined, but won't actually impact the outlining result) if there -# is no need to unwind. CFIs will be stripped when we build outlined functions. +# Combined tests for outlining with CFI instructions on RISC-V: +# 1) All CFIs present in candidate: outline as tail-call and keep CFIs. +# 2) Partial CFIs in function (extra outside candidate): do not outline. +# 3) CFIs present but candidate is not a tail-call: do not outline. --- | - define void @func1(i32 %a, i32 %b) nounwind { ret void } - - define void @func2(i32 %a, i32 %b) nounwind { ret void } - - define void @func3(i32 %a, i32 %b) nounwind { ret void } + define void @funcA(i32 %a, i32 %b) nounwind { ret void } + define void @funcB(i32 %a, i32 %b) nounwind { ret void } + define void @funcC(i32 %a, i32 %b) nounwind { ret void } + define void @funcD(i32 %a, i32 %b) nounwind { ret void } + define void @funcE(i32 %a, i32 %b) nounwind { ret void } + define void @funcF(i32 %a, i32 %b) nounwind { ret void } ... + +# Case 1: All CFIs present; expect outlining and CFIs retained in outlined body. --- -name: func1 +name: funcA tracksRegLiveness: true body: | bb.0: liveins: $x10, $x11 - ; RV32I-MO-LABEL: name: func1 + ; RV32I-MO-LABEL: name: funcA ; RV32I-MO: liveins: $x10, $x11 ; RV32I-MO-NEXT: {{ $}} ; RV32I-MO-NEXT: PseudoTAIL target-flags(riscv-call) @OUTLINED_FUNCTION_0, implicit $x2, implicit-def $x10, implicit-def $x11, implicit-def $x12, implicit $x2, implicit $x10, implicit $x11 - ; RV64I-MO-LABEL: name: func1 + ; + ; RV64I-MO-LABEL: name: funcA ; RV64I-MO: liveins: $x10, $x11 ; RV64I-MO-NEXT: {{ $}} ; RV64I-MO-NEXT: PseudoTAIL target-flags(riscv-call) @OUTLINED_FUNCTION_0, implicit $x2, implicit-def $x10, implicit-def $x11, implicit-def $x12, implicit $x2, implicit $x10, implicit $x11 @@ -39,62 +45,213 @@ body: | PseudoRET ... --- -name: func2 +name: funcB tracksRegLiveness: true body: | bb.0: liveins: $x10, $x11 - ; RV32I-MO-LABEL: name: func2 + ; RV32I-MO-LABEL: name: funcB ; RV32I-MO: liveins: $x10, $x11 ; RV32I-MO-NEXT: {{ $}} ; RV32I-MO-NEXT: PseudoTAIL target-flags(riscv-call) @OUTLINED_FUNCTION_0, implicit $x2, implicit-def $x10, implicit-def $x11, implicit-def $x12, implicit $x2, implicit $x10, implicit $x11 - ; RV64I-MO-LABEL: name: func2 + ; + ; RV64I-MO-LABEL: name: funcB ; RV64I-MO: liveins: $x10, $x11 ; RV64I-MO-NEXT: {{ $}} ; RV64I-MO-NEXT: PseudoTAIL target-flags(riscv-call) @OUTLINED_FUNCTION_0, implicit $x2, implicit-def $x10, implicit-def $x11, implicit-def $x12, implicit $x2, implicit $x10, implicit $x11 $x10 = ORI $x10, 1023 CFI_INSTRUCTION offset $x1, 0 $x11 = ORI $x11, 1023 - CFI_INSTRUCTION offset $x1, -8 - $x12 = ADDI $x10, 17 CFI_INSTRUCTION offset $x1, -4 + $x12 = ADDI $x10, 17 + CFI_INSTRUCTION offset $x1, -8 $x11 = AND $x12, $x11 CFI_INSTRUCTION offset $x1, -12 $x10 = SUB $x10, $x11 PseudoRET ... + +# Case 2: Partial CFIs (extra CFI outside candidate in funcD); expect no outlining. --- -name: func3 +name: funcC tracksRegLiveness: true body: | bb.0: liveins: $x10, $x11 - ; RV32I-MO-LABEL: name: func3 + ; RV32I-MO-LABEL: name: funcC ; RV32I-MO: liveins: $x10, $x11 ; RV32I-MO-NEXT: {{ $}} ; RV32I-MO-NEXT: PseudoTAIL target-flags(riscv-call) @OUTLINED_FUNCTION_0, implicit $x2, implicit-def $x10, implicit-def $x11, implicit-def $x12, implicit $x2, implicit $x10, implicit $x11 - ; RV64I-MO-LABEL: name: func3 + ; + ; RV64I-MO-LABEL: name: funcC ; RV64I-MO: liveins: $x10, $x11 ; RV64I-MO-NEXT: {{ $}} ; RV64I-MO-NEXT: PseudoTAIL target-flags(riscv-call) @OUTLINED_FUNCTION_0, implicit $x2, implicit-def $x10, implicit-def $x11, implicit-def $x12, implicit $x2, implicit $x10, implicit $x11 $x10 = ORI $x10, 1023 - CFI_INSTRUCTION offset $x1, -12 + CFI_INSTRUCTION offset $x1, 0 $x11 = ORI $x11, 1023 + CFI_INSTRUCTION offset $x1, -4 + $x12 = ADDI $x10, 17 CFI_INSTRUCTION offset $x1, -8 + $x11 = AND $x12, $x11 + CFI_INSTRUCTION offset $x1, -12 + $x10 = SUB $x10, $x11 + PseudoRET +... +--- +name: funcD +tracksRegLiveness: true +body: | + bb.0: + liveins: $x10, $x11 + ; RV32I-MO-LABEL: name: funcD + ; RV32I-MO: liveins: $x10, $x11 + ; RV32I-MO-NEXT: {{ $}} + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, -16 + ; RV32I-MO-NEXT: $x10 = ORI $x10, 1023 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, 0 + ; RV32I-MO-NEXT: $x11 = ORI $x11, 1023 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, -4 + ; RV32I-MO-NEXT: $x12 = ADDI $x10, 17 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, -8 + ; RV32I-MO-NEXT: $x11 = AND $x12, $x11 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, -12 + ; RV32I-MO-NEXT: $x10 = SUB $x10, $x11 + ; RV32I-MO-NEXT: PseudoRET + ; + ; RV64I-MO-LABEL: name: funcD + ; RV64I-MO: liveins: $x10, $x11 + ; RV64I-MO-NEXT: {{ $}} + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, -16 + ; RV64I-MO-NEXT: $x10 = ORI $x10, 1023 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, 0 + ; RV64I-MO-NEXT: $x11 = ORI $x11, 1023 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, -4 + ; RV64I-MO-NEXT: $x12 = ADDI $x10, 17 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, -8 + ; RV64I-MO-NEXT: $x11 = AND $x12, $x11 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, -12 + ; RV64I-MO-NEXT: $x10 = SUB $x10, $x11 + ; RV64I-MO-NEXT: PseudoRET + CFI_INSTRUCTION offset $x1, -16 + $x10 = ORI $x10, 1023 + CFI_INSTRUCTION offset $x1, 0 + $x11 = ORI $x11, 1023 + CFI_INSTRUCTION offset $x1, -4 $x12 = ADDI $x10, 17 + CFI_INSTRUCTION offset $x1, -8 + $x11 = AND $x12, $x11 + CFI_INSTRUCTION offset $x1, -12 + $x10 = SUB $x10, $x11 + PseudoRET +... + +# Case 3: CFIs present but candidate is not a tail-call; expect no outlining. +--- +name: funcE +tracksRegLiveness: true +body: | + bb.0: + liveins: $x10, $x11 + ; RV32I-MO-LABEL: name: funcE + ; RV32I-MO: liveins: $x10, $x11 + ; RV32I-MO-NEXT: {{ $}} + ; RV32I-MO-NEXT: $x10 = ORI $x10, 1023 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, 0 + ; RV32I-MO-NEXT: $x11 = ORI $x11, 1023 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, -4 + ; RV32I-MO-NEXT: $x12 = ADDI $x10, 17 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, -8 + ; RV32I-MO-NEXT: $x11 = AND $x12, $x11 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, -12 + ; RV32I-MO-NEXT: $x10 = SUB $x10, $x11 + ; RV32I-MO-NEXT: $x10 = ADDI $x10, 1 + ; RV32I-MO-NEXT: PseudoRET + ; + ; RV64I-MO-LABEL: name: funcE + ; RV64I-MO: liveins: $x10, $x11 + ; RV64I-MO-NEXT: {{ $}} + ; RV64I-MO-NEXT: $x10 = ORI $x10, 1023 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, 0 + ; RV64I-MO-NEXT: $x11 = ORI $x11, 1023 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, -4 + ; RV64I-MO-NEXT: $x12 = ADDI $x10, 17 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, -8 + ; RV64I-MO-NEXT: $x11 = AND $x12, $x11 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, -12 + ; RV64I-MO-NEXT: $x10 = SUB $x10, $x11 + ; RV64I-MO-NEXT: $x10 = ADDI $x10, 1 + ; RV64I-MO-NEXT: PseudoRET + $x10 = ORI $x10, 1023 + CFI_INSTRUCTION offset $x1, 0 + $x11 = ORI $x11, 1023 CFI_INSTRUCTION offset $x1, -4 + $x12 = ADDI $x10, 17 + CFI_INSTRUCTION offset $x1, -8 $x11 = AND $x12, $x11 + CFI_INSTRUCTION offset $x1, -12 + $x10 = SUB $x10, $x11 + $x10 = ADDI $x10, 1 + PseudoRET +... +--- +name: funcF +tracksRegLiveness: true +body: | + bb.0: + liveins: $x10, $x11 + ; RV32I-MO-LABEL: name: funcF + ; RV32I-MO: liveins: $x10, $x11 + ; RV32I-MO-NEXT: {{ $}} + ; RV32I-MO-NEXT: $x10 = ORI $x10, 1023 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, 0 + ; RV32I-MO-NEXT: $x11 = ORI $x11, 1023 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, -4 + ; RV32I-MO-NEXT: $x12 = ADDI $x10, 17 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, -8 + ; RV32I-MO-NEXT: $x11 = AND $x12, $x11 + ; RV32I-MO-NEXT: CFI_INSTRUCTION offset $x1, -12 + ; RV32I-MO-NEXT: $x10 = SUB $x10, $x11 + ; RV32I-MO-NEXT: $x10 = ADDI $x10, 2 + ; RV32I-MO-NEXT: PseudoRET + ; + ; RV64I-MO-LABEL: name: funcF + ; RV64I-MO: liveins: $x10, $x11 + ; RV64I-MO-NEXT: {{ $}} + ; RV64I-MO-NEXT: $x10 = ORI $x10, 1023 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, 0 + ; RV64I-MO-NEXT: $x11 = ORI $x11, 1023 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, -4 + ; RV64I-MO-NEXT: $x12 = ADDI $x10, 17 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, -8 + ; RV64I-MO-NEXT: $x11 = AND $x12, $x11 + ; RV64I-MO-NEXT: CFI_INSTRUCTION offset $x1, -12 + ; RV64I-MO-NEXT: $x10 = SUB $x10, $x11 + ; RV64I-MO-NEXT: $x10 = ADDI $x10, 2 + ; RV64I-MO-NEXT: PseudoRET + $x10 = ORI $x10, 1023 CFI_INSTRUCTION offset $x1, 0 + $x11 = ORI $x11, 1023 + CFI_INSTRUCTION offset $x1, -4 + $x12 = ADDI $x10, 17 + CFI_INSTRUCTION offset $x1, -8 + $x11 = AND $x12, $x11 + CFI_INSTRUCTION offset $x1, -12 $x10 = SUB $x10, $x11 + $x10 = ADDI $x10, 2 PseudoRET - +... # OUTLINED-LABEL: name: OUTLINED_FUNCTION_0 # OUTLINED: liveins: $x11, $x10 # OUTLINED-NEXT: {{ $}} # OUTLINED-NEXT: $x10 = ORI $x10, 1023 +# OUTLINED-NEXT: CFI_INSTRUCTION offset $x1, 0 # OUTLINED-NEXT: $x11 = ORI $x11, 1023 +# OUTLINED-NEXT: CFI_INSTRUCTION offset $x1, -4 # OUTLINED-NEXT: $x12 = ADDI $x10, 17 +# OUTLINED-NEXT: CFI_INSTRUCTION offset $x1, -8 # OUTLINED-NEXT: $x11 = AND $x12, $x11 +# OUTLINED-NEXT: CFI_INSTRUCTION offset $x1, -12 # OUTLINED-NEXT: $x10 = SUB $x10, $x11 # OUTLINED-NEXT: PseudoRET