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(); + std::vector PatchableFunctionEntries; + void recordPatchableFunctionEntry(); + //===------------------------------------------------------------------===// // 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 @@ -1432,20 +1432,27 @@ if (TM.getTargetTriple().isOSBinFormatELF()) { MachineModuleInfoELF &MMIELF = MMI->getObjFileInfo(); + const unsigned PointerSize = M.getDataLayout().getPointerSize(0); // Output stubs for external and common global variables. MachineModuleInfoELF::SymbolListTy Stubs = MMIELF.GetGVStubList(); if (!Stubs.empty()) { OutStreamer->SwitchSection(TLOF.getDataSection()); - const DataLayout &DL = M.getDataLayout(); - - EmitAlignment(Align(DL.getPointerSize())); + EmitAlignment(Align(PointerSize)); for (const auto &Stub : Stubs) { OutStreamer->EmitLabel(Stub.first); - OutStreamer->EmitSymbolValue(Stub.second.getPointer(), - DL.getPointerSize()); + OutStreamer->EmitSymbolValue(Stub.second.getPointer(), PointerSize); } } + + if (!PatchableFunctionEntries.empty()) { + OutStreamer->SwitchSection(OutContext.getELFSection( + "__patchable_function_entries", ELF::SHT_PROGBITS, + ELF::SHF_WRITE | ELF::SHF_ALLOC)); + EmitAlignment(Align(PointerSize)); + for (const MCSymbol *Sym : PatchableFunctionEntries) + OutStreamer->EmitSymbolValue(Sym, PointerSize); + } } if (TM.getTargetTriple().isOSBinFormatCOFF()) { @@ -3194,6 +3201,10 @@ AlwaysInstrument, &F, Version}); } +void AsmPrinter::recordPatchableFunctionEntry() { + PatchableFunctionEntries.push_back(CurrentFnSym); +} + 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,19 @@ void AArch64AsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI) { + const Function &F = MF->getFunction(); + if (F.hasFnAttribute("patchable-function-entry")) { + recordPatchableFunctionEntry(); + 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,29 @@ +; RUN: llc -mtriple=aarch64 %s -o - | FileCheck %s + +define i32 @f0() "patchable-function-entry"="0" { +; CHECK-LABEL: f0: +; CHECK-NOT: nop +; CHECK: mov w0, wzr + ret i32 0 +} + +define i32 @f1() "patchable-function-entry"="1" { +; CHECK-LABEL: f1: +; CHECK: nop +; CHECK-NEXT: mov w0, wzr + ret i32 0 +} + +define void @f3() "patchable-function-entry"="3" { +; CHECK-LABEL: f3: +; CHECK-COUNT-3: nop +; CHECK-NEXT: sub sp, sp, #16 + %frame = alloca i8, i32 16 + ret void +} + +; CHECK: .section __patchable_function_entries,"aw",@progbits +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: .xword f0 +; CHECK-NEXT: .xword f1 +; CHECK-NEXT: .xword f3 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 }