Skip to content
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
52 changes: 31 additions & 21 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -3526,6 +3527,27 @@ RISCVInstrInfo::getOutliningCandidateInfo(
Candidate.getMF()->getSubtarget<RISCVSubtarget>().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<MCCFIInstruction> CFIInstructions =
C.getMF()->getFrameInstructions();

if (CFICount > 0 && CFICount != CFIInstructions.size())
return std::nullopt;
}

MachineOutlinerConstructionID MOCI = MachineOutlinerDefault;
if (Candidate.back().isReturn()) {
MOCI = MachineOutlinerTailCall;
Expand All @@ -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);

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

Expand Down
197 changes: 177 additions & 20 deletions llvm/test/CodeGen/RISCV/machine-outliner-cfi.mir
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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