https://github.com/dpaoliello updated 
https://github.com/llvm/llvm-project/pull/125320

>From 061d2521e8db70c591e9c0d8f2e6348120cb55c1 Mon Sep 17 00:00:00 2001
From: "Daniel Paoliello (HE/HIM)" <dan...@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    |  13 +++
 clang/include/clang/Driver/Options.td         |   7 ++
 clang/lib/CodeGen/CGCall.cpp                  |   4 +
 clang/lib/Driver/ToolChains/Clang.cpp         |   6 ++
 .../CodeGen/loader-replaceable-function.c     |  14 +++
 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    | 100 ++++++++++++++++++
 .../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, 265 insertions(+), 63 deletions(-)
 create mode 100644 clang/test/CodeGen/loader-replaceable-function.c
 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 c531c656f42b7e..579f50043369cb 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -489,6 +489,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)
@@ -561,6 +564,16 @@ 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 d8123cc39fdc95..8dcf0558519185 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -7592,6 +7592,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]
 
 
//===----------------------------------------------------------------------===//
@@ -8838,6 +8841,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 2dce86410db857..f8cc8108352852 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 9b5132c5625faa..466bb42e557d41 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8504,6 +8504,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.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 3da63af5ba5716..e1cdf6ad520741 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 70b9a2c488d3e9..1a35fc6632eea1 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 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 44b10c3ef99726..0e558f8a4e88e9 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -4661,3 +4661,103 @@ 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..b63b798f86a331 100644
--- a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
@@ -808,7 +808,8 @@ 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 +817,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 f1f25b65fc53fa..03d21e6f80a535 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -314,32 +314,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 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 6595f54bda9804..42b1a3a29aec43 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" }

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to