[Mlir-commits] [clang] [llvm] [mlir] [AMDGPU] Implement AsyncMark Stages (PR #220442)
Krzysztof Drewniak
llvmlistbot at llvm.org
Tue Oct 13 11:46:04 PDT 2026
================
@@ -0,0 +1,205 @@
+//===- AMDGPUAsyncStages.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
+/// Shared AMDGPU asyncmark stage definitions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_AMDGPUASYNCSTAGES_H
+#define LLVM_SUPPORT_AMDGPUASYNCSTAGES_H
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/bit.h"
+#include "llvm/ADT/iterator.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstdint>
+#include <iterator>
+
+namespace llvm {
+namespace AMDGPU {
+
+/// Bit mask of the async stages tracked by the asyncmark / wait_asyncmark
+/// intrinsics. Each stage has its own independent sequence of marks.
+///
+/// A single stage is represented as a mask with exactly one bit set. Iterating
+/// over a mask visits each stage it names, from the least significant bit up.
+///
+/// This type also provides certain stronger guarantees than a simple integer:
+/// - Default constructor initializes the mask to zero.
+/// - Constructor ensures undefined bits cannot be set.
+class AsyncStages {
+public:
+ using value_type = uint32_t;
+
+ static constexpr unsigned NUM_STAGES = 11;
+
+ // The bit of each stage is part of the IR, so do not renumber them. Some
+ // values are RESERVED for later use.
+ enum : value_type {
+ NONE = 0,
+ // Tensor loads to LDS and tensor stores from LDS.
+ TENSOR = 1u << 0,
+ // Asynchronous global loads to LDS.
+ GLOBAL_LOAD_ASYNC_TO_LDS = 1u << 1,
+ // Asynchronous multicast (cluster) global loads to LDS.
+ GLOBAL_LOAD_ASYNC_TO_LDS_MCAST = 1u << 2,
+ // Asynchronous global stores from LDS.
+ GLOBAL_STORE_ASYNC_FROM_LDS = 1u << 3,
+ // Held for downstream use.
+ RESERVED_4 = 1u << 4,
+ // Buffer loads to LDS and pre-gfx1250 global loads to LDS.
+ BUFFER_GLOBAL_LOAD = 1u << 5,
+ RESERVED_6 = 1u << 6,
+ RESERVED_7 = 1u << 7,
+ RESERVED_8 = 1u << 8,
+ RESERVED_9 = 1u << 9,
+ RESERVED_10 = 1u << 10,
+
+ RESERVED = RESERVED_4 | RESERVED_6 | RESERVED_7 | RESERVED_8 | RESERVED_9 |
+ RESERVED_10,
+
+ // Bits that a mask may legally set. Reserved stages are included: naming a
+ // stage whose operations do not exist yet is harmless, and accepting the
+ // bit keeps masks portable as stages are filled in.
+ ALL = (1u << NUM_STAGES) - 1
+ };
+
+ /// Iterates over the stages named by a mask.
+ /// NOLINTNEXTLINE
+ class const_iterator
+ : public iterator_facade_base<const_iterator, std::forward_iterator_tag,
+ AsyncStages> {
+ // The "end" iterator is also the default-constructed iterator.
+ // We naturally move towards the "end" by clearing the set bits from least
+ // to most significant.
+ AsyncStages::value_type Cur = 0;
+
+ public:
+ const_iterator() = default;
+ const_iterator(AsyncStages S) : Cur(S.value()) {}
+
+ bool operator==(const const_iterator &Other) const {
+ return Cur == Other.Cur;
+ }
+
+ AsyncStages operator*() const {
+ // Return only rightmost (least significant) bit set.
+ return Cur ? (Cur & (1u << countr_zero(Cur))) : 0;
+ }
+
+ const_iterator &operator++() {
+ // Keep all bits except the least significant bit set.
+ Cur &= maskTrailingZeros<AsyncStages::value_type>(countr_zero(Cur) + 1);
+ return *this;
+ }
+ };
+
+ constexpr AsyncStages() = default;
+ constexpr AsyncStages(value_type V) : Data(V) {
+ assert((V & ALL) == V && "Bits set out of bounds!");
+ }
+
+ /// \returns true if \p Imm is a legal stage mask operand of the asyncmark /
+ /// wait_asyncmark intrinsics and pseudos.
+ static constexpr bool isValidMask(int64_t Imm) {
+ return Imm >= 0 && (Imm & ~int64_t(ALL)) == 0;
+ }
+
+ /// \returns the stages named by the stage mask operand \p Imm of the
+ /// asyncmark / wait_asyncmark intrinsics and pseudos. The zero mask is the
+ /// one exception to a set bit naming a stage: it names every stage rather
+ /// than none.
+ static constexpr AsyncStages fromMaskOperand(value_type Imm) {
+ return Imm ? AsyncStages(Imm) : AsyncStages(ALL);
+ }
+
+ constexpr unsigned size() const { return popcount(Data); }
+ constexpr bool any() const { return Data != 0; }
+ constexpr bool none() const { return Data == 0; }
+ constexpr value_type value() const { return Data; }
+
+ explicit constexpr operator bool() const { return any(); }
+
+ const_iterator begin() const { return *this; }
+ const_iterator end() const { return {}; }
+
+ /// \returns the position of this single stage in a mask, for indexing
+ /// per-stage data.
+ unsigned index() const {
+ assert(size() == 1 && "Expected a single stage");
+ return countr_zero(Data);
+ }
+
+ /// \returns the name of this single stage.
+ const char *getName() const {
+ switch (Data) {
+ case TENSOR:
+ return "TENSOR";
+ case GLOBAL_LOAD_ASYNC_TO_LDS:
+ return "GLOBAL_LOAD_ASYNC_TO_LDS";
+ case GLOBAL_LOAD_ASYNC_TO_LDS_MCAST:
+ return "GLOBAL_LOAD_ASYNC_TO_LDS_MCAST";
+ case GLOBAL_STORE_ASYNC_FROM_LDS:
+ return "GLOBAL_STORE_ASYNC_FROM_LDS";
+ case RESERVED_4:
+ return "RESERVED_4";
+ case BUFFER_GLOBAL_LOAD:
+ return "BUFFER_GLOBAL_LOAD";
+ case RESERVED_6:
+ return "RESERVED_6";
+ case RESERVED_7:
+ return "RESERVED_7";
+ case RESERVED_8:
+ return "RESERVED_8";
+ case RESERVED_9:
+ return "RESERVED_9";
+ case RESERVED_10:
+ return "RESERVED_10";
+ }
+ llvm_unreachable("Expected a single stage");
+ }
+
+ constexpr AsyncStages operator&(AsyncStages Other) const {
+ return Data & Other.Data;
+ }
+ constexpr AsyncStages operator-(AsyncStages Other) const {
+ return Data & ~Other.Data;
+ }
+
+ constexpr bool operator==(AsyncStages Other) const {
+ return Data == Other.Data;
+ }
+
+ constexpr AsyncStages &operator|=(AsyncStages Other) {
+ Data |= Other.Data;
+ return *this;
+ }
+
+private:
+ value_type Data = NONE;
+};
----------------
krzysz00 wrote:
Isn't there a shiny LLVM macro for bitmask enums?
https://github.com/llvm/llvm-project/pull/220442
More information about the Mlir-commits
mailing list