[llvm] [SPIRV] Preserve AMDGPU metadata through NonSemantic.AuxData (PR #215510)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 11 03:24:48 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-spir-v

@llvm/pr-subscribers-backend-amdgpu

Author: Marcos Maronas (maarquitos14)

<details>
<summary>Changes</summary>

The PR implements an extension to `NonSemantic.AuxData` spec to add `NonSemanticAuxDataInstructionMetadata`, which enables preservation of metadata for instructions other than `GlobalVariable` and `Function` objects for some AMDGPU metadata.

Part 2 of #<!-- -->213685 split, stacked on top of #<!-- -->215507.

---

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


19 Files Affected:

- (modified) llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp (+2-5) 
- (modified) llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp (+29-3) 
- (modified) llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h (+3-3) 
- (modified) llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp (+67-11) 
- (modified) llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp (+5) 
- (modified) llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp (+21) 
- (modified) llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h (+25) 
- (modified) llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td (+2) 
- (modified) llvm/lib/Target/SPIRV/SPIRVUtils.cpp (+2) 
- (modified) llvm/lib/Target/SPIRV/SPIRVUtils.h (+8) 
- (modified) llvm/test/CodeGen/SPIRV/amdgcnspirv-atomic-metadata-decoration.ll (+12-9) 
- (added) llvm/test/CodeGen/SPIRV/atomicrmw-uinc-udec-wrap-non-amd.ll (+30) 
- (added) llvm/test/CodeGen/SPIRV/atomicrmw-uinc-udec-wrap-orderings.ll (+66) 
- (added) llvm/test/CodeGen/SPIRV/atomicrmw-uinc-udec-wrap-scopes.ll (+59) 
- (added) llvm/test/CodeGen/SPIRV/atomicrmw-uinc-udec-wrap-signatures.ll (+67) 
- (modified) llvm/test/CodeGen/SPIRV/atomicrmw-uinc-udec-wrap.ll (+20-35) 
- (added) llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata-amdgpu-atomic-metadata.ll (+78) 
- (added) llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata-atomic-metadata-generic-target.ll (+36) 
- (added) llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata-uinc-udec-wrap-metadata.ll (+67) 


``````````diff
diff --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
index 040a6858b9009..3f7bf566f42b5 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
@@ -886,11 +886,8 @@ void SPIRVAsmPrinter::outputModuleSections() {
   MAI = &getAnalysis<SPIRVModuleAnalysis>().MAI;
   assert(ST && TII && MAI && M && "Module analysis is required");
 
-  if (!AuxDataHandler) {
-    auto Handler = std::make_unique<SPIRVAuxDataHandler>(*this, *M);
-    if (Handler->hasWork())
-      AuxDataHandler = std::move(Handler);
-  }
+  if (!AuxDataHandler && spirvPreserveAuxData())
+    AuxDataHandler = std::make_unique<SPIRVAuxDataHandler>(*this, *M);
 
   // Let the NSDI handler add its extension and ext inst import entry to MAI
   // before the module header sections are emitted.
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
index 469c261ac433e..b7af23862d7d2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
@@ -63,11 +63,11 @@ SPIRVAuxDataHandler::SPIRVAuxDataHandler(AsmPrinter &AP, const Module &M)
       LinkagePreservedGOs.push_back(&GO);
 }
 
-bool SPIRVAuxDataHandler::hasWork() const { return SPVPreserveAuxData; }
+bool llvm::spirvPreserveAuxData() { return SPVPreserveAuxData; }
 
 void SPIRVAuxDataHandler::prepareModuleOutput(const SPIRVSubtarget &ST,
                                               SPIRV::ModuleAnalysisInfo &MAI) {
-  if (!hasWork())
+  if (!spirvPreserveAuxData())
     return;
   if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_non_semantic_info)) {
     if (SPVPreserveAuxData)
@@ -76,7 +76,7 @@ void SPIRVAuxDataHandler::prepareModuleOutput(const SPIRVSubtarget &ST,
     return;
   }
   MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info);
-  if (!MAI.ExtInstSetMap.count(NonSemanticAuxDataSet))
+  if (!MAI.ExtInstSetMap.contains(NonSemanticAuxDataSet))
     MAI.ExtInstSetMap[NonSemanticAuxDataSet] = MAI.getNextIDRegister();
 }
 
@@ -181,6 +181,22 @@ void SPIRVAuxDataHandler::emitAuxDataStrings(SPIRV::ModuleAnalysisInfo &MAI) {
     collectAttributesFor(&GO, MAI);
     collectMetadataFor(&GO, MDNames, MAI);
   }
+  // Only a handful of distinct metadata names exist, one per AMDGPUAtomicMDKind
+  // enumerator. Track which we've seen so we can stop once every name has been
+  // emitted, instead of scanning potentially thousands of records with
+  // redundant hash lookups.
+  constexpr unsigned AllMDKindsSeen =
+      (1u << (static_cast<unsigned>(
+                  SPIRV::ModuleAnalysisInfo::AMDGPUAtomicMDKind::Last) +
+              1)) -
+      1;
+  unsigned SeenMask = 0;
+  for (const auto &Rec : MAI.InstrAuxDataRecords) {
+    SeenMask |= 1u << static_cast<unsigned>(Rec.Kind);
+    getOrEmitString(MAI.getAMDGPUAtomicMDName(Rec.Kind), MAI);
+    if (SeenMask == AllMDKindsSeen)
+      break;
+  }
 }
 
 void SPIRVAuxDataHandler::emitAuxData(SPIRV::ModuleAnalysisInfo &MAI) {
@@ -201,6 +217,16 @@ void SPIRVAuxDataHandler::emitAuxData(SPIRV::ModuleAnalysisInfo &MAI) {
     emitAuxDataExtInst(Rec.Opcode, VoidTypeReg, ExtSetReg, Operands, MAI);
   }
 
+  for (const auto &Rec : MAI.InstrAuxDataRecords) {
+    MCRegister TargetReg = MAI.getRegisterAlias(Rec.MF, Rec.TargetReg);
+    if (!TargetReg.isValid())
+      continue;
+    MCRegister MDNameReg =
+        getOrEmitString(MAI.getAMDGPUAtomicMDName(Rec.Kind), MAI);
+    emitAuxDataExtInst(InstructionMetadataOpcode, VoidTypeReg, ExtSetReg,
+                       {TargetReg, MDNameReg}, MAI);
+  }
+
   if (LinkagePreservedGOs.empty())
     return;
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
index 1d6ba998e6c2e..6ad5c45f6a086 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
@@ -29,7 +29,6 @@ namespace llvm {
 
 class AsmPrinter;
 class Constant;
-class Function;
 class GlobalObject;
 class Module;
 class SPIRVSubtarget;
@@ -42,14 +41,13 @@ enum AuxDataOpcode : int64_t {
   GlobalVariableMetadataOpcode = 2,
   GlobalVariableAttributeOpcode = 3,
   LinkageOpcode = 4,
+  InstructionMetadataOpcode = 5,
 };
 
 class SPIRVAuxDataHandler {
 public:
   SPIRVAuxDataHandler(AsmPrinter &AP, const Module &M);
 
-  bool hasWork() const;
-
   /// Register extension + ext-inst-set; call before output of section 1.
   void prepareModuleOutput(const SPIRVSubtarget &ST,
                            SPIRV::ModuleAnalysisInfo &MAI);
@@ -111,6 +109,8 @@ class SPIRVAuxDataHandler {
                           SPIRV::ModuleAnalysisInfo &MAI);
 };
 
+bool spirvPreserveAuxData();
+
 } // namespace llvm
 
 #endif // LLVM_LIB_TARGET_SPIRV_SPIRVAUXDATAHANDLER_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 8d0b80b9a2e1c..34544783faac6 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -13,6 +13,7 @@
 
 #include "SPIRVEmitIntrinsics.h"
 #include "SPIRV.h"
+#include "SPIRVAuxDataHandler.h"
 #include "SPIRVBuiltins.h"
 #include "SPIRVSubtarget.h"
 #include "SPIRVTargetMachine.h"
@@ -394,6 +395,7 @@ class SPIRVEmitIntrinsicsImpl
   Instruction *visitStoreInst(StoreInst &I);
   Instruction *visitAllocaInst(AllocaInst &I);
   Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
+  Instruction *visitAtomicRMWInst(AtomicRMWInst &I);
   Instruction *visitUnreachableInst(UnreachableInst &I);
   Instruction *visitCallInst(CallInst &I);
 
@@ -2620,6 +2622,61 @@ SPIRVEmitIntrinsicsImpl::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) {
   return NewI;
 }
 
+Instruction *SPIRVEmitIntrinsicsImpl::visitAtomicRMWInst(AtomicRMWInst &I) {
+  auto Op = I.getOperation();
+  if (Op != AtomicRMWInst::UIncWrap && Op != AtomicRMWInst::UDecWrap)
+    return &I;
+
+  // Carrying these across the SPIR-V boundary as a call to an imported helper
+  // is an AMD extension: there is no SPIR-V opcode for them, so a consumer has
+  // to recognize the helper by name to make sense of the module. Restrict it to
+  // AMD targets and let everyone else keep the generic expansion.
+  if (!isAMDTarget(TM.getTargetTriple()))
+    return &I;
+
+  Module *M = I.getModule();
+  IRBuilder<> B(I.getParent());
+  B.SetInsertPoint(&I);
+
+  const SPIRVSubtarget &ST = TM.getSubtarget<SPIRVSubtarget>(*I.getFunction());
+  unsigned AS = I.getPointerOperand()->getType()->getPointerAddressSpace();
+
+  uint32_t Scope = static_cast<uint32_t>(
+      getMemScope(TM.getTargetTriple(), I.getContext(), I.getSyncScopeID()));
+  uint32_t ScSem = static_cast<uint32_t>(
+      getMemSemanticsForStorageClass(addressSpaceToStorageClass(AS, ST)));
+  uint32_t MemSem =
+      static_cast<uint32_t>(getMemSemantics(I.getOrdering())) | ScSem;
+
+  std::string FuncName = (Op == AtomicRMWInst::UIncWrap)
+                             ? "__translate_spirv_atomic_uinc_wrap"
+                             : "__translate_spirv_atomic_udec_wrap";
+
+  Type *ValTy = I.getValOperand()->getType();
+  Type *PtrTy = I.getPointerOperand()->getType();
+  // Encode the address space and the value type in the name, the same way
+  // lowerLLVMIntrinsicName() does for spirv.llvm_memset_p1_i64. A module may
+  // need several mutually incompatible signatures, while SPIR-V resolves an
+  // imported function by its linkage name alone.
+  FuncName += "_p" + std::to_string(AS) + "_i" +
+              std::to_string(ValTy->getIntegerBitWidth());
+
+  Type *Int32Ty = B.getInt32Ty();
+  SmallVector<Type *, 4> ArgTys = {PtrTy, Int32Ty, Int32Ty, ValTy};
+  FunctionType *FT = FunctionType::get(ValTy, ArgTys, false);
+  FunctionCallee FC = M->getOrInsertFunction(FuncName, FT);
+  if (auto *F = dyn_cast<Function>(FC.getCallee()))
+    F->setCallingConv(CallingConv::SPIR_FUNC);
+
+  SmallVector<Value *, 4> Args = {I.getPointerOperand(), B.getInt32(Scope),
+                                  B.getInt32(MemSem), I.getValOperand()};
+  CallInst *CI = B.CreateCall(FC, Args);
+  CI->setCallingConv(CallingConv::SPIR_FUNC);
+
+  replaceAllUsesWithAndErase(B, &I, CI);
+  return CI;
+}
+
 static bool isAbortCall(const Instruction &I, const SPIRVSubtarget &ST) {
   auto *CI = dyn_cast<CallInst>(&I);
   if (!CI)
@@ -3015,27 +3072,26 @@ void SPIRVEmitIntrinsicsImpl::insertSpirvDecorations(Instruction *I,
                       {I->getType()},
                       {I, MetadataAsValue::get(I->getContext(), MD)});
   }
-  if (I->getModule()->getTargetTriple().getVendor() == Triple::AMD &&
-      isa<AtomicRMWInst>(I)) {
-    // If present, we encode AMDGPU atomic metadata as UserSemantic string
-    // decorations, which will be parsed during reverse translation.
-    auto &Ctx = B.getContext();
-    auto *US = ConstantAsMetadata::get(
-        ConstantInt::get(B.getInt32Ty(), SPIRV::Decoration::UserSemantic));
+  if (spirvPreserveAuxData() && isa<AtomicRMWInst>(I)) {
+    LLVMContext &Ctx = B.getContext();
+    auto *AuxMD = ConstantAsMetadata::get(ConstantInt::get(
+        B.getInt32Ty(), SPIRV::Decoration::AuxDataInstructionMetadata));
 
     SmallVector<Metadata *> MDs;
     if (I->hasMetadata("amdgpu.no.fine.grained.memory"))
       MDs.push_back(MDNode::get(
-          Ctx, {US, MDString::get(Ctx, "amdgpu.no.fine.grained.memory")}));
+          Ctx, {AuxMD, MDString::get(Ctx, "amdgpu.no.fine.grained.memory")}));
     if (I->hasMetadata("amdgpu.no.remote.memory"))
       MDs.push_back(MDNode::get(
-          Ctx, {US, MDString::get(Ctx, "amdgpu.no.remote.memory")}));
+          Ctx, {AuxMD, MDString::get(Ctx, "amdgpu.no.remote.memory")}));
     if (I->hasMetadata("amdgpu.ignore.denormal.mode"))
       MDs.push_back(MDNode::get(
-          Ctx, {US, MDString::get(Ctx, "amdgpu.ignore.denormal.mode")}));
-    if (!MDs.empty())
+          Ctx, {AuxMD, MDString::get(Ctx, "amdgpu.ignore.denormal.mode")}));
+    if (!MDs.empty()) {
+      setInsertPointAfterDef(B, I);
       B.CreateIntrinsic(Intrinsic::spv_assign_decoration, {I->getType()},
                         {I, MetadataAsValue::get(Ctx, MDNode::get(Ctx, MDs))});
+    }
   }
 }
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
index 245d2745f1498..674d9f3e5eabf 100644
--- a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
@@ -679,6 +679,11 @@ SPIRVTargetLowering::shouldExpandAtomicRMWInIR(const AtomicRMWInst *RMW) const {
     return AtomicExpansionKind::None;
   case AtomicRMWInst::UIncWrap:
   case AtomicRMWInst::UDecWrap:
+    // On AMD targets these are translated into a call to an imported helper by
+    // SPIRVEmitIntrinsics, so they must survive to that point unexpanded. Any
+    // other target has no such helper and needs the generic expansion.
+    return isAMDTarget(STI.getTargetTriple()) ? AtomicExpansionKind::None
+                                              : AtomicExpansionKind::CmpXChg;
   case AtomicRMWInst::Nand:
     return AtomicExpansionKind::CmpXChg;
   default:
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 2836a2cc43ef0..7cee61f9e0a55 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -26,6 +26,7 @@
 #include "SPIRVTargetMachine.h"
 #include "SPIRVUtils.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/CodeGen/MachineModuleInfo.h"
 #include "llvm/CodeGen/TargetPassConfig.h"
 
@@ -747,6 +748,26 @@ void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
         } else if (TII->isAliasingInstr(MI)) {
           collectOtherInstr(MI, MAI, SPIRV::MB_AliasingInsts, IS);
         } else if (TII->isDecorationInstr(MI)) {
+          if (MI.getOpcode() == SPIRV::OpDecorate &&
+              MI.getOperand(1).getImm() ==
+                  static_cast<unsigned>(
+                      SPIRV::Decoration::AuxDataInstructionMetadata)) {
+            MAI.setSkipEmission(&MI);
+            std::string Str = getStringImm(MI, 2);
+            using AMDMD = SPIRV::ModuleAnalysisInfo::AMDGPUAtomicMDKind;
+            auto MaybeKind =
+                StringSwitch<std::optional<AMDMD>>(Str)
+                    .Case("amdgpu.no.fine.grained.memory",
+                          AMDMD::NoFineGrainedMemory)
+                    .Case("amdgpu.no.remote.memory", AMDMD::NoRemoteMemory)
+                    .Case("amdgpu.ignore.denormal.mode",
+                          AMDMD::IgnoreDenormalMode)
+                    .Default(std::nullopt);
+            if (MaybeKind)
+              MAI.InstrAuxDataRecords.push_back(
+                  {MF, MI.getOperand(0).getReg(), *MaybeKind});
+            continue;
+          }
           collectOtherInstr(MI, MAI, SPIRV::MB_Annotations, IS);
           collectFuncNames(MI, &F);
         } else if (TII->isConstantInstr(MI)) {
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
index 6559b5cfc7457..8114aa63610d1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
@@ -167,6 +167,20 @@ struct ModuleAnalysisInfo {
   DenseMap<const Function *, SPIRV::FPFastMathDefaultInfoVector>
       FPFastMathDefaultInfoMap;
 
+  enum class AMDGPUAtomicMDKind : uint8_t {
+    NoFineGrainedMemory,
+    NoRemoteMemory,
+    IgnoreDenormalMode,
+
+    Last = IgnoreDenormalMode,
+  };
+  struct InstrAuxDataRecord {
+    const MachineFunction *MF;
+    Register TargetReg;
+    AMDGPUAtomicMDKind Kind;
+  };
+  SmallVector<InstrAuxDataRecord> InstrAuxDataRecords;
+
   MCRegister getGlobalObjReg(const GlobalObject *GO) {
     assert(GO && "GlobalObject is null");
     return GlobalObjMap.lookup(GO);
@@ -211,6 +225,17 @@ struct ModuleAnalysisInfo {
       It->second = getNextIDRegister();
     return It->second;
   }
+  static StringRef getAMDGPUAtomicMDName(AMDGPUAtomicMDKind Kind) {
+    switch (Kind) {
+    case AMDGPUAtomicMDKind::NoFineGrainedMemory:
+      return "amdgpu.no.fine.grained.memory";
+    case AMDGPUAtomicMDKind::NoRemoteMemory:
+      return "amdgpu.no.remote.memory";
+    case AMDGPUAtomicMDKind::IgnoreDenormalMode:
+      return "amdgpu.ignore.denormal.mode";
+    }
+    llvm_unreachable("unknown AMDGPUAtomicMDKind");
+  }
 };
 } // namespace SPIRV
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index 7da57bc1b2d47..cf4393c4bb884 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -1435,6 +1435,8 @@ defm FunctionFloatingPointModeINTEL : DecorationOperand<6080, 0, 0, [], [Functio
 defm AliasScopeINTEL : DecorationOperand<5914, 0, 0, [], [MemoryAccessAliasingINTEL]>;
 defm NoAliasINTEL : DecorationOperand<5915, 0, 0, [], [MemoryAccessAliasingINTEL]>;
 defm FPMaxErrorDecorationINTEL : DecorationOperand<6170, 0, 0, [], [FPMaxErrorINTEL]>;
+// Internal-only sentinel; intercepted by SPIRVModuleAnalysis, never emitted.
+defm AuxDataInstructionMetadata : DecorationOperand<0xFFFF, 0, 0, [], []>;
 
 //===----------------------------------------------------------------------===//
 // Multiclass used to define BuiltIn enum values and at the same time
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
index ce993299096ad..e1384e900124f 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
@@ -449,6 +449,8 @@ SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord) {
   llvm_unreachable(nullptr);
 }
 
+bool isAMDTarget(const Triple &TT) { return TT.getVendor() == Triple::AMD; }
+
 SPIRV::Scope::Scope getMemScope(const Triple &TT, LLVMContext &Ctx,
                                 SyncScope::ID Id) {
   // Named by
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index b95f09eba95f1..fe18ce47c54a2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -289,6 +289,14 @@ SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord);
 SPIRV::Scope::Scope getMemScope(const Triple &TT, LLVMContext &Ctx,
                                 SyncScope::ID Id);
 
+// Returns true if TT targets an AMD SPIR-V flavour. Gates AMD-specific
+// extensions to the emitted SPIR-V that a generic consumer could not process,
+// such as translating atomicrmw uinc_wrap/udec_wrap into a call to an imported
+// helper. Both the decision to lower and the decision not to expand such an
+// atomicrmw generically must consult this, or the operation reaches the
+// legalizer neither expanded nor lowered.
+bool isAMDTarget(const Triple &TT);
+
 // Find def instruction for the given ConstReg, walking through
 // spv_track_constant and ASSIGN_TYPE instructions. Updates ConstReg by def
 // of OpConstant instruction.
diff --git a/llvm/test/CodeGen/SPIRV/amdgcnspirv-atomic-metadata-decoration.ll b/llvm/test/CodeGen/SPIRV/amdgcnspirv-atomic-metadata-decoration.ll
index 9c6300f374261..3d679fe7e2281 100644
--- a/llvm/test/CodeGen/SPIRV/amdgcnspirv-atomic-metadata-decoration.ll
+++ b/llvm/test/CodeGen/SPIRV/amdgcnspirv-atomic-metadata-decoration.ll
@@ -1,18 +1,21 @@
-; RUN: llc -O0 -mtriple=spirv64-amd-amdhsa %s -o - | FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-amd-amdhsa %s -o - -filetype=obj | spirv-val %}
+; Without -spirv-preserve-auxdata, AMDGPU atomic metadata must not appear
+; as UserSemantic decorations or any other form in the SPIR-V output.
 
-; CHECK: OpDecorate %[[#Add:]] UserSemantic "amdgpu.no.fine.grained.memory"
-; CHECK-NEXT: OpDecorate %[[#Add]] UserSemantic "amdgpu.no.remote.memory"
-; CHECK-NEXT: OpDecorate %[[#FAdd:]] UserSemantic "amdgpu.no.fine.grained.memory"
-; CHECK-NEXT: OpDecorate %[[#FAdd]] UserSemantic "amdgpu.no.remote.memory"
-; CHECK-NEXT: OpDecorate %[[#FAdd]] UserSemantic "amdgpu.ignore.denormal.mode"
-; CHECK: %[[#Add]] = OpAtomicIAdd
-; CHECK: %[[#FAdd]] = OpAtomicFAddEXT
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-amd-amdhsa %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -verify-machineinstrs -O0 -mtriple=spirv64-amd-amdhsa %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-NOT: amdgpu.no.fine.grained.memory
+; CHECK-NOT: amdgpu.no.remote.memory
+; CHECK-NOT: amdgpu.ignore.denormal.mode
+; CHECK: %[[#Add:]] = OpAtomicIAdd
+; CHECK: %[[#FAdd:]] = OpAtomicFAddEXT
+; CHECK: %[[#Xchg:]] = OpAtomicExchange
 
 define spir_func void @foo(ptr addrspace(1) %p) {
 entry:
   %atomic.add = atomicrmw add ptr addrspace(1) %p, i32 1 seq_cst, !amdgpu.no.fine.grained.memory !0, !amdgpu.no.remote.memory !0
   %atomic.fadd = atomicrmw fadd ptr addrspace(1) %p, float 1.0 seq_cst, !amdgpu.no.fine.grained.memory !0, !amdgpu.no.remote.memory !0, !amdgpu.ignore.denormal.mode !0
+  %atomic.xchg = atomicrmw xchg ptr addrspace(1) %p, i32 1 seq_cst, !amdgpu.no.fine.grained.memory !0
   ret void
 }
 
diff --git a/llvm/test/CodeGen/SPIRV/atomicrmw-uinc-udec-wrap-non-amd.ll b/llvm/test/CodeGen/SPIRV/atomicrmw-uinc-udec-wrap-non-amd.ll
new file mode 100644
index 0000000000000..fde6dd15cc0d5
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/atomicrmw-uinc-udec-wrap-non-amd.ll
@@ -0,0 +1,30 @@
+; Translating atomicrmw uinc_wrap/udec_wrap into a call to an imported helper is
+; an AMD extension: there is no SPIR-V opcode for these, so a consumer has to
+; recognize the helper by name to make sense of the module. Verify that a
+; non-AMD target does not emit it, and instead falls back to the generic CmpXChg
+; expansion. The AMD behaviour is covered by atomicrmw-uinc-udec-wrap.ll.
+;
+; --implicit-check-not applies over the whole module, unlike a CHECK-NOT, which
+; would only cover the input up to the first positive match below.
+
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --implicit-check-not=__translate_spirv_atomic
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --implicit-check-not=__translate_spirv_atomic
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+ at ui = common dso_local addrspace(1) global i32 0, align...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list