Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1405,6 +1405,30 @@ passes make choices that keep the code size of this function low, and otherwise do optimizations specifically to reduce code size as long as they do not significantly impact runtime performance. +``"patchable-prologue"`` + This attribute tells the code generator that the prologue + generated for this function needs to follow a specific format that + makes it possible for a runtime function to patch over it later. + The exact effect of this attribute depends on the string value of + this attribute, for which there currently is one legal value: + + * ``"hotpatch-compact"`` - This style of patchable prologues is + intended to support patching a function prologue to redirect + control away from the function in a thread safe manner. It + guarantees that the first instruction of the function will be + large enough to accommodate a short jump instruction, and will + be sufficiently aligned to allow being fully changed via an + atomic compare-and-swap instruction. While the first + requirement can be satisfied by inserting large enough NOP, + LLVM can and will try to re-purpose an existing instruction + (i.e. one that would have to be emitted anyway) as the + patchable instruction larger than a short jump. + + ``"hotpatch-compact"`` is currently only supported on x86-64. + + This attribute by itself does not imply restrictions on + inter-procedural optimizations. All of the semantic effects the + patching may have to be separately conveyed via the linkage type. ``readnone`` On a function, this attribute indicates that the function computes its result (or decides to unwind an exception) based strictly on its arguments, Index: include/llvm/CodeGen/Passes.h =================================================================== --- include/llvm/CodeGen/Passes.h +++ include/llvm/CodeGen/Passes.h @@ -599,6 +599,9 @@ /// \brief This pass lays out funclets contiguously. extern char &FuncletLayoutID; + /// \brief This pass implements the "patchable-prologue" attribute. + extern char &PatchableProloguesID; + /// createStackProtectorPass - This pass adds stack protectors to functions. /// FunctionPass *createStackProtectorPass(const TargetMachine *TM); Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -329,6 +329,7 @@ void initializeFunctionImportPassPass(PassRegistry &); void initializeLoopVersioningPassPass(PassRegistry &); void initializeWholeProgramDevirtPass(PassRegistry &); +void initializePatchableProloguesPass(PassRegistry &); } #endif Index: include/llvm/Target/TargetLowering.h =================================================================== --- include/llvm/Target/TargetLowering.h +++ include/llvm/Target/TargetLowering.h @@ -1794,6 +1794,19 @@ return LibcallCallingConvs[Call]; } + //===--------------------------------------------------------------------===// + // LLVM support for hotpatching + // + + enum PatchablePrologueFlavor { PPF_HOTPATCH_COMPACT, PPF_UNKNOWN }; + + /// Implement a specific variant of the "patchable-prologue" attribute. + virtual void + makeFunctionProloguePatchable(MachineFunction &MF, + PatchablePrologueFlavor PPF) const { + llvm_unreachable("Not implemented for this target!"); + } + private: const TargetMachine &TM; Index: lib/CodeGen/CMakeLists.txt =================================================================== --- lib/CodeGen/CMakeLists.txt +++ lib/CodeGen/CMakeLists.txt @@ -76,6 +76,7 @@ MachineSSAUpdater.cpp MachineTraceMetrics.cpp MachineVerifier.cpp + PatchablePrologues.cpp MIRPrinter.cpp MIRPrintingPass.cpp OptimizePHIs.cpp Index: lib/CodeGen/CodeGen.cpp =================================================================== --- lib/CodeGen/CodeGen.cpp +++ lib/CodeGen/CodeGen.cpp @@ -55,6 +55,7 @@ initializeMachineSchedulerPass(Registry); initializeMachineSinkingPass(Registry); initializeMachineVerifierPassPass(Registry); + initializePatchableProloguesPass(Registry); initializeOptimizePHIsPass(Registry); initializePEIPass(Registry); initializePHIEliminationPass(Registry); Index: lib/CodeGen/Passes.cpp =================================================================== --- lib/CodeGen/Passes.cpp +++ lib/CodeGen/Passes.cpp @@ -602,6 +602,8 @@ addPass(&StackMapLivenessID, false); addPass(&LiveDebugValuesID, false); + addPass(&PatchableProloguesID, false); + AddingMachinePasses = false; } Index: lib/CodeGen/PatchablePrologues.cpp =================================================================== --- /dev/null +++ lib/CodeGen/PatchablePrologues.cpp @@ -0,0 +1,56 @@ +//===-- PatchablePrologues.cpp - Patchable prologues for LLVM -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements edits function prologues in place to support the +// "patchable-prologue" attribute. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/Analysis.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/Target/TargetLowering.h" +#include "llvm/Target/TargetSubtargetInfo.h" + +using namespace llvm; + +namespace { +struct PatchablePrologues : public MachineFunctionPass { + static char ID; // Pass identification, replacement for typeid + PatchablePrologues() : MachineFunctionPass(ID) { + initializePatchableProloguesPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &F) override; + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::AllVRegsAllocated); + } +}; +} + +bool PatchablePrologues::runOnMachineFunction(MachineFunction &MF) { + if (!MF.getFunction()->hasFnAttribute("patchable-prologue")) + return false; + + Attribute PatchAttr = MF.getFunction()->getFnAttribute("patchable-prologue"); + StringRef PatchType = PatchAttr.getValueAsString(); + + assert(PatchType == "hotpatch-compact" && "Only possibility today!"); + (void)PatchType; + + auto *TLI = MF.getSubtarget().getTargetLowering(); + TLI->makeFunctionProloguePatchable(MF, TargetLowering::PPF_HOTPATCH_COMPACT); + return true; +} + +char PatchablePrologues::ID = 0; +char &llvm::PatchableProloguesID = PatchablePrologues::ID; +INITIALIZE_PASS(PatchablePrologues, "patchable-prologues", "", false, false) Index: lib/Target/X86/X86ISelLowering.h =================================================================== --- lib/Target/X86/X86ISelLowering.h +++ lib/Target/X86/X86ISelLowering.h @@ -1217,6 +1217,12 @@ /// Reassociate floating point divisions into multiply by reciprocal. unsigned combineRepeatedFPDivisors() const override; + + /// Hook to make function prologues patchable, as dictated by the + /// "patchable-prologue" attribute. + void makeFunctionProloguePatchable( + MachineFunction &MF, + TargetLowering::PatchablePrologueFlavor PPF) const override; }; namespace X86 { Index: lib/Target/X86/X86ISelLowering.cpp =================================================================== --- lib/Target/X86/X86ISelLowering.cpp +++ lib/Target/X86/X86ISelLowering.cpp @@ -30598,3 +30598,60 @@ .addReg(NewVR); } } + +void X86TargetLowering::makeFunctionProloguePatchable( + MachineFunction &MF, TargetLowering::PatchablePrologueFlavor PPF) const { + assert(PPF == TargetLowering::PPF_HOTPATCH_COMPACT && + "Only one possibility!"); + assert(Subtarget.is64Bit() && "Unsupported!"); + + auto &FirstMI = *MF.begin()->begin(); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + + // We need to ensure that FirstMI is at least two bytes long (a short jump can + // be encoded in two bytes) and does not span a 16 byte boundary (to be + // patchable by a CAS). + + // Programmatically getting the size of an x86 instruction at the MachineInstr + // level is difficult. So we instead hard-code a list of instructions that + // are commonly expected to appear as the first instruction in MF. + switch (FirstMI.getOpcode()) { + case X86::PUSH64r: + switch (FirstMI.getOperand(0).getReg()) { + case X86::RAX: + case X86::RBX: + case X86::RCX: + case X86::RDX: + case X86::RSP: + case X86::RBP: + case X86::RSI: + case X86::RDI: + // These push instructions are one byte long. "Fatten" them to take up + // two bytes. + FirstMI.setDesc(TII->get(X86::PUSH64rmr)); + break; + + default: + // All other push instructions are at least two bytes long, nothing more + // needs to be done here. + break; + } + break; + + case X86::SUB64ri32: + // We know this instruction takes more than two bytes. + break; + + default: + // We didn't recognize the instruction, so add a two byte nop just to be + // safe. + BuildMI(*MF.begin(), MF.begin()->begin(), DebugLoc(), + TII->get(X86::XCHG16ar)).addReg(X86::AX); + break; + } + + // We cannot let the first instruction cross a 16 byte boundary. Given that + // the largest x86 instruction is 15 bytes long, it is sufficient to align the + // start of the function to 16 bytes. + MF.ensureAlignment(4); +} Index: test/CodeGen/X86/patchable-prologue.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/patchable-prologue.ll @@ -0,0 +1,43 @@ +; RUN: llc -filetype=obj -o - -mtriple=x86_64-apple-macosx < %s | llvm-objdump -triple x86_64-apple-macosx -disassemble - +; RUN: llc -mtriple=x86_64-apple-macosx < %s | FileCheck %s --check-prefix=CHECK-ALIGN + +declare void @callee(i64*) + +define void @f0() "patchable-prologue"="hotpatch-compact" { +; CHECK-LABEL: _f0: +; CHECK-NEXT: 66 90 nop + +; CHECK-ALIGN: .p2align 4, 0x90 +; CHECK-ALIGN: _f0: + + ret void +} + +define void @f1() "patchable-prologue"="hotpatch-compact" "no-frame-pointer-elim"="true" { +; CHECK-LABEL: _f1 +; CHECK-NEXT: ff f5 pushq %rbp + +; CHECK-ALIGN: .p2align 4, 0x90 +; CHECK-ALIGN: _f1: + ret void +} + +define void @f2() "patchable-prologue"="hotpatch-compact" { +; CHECK-LABEL: _f2 +; CHECK-NEXT: 48 81 ec a8 00 00 00 subq $168, %rsp + +; CHECK-ALIGN: .p2align 4, 0x90 +; CHECK-ALIGN: _f2: + %ptr = alloca i64, i32 20 + call void @callee(i64* %ptr) + ret void +} + +define void @f3() "patchable-prologue"="hotpatch-compact" optsize { +; CHECK-LABEL: _f3 +; CHECK-NEXT: 66 90 nop + +; CHECK-ALIGN: .p2align 4, 0x90 +; CHECK-ALIGN: _f3: + ret void +}