Wavefront-level builtins for AMD GPUs#

Wavefront-level operations give you direct access to the hardware mechanisms that move and aggregate data within a wavefront. The builtins in this topic work across both CDNA (AMD Instinct) and RDNA (AMD Radeon) architectures, and cover three areas: lane operations, wavefront reductions, and wavefront voting.

Architecture availability#

Most wavefront-level builtins are available on all AMD Instinct (CDNA) and AMD Radeon (RDNA) architectures. Some builtins — particularly Data Parallel Primitives (DPP) DPP8, cross-row permutations, and ballot width variants — are limited to specific generations. For per-builtin availability, see the architecture tables in the reference pages below.

The complete source file is available for download:

Lane operations#

Within a wavefront, lanes execute the same instruction simultaneously, but each holds its own register values. Many algorithms require lanes to exchange or replicate those values - to share a computed result, apply a cyclic shift, or reorganize data before the next computation step. The builtins in this topic cover the distinct ways of expressing that communication: reading from a named lane, moving data in a cyclic pattern, applying a compile-time-fixed permutation, and broadcasting within a fixed-size group. Each fills a different niche, and choosing the right one affects both correctness and performance.

Broadcasting from a specific lane with readlane#

When a single lane holds a value that every other lane needs - a wavefront-wide maximum, a shared configuration parameter, a count computed by one thread - the most direct way to share it is __builtin_amdgcn_readlane. Unlike a shuffle, which requires every lane to participate with a relative offset, readlane reads from one named lane regardless of who is calling, making it the natural choice when the source is fixed rather than relative. The example here uses it to broadcast a wavefront maximum after a __shfl_down reduction, which is a representative case: one lane holds the answer, and all others need it.

__builtin_amdgcn_readlane(val, lane) returns the value of val held by the lane specified by lane. Unlike readfirstlane, which always reads the first active lane, readlane accepts a runtime lane index. The lane index must be uniform — the same value across all lanes in the wavefront at the point of the call. If the index is derived from per-lane data, use readfirstlane to promote it to a uniform value first. readfirstlane reads from the first active lane of the wavefront, which is a deterministic choice, so every lane in the wavefront receives the same index value and the uniformity requirement is satisfied.

 1__global__ void broadcast_readlane(const int *input, int *output, int n)
 2{
 3    int gid  = blockIdx.x * blockDim.x + threadIdx.x;
 4    int lane = threadIdx.x % warpSize;
 5    int val  = (gid < n) ? input[gid] : 0;
 6
 7    // Find the warp maximum via __shfl_down reduction.
 8    int max_val = val;
 9    for (int offset = warpSize / 2; offset > 0; offset >>= 1)
10    {
11        max_val = max(max_val, __shfl_down(max_val, offset));
12    }
13
14    // Broadcast the maximum from lane 0 to all lanes.
15    // The lane index must be uniform; use readfirstlane to promote a
16    // runtime-derived index to a uniform value.
17    int broadcast = __builtin_amdgcn_readlane(max_val, 0);
18
19    if (gid < n)
20    {
21        output[gid] = broadcast;
22    }
23
24    (void)lane;
25}

After a __shfl_down reduction, lane 0 holds the wavefront maximum. Passing the literal 0 as the lane index satisfies the uniformity requirement and broadcasts that value to every lane in the wavefront.

For the full signatures and parameter details, see readlane and readfirstlane in the shuffle and lane access reference.

Wavefront rotation using mov_dpp and ds_bpermute#

Rotation shifts every lane’s value one position around the wavefront, so lane i receives what lane i - 1 held, and lane 0 wraps around to receive what the last lane held. This cyclic movement is a building block for algorithms that scan or pipeline values across lanes, such as prefix operations or producer-consumer patterns within a wavefront. Data Parallel Primitives (DPP) is the hardware-native way to express rotation on AMD GPUs, executing in a dedicated unit without consuming the general instruction pipeline. On CDNA-based GPUs, a single wave_ror:1 instruction covers the entire wave64 atomically. On RDNA-based GPUs, where wave_ror is not available on gfx11xx and later, the same result requires two steps: a row_ror:1 rotates within each 16-lane row, and ds_bpermute supplies the wrap-around value at the boundary of each row.

 1// wave_ror:1 (CDNA): rotates the entire warp right by one lane.
 2// row_ror:1 (RDNA): rotates within each row of 16; ds_bpermute fixes wrap-around.
 3#ifdef HAS_DPP_BROADCAST
 4constexpr int dpp_wave_ror1 = 0x134;
 5#else
 6constexpr int dpp_row_ror1  = 0x121;
 7#endif
 8__device__ int warp_rotate_right(int val)
 9{
10#ifdef HAS_DPP_BROADCAST
11    // CDNA: single wave_ror:1 rotates all lanes atomically.
12    return __builtin_amdgcn_mov_dpp(val, dpp_wave_ror1, 0xf, 0xf, false);
13#else
14    // RDNA: row_ror:1 rotates within each row of 16.
15    int rotated = __builtin_amdgcn_mov_dpp(val, dpp_row_ror1, 0xf, 0xf, false);
16
17    // ds_bpermute (byte addressing: lane k -> addr k*4) fixes up the
18    // wrap-around value at lane 0 of each row.
19    int lane    = threadIdx.x % warpSize;
20    int addr    = (lane == 0) ? 124 : (lane == 16) ? 60 : lane * 4;
21    int wrapped = __builtin_amdgcn_ds_bpermute(addr, val);
22
23    return (lane == 0 || lane == 16) ? wrapped : rotated;
24#endif
25}
26
27__global__ void rotate_warp(const int *input, int *output, int n)
28{
29    int gid = blockIdx.x * blockDim.x + threadIdx.x;
30    int val = (gid < n) ? input[gid] : 0;
31
32    // Rotate right warpSize times, XORing each step with the step index.
33    for (int i = 0; i < warpSize; ++i)
34    {
35        val = warp_rotate_right(val);
36        val ^= i;
37    }
38
39    if (gid < n)
40    {
41        output[gid] = val;
42    }
43}

ds_bpermute uses byte addressing: to read from lane k, pass k * 4 as the index. Lane 0 of the first row reads from lane 31 (address 124), and lane 0 of the second row reads from lane 15 (address 60). All other lanes keep the row_ror result unchanged.

For the full signatures and parameter details, see mov_dpp and ds_bpermute in the DPP and data-share permutation reference.

Wavefront rotation using __shfl#

The DPP rotation above is the preferred approach on AMD hardware, but the same pattern can be expressed using __shfl. This is useful when the performance difference is not significant for your workload. Each lane computes its source index as (lane - 1 + warpSize) % warpSize and because __shfl accepts any uniform lane index, the modular arithmetic produces the wrap-around without the architecture-specific workaround that the DPP path requires.

 1__device__ int warp_rotate_right_shfl(int val)
 2{
 3    int lane = threadIdx.x % warpSize;
 4    // Rotate right: each lane reads from (lane - 1 + warpSize) % warpSize.
 5    return __shfl(val, (lane - 1 + warpSize) % warpSize);
 6}
 7
 8__global__ void rotate_warp_shfl(const int *input, int *output, int n)
 9{
10    int gid = blockIdx.x * blockDim.x + threadIdx.x;
11    int val = (gid < n) ? input[gid] : 0;
12
13    // Rotate right warpSize times, XORing each step with the step index.
14    // Produces the same result as rotate_warp.
15    for (int i = 0; i < warpSize; ++i)
16    {
17        val = warp_rotate_right_shfl(val);
18        val ^= i;
19    }
20
21    if (gid < n)
22    {
23        output[gid] = val;
24    }
25}

The DPP and __shfl kernels produce identical output and can be verified against the same CPU reference.

The two paths compile to different Instruction Set Architecture (ISA) on CDNA-based GPUs: the DPP path emits v_mov_b32_dpp wave_rol:1, a pure register move that executes in the dedicated DPP unit without touching memory, while the __shfl path emits ds_bpermute_b32, which routes through the data-share unit. On RDNA-based GPUs, where wave_ror is unavailable, the DPP path uses v_mov_b32_dpp row_ror:1 for the within-row lanes and ds_bpermute_b32 for the cross-row fixup; the __shfl path uses only ds_bpermute_b32. The ISA output for both can be inspected by compiling with -save-temps and examining the generated .s file.

Lane swap using ds_swizzle#

While readlane and rotation address individual lanes or shift the whole wavefront by one, some algorithms need a fixed, symmetric rearrangement of groups - for example, swapping pairs of lanes, or exchanging two halves of a tile (a fixed-size contiguous sub-group of lanes within a wavefront). __builtin_amdgcn_ds_swizzle is designed exactly for this: the permutation is encoded entirely in a compile-time mask, so that the hardware can apply it in a single instruction with no per-lane index computation. The mask specifies a bitwise transformation of each lane index to its source, making it well-suited to power-of-two group swaps that appear in butterfly networks, data rearrangement before matrix operations, or paired-lane exchanges. For the full signature, see ds_swizzle in the DPP and data-share permutation reference.

The 16-bit mask encodes the permutation: bits [14:10] hold and_mask, bits [9:5] hold or_mask, and bits [4:0] hold xor_mask. The source lane is computed as (lane & and_mask) | or_mask ^ xor_mask. Setting and_mask = 0x1F and or_mask = 0, and placing a single set bit in xor_mask, swaps neighboring groups whose size is determined by the position of that bit. The example demonstrates the swap pattern; the mask value and how it determines group size are explained in the prose below.

 1// ds_swizzle mask layout: bits [14:10]=and_mask, [9:5]=or_mask, [4:0]=xor_mask.
 2// Source lane = (lane & and_mask) | or_mask ^ xor_mask.
 3// 0x101F: and_mask=0x1F, xor_mask=0x04 - swaps neighboring groups of 4.
 4constexpr int swizzle_swap_groups4 = 0x101F;
 5__global__ void swizzle_swap_groups(const int *input, int *output, int n)
 6{
 7    int gid = blockIdx.x * blockDim.x + threadIdx.x;
 8    int val = (gid < n) ? input[gid] : 0;
 9
10    int swapped = __builtin_amdgcn_ds_swizzle(val, swizzle_swap_groups4);
11
12    if (gid < n)
13    {
14        output[gid] = swapped;
15    }
16}

The mask 0x101F sets xor_mask = 0x04, which applies a bitwise XOR between each lane index and 4. Lanes 0–3 read from lanes 4–7 and vice versa, lanes 8–11 read from lanes 12–15, and so on across the wavefront. Shifting the set bit changes the group size: 0x041F swaps neighboring pairs, 0x081F swaps groups of two, 0x201F swaps groups of eight, and 0x401F swaps groups of sixteen.

For the full signature and parameter details, see ds_swizzle in the DPP and data-share permutation reference.

Wavefront reductions#

A wavefront reduction combines a value held by each lane into a single scalar result. This pattern appears constantly in GPU kernels - summing partial products, finding a maximum across a tile, counting active threads - and the efficiency of the reduction matters because it often sits in the critical path of a kernel. The three implementations below all express the same butterfly pattern, where lanes exchange values with increasingly distant partners until one lane holds the aggregate. Still, they differ significantly in how directly they map to hardware. The first two reduce floating-point values and show the progression from shuffle-based code to hardware-native DPP. The third uses an unsigned integer reduction to demonstrate wave_reduce_add_u32, the single-instruction form currently available in the compiler. Understanding all three lets you choose the right level of abstraction for your target and performance requirements.

Reduction using __shfl_down#

__shfl_down is the most readable starting point for wavefront reductions. It reads from a lane a fixed offset ahead of the current lane, which maps directly onto the butterfly pattern when called in a loop that halves the offset on each step. The code structure closely mirrors the algorithm’s logical structure, making it straightforward to reason about and maintain. The kernel is templated on WarpSize, a compile-time constant passed at the call site, which allows the compiler to fully unroll the reduction loop and makes the code adapt to wave32 and wave64 without modification. This makes __shfl_down the right choice when clarity and simplicity matter, or as a reference implementation against which a more hardware-specific version can be verified.

 1template<int WarpSize>
 2__device__ float shfl_down_reduce(float val)
 3{
 4    // Halve the active offset each step until all lanes have contributed.
 5    // WarpSize is a compile-time constant, allowing the compiler to fully
 6    // unroll this loop.
 7    for (int offset = WarpSize / 2; offset > 0; offset >>= 1)
 8    {
 9        val += __shfl_down(val, offset);
10    }
11    return val;
12}
13
14template<int WarpSize>
15__global__ __launch_bounds__(block_size)
16void reduce_shfl_down(const float *input, float *output, int n)
17{
18    int gid    = blockIdx.x * blockDim.x + threadIdx.x;
19    int lane   = threadIdx.x % WarpSize;
20    int warpid = threadIdx.x / WarpSize;
21
22    float val = (gid < n) ? input[gid] : 0.0f;
23    val = shfl_down_reduce<WarpSize>(val);
24
25    // Collect one partial result per warp into shared memory, then reduce
26    // those with the first warp.
27    extern __shared__ float sdata[];
28    int num_warps = blockDim.x / WarpSize;
29    if (lane == 0)
30    {
31        sdata[warpid] = val;
32    }
33    __syncthreads();
34
35    if (warpid == 0)
36    {
37        val = (lane < num_warps) ? sdata[lane] : 0.0f;
38        val = shfl_down_reduce<WarpSize>(val);
39    }
40
41    if (threadIdx.x == 0)
42    {
43        output[blockIdx.x] = val;
44    }
45}

After the loop, lane 0 holds the sum of all lanes in the wavefront. The kernel uses a two-phase structure: each wavefront reduces its slice independently, lane 0 writes its partial result to shared memory, and the first wavefront reduces those partials to the block result.

Reduction using mov_dpp#

When targeting supported AMD GPUs specifically, Data Parallel Primitives offer a faster path. __builtin_amdgcn_mov_dpp moves data between lanes according to a DPP control word, executing in a dedicated hardware unit rather than the general instruction pipeline used by shuffle operations. The same butterfly pattern as above can be expressed with DPP control words, and the result is a reduction that makes better use of the hardware’s data movement capabilities. The trade-off is that DPP instructions expose architectural differences directly: broadcast instructions are available on gfx9xx targets (CDNA), but not on gfx10xx and later (RDNA, wave32), so the HAS_DPP_BROADCAST macro selects the appropriate path at compile time.

 1// DPP control words for butterfly reduction steps.
 2// quad_perm and row_ror steps work on both CDNA and RDNA.
 3// row_bcast steps are CDNA only; ds_swizzle mask 0x1e0 is the RDNA fallback.
 4constexpr int dpp_quad_perm_1032 = 0xb1;
 5constexpr int dpp_quad_perm_2301 = 0x4e;
 6constexpr int dpp_row_ror4       = 0x124;
 7constexpr int dpp_row_ror8       = 0x128;
 8#ifdef HAS_DPP_BROADCAST
 9constexpr int dpp_row_bcast15    = 0x142;
10constexpr int dpp_row_bcast31    = 0x143;
11#else
12constexpr int swizzle_bcast15    = 0x1e0;
13#endif
14template<int WarpSize>
15__device__ float dpp_warp_reduce(float val)
16{
17    // Reinterpret float bits as int for DPP and swizzle intrinsics.
18    auto as_int   = [](float f) { return __builtin_bit_cast(int, f); };
19    auto as_float = [](int i)   { return __builtin_bit_cast(float, i); };
20
21    // Steps 1-2: combine adjacent pairs within groups of 4.
22    val += as_float(__builtin_amdgcn_mov_dpp(as_int(val), dpp_quad_perm_1032, 0xf, 0xf, false));
23    val += as_float(__builtin_amdgcn_mov_dpp(as_int(val), dpp_quad_perm_2301, 0xf, 0xf, false));
24    // Steps 3-4: combine groups of 4 into a row of 16.
25    val += as_float(__builtin_amdgcn_mov_dpp(as_int(val), dpp_row_ror4, 0xf, 0xf, false));
26    val += as_float(__builtin_amdgcn_mov_dpp(as_int(val), dpp_row_ror8, 0xf, 0xf, false));
27
28#ifdef HAS_DPP_BROADCAST
29    // Steps 5-6 (CDNA): combine two rows of 16 into the full wave64 result.
30    val += as_float(__builtin_amdgcn_mov_dpp(as_int(val), dpp_row_bcast15, 0xa, 0xf, false));
31    val += as_float(__builtin_amdgcn_mov_dpp(as_int(val), dpp_row_bcast31, 0xc, 0xf, false));
32#else
33    // Step 5 (RDNA): ds_swizzle combines the two rows of the wave32 warp.
34    val += as_float(__builtin_amdgcn_ds_swizzle(as_int(val), swizzle_bcast15));
35#endif
36
37    return val;
38}
39
40template<int WarpSize>
41__global__ __launch_bounds__(block_size)
42void reduce_dpp(const float *input, float *output, int n)
43{
44    int gid    = blockIdx.x * blockDim.x + threadIdx.x;
45    int lane   = threadIdx.x % WarpSize;
46    int warpid = threadIdx.x / WarpSize;
47
48    float val = (gid < n) ? input[gid] : 0.0f;
49    val = dpp_warp_reduce<WarpSize>(val);
50
51    extern __shared__ float sdata[];
52    int num_warps = blockDim.x / WarpSize;
53    if (lane == WarpSize - 1)
54    {
55        sdata[warpid] = val;
56    }
57    __syncthreads();
58
59    if (warpid == 0)
60    {
61        val = (lane < num_warps) ? sdata[lane] : 0.0f;
62        val = dpp_warp_reduce<WarpSize>(val);
63    }
64
65    if (threadIdx.x == WarpSize - 1)
66    {
67        output[blockIdx.x] = val;
68    }
69}

The first two steps use quad_perm control words, which exchange lanes within groups of four. Steps three and four use row_ror rather than row_shr: rotation wraps lanes within the row boundary, whereas a shift leaves destination registers undefined when the source is out of range. On RDNA, where DPP broadcasts are unavailable, ds_swizzle with mask 0x1e0 replicates the value held by lane 15 across lanes 16–31, combining the two rows of the wave32 wavefront in a single instruction. The control word constants used in the example (0xb1, 0x4e, 0x124, and so on) are user-defined values derived from the DPP control word encoding; they are not predefined by HIP or LLVM. For the full DPP control word encoding and available patterns, refer to the Data Parallel Primitives chapter of the CDNA3 ISA or RDNA3 ISA.

Reduction using wave_reduce_add_u32#

Both implementations above express the butterfly pattern explicitly in code, requiring you to select control words, handle the architecture split, and manage the reduction steps yourself. __builtin_amdgcn_wave_reduce_add_u32 encapsulates all of that: you supply the value and a strategy hint, and the builtin handles the reduction without you needing to implement or maintain the underlying pattern. This represents the most hardware-specific end of the spectrum - the implementation details are handled by the compiler and runtime rather than by your code. Note that the wave reduce builtins currently cover integer and bitwise operations; for float workloads, the DPP path above remains the recommended approach.

 1template<int WarpSize>
 2__global__ __launch_bounds__(block_size)
 3void reduce_wave(const unsigned int *input, unsigned int *output, int n)
 4{
 5    int gid    = blockIdx.x * blockDim.x + threadIdx.x;
 6    int lane   = threadIdx.x % WarpSize;
 7    int warpid = threadIdx.x / WarpSize;
 8
 9    unsigned int val = (gid < n) ? input[gid] : 0u;
10
11    // Reduces val across all active lanes in a single instruction.
12    // The result is broadcast to every lane.
13    unsigned int sum = __builtin_amdgcn_wave_reduce_add_u32(val, 0);
14
15    extern __shared__ unsigned int sdataui[];
16    int num_warps = blockDim.x / WarpSize;
17    if (lane == 0)
18    {
19        sdataui[warpid] = sum;
20    }
21    __syncthreads();
22
23    if (warpid == 0)
24    {
25        sum = (lane < num_warps) ? sdataui[lane] : 0u;
26        sum = __builtin_amdgcn_wave_reduce_add_u32(sum, 0);
27    }
28
29    if (threadIdx.x == 0)
30    {
31        output[blockIdx.x] = sum;
32    }
33}

The second argument is a strategy hint: 0 lets the compiler choose, 1 requests the iterative strategy, and 2 requests the DPP-based strategy.

For the full signature and parameter details, see wave_reduce_add_u32 in the wavefront reduction reference.

Wavefront voting#

Lane operations move data between lanes, and reductions aggregate it. Wavefront voting answers a different question: which lanes satisfy a condition, and what can the wavefront do with that information collectively? The ballot builtin captures the answer as a bitmask - one bit per lane - which can then be inspected, counted, or used to coordinate writes. The mbcnt builtin builds on this by counting the number of lanes before the current one that have their bit set, giving each qualifying lane a unique sequential index. Together, ballot and mbcnt is the standard mechanism for stream compaction within a wavefront: filtering a set of values to only those that pass a predicate, and writing them contiguously to an output buffer without gaps or collisions, all without shared memory or barriers.

Compaction index using ballot and mbcnt#

 1__global__ void compaction_index(const int *input, int *output, int n)
 2{
 3    int gid    = blockIdx.x * blockDim.x + threadIdx.x;
 4    int lane   = threadIdx.x % warpSize;
 5    int warpid = threadIdx.x / warpSize;
 6    int val    = (gid < n) ? input[gid] : 0;
 7
 8    bool keep  = (gid < n) && (val > 0);
 9    int  index = 0;
10
11#ifdef HAS_DPP_BROADCAST
12    // wave64 (CDNA): chain mbcnt_lo and mbcnt_hi over the 64-bit mask.
13    unsigned long long mask = __builtin_amdgcn_ballot_w64(keep);
14    unsigned int lo         = static_cast<unsigned int>(mask);
15    unsigned int hi         = static_cast<unsigned int>(mask >> 32);
16    index                   = __builtin_amdgcn_mbcnt_lo(lo, 0);
17    index                   = __builtin_amdgcn_mbcnt_hi(hi, index);
18#else
19    // wave32 (RDNA): mbcnt_lo over the 32-bit mask suffices.
20    unsigned int mask = __builtin_amdgcn_ballot_w32(keep);
21    index             = __builtin_amdgcn_mbcnt_lo(mask, 0);
22#endif
23
24    // Compute per-warp base offsets for contiguous compacted output.
25    extern __shared__ int sdatai[];
26    int num_warps  = blockDim.x / warpSize;
27    int warp_count = __builtin_amdgcn_readlane(index + (keep ? 1 : 0), warpSize - 1);
28    if (lane == 0)
29    {
30        sdatai[warpid] = warp_count;
31    }
32    __syncthreads();
33
34    int warp_base = 0;
35    for (int w = 0; w < warpid; ++w)
36    {
37        warp_base += sdatai[w];
38    }
39
40    if (keep)
41    {
42        output[blockIdx.x * blockDim.x + warp_base + index] = val;
43    }
44
45    (void)num_warps;
46}

mbcnt_hi takes the upper 32 bits of the mask and the result of mbcnt_lo as its accumulator, so the final index is the count of passing lanes strictly below the current lane across the full 64-bit mask. Lanes where the predicate is false receive an index, too, but should not write to the output.

For the full signatures and parameter details, see ballot_w64 and mbcnt_lo / mbcnt_hi in the wavefront voting reference.

Compile and run:

amdclang++ -O3 -std=c++17 --offload-arch=gfx942 \
    wavefront_builtins.hip -o wavefront_builtins
./wavefront_builtins

Note

The example above targets CDNA3 (gfx942, wave64). Replace --offload-arch with the appropriate target for your GPU — for example, gfx90a for CDNA2 or gfx1200 for RDNA4 (wave32). The __shfl* builtins work on all architectures; the DPP-specific code paths are guarded by architecture macros in the example file.

Naming convention#

Wavefront-level builtins come in two families:

HIP wrappers use the __shfl prefix:

__shfl[_up|_down|_xor](val, offset_or_lane, width)

The suffix indicates the direction or mode: _up reads from a lower lane, _down from a higher lane, _xor reads from a lane whose index is the bitwise XOR of the current lane index and a mask, and no suffix reads from an absolute lane index.

Compiler builtins use the __builtin_amdgcn_ prefix and map directly to ISA instructions:

__builtin_amdgcn_<operation>(args...)

Key operation names:

  • readlane / readfirstlane / writelane – named lane access

  • mov_dpp / update_dpp / mov_dpp8 – Data Parallel Primitives

  • ds_swizzle / ds_permute / ds_bpermute – data-share permutations

  • permlane16 / permlanex16 / permlane64 – cross-row permutations

  • ballot_w64 / ballot_w32 – wavefront voting

  • mbcnt_lo / mbcnt_hi – masked bit count (compaction index)

  • wave_reduce_<op>_<type> – single-instruction wavefront reductions, where <op> is add, sub, min, max, and, or, or xor, and <type> is u32, u64, i32, i64, b32, or b64

Use the __shfl* family for standard shuffle operations. Use the __builtin_amdgcn_* intrinsics when you need a specific hardware feature (DPP, ds_swizzle, wavefront voting) or when the ISA instruction gives measurable performance benefit.

Wavefront builtin reference#

Each reference page documents the full signature, parameter details, and architecture support for every builtin in that family.

Family

Description

Shuffle and lane access

__shfl* wrappers and hardware readlane, readfirstlane, writelane

DPP and data-share permutations

mov_dpp, update_dpp, mov_dpp8, ds_swizzle, ds_permute, ds_bpermute

Cross-row permutations

permlane16, permlanex16, permlane64, and runtime/swap variants

Wavefront reductions

wave_reduce_<op>_<type> for add, sub, min, max, and, or, xor

Wavefront voting and synchronization

ballot, inverse_ballot, mbcnt, wave_barrier, wave_id