diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -730,7 +730,8 @@ def PatchableFunctionEntry : InheritableAttr, - TargetSpecificAttr> { + TargetSpecificAttr> { let Spellings = [GCC<"patchable_function_entry">]; let Subjects = SubjectList<[Function, ObjCMethod]>; let Args = [UnsignedArgument<"Count">, DefaultIntArgument<"Offset", 0>]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4810,6 +4810,9 @@ 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. }]; } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5614,8 +5614,7 @@ 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) || diff --git a/clang/test/Driver/fpatchable-function-entry.c b/clang/test/Driver/fpatchable-function-entry.c --- a/clang/test/Driver/fpatchable-function-entry.c +++ b/clang/test/Driver/fpatchable-function-entry.c @@ -2,6 +2,8 @@ // 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 diff --git a/clang/test/Sema/patchable-function-entry-attr.cpp b/clang/test/Sema/patchable-function-entry-attr.cpp --- a/clang/test/Sema/patchable-function-entry-attr.cpp +++ b/clang/test/Sema/patchable-function-entry-attr.cpp @@ -2,6 +2,8 @@ // 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 diff --git a/llvm/lib/Target/RISCV/RISCV.h b/llvm/lib/Target/RISCV/RISCV.h --- a/llvm/lib/Target/RISCV/RISCV.h +++ b/llvm/lib/Target/RISCV/RISCV.h @@ -30,8 +30,8 @@ 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); 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 @@ -93,8 +93,8 @@ 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, 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 @@ -29,6 +29,8 @@ public: explicit RISCVInstrInfo(RISCVSubtarget &STI); + MCInst getNop() const override; + unsigned isLoadFromStackSlot(const MachineInstr &MI, int &FrameIndex) const override; unsigned isStoreToStackSlot(const MachineInstr &MI, 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 @@ -23,6 +23,7 @@ #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" @@ -49,6 +50,15 @@ : 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()) { diff --git a/llvm/lib/Target/RISCV/RISCVMCInstLower.cpp b/llvm/lib/Target/RISCV/RISCVMCInstLower.cpp --- a/llvm/lib/Target/RISCV/RISCVMCInstLower.cpp +++ b/llvm/lib/Target/RISCV/RISCVMCInstLower.cpp @@ -204,10 +204,10 @@ 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()); @@ -217,19 +217,32 @@ 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; } diff --git a/llvm/test/CodeGen/RISCV/patchable-function-entry.ll b/llvm/test/CodeGen/RISCV/patchable-function-entry.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/patchable-function-entry.ll @@ -0,0 +1,71 @@ +;; 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 +}