diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -631,7 +631,7 @@ 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. diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp --- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -19,6 +19,7 @@ #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" @@ -72,6 +73,7 @@ typedef std::tuple HwasanMemaccessTuple; std::map HwasanMemaccessSymbols; void LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI); + void LowerKCFI_CHECK(const MachineInstr &MI); void EmitHwasanMemaccessSymbols(Module &M); // Wrapper needed for tblgenned pseudo lowering. @@ -150,6 +152,9 @@ 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: @@ -305,6 +310,92 @@ 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; diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h --- a/llvm/lib/Target/RISCV/RISCVISelLowering.h +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h @@ -759,6 +759,12 @@ 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. diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -15395,17 +15395,24 @@ 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); @@ -16864,6 +16871,24 @@ 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" diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h @@ -237,6 +237,9 @@ protected: const RISCVSubtarget &STI; + +private: + unsigned getInstBundleLength(const MachineInstr &MI) const; }; namespace RISCV { diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -1265,6 +1265,9 @@ } } + if (Opcode == TargetOpcode::BUNDLE) + return getInstBundleLength(MI); + if (MI.getParent() && MI.getParent()->getParent()) { if (isCompressibleInst(MI, STI)) return 2; @@ -1272,6 +1275,17 @@ 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) { diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -1887,6 +1887,13 @@ [(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)), diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp --- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -76,6 +76,7 @@ RegisterTargetMachine Y(getTheRISCV64Target()); auto *PR = PassRegistry::getPassRegistry(); initializeGlobalISel(*PR); + initializeKCFIPass(*PR); initializeRISCVMakeCompressibleOptPass(*PR); initializeRISCVGatherScatterLoweringPass(*PR); initializeRISCVCodeGenPreparePass(*PR); @@ -333,7 +334,10 @@ return false; } -void RISCVPassConfig::addPreSched2() {} +void RISCVPassConfig::addPreSched2() { + // Emit KCFI checks for indirect calls. + addPass(createKCFIPass()); +} void RISCVPassConfig::addPreEmitPass() { addPass(&BranchRelaxationPassID); @@ -357,6 +361,11 @@ // 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() { diff --git a/llvm/test/CodeGen/RISCV/O0-pipeline.ll b/llvm/test/CodeGen/RISCV/O0-pipeline.ll --- a/llvm/test/CodeGen/RISCV/O0-pipeline.ll +++ b/llvm/test/CodeGen/RISCV/O0-pipeline.ll @@ -51,6 +51,7 @@ ; 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 @@ -66,6 +67,7 @@ ; 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 diff --git a/llvm/test/CodeGen/RISCV/O3-pipeline.ll b/llvm/test/CodeGen/RISCV/O3-pipeline.ll --- a/llvm/test/CodeGen/RISCV/O3-pipeline.ll +++ b/llvm/test/CodeGen/RISCV/O3-pipeline.ll @@ -155,6 +155,7 @@ ; 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 @@ -180,6 +181,7 @@ ; 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 diff --git a/llvm/test/CodeGen/RISCV/kcfi-isel.mir b/llvm/test/CodeGen/RISCV/kcfi-isel.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/kcfi-isel.mir @@ -0,0 +1,175 @@ +# 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 = '' + source_filename = "" + 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 + +... diff --git a/llvm/test/CodeGen/RISCV/kcfi-patchable-function-prefix.ll b/llvm/test/CodeGen/RISCV/kcfi-patchable-function-prefix.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/kcfi-patchable-function-prefix.ll @@ -0,0 +1,54 @@ +; 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} diff --git a/llvm/test/CodeGen/RISCV/kcfi.ll b/llvm/test/CodeGen/RISCV/kcfi.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/kcfi.ll @@ -0,0 +1,66 @@ +; 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} diff --git a/llvm/test/CodeGen/RISCV/kcfi.mir b/llvm/test/CodeGen/RISCV/kcfi.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/kcfi.mir @@ -0,0 +1,185 @@ +# 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 = '' + source_filename = "" + 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 + } + +...