Skip to content

Commit 5af2433

Browse files
committed
[clang-cl] Support the /HOTPATCH flag
This patch adds support for the MSVC /HOTPATCH flag: https://docs.microsoft.com/sv-se/cpp/build/reference/hotpatch-create-hotpatchable-image?view=msvc-170&viewFallbackFrom=vs-2019 The flag is translated to a new -fms-hotpatch flag, which in turn adds a 'patchable-function' attribute for each function in the TU. This is then picked up by the PatchableFunction pass which would generate a TargetOpcode::PATCHABLE_OP of minsize = 2 (which means the target instruction must resolve to at least two bytes). TargetOpcode::PATCHABLE_OP is only implemented for x86/x64. When targetting ARM/ARM64, /HOTPATCH isn't required (instructions are always 2/4 bytes and suitable for hotpatching). Additionally, when using /Z7, we generate a 'hot patchable' flag in the CodeView debug stream, in the S_COMPILE3 record. This flag is then picked up by LLD (or link.exe) and is used in conjunction with the linker /FUNCTIONPADMIN flag to generate extra space before each function, to accommodate for live patching long jumps. Please see: https://github.com/llvm/llvm-project/blob/d703b922961e0d02a5effdd4bfbb23ad50a3cc9f/lld/COFF/Writer.cpp#L1298 The outcome is that we can finally use Live++ or Recode along with clang-cl. NOTE: It seems that MSVC cl.exe always enables /HOTPATCH on x64 by default, although if we did the same I thought we might generate sub-optimal code (if this flag was active by default). Additionally, MSVC always generates a .debug$S section and a S_COMPILE3 record, which Clang doesn't do without /Z7. Therefore, the following MSVC command-line "cl /c file.cpp" would have to be written with Clang such as "clang-cl /c file.cpp /HOTPATCH /Z7" in order to obtain the same result. Depends on D43002, D80833 and D81301 for the full feature. Differential Revision: https://reviews.llvm.org/D116511
1 parent 237502c commit 5af2433

File tree

15 files changed

+103
-7
lines changed

15 files changed

+103
-7
lines changed

clang/docs/ReleaseNotes.rst

+10
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,16 @@ Windows Support
136136
or pass ``/permissive`` to disable C++ operator names altogether. See
137137
`PR42427 <https://llvm.org/pr42427>` for more info.
138138

139+
- Add support for MSVC-compatible ``/hotpatch`` flag in clang-cl, and equivalent
140+
-cc1 flag ``-fms-hotpatch``. Along with the linker flag ``/functionpadmin``
141+
this creates executable images suitable for runtime code patching. This flag
142+
is only required for x86/x64 targets; ARM/ARM64 simply needs the linker
143+
``/functionpadmin``.
144+
145+
With this addition, clang-cl can be used in live code patching scenarios,
146+
along with tools such as Live++ or Recode. Microsoft Edit and Continue isn't
147+
currently supported.
148+
139149
C Language Changes in Clang
140150
---------------------------
141151

clang/include/clang/Basic/CodeGenOptions.def

+3
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ VALUE_CODEGENOPT(XRaySelectedFunctionGroup, 32, 0)
139139
VALUE_CODEGENOPT(PatchableFunctionEntryCount , 32, 0) ///< Number of NOPs at function entry
140140
VALUE_CODEGENOPT(PatchableFunctionEntryOffset , 32, 0)
141141

142+
CODEGENOPT(HotPatch, 1, 0) ///< Supports the Microsoft /HOTPATCH flag and
143+
///< generates a 'patchable-function' attribute.
144+
142145
CODEGENOPT(InstrumentForProfiling , 1, 0) ///< Set when -pg is enabled.
143146
CODEGENOPT(CallFEntry , 1, 0) ///< Set when -mfentry is enabled.
144147
CODEGENOPT(MNopMCount , 1, 0) ///< Set when -mnop-mcount is enabled.

clang/include/clang/Driver/Options.td

+5-1
Original file line numberDiff line numberDiff line change
@@ -2498,6 +2498,9 @@ defm pascal_strings : BoolFOption<"pascal-strings",
24982498
def fpatchable_function_entry_EQ : Joined<["-"], "fpatchable-function-entry=">, Group<f_Group>, Flags<[CC1Option]>,
24992499
MetaVarName<"<N,M>">, HelpText<"Generate M NOPs before function entry and N-M NOPs after function entry">,
25002500
MarshallingInfoInt<CodeGenOpts<"PatchableFunctionEntryCount">>;
2501+
def fms_hotpatch : Flag<["-"], "fms-hotpatch">, Group<f_Group>, Flags<[CC1Option, CoreOption]>,
2502+
HelpText<"Ensure that all functions can be hotpatched at runtime">,
2503+
MarshallingInfoFlag<CodeGenOpts<"HotPatch">>;
25012504
def fpcc_struct_return : Flag<["-"], "fpcc-struct-return">, Group<f_Group>, Flags<[CC1Option]>,
25022505
HelpText<"Override the default ABI to return all structs on the stack">;
25032506
def fpch_preprocess : Flag<["-"], "fpch-preprocess">, Group<f_Group>;
@@ -6124,6 +6127,8 @@ def _SLASH_Gw_ : CLFlag<"Gw-">,
61246127
def _SLASH_help : CLFlag<"help">, Alias<help>,
61256128
HelpText<"Display available options">;
61266129
def _SLASH_HELP : CLFlag<"HELP">, Alias<help>;
6130+
def _SLASH_hotpatch : CLFlag<"hotpatch">, Alias<fms_hotpatch>,
6131+
HelpText<"Create hotpatchable image">;
61276132
def _SLASH_I : CLJoinedOrSeparate<"I">,
61286133
HelpText<"Add directory to include search path">, MetaVarName<"<dir>">,
61296134
Alias<I>;
@@ -6480,7 +6485,6 @@ def _SLASH_headerUnit : CLJoinedOrSeparate<"headerUnit">;
64806485
def _SLASH_headerUnitAngle : CLJoinedOrSeparate<"headerUnit:angle">;
64816486
def _SLASH_headerUnitQuote : CLJoinedOrSeparate<"headerUnit:quote">;
64826487
def _SLASH_homeparams : CLFlag<"homeparams">;
6483-
def _SLASH_hotpatch : CLFlag<"hotpatch">;
64846488
def _SLASH_kernel : CLFlag<"kernel">;
64856489
def _SLASH_LN : CLFlag<"LN">;
64866490
def _SLASH_MP : CLJoined<"MP">;

clang/lib/CodeGen/BackendUtil.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ static bool initTargetOptions(DiagnosticsEngine &Diags,
645645
Options.MCOptions.CommandLineArgs = CodeGenOpts.CommandLineArgs;
646646
Options.DebugStrictDwarf = CodeGenOpts.DebugStrictDwarf;
647647
Options.ObjectFilenameForDebug = CodeGenOpts.ObjectFilenameForDebug;
648+
Options.Hotpatch = CodeGenOpts.HotPatch;
648649

649650
return true;
650651
}

clang/lib/CodeGen/CodeGenFunction.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
882882
if (Offset)
883883
Fn->addFnAttr("patchable-function-prefix", std::to_string(Offset));
884884
}
885+
// Instruct that functions for COFF/CodeView targets should start with a
886+
// patchable instruction, but only on x86/x64. Don't forward this to ARM/ARM64
887+
// backends as they don't need it -- instructions on these architectures are
888+
// always atomically patchable at runtime.
889+
if (CGM.getCodeGenOpts().HotPatch &&
890+
getContext().getTargetInfo().getTriple().isX86())
891+
Fn->addFnAttr("patchable-function", "prologue-short-redirect");
885892

886893
// Add no-jump-tables value.
887894
if (CGM.getCodeGenOpts().NoUseJumpTables)

clang/lib/Driver/ToolChains/Clang.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -6001,6 +6001,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
60016001
}
60026002
}
60036003

6004+
Args.AddLastArg(CmdArgs, options::OPT_fms_hotpatch);
6005+
60046006
if (TC.SupportsProfiling()) {
60056007
Args.AddLastArg(CmdArgs, options::OPT_pg);
60066008

clang/lib/Driver/ToolChains/MSVC.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,11 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
511511
if (Args.hasArg(options::OPT_g_Group, options::OPT__SLASH_Z7))
512512
CmdArgs.push_back("-debug");
513513

514+
// If we specify /hotpatch, let the linker add padding in front of each
515+
// function, like MSVC does.
516+
if (Args.hasArg(options::OPT_fms_hotpatch, options::OPT__SLASH_hotpatch))
517+
CmdArgs.push_back("-functionpadmin");
518+
514519
// Pass on /Brepro if it was passed to the compiler.
515520
// Note that /Brepro maps to -mno-incremental-linker-compatible.
516521
bool DefaultIncrementalLinkerCompatible =

clang/test/CodeGen/patchable-function-entry.c

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// RUN: %clang_cc1 -triple aarch64 -emit-llvm %s -o - | FileCheck %s
22
// RUN: %clang_cc1 -triple x86_64 -emit-llvm %s -fpatchable-function-entry=1 -o - | FileCheck --check-prefixes=CHECK,OPT %s
3+
// RUN: %clang_cc1 -triple x86_64 -emit-llvm %s -fms-hotpatch -o - | FileCheck --check-prefixes=HOTPATCH %s
34

45
// CHECK: define{{.*}} void @f0() #0
56
__attribute__((patchable_function_entry(0))) void f0() {}
@@ -34,3 +35,7 @@ void f() {}
3435
// CHECK: attributes #2 = { {{.*}} "patchable-function-entry"="0" "patchable-function-prefix"="4"
3536
// CHECK: attributes #3 = { {{.*}} "patchable-function-entry"="3" "patchable-function-prefix"="2"
3637
// OPT: attributes #4 = { {{.*}} "patchable-function-entry"="1"
38+
// HOTPATCH: attributes #0 = { {{.*}} "patchable-function"="prologue-short-redirect"
39+
// HOTPATCH: attributes #1 = { {{.*}} "patchable-function"="prologue-short-redirect"
40+
// HOTPATCH: attributes #2 = { {{.*}} "patchable-function"="prologue-short-redirect"
41+
// HOTPATCH: attributes #3 = { {{.*}} "patchable-function"="prologue-short-redirect"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// REQUIRES: aarch64-registered-target || arm-registered-target
2+
///
3+
/// Check that using /hotpatch doesn't generate an error.
4+
/// Binaries are always hotpatchable on ARM/ARM64.
5+
///
6+
// RUN: %clang_cl --target=arm-pc-windows-msvc /c /hotpatch /Z7 -- %s 2>&1
7+
// RUN: %clang_cl --target=aarch64-pc-windows-msvc /c /hotpatch /Z7 -- %s 2>&1
8+
///
9+
/// Ensure that we set the hotpatchable flag in the debug information.
10+
///
11+
// RUN: %clang_cl --target=arm-pc-windows-msvc /c /Z7 -o %t.obj -- %s
12+
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=HOTPATCH
13+
// RUN: %clang_cl --target=aarch64-pc-windows-msvc /c /Z7 -o %t.obj -- %s
14+
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=HOTPATCH
15+
// HOTPATCH: S_COMPILE3 [size = [[#]]]
16+
// HOTPATCH: flags = hot patchable
17+
///
18+
/// Unfortunately we need /Z7, Clang does not systematically generate S_COMPILE3.
19+
///
20+
// RUN: %clang_cl --target=aarch64-pc-windows-msvc /c -o %t.obj -- %s
21+
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=NO-HOTPATCH
22+
// NO-HOTPATCH-NOT: flags = hot patchable
23+
24+
int main() {
25+
return 0;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// REQUIRES: x86-registered-target
2+
///
3+
// RUN: %clang_cl --target=x86_64-windows-msvc /c /hotpatch /Z7 -o %t.obj -- %s
4+
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=HOTPATCH
5+
// HOTPATCH: S_COMPILE3 [size = [[#]]]
6+
// HOTPATCH: flags = hot patchable
7+
///
8+
// RUN: %clang_cl --target=x86_64-windows-msvc /c /Z7 -o %t.obj -- %s
9+
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=NO-HOTPATCH
10+
// NO-HOTPATCH-NOT: flags = hot patchable
11+
///
12+
// RUN: %clang_cl --target=x86_64-windows-msvc /hotpatch -### -- %s 2>&1 \
13+
// RUN: | FileCheck %s --check-prefix=FUNCTIONPADMIN
14+
// FUNCTIONPADMIN: clang{{.*}}
15+
// FUNCTIONPADMIN: {{link.*"}}
16+
// FUNCTIONPADMIN: -functionpadmin
17+
18+
int main() {
19+
return 0;
20+
}

clang/test/Driver/cl-options.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@
118118
// RUN: %clang_cl /Gw /Gw- -### -- %s 2>&1 | FileCheck -check-prefix=Gw_ %s
119119
// Gw_-NOT: -fdata-sections
120120

121+
// RUN: %clang_cl /hotpatch -### -- %s 2>&1 | FileCheck -check-prefix=hotpatch %s
122+
// hotpatch: -fms-hotpatch
123+
121124
// RUN: %clang_cl /Imyincludedir -### -- %s 2>&1 | FileCheck -check-prefix=SLASH_I %s
122125
// RUN: %clang_cl /I myincludedir -### -- %s 2>&1 | FileCheck -check-prefix=SLASH_I %s
123126
// SLASH_I: "-I" "myincludedir"
@@ -483,7 +486,6 @@
483486
// RUN: /GZ \
484487
// RUN: /H \
485488
// RUN: /homeparams \
486-
// RUN: /hotpatch \
487489
// RUN: /JMC \
488490
// RUN: /kernel \
489491
// RUN: /LN \

llvm/include/llvm/Target/TargetOptions.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ namespace llvm {
140140
EnableMachineFunctionSplitter(false), SupportsDefaultOutlining(false),
141141
EmitAddrsig(false), EmitCallSiteInfo(false),
142142
SupportsDebugEntryValues(false), EnableDebugEntryValues(false),
143-
ValueTrackingVariableLocations(false),
144-
ForceDwarfFrameSection(false), XRayOmitFunctionIndex(false),
145-
DebugStrictDwarf(false),
143+
ValueTrackingVariableLocations(false), ForceDwarfFrameSection(false),
144+
XRayOmitFunctionIndex(false), DebugStrictDwarf(false),
145+
Hotpatch(false),
146146
FPDenormalMode(DenormalMode::IEEE, DenormalMode::IEEE) {}
147147

148148
/// DisableFramePointerElim - This returns true if frame pointer elimination
@@ -342,6 +342,9 @@ namespace llvm {
342342
/// By default, it is set to false.
343343
unsigned DebugStrictDwarf : 1;
344344

345+
/// Emit the hotpatch flag in CodeView debug.
346+
unsigned Hotpatch : 1;
347+
345348
/// Name of the stack usage file (i.e., .su file) if user passes
346349
/// -fstack-usage. If empty, it can be implied that -fstack-usage is not
347350
/// passed on the command line.

llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,12 @@ void CodeViewDebug::emitCompilerInformation() {
846846
if (MMI->getModule()->getProfileSummary(/*IsCS*/ false) != nullptr) {
847847
Flags |= static_cast<uint32_t>(CompileSym3Flags::PGO);
848848
}
849+
using ArchType = llvm::Triple::ArchType;
850+
ArchType Arch = Triple(MMI->getModule()->getTargetTriple()).getArch();
851+
if (Asm->TM.Options.Hotpatch || Arch == ArchType::thumb ||
852+
Arch == ArchType::aarch64) {
853+
Flags |= static_cast<uint32_t>(CompileSym3Flags::HotPatch);
854+
}
849855

850856
OS.AddComment("Flags and language");
851857
OS.emitInt32(Flags);

llvm/test/DebugInfo/COFF/ARMNT/arm-register-variables.ll

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
; OBJ: Compile3Sym {
2525
; OBJ-NEXT: Kind: S_COMPILE3 (0x113C)
2626
; OBJ-NEXT: Language: C (0x0)
27-
; OBJ-NEXT: Flags [ (0x0)
27+
; OBJ-NEXT: Flags [ (0x4000)
28+
; OBJ-NEXT: HotPatch (0x4000)
2829
; OBJ-NEXT: ]
2930
; OBJ-NEXT: Machine: ARMNT (0xF4)
3031

llvm/test/MC/AArch64/coff-debug.ll

+2-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-ma
7777
; CHECK: Compile3Sym {
7878
; CHECK: Kind: S_COMPILE3 (0x113C)
7979
; CHECK: Language: C (0x0)
80-
; CHECK: Flags [ (0x0)
80+
; CHECK: Flags [ (0x4000
81+
; CHECK: HotPatch (0x4000)
8182
; CHECK: ]
8283
; CHECK: }
8384
; CHECK: ]

0 commit comments

Comments
 (0)