[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