https://github.com/dpaoliello created https://github.com/llvm/llvm-project/pull/125320
Adds support for MSVC's undocumented `/funcoverride` flag, which marks functions as being replaceable by the Windows kernel loader. This is used to allow functions to be upgraded depending on the capabilities of the current processor (e.g., the kernel can be built with the naive implementation of a function, but that function can be replaced at boot with one that uses SIMD instructions if the processor supports them). For each marked function we need to generate: * An undefined symbol named `<name>_$fo$`. * A defined symbol `<name>_$fo_default$` that points to the `.data` section (anywhere in the data section, it is assumed to be zero sized). * An `/ALTERNATENAME` linker directive that points from `<name>_$fo$` to `<name>_$fo_default$`. This is used by the MSVC linker to generate the appropriate metadata in the Dynamic Value Relocation Table. Marked function must never be inlined (otherwise those inline sites can't be replaced). Note that I've chosen to implement this in AsmPrinter as there was no way to create a `GlobalVariable` for `<name>_$fo$` that would result in a symbol being emitted (as nothing consumes it and it has no initializer). I tried to have `llvm.used` and `llvm.compiler.used` point to it, but this didn't help. Within LLVM I referred to this feature as "loader replaceable" as "function override" already has a different meaning to C++ developers... I also took the opportunity to extract the feature symbol generation code used by both AArch64 and X86 into a common function in AsmPrinter. >From 01ee35c8778c8c63de24b0ae8a566a54d45abdd3 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 | 12 +++ clang/include/clang/Driver/Options.td | 7 ++ clang/lib/CodeGen/CGCall.cpp | 4 + clang/lib/Driver/ToolChains/Clang.cpp | 4 + .../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 | 95 +++++++++++++++++++ .../AArch64/AArch64Arm64ECCallLowering.cpp | 4 +- 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, 256 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 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 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