-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[aarch64][x86][win] Add compiler support for MSVC's /funcoverride flag (Windows kernel loader replaceable functions) #125320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-backend-x86 @llvm/pr-subscribers-llvm-ir Author: Daniel Paoliello (dpaoliello) ChangesAdds support for MSVC's undocumented For each marked function we need to generate:
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 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. Patch is 20.76 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/125320.diff 17 Files Affected:
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index b64ad74d711c60..8985f854bfdb3e 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -483,6 +483,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// The name of a file to use with \c .secure_log_unique directives.
std::string AsSecureLogFile;
+ /// A list of functions that are replacable by the loader.
+ std::vector<std::string> LoaderReplaceableFunctionNames;
+
public:
// Define accessors/mutators for code generation options of enumeration type.
#define CODEGENOPT(Name, Bits, Default)
@@ -555,6 +558,15 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// Reset all of the options that are not considered when building a
/// module.
void resetNonModularOptions(StringRef ModuleFormat);
+
+ // Is the given function name one of the functions that can be replaced by the
+ // loader?
+ bool isLoaderReplaceableFunctionName(StringRef FuncName) const {
+ return std::any_of(LoaderReplaceableFunctionNames.begin(), LoaderReplaceableFunctionNames.end(),
+ [&](const std::string &ReplaceableName) {
+ return FuncName == ReplaceableName;
+ });
+ }
};
} // end namespace clang
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index df705104d9ea31..c6d66141e10040 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -7667,6 +7667,9 @@ def fexperimental_assignment_tracking_EQ : Joined<["-"], "fexperimental-assignme
def enable_tlsdesc : Flag<["-"], "enable-tlsdesc">,
MarshallingInfoFlag<CodeGenOpts<"EnableTLSDESC">>;
+def replaceable_function: Joined<["-"], "loader-replaceable-function=">,
+ MarshallingInfoStringVector<CodeGenOpts<"LoaderReplaceableFunctionNames">>;
+
} // let Visibility = [CC1Option]
//===----------------------------------------------------------------------===//
@@ -8926,6 +8929,10 @@ def _SLASH_Gregcall : CLFlag<"Gregcall">,
def _SLASH_Gregcall4 : CLFlag<"Gregcall4">,
HelpText<"Set __regcall4 as a default calling convention to respect __regcall ABI v.4">;
+def _SLASH_funcoverride : CLCompileJoined<"funcoverride:">,
+ HelpText<"Mark <function> as being replaceable by the Windows kernel loader">,
+ MetaVarName<"<function>">;
+
// GNU Driver aliases
def : Separate<["-"], "Xmicrosoft-visualc-tools-root">, Alias<_SLASH_vctoolsdir>;
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index e0cf6ca69f0df2..9bdbd5db75981e 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -2574,6 +2574,10 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs);
}
+ // Mark functions that are replaceable by the loader.
+ if (CodeGenOpts.isLoaderReplaceableFunctionName(Name))
+ FuncAttrs.addAttribute("loader-replaceable");
+
// Collect attributes from arguments and return values.
ClangToLLVMArgMapping IRFunctionArgs(getContext(), FI);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 518113e20cb063..3715ca022518ce 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8592,6 +8592,10 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType,
}
A->claim();
}
+
+ for (const auto &FuncOverride : Args.getAllArgValues(options::OPT__SLASH_funcoverride)) {
+ CmdArgs.push_back(Args.MakeArgString(Twine("-loader-replaceable-function=") + FuncOverride));
+ }
}
const char *Clang::getBaseInputName(const ArgList &Args,
diff --git a/clang/test/CodeGen/loader-replaceable-function.c b/clang/test/CodeGen/loader-replaceable-function.c
new file mode 100644
index 00000000000000..16cda854b1932a
--- /dev/null
+++ b/clang/test/CodeGen/loader-replaceable-function.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -loader-replaceable-function=override_me -emit-llvm -std=c11 -o - %s | FileCheck %s
+
+// CHECK: define dso_local void @override_me() #0
+void override_me() {}
+
+// CHECK: define dso_local void @dont_override_me() #1
+void dont_override_me() {}
+
+// CHECK: attributes #0 = {
+// CHECK-SAME: loader-replaceable
+
+// CHECK: attributes #1 = {
+// CHECK-NOT: loader-replaceable
+// CHECK-SAME: }
diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c
index 29a0fcbc17ac60..136abee8113d88 100644
--- a/clang/test/Driver/cl-options.c
+++ b/clang/test/Driver/cl-options.c
@@ -813,4 +813,8 @@
// RUN: %clang_cl -vctoolsdir "" /arm64EC /c -target x86_64-pc-windows-msvc -### -- %s 2>&1 | FileCheck --check-prefix=ARM64EC_OVERRIDE %s
// ARM64EC_OVERRIDE: warning: /arm64EC has been overridden by specified target: x86_64-pc-windows-msvc; option ignored
+// RUN: %clang_cl /funcoverride:override_me1 /funcoverride:override_me2 /c -### -- %s 2>&1 | FileCheck %s --check-prefix=FUNCOVERRIDE
+// FUNCOVERRIDE: -loader-replaceable-function=override_me1
+// FUNCOVERRIDE-SAME: -loader-replaceable-function=override_me2
+
void f(void) { }
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 5291369b3b9f1d..6015f7614cf660 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -796,6 +796,17 @@ class AsmPrinter : public MachineFunctionPass {
getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr,
const MCSymbol *BranchLabel) const;
+ //===------------------------------------------------------------------===//
+ // COFF Helper Routines
+ //===------------------------------------------------------------------===//
+
+ /// Emits symbols and data to allow functions marked with the
+ /// loader-replaceable attribute to be replaceable.
+ void emitCOFFReplaceableFunctionData(Module &M);
+
+ /// Emits the @feat.00 symbol indicating the features enabled in this module.
+ void emitCOFFFeatureSymbol(Module &M);
+
//===------------------------------------------------------------------===//
// Inline Asm Support
//===------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 4396ec4d04c416..f475b3d5b13681 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -403,6 +403,7 @@ def NoJumpTables : StrBoolAttr<"no-jump-tables">;
def NoInlineLineTables : StrBoolAttr<"no-inline-line-tables">;
def ProfileSampleAccurate : StrBoolAttr<"profile-sample-accurate">;
def UseSampleProfile : StrBoolAttr<"use-sample-profile">;
+def LoaderReplaceable : StrBoolAttr<"loader-replaceable">;
def DenormalFPMath : ComplexStrAttr<"denormal-fp-math", [FnAttr]>;
def DenormalFPMathF32 : ComplexStrAttr<"denormal-fp-math-f32", [FnAttr]>;
diff --git a/llvm/include/llvm/IR/Mangler.h b/llvm/include/llvm/IR/Mangler.h
index 6c8ebf5f072f28..edbd0a5efb5dcc 100644
--- a/llvm/include/llvm/IR/Mangler.h
+++ b/llvm/include/llvm/IR/Mangler.h
@@ -25,6 +25,8 @@ class Triple;
class Twine;
class raw_ostream;
+constexpr std::string_view HybridPatchableTargetSuffix = "$hp_target";
+
class Mangler {
/// We need to give global values the same name every time they are mangled.
/// This keeps track of the number we give to anonymous ones.
diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp
index 8fa150f7d690e7..af913ffef3141a 100644
--- a/llvm/lib/Analysis/InlineCost.cpp
+++ b/llvm/lib/Analysis/InlineCost.cpp
@@ -3078,6 +3078,10 @@ std::optional<InlineResult> llvm::getAttributeBasedInliningDecision(
if (Call.isNoInline())
return InlineResult::failure("noinline call site attribute");
+ // Don't inline functions that are loader replaceable.
+ if (Callee->hasFnAttribute("loader-replaceable"))
+ return InlineResult::failure("loader replaceable function attribute");
+
return std::nullopt;
}
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index b2a4721f37b268..748747698d8d89 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -4621,3 +4621,98 @@ AsmPrinter::getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr,
return std::make_tuple(Base, 0, BranchLabel,
codeview::JumpTableEntrySize::Int32);
}
+
+void AsmPrinter::emitCOFFReplaceableFunctionData(Module &M) {
+ const Triple &TT = TM.getTargetTriple();
+ assert(TT.isOSBinFormatCOFF());
+
+ bool IsTargetArm64EC = TT.isWindowsArm64EC();
+ SmallVector<char> Buf;
+ SmallVector<MCSymbol *> FuncOverrideDefaultSymbols;
+ OutStreamer->switchSection(OutContext.getObjectFileInfo()->getDrectveSection());
+ for (const auto& F : M.functions()) {
+ if (F.hasFnAttribute("loader-replaceable")) {
+ auto Name = F.getName();
+
+ // For hybrid-patchable targets, strip the prefix so that we can mark
+ // the real function as replaceable.
+ if (IsTargetArm64EC && Name.ends_with(HybridPatchableTargetSuffix)) {
+ Name = Name.substr(0, Name.size() - HybridPatchableTargetSuffix.size());
+ }
+
+ llvm::Twine FuncOverrideName = Name + "_$fo$";
+ auto FuncOverrideSymbol = MMI->getContext().getOrCreateSymbol(FuncOverrideName);
+ OutStreamer->beginCOFFSymbolDef(FuncOverrideSymbol);
+ OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_EXTERNAL);
+ OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL);
+ OutStreamer->endCOFFSymbolDef();
+
+ llvm::Twine FuncOverrideDefaultName = Name + "_$fo_default$";
+ auto FuncOverrideDefaultSymbol = MMI->getContext().getOrCreateSymbol(FuncOverrideDefaultName);
+ OutStreamer->beginCOFFSymbolDef(FuncOverrideDefaultSymbol);
+ OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_EXTERNAL);
+ OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL);
+ OutStreamer->endCOFFSymbolDef();
+ FuncOverrideDefaultSymbols.push_back(FuncOverrideDefaultSymbol);
+
+ OutStreamer->emitBytes((Twine(" /ALTERNATENAME:") + FuncOverrideName + "=" + FuncOverrideDefaultName).toStringRef(Buf));
+ Buf.clear();
+ }
+ }
+ OutStreamer->popSection();
+
+ if (FuncOverrideDefaultSymbols.empty())
+ return;
+
+ // MSVC emits the symbols for the default variables pointing at the start of
+ // the .data section, but doesn't actually allocate any space for them. LLVM
+ // can't do this, so have all of the variables pointing at a single byte
+ // instead.
+ OutStreamer->switchSection(OutContext.getObjectFileInfo()->getDataSection());
+ for (auto Symbol : FuncOverrideDefaultSymbols) {
+ OutStreamer->emitLabel(Symbol);
+ }
+ OutStreamer->emitZeros(1);
+ OutStreamer->popSection();
+}
+
+void AsmPrinter::emitCOFFFeatureSymbol(Module &M) {
+ const Triple &TT = TM.getTargetTriple();
+ assert(TT.isOSBinFormatCOFF());
+
+ // Emit an absolute @feat.00 symbol.
+ MCSymbol *S = MMI->getContext().getOrCreateSymbol(StringRef("@feat.00"));
+ OutStreamer->beginCOFFSymbolDef(S);
+ OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_STATIC);
+ OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL);
+ OutStreamer->endCOFFSymbolDef();
+ int64_t Feat00Value = 0;
+
+ if (TT.getArch() == Triple::x86) {
+ // According to the PE-COFF spec, the LSB of this value marks the object
+ // for "registered SEH". This means that all SEH handler entry points
+ // must be registered in .sxdata. Use of any unregistered handlers will
+ // cause the process to terminate immediately. LLVM does not know how to
+ // register any SEH handlers, so its object files should be safe.
+ Feat00Value |= COFF::Feat00Flags::SafeSEH;
+ }
+
+ if (M.getModuleFlag("cfguard")) {
+ // Object is CFG-aware.
+ Feat00Value |= COFF::Feat00Flags::GuardCF;
+ }
+
+ if (M.getModuleFlag("ehcontguard")) {
+ // Object also has EHCont.
+ Feat00Value |= COFF::Feat00Flags::GuardEHCont;
+ }
+
+ if (M.getModuleFlag("ms-kernel")) {
+ // Object is compiled with /kernel.
+ Feat00Value |= COFF::Feat00Flags::Kernel;
+ }
+
+ OutStreamer->emitSymbolAttribute(S, MCSA_Global);
+ OutStreamer->emitAssignment(
+ S, MCConstantExpr::create(Feat00Value, MMI->getContext()));
+}
diff --git a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
index abd2df301880c4..269f2c4efd9ba6 100644
--- a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
@@ -808,7 +808,7 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) {
for (Function &F : Mod) {
if (!F.hasFnAttribute(Attribute::HybridPatchable) || F.isDeclaration() ||
- F.hasLocalLinkage() || F.getName().ends_with("$hp_target"))
+ F.hasLocalLinkage() || F.getName().ends_with(HybridPatchableTargetSuffix))
continue;
// Rename hybrid patchable functions and change callers to use a global
@@ -816,7 +816,7 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) {
if (std::optional<std::string> MangledName =
getArm64ECMangledFunctionName(F.getName().str())) {
std::string OrigName(F.getName());
- F.setName(MangledName.value() + "$hp_target");
+ F.setName(MangledName.value() + HybridPatchableTargetSuffix);
// The unmangled symbol is a weak alias to an undefined symbol with the
// "EXP+" prefix. This undefined symbol is resolved by the linker by
diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index 8d8520c68232be..330a0b795527c5 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -318,32 +318,8 @@ void AArch64AsmPrinter::emitStartOfAsmFile(Module &M) {
const Triple &TT = TM.getTargetTriple();
if (TT.isOSBinFormatCOFF()) {
- // Emit an absolute @feat.00 symbol
- MCSymbol *S = MMI->getContext().getOrCreateSymbol(StringRef("@feat.00"));
- OutStreamer->beginCOFFSymbolDef(S);
- OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_STATIC);
- OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL);
- OutStreamer->endCOFFSymbolDef();
- int64_t Feat00Value = 0;
-
- if (M.getModuleFlag("cfguard")) {
- // Object is CFG-aware.
- Feat00Value |= COFF::Feat00Flags::GuardCF;
- }
-
- if (M.getModuleFlag("ehcontguard")) {
- // Object also has EHCont.
- Feat00Value |= COFF::Feat00Flags::GuardEHCont;
- }
-
- if (M.getModuleFlag("ms-kernel")) {
- // Object is compiled with /kernel.
- Feat00Value |= COFF::Feat00Flags::Kernel;
- }
-
- OutStreamer->emitSymbolAttribute(S, MCSA_Global);
- OutStreamer->emitAssignment(
- S, MCConstantExpr::create(Feat00Value, MMI->getContext()));
+ emitCOFFFeatureSymbol(M);
+ emitCOFFReplaceableFunctionData(M);
}
if (!TT.isOSBinFormatELF())
diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp
index f01e47b41cf5e4..d85e4df0a08b27 100644
--- a/llvm/lib/Target/X86/X86AsmPrinter.cpp
+++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp
@@ -885,41 +885,8 @@ void X86AsmPrinter::emitStartOfAsmFile(Module &M) {
OutStreamer->switchSection(getObjFileLowering().getTextSection());
if (TT.isOSBinFormatCOFF()) {
- // Emit an absolute @feat.00 symbol.
- MCSymbol *S = MMI->getContext().getOrCreateSymbol(StringRef("@feat.00"));
- OutStreamer->beginCOFFSymbolDef(S);
- OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_STATIC);
- OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL);
- OutStreamer->endCOFFSymbolDef();
- int64_t Feat00Value = 0;
-
- if (TT.getArch() == Triple::x86) {
- // According to the PE-COFF spec, the LSB of this value marks the object
- // for "registered SEH". This means that all SEH handler entry points
- // must be registered in .sxdata. Use of any unregistered handlers will
- // cause the process to terminate immediately. LLVM does not know how to
- // register any SEH handlers, so its object files should be safe.
- Feat00Value |= COFF::Feat00Flags::SafeSEH;
- }
-
- if (M.getModuleFlag("cfguard")) {
- // Object is CFG-aware.
- Feat00Value |= COFF::Feat00Flags::GuardCF;
- }
-
- if (M.getModuleFlag("ehcontguard")) {
- // Object also has EHCont.
- Feat00Value |= COFF::Feat00Flags::GuardEHCont;
- }
-
- if (M.getModuleFlag("ms-kernel")) {
- // Object is compiled with /kernel.
- Feat00Value |= COFF::Feat00Flags::Kernel;
- }
-
- OutStreamer->emitSymbolAttribute(S, MCSA_Global);
- OutStreamer->emitAssignment(
- S, MCConstantExpr::create(Feat00Value, MMI->getContext()));
+ emitCOFFFeatureSymbol(M);
+ emitCOFFReplaceableFunctionData(M);
}
OutStreamer->emitSyntaxDirective();
diff --git a/llvm/test/CodeGen/AArch64/win-loader-replaceable-function.ll b/llvm/test/CodeGen/AArch64/win-loader-replaceable-function.ll
new file mode 100644
index 00000000000000..745a67ae41b460
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/win-loader-replaceable-function.ll
@@ -0,0 +1,40 @@
+; RUN: llc -mtriple=aarch64-pc-windows-msvc < %s | FileCheck %s
+
+define dso_local i32 @override_me1() "loader-replaceable" {
+entry:
+ ret i32 1
+}
+
+define dso_local i32 @override_me2() "loader-replaceable" {
+entry:
+ ret i32 2
+}
+
+define dso_local i32 @dont_override_me() {
+entry:
+ ret i32 3
+}
+
+; CHECK: .section .drectve,"yni"
+; CHECK-NEXT: .def override_me1_$fo$;
+; CHECK-NEXT: .scl 2;
+; CHECK-NEXT: .type 0;
+; CHECK-NEXT: .endef
+; CHECK-NEXT: .def override_me1_$fo_default$;
+; CHECK-NEXT: .scl 2;
+; CHECK-NEXT: .type 0;
+; CHECK-NEXT: .endef
+; CHECK-NEXT: .ascii " /ALTERNATENAME:override_me1_$fo$=override_me1_$fo_default$"
+; CHECK-NEXT: .def override_me2_$fo$;
+; CHECK-NEXT: .scl 2;
+; CHECK-NEXT: .type 0;
+; CHECK-NEXT: .endef
+; CHECK-NEXT: .def override_me2_$fo_default$;
+; CHECK-NEXT: .scl 2;
+; CHECK-NEXT: .type 0;
+; CHECK-NEXT: .endef
+; CHECK-NEXT: .ascii " /ALTERNATENAME:override_me2_$fo$=override_me2_$fo_default$"
+; CHECK-NEXT: .data
+; CHECK-NEXT: override_me1_$fo_default$:
+; CHECK-NEXT: override_me2_$fo_default$:
+; CHECK-NEXT: .zero 1
diff --git a/llvm/test/CodeGen/X86/win-loader-replaceable-function.ll b/llvm/test/CodeGen/X86/win-loader-replaceable-function.ll
new file mode 100644
index 00000000000000..69212d3d56da8e
--- /dev/null
+++ b/llvm/test/CodeGen/X86/win-loader-replaceable-function.ll
@@ -0,0 +1,40 @@
+; RUN: llc -mtriple=x86_64-pc-windows-msvc < %s | FileCheck %s
+
+define dso_local i32 @override_me1() "loader-replaceable" {
+entry:
+ ret i32 1
+}
+
+define dso_local i32 @override_me2() "loader-replaceable" {
+entry:
+ ret i32 2
+}
+
+define dso_local i32 @dont_override_me() {
+entry:
+ ret i32 3
+}
+
+; CHECK: .section .drectve,"yni"
+; CHECK-NEXT: .def override_me1_$fo$;
+; CHECK-NEXT: .scl 2;
+; CHECK-NEXT: .type 0;
+; CHECK-NEXT: .endef
+; CHECK-NEXT: .def override_me1_$fo_default$;
+; CHECK-NEXT: .scl 2;
+; CHECK-NEXT: .type 0;
+; CHECK-NEXT: .endef
+; CHECK-NEXT: .ascii " /ALTERNATENAME:override_me1_$fo$=override_me1_$fo_default$"
+; CHECK-NEXT: .def override_me2_$fo$;
+; CHECK-NEXT: .scl 2;
+; CHECK-NEXT: .type 0;
+; CHECK-NEXT: .endef
+; CHECK-NEXT: .def override_me2_$fo_default$;
+; CHECK-NEXT: .scl 2;
+; CHECK-NEXT: .type 0;
+; CHECK-NEXT: .endef
+; CHECK-NEXT: .ascii " /ALTERNATENAME:override_me2_$fo$=override_me2_$fo_default$"
+; CHECK-NEXT: .data
+; CHECK-NEXT: override_me1_$fo_default$:
+; CHECK-NEXT: override_me2_$fo_default$:
+; CHECK-NEXT: .zero 1
diff --git a/llvm/test/Transforms/Inline/attributes.ll b/llvm/test/Transforms/Inline/attributes.ll
index 659...
[truncated]
|
061d252
to
f6b91ae
Compare
void override_me() {} | ||
|
||
// CHECK: define dso_local void @dont_override_me() #1 | ||
void dont_override_me() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a C++ testcase showing the interaction with mangling
SwitchedToDirectiveSection = true; | ||
} | ||
|
||
auto Name = F.getName(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use auto
for easily nameable types like "StringRef".
Name = Name.substr(0, Name.size() - HybridPatchableTargetSuffix.size()); | ||
} | ||
|
||
llvm::Twine FuncOverrideName = Name + "_$fo$"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sticking a Twine in a local variable is a bad idea; you end up with references to dead temporaries because a Twine often contains a reference to another Twine, or some other temporary value. (Not sure if that actually happens here, but we discourage the practice in general to avoid issues.)
// Is the given function name one of the functions that can be replaced by the | ||
// loader? | ||
bool isLoaderReplaceableFunctionName(StringRef FuncName) const { | ||
return std::any_of(LoaderReplaceableFunctionNames.begin(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
llvm::is_contained
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks - I figured there had to be an easier way to do this!
f6b91ae
to
dc48eeb
Compare
fe5b7ad
to
7e68b84
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -7613,6 +7613,9 @@ def import_call_optimization : Flag<["-"], "import-call-optimization">, | |||
"by the Windows kernel to enable import call optimization">, | |||
MarshallingInfoFlag<CodeGenOpts<"ImportCallOptimization">>; | |||
|
|||
def replaceable_function: Joined<["-"], "loader-replaceable-function=">, | |||
MarshallingInfoStringVector<CodeGenOpts<"LoaderReplaceableFunctionNames">>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also add a clang driver option... but maybe not really necessary given the expected usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only expect to use this via clang-cl, so if someone wants a clang option they can add that later.
// For hybrid-patchable targets, strip the prefix so that we can mark | ||
// the real function as replaceable. | ||
if (IsTargetArm64EC && Name.ends_with(HybridPatchableTargetSuffix)) { | ||
Name = Name.substr(0, Name.size() - HybridPatchableTargetSuffix.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Name = Name.substr(0, Name.size() - HybridPatchableTargetSuffix.size()); | |
Name = Name.drop_back(HybridPatchableTargetSuffix.size()); |
…s kernel loader replaceable functions)
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:
<name>_$fo$
.<name>_$fo_default$
that points to the.data
section (anywhere in the data section, it is assumed to be zero sized)./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 havellvm.used
andllvm.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.