https://github.com/dpaoliello updated https://github.com/llvm/llvm-project/pull/125320
>From 7e68b84f5d2ebdc8d4ac35af97b163f10bcaf455 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 | 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 c531c656f42b7..3209762c0b0eb 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,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 1cf62ab466134..2aea442efea6f 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -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">>; + } // let Visibility = [CC1Option] //===----------------------------------------------------------------------===// @@ -8859,6 +8862,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 2dce86410db85..f8cc810835285 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 5deafa2ad0f4a..10723544c30c6 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -8553,6 +8553,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 9f9ca1bf1a8fd..8a9ef7bb7f353 100644 --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -817,4 +817,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 3da63af5ba571..e1cdf6ad52074 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 70b9a2c488d3e..1a35fc6632eea 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 8fa150f7d690e..af913ffef3141 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 44b10c3ef9972..f8a39fca6144b 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -4661,3 +4661,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.substr(0, Name.size() - 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 abd2df301880c..b63b798f86a33 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 f1f25b65fc53f..03d21e6f80a53 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 79aa898e18bfa..622244ed1bfa9 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.cpp +++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -906,41 +906,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" } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits