def PatchableFunctionEntry
: InheritableAttr,
- TargetSpecificAttr<TargetArch<["aarch64", "aarch64_be", "x86", "x86_64"]>> {
+ TargetSpecificAttr<TargetArch<
+ ["aarch64", "aarch64_be", "riscv32", "riscv64", "x86", "x86_64"]>> {
let Spellings = [GCC<"patchable_function_entry">];
let Subjects = SubjectList<[Function, ObjCMethod]>;
let Args = [UnsignedArgument<"Count">, DefaultIntArgument<"Offset", 0>];
before the function entry and N-M NOPs after the function entry. This attribute
takes precedence over the command line option ``-fpatchable-function-entry=N,M``.
``M`` defaults to 0 if omitted.
+
+This attribute is only supported on
+aarch64/aarch64-be/riscv32/riscv64/i386/x86-64 targets.
}];
}
if (Arg *A = Args.getLastArg(options::OPT_fpatchable_function_entry_EQ)) {
StringRef S0 = A->getValue(), S = S0;
unsigned Size, Offset = 0;
- if (!Triple.isAArch64() && Triple.getArch() != llvm::Triple::x86 &&
- Triple.getArch() != llvm::Triple::x86_64)
+ if (!Triple.isAArch64() && !Triple.isRISCV() && !Triple.isX86())
D.Diag(diag::err_drv_unsupported_opt_for_target)
<< A->getAsString(Args) << TripleStr;
else if (S.consumeInteger(10, Size) ||
// RUN: %clang -target x86_64 %s -fpatchable-function-entry=1 -c -### 2>&1 | FileCheck %s
// RUN: %clang -target aarch64 %s -fpatchable-function-entry=1 -c -### 2>&1 | FileCheck %s
// RUN: %clang -target aarch64 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
+// RUN: %clang -target riscv32 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
+// RUN: %clang -target riscv64 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
// CHECK: "-fpatchable-function-entry=1"
// RUN: %clang -target aarch64 -fsyntax-only %s -fpatchable-function-entry=1,1 -c -### 2>&1 | FileCheck --check-prefix=11 %s
// RUN: %clang_cc1 -triple aarch64_be -fsyntax-only -verify=silence %s
// RUN: %clang_cc1 -triple i386 -fsyntax-only -verify=silence %s
// RUN: %clang_cc1 -triple x86_64 -fsyntax-only -verify=silence %s
+// RUN: %clang_cc1 -triple riscv32 -fsyntax-only -verify=silence %s
+// RUN: %clang_cc1 -triple riscv64 -fsyntax-only -verify=silence %s
// RUN: %clang_cc1 -triple ppc64le -fsyntax-only -verify %s
// silence-no-diagnostics
class MachineOperand;
class PassRegistry;
-void LowerRISCVMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
- const AsmPrinter &AP);
+bool lowerRISCVMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
+ AsmPrinter &AP);
bool LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO,
MCOperand &MCOp, const AsmPrinter &AP);
return;
MCInst TmpInst;
- LowerRISCVMachineInstrToMCInst(MI, TmpInst, *this);
- EmitToStreamer(*OutStreamer, TmpInst);
+ if (!lowerRISCVMachineInstrToMCInst(MI, TmpInst, *this))
+ EmitToStreamer(*OutStreamer, TmpInst);
}
bool RISCVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RegisterScavenging.h"
+#include "llvm/MC/MCInstBuilder.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TargetRegistry.h"
: RISCVGenInstrInfo(RISCV::ADJCALLSTACKDOWN, RISCV::ADJCALLSTACKUP),
STI(STI) {}
+MCInst RISCVInstrInfo::getNop() const {
+ if (STI.getFeatureBits()[RISCV::FeatureStdExtC])
+ return MCInstBuilder(RISCV::C_NOP);
+ return MCInstBuilder(RISCV::ADDI)
+ .addReg(RISCV::X0)
+ .addReg(RISCV::X0)
+ .addImm(0);
+}
+
unsigned RISCVInstrInfo::isLoadFromStackSlot(const MachineInstr &MI,
int &FrameIndex) const {
switch (MI.getOpcode()) {
public:
explicit RISCVInstrInfo(RISCVSubtarget &STI);
+ MCInst getNop() const override;
+
unsigned isLoadFromStackSlot(const MachineInstr &MI,
int &FrameIndex) const override;
unsigned isStoreToStackSlot(const MachineInstr &MI,
return true;
}
-void llvm::LowerRISCVMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
- const AsmPrinter &AP) {
+bool llvm::lowerRISCVMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
+ AsmPrinter &AP) {
if (lowerRISCVVMachineInstrToMCInst(MI, OutMI))
- return;
+ return false;
OutMI.setOpcode(MI->getOpcode());
OutMI.addOperand(MCOp);
}
- if (OutMI.getOpcode() == RISCV::PseudoReadVLENB) {
+ switch (OutMI.getOpcode()) {
+ case TargetOpcode::PATCHABLE_FUNCTION_ENTER: {
+ const Function &F = MI->getParent()->getParent()->getFunction();
+ if (F.hasFnAttribute("patchable-function-entry")) {
+ unsigned Num;
+ if (F.getFnAttribute("patchable-function-entry")
+ .getValueAsString()
+ .getAsInteger(10, Num))
+ return false;
+ AP.emitNops(Num);
+ return true;
+ }
+ break;
+ }
+ case RISCV::PseudoReadVLENB:
OutMI.setOpcode(RISCV::CSRRS);
OutMI.addOperand(MCOperand::createImm(
RISCVSysReg::lookupSysRegByName("VLENB")->Encoding));
OutMI.addOperand(MCOperand::createReg(RISCV::X0));
- return;
- }
-
- if (OutMI.getOpcode() == RISCV::PseudoReadVL) {
+ break;
+ case RISCV::PseudoReadVL:
OutMI.setOpcode(RISCV::CSRRS);
- OutMI.addOperand(MCOperand::createImm(
- RISCVSysReg::lookupSysRegByName("VL")->Encoding));
+ OutMI.addOperand(
+ MCOperand::createImm(RISCVSysReg::lookupSysRegByName("VL")->Encoding));
OutMI.addOperand(MCOperand::createReg(RISCV::X0));
- return;
+ break;
}
+ return false;
}
--- /dev/null
+;; Test the function attribute "patchable-function-entry".
+; RUN: llc -mtriple=riscv32 --riscv-no-aliases < %s | FileCheck %s --check-prefixes=CHECK,RV32,NORVC
+; RUN: llc -mtriple=riscv64 --riscv-no-aliases < %s | FileCheck %s --check-prefixes=CHECK,RV64,NORVC
+; RUN: llc -mtriple=riscv32 -mattr=+c --riscv-no-aliases < %s | FileCheck %s --check-prefixes=CHECK,RV32,RVC
+; RUN: llc -mtriple=riscv64 -mattr=+c --riscv-no-aliases < %s | FileCheck %s --check-prefixes=CHECK,RV64,RVC
+
+define void @f0() "patchable-function-entry"="0" {
+; CHECK-LABEL: f0:
+; CHECK-NEXT: .Lfunc_begin0:
+; CHECK-NOT: {{addi|c.nop}}
+; NORVC: jalr zero, 0(ra)
+; RVC: c.jr ra
+; CHECK-NOT: .section __patchable_function_entries
+ ret void
+}
+
+define void @f1() "patchable-function-entry"="1" {
+; CHECK-LABEL: f1:
+; CHECK-NEXT: .Lfunc_begin1:
+; NORVC: addi zero, zero, 0
+; NORVC-NEXT: jalr zero, 0(ra)
+; RVC: c.nop
+; RVC-NEXT: c.jr ra
+; CHECK: .section __patchable_function_entries,"awo",@progbits,f1{{$}}
+; 32: .p2align 2
+; 32-NEXT: .word .Lfunc_begin1
+; 64: .p2align 3
+; 64-NEXT: .quad .Lfunc_begin1
+ ret void
+}
+
+$f5 = comdat any
+define void @f5() "patchable-function-entry"="5" comdat {
+; CHECK-LABEL: f5:
+; CHECK-NEXT: .Lfunc_begin2:
+; NORVC-COUNT-5: addi zero, zero, 0
+; NORVC-NEXT: jalr zero, 0(ra)
+; RVC-COUNT-5: c.nop
+; RVC-NEXT: c.jr ra
+; CHECK: .section __patchable_function_entries,"aGwo",@progbits,f5,comdat,f5{{$}}
+; RV32: .p2align 2
+; RV32-NEXT: .word .Lfunc_begin2
+; RV64: .p2align 3
+; RV64-NEXT: .quad .Lfunc_begin2
+ ret void
+}
+
+;; -fpatchable-function-entry=3,2
+;; "patchable-function-prefix" emits data before the function entry label.
+define void @f3_2() "patchable-function-entry"="1" "patchable-function-prefix"="2" {
+; CHECK-LABEL: .type f3_2,@function
+; CHECK-NEXT: .Ltmp0: # @f3_2
+; NORVC-COUNT-2: addi zero, zero, 0
+; RVC-COUNT-2: c.nop
+; CHECK-NEXT: f3_2:
+; CHECK: # %bb.0:
+; NORVC-NEXT: addi zero, zero, 0
+; NORVC-NEXT: addi sp, sp, -16
+; RVC-NEXT: c.nop
+; RVC-NEXT: c.addi sp, -16
+;; .size does not include the prefix.
+; CHECK: .Lfunc_end3:
+; CHECK-NEXT: .size f3_2, .Lfunc_end3-f3_2
+; CHECK: .section __patchable_function_entries,"awo",@progbits,f3_2{{$}}
+; RV32: .p2align 2
+; RV32-NEXT: .word .Ltmp0
+; RV64: .p2align 3
+; RV64-NEXT: .quad .Ltmp0
+ %frame = alloca i8, i32 16
+ ret void
+}