[llvm-branch-commits] [llvm] [AMDGPU] Add `.amdgpu.info` section for per-function metadata (PR #192384)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Apr 15 20:58:48 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Shilei Tian (shiltian)

<details>
<summary>Changes</summary>

AMDGPU object linking requires the linker to propagate resource usage
(registers, stack, LDS) across translation units. To support this, the compiler
must emit per-function metadata and call graph edges in the relocatable object
so the linker can compute whole-program resource requirements.

This PR introduces a `.amdgpu.info` ELF section using a tagged, length-prefixed
binary format: each entry is encoded as:

```
[kind: u8] [len: u8] [payload: <len> bytes]
```

A function scope is opened by an `INFO_FUNC` entry (containing a symbol
reference), followed by per-function attributes (register counts, flags, private
segment size) and relational edges (direct calls, LDS uses, indirect call
signatures). String data such as function type signatures is stored in a
companion `.amdgpu.strtab` section.

The format is forward-compatible: a consumer that encounters an unknown kind can
skip it by reading the length byte, allowing new entry kinds to be added without
breaking existing toolchains.


---

Patch is 48.06 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/192384.diff


14 Files Affected:

- (added) llvm/include/llvm/Support/AMDGPUObjLinkingInfo.h (+53) 
- (modified) llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp (+172-2) 
- (modified) llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.h (+14) 
- (modified) llvm/lib/Target/AMDGPU/AMDGPUMCInstLower.cpp (+3) 
- (modified) llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp (+117) 
- (modified) llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp (+192) 
- (modified) llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.h (+33) 
- (added) llvm/test/CodeGen/AMDGPU/lds-link-time-codegen-callgraph.ll (+60) 
- (added) llvm/test/CodeGen/AMDGPU/lds-link-time-codegen-cross-tu-addr-taken.ll (+51) 
- (added) llvm/test/CodeGen/AMDGPU/lds-link-time-codegen-indirect.ll (+63) 
- (modified) llvm/test/CodeGen/AMDGPU/lds-link-time-codegen-named-barrier.ll (+26-4) 
- (added) llvm/test/CodeGen/AMDGPU/lds-link-time-codegen-prototype.ll (+83) 
- (modified) llvm/test/CodeGen/AMDGPU/lds-link-time-codegen.ll (+45-7) 
- (added) llvm/test/MC/AMDGPU/amdgpu-info-roundtrip.s (+121) 


``````````diff
diff --git a/llvm/include/llvm/Support/AMDGPUObjLinkingInfo.h b/llvm/include/llvm/Support/AMDGPUObjLinkingInfo.h
new file mode 100644
index 0000000000000..f374d4dbb37d7
--- /dev/null
+++ b/llvm/include/llvm/Support/AMDGPUObjLinkingInfo.h
@@ -0,0 +1,53 @@
+//===--- AMDGPUObjLinkingInfo.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Enums shared between the AMDGPU backend (LLVM) and the ELF linker (LLD)
+/// for the `.amdgpu.info` object-linking metadata section.
+///
+/// Binary layout of each entry: [kind: u8] [len: u8] [payload: <len> bytes].
+/// Unknown kinds are forward-compatible: a consumer skips them by reading len.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_AMDGPUOBJECTLINKINGINFO_H
+#define LLVM_SUPPORT_AMDGPUOBJECTLINKINGINFO_H
+
+#include <cstdint>
+
+namespace llvm {
+namespace AMDGPU {
+
+/// Entry kind values for the `.amdgpu.info` section.
+enum InfoKind : uint8_t {
+  INFO_FUNC = 1,                 // [symbol_ref: 8B] — opens function scope
+  INFO_NUM_VGPR = 2,             // [u32]
+  INFO_NUM_AGPR = 3,             // [u32]
+  INFO_NUM_SGPR = 4,             // [u32]
+  INFO_NUM_NAMED_BARRIER = 5,    // [u32]
+  INFO_PRIVATE_SEGMENT_SIZE = 6, // [u32]
+  INFO_OCCUPANCY_LDS_LIMIT = 7,  // [u32]
+  INFO_FLAGS = 8,                // [u32] — FuncInfoFlags bitfield
+  INFO_USE = 9,                  // [dst_symbol: 8B]
+  INFO_CALL = 10,                // [dst_symbol: 8B]
+  INFO_INDIRECT_CALL = 11,       // [strtab_offset: u32]
+  INFO_SIGNATURE = 12,           // [strtab_offset: u32]
+};
+
+/// Per-function flags packed into INFO_FLAGS entries.
+enum FuncInfoFlags : uint32_t {
+  FUNC_IS_KERNEL = 0x1,
+  FUNC_USES_VCC = 0x2,
+  FUNC_USES_FLAT_SCRATCH = 0x4,
+  FUNC_HAS_DYN_STACK = 0x8,
+};
+
+} // namespace AMDGPU
+} // namespace llvm
+
+#endif // LLVM_SUPPORT_AMDGPUOBJECTLINKINGINFO_H
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp b/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
index 718b2b154e251..d5c0130475cca 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
@@ -32,6 +32,7 @@
 #include "Utils/AMDGPUBaseInfo.h"
 #include "Utils/AMDKernelCodeTUtils.h"
 #include "Utils/SIDefinesUtils.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/CodeGen/AsmPrinterHandler.h"
@@ -537,6 +538,150 @@ void AMDGPUAsmPrinter::validateMCResourceInfo(Function &F) {
   }
 }
 
+static void appendTypeEncoding(std::string &Enc, Type *Ty,
+                               const DataLayout &DL) {
+  if (Ty->isVoidTy()) {
+    Enc += 'v';
+    return;
+  }
+  unsigned Bits = DL.getTypeSizeInBits(Ty);
+  if (Bits <= 32)
+    Enc += 'i';
+  else if (Bits <= 64)
+    Enc += 'l';
+  else
+    Enc.append(divideCeil(Bits, 32), 'i');
+}
+
+static std::string computeTypeId(const FunctionType *FTy,
+                                 const DataLayout &DL) {
+  std::string Enc;
+  appendTypeEncoding(Enc, FTy->getReturnType(), DL);
+  for (Type *ParamTy : FTy->params())
+    appendTypeEncoding(Enc, ParamTy, DL);
+  return Enc;
+}
+
+void AMDGPUAsmPrinter::collectCallEdge(const MachineInstr &MI) {
+  if (!AMDGPUTargetMachine::EnableObjectLinking)
+    return;
+  const GCNSubtarget &STI = MF->getSubtarget<GCNSubtarget>();
+  const SIInstrInfo *TII = STI.getInstrInfo();
+  const MachineOperand *CalleeOp =
+      TII->getNamedOperand(MI, AMDGPU::OpName::callee);
+  if (!CalleeOp || !CalleeOp->isGlobal())
+    return;
+  DirectCallEdges.insert(
+      {getSymbol(&MF->getFunction()), getSymbol(CalleeOp->getGlobal())});
+}
+
+void AMDGPUAsmPrinter::emitCallGraphSection(Module &M) {
+  if (!AMDGPUTargetMachine::EnableObjectLinking)
+    return;
+
+  const NamedMDNode *LdsMD = M.getNamedMetadata("amdgpu.lds.uses");
+  bool HasLdsUses = LdsMD && LdsMD->getNumOperands() > 0;
+
+  const NamedMDNode *BarMD = M.getNamedMetadata("amdgpu.named_barrier.uses");
+  bool HasNamedBarriers = BarMD && BarMD->getNumOperands() > 0;
+
+  // Collect address-taken functions (with type IDs) and indirect call sites.
+  DenseMap<const Function *, std::string> AddrTakenTypeIds;
+  using IndirectCallInfo = std::pair<const Function *, std::string>;
+  SmallVector<IndirectCallInfo, 8> IndirectCalls;
+
+  for (const Function &F : M) {
+    bool IsKernel = AMDGPU::isKernel(F.getCallingConv());
+
+    if (!IsKernel && F.hasAddressTaken(/*PutOffender=*/nullptr,
+                                       /*IgnoreCallbackUses=*/false,
+                                       /*IgnoreAssumeLikeCalls=*/true,
+                                       /*IgnoreLLVMUsed=*/true)) {
+      AddrTakenTypeIds[&F] =
+          computeTypeId(F.getFunctionType(), M.getDataLayout());
+    }
+
+    if (F.isDeclaration())
+      continue;
+
+    StringSet<> SeenTypeIds;
+    for (const BasicBlock &BB : F) {
+      for (const Instruction &I : BB) {
+        const auto *CB = dyn_cast<CallBase>(&I);
+        if (!CB || !CB->isIndirectCall())
+          continue;
+        std::string TId =
+            computeTypeId(CB->getFunctionType(), M.getDataLayout());
+        if (SeenTypeIds.insert(TId).second)
+          IndirectCalls.push_back({&F, std::move(TId)});
+      }
+    }
+  }
+
+  if (FunctionResourceInfos.empty() && DirectCallEdges.empty() && !HasLdsUses &&
+      !HasNamedBarriers && AddrTakenTypeIds.empty() && IndirectCalls.empty())
+    return;
+
+  AMDGPU::InfoSectionData Data;
+
+  DenseSet<MCSymbol *> DefinedSyms;
+
+  for (const auto &PFRI : FunctionResourceInfos) {
+    MCSymbol *Sym = getSymbol(PFRI.F);
+    DefinedSyms.insert(Sym);
+
+    AMDGPU::FuncInfo FI;
+    FI.Sym = Sym;
+    FI.IsKernel = AMDGPU::isKernel(PFRI.F->getCallingConv());
+    FI.NumArchVGPR = PFRI.RI.NumVGPR;
+    FI.NumAccVGPR = PFRI.RI.NumAGPR;
+    FI.NumSGPR = PFRI.RI.NumExplicitSGPR;
+    FI.NumNamedBarrier = PFRI.RI.NumNamedBarrier;
+    FI.PrivateSegmentSize = static_cast<uint32_t>(PFRI.RI.PrivateSegmentSize);
+    FI.OccupancyLDSLimit = PFRI.OccupancyLDSLimit;
+    FI.UsesVCC = PFRI.RI.UsesVCC;
+    FI.UsesFlatScratch = PFRI.RI.UsesFlatScratch;
+    FI.HasDynStack = PFRI.RI.HasDynamicallySizedStack;
+
+    Data.Funcs.push_back(std::move(FI));
+  }
+
+  for (auto &[F, TypeId] : AddrTakenTypeIds) {
+    MCSymbol *Sym = getSymbol(F);
+    Data.Signatures.push_back({Sym, TypeId});
+  }
+
+  for (auto &[CallerSym, CalleeSym] : DirectCallEdges)
+    Data.Calls.push_back({CallerSym, CalleeSym});
+  DirectCallEdges.clear();
+
+  if (HasLdsUses) {
+    for (const MDNode *N : LdsMD->operands()) {
+      auto *Func = mdconst::extract<Function>(N->getOperand(0));
+      auto *LdsVar = mdconst::extract<GlobalVariable>(N->getOperand(1));
+      Data.Uses.push_back({getSymbol(Func), getSymbol(LdsVar)});
+    }
+  }
+
+  if (HasNamedBarriers) {
+    for (const MDNode *N : BarMD->operands()) {
+      auto *BarVar = mdconst::extract<GlobalVariable>(N->getOperand(0));
+      MCSymbol *BarSym = getSymbol(BarVar);
+      for (unsigned I = 1, E = N->getNumOperands(); I < E; ++I) {
+        auto *Func = mdconst::extract<Function>(N->getOperand(I));
+        Data.Uses.push_back({getSymbol(Func), BarSym});
+      }
+    }
+  }
+
+  for (auto &[Caller, Enc] : IndirectCalls) {
+    MCSymbol *CallerSym = getSymbol(Caller);
+    Data.IndirectCalls.push_back({CallerSym, Enc});
+  }
+
+  getTargetStreamer()->EmitAMDGPUInfo(Data);
+}
+
 bool AMDGPUAsmPrinter::doFinalization(Module &M) {
   // Pad with s_code_end to help tools and guard against instruction prefetch
   // causing stale data in caches. Arguably this should be done by the linker,
@@ -553,6 +698,9 @@ bool AMDGPUAsmPrinter::doFinalization(Module &M) {
     }
   }
 
+  // Emit unified .amdgpu.callgraph section (call graph + resource usage).
+  emitCallGraphSection(M);
+
   // Assign expressions which can only be resolved when all other functions are
   // known.
   RI.finalize(OutContext);
@@ -567,8 +715,10 @@ bool AMDGPUAsmPrinter::doFinalization(Module &M) {
       RI.getMaxSGPRSymbol(OutContext), RI.getMaxNamedBarrierSymbol(OutContext));
   OutStreamer->popSection();
 
-  for (Function &F : M.functions())
-    validateMCResourceInfo(F);
+  if (!AMDGPUTargetMachine::EnableObjectLinking) {
+    for (Function &F : M.functions())
+      validateMCResourceInfo(F);
+  }
 
   RI.reset();
 
@@ -729,6 +879,26 @@ bool AMDGPUAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
 
   RI.gatherResourceInfo(MF, *ResourceUsage, OutContext);
 
+  if (AMDGPUTargetMachine::EnableObjectLinking) {
+    PerFunctionResourceInfo PFRI = {&MF.getFunction(), *ResourceUsage};
+    if (AMDGPU::isKernel(MF.getFunction().getCallingConv())) {
+      unsigned TotalLDS = STM.getLocalMemorySize();
+      const auto [MinWEU, MaxWEU] = AMDGPU::getIntegerPairAttribute(
+          MF.getFunction(), "amdgpu-waves-per-eu", {0, 0}, true);
+      if (MinWEU > 0) {
+        const SIMachineFunctionInfo &SIMFI =
+            *MF.getInfo<SIMachineFunctionInfo>();
+        unsigned FlatWGSizeMax = SIMFI.getFlatWorkGroupSizes().second;
+        unsigned WavesPerWG = divideCeil(FlatWGSizeMax, STM.getWavefrontSize());
+        unsigned MinWGs = divideCeil(MinWEU * STM.getEUsPerCU(), WavesPerWG);
+        PFRI.OccupancyLDSLimit = MinWGs > 0 ? TotalLDS / MinWGs : TotalLDS;
+      } else {
+        PFRI.OccupancyLDSLimit = TotalLDS;
+      }
+    }
+    FunctionResourceInfos.push_back(PFRI);
+  }
+
   if (MFI->isModuleEntryFunction()) {
     getSIProgramInfo(CurrentProgramInfo, MF);
   }
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.h b/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.h
index 31d10fe92ca26..073440a73c8e6 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.h
+++ b/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.h
@@ -15,7 +15,9 @@
 #define LLVM_LIB_TARGET_AMDGPU_AMDGPUASMPRINTER_H
 
 #include "AMDGPUMCResourceInfo.h"
+#include "AMDGPUResourceUsageAnalysis.h"
 #include "SIProgramInfo.h"
+#include "llvm/ADT/SetVector.h"
 #include "llvm/CodeGen/AsmPrinter.h"
 
 namespace llvm {
@@ -86,6 +88,18 @@ class AMDGPUAsmPrinter final : public AsmPrinter {
 
   void initTargetStreamer(Module &M);
 
+  void emitCallGraphSection(Module &M);
+  void collectCallEdge(const MachineInstr &MI);
+
+  SetVector<std::pair<MCSymbol *, MCSymbol *>> DirectCallEdges;
+
+  struct PerFunctionResourceInfo {
+    const Function *F;
+    AMDGPUResourceUsageAnalysisImpl::SIFunctionResourceInfo RI;
+    uint32_t OccupancyLDSLimit = 0;
+  };
+  SmallVector<PerFunctionResourceInfo> FunctionResourceInfos;
+
   SmallString<128> getMCExprStr(const MCExpr *Value);
 
   /// Attempts to replace the validation that is missed in getSIProgramInfo due
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUMCInstLower.cpp b/llvm/lib/Target/AMDGPU/AMDGPUMCInstLower.cpp
index 56592bde3b1c7..3c89e3d287b32 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUMCInstLower.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUMCInstLower.cpp
@@ -320,6 +320,9 @@ static void emitVGPRBlockComment(const MachineInstr *MI, const SIInstrInfo *TII,
 }
 
 void AMDGPUAsmPrinter::emitInstruction(const MachineInstr *MI) {
+  if (MI->isCall())
+    collectCallEdge(*MI);
+
   // FIXME: Enable feature predicate checks once all the test pass.
   // AMDGPU_MC::verifyInstructionPredicates(MI->getOpcode(),
   //                                        getSubtargetInfo().getFeatureBits());
diff --git a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
index 3777adc9790e8..4b36f5aff53a5 100644
--- a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
+++ b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
@@ -1382,6 +1382,9 @@ class AMDGPUAsmParser : public MCTargetAsmParser {
     return getRegBitWidth(RCID) / 8;
   }
 
+  AMDGPU::InfoSectionData InfoData;
+  bool HasInfoData = false;
+
 private:
   void createConstantSymbol(StringRef Id, int64_t Val);
 
@@ -1422,6 +1425,7 @@ class AMDGPUAsmParser : public MCTargetAsmParser {
   bool ParseDirectivePALMetadataBegin();
   bool ParseDirectivePALMetadata();
   bool ParseDirectiveAMDGPULDS();
+  bool ParseDirectiveAMDGPUInfo();
 
   /// Common code to parse out a block of text (typically YAML) between start and
   /// end directives.
@@ -1676,6 +1680,7 @@ class AMDGPUAsmParser : public MCTargetAsmParser {
                                uint64_t &ErrorInfo,
                                bool MatchingInlineAsm) override;
   bool ParseDirective(AsmToken DirectiveID) override;
+  void onEndOfFile() override;
   ParseStatus parseOperand(OperandVector &Operands, StringRef Mnemonic,
                            OperandMode Mode = OperandMode_Default);
   StringRef parseMnemonicSuffix(StringRef Name);
@@ -6741,6 +6746,115 @@ bool AMDGPUAsmParser::ParseDirectiveAMDGPULDS() {
   return false;
 }
 
+bool AMDGPUAsmParser::ParseDirectiveAMDGPUInfo() {
+  if (getParser().checkForValidSection())
+    return true;
+
+  StringRef FuncName;
+  if (getParser().parseIdentifier(FuncName))
+    return TokError("expected symbol name after .amdgpu_info");
+
+  MCSymbol *FuncSym = getContext().getOrCreateSymbol(FuncName);
+  AMDGPU::FuncInfo FI;
+  FI.Sym = FuncSym;
+  bool HasScalarAttrs = false;
+
+  while (true) {
+    while (trySkipToken(AsmToken::EndOfStatement))
+      ;
+
+    StringRef ID;
+    SMLoc IDLoc = getLoc();
+    if (!parseId(ID, "expected directive or .end_amdgpu_info"))
+      return true;
+
+    if (ID == ".end_amdgpu_info")
+      break;
+
+    if (ID == ".amdgpu_flags") {
+      int64_t Val;
+      if (getParser().parseAbsoluteExpression(Val))
+        return true;
+      uint32_t Flags = static_cast<uint32_t>(Val);
+      FI.IsKernel = (Flags & AMDGPU::FUNC_IS_KERNEL) != 0;
+      FI.UsesVCC = (Flags & AMDGPU::FUNC_USES_VCC) != 0;
+      FI.UsesFlatScratch = (Flags & AMDGPU::FUNC_USES_FLAT_SCRATCH) != 0;
+      FI.HasDynStack = (Flags & AMDGPU::FUNC_HAS_DYN_STACK) != 0;
+      HasScalarAttrs = true;
+    } else if (ID == ".amdgpu_num_vgpr") {
+      int64_t Val;
+      if (getParser().parseAbsoluteExpression(Val))
+        return true;
+      FI.NumArchVGPR = static_cast<uint32_t>(Val);
+      HasScalarAttrs = true;
+    } else if (ID == ".amdgpu_num_agpr") {
+      int64_t Val;
+      if (getParser().parseAbsoluteExpression(Val))
+        return true;
+      FI.NumAccVGPR = static_cast<uint32_t>(Val);
+      HasScalarAttrs = true;
+    } else if (ID == ".amdgpu_num_sgpr") {
+      int64_t Val;
+      if (getParser().parseAbsoluteExpression(Val))
+        return true;
+      FI.NumSGPR = static_cast<uint32_t>(Val);
+      HasScalarAttrs = true;
+    } else if (ID == ".amdgpu_num_named_barrier") {
+      int64_t Val;
+      if (getParser().parseAbsoluteExpression(Val))
+        return true;
+      FI.NumNamedBarrier = static_cast<uint32_t>(Val);
+      HasScalarAttrs = true;
+    } else if (ID == ".amdgpu_private_segment_size") {
+      int64_t Val;
+      if (getParser().parseAbsoluteExpression(Val))
+        return true;
+      FI.PrivateSegmentSize = static_cast<uint32_t>(Val);
+      HasScalarAttrs = true;
+    } else if (ID == ".amdgpu_occupancy_lds_limit") {
+      int64_t Val;
+      if (getParser().parseAbsoluteExpression(Val))
+        return true;
+      FI.OccupancyLDSLimit = static_cast<uint32_t>(Val);
+      HasScalarAttrs = true;
+    } else if (ID == ".amdgpu_use") {
+      StringRef ResName;
+      if (getParser().parseIdentifier(ResName))
+        return TokError("expected resource symbol for .amdgpu_use");
+      InfoData.Uses.push_back(
+          {FuncSym, getContext().getOrCreateSymbol(ResName)});
+    } else if (ID == ".amdgpu_call") {
+      StringRef DstName;
+      if (getParser().parseIdentifier(DstName))
+        return TokError("expected callee symbol for .amdgpu_call");
+      InfoData.Calls.push_back(
+          {FuncSym, getContext().getOrCreateSymbol(DstName)});
+    } else if (ID == ".amdgpu_indirect_call") {
+      std::string TypeId;
+      if (getParser().parseEscapedString(TypeId))
+        return TokError("expected type ID string for .amdgpu_indirect_call");
+      InfoData.IndirectCalls.push_back({FuncSym, std::move(TypeId)});
+    } else if (ID == ".amdgpu_signature") {
+      std::string TypeId;
+      if (getParser().parseEscapedString(TypeId))
+        return TokError("expected type ID string for .amdgpu_signature");
+      InfoData.Signatures.push_back({FuncSym, std::move(TypeId)});
+    } else {
+      return Error(IDLoc, "unknown .amdgpu_info directive '" + ID + "'");
+    }
+  }
+
+  if (HasScalarAttrs)
+    InfoData.Funcs.push_back(std::move(FI));
+  HasInfoData = true;
+  return false;
+}
+
+void AMDGPUAsmParser::onEndOfFile() {
+  if (HasInfoData)
+    getTargetStreamer().EmitAMDGPUInfo(InfoData);
+}
+
 bool AMDGPUAsmParser::ParseDirective(AsmToken DirectiveID) {
   StringRef IDVal = DirectiveID.getString();
 
@@ -6778,6 +6892,9 @@ bool AMDGPUAsmParser::ParseDirective(AsmToken DirectiveID) {
   if (IDVal == ".amdgpu_lds")
     return ParseDirectiveAMDGPULDS();
 
+  if (IDVal == ".amdgpu_info")
+    return ParseDirectiveAMDGPUInfo();
+
   if (IDVal == PALMD::AssemblerDirectiveBegin)
     return ParseDirectivePALMetadataBegin();
 
diff --git a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp
index d276bab0ff3be..95ff7e0aac1ea 100644
--- a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp
+++ b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp
@@ -664,6 +664,76 @@ void AMDGPUTargetAsmStreamer::EmitAmdhsaKernelDescriptor(
   OS << "\t.end_amdhsa_kernel\n";
 }
 
+void AMDGPUTargetAsmStreamer::EmitAMDGPUInfo(
+    const AMDGPU::InfoSectionData &Data) {
+  // Group edges by source function symbol.
+  DenseMap<MCSymbol *, SmallVector<MCSymbol *, 2>> FuncUses;
+  DenseMap<MCSymbol *, SmallVector<MCSymbol *, 4>> FuncCalls;
+  DenseMap<MCSymbol *, SmallVector<StringRef, 2>> FuncIndirectCalls;
+  DenseMap<MCSymbol *, SmallVector<StringRef, 1>> FuncSignatures;
+  for (const auto &[Func, Res] : Data.Uses)
+    FuncUses[Func].push_back(Res);
+  for (const auto &[Src, Dst] : Data.Calls)
+    FuncCalls[Src].push_back(Dst);
+  for (const auto &[Func, TypeId] : Data.IndirectCalls)
+    FuncIndirectCalls[Func].push_back(TypeId);
+  for (const auto &[Sym, TypeId] : Data.Signatures)
+    FuncSignatures[Sym].push_back(TypeId);
+
+  DenseSet<MCSymbol *> Emitted;
+  auto EmitScope = [&](MCSymbol *Sym, const AMDGPU::FuncInfo *Info) {
+    if (!Emitted.insert(Sym).second)
+      return;
+    OS << "\t.amdgpu_info " << Sym->getName() << '\n';
+    if (Info) {
+      uint32_t Flags = 0;
+      if (Info->IsKernel)
+        Flags |= AMDGPU::FUNC_IS_KERNEL;
+      if (Info->UsesVCC)
+        Flags |= AMDGPU::FUNC_USES_VCC;
+      if (Info->UsesFlatScratch)
+        Flags |= AMDGPU::FUNC_USES_FLAT_SCRATCH;
+      if (Info->HasDynStack)
+        Flags |= AMDGPU::FUNC_HAS_DYN_STACK;
+      OS << "\t\t.amdgpu_flags " << Flags << '\n';
+      OS << "\t\t.amdgpu_num_vgpr " << Info->NumArchVGPR << '\n';
+      OS << "\t\t.amdgpu_num_agpr " << Info->NumAccVGPR << '\n';
+      OS << "\t\t.amdgpu_num_sgpr " << Info->NumSGPR << '\n';
+      OS << "\t\t.amdgpu_num_named_barrier " << Info->NumNamedBarrier << '\n';
+      OS << "\t\t.amdgpu_private_segment_size " << Info->PrivateSegmentSize
+         << '\n';
+      OS << "\t\t.amdgpu_occupancy_lds_limit " << Info->OccupancyLDSLimit
+         << '\n';
+    }
+    if (auto It = FuncUses.find(Sym); It != FuncUses.end())
+      for (MCSymbol *Res : It->second)
+        OS << "\t\t.amdgpu_use " << Res->getName() << '\n';
+    if (auto It = FuncCalls.find(Sym); It != FuncCalls.end())
+      for (MCSymbol *Dst : It->second)
+        OS << "\t\t.amdgpu_call " << Dst->getName() << '\n';
+    if (auto It = FuncIndirectCalls.find(Sym); It != FuncIndirectCalls.end())
+      for (StringRef TypeId : It->second)
+        OS << "\t\t.amdgpu_indirect_call \"" << TypeId << "\"\n";
+    if (auto It = FuncSignatures.find(Sym); It != FuncSignatures.end())
+      for (StringRef TypeId : It->second)
+        OS << "\t\t.amdgpu_signature \"" << TypeId...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/192384


More information about the llvm-branch-commits mailing list