Skip to content

Commit 97a58b0

Browse files
authored
[aarch64][x86][win] Add compiler support for MSVC's /funcoverride flag (Windows kernel loader replaceable functions) (#125320)
Adds support for MSVC's undocumented `/funcoverride` flag, which marks functions as being replaceable by the Windows kernel loader. This is used to allow functions to be upgraded depending on the capabilities of the current processor (e.g., the kernel can be built with the naive implementation of a function, but that function can be replaced at boot with one that uses SIMD instructions if the processor supports them). For each marked function we need to generate: * An undefined symbol named `<name>_$fo$`. * A defined symbol `<name>_$fo_default$` that points to the `.data` section (anywhere in the data section, it is assumed to be zero sized). * An `/ALTERNATENAME` linker directive that points from `<name>_$fo$` to `<name>_$fo_default$`. This is used by the MSVC linker to generate the appropriate metadata in the Dynamic Value Relocation Table. Marked function must never be inlined (otherwise those inline sites can't be replaced). Note that I've chosen to implement this in AsmPrinter as there was no way to create a `GlobalVariable` for `<name>_$fo$` that would result in a symbol being emitted (as nothing consumes it and it has no initializer). I tried to have `llvm.used` and `llvm.compiler.used` point to it, but this didn't help. Within LLVM I referred to this feature as "loader replaceable" as "function override" already has a different meaning to C++ developers... I also took the opportunity to extract the feature symbol generation code used by both AArch64 and X86 into a common function in AsmPrinter.
1 parent 8293955 commit 97a58b0

File tree

17 files changed

+270
-63
lines changed

17 files changed

+270
-63
lines changed

clang/include/clang/Basic/CodeGenOptions.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
499499
/// The name of a file to use with \c .secure_log_unique directives.
500500
std::string AsSecureLogFile;
501501

502+
/// A list of functions that are replacable by the loader.
503+
std::vector<std::string> LoaderReplaceableFunctionNames;
504+
502505
public:
503506
// Define accessors/mutators for code generation options of enumeration type.
504507
#define CODEGENOPT(Name, Bits, Default)
@@ -571,6 +574,12 @@ class CodeGenOptions : public CodeGenOptionsBase {
571574
/// Reset all of the options that are not considered when building a
572575
/// module.
573576
void resetNonModularOptions(StringRef ModuleFormat);
577+
578+
// Is the given function name one of the functions that can be replaced by the
579+
// loader?
580+
bool isLoaderReplaceableFunctionName(StringRef FuncName) const {
581+
return llvm::is_contained(LoaderReplaceableFunctionNames, FuncName);
582+
}
574583
};
575584

576585
} // end namespace clang

clang/include/clang/Driver/Options.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7826,6 +7826,9 @@ def import_call_optimization : Flag<["-"], "import-call-optimization">,
78267826
"by the Windows kernel to enable import call optimization">,
78277827
MarshallingInfoFlag<CodeGenOpts<"ImportCallOptimization">>;
78287828

7829+
def replaceable_function: Joined<["-"], "loader-replaceable-function=">,
7830+
MarshallingInfoStringVector<CodeGenOpts<"LoaderReplaceableFunctionNames">>;
7831+
78297832
} // let Visibility = [CC1Option]
78307833

78317834
//===----------------------------------------------------------------------===//
@@ -9088,6 +9091,10 @@ def _SLASH_Gregcall : CLFlag<"Gregcall">,
90889091
def _SLASH_Gregcall4 : CLFlag<"Gregcall4">,
90899092
HelpText<"Set __regcall4 as a default calling convention to respect __regcall ABI v.4">;
90909093

9094+
def _SLASH_funcoverride : CLCompileJoined<"funcoverride:">,
9095+
HelpText<"Mark <function> as being replaceable by the Windows kernel loader">,
9096+
MetaVarName<"<function>">;
9097+
90919098
// GNU Driver aliases
90929099

90939100
def : Separate<["-"], "Xmicrosoft-visualc-tools-root">, Alias<_SLASH_vctoolsdir>;

clang/lib/CodeGen/CGCall.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,6 +2641,10 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
26412641
GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs);
26422642
}
26432643

2644+
// Mark functions that are replaceable by the loader.
2645+
if (CodeGenOpts.isLoaderReplaceableFunctionName(Name))
2646+
FuncAttrs.addAttribute("loader-replaceable");
2647+
26442648
// Collect attributes from arguments and return values.
26452649
ClangToLLVMArgMapping IRFunctionArgs(getContext(), FI);
26462650

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8578,6 +8578,12 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType,
85788578
}
85798579
A->claim();
85808580
}
8581+
8582+
for (const auto &FuncOverride :
8583+
Args.getAllArgValues(options::OPT__SLASH_funcoverride)) {
8584+
CmdArgs.push_back(Args.MakeArgString(
8585+
Twine("-loader-replaceable-function=") + FuncOverride));
8586+
}
85818587
}
85828588

85838589
const char *Clang::getBaseInputName(const ArgList &Args,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %clang_cc1 -triple=x86_64-pc-windows-msvc -loader-replaceable-function=override_me -loader-replaceable-function="?override_me_cpp@@YAXXZ" -emit-llvm -o - %s | FileCheck %s
2+
3+
// CHECK: define dso_local void @override_me() #0
4+
extern "C" void override_me() {}
5+
6+
// CHECK: define dso_local void @"?override_me_cpp@@YAXXZ"() #0
7+
void override_me_cpp() {}
8+
9+
// CHECK: define dso_local void @dont_override_me() #1
10+
extern "C" void dont_override_me() {}
11+
12+
// CHECK: attributes #0 = {
13+
// CHECK-SAME: loader-replaceable
14+
15+
// CHECK: attributes #1 = {
16+
// CHECK-NOT: loader-replaceable
17+
// CHECK-SAME: }

clang/test/Driver/cl-options.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,4 +823,8 @@
823823
// RUN: %clang_cl /d2epilogunwind /c -### -- %s 2>&1 | FileCheck %s --check-prefix=EPILOGUNWIND
824824
// EPILOGUNWIND: -fwinx64-eh-unwindv2
825825

826+
// RUN: %clang_cl /funcoverride:override_me1 /funcoverride:override_me2 /c -### -- %s 2>&1 | FileCheck %s --check-prefix=FUNCOVERRIDE
827+
// FUNCOVERRIDE: -loader-replaceable-function=override_me1
828+
// FUNCOVERRIDE-SAME: -loader-replaceable-function=override_me2
829+
826830
void f(void) { }

llvm/include/llvm/CodeGen/AsmPrinter.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,17 @@ class AsmPrinter : public MachineFunctionPass {
812812
getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr,
813813
const MCSymbol *BranchLabel) const;
814814

815+
//===------------------------------------------------------------------===//
816+
// COFF Helper Routines
817+
//===------------------------------------------------------------------===//
818+
819+
/// Emits symbols and data to allow functions marked with the
820+
/// loader-replaceable attribute to be replaceable.
821+
void emitCOFFReplaceableFunctionData(Module &M);
822+
823+
/// Emits the @feat.00 symbol indicating the features enabled in this module.
824+
void emitCOFFFeatureSymbol(Module &M);
825+
815826
//===------------------------------------------------------------------===//
816827
// Inline Asm Support
817828
//===------------------------------------------------------------------===//

llvm/include/llvm/IR/Attributes.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ def NoJumpTables : StrBoolAttr<"no-jump-tables">;
400400
def NoInlineLineTables : StrBoolAttr<"no-inline-line-tables">;
401401
def ProfileSampleAccurate : StrBoolAttr<"profile-sample-accurate">;
402402
def UseSampleProfile : StrBoolAttr<"use-sample-profile">;
403+
def LoaderReplaceable : StrBoolAttr<"loader-replaceable">;
403404

404405
def DenormalFPMath : ComplexStrAttr<"denormal-fp-math", [FnAttr]>;
405406
def DenormalFPMathF32 : ComplexStrAttr<"denormal-fp-math-f32", [FnAttr]>;

llvm/include/llvm/IR/Mangler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class Triple;
2525
class Twine;
2626
class raw_ostream;
2727

28+
constexpr std::string_view HybridPatchableTargetSuffix = "$hp_target";
29+
2830
class Mangler {
2931
/// We need to give global values the same name every time they are mangled.
3032
/// This keeps track of the number we give to anonymous ones.

llvm/lib/Analysis/InlineCost.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3174,6 +3174,10 @@ std::optional<InlineResult> llvm::getAttributeBasedInliningDecision(
31743174
if (Call.isNoInline())
31753175
return InlineResult::failure("noinline call site attribute");
31763176

3177+
// Don't inline functions that are loader replaceable.
3178+
if (Callee->hasFnAttribute("loader-replaceable"))
3179+
return InlineResult::failure("loader replaceable function attribute");
3180+
31773181
return std::nullopt;
31783182
}
31793183

0 commit comments

Comments
 (0)