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

408 lines
13 KiB
C++

// Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
/*
* helper_rocke.helpers.io.c -- C99 port of rocke.helpers.io.io_ir_type,
* store_scalar_from_f32, load_scalar, load_scalar_as_f32, load_vec,
* load_vec_as_f32, load_lane_slice_f32, store_vec, pack_f32_to and
* vector_row_copy.
*
* Faithful translation of:
*
* def io_ir_type(dtype: str) -> Type:
* if dtype in ("f16", "fp16"): return F16
* if dtype == "bf16": return BF16
* raise ValueError(
* f"unsupported I/O dtype {dtype!r}; expected f16/fp16/bf16")
*
* Mapping invariants (must stay byte-identical to the Python so downstream IR is
* identical):
* "f16" -> rocke_f16() (Python F16)
* "fp16" -> rocke_f16() (alias; Python F16)
* "bf16" -> rocke_bf16() (Python BF16)
* else -> NULL (Python ValueError)
*/
#include "rocke/helper_rocke.helpers.io.h"
#include <string.h>
#include "rocke/arena.h" /* rocke_arena_alloc */
#include "rocke/ir_internal.h" /* rocke_i_set_err, rocke_i_live */
/* Power-of-two vector widths the DSL's global_load_vN covers. Mirrors the
* module-level _VEC_WIDTHS = (2, 4, 8) in rocke.helpers.io. */
static int rocke_io_is_vec_width(int n)
{
return n == 2 || n == 4 || n == 8;
}
const rocke_type_t* rocke_io_ir_type(const char* dtype)
{
if(dtype == NULL)
{
return NULL;
}
/* `dtype in ("f16", "fp16")` -> F16 */
if(strcmp(dtype, "f16") == 0 || strcmp(dtype, "fp16") == 0)
{
return rocke_f16();
}
/* `dtype == "bf16"` -> BF16 */
if(strcmp(dtype, "bf16") == 0)
{
return rocke_bf16();
}
/* Python: raise ValueError. No builder here, so signal via NULL. */
return NULL;
}
const rocke_type_t* rocke_b_io_ir_type(rocke_ir_builder_t* b, const char* dtype)
{
const rocke_type_t* ty;
/* Sticky-error model: a failed builder makes every call a NULL no-op. */
if(!rocke_i_live(b))
{
return NULL;
}
ty = rocke_io_ir_type(dtype);
if(ty == NULL)
{
/* Mirror the Python ValueError, including the {dtype!r} single-quote
* repr for the (non-NULL) string case. NULL is reported as "None" to
* match Python's repr(None). */
return (const rocke_type_t*)rocke_i_set_err(
b,
ROCKE_ERR_VALUE,
"unsupported I/O dtype %s%s%s; expected f16/fp16/bf16",
dtype ? "'" : "",
dtype ? dtype : "None",
dtype ? "'" : "");
}
return ty;
}
void rocke_b_store_scalar_from_f32(rocke_ir_builder_t* b,
rocke_value_t* ptr,
rocke_value_t* idx,
rocke_value_t* value_f32,
const char* dtype)
{
const rocke_type_t* target;
rocke_value_t* cast;
/* Sticky-error model: a failed builder makes every call a NULL no-op. */
if(!rocke_i_live(b))
{
return;
}
/* Python: target = io_ir_type(dtype). Use the builder-aware variant so an
* unsupported dtype records the same ValueError on the sticky-error model;
* on failure it returns NULL and we stop (Python would have raised). */
target = rocke_b_io_ir_type(b, dtype);
if(target == NULL)
{
return;
}
/* Python: b.global_store(ptr, idx, b.cast_f32_to(value_f32, target)).
* align defaults to 0 (Python global_store takes no align kwarg here). */
cast = rocke_b_cast_f32_to(b, value_f32, target);
rocke_b_global_store(b, ptr, idx, cast, 0);
}
rocke_value_t* rocke_b_load_scalar(rocke_ir_builder_t* b,
rocke_value_t* ptr,
rocke_value_t* idx,
const char* dtype)
{
if(!rocke_i_live(b))
{
return NULL;
}
/* Python: if dtype in ("f16", "fp16"): return b.global_load_f16(ptr, idx) */
if(dtype != NULL && (strcmp(dtype, "f16") == 0 || strcmp(dtype, "fp16") == 0))
{
/* Python passes no align kwarg -> default. 0 selects the builder default. */
return rocke_b_global_load_f16(b, ptr, idx, 0);
}
/* Python: if dtype == "bf16": return b.global_load_bf16(ptr, idx) */
if(dtype != NULL && strcmp(dtype, "bf16") == 0)
{
return rocke_b_global_load_bf16(b, ptr, idx, 0);
}
/* Python: raise ValueError(f"unsupported I/O dtype {dtype!r}"). Note this
* message has NO ", expected ..." suffix -- it is load_scalar's own text,
* distinct from io_ir_type's. Mirror it exactly. */
return (rocke_value_t*)rocke_i_set_err(b,
ROCKE_ERR_VALUE,
"unsupported I/O dtype %s%s%s",
dtype ? "'" : "",
dtype ? dtype : "None",
dtype ? "'" : "");
}
rocke_value_t* rocke_b_load_scalar_as_f32(rocke_ir_builder_t* b,
rocke_value_t* ptr,
rocke_value_t* idx,
const char* dtype)
{
rocke_value_t* v;
if(!rocke_i_live(b))
{
return NULL;
}
/* Python: return b.cast_to_f32(load_scalar(b, ptr, idx, dtype=dtype)).
* load_scalar records the ValueError on an unsupported dtype and returns
* NULL; cast_to_f32 of NULL on an errored builder is itself a NULL no-op. */
v = rocke_b_load_scalar(b, ptr, idx, dtype);
if(v == NULL)
{
return NULL;
}
return rocke_b_cast_to_f32(b, v);
}
rocke_value_t* rocke_b_load_vec(
rocke_ir_builder_t* b, rocke_value_t* ptr, rocke_value_t* idx, const char* dtype, int n)
{
const rocke_type_t* ty;
if(!rocke_i_live(b))
{
return NULL;
}
/* Python checks n FIRST: if n not in (2,4,8): raise ValueError(...). */
if(!rocke_io_is_vec_width(n))
{
return (rocke_value_t*)rocke_i_set_err(
b, ROCKE_ERR_VALUE, "load_vec n must be 2/4/8 (got %d); use load_scalar for n=1", n);
}
/* Python: ty = io_ir_type(dtype). Builder-aware: an unsupported dtype
* records io_ir_type's ValueError and returns NULL here. */
ty = rocke_b_io_ir_type(b, dtype);
if(ty == NULL)
{
return NULL;
}
/* Python: if dtype in ("f16","fp16"): return b.global_load_vN_f16(ptr,idx,n)
* return b.global_load_vN(ptr, idx, ty, n)
* Python passes no align -> default (0). */
if(strcmp(dtype, "f16") == 0 || strcmp(dtype, "fp16") == 0)
{
return rocke_b_global_load_vN_f16(b, ptr, idx, n, 0);
}
return rocke_b_global_load_vN(b, ptr, idx, ty, n, 0);
}
int rocke_b_load_vec_as_f32(rocke_ir_builder_t* b,
rocke_value_t* ptr,
rocke_value_t* idx,
const char* dtype,
int n,
rocke_value_t** out)
{
rocke_value_t* v;
int i;
if(!rocke_i_live(b))
{
return 0;
}
if(out == NULL)
{
return 0;
}
/* Python: v = load_vec(b, ptr, idx, dtype=dtype, n=n) */
v = rocke_b_load_vec(b, ptr, idx, dtype, n);
if(v == NULL)
{
return 0;
}
/* Python: return [b.cast_to_f32(b.vec_extract(v, i)) for i in range(n)] */
for(i = 0; i < n; ++i)
{
out[i] = rocke_b_cast_to_f32(b, rocke_b_vec_extract(b, v, i));
}
return 1;
}
int rocke_b_load_lane_slice_f32(rocke_ir_builder_t* b,
rocke_value_t* ptr,
rocke_value_t* row_base,
rocke_value_t* lane_d_base,
const char* dtype,
int ept,
rocke_value_t** out)
{
int k;
if(!rocke_i_live(b))
{
return 0;
}
if(out == NULL)
{
return 0;
}
/* Python: if ept in _VEC_WIDTHS:
* return load_vec_as_f32(
* b, ptr, b.add(row_base, lane_d_base), dtype=dtype, n=ept) */
if(rocke_io_is_vec_width(ept))
{
return rocke_b_load_vec_as_f32(
b, ptr, rocke_b_add(b, row_base, lane_d_base), dtype, ept, out);
}
/* Python scalar fallback:
* return [load_scalar_as_f32(
* b, ptr,
* b.add(row_base, b.add(lane_d_base, b.const_i32(k))),
* dtype=dtype)
* for k in range(ept)] */
for(k = 0; k < ept; ++k)
{
rocke_value_t* addr
= rocke_b_add(b, row_base, rocke_b_add(b, lane_d_base, rocke_b_const_i32(b, k)));
out[k] = rocke_b_load_scalar_as_f32(b, ptr, addr, dtype);
if(out[k] == NULL)
{
/* load_scalar_as_f32 recorded the ValueError already. */
return 0;
}
}
return 1;
}
void rocke_b_store_vec(
rocke_ir_builder_t* b, rocke_value_t* ptr, rocke_value_t* idx, rocke_value_t* value, int n)
{
if(!rocke_i_live(b))
{
return;
}
/* Python: if n not in (2,4,8): raise ValueError(...) */
if(!rocke_io_is_vec_width(n))
{
(void)rocke_i_set_err(
b, ROCKE_ERR_VALUE, "store_vec n must be 2/4/8 (got %d); use store_scalar for n=1", n);
return;
}
/* Python: b.global_store_vN(ptr, idx, value, n). No align -> default (0). */
rocke_b_global_store_vN(b, ptr, idx, value, n, 0);
}
rocke_value_t* rocke_b_pack_f32_to(rocke_ir_builder_t* b,
rocke_value_t* const* scalars_f32,
int n,
const char* dtype)
{
const rocke_type_t* target;
rocke_value_t** casts;
int i;
if(!rocke_i_live(b))
{
return NULL;
}
/* Python: target = io_ir_type(dtype). Builder-aware -> records ValueError
* on an unsupported dtype and returns NULL. */
target = rocke_b_io_ir_type(b, dtype);
if(target == NULL)
{
return NULL;
}
/* Python: casts = [b.cast_f32_to(v, target) for v in scalars_f32].
* Build the cast list in the arena so vec_pack gets a contiguous array. */
casts = (rocke_value_t**)rocke_arena_alloc(&b->arena, (size_t)n * sizeof(*casts));
if(casts == NULL)
{
return NULL;
}
for(i = 0; i < n; ++i)
{
casts[i] = rocke_b_cast_f32_to(b, scalars_f32[i], target);
}
/* Python: return b.vec_pack(casts, target). */
return rocke_b_vec_pack(b, casts, n, target);
}
void rocke_b_vector_row_copy(rocke_ir_builder_t* b,
rocke_value_t* src,
rocke_value_t* dst,
rocke_value_t* src_base,
rocke_value_t* dst_base,
int H,
const char* dtype,
int vec_bytes)
{
const rocke_type_t* ty;
const int elem_bytes = 2; /* f16 / bf16 storage width */
int vec;
int n_chunks;
int c;
int d;
if(!rocke_i_live(b))
{
return;
}
/* Python: ty = io_ir_type(dtype). Builder-aware -> records ValueError on an
* unsupported dtype and returns NULL. */
ty = rocke_b_io_ir_type(b, dtype);
if(ty == NULL)
{
return;
}
/* Python: vec = vec_bytes // elem_bytes; if vec not in (2,4,8): raise. */
vec = vec_bytes / elem_bytes;
if(!rocke_io_is_vec_width(vec))
{
(void)rocke_i_set_err(b,
ROCKE_ERR_VALUE,
"vector_row_copy: vec_bytes %d maps to vec=%d; expected 4/8/16-byte "
"aligned",
vec_bytes,
vec);
return;
}
/* Python: n_chunks = H // vec
* for c in range(n_chunks):
* d = c * vec
* src_addr = b.add(src_base, b.const_i32(d))
* dst_addr = b.add(dst_base, b.const_i32(d))
* v = b.global_load_vN(src, src_addr, ty, vec, align=vec*elem_bytes)
* b.global_store_vN(dst, dst_addr, v, vec, align=vec*elem_bytes) */
n_chunks = H / vec;
for(c = 0; c < n_chunks; ++c)
{
int dd = c * vec;
rocke_value_t* src_addr = rocke_b_add(b, src_base, rocke_b_const_i32(b, dd));
rocke_value_t* dst_addr = rocke_b_add(b, dst_base, rocke_b_const_i32(b, dd));
rocke_value_t* v = rocke_b_global_load_vN(b, src, src_addr, ty, vec, vec * elem_bytes);
rocke_b_global_store_vN(b, dst, dst_addr, v, vec, vec * elem_bytes);
}
/* Python: for d in range(n_chunks * vec, H):
* s = load_scalar_as_f32(b, src, b.add(src_base, b.const_i32(d)),
* dtype=dtype)
* store_scalar_from_f32(b, dst, b.add(dst_base, b.const_i32(d)),
* s, dtype=dtype) */
for(d = n_chunks * vec; d < H; ++d)
{
rocke_value_t* s = rocke_b_load_scalar_as_f32(
b, src, rocke_b_add(b, src_base, rocke_b_const_i32(b, d)), dtype);
rocke_b_store_scalar_from_f32(
b, dst, rocke_b_add(b, dst_base, rocke_b_const_i32(b, d)), s, dtype);
}
}