llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-ir

Author: Daniel Paoliello (dpaoliello)

<details>
<summary>Changes</summary>

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 `&lt;name&gt;_$fo$`.
* A defined symbol `&lt;name&gt;_$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 `&lt;name&gt;_$fo$` to 
`&lt;name&gt;_$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 `&lt;name&gt;_$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.

---

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:

- (modified) clang/include/clang/Basic/CodeGenOptions.h (+12) 
- (modified) clang/include/clang/Driver/Options.td (+7) 
- (modified) clang/lib/CodeGen/CGCall.cpp (+4) 
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+4) 
- (added) clang/test/CodeGen/loader-replaceable-function.c (+14) 
- (modified) clang/test/Driver/cl-options.c (+4) 
- (modified) llvm/include/llvm/CodeGen/AsmPrinter.h (+11) 
- (modified) llvm/include/llvm/IR/Attributes.td (+1) 
- (modified) llvm/include/llvm/IR/Mangler.h (+2) 
- (modified) llvm/lib/Analysis/InlineCost.cpp (+4) 
- (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (+95) 
- (modified) llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp (+2-2) 
- (modified) llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp (+2-26) 
- (modified) llvm/lib/Target/X86/X86AsmPrinter.cpp (+2-35) 
- (added) llvm/test/CodeGen/AArch64/win-loader-replaceable-function.ll (+40) 
- (added) llvm/test/CodeGen/X86/win-loader-replaceable-function.ll (+40) 
- (modified) llvm/test/Transforms/Inline/attributes.ll (+12) 


``````````diff
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]

``````````

</details>


https://github.com/llvm/llvm-project/pull/125320
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to