Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1447,7 +1447,7 @@ generated for this function needs to follow certain conventions that make it possible for a runtime function to patch over it later. The exact effect of this attribute depends on its string value, - for which there currently is one legal possibility: + for which there currently are two legal possiblities: * ``"prologue-short-redirect"`` - This style of patchable function is intended to support patching a function prologue to @@ -1463,6 +1463,24 @@ ``"prologue-short-redirect"`` is currently only supported on x86-64. + * ``"ms-hotpatch"`` - This style of patchable function is similar to + ``"prologue-short-redirect"``, but it also imposes several additional + guarantees to support the style of hotpatching used on Windows. On + 32-bit x86, the first instruction will be a ``mov %edi, %edi`` + instruction; this is frequently used as a magic value indicating a + hotpatchable function. On other architectures, however, the first + instruction can be anything allowed in a Windows-style prologue; + this is because all functions on the non-i386 architectures Windows + supports are assumed to be hotpatchable. Additionally, when not + targeting a Visual C++-style toolchain, patch space will be provided + prior to the function's entry point of an architecturally specific + size. These sizes are compatible with GCC: on 32-bit x86, the patch + space is 64 bytes long; on x86-64, it is 128 bytes long. The patch + space is not provided for MSVC toolchains because the + `/FUNCTIONPADMIN `_ + option, which provides this space, is expected to be used there. + + ``"ms-hotpatch"`` is currently only supported on x86 and x86-64. This attribute by itself does not imply restrictions on inter-procedural optimizations. All of the semantic effects the Index: include/llvm/Target/TargetInstrInfo.h =================================================================== --- include/llvm/Target/TargetInstrInfo.h +++ include/llvm/Target/TargetInstrInfo.h @@ -1481,6 +1481,19 @@ return None; } + /// Emit a patchable operation in the given basic block at the given + /// insertion point. + /// + /// Most of the time, this will be a straight-up \c TargetOpcode::PATCHABLE_OP + /// instruction, which will be lowered by the target to a no-op that can + /// be safely replaced with a short jump. However, some targets under certain + /// conditions can have peculiar requirements for this instruction; these + /// targets can provide their own implementation of this to emit the correct + /// instruction. + virtual void + emitPatchableOp(StringRef PatchType, MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI) const; + private: unsigned CallFrameSetupOpcode, CallFrameDestroyOpcode; unsigned CatchRetOpcode; Index: lib/CodeGen/PatchableFunction.cpp =================================================================== --- lib/CodeGen/PatchableFunction.cpp +++ lib/CodeGen/PatchableFunction.cpp @@ -15,7 +15,6 @@ #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/Target/TargetFrameLowering.h" #include "llvm/Target/TargetInstrInfo.h" #include "llvm/Target/TargetSubtargetInfo.h" @@ -30,7 +29,7 @@ } bool runOnMachineFunction(MachineFunction &F) override; - MachineFunctionProperties getRequiredProperties() const override { + MachineFunctionProperties getRequiredProperties() const override { return MachineFunctionProperties().set( MachineFunctionProperties::Property::AllVRegsAllocated); } @@ -57,11 +56,10 @@ if (!MF.getFunction()->hasFnAttribute("patchable-function")) return false; -#ifndef NDEBUG Attribute PatchAttr = MF.getFunction()->getFnAttribute("patchable-function"); StringRef PatchType = PatchAttr.getValueAsString(); - assert(PatchType == "prologue-short-redirect" && "Only possibility today!"); -#endif + assert((PatchType == "prologue-short-redirect" || + PatchType == "ms-hotpatch") && "Only possibilities today!"); auto &FirstMBB = *MF.begin(); MachineBasicBlock::iterator FirstActualI = FirstMBB.begin(); @@ -69,15 +67,8 @@ assert(FirstActualI != FirstMBB.end()); auto *TII = MF.getSubtarget().getInstrInfo(); - auto MIB = BuildMI(FirstMBB, FirstActualI, FirstActualI->getDebugLoc(), - TII->get(TargetOpcode::PATCHABLE_OP)) - .addImm(2) - .addImm(FirstActualI->getOpcode()); + TII->emitPatchableOp(PatchType, FirstMBB, FirstActualI); - for (auto &MO : FirstActualI->operands()) - MIB.addOperand(MO); - - FirstActualI->eraseFromParent(); MF.ensureAlignment(4); return true; } Index: lib/CodeGen/TargetInstrInfo.cpp =================================================================== --- lib/CodeGen/TargetInstrInfo.cpp +++ lib/CodeGen/TargetInstrInfo.cpp @@ -1193,3 +1193,16 @@ InsertedReg.SubIdx = (unsigned)MOSubIdx.getImm(); return true; } + +void TargetInstrInfo::emitPatchableOp(StringRef, MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI) const { + auto MIB = BuildMI(MBB, MBBI, MBBI->getDebugLoc(), + get(TargetOpcode::PATCHABLE_OP)) + .addImm(2) + .addImm(MBBI->getOpcode()); + + for (auto &MO : MBBI->operands()) + MIB.addOperand(MO); + + MBBI->eraseFromParent(); +} Index: lib/Target/X86/X86AsmPrinter.h =================================================================== --- lib/Target/X86/X86AsmPrinter.h +++ lib/Target/X86/X86AsmPrinter.h @@ -140,6 +140,8 @@ SMShadowTracker.emitShadowPadding(*OutStreamer, getSubtargetInfo()); } + void EmitConstantPool() override; + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, unsigned AsmVariant, const char *ExtraCode, raw_ostream &OS) override; Index: lib/Target/X86/X86AsmPrinter.cpp =================================================================== --- lib/Target/X86/X86AsmPrinter.cpp +++ lib/Target/X86/X86AsmPrinter.cpp @@ -76,6 +76,31 @@ return false; } +void X86AsmPrinter::EmitConstantPool() { + if (MF) { + // If an MS hotpatch function, we need to ensure 64 (32-bit) or 128 (64-bit) + // bytes of padding precede the label. This is the scratch space used + // by the hotpatching mechanism to insert the patch code. The movl %edi, + // %edi instruction emitted as the very first instruction of a hotpatch + // function is usually overwritten with a short jump instruction when the + // patch is installed, so it will jump directly into this space. (But + // don't add the space when targeting MSVC. There, the /FUNCTIONPADMIN + // option to link.exe is expected to be used.) + const Function *Fn = MF->getFunction(); + if (!Subtarget->isTargetKnownWindowsMSVC() && + Fn->hasFnAttribute("patchable-function") && + Fn->getFnAttribute("patchable-function").getValueAsString() == + "ms-hotpatch") { + // Emit INT3 instructions instead of NOPs. If a patch runs off the end, + // best to let the patcher know with a crash/debug break than to silently + // continue, only to run into the jump back into the patch. + OutStreamer->emitFill(Subtarget->is64Bit() ? 128 : 64, 0xcc); + } + } + + AsmPrinter::EmitConstantPool(); +} + /// printSymbolOperand - Print a raw symbol reference operand. This handles /// jump tables, constant pools, global address and external symbols, all of /// which print to a label with various suffixes for relocation types etc. Index: lib/Target/X86/X86FrameLowering.cpp =================================================================== --- lib/Target/X86/X86FrameLowering.cpp +++ lib/Target/X86/X86FrameLowering.cpp @@ -930,6 +930,10 @@ bool NeedsWinCFI = IsWin64Prologue && Fn->needsUnwindTableEntry(); bool NeedsDwarfCFI = !IsWin64Prologue && (MMI.hasDebugInfo() || Fn->needsUnwindTableEntry()); + bool IsMSHotpatch = + Fn->hasFnAttribute("patchable-function") && + Fn->getFnAttribute("patchable-function").getValueAsString() == + "ms-hotpatch"; unsigned FramePtr = TRI->getFrameRegister(MF); const unsigned MachineFramePtr = STI.isTarget64BitILP32() @@ -1071,7 +1075,9 @@ if (!IsWin64Prologue && !IsFunclet) { // Update EBP with the new base value. BuildMI(MBB, MBBI, DL, - TII.get(Uses64BitFramePtr ? X86::MOV64rr : X86::MOV32rr), + TII.get(IsMSHotpatch ? + (Uses64BitFramePtr ? X86::MOV64rr_REV : X86::MOV32rr_REV): + (Uses64BitFramePtr ? X86::MOV64rr : X86::MOV32rr)), FramePtr) .addReg(StackPtr) .setMIFlag(MachineInstr::FrameSetup); Index: lib/Target/X86/X86ISelLowering.cpp =================================================================== --- lib/Target/X86/X86ISelLowering.cpp +++ lib/Target/X86/X86ISelLowering.cpp @@ -2574,10 +2574,13 @@ X86MachineFunctionInfo *FuncInfo = MF.getInfo(); const TargetFrameLowering &TFI = *Subtarget.getFrameLowering(); - const Function *Fn = MF.getFunction(); - if (Fn->hasExternalLinkage() && - Subtarget.isTargetCygMing() && - Fn->getName() == "main") + const Function* Fn = MF.getFunction(); + if ((Fn->hasExternalLinkage() && + Subtarget.isTargetCygMing() && + Fn->getName() == "main") || + (!Subtarget.is64Bit() && Fn->hasFnAttribute("patchable-function") && + Fn->getFnAttribute("patchable-function").getValueAsString() == + "ms-hotpatch")) FuncInfo->setForceFramePointer(true); MachineFrameInfo &MFI = MF.getFrameInfo(); Index: lib/Target/X86/X86InstrInfo.h =================================================================== --- lib/Target/X86/X86InstrInfo.h +++ lib/Target/X86/X86InstrInfo.h @@ -541,6 +541,10 @@ ArrayRef> getSerializableDirectMachineOperandTargetFlags() const override; + void + emitPatchableOp(StringRef PatchType, MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI) const override; + protected: /// Commutes the operands in the given instruction by changing the operands /// order and/or changing the instruction's opcode and/or the immediate value Index: lib/Target/X86/X86InstrInfo.cpp =================================================================== --- lib/Target/X86/X86InstrInfo.cpp +++ lib/Target/X86/X86InstrInfo.cpp @@ -7901,6 +7901,19 @@ return makeArrayRef(TargetFlags); } +void X86InstrInfo::emitPatchableOp(StringRef PatchType, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI) const { + if (PatchType != "ms-hotpatch" || !Subtarget.is32Bit()) { + TargetInstrInfo::emitPatchableOp(PatchType, MBB, MBBI); + return; + } + + BuildMI(MBB, MBBI, MBBI->getDebugLoc(), get(X86::MOV32rr_REV), X86::EDI) + .addReg(X86::EDI) + .setMIFlag(MachineInstr::FrameSetup); +} + namespace { /// Create Global Base Reg pass. This initializes the PIC /// global base register for x86-32. Index: test/CodeGen/X86/ms-hotpatch-attr.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/ms-hotpatch-attr.ll @@ -0,0 +1,27 @@ +; RUN: llc < %s -march=x86 -filetype=asm | FileCheck -check-prefix=CHECK-32 %s +; RUN: llc < %s -march=x86-64 -filetype=asm | FileCheck -check-prefix=CHECK-64 %s +; RUN: llc < %s -mtriple=i386-windows-msvc -filetype=asm | FileCheck -check-prefix=MSVC-32 %s +; RUN: llc < %s -mtriple=x86_64-windows-msvc -filetype=asm | FileCheck -check-prefix=MSVC-64 %s + +; CHECK-32: .space 64,204 +; CHECK-32: .p2align 4, 0x90 +; CHECK-32-LABEL: foo: +; CHECK-32: movl %edi, %edi +; CHECK-32-NEXT: pushl %ebp +; CHECK-32-NEXT: movl %esp, %ebp +; CHECK-64: .space 128,204 +; CHECK-64: .p2align 4, 0x90 +; CHECK-64-LABEL: foo: +; CHECK-64: xchgw %ax, %ax +; MSVC-32-NOT: .space 64,204 +; MSVC-32-LABEL: _foo: +; MSVC-32: movl %edi, %edi +; MSVC-32-NEXT: pushl %ebp +; MSVC-32-NEXT: movl %esp, %ebp +; MSVC-64-NOT: .space 128,204 +; MSVC-64-LABEL: foo: +; MSVC-64: xchgw %ax, %ax +define void @foo() nounwind "patchable-function"="ms-hotpatch" { +entry: + ret void +}