PassBuilder &PB) {
// If the back-end supports KCFI operand bundle lowering, skip KCFIPass.
if (TargetTriple.getArch() == llvm::Triple::x86_64 ||
- TargetTriple.isAArch64(64))
+ TargetTriple.isAArch64(64) || TargetTriple.isRISCV())
return;
// Ensure we lower KCFI operand bundles with -O0.
#include "RISCVMachineFunctionInfo.h"
#include "RISCVTargetMachine.h"
#include "TargetInfo/RISCVTargetInfo.h"
+#include "llvm/ADT/APInt.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/CodeGen/AsmPrinter.h"
typedef std::tuple<unsigned, uint32_t> HwasanMemaccessTuple;
std::map<HwasanMemaccessTuple, MCSymbol *> HwasanMemaccessSymbols;
void LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI);
+ void LowerKCFI_CHECK(const MachineInstr &MI);
void EmitHwasanMemaccessSymbols(Module &M);
// Wrapper needed for tblgenned pseudo lowering.
case RISCV::HWASAN_CHECK_MEMACCESS_SHORTGRANULES:
LowerHWASAN_CHECK_MEMACCESS(*MI);
return;
+ case RISCV::KCFI_CHECK:
+ LowerKCFI_CHECK(*MI);
+ return;
case RISCV::PseudoRVVInitUndefM1:
case RISCV::PseudoRVVInitUndefM2:
case RISCV::PseudoRVVInitUndefM4:
EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::PseudoCALL).addExpr(Expr));
}
+void RISCVAsmPrinter::LowerKCFI_CHECK(const MachineInstr &MI) {
+ Register AddrReg = MI.getOperand(0).getReg();
+ assert(std::next(MI.getIterator())->isCall() &&
+ "KCFI_CHECK not followed by a call instruction");
+ assert(std::next(MI.getIterator())->getOperand(0).getReg() == AddrReg &&
+ "KCFI_CHECK call target doesn't match call operand");
+
+ // Temporary registers for comparing the hashes. If a register is used
+ // for the call target, or reserved by the user, we can clobber another
+ // temporary register as the check is immediately followed by the
+ // call. The check defaults to X6/X7, but can fall back to X28-X31 if
+ // needed.
+ unsigned ScratchRegs[] = {RISCV::X6, RISCV::X7};
+ unsigned NextReg = RISCV::X28;
+ auto isRegAvailable = [&](unsigned Reg) {
+ return Reg != AddrReg && !STI->isRegisterReservedByUser(Reg);
+ };
+ for (auto &Reg : ScratchRegs) {
+ if (isRegAvailable(Reg))
+ continue;
+ while (!isRegAvailable(NextReg))
+ ++NextReg;
+ Reg = NextReg++;
+ if (Reg > RISCV::X31)
+ report_fatal_error("Unable to find scratch registers for KCFI_CHECK");
+ }
+
+ if (AddrReg == RISCV::X0) {
+ // Checking X0 makes no sense. Instead of emitting a load, zero
+ // ScratchRegs[0].
+ EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::ADDI)
+ .addReg(ScratchRegs[0])
+ .addReg(RISCV::X0)
+ .addImm(0));
+ } else {
+ // Adjust the offset for patchable-function-prefix. This assumes that
+ // patchable-function-prefix is the same for all functions.
+ int NopSize = STI->hasStdExtCOrZca() ? 2 : 4;
+ int64_t PrefixNops = 0;
+ (void)MI.getMF()
+ ->getFunction()
+ .getFnAttribute("patchable-function-prefix")
+ .getValueAsString()
+ .getAsInteger(10, PrefixNops);
+
+ // Load the target function type hash.
+ EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::LW)
+ .addReg(ScratchRegs[0])
+ .addReg(AddrReg)
+ .addImm(-(PrefixNops * NopSize + 4)));
+ }
+
+ // Load the expected 32-bit type hash.
+ const int64_t Type = MI.getOperand(1).getImm();
+ const int64_t Hi20 = ((Type + 0x800) >> 12) & 0xFFFFF;
+ const int64_t Lo12 = SignExtend64<12>(Type);
+ if (Hi20) {
+ EmitToStreamer(
+ *OutStreamer,
+ MCInstBuilder(RISCV::LUI).addReg(ScratchRegs[1]).addImm(Hi20));
+ }
+ if (Lo12 || Hi20 == 0) {
+ EmitToStreamer(*OutStreamer,
+ MCInstBuilder((STI->hasFeature(RISCV::Feature64Bit) && Hi20)
+ ? RISCV::ADDIW
+ : RISCV::ADDI)
+ .addReg(ScratchRegs[1])
+ .addReg(ScratchRegs[1])
+ .addImm(Lo12));
+ }
+
+ // Compare the hashes and trap if there's a mismatch.
+ MCSymbol *Pass = OutContext.createTempSymbol();
+ EmitToStreamer(*OutStreamer,
+ MCInstBuilder(RISCV::BEQ)
+ .addReg(ScratchRegs[0])
+ .addReg(ScratchRegs[1])
+ .addExpr(MCSymbolRefExpr::create(Pass, OutContext)));
+
+ MCSymbol *Trap = OutContext.createTempSymbol();
+ OutStreamer->emitLabel(Trap);
+ EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::EBREAK));
+ emitKCFITrapEntry(*MI.getMF(), Trap);
+ OutStreamer->emitLabel(Pass);
+}
+
void RISCVAsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
if (HwasanMemaccessSymbols.empty())
return;
if (Glue.getNode())
Ops.push_back(Glue);
+ assert((!CLI.CFIType || CLI.CB->isIndirectCall()) &&
+ "Unexpected CFI type for a direct call");
+
// Emit the call.
SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
if (IsTailCall) {
MF.getFrameInfo().setHasTailCall();
SDValue Ret = DAG.getNode(RISCVISD::TAIL, DL, NodeTys, Ops);
+ if (CLI.CFIType)
+ Ret.getNode()->setCFIType(CLI.CFIType->getZExtValue());
DAG.addNoMergeSiteInfo(Ret.getNode(), CLI.NoMerge);
return Ret;
}
Chain = DAG.getNode(RISCVISD::CALL, DL, NodeTys, Ops);
+ if (CLI.CFIType)
+ Chain.getNode()->setCFIType(CLI.CFIType->getZExtValue());
DAG.addNoMergeSiteInfo(Chain.getNode(), CLI.NoMerge);
Glue = Chain.getValue(1);
return true;
}
+MachineInstr *
+RISCVTargetLowering::EmitKCFICheck(MachineBasicBlock &MBB,
+ MachineBasicBlock::instr_iterator &MBBI,
+ const TargetInstrInfo *TII) const {
+ assert(MBBI->isCall() && MBBI->getCFIType() &&
+ "Invalid call instruction for a KCFI check");
+ assert(is_contained({RISCV::PseudoCALLIndirect, RISCV::PseudoTAILIndirect},
+ MBBI->getOpcode()));
+
+ MachineOperand &Target = MBBI->getOperand(0);
+ Target.setIsRenamable(false);
+
+ return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(RISCV::KCFI_CHECK))
+ .addReg(Target.getReg())
+ .addImm(MBBI->getCFIType())
+ .getInstr();
+}
+
#define GET_REGISTER_MATCHER
#include "RISCVGenAsmMatcher.inc"
bool lowerInterleavedStore(StoreInst *SI, ShuffleVectorInst *SVI,
unsigned Factor) const override;
+ bool supportKCFIBundles() const override { return true; }
+
+ MachineInstr *EmitKCFICheck(MachineBasicBlock &MBB,
+ MachineBasicBlock::instr_iterator &MBBI,
+ const TargetInstrInfo *TII) const override;
+
/// RISCVCCAssignFn - This target-specific function extends the default
/// CCValAssign with additional information used to lower RISC-V calling
/// conventions.
}
}
+ if (Opcode == TargetOpcode::BUNDLE)
+ return getInstBundleLength(MI);
+
if (MI.getParent() && MI.getParent()->getParent()) {
if (isCompressibleInst(MI, STI))
return 2;
return get(Opcode).getSize();
}
+unsigned RISCVInstrInfo::getInstBundleLength(const MachineInstr &MI) const {
+ unsigned Size = 0;
+ MachineBasicBlock::const_instr_iterator I = MI.getIterator();
+ MachineBasicBlock::const_instr_iterator E = MI.getParent()->instr_end();
+ while (++I != E && I->isInsideBundle()) {
+ assert(!I->isBundle() && "No nested bundle!");
+ Size += getInstSizeInBytes(*I);
+ }
+ return Size;
+}
+
bool RISCVInstrInfo::isAsCheapAsAMove(const MachineInstr &MI) const {
const unsigned Opcode = MI.getOpcode();
switch (Opcode) {
protected:
const RISCVSubtarget &STI;
+
+private:
+ unsigned getInstBundleLength(const MachineInstr &MI) const;
};
namespace RISCV {
[(int_hwasan_check_memaccess_shortgranules X5, GPRJALR:$ptr,
(i32 timm:$accessinfo))]>;
+// This gets lowered into a 20-byte instruction sequence (at most)
+let hasSideEffects = 0, mayLoad = 1, mayStore = 0,
+ Defs = [ X6, X7, X28, X29, X30, X31 ], Size = 20 in {
+def KCFI_CHECK
+ : Pseudo<(outs), (ins GPRJALR:$ptr, i32imm:$type), []>, Sched<[]>;
+}
+
/// Simple optimization
def : Pat<(XLenVT (add GPR:$rs1, (AddiPair:$rs2))),
(ADDI (ADDI GPR:$rs1, (AddiPairImmLarge AddiPair:$rs2)),
RegisterTargetMachine<RISCVTargetMachine> Y(getTheRISCV64Target());
auto *PR = PassRegistry::getPassRegistry();
initializeGlobalISel(*PR);
+ initializeKCFIPass(*PR);
initializeRISCVMakeCompressibleOptPass(*PR);
initializeRISCVGatherScatterLoweringPass(*PR);
initializeRISCVCodeGenPreparePass(*PR);
return false;
}
-void RISCVPassConfig::addPreSched2() {}
+void RISCVPassConfig::addPreSched2() {
+ // Emit KCFI checks for indirect calls.
+ addPass(createKCFIPass());
+}
void RISCVPassConfig::addPreEmitPass() {
addPass(&BranchRelaxationPassID);
// possibility for other passes to break the requirements for forward
// progress in the LR/SC block.
addPass(createRISCVExpandAtomicPseudoPass());
+
+ // KCFI indirect call checks are lowered to a bundle.
+ addPass(createUnpackMachineBundles([&](const MachineFunction &MF) {
+ return MF.getFunction().getParent()->getModuleFlag("kcfi");
+ }));
}
void RISCVPassConfig::addMachineSSAOptimization() {
; CHECK-NEXT: Machine Optimization Remark Emitter
; CHECK-NEXT: Prologue/Epilogue Insertion & Frame Finalization
; CHECK-NEXT: Post-RA pseudo instruction expansion pass
+; CHECK-NEXT: Insert KCFI indirect call checks
; CHECK-NEXT: Analyze Machine Code For Garbage Collection
; CHECK-NEXT: Insert fentry calls
; CHECK-NEXT: Insert XRay ops
; CHECK-NEXT: Stack Frame Layout Analysis
; CHECK-NEXT: RISC-V pseudo instruction expansion pass
; CHECK-NEXT: RISC-V atomic pseudo instruction expansion pass
+; CHECK-NEXT: Unpack machine instruction bundles
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
; CHECK-NEXT: Machine Optimization Remark Emitter
; CHECK-NEXT: RISC-V Assembly Printer
; CHECK-NEXT: Tail Duplication
; CHECK-NEXT: Machine Copy Propagation Pass
; CHECK-NEXT: Post-RA pseudo instruction expansion pass
+; CHECK-NEXT: Insert KCFI indirect call checks
; CHECK-NEXT: MachineDominator Tree Construction
; CHECK-NEXT: Machine Natural Loop Construction
; CHECK-NEXT: Post RA top-down list latency scheduler
; CHECK-NEXT: RISC-V Zcmp move merging pass
; CHECK-NEXT: RISC-V pseudo instruction expansion pass
; CHECK-NEXT: RISC-V atomic pseudo instruction expansion pass
+; CHECK-NEXT: Unpack machine instruction bundles
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
; CHECK-NEXT: Machine Optimization Remark Emitter
; CHECK-NEXT: RISC-V Assembly Printer
--- /dev/null
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 2
+# RUN: llc -mtriple=riscv64 -stop-after=finalize-isel -o - %s | FileCheck %s
+--- |
+ ; ModuleID = '<stdin>'
+ source_filename = "<stdin>"
+ target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
+ target triple = "riscv64"
+
+ define void @f1(ptr noundef %x) !kcfi_type !1 {
+ call void %x() [ "kcfi"(i32 12345678) ]
+ ret void
+ }
+
+ define void @f2(ptr noundef %x) #0 {
+ tail call void %x() [ "kcfi"(i32 12345678) ]
+ ret void
+ }
+
+ attributes #0 = { "patchable-function-entry"="2" }
+
+ !llvm.module.flags = !{!0}
+
+ !0 = !{i32 4, !"kcfi", i32 1}
+ !1 = !{i32 12345678}
+
+...
+---
+name: f1
+alignment: 4
+exposesReturnsTwice: false
+legalized: false
+regBankSelected: false
+selected: false
+failedISel: false
+tracksRegLiveness: true
+hasWinCFI: false
+callsEHReturn: false
+callsUnwindInit: false
+hasEHCatchret: false
+hasEHScopes: false
+hasEHFunclets: false
+isOutlined: false
+debugInstrRef: false
+failsVerification: false
+tracksDebugUserValues: false
+registers:
+ - { id: 0, class: gprjalr, preferred-register: '' }
+liveins:
+ - { reg: '$x10', virtual-reg: '%0' }
+frameInfo:
+ isFrameAddressTaken: false
+ isReturnAddressTaken: false
+ hasStackMap: false
+ hasPatchPoint: false
+ stackSize: 0
+ offsetAdjustment: 0
+ maxAlignment: 1
+ adjustsStack: false
+ hasCalls: true
+ stackProtector: ''
+ functionContext: ''
+ maxCallFrameSize: 4294967295
+ cvBytesOfCalleeSavedRegisters: 0
+ hasOpaqueSPAdjustment: false
+ hasVAStart: false
+ hasMustTailInVarArgFunc: false
+ hasTailCall: false
+ localFrameSize: 0
+ savePoint: ''
+ restorePoint: ''
+fixedStack: []
+stack: []
+callSites: []
+debugValueSubstitutions: []
+constants: []
+machineFunctionInfo:
+ varArgsFrameIndex: 0
+ varArgsSaveSize: 0
+body: |
+ bb.0 (%ir-block.0):
+ liveins: $x10
+
+ ; CHECK-LABEL: name: f1
+ ; CHECK: liveins: $x10, $x10, $x10
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gprjalr = COPY $x10
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gprjalr = COPY $x10
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprjalr = COPY $x10
+ ; CHECK-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+ ; CHECK-NEXT: PseudoCALLIndirect [[COPY2]], csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2, cfi-type 12345678
+ ; CHECK-NEXT: ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+ ; CHECK-NEXT: PseudoRET
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: .1 (%ir-block.0):
+ ; CHECK-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+ ; CHECK-NEXT: PseudoCALLIndirect [[COPY]], csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2, cfi-type 12345678
+ ; CHECK-NEXT: ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+ ; CHECK-NEXT: PseudoRET
+ %0:gprjalr = COPY $x10
+ ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+ PseudoCALLIndirect %0, csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2, cfi-type 12345678
+ ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+ PseudoRET
+
+...
+---
+name: f2
+alignment: 4
+exposesReturnsTwice: false
+legalized: false
+regBankSelected: false
+selected: false
+failedISel: false
+tracksRegLiveness: true
+hasWinCFI: false
+callsEHReturn: false
+callsUnwindInit: false
+hasEHCatchret: false
+hasEHScopes: false
+hasEHFunclets: false
+isOutlined: false
+debugInstrRef: false
+failsVerification: false
+tracksDebugUserValues: false
+registers:
+ - { id: 0, class: gprtc, preferred-register: '' }
+liveins:
+ - { reg: '$x10', virtual-reg: '%0' }
+frameInfo:
+ isFrameAddressTaken: false
+ isReturnAddressTaken: false
+ hasStackMap: false
+ hasPatchPoint: false
+ stackSize: 0
+ offsetAdjustment: 0
+ maxAlignment: 1
+ adjustsStack: false
+ hasCalls: false
+ stackProtector: ''
+ functionContext: ''
+ maxCallFrameSize: 4294967295
+ cvBytesOfCalleeSavedRegisters: 0
+ hasOpaqueSPAdjustment: false
+ hasVAStart: false
+ hasMustTailInVarArgFunc: false
+ hasTailCall: true
+ localFrameSize: 0
+ savePoint: ''
+ restorePoint: ''
+fixedStack: []
+stack: []
+callSites: []
+debugValueSubstitutions: []
+constants: []
+machineFunctionInfo:
+ varArgsFrameIndex: 0
+ varArgsSaveSize: 0
+body: |
+ bb.0 (%ir-block.0):
+ liveins: $x10
+
+ ; CHECK-LABEL: name: f2
+ ; CHECK: liveins: $x10, $x10, $x10
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gprtc = COPY $x10
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gprtc = COPY $x10
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprtc = COPY $x10
+ ; CHECK-NEXT: PseudoTAILIndirect [[COPY2]], implicit $x2, cfi-type 12345678
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: .1 (%ir-block.0):
+ ; CHECK-NEXT: PseudoTAILIndirect [[COPY]], implicit $x2, cfi-type 12345678
+ %0:gprtc = COPY $x10
+ PseudoTAILIndirect %0, implicit $x2, cfi-type 12345678
+
+...
--- /dev/null
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,NOC
+; RUN: llc -mtriple=riscv64 -mattr=+c -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,C
+
+; NOC: .p2align 2
+; C: .p2align 1
+; CHECK-NOT: nop
+; CHECK: .word 12345678
+; CHECK-LABEL: f1:
+define void @f1(ptr noundef %x) !kcfi_type !1 {
+; CHECK: lw t1, -4(a0)
+ call void %x() [ "kcfi"(i32 12345678) ]
+ ret void
+}
+
+; NOC: .p2align 2
+; C: .p2align 1
+; CHECK-NOT: .word
+; CHECK-NOT: nop
+; CHECK-LABEL: f2:
+define void @f2(ptr noundef %x) {
+; CHECK: lw t1, -4(a0)
+ call void %x() [ "kcfi"(i32 12345678) ]
+ ret void
+}
+
+; NOC: .p2align 2
+; C: .p2align 1
+; CHECK: .word 12345678
+; CHECK-COUNT-11: nop
+; CHECK-LABEL: f3:
+define void @f3(ptr noundef %x) #0 !kcfi_type !1 {
+; NOC: lw t1, -48(a0)
+; C: lw t1, -26(a0)
+ call void %x() [ "kcfi"(i32 12345678) ]
+ ret void
+}
+
+; NOC: .p2align 2
+; C: .p2align 1
+; CHECK-NOT: .word
+; CHECK-COUNT-11: nop
+; CHECK-LABEL: f4:
+define void @f4(ptr noundef %x) #0 {
+; NOC: lw t1, -48(a0)
+; C: lw t1, -26(a0)
+ call void %x() [ "kcfi"(i32 12345678) ]
+ ret void
+}
+
+attributes #0 = { "patchable-function-prefix"="11" }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 4, !"kcfi", i32 1}
+!1 = !{i32 12345678}
--- /dev/null
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs -riscv-no-aliases < %s \
+; RUN: | FileCheck %s --check-prefixes=CHECK,RV32
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs -riscv-no-aliases < %s \
+; RUN: | FileCheck %s --check-prefixes=CHECK,RV64
+
+; CHECK: .word 12345678
+define void @f1(ptr noundef %x) !kcfi_type !1 {
+; CHECK-LABEL: f1:
+; CHECK: # %bb.0:
+; CHECK: lw t1, -4(a0)
+; CHECK-NEXT: lui t2, 3014
+; RV32-NEXT: addi t2, t2, 334
+; RV64-NEXT: addiw t2, t2, 334
+; CHECK-NEXT: beq t1, t2, .Ltmp0
+; CHECK-NEXT: .Ltmp1:
+; CHECK-NEXT: ebreak
+; CHECK-NEXT: .section .kcfi_traps,"ao",@progbits,.text
+; CHECK-NEXT: .Ltmp2:
+; CHECK-NEXT: .word .Ltmp1-.Ltmp2
+; CHECK-NEXT: .text
+; CHECK-NEXT: .Ltmp0:
+; CHECK-NEXT: jalr ra, 0(a0)
+ call void %x() [ "kcfi"(i32 12345678) ]
+; CHECK: lw t1, -4(s0)
+; CHECK-NEXT: addi t2, t2, 1234
+; CHECK-NEXT: beq t1, t2, .Ltmp3
+; CHECK-NEXT: .Ltmp4:
+; CHECK-NEXT: ebreak
+; CHECK-NEXT: .section .kcfi_traps,"ao",@progbits,.text
+; CHECK-NEXT: .Ltmp5:
+; CHECK-NEXT: .word .Ltmp4-.Ltmp5
+; CHECK-NEXT: .text
+; CHECK-NEXT: .Ltmp3:
+; CHECK-NEXT: jalr ra, 0(s0)
+ call void %x() [ "kcfi"(i32 1234) ]
+ ret void
+}
+
+; CHECK-NOT: .word:
+define void @f2(ptr noundef %x) #0 {
+; CHECK-LABEL: f2:
+; CHECK: # %bb.0:
+; CHECK-NEXT: addi zero, zero, 0
+; CHECK-NEXT: addi zero, zero, 0
+; CHECK-NEXT: lw t1, -4(a0)
+; CHECK-NEXT: lui t2, 3014
+; RV32-NEXT: addi t2, t2, 334
+; RV64-NEXT: addiw t2, t2, 334
+; CHECK-NEXT: beq t1, t2, .Ltmp6
+; CHECK-NEXT: .Ltmp7:
+; CHECK-NEXT: ebreak
+; CHECK-NEXT: .section .kcfi_traps,"ao",@progbits,.text
+; CHECK-NEXT: .Ltmp8:
+; CHECK-NEXT: .word .Ltmp7-.Ltmp8
+; CHECK-NEXT: .text
+; CHECK-NEXT: .Ltmp6:
+; CHECK-NEXT: jalr zero, 0(a0)
+ tail call void %x() [ "kcfi"(i32 12345678) ]
+ ret void
+}
+
+attributes #0 = { "patchable-function-entry"="2" }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 4, !"kcfi", i32 1}
+!1 = !{i32 12345678}
--- /dev/null
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 2
+# RUN: llc -mtriple=riscv64 -stop-after=kcfi -o - %s | FileCheck %s
+--- |
+ ; ModuleID = '<stdin>'
+ source_filename = "<stdin>"
+ target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
+ target triple = "riscv64"
+
+ define void @f1(ptr noundef %x) !kcfi_type !1 {
+ call void %x() [ "kcfi"(i32 12345678) ]
+ ret void
+ }
+
+ define void @f2(ptr noundef %x) #0 {
+ tail call void %x() [ "kcfi"(i32 12345678) ]
+ ret void
+ }
+
+ attributes #0 = { "patchable-function-entry"="2" }
+
+ !llvm.module.flags = !{!0}
+
+ !0 = !{i32 4, !"kcfi", i32 1}
+ !1 = !{i32 12345678}
+
+...
+---
+name: f1
+alignment: 4
+exposesReturnsTwice: false
+legalized: false
+regBankSelected: false
+selected: false
+failedISel: false
+tracksRegLiveness: true
+hasWinCFI: false
+callsEHReturn: false
+callsUnwindInit: false
+hasEHCatchret: false
+hasEHScopes: false
+hasEHFunclets: false
+isOutlined: false
+debugInstrRef: false
+failsVerification: false
+tracksDebugUserValues: true
+registers: []
+liveins:
+ - { reg: '$x10', virtual-reg: '' }
+frameInfo:
+ isFrameAddressTaken: false
+ isReturnAddressTaken: false
+ hasStackMap: false
+ hasPatchPoint: false
+ stackSize: 16
+ offsetAdjustment: 0
+ maxAlignment: 8
+ adjustsStack: true
+ hasCalls: true
+ stackProtector: ''
+ functionContext: ''
+ maxCallFrameSize: 0
+ cvBytesOfCalleeSavedRegisters: 0
+ hasOpaqueSPAdjustment: false
+ hasVAStart: false
+ hasMustTailInVarArgFunc: false
+ hasTailCall: false
+ localFrameSize: 0
+ savePoint: ''
+ restorePoint: ''
+fixedStack: []
+stack:
+ - { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8,
+ stack-id: default, callee-saved-register: '$x1', callee-saved-restored: true,
+ debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+callSites: []
+debugValueSubstitutions: []
+constants: []
+machineFunctionInfo:
+ varArgsFrameIndex: 0
+ varArgsSaveSize: 0
+body: |
+ bb.0 (%ir-block.0):
+ liveins: $x10, $x1
+
+ ; CHECK-LABEL: name: f1
+ ; CHECK: liveins: $x1, $x10
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: $x2 = frame-setup ADDI $x2, -16
+ ; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+ ; CHECK-NEXT: SD $x1, $x2, 8 :: (store (s64) into %stack.1)
+ ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $x1, -8
+ ; CHECK-NEXT: $x2 = frame-setup ADDI $x2, -16
+ ; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+ ; CHECK-NEXT: SD $x1, $x2, 8 :: (store (s64) into %stack.0)
+ ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $x1, -8
+ ; CHECK-NEXT: BUNDLE implicit-def dead $x6, implicit-def dead $x7, implicit-def dead $x28, implicit-def dead $x29, implicit-def dead $x30, implicit-def dead $x31, implicit-def dead $x1, implicit-def $x2, implicit $x10 {
+ ; CHECK-NEXT: KCFI_CHECK $x10, 12345678, implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31
+ ; CHECK-NEXT: PseudoCALLIndirect $x10, csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2
+ ; CHECK-NEXT: }
+ ; CHECK-NEXT: dead $x1 = LD $x2, 8 :: (load (s64) from %stack.0)
+ ; CHECK-NEXT: $x2 = frame-destroy ADDI $x2, 16
+ ; CHECK-NEXT: $x1 = LD $x2, 8 :: (load (s64) from %stack.1)
+ ; CHECK-NEXT: $x2 = frame-destroy ADDI $x2, 16
+ ; CHECK-NEXT: PseudoRET
+ $x2 = frame-setup ADDI $x2, -16
+ frame-setup CFI_INSTRUCTION def_cfa_offset 16
+ SD killed $x1, $x2, 8 :: (store (s64) into %stack.0)
+ frame-setup CFI_INSTRUCTION offset $x1, -8
+ BUNDLE implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31, implicit-def dead $x1, implicit-def $x2, implicit killed $x10 {
+ KCFI_CHECK $x10, 12345678, implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31
+ PseudoCALLIndirect killed $x10, csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2
+ }
+ $x1 = LD $x2, 8 :: (load (s64) from %stack.0)
+ $x2 = frame-destroy ADDI $x2, 16
+ PseudoRET
+
+...
+---
+name: f2
+alignment: 4
+exposesReturnsTwice: false
+legalized: false
+regBankSelected: false
+selected: false
+failedISel: false
+tracksRegLiveness: true
+hasWinCFI: false
+callsEHReturn: false
+callsUnwindInit: false
+hasEHCatchret: false
+hasEHScopes: false
+hasEHFunclets: false
+isOutlined: false
+debugInstrRef: false
+failsVerification: false
+tracksDebugUserValues: true
+registers: []
+liveins:
+ - { reg: '$x10', virtual-reg: '' }
+frameInfo:
+ isFrameAddressTaken: false
+ isReturnAddressTaken: false
+ hasStackMap: false
+ hasPatchPoint: false
+ stackSize: 0
+ offsetAdjustment: 0
+ maxAlignment: 1
+ adjustsStack: false
+ hasCalls: false
+ stackProtector: ''
+ functionContext: ''
+ maxCallFrameSize: 0
+ cvBytesOfCalleeSavedRegisters: 0
+ hasOpaqueSPAdjustment: false
+ hasVAStart: false
+ hasMustTailInVarArgFunc: false
+ hasTailCall: true
+ localFrameSize: 0
+ savePoint: ''
+ restorePoint: ''
+fixedStack: []
+stack: []
+callSites: []
+debugValueSubstitutions: []
+constants: []
+machineFunctionInfo:
+ varArgsFrameIndex: 0
+ varArgsSaveSize: 0
+body: |
+ bb.0 (%ir-block.0):
+ liveins: $x10
+
+ ; CHECK-LABEL: name: f2
+ ; CHECK: liveins: $x10
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: BUNDLE implicit-def dead $x6, implicit-def dead $x7, implicit-def dead $x28, implicit-def dead $x29, implicit-def dead $x30, implicit-def dead $x31, implicit $x10, implicit $x2 {
+ ; CHECK-NEXT: KCFI_CHECK $x10, 12345678, implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31
+ ; CHECK-NEXT: PseudoTAILIndirect $x10, implicit $x2
+ ; CHECK-NEXT: }
+ BUNDLE implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31, implicit killed $x10, implicit $x2 {
+ KCFI_CHECK $x10, 12345678, implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31
+ PseudoTAILIndirect killed $x10, implicit $x2
+ }
+
+...