diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -286,6 +286,9 @@ /// Emit a table with all XRay instrumentation points. void emitXRayTable(); + DenseMap PatchableFunctionEntryID; + void emitPatchableFunctionEntries(); + //===------------------------------------------------------------------===// // MachineFunctionPass Implementation. //===------------------------------------------------------------------===// diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -1222,6 +1222,8 @@ // Emit section containing stack size metadata. emitStackSizeSection(*MF); + emitPatchableFunctionEntries(); + if (isVerbose()) OutStreamer->GetCommentOS() << "-- End function\n"; @@ -1660,6 +1662,7 @@ void AsmPrinter::SetupMachineFunction(MachineFunction &MF) { this->MF = &MF; + const Function &F = MF.getFunction(); // Get the function symbol. if (MAI->needsFunctionDescriptors()) { @@ -1672,7 +1675,6 @@ CurrentFnSym = OutContext.getOrCreateSymbol("." + CurrentFnDescSym->getName()); - const Function &F = MF.getFunction(); MCSectionXCOFF *FnEntryPointSec = cast(getObjFileLowering().SectionForGlobal(&F, TM)); // Set the containing csect. @@ -1685,7 +1687,8 @@ CurrentFnBegin = nullptr; CurExceptionSym = nullptr; bool NeedsLocalForSize = MAI->needsLocalForSize(); - if (needFuncLabelsForEHOrDebugInfo(MF, MMI) || NeedsLocalForSize || + if (F.hasFnAttribute("patchable-function-entry") || + needFuncLabelsForEHOrDebugInfo(MF, MMI) || NeedsLocalForSize || MF.getTarget().Options.EmitStackSizeSection) { CurrentFnBegin = createTempSymbol("func_begin"); if (NeedsLocalForSize) @@ -3194,6 +3197,38 @@ AlwaysInstrument, &F, Version}); } +void AsmPrinter::emitPatchableFunctionEntries() { + const Function &F = MF->getFunction(); + if (!F.hasFnAttribute("patchable-function-entry")) + return; + const unsigned PointerSize = getPointerSize(); + if (TM.getTargetTriple().isOSBinFormatELF()) { + auto Flags = ELF::SHF_WRITE | ELF::SHF_ALLOC; + std::string GroupName; + if (F.hasComdat()) { + Flags |= ELF::SHF_GROUP; + GroupName = F.getComdat()->getName(); + } + + // As of binutils 2.33, GNU as does not support section flag "o". Use + // SHF_LINK_ORDER if we are using the integrated assembler. + MCSymbolELF *Link = MAI->useIntegratedAssembler() + ? cast(CurrentFnSym) + : nullptr; + if (Link) + Flags |= ELF::SHF_LINK_ORDER; + + MCSection *Section = getObjFileLowering().SectionForGlobal(&F, TM); + auto R = PatchableFunctionEntryID.try_emplace( + Section, PatchableFunctionEntryID.size()); + OutStreamer->SwitchSection(OutContext.getELFSection( + "__patchable_function_entries", ELF::SHT_PROGBITS, Flags, 0, GroupName, + R.first->second, Link)); + EmitAlignment(Align(PointerSize)); + OutStreamer->EmitSymbolValue(CurrentFnBegin, PointerSize); + } +} + uint16_t AsmPrinter::getDwarfVersion() const { return OutStreamer->getContext().getDwarfVersion(); } diff --git a/llvm/lib/CodeGen/PatchableFunction.cpp b/llvm/lib/CodeGen/PatchableFunction.cpp --- a/llvm/lib/CodeGen/PatchableFunction.cpp +++ b/llvm/lib/CodeGen/PatchableFunction.cpp @@ -55,6 +55,15 @@ } bool PatchableFunction::runOnMachineFunction(MachineFunction &MF) { + if (MF.getFunction().hasFnAttribute("patchable-function-entry")) { + MachineBasicBlock &FirstMBB = *MF.begin(); + MachineInstr &FirstMI = *FirstMBB.begin(); + const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(), + TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER)); + return true; + } + if (!MF.getFunction().hasFnAttribute("patchable-function")) return false; diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1851,6 +1851,18 @@ if (FP != "all" && FP != "non-leaf" && FP != "none") CheckFailed("invalid value for 'frame-pointer' attribute: " + FP, V); } + + if (Attrs.hasFnAttribute("patchable-function-entry")) { + StringRef S0 = Attrs + .getAttribute(AttributeList::FunctionIndex, + "patchable-function-entry") + .getValueAsString(); + StringRef S = S0; + unsigned N; + if (S.getAsInteger(10, N)) + CheckFailed( + "\"patchable-function-entry\" takes an unsigned integer: " + S0, V); + } } void Verifier::verifyFunctionMetadata( diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -243,6 +243,18 @@ void AArch64AsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI) { + const Function &F = MF->getFunction(); + if (F.hasFnAttribute("patchable-function-entry")) { + unsigned Num; + if (F.getFnAttribute("patchable-function-entry") + .getValueAsString() + .getAsInteger(10, Num)) + return; + for (; Num; --Num) + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::HINT).addImm(0)); + return; + } + EmitSled(MI, SledKind::FUNCTION_ENTER); } diff --git a/llvm/test/CodeGen/AArch64/patchable-function-entry.ll b/llvm/test/CodeGen/AArch64/patchable-function-entry.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/patchable-function-entry.ll @@ -0,0 +1,55 @@ +; RUN: llc -mtriple=aarch64 %s -o - | FileCheck --check-prefixes=CHECK,NOFSECT %s +; RUN: llc -mtriple=aarch64 -function-sections %s -o - | FileCheck --check-prefixes=CHECK,FSECT %s +; RUN: llc -mtriple=aarch64 -no-integrated-as %s -o - | FileCheck --check-prefix=NOLINK %s + +; NOLINK-NOT: "awo" + +define i32 @f0() "patchable-function-entry"="0" { +; CHECK-LABEL: f0: +; CHECK-NEXT: .Lfunc_begin0: +; CHECK-NOT: nop +; CHECK: mov w0, wzr +; CHECK: .section __patchable_function_entries,"awo",@progbits,f0,unique,0 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: .xword .Lfunc_begin0 + ret i32 0 +} + +define i32 @f1() "patchable-function-entry"="1" { +; CHECK-LABEL: f1: +; CHECK-NEXT: .Lfunc_begin1: +; CHECK: nop +; CHECK-NEXT: mov w0, wzr +; NOFSECT: .section __patchable_function_entries,"awo",@progbits,f0,unique,0 +; FSECT: .section __patchable_function_entries,"awo",@progbits,f1,unique,1 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: .xword .Lfunc_begin1 + ret i32 0 +} + +$f3 = comdat any +define void @f3() "patchable-function-entry"="3" comdat { +; CHECK-LABEL: f3: +; CHECK-NEXT: .Lfunc_begin2: +; CHECK-COUNT-3: nop +; CHECK-NEXT: ret +; NOFSECT: .section __patchable_function_entries,"aGwo",@progbits,f3,comdat,f3,unique,1 +; FSECT: .section __patchable_function_entries,"aGwo",@progbits,f3,comdat,f3,unique,2 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: .xword .Lfunc_begin2 + ret void +} + +$f5 = comdat any +define void @f5() "patchable-function-entry"="5" comdat { +; CHECK-LABEL: f5: +; CHECK-NEXT: .Lfunc_begin3: +; CHECK-COUNT-5: nop +; CHECK-NEXT: sub sp, sp, #16 +; NOFSECT .section __patchable_function_entries,"aGwo",@progbits,f5,comdat,f5,unique,2 +; FSECT: .section __patchable_function_entries,"aGwo",@progbits,f5,comdat,f5,unique,3 +; CHECK: .p2align 3 +; CHECK-NEXT: .xword .Lfunc_begin3 + %frame = alloca i8, i32 16 + ret void +} diff --git a/llvm/test/Verifier/invalid-patchable-function-entry.ll b/llvm/test/Verifier/invalid-patchable-function-entry.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/invalid-patchable-function-entry.ll @@ -0,0 +1,11 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +; CHECK: "patchable-function-entry" takes an unsigned integer: +; CHECK: "patchable-function-entry" takes an unsigned integer: a +; CHECK: "patchable-function-entry" takes an unsigned integer: -1 +; CHECK: "patchable-function-entry" takes an unsigned integer: 3, + +define void @f() "patchable-function-entry" { ret void } +define void @fa() "patchable-function-entry"="a" { ret void } +define void @f_1() "patchable-function-entry"="-1" { ret void } +define void @f3comma() "patchable-function-entry"="3," { ret void }