Skip to content

Commit e956974

Browse files
committed
[CodeGen][KCFI] Move cfi-type lowering to TargetLowering
KCFI machine function passes transform indirect calls with a cfi-type attribute into architecture-specific type checks bundled together with the calls. Instead of having a separate pass for each architecture, add a generic machine function pass for KCFI and move the architecture-specific code that emits the actual check to TargetLowering. This avoids unnecessary duplication and makes it easier to add KCFI support to other architectures. Reviewed By: nickdesaulniers Differential Revision: https://reviews.llvm.org/D149915
1 parent 640e07c commit e956974

21 files changed

+157
-204
lines changed

llvm/include/llvm/CodeGen/Passes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,9 @@ namespace llvm {
598598
FunctionPass *createSelectOptimizePass();
599599

600600
FunctionPass *createCallBrPass();
601+
602+
/// Lowers KCFI operand bundles for indirect calls.
603+
FunctionPass *createKCFIPass();
601604
} // End llvm namespace
602605

603606
#endif

llvm/include/llvm/CodeGen/TargetLowering.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2080,6 +2080,18 @@ class TargetLoweringBase {
20802080
llvm_unreachable("Masked cmpxchg expansion unimplemented on this target");
20812081
}
20822082

2083+
//===--------------------------------------------------------------------===//
2084+
/// \name KCFI check lowering.
2085+
/// @{
2086+
2087+
virtual MachineInstr *EmitKCFICheck(MachineBasicBlock &MBB,
2088+
MachineBasicBlock::instr_iterator &MBBI,
2089+
const TargetInstrInfo *TII) const {
2090+
llvm_unreachable("KCFI is not supported on this target");
2091+
}
2092+
2093+
/// @}
2094+
20832095
/// Inserts in the IR a target-specific intrinsic specifying a fence.
20842096
/// It is called by AtomicExpandPass before expanding an
20852097
/// AtomicRMW/AtomicCmpXchg/AtomicStore/AtomicLoad

llvm/include/llvm/InitializePasses.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ void initializeInterleavedLoadCombinePass(PassRegistry &);
165165
void initializeIntervalPartitionPass(PassRegistry&);
166166
void initializeJMCInstrumenterPass(PassRegistry&);
167167
void initializeJumpThreadingPass(PassRegistry&);
168+
void initializeKCFIPass(PassRegistry &);
168169
void initializeLCSSAVerificationPassPass(PassRegistry&);
169170
void initializeLCSSAWrapperPassPass(PassRegistry&);
170171
void initializeLazyBlockFrequencyInfoPassPass(PassRegistry&);

llvm/include/llvm/LinkAllPasses.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ namespace {
9393
(void) llvm::createInstSimplifyLegacyPass();
9494
(void) llvm::createInstructionCombiningPass();
9595
(void) llvm::createJMCInstrumenterPass();
96+
(void) llvm::createKCFIPass();
9697
(void) llvm::createLCSSAPass();
9798
(void) llvm::createLICMPass();
9899
(void) llvm::createLoopSinkPass();

llvm/lib/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ add_llvm_component_library(LLVMCodeGen
9393
InterleavedLoadCombinePass.cpp
9494
IntrinsicLowering.cpp
9595
JMCInstrumenter.cpp
96+
KCFI.cpp
9697
LatencyPriorityQueue.cpp
9798
LazyMachineBlockFrequencyInfo.cpp
9899
LexicalScopes.cpp

llvm/lib/Target/AArch64/AArch64KCFI.cpp renamed to llvm/lib/CodeGen/KCFI.cpp

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,78 @@
1-
//===---- AArch64KCFI.cpp - Implements KCFI -------------------------------===//
1+
//===---- KCFI.cpp - Implements KCFI --------------------------------------===//
22
//
33
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44
// See https://llvm.org/LICENSE.txt for license information.
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
88
//
9-
// This file implements KCFI indirect call checking.
9+
// This pass implements KCFI indirect call check lowering.
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "AArch64.h"
14-
#include "AArch64InstrInfo.h"
15-
#include "AArch64Subtarget.h"
16-
#include "AArch64TargetMachine.h"
1713
#include "llvm/ADT/Statistic.h"
1814
#include "llvm/CodeGen/MachineFunctionPass.h"
1915
#include "llvm/CodeGen/MachineInstrBuilder.h"
2016
#include "llvm/CodeGen/MachineInstrBundle.h"
2117
#include "llvm/CodeGen/MachineModuleInfo.h"
18+
#include "llvm/CodeGen/TargetInstrInfo.h"
19+
#include "llvm/CodeGen/TargetLowering.h"
20+
#include "llvm/CodeGen/TargetSubtargetInfo.h"
21+
#include "llvm/InitializePasses.h"
2222

2323
using namespace llvm;
2424

25-
#define DEBUG_TYPE "aarch64-kcfi"
26-
#define AARCH64_KCFI_PASS_NAME "Insert KCFI indirect call checks"
25+
#define DEBUG_TYPE "kcfi"
26+
#define KCFI_PASS_NAME "Insert KCFI indirect call checks"
2727

2828
STATISTIC(NumKCFIChecksAdded, "Number of indirect call checks added");
2929

3030
namespace {
31-
class AArch64KCFI : public MachineFunctionPass {
31+
class KCFI : public MachineFunctionPass {
3232
public:
3333
static char ID;
3434

35-
AArch64KCFI() : MachineFunctionPass(ID) {}
35+
KCFI() : MachineFunctionPass(ID) {}
3636

37-
StringRef getPassName() const override { return AARCH64_KCFI_PASS_NAME; }
37+
StringRef getPassName() const override { return KCFI_PASS_NAME; }
3838
bool runOnMachineFunction(MachineFunction &MF) override;
3939

4040
private:
4141
/// Machine instruction info used throughout the class.
42-
const AArch64InstrInfo *TII = nullptr;
42+
const TargetInstrInfo *TII = nullptr;
43+
44+
/// Target lowering for arch-specific parts.
45+
const TargetLowering *TLI = nullptr;
4346

4447
/// Emits a KCFI check before an indirect call.
4548
/// \returns true if the check was added and false otherwise.
4649
bool emitCheck(MachineBasicBlock &MBB,
4750
MachineBasicBlock::instr_iterator I) const;
4851
};
4952

50-
char AArch64KCFI::ID = 0;
53+
char KCFI::ID = 0;
5154
} // end anonymous namespace
5255

53-
INITIALIZE_PASS(AArch64KCFI, DEBUG_TYPE, AARCH64_KCFI_PASS_NAME, false, false)
56+
INITIALIZE_PASS(KCFI, DEBUG_TYPE, KCFI_PASS_NAME, false, false)
5457

55-
FunctionPass *llvm::createAArch64KCFIPass() { return new AArch64KCFI(); }
58+
FunctionPass *llvm::createKCFIPass() { return new KCFI(); }
5659

57-
bool AArch64KCFI::emitCheck(MachineBasicBlock &MBB,
58-
MachineBasicBlock::instr_iterator MBBI) const {
60+
bool KCFI::emitCheck(MachineBasicBlock &MBB,
61+
MachineBasicBlock::instr_iterator MBBI) const {
5962
assert(TII && "Target instruction info was not initialized");
63+
assert(TLI && "Target lowering was not initialized");
6064

6165
// If the call instruction is bundled, we can only emit a check safely if
6266
// it's the first instruction in the bundle.
6367
if (MBBI->isBundled() && !std::prev(MBBI)->isBundle())
6468
report_fatal_error("Cannot emit a KCFI check for a bundled call");
6569

66-
switch (MBBI->getOpcode()) {
67-
case AArch64::BLR:
68-
case AArch64::BLRNoIP:
69-
case AArch64::TCRETURNri:
70-
case AArch64::TCRETURNriBTI:
71-
break;
72-
default:
73-
llvm_unreachable("Unexpected CFI call opcode");
74-
}
75-
76-
MachineOperand &Target = MBBI->getOperand(0);
77-
assert(Target.isReg() && "Invalid target operand for an indirect call");
78-
Target.setIsRenamable(false);
70+
// Emit a KCFI check for the call instruction at MBBI. The implementation
71+
// must unfold memory operands if applicable.
72+
MachineInstr *Check = TLI->EmitKCFICheck(MBB, MBBI, TII);
7973

80-
MachineInstr *Check =
81-
BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(AArch64::KCFI_CHECK))
82-
.addReg(Target.getReg())
83-
.addImm(MBBI->getCFIType())
84-
.getInstr();
74+
// Clear the original call's CFI type.
75+
assert(MBBI->isCall() && "Unexpected instruction type");
8576
MBBI->setCFIType(*MBB.getParent(), 0);
8677

8778
// If not already bundled, bundle the check and the call to prevent
@@ -93,16 +84,18 @@ bool AArch64KCFI::emitCheck(MachineBasicBlock &MBB,
9384
return true;
9485
}
9586

96-
bool AArch64KCFI::runOnMachineFunction(MachineFunction &MF) {
87+
bool KCFI::runOnMachineFunction(MachineFunction &MF) {
9788
const Module *M = MF.getMMI().getModule();
9889
if (!M->getModuleFlag("kcfi"))
9990
return false;
10091

101-
const auto &SubTarget = MF.getSubtarget<AArch64Subtarget>();
92+
const auto &SubTarget = MF.getSubtarget();
10293
TII = SubTarget.getInstrInfo();
94+
TLI = SubTarget.getTargetLowering();
10395

10496
bool Changed = false;
10597
for (MachineBasicBlock &MBB : MF) {
98+
// Use instr_iterator because we don't want to skip bundles.
10699
for (MachineBasicBlock::instr_iterator MII = MBB.instr_begin(),
107100
MIE = MBB.instr_end();
108101
MII != MIE; ++MII) {

llvm/lib/Target/AArch64/AArch64.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ FunctionPass *createAArch64ExpandPseudoPass();
4242
FunctionPass *createAArch64SLSHardeningPass();
4343
FunctionPass *createAArch64IndirectThunks();
4444
FunctionPass *createAArch64SpeculationHardeningPass();
45-
FunctionPass *createAArch64KCFIPass();
4645
FunctionPass *createAArch64LoadStoreOptimizationPass();
4746
ModulePass *createAArch64LowerHomogeneousPrologEpilogPass();
4847
FunctionPass *createAArch64SIMDInstrOptPass();
@@ -86,7 +85,6 @@ void initializeAArch64DAGToDAGISelPass(PassRegistry &);
8685
void initializeAArch64DeadRegisterDefinitionsPass(PassRegistry&);
8786
void initializeAArch64ExpandPseudoPass(PassRegistry &);
8887
void initializeAArch64GlobalsTaggingPass(PassRegistry &);
89-
void initializeAArch64KCFIPass(PassRegistry &);
9088
void initializeAArch64LoadStoreOptPass(PassRegistry&);
9189
void initializeAArch64LowerHomogeneousPrologEpilogPass(PassRegistry &);
9290
void initializeAArch64MIPeepholeOptPass(PassRegistry &);

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23856,6 +23856,33 @@ bool AArch64TargetLowering::shouldConvertFpToSat(unsigned Op, EVT FPVT,
2385623856
return TargetLowering::shouldConvertFpToSat(Op, FPVT, VT);
2385723857
}
2385823858

23859+
MachineInstr *
23860+
AArch64TargetLowering::EmitKCFICheck(MachineBasicBlock &MBB,
23861+
MachineBasicBlock::instr_iterator &MBBI,
23862+
const TargetInstrInfo *TII) const {
23863+
assert(MBBI->isCall() && MBBI->getCFIType() &&
23864+
"Invalid call instruction for a KCFI check");
23865+
23866+
switch (MBBI->getOpcode()) {
23867+
case AArch64::BLR:
23868+
case AArch64::BLRNoIP:
23869+
case AArch64::TCRETURNri:
23870+
case AArch64::TCRETURNriBTI:
23871+
break;
23872+
default:
23873+
llvm_unreachable("Unexpected CFI call opcode");
23874+
}
23875+
23876+
MachineOperand &Target = MBBI->getOperand(0);
23877+
assert(Target.isReg() && "Invalid target operand for an indirect call");
23878+
Target.setIsRenamable(false);
23879+
23880+
return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(AArch64::KCFI_CHECK))
23881+
.addReg(Target.getReg())
23882+
.addImm(MBBI->getCFIType())
23883+
.getInstr();
23884+
}
23885+
2385923886
bool AArch64TargetLowering::enableAggressiveFMAFusion(EVT VT) const {
2386023887
return Subtarget->hasAggressiveFMA() && VT.isFloatingPoint();
2386123888
}

llvm/lib/Target/AArch64/AArch64ISelLowering.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,10 @@ class AArch64TargetLowering : public TargetLowering {
862862

863863
bool supportKCFIBundles() const override { return true; }
864864

865+
MachineInstr *EmitKCFICheck(MachineBasicBlock &MBB,
866+
MachineBasicBlock::instr_iterator &MBBI,
867+
const TargetInstrInfo *TII) const override;
868+
865869
/// Enable aggressive FMA fusion on targets that want it.
866870
bool enableAggressiveFMAFusion(EVT VT) const override;
867871

llvm/lib/Target/AArch64/AArch64TargetMachine.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,6 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAArch64Target() {
215215
initializeAArch64ConditionOptimizerPass(*PR);
216216
initializeAArch64DeadRegisterDefinitionsPass(*PR);
217217
initializeAArch64ExpandPseudoPass(*PR);
218-
initializeAArch64KCFIPass(*PR);
219218
initializeAArch64LoadStoreOptPass(*PR);
220219
initializeAArch64MIPeepholeOptPass(*PR);
221220
initializeAArch64SIMDInstrOptPass(*PR);
@@ -230,6 +229,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAArch64Target() {
230229
initializeFalkorHWPFFixPass(*PR);
231230
initializeFalkorMarkStridedAccessesLegacyPass(*PR);
232231
initializeLDTLSCleanupPass(*PR);
232+
initializeKCFIPass(*PR);
233233
initializeSMEABIPass(*PR);
234234
initializeSVEIntrinsicOptsPass(*PR);
235235
initializeAArch64SpeculationHardeningPass(*PR);
@@ -772,7 +772,7 @@ void AArch64PassConfig::addPreSched2() {
772772
addPass(createAArch64LoadStoreOptimizationPass());
773773
}
774774
// Emit KCFI checks for indirect calls.
775-
addPass(createAArch64KCFIPass());
775+
addPass(createKCFIPass());
776776

777777
// The AArch64SpeculationHardeningPass destroys dominator tree and natural
778778
// loop info, which is needed for the FalkorHWPFFixPass and also later on.

llvm/lib/Target/AArch64/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ add_llvm_target(AArch64CodeGen
6363
AArch64ISelDAGToDAG.cpp
6464
AArch64ISelLowering.cpp
6565
AArch64InstrInfo.cpp
66-
AArch64KCFI.cpp
6766
AArch64LoadStoreOptimizer.cpp
6867
AArch64LowerHomogeneousPrologEpilog.cpp
6968
AArch64MachineFunctionInfo.cpp

llvm/lib/Target/X86/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ set(sources
6363
X86InstrFoldTables.cpp
6464
X86InstrInfo.cpp
6565
X86EvexToVex.cpp
66-
X86KCFI.cpp
6766
X86LegalizerInfo.cpp
6867
X86LoadValueInjectionLoadHardening.cpp
6968
X86LoadValueInjectionRetHardening.cpp

llvm/lib/Target/X86/X86.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@ FunctionPass *createX86IssueVZeroUpperPass();
5151
/// destinations as part of CET IBT mechanism.
5252
FunctionPass *createX86IndirectBranchTrackingPass();
5353

54-
/// This pass inserts KCFI checks before indirect calls.
55-
FunctionPass *createX86KCFIPass();
56-
5754
/// Return a pass that pads short functions with NOOPs.
5855
/// This will prevent a stall when returning on the Atom.
5956
FunctionPass *createX86PadShortFunctions();
@@ -187,7 +184,6 @@ void initializeX86FastPreTileConfigPass(PassRegistry &);
187184
void initializeX86FastTileConfigPass(PassRegistry &);
188185
void initializeX86FixupSetCCPassPass(PassRegistry &);
189186
void initializeX86FlagsCopyLoweringPassPass(PassRegistry &);
190-
void initializeX86KCFIPass(PassRegistry &);
191187
void initializeX86LoadValueInjectionLoadHardeningPassPass(PassRegistry &);
192188
void initializeX86LoadValueInjectionRetHardeningPassPass(PassRegistry &);
193189
void initializeX86LowerAMXIntrinsicsLegacyPassPass(PassRegistry &);

llvm/lib/Target/X86/X86ISelLowering.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59144,6 +59144,71 @@ bool X86TargetLowering::supportSwiftError() const {
5914459144
return Subtarget.is64Bit();
5914559145
}
5914659146

59147+
MachineInstr *
59148+
X86TargetLowering::EmitKCFICheck(MachineBasicBlock &MBB,
59149+
MachineBasicBlock::instr_iterator &MBBI,
59150+
const TargetInstrInfo *TII) const {
59151+
assert(MBBI->isCall() && MBBI->getCFIType() &&
59152+
"Invalid call instruction for a KCFI check");
59153+
59154+
MachineFunction &MF = *MBB.getParent();
59155+
// If the call target is a memory operand, unfold it and use R11 for the
59156+
// call, so KCFI_CHECK won't have to recompute the address.
59157+
switch (MBBI->getOpcode()) {
59158+
case X86::CALL64m:
59159+
case X86::CALL64m_NT:
59160+
case X86::TAILJMPm64:
59161+
case X86::TAILJMPm64_REX: {
59162+
MachineBasicBlock::instr_iterator OrigCall = MBBI;
59163+
SmallVector<MachineInstr *, 2> NewMIs;
59164+
if (!TII->unfoldMemoryOperand(MF, *OrigCall, X86::R11, /*UnfoldLoad=*/true,
59165+
/*UnfoldStore=*/false, NewMIs))
59166+
report_fatal_error("Failed to unfold memory operand for a KCFI check");
59167+
for (auto *NewMI : NewMIs)
59168+
MBBI = MBB.insert(OrigCall, NewMI);
59169+
assert(MBBI->isCall() &&
59170+
"Unexpected instruction after memory operand unfolding");
59171+
if (OrigCall->shouldUpdateCallSiteInfo())
59172+
MF.moveCallSiteInfo(&*OrigCall, &*MBBI);
59173+
MBBI->setCFIType(MF, OrigCall->getCFIType());
59174+
OrigCall->eraseFromParent();
59175+
break;
59176+
}
59177+
default:
59178+
break;
59179+
}
59180+
59181+
MachineOperand &Target = MBBI->getOperand(0);
59182+
Register TargetReg;
59183+
switch (MBBI->getOpcode()) {
59184+
case X86::CALL64r:
59185+
case X86::CALL64r_NT:
59186+
case X86::TAILJMPr64:
59187+
case X86::TAILJMPr64_REX:
59188+
assert(Target.isReg() && "Unexpected target operand for an indirect call");
59189+
Target.setIsRenamable(false);
59190+
TargetReg = Target.getReg();
59191+
break;
59192+
case X86::CALL64pcrel32:
59193+
case X86::TAILJMPd64:
59194+
assert(Target.isSymbol() && "Unexpected target operand for a direct call");
59195+
// X86TargetLowering::EmitLoweredIndirectThunk always uses r11 for
59196+
// 64-bit indirect thunk calls.
59197+
assert(StringRef(Target.getSymbolName()).endswith("_r11") &&
59198+
"Unexpected register for an indirect thunk call");
59199+
TargetReg = X86::R11;
59200+
break;
59201+
default:
59202+
llvm_unreachable("Unexpected CFI call opcode");
59203+
break;
59204+
}
59205+
59206+
return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(X86::KCFI_CHECK))
59207+
.addReg(TargetReg)
59208+
.addImm(MBBI->getCFIType())
59209+
.getInstr();
59210+
}
59211+
5914759212
/// Returns true if stack probing through a function call is requested.
5914859213
bool X86TargetLowering::hasStackProbeSymbol(const MachineFunction &MF) const {
5914959214
return !getStackProbeSymbolName(MF).empty();

llvm/lib/Target/X86/X86ISelLowering.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,10 @@ namespace llvm {
15211521

15221522
bool supportKCFIBundles() const override { return true; }
15231523

1524+
MachineInstr *EmitKCFICheck(MachineBasicBlock &MBB,
1525+
MachineBasicBlock::instr_iterator &MBBI,
1526+
const TargetInstrInfo *TII) const override;
1527+
15241528
bool hasStackProbeSymbol(const MachineFunction &MF) const override;
15251529
bool hasInlineStackProbe(const MachineFunction &MF) const override;
15261530
StringRef getStackProbeSymbolName(const MachineFunction &MF) const override;

llvm/lib/Target/X86/X86InstrInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6169,7 +6169,7 @@ MachineInstr *X86InstrInfo::foldMemoryOperandImpl(
61696169
return nullptr;
61706170

61716171
// Don't fold loads into indirect calls that need a KCFI check as we'll
6172-
// have to unfold these in X86KCFIPass anyway.
6172+
// have to unfold these in X86TargetLowering::EmitKCFICheck anyway.
61736173
if (MI.isCall() && MI.getCFIType())
61746174
return nullptr;
61756175

0 commit comments

Comments
 (0)