https://github.com/asavonic updated 
https://github.com/llvm/llvm-project/pull/174995

>From 77de5dd7643e400d49dd5162645a0338b1499e95 Mon Sep 17 00:00:00 2001
From: Andrew Savonichev <[email protected]>
Date: Fri, 19 Dec 2025 16:12:39 +0900
Subject: [PATCH 1/4] [clang][LTO] Emit symbols for global inline assembly as
 module flags

ModuleSymbolTable used to get symbols from global inline assembly by
running the target asm parser with a generic CPU and no target flags.

This caused problems for top-level inline assembly where instructions
require a target feature. For example, in global-inline-asm-flags.c we
have PACIB and RETAB instructions that need a +pauth target flag. This
test used to fail with a diagnostic:

    <inline asm>:4:1: error: instruction requires: pauth
        4 | pacib     x30, x27
    <inline asm>:5:1: error: instruction requires: pauth
        5 | retab

The patch resolves this problem by moving assembly parsing to clang,
where we have correct CPU and Features and can initialize asm parser
correctly. Clang now records all symbols and symvers as module flags
with ModFlagBehavior::Append. This ensures that when modules are
linked, these flags are consistent with (merged) inline asm.

This issue was previously discussed in
https://discourse.llvm.org/t/rfc-target-cpu-and-features-for-module-level-inline-assembly/74713,
and in the issue #67698 "LTO scan of module-level inline assembly does not 
respect CPU".
---
 clang/lib/CodeGen/CodeGenModule.cpp           |   8 +
 .../CodeGen/AArch64/global-inline-asm-flags.c |  46 ++++
 .../CodeGen/RISCV/global-inline-asm-flags.c   |  12 +
 llvm/docs/ReleaseNotes.md                     |   5 +
 llvm/include/llvm/Object/ModuleSymbolTable.h  |  11 +
 llvm/lib/Object/ModuleSymbolTable.cpp         | 251 ++++++++++++++----
 .../AArch64/Inputs/global-inline-asm-flags.ll |  32 +++
 .../LTO/AArch64/global-inline-asm-flags.ll    | 105 ++++++++
 .../test/LTO/RISCV/global-inline-asm-flags.ll |  26 ++
 9 files changed, 440 insertions(+), 56 deletions(-)
 create mode 100644 clang/test/CodeGen/AArch64/global-inline-asm-flags.c
 create mode 100644 clang/test/CodeGen/RISCV/global-inline-asm-flags.c
 create mode 100644 llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll
 create mode 100644 llvm/test/LTO/AArch64/global-inline-asm-flags.ll
 create mode 100644 llvm/test/LTO/RISCV/global-inline-asm-flags.ll

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 6a087be3751f0..03e8e1015c4a1 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -55,10 +55,13 @@
 #include "llvm/IR/AttributeMask.h"
 #include "llvm/IR/CallingConv.h"
 #include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IR/ProfileSummary.h"
+#include "llvm/Object/ModuleSymbolTable.h"
+#include "llvm/Object/SymbolicFile.h"
 #include "llvm/ProfileData/InstrProfReader.h"
 #include "llvm/ProfileData/SampleProf.h"
 #include "llvm/Support/ARMBuildAttributes.h"
@@ -1615,6 +1618,11 @@ void CodeGenModule::Release() {
     getModule().addModuleFlag(llvm::Module::Error, "MaxTLSAlign",
                               getContext().getTargetInfo().getMaxTLSAlign());
 
+  // Emit module flags for global inline assembly symbols.
+  llvm::ModuleSymbolTable::EmitModuleFlags(
+      TheModule, getTarget().getTargetOpts().CPU,
+      llvm::join(getTarget().getTargetOpts().Features, ","));
+
   getTargetCodeGenInfo().emitTargetGlobals(*this);
 
   getTargetCodeGenInfo().emitTargetMetadata(*this, MangledDeclNames);
diff --git a/clang/test/CodeGen/AArch64/global-inline-asm-flags.c 
b/clang/test/CodeGen/AArch64/global-inline-asm-flags.c
new file mode 100644
index 0000000000000..432e306b3eac7
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/global-inline-asm-flags.c
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -target-feature +pauth -flto=thin 
-emit-llvm -o - %s | FileCheck %s
+// REQUIRES: aarch64-registered-target
+
+asm (
+    ".text" "\n"
+    ".balign 16" "\n"
+    ".globl foo\n"
+    "foo:\n"
+    "pacib     x30, x27" "\n"
+    "retab" "\n"
+    ".symver foo, foo@VER" "\n"
+    ".symver foo, foo@ANOTHERVER" "\n"
+    ".globl bar\n"
+    "bar:\n"
+    "pacib     x30, x27" "\n"
+    "retab" "\n"
+    ".symver bar, bar@VER" "\n"
+    ".previous" "\n"
+);
+
+// CHECK: module asm ".text"
+// CHECK: module asm ".balign 16"
+// CHECK: module asm ".globl foo"
+// CHECK: module asm "foo:"
+// CHECK: module asm "pacib     x30, x27"
+// CHECK: module asm "retab"
+// CHECK: module asm ".symver foo, foo@VER"
+// CHECK: module asm ".symver foo, foo@ANOTHERVER"
+// CHECK: module asm ".globl bar"
+// CHECK: module asm "bar:"
+// CHECK: module asm "pacib     x30, x27"
+// CHECK: module asm "retab"
+// CHECK: module asm ".symver bar, bar@VER"
+// CHECK: module asm ".previous"
+
+// CHECK: !{{.*}} = !{i32 5, !"global-asm-symbols", ![[SYM:[0-9]+]]}
+// CHECK: ![[SYM]] = !{![[SBAR1:[0-9]+]], ![[SBAR2:[0-9]+]], 
![[SBAR3:[0-9]+]], ![[SFOO1:[0-9]+]], ![[SFOO2:[0-9]+]]}
+// CHECK: ![[SBAR1]] = !{!"bar", i32 2050}
+// CHECK: ![[SBAR2]] = !{!"bar@VER", i32 2050}
+// CHECK: ![[SBAR3]] = !{!"foo@ANOTHERVER", i32 2050}
+// CHECK: ![[SFOO1]] = !{!"foo", i32 2050}
+// CHECK: ![[SFOO2]] = !{!"foo@VER", i32 2050}
+// CHECK: !{{.*}} = !{i32 5, !"global-asm-symvers", ![[SYMVER:[0-9]+]]}
+// CHECK: ![[SYMVER]] = !{![[VFOO:[0-9]+]], ![[VBAR:[0-9]+]]}
+// CHECK: ![[VFOO:[0-9]+]] = !{!"foo", !"foo@VER", !"foo@ANOTHERVER"}
+// CHECK: ![[VBAR:[0-9]+]] = !{!"bar", !"bar@VER"}
diff --git a/clang/test/CodeGen/RISCV/global-inline-asm-flags.c 
b/clang/test/CodeGen/RISCV/global-inline-asm-flags.c
new file mode 100644
index 0000000000000..65fefac4d453a
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/global-inline-asm-flags.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -triple riscv64 -target-feature +zcmp -flto=full -emit-llvm 
-o - %s | FileCheck %s
+// REQUIRES: riscv-registered-target
+
+asm(".globl func; func: cm.mvsa01 s1, s0; ret");
+
+// CHECK: module asm ".globl func; func: cm.mvsa01 s1, s0; ret"
+
+// CHECK: !{{.*}} = !{i32 5, !"global-asm-symbols", ![[SYM:[0-9]+]]}
+// CHECK: ![[SYM]] = !{![[FUNC:[0-9]+]]}
+// CHECK: ![[FUNC]] = !{!"func", i32 2050}
+// CHECK: !{{.*}} = !{i32 5, !"global-asm-symvers", ![[SYMVERS:[0-9]+]]}
+// CHECK: ![[SYMVERS]] = !{}
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 128c19296e75e..310b224129def 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -69,6 +69,11 @@ Changes to the LLVM IR
 * The `"nooutline"` attribute is now writen as `nooutline`. Existing IR and
   bitcode will be automatically updated.
 
+* Added `global-asm-symbols` and `global-asm-symvers` module flags to
+  keep a list of symbols and symvers defined in global inline
+  assembly. This allows LLVM tools to build a symbol table for a
+  module without running AsmParser to find symbols in assembly.
+
 Changes to LLVM infrastructure
 ------------------------------
 
diff --git a/llvm/include/llvm/Object/ModuleSymbolTable.h 
b/llvm/include/llvm/Object/ModuleSymbolTable.h
index 564ce76b3feb1..9b079ff7fb1c3 100644
--- a/llvm/include/llvm/Object/ModuleSymbolTable.h
+++ b/llvm/include/llvm/Object/ModuleSymbolTable.h
@@ -67,6 +67,17 @@ class ModuleSymbolTable {
   LLVM_ABI static void
   CollectAsmSymvers(const Module &M,
                     function_ref<void(StringRef, StringRef)> AsmSymver);
+
+  /// Emit module flags for symbols and symvers defined in global
+  /// inline assembly. If these flags are present, CollectAsmSymbols
+  /// and CollectAsmSymvers will use them instead of trying to parse
+  /// assembly again.
+  ///
+  /// This allows LLVM IR tools to build a symbol table for an IR
+  /// module without knowing exact CPU and Features required to parse
+  /// its global inline assembly.
+  LLVM_ABI static bool EmitModuleFlags(Module &M, StringRef CPU,
+                                       StringRef Features);
 };
 
 } // end namespace llvm
diff --git a/llvm/lib/Object/ModuleSymbolTable.cpp 
b/llvm/lib/Object/ModuleSymbolTable.cpp
index 9442becdb7d33..f05e029725171 100644
--- a/llvm/lib/Object/ModuleSymbolTable.cpp
+++ b/llvm/lib/Object/ModuleSymbolTable.cpp
@@ -15,6 +15,7 @@
 #include "llvm/Object/ModuleSymbolTable.h"
 #include "RecordStreamer.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Constants.h"
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/GlobalAlias.h"
@@ -49,6 +50,24 @@
 using namespace llvm;
 using namespace object;
 
+static void addSpecialSymbols(
+    const Module &M,
+    function_ref<void(StringRef, BasicSymbolRef::Flags)> AsmSymbol) {
+  // In ELF, object code generated for x86-32 and some code models of x86-64 
may
+  // reference the special symbol _GLOBAL_OFFSET_TABLE_ that is not used in the
+  // IR. Record it like inline asm symbols.
+  Triple TT(M.getTargetTriple());
+  if (!TT.isOSBinFormatELF() || !TT.isX86())
+    return;
+  auto CM = M.getCodeModel();
+  if (TT.getArch() == Triple::x86 || CM == CodeModel::Medium ||
+      CM == CodeModel::Large) {
+    AsmSymbol("_GLOBAL_OFFSET_TABLE_",
+              BasicSymbolRef::Flags(BasicSymbolRef::SF_Undefined |
+                                    BasicSymbolRef::SF_Global));
+  }
+}
+
 void ModuleSymbolTable::addModule(Module *M) {
   if (FirstMod)
     assert(FirstMod->getTargetTriple() == M->getTargetTriple());
@@ -58,15 +77,27 @@ void ModuleSymbolTable::addModule(Module *M) {
   for (GlobalValue &GV : M->global_values())
     SymTab.push_back(&GV);
 
-  CollectAsmSymbols(*M, [this](StringRef Name, BasicSymbolRef::Flags Flags) {
+  auto AddSymbols = [this](StringRef Name, BasicSymbolRef::Flags Flags) {
     SymTab.push_back(new (AsmSymbols.Allocate())
                          AsmSymbol(std::string(Name), Flags));
-  });
+  };
+
+  if (M->getModuleInlineAsm().empty()) {
+    addSpecialSymbols(*M, AddSymbols);
+    return;
+  }
+
+  // Make sure that global-asm-symbols is materialized. Otherwise
+  // CollectAsmSymbols falls back to parsing.
+  consumeError(M->materializeMetadata());
+
+  CollectAsmSymbols(*M, AddSymbols);
 }
 
-static void
-initializeRecordStreamer(const Module &M,
-                         function_ref<void(RecordStreamer &)> Init) {
+static void initializeRecordStreamer(
+    const Module &M, StringRef CPU, StringRef Features,
+    function_ref<void(RecordStreamer &)> Init,
+    function_ref<void(const DiagnosticInfo &DI)> DiagHandler) {
   // This function may be called twice, once for ModuleSummaryIndexAnalysis and
   // the other when writing the IR symbol table. If parsing inline assembly has
   // caused errors in the first run, suppress the second run.
@@ -90,7 +121,8 @@ initializeRecordStreamer(const Module &M,
   if (!MAI)
     return;
 
-  std::unique_ptr<MCSubtargetInfo> STI(T->createMCSubtargetInfo(TT, "", ""));
+  std::unique_ptr<MCSubtargetInfo> STI(
+      T->createMCSubtargetInfo(TT, CPU, Features));
   if (!STI)
     return;
 
@@ -121,8 +153,12 @@ initializeRecordStreamer(const Module &M,
   MCCtx.setDiagnosticHandler([&](const SMDiagnostic &SMD, bool IsInlineAsm,
                                  const SourceMgr &SrcMgr,
                                  std::vector<const MDNode *> &LocInfos) {
-    M.getContext().diagnose(
-        DiagnosticInfoSrcMgr(SMD, M.getName(), IsInlineAsm, /*LocCookie=*/0));
+    DiagnosticInfoSrcMgr Diag(SMD, M.getName(), IsInlineAsm, /*LocCookie=*/0);
+    if (DiagHandler) {
+      DiagHandler(Diag);
+      return;
+    }
+    M.getContext().diagnose(Diag);
   });
 
   // Module-level inline asm is assumed to use At&t syntax (see
@@ -136,64 +172,167 @@ initializeRecordStreamer(const Module &M,
   Init(Streamer);
 }
 
+static void
+addSymbols(RecordStreamer &Streamer,
+           function_ref<void(StringRef, BasicSymbolRef::Flags)> AsmSymbol) {
+  Streamer.flushSymverDirectives();
+
+  for (const auto &[Name, State] : Streamer) {
+    // FIXME: For now we just assume that all asm symbols are executable.
+    uint32_t Res = BasicSymbolRef::SF_Executable;
+    switch (State) {
+    case RecordStreamer::NeverSeen:
+      llvm_unreachable("NeverSeen should have been replaced earlier");
+    case RecordStreamer::DefinedGlobal:
+      Res |= BasicSymbolRef::SF_Global;
+      break;
+    case RecordStreamer::Defined:
+      break;
+    case RecordStreamer::Global:
+    case RecordStreamer::Used:
+      Res |= BasicSymbolRef::SF_Undefined;
+      Res |= BasicSymbolRef::SF_Global;
+      break;
+    case RecordStreamer::DefinedWeak:
+      Res |= BasicSymbolRef::SF_Weak;
+      Res |= BasicSymbolRef::SF_Global;
+      break;
+    case RecordStreamer::UndefinedWeak:
+      Res |= BasicSymbolRef::SF_Weak;
+      Res |= BasicSymbolRef::SF_Undefined;
+    }
+    AsmSymbol(Name, BasicSymbolRef::Flags(Res));
+  }
+}
+
 void ModuleSymbolTable::CollectAsmSymbols(
     const Module &M,
     function_ref<void(StringRef, BasicSymbolRef::Flags)> AsmSymbol) {
-  initializeRecordStreamer(M, [&](RecordStreamer &Streamer) {
-    Streamer.flushSymverDirectives();
-
-    for (auto &KV : Streamer) {
-      StringRef Key = KV.first();
-      RecordStreamer::State Value = KV.second;
-      // FIXME: For now we just assume that all asm symbols are executable.
-      uint32_t Res = BasicSymbolRef::SF_Executable;
-      switch (Value) {
-      case RecordStreamer::NeverSeen:
-        llvm_unreachable("NeverSeen should have been replaced earlier");
-      case RecordStreamer::DefinedGlobal:
-        Res |= BasicSymbolRef::SF_Global;
-        break;
-      case RecordStreamer::Defined:
-        break;
-      case RecordStreamer::Global:
-      case RecordStreamer::Used:
-        Res |= BasicSymbolRef::SF_Undefined;
-        Res |= BasicSymbolRef::SF_Global;
-        break;
-      case RecordStreamer::DefinedWeak:
-        Res |= BasicSymbolRef::SF_Weak;
-        Res |= BasicSymbolRef::SF_Global;
-        break;
-      case RecordStreamer::UndefinedWeak:
-        Res |= BasicSymbolRef::SF_Weak;
-        Res |= BasicSymbolRef::SF_Undefined;
-      }
-      AsmSymbol(Key, BasicSymbolRef::Flags(Res));
-    }
-  });
 
-  // In ELF, object code generated for x86-32 and some code models of x86-64 
may
-  // reference the special symbol _GLOBAL_OFFSET_TABLE_ that is not used in the
-  // IR. Record it like inline asm symbols.
-  Triple TT(M.getTargetTriple());
-  if (!TT.isOSBinFormatELF() || !TT.isX86())
+  MDTuple *SymbolsMD =
+      dyn_cast_if_present<MDTuple>(M.getModuleFlag("global-asm-symbols"));
+
+  if (SymbolsMD) {
+    for (const Metadata *MD : SymbolsMD->operands()) {
+      const MDTuple *SymMD = cast<MDTuple>(MD);
+      const MDString *Name = cast<MDString>(SymMD->getOperand(0));
+      const ConstantInt *Flags =
+          mdconst::extract<ConstantInt>(SymMD->getOperand(1));
+      AsmSymbol(Name->getString(),
+                static_cast<BasicSymbolRef::Flags>(Flags->getZExtValue()));
+    }
+    addSpecialSymbols(M, AsmSymbol);
     return;
-  auto CM = M.getCodeModel();
-  if (TT.getArch() == Triple::x86 || CM == CodeModel::Medium ||
-      CM == CodeModel::Large) {
-    AsmSymbol("_GLOBAL_OFFSET_TABLE_",
-              BasicSymbolRef::Flags(BasicSymbolRef::SF_Undefined |
-                                    BasicSymbolRef::SF_Global));
   }
+
+  initializeRecordStreamer(
+      M, /*CPU=*/"", /*Features=*/"",
+      [&](RecordStreamer &Streamer) { addSymbols(Streamer, AsmSymbol); },
+      /*DiagHandler=*/nullptr);
+
+  addSpecialSymbols(M, AsmSymbol);
+}
+
+static void addSymvers(RecordStreamer &Streamer,
+                       function_ref<void(StringRef, StringRef)> AsmSymver) {
+  for (const auto &[Name, Aliases] : Streamer.symverAliases())
+    for (StringRef Alias : Aliases)
+      AsmSymver(Name->getName(), Alias);
 }
 
 void ModuleSymbolTable::CollectAsmSymvers(
     const Module &M, function_ref<void(StringRef, StringRef)> AsmSymver) {
-  initializeRecordStreamer(M, [&](RecordStreamer &Streamer) {
-    for (auto &KV : Streamer.symverAliases())
-      for (auto &Alias : KV.second)
-        AsmSymver(KV.first->getName(), Alias);
-  });
+
+  MDTuple *SymversMD =
+      dyn_cast_if_present<MDTuple>(M.getModuleFlag("global-asm-symvers"));
+
+  if (!SymversMD) {
+    initializeRecordStreamer(
+        M, /*CPU=*/"", /*Features=*/"",
+        [&](RecordStreamer &Streamer) { addSymvers(Streamer, AsmSymver); },
+        /*DiagHandler=*/nullptr);
+    return;
+  }
+
+  for (const Metadata *MD : SymversMD->operands()) {
+    const MDTuple *SymverMD = cast<MDTuple>(MD);
+    StringRef Name = cast<MDString>(SymverMD->getOperand(0))->getString();
+    for (size_t Idx = 1, End = SymverMD->getNumOperands(); Idx < End; ++Idx)
+      AsmSymver(Name, cast<MDString>(SymverMD->getOperand(Idx))->getString());
+  }
+}
+
+bool ModuleSymbolTable::EmitModuleFlags(Module &M, StringRef CPU,
+                                        StringRef Features) {
+  if (M.getModuleInlineAsm().empty())
+    return false;
+
+  llvm::LLVMContext &Ctx = M.getContext();
+
+  bool HaveErrors = false;
+  auto DiagHandler = [&](const llvm::DiagnosticInfo &DI) {
+    // Ignore diagnostics from the assembly parser.
+    //
+    // Errors in assembly mean that we cannot build a symbol table
+    // from it. However, we do not diagnose them here, because we
+    // don't know if the Module is ever going to actually reach
+    // CodeGen where this would matter.
+    if (DI.getSeverity() == llvm::DS_Error)
+      HaveErrors = true;
+  };
+
+  // Build global-asm-symbols as a list of pairs (name, flags bitmask).
+  SmallVector<llvm::Metadata *, 16> Symbols;
+
+  auto AsmSymbol = [&](StringRef Name,
+                       llvm::object::BasicSymbolRef::Flags Flags) {
+    Symbols.push_back(llvm::MDNode::get(
+        Ctx, {llvm::MDString::get(Ctx, Name),
+              llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
+                  llvm::Type::getInt32Ty(Ctx), Flags))}));
+  };
+
+  // Build global-asm-symvers as a list of lists (name, followed by all
+  // aliases).
+  llvm::MapVector<StringRef, SmallVector<llvm::Metadata *, 2>> SymversMap;
+
+  auto AsmSymver = [&](StringRef Name, StringRef Alias) {
+    auto ItNew = SymversMap.try_emplace(Name);
+    SmallVector<llvm::Metadata *, 2> &Aliases = ItNew.first->second;
+
+    // If it is a new list, insert the primary name at the front.
+    if (ItNew.second)
+      Aliases.push_back(llvm::MDString::get(Ctx, Name));
+
+    Aliases.push_back(llvm::MDString::get(Ctx, Alias));
+  };
+
+  // Parse global inline assembly and collect all symbols and symvers.
+  initializeRecordStreamer(
+      M, CPU, Features,
+      [&](RecordStreamer &Streamer) {
+        addSymvers(Streamer, AsmSymver);
+        addSymbols(Streamer, AsmSymbol);
+      },
+      DiagHandler);
+
+  if (HaveErrors)
+    return false;
+
+  // Emit a symbol table as module flags, so they can be traversed
+  // later with CollectAsmSymbols and CollectAsmSymvers.
+  M.addModuleFlag(llvm::Module::Append, "global-asm-symbols",
+                  llvm::MDNode::get(Ctx, Symbols));
+
+  SmallVector<llvm::Metadata *, 16> Symvers;
+  Symvers.reserve(SymversMap.size());
+  for (const auto &KV : SymversMap)
+    Symvers.push_back(llvm::MDNode::get(Ctx, KV.second));
+
+  M.addModuleFlag(llvm::Module::Append, "global-asm-symvers",
+                  llvm::MDNode::get(Ctx, Symvers));
+
+  return true;
 }
 
 void ModuleSymbolTable::printSymbolName(raw_ostream &OS, Symbol S) const {
diff --git a/llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll 
b/llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll
new file mode 100644
index 0000000000000..137a7b8ec24ea
--- /dev/null
+++ b/llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll
@@ -0,0 +1,32 @@
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-unknown-linux-gnu"
+
+module asm ".text"
+module asm ".balign 16"
+module asm ".globl foo"
+module asm "foo:"
+module asm "pacib     x30, x27"
+module asm "retab"
+module asm ".symver foo, foo@VER"
+module asm ".symver foo, foo@ANOTHERVER"
+module asm ".globl bar"
+module asm "bar:"
+module asm "pacib     x30, x27"
+module asm "retab"
+module asm ".symver bar, bar@VER"
+module asm ".previous"
+
+!llvm.module.flags = !{!1, !8}
+
+!1 = !{i32 5, !"global-asm-symbols", !2}
+!2 = !{!3, !4, !5, !6, !7}
+!3 = !{!"bar", i32 2050}
+!4 = !{!"bar@VER", i32 2050}
+!5 = !{!"foo@ANOTHERVER", i32 2050}
+!6 = !{!"foo", i32 2050}
+!7 = !{!"foo@VER", i32 2050}
+!8 = !{i32 5, !"global-asm-symvers", !9}
+!9 = !{!10, !11}
+!10 = !{!"foo", !"foo@VER", !"foo@ANOTHERVER"}
+!11 = !{!"bar", !"bar@VER"}
+
diff --git a/llvm/test/LTO/AArch64/global-inline-asm-flags.ll 
b/llvm/test/LTO/AArch64/global-inline-asm-flags.ll
new file mode 100644
index 0000000000000..269f2a6020314
--- /dev/null
+++ b/llvm/test/LTO/AArch64/global-inline-asm-flags.ll
@@ -0,0 +1,105 @@
+; RUN: llvm-as %s -o %t1.bc
+; RUN: llvm-as %p/Inputs/global-inline-asm-flags.ll -o %t2.bc
+
+; RUN: llvm-lto -save-merged-module -filetype=asm -mattr=+pauth %t1.bc %t2.bc 
-o %t3
+; RUN: llvm-dis %t3.merged.bc -o - | FileCheck %s
+
+; RUN: llvm-lto2 run -save-temps -mattr=+pauth -filetype=asm -o %t4.s %t1.bc 
%t2.bc \
+; RUN:   -r=%t1.bc,baz,p \
+; RUN:   -r=%t1.bc,baz@VER,p \
+; RUN:   -r=%t1.bc,foo@LINKEDVER,p \
+; RUN:   -r=%t2.bc,bar,p \
+; RUN:   -r=%t2.bc,bar@VER,p \
+; RUN:   -r=%t2.bc,foo@ANOTHERVER,p \
+; RUN:   -r=%t2.bc,foo,p \
+; RUN:   -r=%t2.bc,foo@VER,p
+; RUN: llvm-dis %t4.s.0.5.precodegen.bc -o - | FileCheck %s
+
+; Note that -mattr=+pauth option for llvm-lto and llvm-lto2 is still
+; required, because LTO runs full CodeGen at the end. Symbols and
+; Symvers are still extracted from metadata.
+
+; RUN: llvm-nm %t1.bc | FileCheck %s --check-prefix NM1
+; RUN: llvm-nm %t2.bc | FileCheck %s --check-prefix NM2
+; RUN: llvm-nm %t3.merged.bc | FileCheck %s --check-prefixes NM1,NM2
+; RUN: llvm-nm %t4.s.0.5.precodegen.bc | FileCheck %s --check-prefixes NM1,NM2
+
+; Symbols of the first module
+; NM1-DAG: T baz
+; NM1-DAG: T baz@VER
+; NM1-DAG: T foo@LINKEDVER
+
+; Symbols of the second module
+; NM2-DAG: T bar
+; NM2-DAG: T bar@VER
+; NM2-DAG: T foo
+; NM2-DAG: T foo@ANOTHERVER
+; NM2-DAG: T foo@VER
+
+; IR with two modules linked
+; CHECK: module asm ".text"
+; CHECK: module asm ".balign 16"
+; CHECK: module asm ".globl baz"
+; CHECK: module asm "baz:"
+; CHECK: module asm "pacib     x30, x27"
+; CHECK: module asm "retab"
+; CHECK: module asm ".symver baz, baz@VER"
+; CHECK: module asm ".symver foo, foo@LINKEDVER"
+; CHECK: module asm ".previous"
+; CHECK: module asm ".text"
+; CHECK: module asm ".balign 16"
+; CHECK: module asm ".globl foo"
+; CHECK: module asm "foo:"
+; CHECK: module asm "pacib     x30, x27"
+; CHECK: module asm "retab"
+; CHECK: module asm ".symver foo, foo@VER"
+; CHECK: module asm ".symver foo, foo@ANOTHERVER"
+; CHECK: module asm ".globl bar"
+; CHECK: module asm "bar:"
+; CHECK: module asm "pacib     x30, x27"
+; CHECK: module asm "retab"
+; CHECK: module asm ".symver bar, bar@VER"
+; CHECK: module asm ".previous"
+
+; CHECK: !{{[0-9]+}} = distinct !{i32 5, !"global-asm-symbols", 
![[SYM:[0-9]+]]}
+; CHECK: ![[SYM]] = distinct !{![[SBAZ1:[0-9]+]], ![[SBAZ2:[0-9]+]], 
![[SFOO1:[0-9]+]], ![[SBAR1:[0-9]+]], ![[SBAR2:[0-9]+]], ![[SFOO2:[0-9]+]], 
![[SFOO3:[0-9]+]], ![[SFOO4:[0-9]+]]}
+; CHECK: ![[SBAZ1]] = !{!"baz", i32 2050}
+; CHECK: ![[SBAZ2]] = !{!"baz@VER", i32 2050}
+; CHECK: ![[SFOO1]] = !{!"foo@LINKEDVER", i32 2050}
+; CHECK: ![[SBAR1]] = !{!"bar", i32 2050}
+; CHECK: ![[SBAR2]] = !{!"bar@VER", i32 2050}
+; CHECK: ![[SFOO2]] = !{!"foo@ANOTHERVER", i32 2050}
+; CHECK: ![[SFOO3]] = !{!"foo", i32 2050}
+; CHECK: ![[SFOO4]] = !{!"foo@VER", i32 2050}
+
+; CHECK: !{{[0-9]+}} = distinct !{i32 5, !"global-asm-symvers", 
![[SYMVER:[0-9]+]]}
+; CHECK: ![[SYMVER]] = distinct !{![[VBAZ:[0-9]+]], ![[VFOO1:[0-9]+]], 
![[VFOO2:[0-9]+]], ![[VBAR:[0-9]+]]}
+; CHECK: ![[VBAZ]] = !{!"baz", !"baz@VER"}
+; CHECK: ![[VFOO1]] = !{!"foo", !"foo@LINKEDVER"}
+; CHECK: ![[VFOO2]] = !{!"foo", !"foo@VER", !"foo@ANOTHERVER"}
+; CHECK: ![[VBAR]] = !{!"bar", !"bar@VER"}
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-unknown-linux-gnu"
+
+module asm ".text"
+module asm ".balign 16"
+module asm ".globl baz"
+module asm "baz:"
+module asm "pacib     x30, x27"
+module asm "retab"
+module asm ".symver baz, baz@VER"
+module asm ".symver foo, foo@LINKEDVER"
+module asm ".previous"
+
+!llvm.module.flags = !{!0, !5}
+
+!0 = !{i32 5, !"global-asm-symbols", !1}
+!1 = !{!2, !3, !4}
+!2 = !{!"baz", i32 2050}
+!3 = !{!"baz@VER", i32 2050}
+!4 = !{!"foo@LINKEDVER", i32 2050}
+!5 = !{i32 5, !"global-asm-symvers", !6}
+!6 = !{!7, !8}
+!7 = !{!"baz", !"baz@VER"}
+!8 = !{!"foo", !"foo@LINKEDVER"}
diff --git a/llvm/test/LTO/RISCV/global-inline-asm-flags.ll 
b/llvm/test/LTO/RISCV/global-inline-asm-flags.ll
new file mode 100644
index 0000000000000..94c1801ac9ab2
--- /dev/null
+++ b/llvm/test/LTO/RISCV/global-inline-asm-flags.ll
@@ -0,0 +1,26 @@
+; RUN: llvm-as %s -o %t.o
+; RUN: llvm-lto2 run -mattr=+zcmp -filetype=asm -o %t.s %t.o -r=%t.o,func
+; RUN: llvm-nm %t.o | FileCheck %s --check-prefix NM
+
+; NM: T func
+
+; CHECK:      cm.mvsa01 s1, s0
+; CHECK-NEXT: ret
+
+
+target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
+target triple = "riscv64"
+
+module asm ".globl func; func: cm.mvsa01 s1, s0; ret"
+
+!llvm.module.flags = !{!0, !1, !2, !4, !7}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"target-abi", !"lp64"}
+!2 = !{i32 6, !"riscv-isa", !3}
+!3 = !{!"rv64i2p1_c2p0_zca1p0_zcmp1p0"}
+!4 = !{i32 5, !"global-asm-symbols", !5}
+!5 = !{!6}
+!6 = !{!"func", i32 2050}
+!7 = !{i32 5, !"global-asm-symvers", !8}
+!8 = !{}

>From 8f19c09960bb628ece701eeb6e2701d18f03c9a3 Mon Sep 17 00:00:00 2001
From: Andrew Savonichev <[email protected]>
Date: Thu, 5 Feb 2026 20:59:50 +0900
Subject: [PATCH 2/4] Change ModFlagBehavior to AppendUnique and update tests

---
 clang/test/CodeGen/AArch64/global-inline-asm-flags.c    | 4 ++--
 clang/test/CodeGen/RISCV/global-inline-asm-flags.c      | 4 ++--
 llvm/lib/Object/ModuleSymbolTable.cpp                   | 4 ++--
 llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll | 4 ++--
 llvm/test/LTO/AArch64/global-inline-asm-flags.ll        | 8 ++++----
 llvm/test/LTO/RISCV/global-inline-asm-flags.ll          | 4 ++--
 6 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/clang/test/CodeGen/AArch64/global-inline-asm-flags.c 
b/clang/test/CodeGen/AArch64/global-inline-asm-flags.c
index 432e306b3eac7..3160a91e1911a 100644
--- a/clang/test/CodeGen/AArch64/global-inline-asm-flags.c
+++ b/clang/test/CodeGen/AArch64/global-inline-asm-flags.c
@@ -33,14 +33,14 @@ asm (
 // CHECK: module asm ".symver bar, bar@VER"
 // CHECK: module asm ".previous"
 
-// CHECK: !{{.*}} = !{i32 5, !"global-asm-symbols", ![[SYM:[0-9]+]]}
+// CHECK: !{{.*}} = !{i32 6, !"global-asm-symbols", ![[SYM:[0-9]+]]}
 // CHECK: ![[SYM]] = !{![[SBAR1:[0-9]+]], ![[SBAR2:[0-9]+]], 
![[SBAR3:[0-9]+]], ![[SFOO1:[0-9]+]], ![[SFOO2:[0-9]+]]}
 // CHECK: ![[SBAR1]] = !{!"bar", i32 2050}
 // CHECK: ![[SBAR2]] = !{!"bar@VER", i32 2050}
 // CHECK: ![[SBAR3]] = !{!"foo@ANOTHERVER", i32 2050}
 // CHECK: ![[SFOO1]] = !{!"foo", i32 2050}
 // CHECK: ![[SFOO2]] = !{!"foo@VER", i32 2050}
-// CHECK: !{{.*}} = !{i32 5, !"global-asm-symvers", ![[SYMVER:[0-9]+]]}
+// CHECK: !{{.*}} = !{i32 6, !"global-asm-symvers", ![[SYMVER:[0-9]+]]}
 // CHECK: ![[SYMVER]] = !{![[VFOO:[0-9]+]], ![[VBAR:[0-9]+]]}
 // CHECK: ![[VFOO:[0-9]+]] = !{!"foo", !"foo@VER", !"foo@ANOTHERVER"}
 // CHECK: ![[VBAR:[0-9]+]] = !{!"bar", !"bar@VER"}
diff --git a/clang/test/CodeGen/RISCV/global-inline-asm-flags.c 
b/clang/test/CodeGen/RISCV/global-inline-asm-flags.c
index 65fefac4d453a..3572c79e45608 100644
--- a/clang/test/CodeGen/RISCV/global-inline-asm-flags.c
+++ b/clang/test/CodeGen/RISCV/global-inline-asm-flags.c
@@ -5,8 +5,8 @@ asm(".globl func; func: cm.mvsa01 s1, s0; ret");
 
 // CHECK: module asm ".globl func; func: cm.mvsa01 s1, s0; ret"
 
-// CHECK: !{{.*}} = !{i32 5, !"global-asm-symbols", ![[SYM:[0-9]+]]}
+// CHECK: !{{.*}} = !{i32 6, !"global-asm-symbols", ![[SYM:[0-9]+]]}
 // CHECK: ![[SYM]] = !{![[FUNC:[0-9]+]]}
 // CHECK: ![[FUNC]] = !{!"func", i32 2050}
-// CHECK: !{{.*}} = !{i32 5, !"global-asm-symvers", ![[SYMVERS:[0-9]+]]}
+// CHECK: !{{.*}} = !{i32 6, !"global-asm-symvers", ![[SYMVERS:[0-9]+]]}
 // CHECK: ![[SYMVERS]] = !{}
diff --git a/llvm/lib/Object/ModuleSymbolTable.cpp 
b/llvm/lib/Object/ModuleSymbolTable.cpp
index f05e029725171..6dfd31f23c780 100644
--- a/llvm/lib/Object/ModuleSymbolTable.cpp
+++ b/llvm/lib/Object/ModuleSymbolTable.cpp
@@ -321,7 +321,7 @@ bool ModuleSymbolTable::EmitModuleFlags(Module &M, 
StringRef CPU,
 
   // Emit a symbol table as module flags, so they can be traversed
   // later with CollectAsmSymbols and CollectAsmSymvers.
-  M.addModuleFlag(llvm::Module::Append, "global-asm-symbols",
+  M.addModuleFlag(llvm::Module::AppendUnique, "global-asm-symbols",
                   llvm::MDNode::get(Ctx, Symbols));
 
   SmallVector<llvm::Metadata *, 16> Symvers;
@@ -329,7 +329,7 @@ bool ModuleSymbolTable::EmitModuleFlags(Module &M, 
StringRef CPU,
   for (const auto &KV : SymversMap)
     Symvers.push_back(llvm::MDNode::get(Ctx, KV.second));
 
-  M.addModuleFlag(llvm::Module::Append, "global-asm-symvers",
+  M.addModuleFlag(llvm::Module::AppendUnique, "global-asm-symvers",
                   llvm::MDNode::get(Ctx, Symvers));
 
   return true;
diff --git a/llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll 
b/llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll
index 137a7b8ec24ea..32e89dd1d128e 100644
--- a/llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll
+++ b/llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll
@@ -18,14 +18,14 @@ module asm ".previous"
 
 !llvm.module.flags = !{!1, !8}
 
-!1 = !{i32 5, !"global-asm-symbols", !2}
+!1 = !{i32 6, !"global-asm-symbols", !2}
 !2 = !{!3, !4, !5, !6, !7}
 !3 = !{!"bar", i32 2050}
 !4 = !{!"bar@VER", i32 2050}
 !5 = !{!"foo@ANOTHERVER", i32 2050}
 !6 = !{!"foo", i32 2050}
 !7 = !{!"foo@VER", i32 2050}
-!8 = !{i32 5, !"global-asm-symvers", !9}
+!8 = !{i32 6, !"global-asm-symvers", !9}
 !9 = !{!10, !11}
 !10 = !{!"foo", !"foo@VER", !"foo@ANOTHERVER"}
 !11 = !{!"bar", !"bar@VER"}
diff --git a/llvm/test/LTO/AArch64/global-inline-asm-flags.ll 
b/llvm/test/LTO/AArch64/global-inline-asm-flags.ll
index 269f2a6020314..d95957dea17d0 100644
--- a/llvm/test/LTO/AArch64/global-inline-asm-flags.ll
+++ b/llvm/test/LTO/AArch64/global-inline-asm-flags.ll
@@ -61,7 +61,7 @@
 ; CHECK: module asm ".symver bar, bar@VER"
 ; CHECK: module asm ".previous"
 
-; CHECK: !{{[0-9]+}} = distinct !{i32 5, !"global-asm-symbols", 
![[SYM:[0-9]+]]}
+; CHECK: !{{[0-9]+}} = distinct !{i32 6, !"global-asm-symbols", 
![[SYM:[0-9]+]]}
 ; CHECK: ![[SYM]] = distinct !{![[SBAZ1:[0-9]+]], ![[SBAZ2:[0-9]+]], 
![[SFOO1:[0-9]+]], ![[SBAR1:[0-9]+]], ![[SBAR2:[0-9]+]], ![[SFOO2:[0-9]+]], 
![[SFOO3:[0-9]+]], ![[SFOO4:[0-9]+]]}
 ; CHECK: ![[SBAZ1]] = !{!"baz", i32 2050}
 ; CHECK: ![[SBAZ2]] = !{!"baz@VER", i32 2050}
@@ -72,7 +72,7 @@
 ; CHECK: ![[SFOO3]] = !{!"foo", i32 2050}
 ; CHECK: ![[SFOO4]] = !{!"foo@VER", i32 2050}
 
-; CHECK: !{{[0-9]+}} = distinct !{i32 5, !"global-asm-symvers", 
![[SYMVER:[0-9]+]]}
+; CHECK: !{{[0-9]+}} = distinct !{i32 6, !"global-asm-symvers", 
![[SYMVER:[0-9]+]]}
 ; CHECK: ![[SYMVER]] = distinct !{![[VBAZ:[0-9]+]], ![[VFOO1:[0-9]+]], 
![[VFOO2:[0-9]+]], ![[VBAR:[0-9]+]]}
 ; CHECK: ![[VBAZ]] = !{!"baz", !"baz@VER"}
 ; CHECK: ![[VFOO1]] = !{!"foo", !"foo@LINKEDVER"}
@@ -94,12 +94,12 @@ module asm ".previous"
 
 !llvm.module.flags = !{!0, !5}
 
-!0 = !{i32 5, !"global-asm-symbols", !1}
+!0 = !{i32 6, !"global-asm-symbols", !1}
 !1 = !{!2, !3, !4}
 !2 = !{!"baz", i32 2050}
 !3 = !{!"baz@VER", i32 2050}
 !4 = !{!"foo@LINKEDVER", i32 2050}
-!5 = !{i32 5, !"global-asm-symvers", !6}
+!5 = !{i32 6, !"global-asm-symvers", !6}
 !6 = !{!7, !8}
 !7 = !{!"baz", !"baz@VER"}
 !8 = !{!"foo", !"foo@LINKEDVER"}
diff --git a/llvm/test/LTO/RISCV/global-inline-asm-flags.ll 
b/llvm/test/LTO/RISCV/global-inline-asm-flags.ll
index 94c1801ac9ab2..4f1d5987ec996 100644
--- a/llvm/test/LTO/RISCV/global-inline-asm-flags.ll
+++ b/llvm/test/LTO/RISCV/global-inline-asm-flags.ll
@@ -19,8 +19,8 @@ module asm ".globl func; func: cm.mvsa01 s1, s0; ret"
 !1 = !{i32 1, !"target-abi", !"lp64"}
 !2 = !{i32 6, !"riscv-isa", !3}
 !3 = !{!"rv64i2p1_c2p0_zca1p0_zcmp1p0"}
-!4 = !{i32 5, !"global-asm-symbols", !5}
+!4 = !{i32 6, !"global-asm-symbols", !5}
 !5 = !{!6}
 !6 = !{!"func", i32 2050}
-!7 = !{i32 5, !"global-asm-symvers", !8}
+!7 = !{i32 6, !"global-asm-symvers", !8}
 !8 = !{}

>From ab6669eda50ac88455137d4709cbb9d063781cb2 Mon Sep 17 00:00:00 2001
From: Andrew Savonichev <[email protected]>
Date: Fri, 6 Feb 2026 21:59:23 +0900
Subject: [PATCH 3/4] De-duplicate symbols and symvers in ModuleSymbolTable and
 LTO

---
 llvm/lib/LTO/LTO.cpp                          |  32 ++++
 llvm/lib/Object/ModuleSymbolTable.cpp         | 141 +++++++++++++++---
 llvm/test/LTO/AArch64/Inputs/asm-bar1.ll      |  22 +++
 llvm/test/LTO/AArch64/Inputs/asm-foo-pauth.ll |  21 +++
 llvm/test/LTO/AArch64/Inputs/asm-foo1.ll      |  19 +++
 .../AArch64/Inputs/global-inline-asm-flags.ll |  32 ----
 .../global-inline-asm-flags-pauth.test        |  93 ++++++++++++
 .../LTO/AArch64/global-inline-asm-flags.ll    | 105 -------------
 .../LTO/AArch64/global-inline-asm-flags.test  | 125 ++++++++++++++++
 .../test/LTO/RISCV/global-inline-asm-flags.ll |   4 +-
 llvm/test/Object/Inputs/asm-bar-ver-def.ll    |  13 ++
 llvm/test/Object/Inputs/asm-foo-def-ir.ll     |   4 +
 llvm/test/Object/Inputs/asm-foo-ver-def.ll    |  13 ++
 llvm/test/Object/Inputs/asm-foo-ver-undef.ll  |  13 ++
 llvm/test/Object/Inputs/asm-foo.ll            |  11 ++
 llvm/test/Object/global-inline-asm.test       | 139 +++++++++++++++++
 16 files changed, 630 insertions(+), 157 deletions(-)
 create mode 100644 llvm/test/LTO/AArch64/Inputs/asm-bar1.ll
 create mode 100644 llvm/test/LTO/AArch64/Inputs/asm-foo-pauth.ll
 create mode 100644 llvm/test/LTO/AArch64/Inputs/asm-foo1.ll
 delete mode 100644 llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll
 create mode 100644 llvm/test/LTO/AArch64/global-inline-asm-flags-pauth.test
 delete mode 100644 llvm/test/LTO/AArch64/global-inline-asm-flags.ll
 create mode 100644 llvm/test/LTO/AArch64/global-inline-asm-flags.test
 create mode 100644 llvm/test/Object/Inputs/asm-bar-ver-def.ll
 create mode 100644 llvm/test/Object/Inputs/asm-foo-def-ir.ll
 create mode 100644 llvm/test/Object/Inputs/asm-foo-ver-def.ll
 create mode 100644 llvm/test/Object/Inputs/asm-foo-ver-undef.ll
 create mode 100644 llvm/test/Object/Inputs/asm-foo.ll
 create mode 100644 llvm/test/Object/global-inline-asm.test

diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 749af6eda3fdb..ee14d52aef4fd 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -877,6 +877,37 @@ handleNonPrevailingComdat(GlobalValue &GV,
     GO->setComdat(nullptr);
 }
 
+// Update global-asm-symbols metadata to match inline asm with .lto_discard.
+static void
+discardGlobalAsmSymbols(Module &M,
+                        const SmallSet<StringRef, 2> &DiscardSymbols) {
+  MDTuple *SymbolsMD =
+      dyn_cast_if_present<MDTuple>(M.getModuleFlag("global-asm-symbols"));
+
+  if (!SymbolsMD)
+    return;
+
+  bool Changed = false;
+  SmallVector<Metadata *, 16> NewSymbols;
+  for (Metadata *MD : SymbolsMD->operands()) {
+    const MDTuple *SymMD = cast<MDTuple>(MD);
+    const MDString *Name = cast<MDString>(SymMD->getOperand(0));
+
+    if (DiscardSymbols.contains(Name->getString())) {
+      Changed = true;
+      continue;
+    }
+
+    NewSymbols.push_back(MD);
+  }
+
+  if (!Changed)
+    return;
+
+  M.setModuleFlag(llvm::Module::AppendUnique, "global-asm-symbols",
+                  llvm::MDNode::get(M.getContext(), NewSymbols));
+}
+
 // Add a regular LTO object to the link.
 // The resulting module needs to be linked into the combined LTO module with
 // linkRegularLTO.
@@ -1049,6 +1080,7 @@ LTO::addRegularLTO(InputFile &Input, 
ArrayRef<SymbolResolution> InputRes,
               NonPrevailingAsmSymbols.erase(Name);
           });
       NewIA += " " + llvm::join(NonPrevailingAsmSymbols, ", ");
+      discardGlobalAsmSymbols(M, NonPrevailingAsmSymbols);
     }
     NewIA += "\n";
     M.setModuleInlineAsm(NewIA + M.getModuleInlineAsm());
diff --git a/llvm/lib/Object/ModuleSymbolTable.cpp 
b/llvm/lib/Object/ModuleSymbolTable.cpp
index 6dfd31f23c780..685ddd524554d 100644
--- a/llvm/lib/Object/ModuleSymbolTable.cpp
+++ b/llvm/lib/Object/ModuleSymbolTable.cpp
@@ -14,6 +14,8 @@
 
 #include "llvm/Object/ModuleSymbolTable.h"
 #include "RecordStreamer.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DiagnosticInfo.h"
@@ -42,6 +44,7 @@
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TargetParser/Triple.h"
+#include <algorithm>
 #include <cassert>
 #include <cstdint>
 #include <memory>
@@ -205,6 +208,95 @@ addSymbols(RecordStreamer &Streamer,
   }
 }
 
+static void collectAsmSymbolsFromMetadata(
+    const Module &M, MDTuple *SymbolsMD, MDTuple *SymversMD,
+    function_ref<void(StringRef, BasicSymbolRef::Flags)> AsmSymbol) {
+
+  // Extract all symbols from global-asm-symbols module flag. With AppendUnique
+  // ModFlagBehavior there may only be symbols with unique Name and Flags
+  // combination.
+  MapVector<StringRef, SmallVector<BasicSymbolRef::Flags, 2>> Symbols;
+  for (const Metadata *MD : SymbolsMD->operands()) {
+    const MDTuple *SymMD = cast<MDTuple>(MD);
+    const MDString *Name = cast<MDString>(SymMD->getOperand(0));
+    const ConstantInt *Flags =
+        mdconst::extract<ConstantInt>(SymMD->getOperand(1));
+
+    // Collect symbols with the same name, but different flags.
+    Symbols[Name->getString()].push_back(
+        static_cast<BasicSymbolRef::Flags>(Flags->getZExtValue()));
+  }
+
+  // If the same symbol is duplicated with different flags, assume that it can
+  // be redefined and select the most accurate set of flags. If it actually
+  // cannot be redefined, AsmParser will diagnose this later.
+  auto LessFn = [](const BasicSymbolRef::Flags &LHS,
+                   const BasicSymbolRef::Flags &RHS) {
+    // Select defined symbols instead of undefined ones.
+    if (LHS & BasicSymbolRef::SF_Undefined)
+      return false;
+
+    // Return true if LHS is "better" (more defined) than RHS.
+    return true;
+  };
+  // Find the "best" set of flags and put it to the front. Ignore the rest.
+  for (auto &[Name, Flags] : Symbols) {
+    auto FlagsIt = std::min_element(Flags.begin(), Flags.end(), LessFn);
+    Flags[0] = *FlagsIt;
+  }
+
+  // Promote symver symbols from SF_Undefined when the corresponding aliasee
+  // symbol is defined either in assembly, or in IR.
+  MapVector<StringRef, StringRef> SymverToSym;
+  for (const Metadata *MD : SymversMD->operands()) {
+    const MDTuple *SymverMD = cast<MDTuple>(MD);
+
+    StringRef AliaseeName =
+        cast<MDString>(SymverMD->getOperand(0))->getString();
+    auto AliaseeIt = Symbols.find(AliaseeName);
+
+    // Aliasee symbols should normally be emitted along with its symver. Bail
+    // out if it was dropped for some reason.
+    if (AliaseeIt == Symbols.end())
+      continue;
+
+    BasicSymbolRef::Flags AliaseeFlag = AliaseeIt->second[0];
+
+    // Iterate over symvers (aliases) of the aliasee.
+    for (size_t Idx = 1, End = SymverMD->getNumOperands(); Idx < End; ++Idx) {
+      StringRef SymverName =
+          cast<MDString>(SymverMD->getOperand(Idx))->getString();
+
+      auto SymverIt = Symbols.find(SymverName);
+      if (SymverIt == Symbols.end())
+        continue;
+
+      BasicSymbolRef::Flags SymverFlag = SymverIt->second[0];
+      BasicSymbolRef::Flags SymverFlagDefined =
+          static_cast<BasicSymbolRef::Flags>(SymverFlag &
+                                             (~BasicSymbolRef::SF_Undefined));
+
+      if (SymverFlag == SymverFlagDefined)
+        continue;
+
+      // If the aliasee is defined - define the symver.
+      if (!(AliaseeFlag & BasicSymbolRef::SF_Undefined)) {
+        SymverIt->second[0] = SymverFlagDefined;
+        continue;
+      }
+
+      // If aliasee is defined in IR - define the symver.
+      if (const GlobalValue *GV = M.getNamedValue(AliaseeName)) {
+        if (!GV->isDeclarationForLinker())
+          SymverIt->second[0] = SymverFlagDefined;
+      }
+    }
+  }
+
+  for (auto &[Name, Flags] : Symbols)
+    AsmSymbol(Name, Flags[0]);
+}
+
 void ModuleSymbolTable::CollectAsmSymbols(
     const Module &M,
     function_ref<void(StringRef, BasicSymbolRef::Flags)> AsmSymbol) {
@@ -213,14 +305,8 @@ void ModuleSymbolTable::CollectAsmSymbols(
       dyn_cast_if_present<MDTuple>(M.getModuleFlag("global-asm-symbols"));
 
   if (SymbolsMD) {
-    for (const Metadata *MD : SymbolsMD->operands()) {
-      const MDTuple *SymMD = cast<MDTuple>(MD);
-      const MDString *Name = cast<MDString>(SymMD->getOperand(0));
-      const ConstantInt *Flags =
-          mdconst::extract<ConstantInt>(SymMD->getOperand(1));
-      AsmSymbol(Name->getString(),
-                static_cast<BasicSymbolRef::Flags>(Flags->getZExtValue()));
-    }
+    MDTuple *SymversMD = cast<MDTuple>(M.getModuleFlag("global-asm-symvers"));
+    collectAsmSymbolsFromMetadata(M, SymbolsMD, SymversMD, AsmSymbol);
     addSpecialSymbols(M, AsmSymbol);
     return;
   }
@@ -240,26 +326,43 @@ static void addSymvers(RecordStreamer &Streamer,
       AsmSymver(Name->getName(), Alias);
 }
 
+static void collectAsmSymversFromMetadata(
+    MDTuple *SymversMD, function_ref<void(StringRef, StringRef)> AsmSymver) {
+
+  // Extract all symvers from global-asm-symvers module flag. These are stored
+  // as lists of [symbol, symver1, ..., symverN], so they may not be fully
+  // uniqued by AppendUnique ModFlagBehavior.
+  MapVector<StringRef, SmallSet<StringRef, 4>> Symvers;
+  for (const Metadata *MD : SymversMD->operands()) {
+    const MDTuple *SymverMD = cast<MDTuple>(MD);
+    StringRef Name = cast<MDString>(SymverMD->getOperand(0))->getString();
+    for (size_t Idx = 1, End = SymverMD->getNumOperands(); Idx < End; ++Idx) {
+      Symvers[Name].insert(
+          cast<MDString>(SymverMD->getOperand(Idx))->getString());
+    }
+  }
+
+  for (const auto &[Name, Aliases] : Symvers) {
+    for (StringRef Alias : Aliases)
+      AsmSymver(Name, Alias);
+  }
+}
+
 void ModuleSymbolTable::CollectAsmSymvers(
     const Module &M, function_ref<void(StringRef, StringRef)> AsmSymver) {
 
   MDTuple *SymversMD =
       dyn_cast_if_present<MDTuple>(M.getModuleFlag("global-asm-symvers"));
 
-  if (!SymversMD) {
-    initializeRecordStreamer(
-        M, /*CPU=*/"", /*Features=*/"",
-        [&](RecordStreamer &Streamer) { addSymvers(Streamer, AsmSymver); },
-        /*DiagHandler=*/nullptr);
+  if (SymversMD) {
+    collectAsmSymversFromMetadata(SymversMD, AsmSymver);
     return;
   }
 
-  for (const Metadata *MD : SymversMD->operands()) {
-    const MDTuple *SymverMD = cast<MDTuple>(MD);
-    StringRef Name = cast<MDString>(SymverMD->getOperand(0))->getString();
-    for (size_t Idx = 1, End = SymverMD->getNumOperands(); Idx < End; ++Idx)
-      AsmSymver(Name, cast<MDString>(SymverMD->getOperand(Idx))->getString());
-  }
+  initializeRecordStreamer(
+      M, /*CPU=*/"", /*Features=*/"",
+      [&](RecordStreamer &Streamer) { addSymvers(Streamer, AsmSymver); },
+      /*DiagHandler=*/nullptr);
 }
 
 bool ModuleSymbolTable::EmitModuleFlags(Module &M, StringRef CPU,
diff --git a/llvm/test/LTO/AArch64/Inputs/asm-bar1.ll 
b/llvm/test/LTO/AArch64/Inputs/asm-bar1.ll
new file mode 100644
index 0000000000000..bd0aabb3e256c
--- /dev/null
+++ b/llvm/test/LTO/AArch64/Inputs/asm-bar1.ll
@@ -0,0 +1,22 @@
+target datalayout = 
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "aarch64"
+
+module asm ".text"
+module asm ".balign 16"
+module asm ".globl bar"
+module asm "bar:"
+module asm "  nop"
+module asm ".symver bar, bar@VER"
+module asm ".previous"
+
+!llvm.module.flags = !{!0, !1, !5}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 6, !"global-asm-symbols", !2}
+!2 = !{!3, !4}
+!3 = !{!"bar", i32 2050}
+!4 = !{!"bar@VER", i32 2050}
+!5 = !{i32 6, !"global-asm-symvers", !6}
+!6 = !{!7}
+!7 = !{!"bar", !"bar@VER"}
+
diff --git a/llvm/test/LTO/AArch64/Inputs/asm-foo-pauth.ll 
b/llvm/test/LTO/AArch64/Inputs/asm-foo-pauth.ll
new file mode 100644
index 0000000000000..184c4686c7008
--- /dev/null
+++ b/llvm/test/LTO/AArch64/Inputs/asm-foo-pauth.ll
@@ -0,0 +1,21 @@
+target datalayout = 
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "aarch64"
+
+module asm ".text"
+module asm ".balign 16"
+module asm ".globl foo"
+module asm "foo:"
+module asm "pacib x30, x27"
+module asm "retab"
+module asm ".symver foo, foo@VER"
+module asm ".previous"
+
+!llvm.module.flags = !{!1, !5}
+
+!1 = !{i32 6, !"global-asm-symbols", !2}
+!2 = !{!3, !4}
+!3 = !{!"foo", i32 2050}
+!4 = !{!"foo@VER", i32 2050}
+!5 = !{i32 6, !"global-asm-symvers", !6}
+!6 = !{!7}
+!7 = !{!"foo", !"foo@VER"}
diff --git a/llvm/test/LTO/AArch64/Inputs/asm-foo1.ll 
b/llvm/test/LTO/AArch64/Inputs/asm-foo1.ll
new file mode 100644
index 0000000000000..b3be253595466
--- /dev/null
+++ b/llvm/test/LTO/AArch64/Inputs/asm-foo1.ll
@@ -0,0 +1,19 @@
+target datalayout = 
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "aarch64"
+
+module asm ".text"
+module asm ".balign 16"
+module asm ".globl foo"
+module asm "foo:"
+module asm "  nop"
+module asm ".previous"
+
+!llvm.module.flags = !{!0, !1, !4}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 6, !"global-asm-symbols", !2}
+!2 = !{!3}
+!3 = !{!"foo", i32 2050}
+!4 = !{i32 6, !"global-asm-symvers", !5}
+!5 = !{}
+
diff --git a/llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll 
b/llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll
deleted file mode 100644
index 32e89dd1d128e..0000000000000
--- a/llvm/test/LTO/AArch64/Inputs/global-inline-asm-flags.ll
+++ /dev/null
@@ -1,32 +0,0 @@
-target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
-target triple = "aarch64-unknown-linux-gnu"
-
-module asm ".text"
-module asm ".balign 16"
-module asm ".globl foo"
-module asm "foo:"
-module asm "pacib     x30, x27"
-module asm "retab"
-module asm ".symver foo, foo@VER"
-module asm ".symver foo, foo@ANOTHERVER"
-module asm ".globl bar"
-module asm "bar:"
-module asm "pacib     x30, x27"
-module asm "retab"
-module asm ".symver bar, bar@VER"
-module asm ".previous"
-
-!llvm.module.flags = !{!1, !8}
-
-!1 = !{i32 6, !"global-asm-symbols", !2}
-!2 = !{!3, !4, !5, !6, !7}
-!3 = !{!"bar", i32 2050}
-!4 = !{!"bar@VER", i32 2050}
-!5 = !{!"foo@ANOTHERVER", i32 2050}
-!6 = !{!"foo", i32 2050}
-!7 = !{!"foo@VER", i32 2050}
-!8 = !{i32 6, !"global-asm-symvers", !9}
-!9 = !{!10, !11}
-!10 = !{!"foo", !"foo@VER", !"foo@ANOTHERVER"}
-!11 = !{!"bar", !"bar@VER"}
-
diff --git a/llvm/test/LTO/AArch64/global-inline-asm-flags-pauth.test 
b/llvm/test/LTO/AArch64/global-inline-asm-flags-pauth.test
new file mode 100644
index 0000000000000..b041832decdf0
--- /dev/null
+++ b/llvm/test/LTO/AArch64/global-inline-asm-flags-pauth.test
@@ -0,0 +1,93 @@
+RUN: llvm-as %p/Inputs/asm-bar1.ll -o %t-asm-bar1.bc
+RUN: llvm-as %p/Inputs/asm-foo-pauth.ll -o %t-asm-foo-pauth.bc
+
+Test LTO. We use -mattr=+pauth here, because llvm-lto runs CodeGen at
+the end.
+
+RUN: llvm-lto -save-merged-module -mattr=+pauth -filetype=asm %t-asm-bar1.bc 
%t-asm-foo-pauth.bc -o %t1
+RUN: llvm-dis %t1.merged.bc -o - | FileCheck %s --check-prefix CHECK-1
+RUN: llvm-nm %t1.merged.bc | FileCheck %s --check-prefix CHECK-1-NM
+
+CHECK-1-NOT: module asm
+CHECK-1: module asm ".text"
+CHECK-1: module asm ".balign 16"
+CHECK-1: module asm ".globl bar"
+CHECK-1: module asm "bar:"
+CHECK-1: module asm "  nop"
+CHECK-1: module asm ".symver bar, bar@VER"
+CHECK-1: module asm ".previous"
+CHECK-1: module asm ".text"
+CHECK-1: module asm ".balign 16"
+CHECK-1: module asm ".globl foo"
+CHECK-1: module asm "foo:"
+CHECK-1: module asm "pacib x30, x27"
+CHECK-1: module asm "retab"
+CHECK-1: module asm ".symver foo, foo@VER"
+CHECK-1: module asm ".previous"
+CHECK-1-NOT: module asm
+
+CHECK-1: !{i32 6, !"global-asm-symbols", ![[SYMBOLS:[0-9]+]]}
+CHECK-1: ![[SYMBOLS]] = distinct !{![[SYM_BAR:[0-9]+]], 
![[SYM_BAR_VER:[0-9]+]], ![[SYM_FOO:[0-9]+]], ![[SYM_FOO_VER:[0-9]+]]}
+CHECK-1: ![[SYM_BAR]] = !{!"bar", i32 2050}
+CHECK-1: ![[SYM_BAR_VER]] = !{!"bar@VER", i32 2050}
+CHECK-1: ![[SYM_FOO]] = !{!"foo", i32 2050}
+CHECK-1: ![[SYM_FOO_VER]] = !{!"foo@VER", i32 2050}
+CHECK-1: !{i32 6, !"global-asm-symvers", ![[SYMVERS:[0-9]+]]}
+CHECK-1: ![[SYMVERS]] = distinct !{![[SYMVER_BAR:[0-9]+]], 
![[SYMVER_FOO:[0-9]+]]}
+CHECK-1: ![[SYMVER_BAR]] = !{!"bar", !"bar@VER"}
+CHECK-1: ![[SYMVER_FOO]] = !{!"foo", !"foo@VER"}
+
+CHECK-1-NM-NOT:      {{bar|foo}}
+CHECK-1-NM:          T bar
+CHECK-1-NM-NEXT:     T bar@VER
+CHECK-1-NM-NEXT:     T foo
+CHECK-1-NM-NEXT:     T foo@VER
+CHECK-1-NM-NOT:      {{bar|foo}}
+
+Test LTO2.
+
+RUN: llvm-lto2 run -save-temps -mattr=+pauth -filetype=asm %t-asm-bar1.bc 
%t-asm-foo-pauth.bc -o %t2.s \
+RUN:   -r=%t-asm-bar1.bc,bar,p \
+RUN:   -r=%t-asm-bar1.bc,bar@VER,p \
+RUN:   -r=%t-asm-foo-pauth.bc,foo,p \
+RUN:   -r=%t-asm-foo-pauth.bc,foo@VER,p
+RUN: llvm-dis %t2.s.0.5.precodegen.bc -o - | FileCheck %s --check-prefix 
CHECK-2
+RUN: llvm-nm %t2.s.0.5.precodegen.bc | FileCheck %s --check-prefix CHECK-2-NM
+
+CHECK-2-NOT: module asm
+CHECK-2: module asm ".lto_discard"
+CHECK-2: module asm ".text"
+CHECK-2: module asm ".balign 16"
+CHECK-2: module asm ".globl bar"
+CHECK-2: module asm "bar:"
+CHECK-2: module asm "  nop"
+CHECK-2: module asm ".symver bar, bar@VER"
+CHECK-2: module asm ".previous"
+CHECK-2: module asm ".lto_discard"
+CHECK-2: module asm ".text"
+CHECK-2: module asm ".balign 16"
+CHECK-2: module asm ".globl foo"
+CHECK-2: module asm "foo:"
+CHECK-2: module asm "pacib x30, x27"
+CHECK-2: module asm "retab"
+CHECK-2: module asm ".symver foo, foo@VER"
+CHECK-2: module asm ".previous"
+CHECK-2-NOT: module asm
+
+CHECK-2: !{i32 6, !"global-asm-symbols", ![[SYMBOLS:[0-9]+]]}
+CHECK-2: ![[SYMBOLS]] = distinct !{![[SYM_BAR:[0-9]+]], 
![[SYM_BAR_VER:[0-9]+]], ![[SYM_FOO:[0-9]+]], ![[SYM_FOO_VER:[0-9]+]]}
+CHECK-2: ![[SYM_BAR]] = !{!"bar", i32 2050}
+CHECK-2: ![[SYM_BAR_VER]] = !{!"bar@VER", i32 2050}
+CHECK-2: ![[SYM_FOO]] = !{!"foo", i32 2050}
+CHECK-2: ![[SYM_FOO_VER]] = !{!"foo@VER", i32 2050}
+CHECK-2: !{i32 6, !"global-asm-symvers", ![[SYMVERS:[0-9]+]]}
+CHECK-2: ![[SYMVERS]] = distinct !{![[SYMVER_BAR:[0-9]+]], 
![[SYMVER_FOO:[0-9]+]]}
+CHECK-2: ![[SYMVER_BAR]] = !{!"bar", !"bar@VER"}
+CHECK-2: ![[SYMVER_FOO]] = !{!"foo", !"foo@VER"}
+
+CHECK-2-NM-NOT:      {{bar|foo}}
+CHECK-2-NM:          T bar
+CHECK-2-NM-NEXT:     T bar@VER
+CHECK-2-NM-NEXT:     T foo
+CHECK-2-NM-NEXT:     T foo@VER
+CHECK-2-NM-NOT:      {{bar|foo}}
diff --git a/llvm/test/LTO/AArch64/global-inline-asm-flags.ll 
b/llvm/test/LTO/AArch64/global-inline-asm-flags.ll
deleted file mode 100644
index d95957dea17d0..0000000000000
--- a/llvm/test/LTO/AArch64/global-inline-asm-flags.ll
+++ /dev/null
@@ -1,105 +0,0 @@
-; RUN: llvm-as %s -o %t1.bc
-; RUN: llvm-as %p/Inputs/global-inline-asm-flags.ll -o %t2.bc
-
-; RUN: llvm-lto -save-merged-module -filetype=asm -mattr=+pauth %t1.bc %t2.bc 
-o %t3
-; RUN: llvm-dis %t3.merged.bc -o - | FileCheck %s
-
-; RUN: llvm-lto2 run -save-temps -mattr=+pauth -filetype=asm -o %t4.s %t1.bc 
%t2.bc \
-; RUN:   -r=%t1.bc,baz,p \
-; RUN:   -r=%t1.bc,baz@VER,p \
-; RUN:   -r=%t1.bc,foo@LINKEDVER,p \
-; RUN:   -r=%t2.bc,bar,p \
-; RUN:   -r=%t2.bc,bar@VER,p \
-; RUN:   -r=%t2.bc,foo@ANOTHERVER,p \
-; RUN:   -r=%t2.bc,foo,p \
-; RUN:   -r=%t2.bc,foo@VER,p
-; RUN: llvm-dis %t4.s.0.5.precodegen.bc -o - | FileCheck %s
-
-; Note that -mattr=+pauth option for llvm-lto and llvm-lto2 is still
-; required, because LTO runs full CodeGen at the end. Symbols and
-; Symvers are still extracted from metadata.
-
-; RUN: llvm-nm %t1.bc | FileCheck %s --check-prefix NM1
-; RUN: llvm-nm %t2.bc | FileCheck %s --check-prefix NM2
-; RUN: llvm-nm %t3.merged.bc | FileCheck %s --check-prefixes NM1,NM2
-; RUN: llvm-nm %t4.s.0.5.precodegen.bc | FileCheck %s --check-prefixes NM1,NM2
-
-; Symbols of the first module
-; NM1-DAG: T baz
-; NM1-DAG: T baz@VER
-; NM1-DAG: T foo@LINKEDVER
-
-; Symbols of the second module
-; NM2-DAG: T bar
-; NM2-DAG: T bar@VER
-; NM2-DAG: T foo
-; NM2-DAG: T foo@ANOTHERVER
-; NM2-DAG: T foo@VER
-
-; IR with two modules linked
-; CHECK: module asm ".text"
-; CHECK: module asm ".balign 16"
-; CHECK: module asm ".globl baz"
-; CHECK: module asm "baz:"
-; CHECK: module asm "pacib     x30, x27"
-; CHECK: module asm "retab"
-; CHECK: module asm ".symver baz, baz@VER"
-; CHECK: module asm ".symver foo, foo@LINKEDVER"
-; CHECK: module asm ".previous"
-; CHECK: module asm ".text"
-; CHECK: module asm ".balign 16"
-; CHECK: module asm ".globl foo"
-; CHECK: module asm "foo:"
-; CHECK: module asm "pacib     x30, x27"
-; CHECK: module asm "retab"
-; CHECK: module asm ".symver foo, foo@VER"
-; CHECK: module asm ".symver foo, foo@ANOTHERVER"
-; CHECK: module asm ".globl bar"
-; CHECK: module asm "bar:"
-; CHECK: module asm "pacib     x30, x27"
-; CHECK: module asm "retab"
-; CHECK: module asm ".symver bar, bar@VER"
-; CHECK: module asm ".previous"
-
-; CHECK: !{{[0-9]+}} = distinct !{i32 6, !"global-asm-symbols", 
![[SYM:[0-9]+]]}
-; CHECK: ![[SYM]] = distinct !{![[SBAZ1:[0-9]+]], ![[SBAZ2:[0-9]+]], 
![[SFOO1:[0-9]+]], ![[SBAR1:[0-9]+]], ![[SBAR2:[0-9]+]], ![[SFOO2:[0-9]+]], 
![[SFOO3:[0-9]+]], ![[SFOO4:[0-9]+]]}
-; CHECK: ![[SBAZ1]] = !{!"baz", i32 2050}
-; CHECK: ![[SBAZ2]] = !{!"baz@VER", i32 2050}
-; CHECK: ![[SFOO1]] = !{!"foo@LINKEDVER", i32 2050}
-; CHECK: ![[SBAR1]] = !{!"bar", i32 2050}
-; CHECK: ![[SBAR2]] = !{!"bar@VER", i32 2050}
-; CHECK: ![[SFOO2]] = !{!"foo@ANOTHERVER", i32 2050}
-; CHECK: ![[SFOO3]] = !{!"foo", i32 2050}
-; CHECK: ![[SFOO4]] = !{!"foo@VER", i32 2050}
-
-; CHECK: !{{[0-9]+}} = distinct !{i32 6, !"global-asm-symvers", 
![[SYMVER:[0-9]+]]}
-; CHECK: ![[SYMVER]] = distinct !{![[VBAZ:[0-9]+]], ![[VFOO1:[0-9]+]], 
![[VFOO2:[0-9]+]], ![[VBAR:[0-9]+]]}
-; CHECK: ![[VBAZ]] = !{!"baz", !"baz@VER"}
-; CHECK: ![[VFOO1]] = !{!"foo", !"foo@LINKEDVER"}
-; CHECK: ![[VFOO2]] = !{!"foo", !"foo@VER", !"foo@ANOTHERVER"}
-; CHECK: ![[VBAR]] = !{!"bar", !"bar@VER"}
-
-target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
-target triple = "aarch64-unknown-linux-gnu"
-
-module asm ".text"
-module asm ".balign 16"
-module asm ".globl baz"
-module asm "baz:"
-module asm "pacib     x30, x27"
-module asm "retab"
-module asm ".symver baz, baz@VER"
-module asm ".symver foo, foo@LINKEDVER"
-module asm ".previous"
-
-!llvm.module.flags = !{!0, !5}
-
-!0 = !{i32 6, !"global-asm-symbols", !1}
-!1 = !{!2, !3, !4}
-!2 = !{!"baz", i32 2050}
-!3 = !{!"baz@VER", i32 2050}
-!4 = !{!"foo@LINKEDVER", i32 2050}
-!5 = !{i32 6, !"global-asm-symvers", !6}
-!6 = !{!7, !8}
-!7 = !{!"baz", !"baz@VER"}
-!8 = !{!"foo", !"foo@LINKEDVER"}
diff --git a/llvm/test/LTO/AArch64/global-inline-asm-flags.test 
b/llvm/test/LTO/AArch64/global-inline-asm-flags.test
new file mode 100644
index 0000000000000..c7f2bdf1943fe
--- /dev/null
+++ b/llvm/test/LTO/AArch64/global-inline-asm-flags.test
@@ -0,0 +1,125 @@
+RUN: llvm-as %p/Inputs/asm-bar1.ll -o %t-asm-bar1.bc
+RUN: llvm-as %p/Inputs/asm-foo1.ll -o %t-asm-foo1.bc
+
+Test LTO with unique symbols foo, bar, bar@VER.
+
+RUN: llvm-lto -save-merged-module -filetype=asm %t-asm-bar1.bc %t-asm-foo1.bc 
-o %t1
+RUN: llvm-dis %t1.merged.bc -o - | FileCheck %s --check-prefix CHECK-1
+RUN: llvm-nm %t1.merged.bc | FileCheck %s --check-prefix CHECK-1-NM
+
+CHECK-1-NOT: module asm
+CHECK-1: module asm ".text"
+CHECK-1: module asm ".balign 16"
+CHECK-1: module asm ".globl bar"
+CHECK-1: module asm "bar:"
+CHECK-1: module asm "  nop"
+CHECK-1: module asm ".symver bar, bar@VER"
+CHECK-1: module asm ".previous"
+CHECK-1: module asm ".text"
+CHECK-1: module asm ".balign 16"
+CHECK-1: module asm ".globl foo"
+CHECK-1: module asm "foo:"
+CHECK-1: module asm "  nop"
+CHECK-1: module asm ".previous"
+CHECK-1-NOT: module asm
+
+CHECK-1: !{i32 6, !"global-asm-symbols", ![[SYMBOLS:[0-9]+]]}
+CHECK-1: ![[SYMBOLS]] = distinct !{![[SYM_BAR:[0-9]+]], 
![[SYM_BAR_VER:[0-9]+]], ![[SYM_FOO:[0-9]+]]}
+CHECK-1: ![[SYM_BAR]] = !{!"bar", i32 2050}
+CHECK-1: ![[SYM_BAR_VER]] = !{!"bar@VER", i32 2050}
+CHECK-1: ![[SYM_FOO]] = !{!"foo", i32 2050}
+CHECK-1: !{i32 6, !"global-asm-symvers", ![[SYMVERS:[0-9]+]]}
+CHECK-1: ![[SYMVERS]] = distinct !{![[SYMVER_BAR:[0-9]+]]}
+CHECK-1: ![[SYMVER_BAR]] = !{!"bar", !"bar@VER"}
+
+CHECK-1-NM-NOT:      {{bar|foo}}
+CHECK-1-NM:          T bar
+CHECK-1-NM-NEXT:     T bar@VER
+CHECK-1-NM-NEXT:     T foo
+CHECK-1-NM-NOT:      {{bar|foo}}
+
+Test LTO2 with unique symbols foo, bar, bar@VER.
+All symbols are prevailing, so .lto_discard is emtpy and metadata from
+2 modules are combined.
+
+RUN: llvm-lto2 run -save-temps -filetype=asm %t-asm-bar1.bc %t-asm-foo1.bc -o 
%t2.s \
+RUN:   -r=%t-asm-bar1.bc,bar,p \
+RUN:   -r=%t-asm-bar1.bc,bar@VER,p \
+RUN:   -r=%t-asm-foo1.bc,foo,p
+RUN: llvm-dis %t2.s.0.5.precodegen.bc -o - | FileCheck %s --check-prefix 
CHECK-2
+RUN: llvm-nm %t2.s.0.5.precodegen.bc | FileCheck %s --check-prefix CHECK-2-NM
+
+CHECK-2-NOT: module asm
+CHECK-2: module asm ".lto_discard"
+CHECK-2: module asm ".text"
+CHECK-2: module asm ".balign 16"
+CHECK-2: module asm ".globl bar"
+CHECK-2: module asm "bar:"
+CHECK-2: module asm "  nop"
+CHECK-2: module asm ".symver bar, bar@VER"
+CHECK-2: module asm ".previous"
+CHECK-2: module asm ".lto_discard"
+CHECK-2: module asm ".text"
+CHECK-2: module asm ".balign 16"
+CHECK-2: module asm ".globl foo"
+CHECK-2: module asm "foo:"
+CHECK-2: module asm "  nop"
+CHECK-2: module asm ".previous"
+CHECK-2-NOT: module asm
+
+CHECK-2: !{i32 6, !"global-asm-symbols", ![[SYMBOLS:[0-9]+]]}
+CHECK-2: ![[SYMBOLS]] = distinct !{![[SYM_BAR:[0-9]+]], 
![[SYM_BAR_VER:[0-9]+]], ![[SYM_FOO:[0-9]+]]}
+CHECK-2: ![[SYM_BAR]] = !{!"bar", i32 2050}
+CHECK-2: ![[SYM_BAR_VER]] = !{!"bar@VER", i32 2050}
+CHECK-2: ![[SYM_FOO]] = !{!"foo", i32 2050}
+CHECK-2: !{i32 6, !"global-asm-symvers", ![[SYMVERS:[0-9]+]]}
+CHECK-2: ![[SYMVERS]] = distinct !{![[SYMVER_BAR:[0-9]+]]}
+CHECK-2: ![[SYMVER_BAR]] = !{!"bar", !"bar@VER"}
+
+CHECK-2-NM-NOT:      {{bar|foo}}
+CHECK-2-NM:          T bar
+CHECK-2-NM-NEXT:     T bar@VER
+CHECK-2-NM-NEXT:     T foo
+CHECK-2-NM-NOT:      {{bar|foo}}
+
+Test LTO2 with non-prevailing symbol foo, and prevailing bar, bar@VER.
+Non-prevailing symbols should be removed from the metadata
+to match .lto_discard.
+
+RUN: llvm-lto2 run -save-temps -filetype=asm %t-asm-bar1.bc %t-asm-foo1.bc -o 
%t3.s \
+RUN:   -r=%t-asm-bar1.bc,bar,p \
+RUN:   -r=%t-asm-bar1.bc,bar@VER,p \
+RUN:   -r=%t-asm-foo1.bc,foo
+RUN: llvm-dis %t3.s.0.5.precodegen.bc -o - | FileCheck %s --check-prefix 
CHECK-3
+RUN: llvm-nm %t3.s.0.5.precodegen.bc | FileCheck %s --check-prefix CHECK-3-NM
+
+CHECK-3-NOT: module asm
+CHECK-3: module asm ".lto_discard"
+CHECK-3: module asm ".text"
+CHECK-3: module asm ".balign 16"
+CHECK-3: module asm ".globl bar"
+CHECK-3: module asm "bar:"
+CHECK-3: module asm "  nop"
+CHECK-3: module asm ".symver bar, bar@VER"
+CHECK-3: module asm ".previous"
+CHECK-3: module asm ".lto_discard foo"
+CHECK-3: module asm ".text"
+CHECK-3: module asm ".balign 16"
+CHECK-3: module asm ".globl foo"
+CHECK-3: module asm "foo:"
+CHECK-3: module asm "  nop"
+CHECK-3: module asm ".previous"
+CHECK-3-NOT: module asm
+
+CHECK-3: !{i32 6, !"global-asm-symbols", ![[SYMBOLS:[0-9]+]]}
+CHECK-3: ![[SYMBOLS]] = distinct !{![[SYM_BAR:[0-9]+]], 
![[SYM_BAR_VER:[0-9]+]]}
+CHECK-3: ![[SYM_BAR]] = !{!"bar", i32 2050}
+CHECK-3: ![[SYM_BAR_VER]] = !{!"bar@VER", i32 2050}
+CHECK-3: !{i32 6, !"global-asm-symvers", ![[SYMVERS:[0-9]+]]}
+CHECK-3: ![[SYMVERS]] = distinct !{![[SYMVER_BAR:[0-9]+]]}
+CHECK-3: ![[SYMVER_BAR]] = !{!"bar", !"bar@VER"}
+
+CHECK-3-NM-NOT:      {{bar|foo}}
+CHECK-3-NM:          T bar
+CHECK-3-NM-NEXT:     T bar@VER
+CHECK-3-NM-NOT:      {{bar|foo}}
diff --git a/llvm/test/LTO/RISCV/global-inline-asm-flags.ll 
b/llvm/test/LTO/RISCV/global-inline-asm-flags.ll
index 4f1d5987ec996..ecfad7fc47576 100644
--- a/llvm/test/LTO/RISCV/global-inline-asm-flags.ll
+++ b/llvm/test/LTO/RISCV/global-inline-asm-flags.ll
@@ -1,6 +1,8 @@
 ; RUN: llvm-as %s -o %t.o
-; RUN: llvm-lto2 run -mattr=+zcmp -filetype=asm -o %t.s %t.o -r=%t.o,func
+; RUN: llvm-lto2 run -mattr=+zcmp -save-temps -filetype=asm -o %t.s %t.o 
-r=%t.o,func,p
 ; RUN: llvm-nm %t.o | FileCheck %s --check-prefix NM
+; RUN: llvm-nm %t.s.0.5.precodegen.bc | FileCheck %s --check-prefix NM
+; RUN: FileCheck %s --input-file %t.s.0
 
 ; NM: T func
 
diff --git a/llvm/test/Object/Inputs/asm-bar-ver-def.ll 
b/llvm/test/Object/Inputs/asm-bar-ver-def.ll
new file mode 100644
index 0000000000000..56a96faad1ba0
--- /dev/null
+++ b/llvm/test/Object/Inputs/asm-bar-ver-def.ll
@@ -0,0 +1,13 @@
+;; Symbol bar (defined), symver bar@VER (defined).
+
+module asm ".do.not.parse"
+
+!llvm.module.flags = !{!1, !5}
+
+!1 = !{i32 6, !"global-asm-symbols", !2}
+!2 = !{!3, !4}
+!3 = !{!"bar", i32 2050}
+!4 = !{!"bar@VER", i32 2050}
+!5 = !{i32 6, !"global-asm-symvers", !6}
+!6 = !{!7}
+!7 = !{!"bar", !"bar@VER"}
diff --git a/llvm/test/Object/Inputs/asm-foo-def-ir.ll 
b/llvm/test/Object/Inputs/asm-foo-def-ir.ll
new file mode 100644
index 0000000000000..50dcefebf0a5b
--- /dev/null
+++ b/llvm/test/Object/Inputs/asm-foo-def-ir.ll
@@ -0,0 +1,4 @@
+;; Symbol defined in IR can resolve symbols and symvers defined in assembly
+define i32 @foo() {
+  ret i32 42
+}
diff --git a/llvm/test/Object/Inputs/asm-foo-ver-def.ll 
b/llvm/test/Object/Inputs/asm-foo-ver-def.ll
new file mode 100644
index 0000000000000..34f8b2c2d0df6
--- /dev/null
+++ b/llvm/test/Object/Inputs/asm-foo-ver-def.ll
@@ -0,0 +1,13 @@
+;; Symbol foo is defined, foo@VER is a symver (also defined).
+
+module asm ".do.not.parse"
+
+!llvm.module.flags = !{!1, !5}
+
+!1 = !{i32 6, !"global-asm-symbols", !2}
+!2 = !{!3, !4}
+!3 = !{!"foo", i32 2050}
+!4 = !{!"foo@VER", i32 2050}
+!5 = !{i32 6, !"global-asm-symvers", !6}
+!6 = !{!7}
+!7 = !{!"foo", !"foo@VER"}
diff --git a/llvm/test/Object/Inputs/asm-foo-ver-undef.ll 
b/llvm/test/Object/Inputs/asm-foo-ver-undef.ll
new file mode 100644
index 0000000000000..425ea5e003d18
--- /dev/null
+++ b/llvm/test/Object/Inputs/asm-foo-ver-undef.ll
@@ -0,0 +1,13 @@
+;; Symbol foo is undefined, foo@VER is a symver (also undefined).
+
+module asm ".do.not.parse"
+
+!llvm.module.flags = !{!1, !5}
+
+!1 = !{i32 6, !"global-asm-symbols", !2}
+!2 = !{!3, !4}
+!3 = !{!"foo", i32 2051}
+!4 = !{!"foo@VER", i32 2051}
+!5 = !{i32 6, !"global-asm-symvers", !6}
+!6 = !{!7}
+!7 = !{!"foo", !"foo@VER"}
diff --git a/llvm/test/Object/Inputs/asm-foo.ll 
b/llvm/test/Object/Inputs/asm-foo.ll
new file mode 100644
index 0000000000000..ff498f2cf31d4
--- /dev/null
+++ b/llvm/test/Object/Inputs/asm-foo.ll
@@ -0,0 +1,11 @@
+;; Symbol foo is defined, no symvers.
+
+module asm ".do.not.parse"
+
+!llvm.module.flags = !{!1, !4}
+
+!1 = !{i32 6, !"global-asm-symbols", !2}
+!2 = !{!3}
+!3 = !{!"foo", i32 2050}
+!4 = !{i32 6, !"global-asm-symvers", !5}
+!5 = !{}
diff --git a/llvm/test/Object/global-inline-asm.test 
b/llvm/test/Object/global-inline-asm.test
new file mode 100644
index 0000000000000..dff44a24336ad
--- /dev/null
+++ b/llvm/test/Object/global-inline-asm.test
@@ -0,0 +1,139 @@
+Link modules:
+
+- asm-foo.ll: symbol foo (defined).
+- asm-foo-ver-undef.ll: symbol foo (undefined), symver foo@VER
+  (undefined, because foo is undefined).
+
+The result should be a module with foo and foo@VER both defined.
+
+RUN: llvm-link %p/Inputs/asm-foo.ll %p/Inputs/asm-foo-ver-undef.ll -o %t1.bc
+RUN: llvm-dis %t1.bc -o - | FileCheck %s --check-prefix CHECK-1
+RUN: llvm-nm %t1.bc | FileCheck %s --check-prefix NM-1
+
+CHECK-1: module asm ".do.not.parse"
+CHECK-1: module asm ".do.not.parse"
+CHECK-1: !llvm.module.flags = !{![[SYMBOLS_FLAG:[0-9]+]], 
![[SYMVERS_FLAG:[0-9]+]]}
+CHECK-1: ![[SYMBOLS_FLAG]] = distinct !{i32 6, !"global-asm-symbols", 
![[SYMBOLS:[0-9]+]]}
+CHECK-1: ![[SYMBOLS]] = distinct !{![[SYM_DEF:[0-9]+]], ![[SYM_UNDEF:[0-9]+]], 
![[SYM_SYMVER:[0-9]+]]}
+CHECK-1: ![[SYM_DEF:[0-9]+]] = !{!"foo", i32 2050}
+CHECK-1: ![[SYM_UNDEF:[0-9]+]] = !{!"foo", i32 2051}
+CHECK-1: ![[SYM_SYMVER:[0-9]+]] = !{!"foo@VER", i32 2051}
+CHECK-1: ![[SYMVERS_FLAG]] = distinct !{i32 6, !"global-asm-symvers", 
![[SYMVERS:[0-9]+]]}
+CHECK-1: ![[SYMVERS]] = distinct !{![[SYMVER:[0-9]+]]}
+CHECK-1: ![[SYMVER]] = !{!"foo", !"foo@VER"}
+
+NM-1-NOT: foo
+NM-1: T foo
+NM-1: T foo@VER
+NM-1-NOT: foo
+
+
+Link two identical modules: symbol foo (undefined), symver foo@VER
+(undefined, because foo is undefined).
+
+The result should have them foo and foo@VER undefined as well.
+
+RUN: llvm-link %p/Inputs/asm-foo-ver-undef.ll %p/Inputs/asm-foo-ver-undef.ll 
-o %t2.bc
+RUN: llvm-dis %t2.bc -o - | FileCheck %s --check-prefix CHECK-2
+RUN: llvm-nm %t2.bc | FileCheck %s --check-prefix NM-2
+
+CHECK-2: module asm ".do.not.parse"
+CHECK-2: module asm ".do.not.parse"
+CHECK-2: !llvm.module.flags = !{![[SYMBOLS_FLAG:[0-9]+]], 
![[SYMVERS_FLAG:[0-9]+]]}
+CHECK-2: ![[SYMBOLS_FLAG]] = distinct !{i32 6, !"global-asm-symbols", 
![[SYMBOLS:[0-9]+]]}
+CHECK-2: ![[SYMBOLS]] = distinct !{![[SYM_UNDEF:[0-9]+]], 
![[SYM_SYMVER:[0-9]+]]}
+CHECK-2: ![[SYM_UNDEF:[0-9]+]] = !{!"foo", i32 2051}
+CHECK-2: ![[SYM_SYMVER:[0-9]+]] = !{!"foo@VER", i32 2051}
+CHECK-2: ![[SYMVERS_FLAG]] = distinct !{i32 6, !"global-asm-symvers", 
![[SYMVERS:[0-9]+]]}
+CHECK-2: ![[SYMVERS]] = distinct !{![[SYMVER:[0-9]+]]}
+CHECK-2: ![[SYMVER]] = !{!"foo", !"foo@VER"}
+
+NM-2-NOT: foo
+NM-2: U foo
+NM-2: U foo@VER
+NM-2-NOT: foo
+
+Link two identical modules: symbol foo (defined), symver foo@VER (defined).
+The result should have them defined as well.
+
+RUN: llvm-link %p/Inputs/asm-foo-ver-def.ll %p/Inputs/asm-foo-ver-def.ll -o 
%t3.bc
+RUN: llvm-dis %t3.bc -o - | FileCheck %s --check-prefix CHECK-3
+RUN: llvm-nm %t3.bc | FileCheck %s --check-prefix NM-3
+
+CHECK-3: module asm ".do.not.parse"
+CHECK-3: module asm ".do.not.parse"
+CHECK-3: !llvm.module.flags = !{![[SYMBOLS_FLAG:[0-9]+]], 
![[SYMVERS_FLAG:[0-9]+]]}
+CHECK-3: ![[SYMBOLS_FLAG]] = distinct !{i32 6, !"global-asm-symbols", 
![[SYMBOLS:[0-9]+]]}
+CHECK-3: ![[SYMBOLS]] = distinct !{![[SYM_DEF:[0-9]+]], ![[SYM_SYMVER:[0-9]+]]}
+CHECK-3: ![[SYM_DEF:[0-9]+]] = !{!"foo", i32 2050}
+CHECK-3: ![[SYM_SYMVER:[0-9]+]] = !{!"foo@VER", i32 2050}
+CHECK-3: ![[SYMVERS_FLAG]] = distinct !{i32 6, !"global-asm-symvers", 
![[SYMVERS:[0-9]+]]}
+CHECK-3: ![[SYMVERS]] = distinct !{![[SYMVER:[0-9]+]]}
+CHECK-3: ![[SYMVER]] = !{!"foo", !"foo@VER"}
+
+NM-3-NOT: foo
+NM-3: T foo
+NM-3: T foo@VER
+NM-3-NOT: foo
+
+Link modules:
+- asm-bar-ver-def.ll: symbol bar (defined), symver bar@VER (defined).
+- asm-foo.ll: symbol foo (defined).
+- asm-foo-undef.ll: symbol foo (undefined), symver foo@VER (undefined).
+
+The result should have all symbols and symvers defined.
+
+RUN: llvm-link %p/Inputs/asm-bar-ver-def.ll %p/Inputs/asm-foo.ll 
%p/Inputs/asm-foo-ver-undef.ll -o %t4.bc
+RUN: llvm-dis %t4.bc -o - | FileCheck %s --check-prefix CHECK-4
+RUN: llvm-nm %t4.bc | FileCheck %s --check-prefix NM-4
+
+CHECK-4: module asm ".do.not.parse"
+CHECK-4: module asm ".do.not.parse"
+CHECK-4: module asm ".do.not.parse"
+CHECK-4: !llvm.module.flags = !{![[SYMBOLS_FLAG:[0-9]+]], 
![[SYMVERS_FLAG:[0-9]+]]}
+CHECK-4: ![[SYMBOLS_FLAG]] = distinct !{i32 6, !"global-asm-symbols", 
![[SYMBOLS:[0-9]+]]}
+CHECK-4: ![[SYMBOLS]] = distinct !{![[SYM_BAR_DEF:[0-9]+]], 
![[SYM_BAR_SYMVER_DEF:[0-9]+]], ![[SYM_FOO_DEF:[0-9]+]], 
![[SYM_FOO_UNDEF:[0-9]+]], ![[SYM_FOO_SYMVER_UNDEF:[0-9]+]]}
+CHECK-4: ![[SYM_BAR_DEF]] = !{!"bar", i32 2050}
+CHECK-4: ![[SYM_BAR_SYMVER_DEF]] = !{!"bar@VER", i32 2050}
+CHECK-4: ![[SYM_FOO_DEF]] = !{!"foo", i32 2050}
+CHECK-4: ![[SYM_FOO_UNDEF]] = !{!"foo", i32 2051}
+CHECK-4: ![[SYM_FOO_SYMVER_UNDEF]] = !{!"foo@VER", i32 2051}
+CHECK-4: ![[SYMVERS_FLAG]] = distinct !{i32 6, !"global-asm-symvers", 
![[SYMVERS:[0-9]+]]}
+CHECK-4: ![[SYMVERS]] = distinct !{![[SYMVER_BAR:[0-9]+]], 
![[SYMVER_FOO:[0-9]+]]}
+CHECK-4: ![[SYMVER_BAR]] = !{!"bar", !"bar@VER"}
+CHECK-4: ![[SYMVER_FOO]] = !{!"foo", !"foo@VER"}
+
+NM-4-NOT: foo
+NM-4-NOT: bar
+NM-4: T bar
+NM-4: T bar@VER
+NM-4: T foo
+NM-4: T foo@VER
+NM-4-NOT: foo
+NM-4-NOT: bar
+
+Link modules:
+- asm-foo-ver-undef.ll: symbol foo (undefined), symver foo@VER (undefined).
+- asm-foo-def-ir.ll: symbol foo is defined as a function in IR.
+
+The result should have foo and foo@VER defined. Symbols in IR define
+symvers in assembly.
+
+RUN: llvm-link %p/Inputs/asm-foo-def-ir.ll %p/Inputs/asm-foo-ver-undef.ll -o 
%t5.bc
+RUN: llvm-dis %t5.bc -o - | FileCheck %s --check-prefix CHECK-5
+RUN: llvm-nm %t5.bc | FileCheck %s --check-prefix NM-5
+
+CHECK-5: module asm ".do.not.parse"
+CHECK-5: !llvm.module.flags = !{![[SYMBOLS_FLAG:[0-9]+]], 
![[SYMVERS_FLAG:[0-9]+]]}
+CHECK-5: ![[SYMBOLS_FLAG]] = !{i32 6, !"global-asm-symbols", 
![[SYMBOLS:[0-9]+]]}
+CHECK-5: ![[SYMBOLS]] = !{![[SYM_UNDEF:[0-9]+]], ![[SYM_SYMVER:[0-9]+]]}
+CHECK-5: ![[SYM_UNDEF:[0-9]+]] = !{!"foo", i32 2051}
+CHECK-5: ![[SYM_SYMVER:[0-9]+]] = !{!"foo@VER", i32 2051}
+CHECK-5: ![[SYMVERS_FLAG]] = !{i32 6, !"global-asm-symvers", 
![[SYMVERS:[0-9]+]]}
+CHECK-5: ![[SYMVERS]] = !{![[SYMVER:[0-9]+]]}
+CHECK-5: ![[SYMVER]] = !{!"foo", !"foo@VER"}
+
+NM-5-NOT: foo
+NM-5: T foo
+NM-5: T foo@VER
+NM-5-NOT: foo

>From b4f5335dfd46c0abc83b63fe2197f88c0799ae62 Mon Sep 17 00:00:00 2001
From: Andrew Savonichev <[email protected]>
Date: Wed, 18 Feb 2026 00:11:40 +0900
Subject: [PATCH 4/4] Test that flags are always emitted in presence of inline
 asm

---
 .../AArch64/global-inline-asm-flags-empty.c   | 67 +++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 clang/test/CodeGen/AArch64/global-inline-asm-flags-empty.c

diff --git a/clang/test/CodeGen/AArch64/global-inline-asm-flags-empty.c 
b/clang/test/CodeGen/AArch64/global-inline-asm-flags-empty.c
new file mode 100644
index 0000000000000..e8e2fc74913c4
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/global-inline-asm-flags-empty.c
@@ -0,0 +1,67 @@
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - %s -DEMPTY | 
FileCheck %s --check-prefix EMPTY
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - %s -DSYM_ONLY | 
FileCheck %s --check-prefix SYM_ONLY
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - %s -DSYMVER_ONLY 
| FileCheck %s --check-prefix SYMVER_ONLY
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - %s | FileCheck %s 
--check-prefix CHECK
+
+// REQUIRES: aarch64-registered-target
+
+#ifdef EMPTY
+asm (
+    ".text" "\n"
+    ".previous" "\n"
+);
+// Inline assembly does not define any symbols. Flags are set, but empty.
+//
+// EMPTY: module asm ".text"
+// EMPTY: module asm ".previous"
+// EMPTY: !{{.*}} = !{i32 6, !"global-asm-symbols", ![[NONE:[0-9]+]]}
+// EMPTY: ![[NONE]] = !{}
+// EMPTY: !{{.*}} = !{i32 6, !"global-asm-symvers", ![[NONE]]}
+#endif
+
+
+#ifdef SYM_ONLY
+asm (
+    ".text" "\n"
+    "foo:" "\n"
+    ".previous" "\n"
+);
+// Inline assembly defines symbols, but not symvers. Flags are set, but symvers
+// is empty.
+//
+// SYM_ONLY: module asm ".text"
+// SYM_ONLY: module asm "foo:"
+// SYM_ONLY: module asm ".previous"
+// SYM_ONLY: !{{.*}} = !{i32 6, !"global-asm-symbols", ![[SYM:[0-9]+]]}
+// SYM_ONLY: ![[SYM]] = !{![[FOO:[0-9]+]]}
+// SYM_ONLY: ![[FOO]] = !{!"foo", i32 2048}
+// SYM_ONLY: !{{.*}} = !{i32 6, !"global-asm-symvers", ![[NONE:[0-9]+]]}
+// SYM_ONLY: ![[NONE]] = !{}
+#endif
+
+
+#ifdef SYMVER_ONLY
+asm (
+    ".text" "\n"
+    ".symver foo, foo@VER" "\n"
+    ".previous" "\n"
+);
+// Inline assembly defines symvers. The corresponding symbol is implicitly
+// declared as "undefined global".
+//
+// SYMVER_ONLY: module asm ".text"
+// SYMVER_ONLY: module asm ".symver foo, foo@VER"
+// SYMVER_ONLY: module asm ".previous"
+// SYMVER_ONLY: !{{.*}} = !{i32 6, !"global-asm-symbols", ![[SYM:[0-9]+]]}
+// SYMVER_ONLY: ![[SYM]] = !{![[FOO_UNDEF:[0-9]+]]}
+// SYMVER_ONLY: ![[FOO_UNDEF]] = !{!"foo", i32 2051}
+// SYMVER_ONLY: !{{.*}} = !{i32 6, !"global-asm-symvers", ![[SYMVERS:[0-9]+]]}
+// SYMVER_ONLY: ![[SYMVERS]] = !{![[FOO_VER:[0-9]+]]}
+// SYMVER_ONLY: ![[FOO_VER]] = !{!"foo", !"foo@VER"}
+#endif
+
+// If there is no inline assembly, module flags should be omitted.
+// CHECK-NOT: module asm
+// CHECK-NOT: global-asm
+
+void bar() {}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to