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