Files
TheRock/rocm-libraries/dnn-providers/hip-kernel-provider/rocke/platform/cpp/helpers/asm.cpp
T
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

303 lines
10 KiB
C++

// Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
/*
* helper_helpers.asm.c -- C99 port of rocke.helpers.asm
* (mfma_f8f6f4_agpr, mfma_f8f6f4_agpr_cluster).
*
* Byte-faithful translation: the builder-call sequence is reproduced in the
* exact same order as the Python so the emitted IR (and SSA value numbering)
* stays identical. The Python `inline_asm` returns the single result Value and
* `inline_asm_multi` returns the list of result Values; the C builder calls
* return the op, whose `results[]` array holds those Values in declaration
* order.
*
* The Python `result_name_hint` ("mfma" / "mfmacl") is an emission cosmetic the
* C `rocke_b_inline_asm` does not expose (it names asm results "asm"); this is an
* accepted, documented divergence in the C port -- it does not affect the asm
* template, constraints, operands, or result types, which are what the lowerers
* consume.
*/
#include "rocke/helper_helpers.asm.h"
#include "rocke/ir_internal.h" /* rocke_i_set_err, rocke_i_live */
#include "rocke/strbuf.h" /* growable template assembly */
/* The f8f6f4 MFMA srcA/srcB operand type the AMDGPU backend accepts under the
* inline-asm `a` (AGPR) constraint: a 256-bit <8 x i32> (8 AGPRs). Python
* module constant `_MFMA_F8F6F4_SRC_TY = VectorType(I32, 8)`. Built per-call
* via the builder's arena (the C type constructors are builder-scoped). */
static const rocke_type_t* mfma_f8f6f4_src_ty(rocke_ir_builder_t* b)
{
return rocke_vector_type(b, rocke_i32(), 8);
}
/* Python `_as_src_v8i32`: bitcast a 256-bit operand to <8 x i32> (no-op if
* already that type).
*
* def _as_src_v8i32(b, v):
* if isinstance(v.type, VectorType) and v.type == _MFMA_F8F6F4_SRC_TY:
* return v
* return b.vec_bitcast(v, _MFMA_F8F6F4_SRC_TY)
*/
static rocke_value_t* as_src_v8i32(rocke_ir_builder_t* b, rocke_value_t* v)
{
const rocke_type_t* src_ty = mfma_f8f6f4_src_ty(b);
if(!src_ty)
{
return NULL;
}
if(v != NULL && v->type != NULL && v->type->kind == ROCKE_TYPE_VECTOR
&& rocke_type_eq(v->type, src_ty))
{
return v;
}
return rocke_b_vec_bitcast(b, v, src_ty);
}
rocke_value_t* rocke_mfma_f8f6f4_agpr(rocke_ir_builder_t* b,
rocke_value_t* a,
rocke_value_t* bb,
rocke_value_t* acc,
bool convergent,
int hazard_nop)
{
rocke_strbuf_t tmpl;
rocke_value_t* operands[3];
const rocke_type_t* result_types[1];
rocke_inline_asm_opts_t opts;
rocke_op_t* op;
rocke_value_t* result = NULL;
/* Sticky-error model: a failed builder makes every call a NULL no-op. */
if(!rocke_i_live(b))
{
return NULL;
}
if(acc == NULL || acc->type == NULL)
{
return (rocke_value_t*)rocke_i_set_err(
b, ROCKE_ERR_VALUE, "mfma_f8f6f4_agpr: acc must be a typed Value");
}
/* The backend accepts only <8 x i32> for the `a`-constrained MFMA sources;
* bitcast the native fp8 fragment (<32 x fp8e4m3>) if needed. Python emits
* the A bitcast before the B bitcast. */
a = as_src_v8i32(b, a);
bb = as_src_v8i32(b, bb);
/* NOTE: the AMDGPU assembler treats `;` as a COMMENT, so additional
* statements MUST be separated by a NEWLINE (\n), not `;`. */
if(rocke_strbuf_init(&tmpl, 64) != 0)
{
return (rocke_value_t*)rocke_i_set_err(b, ROCKE_ERR_OOM, "mfma_f8f6f4_agpr: OOM");
}
rocke_strbuf_append(&tmpl, "v_mfma_f32_16x16x128_f8f6f4 $0, $2, $3, $1");
if(hazard_nop)
{
rocke_strbuf_appendf(&tmpl, "\n\ts_nop %d", (int)hazard_nop);
}
if(tmpl.oom)
{
rocke_strbuf_free(&tmpl);
return (rocke_value_t*)rocke_i_set_err(b, ROCKE_ERR_OOM, "mfma_f8f6f4_agpr: OOM");
}
/* Python: b.inline_asm(template, "=v,0,a,a", [acc, a, bb],
* result_type=acc.type, sideeffect=True,
* convergent=convergent, result_name_hint="mfma")
* Operand list order: [acc, a, bb]. */
operands[0] = acc;
operands[1] = a;
operands[2] = bb;
result_types[0] = acc->type;
opts.sideeffect = true;
opts.sideeffect_set = true;
opts.convergent = convergent;
opts.convergent_set = true;
op = rocke_b_inline_asm(
b, rocke_strbuf_cstr(&tmpl), "=v,0,a,a", operands, 3, result_types, 1, &opts);
rocke_strbuf_free(&tmpl);
if(op != NULL && op->num_results >= 1)
{
result = op->results[0];
}
return result;
}
int rocke_mfma_f8f6f4_agpr_cluster(rocke_ir_builder_t* b,
rocke_value_t* const* accs,
rocke_value_t* const* srcs_a,
rocke_value_t* const* srcs_b,
int n,
int tail_nop,
int inter_nop,
bool convergent,
rocke_value_t** out_accs)
{
rocke_strbuf_t tmpl;
rocke_strbuf_t cons;
rocke_value_t** src_a = NULL;
rocke_value_t** src_b = NULL;
rocke_value_t** operands = NULL;
const rocke_type_t** result_types = NULL;
rocke_inline_asm_opts_t opts;
rocke_op_t* op;
int i;
int rc = -1;
if(!rocke_i_live(b))
{
return -1;
}
/* Python: assert n == len(srcs) and n >= 1 */
if(n < 1 || accs == NULL || srcs_a == NULL || srcs_b == NULL)
{
rocke_i_set_err(b,
ROCKE_ERR_VALUE,
"mfma_f8f6f4_agpr_cluster: requires n >= 1 and non-null "
"accs/srcs");
return -1;
}
for(i = 0; i < n; ++i)
{
if(accs[i] == NULL || accs[i]->type == NULL)
{
rocke_i_set_err(b,
ROCKE_ERR_VALUE,
"mfma_f8f6f4_agpr_cluster: acc[%d] must be a typed "
"Value",
i);
return -1;
}
}
/* Scratch arrays. Use the builder arena so they live as long as needed and
* are reclaimed with the builder (no manual free path to leak on error). */
src_a = (rocke_value_t**)rocke_arena_alloc(&b->arena, (size_t)n * sizeof(rocke_value_t*));
src_b = (rocke_value_t**)rocke_arena_alloc(&b->arena, (size_t)n * sizeof(rocke_value_t*));
operands
= (rocke_value_t**)rocke_arena_alloc(&b->arena, (size_t)(3 * n) * sizeof(rocke_value_t*));
result_types = (const rocke_type_t**)rocke_arena_alloc(&b->arena,
(size_t)n * sizeof(const rocke_type_t*));
if(!src_a || !src_b || !operands || !result_types)
{
rocke_i_set_err(b, ROCKE_ERR_OOM, "mfma_f8f6f4_agpr_cluster: OOM");
return -1;
}
/* Python: bitcast all sources to <8 x i32>, in (a, bb) order per pair. */
for(i = 0; i < n; ++i)
{
src_a[i] = as_src_v8i32(b, srcs_a[i]);
src_b[i] = as_src_v8i32(b, srcs_b[i]);
}
/* Build the template (one v_mfma line per MFMA, optional inter/tail nops). */
if(rocke_strbuf_init(&tmpl, 128) != 0 || rocke_strbuf_init(&cons, 64) != 0)
{
rocke_strbuf_free(&tmpl);
rocke_i_set_err(b, ROCKE_ERR_OOM, "mfma_f8f6f4_agpr_cluster: OOM");
return -1;
}
/* $0..$(n-1) : outputs
* $(n+i) : tied acc i
* $(2n+2i) : srcA_i ; $(2n+2i+1) : srcB_i
* Lines joined with "\n\t" (Python "\n\t".join(lines)). */
for(i = 0; i < n; ++i)
{
int vdst = i;
int vsrc = n + i;
int srcA = 2 * n + 2 * i;
int srcB = 2 * n + 2 * i + 1;
if(i > 0)
{
rocke_strbuf_append(&tmpl, "\n\t");
}
rocke_strbuf_appendf(
&tmpl, "v_mfma_f32_16x16x128_f8f6f4 $%d, $%d, $%d, $%d", vdst, srcA, srcB, vsrc);
if(inter_nop && (i + 1 < n))
{
rocke_strbuf_appendf(&tmpl, "\n\ts_nop %d", (int)inter_nop);
}
}
if(tail_nop)
{
if(n > 0)
{
rocke_strbuf_append(&tmpl, "\n\t");
}
rocke_strbuf_appendf(&tmpl, "s_nop %d", (int)tail_nop);
}
/* Constraints: "=v"*n , then tied "0..n-1", then "a"*(2n). Python:
* out_constraints = ",".join(["=v"] * n)
* tied_constraints = ",".join(str(i) for i in range(n))
* src_constraints = ",".join(["a"] * (2 * n))
* constraints = f"{out},{tied},{src}" */
for(i = 0; i < n; ++i)
{
rocke_strbuf_append(&cons, (i == 0) ? "=v" : ",=v");
}
for(i = 0; i < n; ++i)
{
rocke_strbuf_appendf(&cons, ",%d", i);
}
for(i = 0; i < 2 * n; ++i)
{
rocke_strbuf_append(&cons, ",a");
}
if(tmpl.oom || cons.oom)
{
rocke_strbuf_free(&tmpl);
rocke_strbuf_free(&cons);
rocke_i_set_err(b, ROCKE_ERR_OOM, "mfma_f8f6f4_agpr_cluster: OOM");
return -1;
}
/* operands = list(accs) + [a0,b0,a1,b1,...]; result_types = [acc.type]*n */
for(i = 0; i < n; ++i)
{
operands[i] = accs[i];
result_types[i] = accs[i]->type;
}
for(i = 0; i < n; ++i)
{
operands[n + 2 * i] = src_a[i];
operands[n + 2 * i + 1] = src_b[i];
}
opts.sideeffect = true;
opts.sideeffect_set = true;
opts.convergent = convergent;
opts.convergent_set = true;
op = rocke_b_inline_asm_multi(b,
rocke_strbuf_cstr(&tmpl),
rocke_strbuf_cstr(&cons),
operands,
3 * n,
result_types,
n,
&opts);
rocke_strbuf_free(&tmpl);
rocke_strbuf_free(&cons);
if(op != NULL && op->num_results >= n)
{
if(out_accs != NULL)
{
for(i = 0; i < n; ++i)
{
out_accs[i] = op->results[i];
}
}
rc = 0;
}
return rc;
}