Skip to content

Commit 18bcf7a

Browse files
committed
Windows hotpatching support
Address PR feedback * Simply use F.getName() for function name. * Fix llvm/test/CodeGen/Generic/ms-hotpatch-direct-global-access.ll * Add negative testing for functions that are not supposed to be hotpatched
1 parent 038d357 commit 18bcf7a

35 files changed

+672
-2
lines changed

clang/include/clang/Basic/CodeGenOptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,13 @@ class CodeGenOptions : public CodeGenOptionsBase {
501501

502502
/// A list of functions that are replacable by the loader.
503503
std::vector<std::string> LoaderReplaceableFunctionNames;
504+
/// The name of a file that contains functions which will be compiled for
505+
/// hotpatching. See -fms-hot-patch-functions-file.
506+
std::string MSHotPatchFunctionsFile;
507+
508+
/// A list of functions which will be compiled for hotpatching.
509+
/// See -fms-hot-patch-functions-list.
510+
std::vector<std::string> MSHotPatchFunctionsList;
504511

505512
public:
506513
// Define accessors/mutators for code generation options of enumeration type.

clang/include/clang/Driver/Options.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3830,6 +3830,20 @@ def fms_hotpatch : Flag<["-"], "fms-hotpatch">, Group<f_Group>,
38303830
Visibility<[ClangOption, CC1Option, CLOption]>,
38313831
HelpText<"Ensure that all functions can be hotpatched at runtime">,
38323832
MarshallingInfoFlag<CodeGenOpts<"HotPatch">>;
3833+
def fms_hotpatch_functions_file
3834+
: Joined<["-"], "fms-hotpatch-functions-file=">,
3835+
Group<f_Group>,
3836+
Visibility<[ClangOption, CC1Option, CLOption]>,
3837+
MarshallingInfoString<CodeGenOpts<"MSHotPatchFunctionsFile">>,
3838+
HelpText<"Path to a file that contains a list of mangled symbol names of "
3839+
"functions that should be hot-patched">;
3840+
def fms_hotpatch_functions_list
3841+
: CommaJoined<["-"], "fms-hotpatch-functions-list=">,
3842+
Group<f_Group>,
3843+
Visibility<[ClangOption, CC1Option, CLOption]>,
3844+
MarshallingInfoStringVector<CodeGenOpts<"MSHotPatchFunctionsList">>,
3845+
HelpText<"List of mangled symbol names of functions that should be "
3846+
"hot-patched">;
38333847
def fpcc_struct_return : Flag<["-"], "fpcc-struct-return">, Group<f_Group>,
38343848
Visibility<[ClangOption, CC1Option]>,
38353849
HelpText<"Override the default ABI to return all structs on the stack">;

clang/lib/CodeGen/CGCall.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2639,6 +2639,15 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
26392639
// CPU/feature overrides. addDefaultFunctionDefinitionAttributes
26402640
// handles these separately to set them based on the global defaults.
26412641
GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs);
2642+
2643+
// Windows hotpatching support
2644+
if (!MSHotPatchFunctions.empty()) {
2645+
bool IsHotPatched = std::binary_search(MSHotPatchFunctions.begin(),
2646+
MSHotPatchFunctions.end(), Name);
2647+
if (IsHotPatched) {
2648+
FuncAttrs.addAttribute(llvm::Attribute::MarkedForWindowsHotPatching);
2649+
}
2650+
}
26422651
}
26432652

26442653
// Mark functions that are replaceable by the loader.

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,37 @@ CodeGenModule::CodeGenModule(ASTContext &C,
453453
if (Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86)
454454
getModule().addModuleFlag(llvm::Module::Error, "NumRegisterParameters",
455455
CodeGenOpts.NumRegisterParameters);
456+
457+
// If there are any functions that are marked for Windows hot-patching,
458+
// then build the list of functions now.
459+
if (!CGO.MSHotPatchFunctionsFile.empty() ||
460+
!CGO.MSHotPatchFunctionsList.empty()) {
461+
if (!CGO.MSHotPatchFunctionsFile.empty()) {
462+
auto BufOrErr = llvm::MemoryBuffer::getFile(CGO.MSHotPatchFunctionsFile);
463+
if (BufOrErr) {
464+
const llvm::MemoryBuffer &FileBuffer = **BufOrErr;
465+
for (llvm::line_iterator I(FileBuffer.getMemBufferRef(), true), E;
466+
I != E; ++I) {
467+
this->MSHotPatchFunctions.push_back(std::string{*I});
468+
}
469+
} else {
470+
auto &DE = Context.getDiagnostics();
471+
unsigned DiagID =
472+
DE.getCustomDiagID(DiagnosticsEngine::Error,
473+
"failed to open hotpatch functions file "
474+
"(-fms-hotpatch-functions-file): %0 : %1");
475+
DE.Report(DiagID) << CGO.MSHotPatchFunctionsFile
476+
<< BufOrErr.getError().message();
477+
}
478+
}
479+
480+
for (const auto &FuncName : CGO.MSHotPatchFunctionsList) {
481+
this->MSHotPatchFunctions.push_back(FuncName);
482+
}
483+
484+
std::sort(this->MSHotPatchFunctions.begin(),
485+
this->MSHotPatchFunctions.end());
486+
}
456487
}
457488

458489
CodeGenModule::~CodeGenModule() {}

clang/lib/CodeGen/CodeGenModule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,11 @@ class CodeGenModule : public CodeGenTypeCache {
678678

679679
AtomicOptions AtomicOpts;
680680

681+
// A set of functions which should be hot-patched; see
682+
// -fms-hotpatch-functions-file (and -list). This will nearly always be empty.
683+
// The list is sorted for binary-searching.
684+
std::vector<std::string> MSHotPatchFunctions;
685+
681686
public:
682687
CodeGenModule(ASTContext &C, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
683688
const HeaderSearchOptions &headersearchopts,

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6946,6 +6946,16 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
69466946

69476947
Args.AddLastArg(CmdArgs, options::OPT_fms_hotpatch);
69486948

6949+
if (Arg *A = Args.getLastArg(options::OPT_fms_hotpatch_functions_file)) {
6950+
Args.AddLastArg(CmdArgs, options::OPT_fms_hotpatch_functions_file);
6951+
}
6952+
6953+
for (const auto &A :
6954+
Args.getAllArgValues(options::OPT_fms_hotpatch_functions_list)) {
6955+
CmdArgs.push_back(
6956+
Args.MakeArgString("-fms-hotpatch-functions-list=" + Twine(A)));
6957+
}
6958+
69496959
if (TC.SupportsProfiling()) {
69506960
Args.AddLastArg(CmdArgs, options::OPT_pg);
69516961

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This verifies that we correctly handle a -fms-hotpatch-functions-file argument that points
2+
// to a missing file.
3+
//
4+
// RUN: not %clang_cl -c --target=x86_64-windows-msvc -O2 /Z7 -fms-hotpatch-functions-file=%S/this-file-is-intentionally-missing-do-not-create-it.txt /Fo%t.obj %s 2>&1 | FileCheck %s
5+
// CHECK: failed to open hotpatch functions file
6+
7+
void this_might_have_side_effects();
8+
9+
int __declspec(noinline) this_gets_hotpatched() {
10+
this_might_have_side_effects();
11+
return 42;
12+
}
13+
14+
int __declspec(noinline) this_does_not_get_hotpatched() {
15+
return this_gets_hotpatched() + 100;
16+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// This verifies that hotpatch function attributes are correctly propagated when compiling directly to OBJ,
2+
// and that name mangling works as expected.
3+
//
4+
// RUN: %clang_cl -c --target=x86_64-windows-msvc -O2 /Z7 -fms-hotpatch-functions-list=?this_gets_hotpatched@@YAHXZ /Fo%t.obj %s
5+
// RUN: llvm-readobj --codeview %t.obj | FileCheck %s
6+
7+
void this_might_have_side_effects();
8+
9+
int __declspec(noinline) this_gets_hotpatched() {
10+
this_might_have_side_effects();
11+
return 42;
12+
}
13+
14+
// CHECK: Kind: S_HOTPATCHFUNC (0x1169)
15+
// CHECK-NEXT: Function: this_gets_hotpatched
16+
// CHECK-NEXT: Name: ?this_gets_hotpatched@@YAHXZ
17+
18+
extern "C" int __declspec(noinline) this_does_not_get_hotpatched() {
19+
return this_gets_hotpatched() + 100;
20+
}
21+
22+
// CHECK-NOT: S_HOTPATCHFUNC
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
this_gets_hotpatched

clang/test/CodeGen/ms-hotpatch-lto.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// This verifies that hotpatch function attributes are correctly propagated through LLVM IR when compiling with LTO.
2+
//
3+
// RUN: %clang_cl -c --target=x86_64-windows-msvc -O2 /Z7 -fms-hotpatch-functions-file=%S/ms-hotpatch-functions.txt -flto /Fo%t.bc %s
4+
// RUN: llvm-dis %t.bc -o - | FileCheck %s
5+
//
6+
// CHECK: ; Function Attrs: marked_for_windows_hot_patching mustprogress nofree noinline norecurse nosync nounwind sspstrong willreturn memory(none) uwtable
7+
// CHECK-NEXT: define dso_local noundef i32 @this_gets_hotpatched() local_unnamed_addr #0 !dbg !13 {
8+
//
9+
// CHECK: ; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind sspstrong willreturn memory(none) uwtable
10+
// CHECK-NEXT: define dso_local noundef i32 @this_does_not_get_hotpatched() local_unnamed_addr #1 !dbg !19 {
11+
12+
int __declspec(noinline) this_gets_hotpatched() {
13+
return 42;
14+
}
15+
16+
int __declspec(noinline) this_does_not_get_hotpatched() {
17+
return this_gets_hotpatched() + 100;
18+
}

0 commit comments

Comments
 (0)