Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

415 lines
14 KiB
C++

// Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
/*
* C99 port of mfma_atom / c_warp_params / make_c_warp_dstr_encoding from
* rocke/helpers/atoms.py. See helper_rocke.helpers.atoms.h for the contract.
*
* None of the three ported symbols emit IR (no rocke_b_* op calls), so the
* byte-identical op-sequence requirement is met by emitting nothing; fidelity
* is on the returned struct/encoding values, reproduced field-for-field below.
*/
#include "rocke/helper_rocke.helpers.atoms.h"
#include <string.h>
#include "rocke/ir_internal.h" /* rocke_i_set_err */
/* ------------------------------------------------------------------ catalog *
*
* Static reproduction of the MfmaAtom factory class-methods, in the EXACT order
* of the Python MFMA_ATOMS tuple:
*
* MFMA_ATOMS = MFMA_F16_ATOMS + MFMA_F32_ATOMS + MFMA_BF16_ATOMS
* + MFMA_FP8_ATOMS + MFMA_MX_ATOMS
*
* MFMA_F16_ATOMS = (f16_4x4x4, f16_16x16x16, f16_16x16x32,
* f16_32x32x8, f16_32x32x16)
* MFMA_F32_ATOMS = (f32_16x16x4, f32_32x32x2)
* MFMA_BF16_ATOMS = (bf16_16x16x16, bf16_16x16x32, bf16_32x32x8,
* bf16_32x32x16)
* MFMA_FP8_ATOMS = (fp8_16x16x32, fp8_32x32x16, bf8_16x16x32,
* bf8_32x32x16, fp8_16x16x128)
* MFMA_MX_ATOMS = (fp4_16x16x128, fp6_16x16x96)
*
* _BY_SHAPE keys each by (dtype_in, m, n, k). All keys are unique, so the linear
* scan below is order-independent for correctness, but the catalog order is
* preserved anyway to match the Python (the only observable order effect, the
* "valid:" list in the ValueError, is not reproduced verbatim -- see notes).
*
* Fields per row: m, n, k, a_per_lane, b_per_lane, c_per_lane,
* dtype_in, dtype_out, name.
*/
static const rocke_mfma_atom_t ROCKE_MFMA_ATOMS[] = {
/* ---- MFMA_F16_ATOMS ---- */
{4, 4, 4, 4, 4, 4, "f16", "f32", "mfma_f32_4x4x4_f16"},
{16, 16, 16, 4, 4, 4, "f16", "f32", "mfma_f32_16x16x16_f16"},
{16, 16, 32, 8, 8, 4, "f16", "f32", "mfma_f32_16x16x32_f16"},
{32, 32, 8, 4, 4, 16, "f16", "f32", "mfma_f32_32x32x8_f16"},
{32, 32, 16, 8, 8, 16, "f16", "f32", "mfma_f32_32x32x16_f16"},
/* ---- MFMA_F32_ATOMS (#8348) ---- */
{16, 16, 4, 1, 1, 4, "fp32", "f32", "mfma_f32_16x16x4_f32"},
{32, 32, 2, 1, 1, 16, "fp32", "f32", "mfma_f32_32x32x2_f32"},
/* ---- MFMA_BF16_ATOMS ---- */
{16, 16, 16, 4, 4, 4, "bf16", "f32", "mfma_f32_16x16x16_bf16"},
{16, 16, 32, 8, 8, 4, "bf16", "f32", "mfma_f32_16x16x32_bf16"},
{32, 32, 8, 4, 4, 16, "bf16", "f32", "mfma_f32_32x32x8_bf16"},
{32, 32, 16, 8, 8, 16, "bf16", "f32", "mfma_f32_32x32x16_bf16"},
/* ---- MFMA_FP8_ATOMS ---- */
{16, 16, 32, 8, 8, 4, "fp8e4m3", "f32", "mfma_f32_16x16x32_fp8"},
{32, 32, 16, 8, 8, 16, "fp8e4m3", "f32", "mfma_f32_32x32x16_fp8"},
{16, 16, 32, 8, 8, 4, "bf8e5m2", "f32", "mfma_f32_16x16x32_bf8"},
{32, 32, 16, 8, 8, 16, "bf8e5m2", "f32", "mfma_f32_32x32x16_bf8"},
{16, 16, 128, 32, 32, 4, "fp8e4m3", "f32", "mfma_f32_16x16x128_fp8"},
/* ---- MFMA_MX_ATOMS ---- */
{16, 16, 128, 16, 16, 4, "fp4", "f32", "mfma_f32_16x16x128_fp4"},
{16, 16, 96, 12, 12, 4, "fp6", "f32", "mfma_f32_16x16x96_fp6"},
};
#define ROCKE_NUM_MFMA_ATOMS ((int)(sizeof(ROCKE_MFMA_ATOMS) / sizeof(ROCKE_MFMA_ATOMS[0])))
/* _DTYPE_ALIAS, reproduced exactly:
* fp8 -> fp8e4m3, fp8e4m3 -> fp8e4m3,
* bf8 -> bf8e5m2, bf8e5m2 -> bf8e5m2,
* f16 -> f16, fp16 -> f16,
* f32 -> fp32, fp32 -> fp32, float -> fp32,
* bf16 -> bf16, bfloat16 -> bf16.
* Any key not in the table maps to itself (Python dict.get(dtype, dtype)). */
static const char* rocke_canon_dtype(const char* dtype)
{
static const struct
{
const char* key;
const char* val;
} aliases[] = {
{"fp8", "fp8e4m3"},
{"fp8e4m3", "fp8e4m3"},
{"bf8", "bf8e5m2"},
{"bf8e5m2", "bf8e5m2"},
{"f16", "f16"},
{"fp16", "f16"},
{"f32", "fp32"},
{"fp32", "fp32"},
{"float", "fp32"},
{"bf16", "bf16"},
{"bfloat16", "bf16"},
};
int i;
int n = (int)(sizeof(aliases) / sizeof(aliases[0]));
if(dtype == NULL)
{
return NULL;
}
for(i = 0; i < n; ++i)
{
if(strcmp(dtype, aliases[i].key) == 0)
{
return aliases[i].val;
}
}
return dtype; /* identity fallthrough */
}
/* ------------------------------------------------------------------ mfma_atom */
const rocke_mfma_atom_t* rocke_mfma_atom(const char* dtype, int m, int n, int k)
{
const char* canon = rocke_canon_dtype(dtype);
int i;
if(canon == NULL)
{
return NULL;
}
for(i = 0; i < ROCKE_NUM_MFMA_ATOMS; ++i)
{
const rocke_mfma_atom_t* a = &ROCKE_MFMA_ATOMS[i];
if(a->m == m && a->n == n && a->k == k && strcmp(a->dtype_in, canon) == 0)
{
return a;
}
}
return NULL; /* Python ValueError path */
}
/* ------------------------------------------------------------------ accessors *
*
* Faithful ports of the MfmaAtom field reads / @property bodies consumed by the
* schedule helper's from_geometry (rocke/helpers/atoms.py:429-465).
*/
const char* rocke_mfma_atom_dtype_in(const rocke_mfma_atom_t* atom)
{
return atom->dtype_in;
}
int rocke_mfma_atom_m(const rocke_mfma_atom_t* atom)
{
return atom->m;
}
int rocke_mfma_atom_n(const rocke_mfma_atom_t* atom)
{
return atom->n;
}
/* @property k_per_xdlops -> self.k (CK KPerXdlops == atom's full per-inst K). */
int rocke_mfma_atom_k_per_xdlops(const rocke_mfma_atom_t* atom)
{
return atom->k;
}
/* @property is_f4f6 -> self.dtype_in in ("fp4", "fp6"). */
bool rocke_mfma_atom_is_f4f6(const rocke_mfma_atom_t* atom)
{
return strcmp(atom->dtype_in, "fp4") == 0 || strcmp(atom->dtype_in, "fp6") == 0;
}
/* @property mfma_cycle -> C_MFMA_Inst_Cycle (blkgemmpipe_scheduler.hpp:63-74).
* speedup = 2 if is_f4f6 else 1
* n == 16: (32 if k == 128 else 16) // speedup
* n == 32: (64 if k == 64 else 32) // speedup
* else : NotImplementedError -> reported here as -1 (out-of-table sentinel).
*/
int rocke_mfma_atom_mfma_cycle(const rocke_mfma_atom_t* atom)
{
int speedup = rocke_mfma_atom_is_f4f6(atom) ? 2 : 1;
if(atom->n == 16)
{
return (atom->k == 128 ? 32 : 16) / speedup;
}
if(atom->n == 32)
{
return (atom->k == 64 ? 64 : 32) / speedup;
}
return -1; /* Python NotImplementedError path (no 16x16/32x32 XDL shape). */
}
const rocke_mfma_atom_t*
rocke_b_mfma_atom(rocke_ir_builder_t* b, const char* dtype, int m, int n, int k)
{
const rocke_mfma_atom_t* atom;
if(b != NULL && b->status != ROCKE_OK)
{
return NULL; /* already in error: no-op, like every other rocke_b_* */
}
atom = rocke_mfma_atom(dtype, m, n, k);
if(atom == NULL)
{
/* Mirrors: raise ValueError(f"no MFMA atom for {key}; valid: {valid}").
* The verbatim sorted "valid:" suffix is not reproduced (it carries no
* IR/value semantics); the (key) prefix is. */
return (const rocke_mfma_atom_t*)rocke_i_set_err(b,
ROCKE_ERR_VALUE,
"no MFMA atom for ('%s', %d, %d, %d)",
dtype != NULL ? dtype : "(null)",
m,
n,
k);
}
return atom;
}
/* ------------------------------------------------------------------ c_warp_params *
*
* _C_WARP_PARAMS = { (16,16): (1,4,4,16), (32,32): (4,2,4,32) }
* tuple order is (kCM0PerLane, kCMLane, kCM1PerLane, kCNLane).
*/
static bool rocke_c_warp_params_lookup(int m, int n, int* m0, int* m_lane, int* m1, int* n_lane)
{
if(m == 16 && n == 16)
{
*m0 = 1;
*m_lane = 4;
*m1 = 4;
*n_lane = 16;
return true;
}
if(m == 32 && n == 32)
{
*m0 = 4;
*m_lane = 2;
*m1 = 4;
*n_lane = 32;
return true;
}
return false;
}
rocke_status_t rocke_c_warp_params(const rocke_mfma_atom_t* atom,
int* out_kCM0PerLane,
int* out_kCMLane,
int* out_kCM1PerLane,
int* out_kCNLane)
{
int m0, m_lane, m1, n_lane;
if(atom == NULL)
{
return ROCKE_ERR_VALUE;
}
if(!rocke_c_warp_params_lookup(atom->m, atom->n, &m0, &m_lane, &m1, &n_lane))
{
return ROCKE_ERR_NOTIMPL; /* raise NotImplementedError */
}
if(m0 * m1 != atom->c_per_lane)
{
return ROCKE_ERR_VALUE; /* raise ValueError */
}
if(out_kCM0PerLane != NULL)
{
*out_kCM0PerLane = m0;
}
if(out_kCMLane != NULL)
{
*out_kCMLane = m_lane;
}
if(out_kCM1PerLane != NULL)
{
*out_kCM1PerLane = m1;
}
if(out_kCNLane != NULL)
{
*out_kCNLane = n_lane;
}
return ROCKE_OK;
}
rocke_status_t rocke_b_c_warp_params(rocke_ir_builder_t* b,
const rocke_mfma_atom_t* atom,
int* out_kCM0PerLane,
int* out_kCMLane,
int* out_kCM1PerLane,
int* out_kCNLane)
{
int m0, m_lane, m1, n_lane;
if(b != NULL && b->status != ROCKE_OK)
{
return b->status; /* already in error: no-op */
}
if(atom == NULL)
{
rocke_i_set_err(b, ROCKE_ERR_VALUE, "c_warp_params: NULL atom");
return ROCKE_ERR_VALUE;
}
if(!rocke_c_warp_params_lookup(atom->m, atom->n, &m0, &m_lane, &m1, &n_lane))
{
rocke_i_set_err(b,
ROCKE_ERR_NOTIMPL,
"no CWarpDstrEncoding for atom %dx%d "
"(only the 16x16 and 32x32 MFMA C tiles are supported)",
atom->m,
atom->n);
return ROCKE_ERR_NOTIMPL;
}
if(m0 * m1 != atom->c_per_lane)
{
rocke_i_set_err(b,
ROCKE_ERR_VALUE,
"atom %s: kCM0PerLane*kCM1PerLane (%d) != c_per_lane (%d)",
atom->name != NULL ? atom->name : "(null)",
m0 * m1,
atom->c_per_lane);
return ROCKE_ERR_VALUE;
}
if(out_kCM0PerLane != NULL)
{
*out_kCM0PerLane = m0;
}
if(out_kCMLane != NULL)
{
*out_kCMLane = m_lane;
}
if(out_kCM1PerLane != NULL)
{
*out_kCM1PerLane = m1;
}
if(out_kCNLane != NULL)
{
*out_kCNLane = n_lane;
}
return ROCKE_OK;
}
/* ----------------------------------------------- make_c_warp_dstr_encoding *
*
* Python:
* m0, m_lane, m1, n_lane = c_warp_params(atom)
* return TileDistributionEncoding(
* Rs=(),
* Hs=((m0, m_lane, m1), (n_lane,)),
* Ps2RHs_major=((1, 2),),
* Ps2RHs_minor=((1, 0),),
* Ys2RHs_major=(1, 1),
* Ys2RHs_minor=(0, 2),
* )
*
* Fed verbatim into rocke_make_tile_distribution_encoding (which runs the
* encoding __post_init__ validation and arena-owns the node).
*/
rocke_tile_distribution_encoding_t* rocke_make_c_warp_dstr_encoding(rocke_ir_builder_t* b,
const rocke_mfma_atom_t* atom)
{
int m0, m_lane, m1, n_lane;
rocke_status_t st;
/* H rows: Hs[0] = (m0, m_lane, m1), Hs[1] = (n_lane,). */
int h0_levels[3];
int h1_levels[1];
rocke_h_row_t Hs[2];
/* Single P dim: Ps2RHs_major=((1,2),), Ps2RHs_minor=((1,0),). */
int p0_major[2];
int p0_minor[2];
rocke_p_seq_t Ps[1];
/* Two Y dims: Ys2RHs_major=(1,1), Ys2RHs_minor=(0,2). */
int Ys_major[2];
int Ys_minor[2];
if(b != NULL && b->status != ROCKE_OK)
{
return NULL; /* already in error: no-op */
}
/* c_warp_params(atom) -- propagates NotImplementedError/ValueError via
* the builder sticky error, matching the Python raise. */
st = rocke_b_c_warp_params(b, atom, &m0, &m_lane, &m1, &n_lane);
if(st != ROCKE_OK)
{
return NULL;
}
h0_levels[0] = m0;
h0_levels[1] = m_lane;
h0_levels[2] = m1;
h1_levels[0] = n_lane;
Hs[0].levels = h0_levels;
Hs[0].count = 3;
Hs[1].levels = h1_levels;
Hs[1].count = 1;
p0_major[0] = 1;
p0_major[1] = 2;
p0_minor[0] = 1;
p0_minor[1] = 0;
Ps[0].major = p0_major;
Ps[0].minor = p0_minor;
Ps[0].count = 2;
Ys_major[0] = 1;
Ys_major[1] = 1;
Ys_minor[0] = 0;
Ys_minor[1] = 2;
/* Rs=() -> num_R==0, Rs==NULL. The constructor copies all arrays into the
* builder arena, so the stack locals above are safe. */
return rocke_make_tile_distribution_encoding(b,
/* Rs */ NULL,
/* num_R */ 0,
/* Hs */ Hs,
/* num_X */ 2,
/* Ps */ Ps,
/* num_P */ 1,
/* Ys_major */ Ys_major,
/* Ys_minor */ Ys_minor,
/* num_Y */ 2);
}