[clang] [llvm] [aarch64][x86][win] Add compiler support for MSVC's /funcoverride flag (Windows kernel loader replaceable functions) (PR #125320)
Daniel Paoliello via llvm-commits
llvm-commits at lists.llvm.org
Fri May 9 11:51:03 PDT 2025
https://github.com/dpaoliello updated https://github.com/llvm/llvm-project/pull/125320
>From f429ad43fa0f56240721f071607b196ad970a6a0 Mon Sep 17 00:00:00 2001
From: "Daniel Paoliello (HE/HIM)" <danpao at microsoft.com>
Date: Fri, 31 Jan 2025 16:47:23 -0800
Subject: [PATCH] [aarch64][x86][win] Add support for MSVC's /funcoverride flag
(Windows kernel loader replaceable functions)
---
clang/include/clang/Basic/CodeGenOptions.h | 9 ++
clang/include/clang/Driver/Options.td | 7 ++
clang/lib/CodeGen/CGCall.cpp | 4 +
clang/lib/Driver/ToolChains/Clang.cpp | 6 +
.../CodeGen/loader-replaceable-function.cpp | 17 +++
clang/test/Driver/cl-options.c | 4 +
llvm/include/llvm/CodeGen/AsmPrinter.h | 11 ++
llvm/include/llvm/IR/Attributes.td | 1 +
llvm/include/llvm/IR/Mangler.h | 2 +
llvm/lib/Analysis/InlineCost.cpp | 4 +
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 106 ++++++++++++++++++
.../AArch64/AArch64Arm64ECCallLowering.cpp | 5 +-
llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 28 +----
llvm/lib/Target/X86/X86AsmPrinter.cpp | 37 +-----
.../win-loader-replaceable-function.ll | 40 +++++++
.../X86/win-loader-replaceable-function.ll | 40 +++++++
llvm/test/Transforms/Inline/attributes.ll | 12 ++
17 files changed, 270 insertions(+), 63 deletions(-)
create mode 100644 clang/test/CodeGen/loader-replaceable-function.cpp
create mode 100644 llvm/test/CodeGen/AArch64/win-loader-replaceable-function.ll
create mode 100644 llvm/test/CodeGen/X86/win-loader-replaceable-function.ll
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index e716b59a119fc..278803f7bb960 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -499,6 +499,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)
@@ -571,6 +574,12 @@ 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 llvm::is_contained(LoaderReplaceableFunctionNames, FuncName);
+ }
};
} // end namespace clang
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 3cdf57d0085ee..bd8df8f6a749a 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -7826,6 +7826,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">>;
+
} // let Visibility = [CC1Option]
//===----------------------------------------------------------------------===//
@@ -9088,6 +9091,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 65970fcdc9648..aa1909443e8cd 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -2641,6 +2641,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 0c09daa17d37f..a08ff044add95 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8578,6 +8578,12 @@ 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.cpp b/clang/test/CodeGen/loader-replaceable-function.cpp
new file mode 100644
index 0000000000000..281da2d9f5088
--- /dev/null
+++ b/clang/test/CodeGen/loader-replaceable-function.cpp
@@ -0,0 +1,17 @@
+// 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
+
+// CHECK: define dso_local void @override_me() #0
+extern "C" void override_me() {}
+
+// CHECK: define dso_local void @"?override_me_cpp@@YAXXZ"() #0
+void override_me_cpp() {}
+
+// CHECK: define dso_local void @dont_override_me() #1
+extern "C" 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 f8833e6995c39..c32b6a7f68c8c 100644
--- a/clang/test/Driver/cl-options.c
+++ b/clang/test/Driver/cl-options.c
@@ -823,4 +823,8 @@
// RUN: %clang_cl /d2epilogunwind /c -### -- %s 2>&1 | FileCheck %s --check-prefix=EPILOGUNWIND
// EPILOGUNWIND: -fwinx64-eh-unwindv2
+// 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 9132a0a6ea5a3..9a8f2d5e398e7 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -812,6 +812,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 fb94926043fc7..d488c5f419b82 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -400,6 +400,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 6c8ebf5f072f2..edbd0a5efb5dc 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 a15c5d78d40b8..8ddfa1e4eb6f7 100644
--- a/llvm/lib/Analysis/InlineCost.cpp
+++ b/llvm/lib/Analysis/InlineCost.cpp
@@ -3174,6 +3174,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 eb076960a5def..fdb81b05d9490 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -4723,3 +4723,109 @@ 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;
+ bool SwitchedToDirectiveSection = false;
+ for (const Function &F : M.functions()) {
+ if (F.hasFnAttribute("loader-replaceable")) {
+ if (!SwitchedToDirectiveSection) {
+ OutStreamer->switchSection(
+ OutContext.getObjectFileInfo()->getDrectveSection());
+ SwitchedToDirectiveSection = true;
+ }
+
+ StringRef 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.drop_back(HybridPatchableTargetSuffix.size());
+ }
+
+ MCSymbol *FuncOverrideSymbol =
+ MMI->getContext().getOrCreateSymbol(Name + "_$fo$");
+ OutStreamer->beginCOFFSymbolDef(FuncOverrideSymbol);
+ OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_EXTERNAL);
+ OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL);
+ OutStreamer->endCOFFSymbolDef();
+
+ MCSymbol *FuncOverrideDefaultSymbol =
+ MMI->getContext().getOrCreateSymbol(Name + "_$fo_default$");
+ 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:") +
+ FuncOverrideSymbol->getName() + "=" +
+ FuncOverrideDefaultSymbol->getName())
+ .toStringRef(Buf));
+ Buf.clear();
+ }
+ }
+
+ if (SwitchedToDirectiveSection)
+ 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 (MCSymbol *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 a79fbdc141835..b8e1eb573e422 100644
--- a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
@@ -815,7 +815,8 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &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
@@ -823,7 +824,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 38be677ec805b..a53606851d0a2 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -329,32 +329,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 (M.getModuleFlag("import-call-optimization"))
EnableImportCallOptimization = true;
diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp
index 754f3f017fd29..ce9a7c42d963c 100644
--- a/llvm/lib/Target/X86/X86AsmPrinter.cpp
+++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp
@@ -910,41 +910,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 0000000000000..745a67ae41b46
--- /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 0000000000000..69212d3d56da8
--- /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 6595f54bda980..42b1a3a29aec4 100644
--- a/llvm/test/Transforms/Inline/attributes.ll
+++ b/llvm/test/Transforms/Inline/attributes.ll
@@ -627,6 +627,18 @@ define i32 @thunk_extern_caller() fn_ret_thunk_extern {
ret i32 %1
}
+; Test that loader replaceable functions never get inlined.
+define i32 @loader_replaceable_callee(i32 %i) "loader-replaceable" {
+ ret i32 %i
+}
+
+define i32 @loader_replaceable_caller() {
+; CHECK: @loader_replaceable_caller() {
+; CHECK-NEXT: call i32 @loader_replaceable_callee()
+ %1 = call i32 @loader_replaceable_callee()
+ ret i32 %1
+}
+
; CHECK: attributes [[SLH]] = { speculative_load_hardening }
; CHECK: attributes [[FPMAD_FALSE]] = { "less-precise-fpmad"="false" }
; CHECK: attributes [[FPMAD_TRUE]] = { "less-precise-fpmad"="true" }
More information about the llvm-commits
mailing list