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(); + unsigned PatchableFunctionEntryID = 0; + void emitPatchableFunctionEntry(); + //===------------------------------------------------------------------===// // 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 @@ -1438,7 +1438,6 @@ if (!Stubs.empty()) { OutStreamer->SwitchSection(TLOF.getDataSection()); const DataLayout &DL = M.getDataLayout(); - EmitAlignment(Align(DL.getPointerSize())); for (const auto &Stub : Stubs) { OutStreamer->EmitLabel(Stub.first); @@ -1660,6 +1659,7 @@ void AsmPrinter::SetupMachineFunction(MachineFunction &MF) { this->MF = &MF; + const Function &F = MF.getFunction(); // Get the function symbol. if (MAI->needsFunctionDescriptors()) { @@ -1672,7 +1672,6 @@ CurrentFnSym = OutContext.getOrCreateSymbol("." + CurrentFnDescSym->getName()); - const Function &F = MF.getFunction(); MCSectionXCOFF *FnEntryPointSec = cast(getObjFileLowering().SectionForGlobal(&F, TM)); // Set the containing csect. @@ -1685,7 +1684,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 +3194,32 @@ AlwaysInstrument, &F, Version}); } +void AsmPrinter::emitPatchableFunctionEntry() { + const Function &F = MF->getFunction(); + if (!F.hasFnAttribute("patchable-function-entry")) + return; + const unsigned PointerSize = getPointerSize(); + if (TM.getTargetTriple().isOSBinFormatELF()) { + auto Flags = ELF::SHF_ALLOC | ELF::SHF_LINK_ORDER; + std::string GroupName; + if (F.hasComdat()) { + Flags |= ELF::SHF_GROUP; + GroupName = F.getComdat()->getName(); + } + OutStreamer->SwitchSection(OutContext.getELFSection( + "__patchable_function_entry", ELF::SHT_PROGBITS, Flags, 0, GroupName, + ++PatchableFunctionEntryID, cast(CurrentFnSym))); + EmitAlignment(Align(PointerSize)); + MCSymbol *Dot = OutContext.createTempSymbol(); + OutStreamer->EmitLabel(Dot); + OutStreamer->EmitValueImpl( + MCBinaryExpr::createSub( + MCSymbolRefExpr::create(CurrentFnBegin, OutContext), + MCSymbolRefExpr::create(Dot, OutContext), OutContext), + 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 @@ -144,6 +144,8 @@ // Emit the XRay table for this function. emitXRayTable(); + emitPatchableFunctionEntry(); + // We didn't modify anything. return false; } @@ -243,6 +245,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,39 @@ +; RUN: llc -mtriple=aarch64 %s -o - | FileCheck %s + +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_entry,"ao",@progbits,f0,unique,1 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: .Ltmp1: +; CHECK-NEXT: .xword .Lfunc_begin0-.Ltmp1 + ret i32 0 +} + +define i32 @f1() "patchable-function-entry"="1" { +; CHECK-LABEL: f1: +; CHECK-NEXT: .Lfunc_begin1: +; CHECK: nop +; CHECK-NEXT: mov w0, wzr +; CHECK: .section __patchable_function_entry,"ao",@progbits,f1,unique,2 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: .Ltmp2: +; CHECK-NEXT: .xword .Lfunc_begin1-.Ltmp2 + 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: sub sp, sp, #16 +; CHECK: .section __patchable_function_entry,"aGo",@progbits,f3,comdat,f3,unique,3 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: .Ltmp3: +; CHECK-NEXT: .xword .Lfunc_begin2-.Ltmp3 + %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 }