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
271 lines
10 KiB
C++
271 lines
10 KiB
C++
// Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
|
|
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* helper_rocke.helpers.sweep.c -- C99 port of rocke.helpers.sweep.
|
|
*
|
|
* Ports sweep_row_chunks, pass2_row_chunks and RowChunkSweepResult. The
|
|
* builder-call sequence is byte-identical to the Python so the emitted IR op
|
|
* stream matches exactly: per chunk both functions emit
|
|
*
|
|
* n_off = b.add(b.mul(b.const_i32(k * block_size), c_vec),
|
|
* b.mul(tid, c_vec))
|
|
*
|
|
* with c_vec = b.const_i32(vec) hoisted once before the loop, exactly as the
|
|
* Python does.
|
|
*/
|
|
#include "rocke/helper_rocke.helpers.sweep.h"
|
|
|
|
#include "rocke/arena.h"
|
|
#include "rocke/ir_internal.h" /* rocke_i_set_err for Python-ValueError parity */
|
|
|
|
/* ------------------------------------------------------------------------
|
|
* sweep_row_chunks
|
|
*
|
|
* Python:
|
|
* if tile.rank != 2: raise ValueError(...)
|
|
* if elems_per_thread % vec: raise ValueError(...)
|
|
* chunks_per_thread = elems_per_thread // vec
|
|
* if row is not None:
|
|
* old_origin = tile.origin
|
|
* tile = TileWindow(view=tile.view, lengths=tile.lengths,
|
|
* origin=(row, old_origin[1]))
|
|
* cached = []
|
|
* c_vec = b.const_i32(vec)
|
|
* for k in range(chunks_per_thread):
|
|
* n_off = b.add(b.mul(b.const_i32(k * block_size), c_vec),
|
|
* b.mul(tid, c_vec))
|
|
* x_scalars = tile.load_vec_as_f32(b, b.const_i32(0), n_off, n=vec)
|
|
* if cache: cached.extend(x_scalars)
|
|
* if body is not None: body(n_off, x_scalars)
|
|
* return RowChunkSweepResult(cached=cached, chunks_per_thread=...)
|
|
* ------------------------------------------------------------------------ */
|
|
rocke_row_chunk_sweep_result_t rocke_sweep_row_chunks(rocke_ir_builder_t* b,
|
|
const rocke_tile_window_t* tile,
|
|
rocke_value_t* tid,
|
|
int block_size,
|
|
int vec,
|
|
int elems_per_thread,
|
|
rocke_value_t* row,
|
|
rocke_sweep_row_body_fn body,
|
|
void* user,
|
|
bool cache)
|
|
{
|
|
rocke_row_chunk_sweep_result_t res;
|
|
rocke_tile_window_t shifted;
|
|
const rocke_tile_window_t* t;
|
|
int chunks_per_thread;
|
|
rocke_value_t* c_vec;
|
|
rocke_value_t** scratch;
|
|
int k;
|
|
|
|
res.cached = NULL;
|
|
res.num_cached = 0;
|
|
res.chunks_per_thread = 0;
|
|
|
|
if(tile->rank != 2)
|
|
{
|
|
rocke_i_set_err(b,
|
|
ROCKE_ERR_VALUE,
|
|
"sweep_row_chunks expects a 2D TileWindow (got rank %d)",
|
|
tile->rank);
|
|
return res;
|
|
}
|
|
if(vec != 0 && (elems_per_thread % vec) != 0)
|
|
{
|
|
rocke_i_set_err(b,
|
|
ROCKE_ERR_VALUE,
|
|
"elems_per_thread (%d) not divisible by vec (%d)",
|
|
elems_per_thread,
|
|
vec);
|
|
return res;
|
|
}
|
|
|
|
chunks_per_thread = elems_per_thread / vec;
|
|
res.chunks_per_thread = chunks_per_thread;
|
|
|
|
/* row is not None: shift the tile origin to (row, old_origin[1]). */
|
|
t = tile;
|
|
if(row != NULL)
|
|
{
|
|
shifted = *tile;
|
|
shifted.origin[0] = row;
|
|
shifted.origin[1] = tile->origin[1];
|
|
t = &shifted;
|
|
}
|
|
|
|
/* cached: when caching, sized chunks_per_thread * vec (element order
|
|
* across chunks), arena-owned to outlive this frame. */
|
|
if(cache && chunks_per_thread > 0 && vec > 0)
|
|
{
|
|
res.cached = (rocke_value_t**)rocke_arena_alloc(
|
|
&b->arena, (size_t)chunks_per_thread * (size_t)vec * sizeof(rocke_value_t*));
|
|
}
|
|
|
|
/* Per-chunk scratch buffer for the vec f32 scalars from the load. */
|
|
scratch = (rocke_value_t**)rocke_arena_alloc(
|
|
&b->arena, (size_t)(vec > 0 ? vec : 1) * sizeof(rocke_value_t*));
|
|
|
|
c_vec = rocke_b_const_i32(b, vec);
|
|
for(k = 0; k < chunks_per_thread; ++k)
|
|
{
|
|
rocke_value_t* n_off;
|
|
rocke_value_t* mul_kbs;
|
|
rocke_value_t* mul_tid;
|
|
rocke_value_t* local_indices[2];
|
|
int i;
|
|
|
|
/* Python: n_off = b.add(b.mul(b.const_i32(k*block_size), c_vec),
|
|
* b.mul(tid, c_vec))
|
|
* Python evaluates the two mul() args left-to-right, so the
|
|
* (k*block_size)*vec multiply is emitted BEFORE the tid*vec multiply.
|
|
* C function-argument evaluation order is unspecified, so the two
|
|
* multiplies are hoisted into temporaries to pin Python's order. */
|
|
mul_kbs = rocke_b_mul(b, rocke_b_const_i32(b, (int64_t)k * (int64_t)block_size), c_vec);
|
|
mul_tid = rocke_b_mul(b, tid, c_vec);
|
|
n_off = rocke_b_add(b, mul_kbs, mul_tid);
|
|
|
|
/* tile.load_vec_as_f32(b, b.const_i32(0), n_off, n=vec)
|
|
* The b.const_i32(0) is allocated at the call site each iteration
|
|
* (NOT hoisted) so the SSA-id counter advances exactly as in Python,
|
|
* which spells the literal-0 column index inline at the load. */
|
|
local_indices[0] = rocke_b_const_i32(b, 0);
|
|
local_indices[1] = n_off;
|
|
rocke_tile_window_load_vec_as_f32(b, t, local_indices, 2, vec, scratch);
|
|
|
|
if(cache && res.cached != NULL)
|
|
{
|
|
for(i = 0; i < vec; ++i)
|
|
{
|
|
res.cached[res.num_cached++] = scratch[i];
|
|
}
|
|
}
|
|
if(body != NULL)
|
|
{
|
|
body(b, n_off, scratch, vec, user);
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------
|
|
* pass2_row_chunks
|
|
*
|
|
* Python:
|
|
* if tile.rank != 2: raise ValueError(...)
|
|
* if body is None: raise ValueError(...)
|
|
* if elems_per_thread % vec: raise ValueError(...)
|
|
* if row is not None:
|
|
* old_origin = tile.origin
|
|
* tile = TileWindow(view=..., lengths=..., origin=(row, old_origin[1]))
|
|
* chunks_per_thread = elems_per_thread // vec
|
|
* c_vec = b.const_i32(vec)
|
|
* for k in range(chunks_per_thread):
|
|
* n_off = b.add(b.mul(b.const_i32(k * block_size), c_vec),
|
|
* b.mul(tid, c_vec))
|
|
* if cached_f32: x_scalars = list(cached_f32[k*vec:(k+1)*vec])
|
|
* else: x_scalars = []
|
|
* out = body(n_off, k, x_scalars)
|
|
* if len(out) != vec: raise ValueError(...)
|
|
* tile.store_vec_from_f32(b, b.const_i32(0), n_off, values=out)
|
|
* ------------------------------------------------------------------------ */
|
|
void rocke_pass2_row_chunks(rocke_ir_builder_t* b,
|
|
const rocke_tile_window_t* tile,
|
|
rocke_value_t* tid,
|
|
int block_size,
|
|
int vec,
|
|
int elems_per_thread,
|
|
rocke_value_t* row,
|
|
rocke_pass2_row_body_fn body,
|
|
void* user,
|
|
rocke_value_t* const* cached_f32,
|
|
int num_cached_f32)
|
|
{
|
|
rocke_tile_window_t shifted;
|
|
const rocke_tile_window_t* t;
|
|
int chunks_per_thread;
|
|
rocke_value_t* c_vec;
|
|
rocke_value_t** out;
|
|
int k;
|
|
|
|
if(tile->rank != 2)
|
|
{
|
|
rocke_i_set_err(b,
|
|
ROCKE_ERR_VALUE,
|
|
"pass2_row_chunks expects a 2D TileWindow (got rank %d)",
|
|
tile->rank);
|
|
return;
|
|
}
|
|
if(body == NULL)
|
|
{
|
|
rocke_i_set_err(b, ROCKE_ERR_VALUE, "pass2_row_chunks requires a body callback");
|
|
return;
|
|
}
|
|
if(vec != 0 && (elems_per_thread % vec) != 0)
|
|
{
|
|
rocke_i_set_err(b,
|
|
ROCKE_ERR_VALUE,
|
|
"elems_per_thread (%d) not divisible by vec (%d)",
|
|
elems_per_thread,
|
|
vec);
|
|
return;
|
|
}
|
|
|
|
/* row is not None: shift the tile origin to (row, old_origin[1]). */
|
|
t = tile;
|
|
if(row != NULL)
|
|
{
|
|
shifted = *tile;
|
|
shifted.origin[0] = row;
|
|
shifted.origin[1] = tile->origin[1];
|
|
t = &shifted;
|
|
}
|
|
|
|
chunks_per_thread = elems_per_thread / vec;
|
|
|
|
/* Per-chunk out buffer for the vec f32 scalars the body returns. */
|
|
out = (rocke_value_t**)rocke_arena_alloc(&b->arena,
|
|
(size_t)(vec > 0 ? vec : 1) * sizeof(rocke_value_t*));
|
|
|
|
c_vec = rocke_b_const_i32(b, vec);
|
|
for(k = 0; k < chunks_per_thread; ++k)
|
|
{
|
|
rocke_value_t* n_off;
|
|
rocke_value_t* mul_kbs;
|
|
rocke_value_t* mul_tid;
|
|
rocke_value_t* const* x_scalars;
|
|
int num_x;
|
|
rocke_value_t* local_indices[2];
|
|
|
|
/* Python: n_off = b.add(b.mul(b.const_i32(k*block_size), c_vec),
|
|
* b.mul(tid, c_vec)) -- left-to-right arg eval
|
|
* emits the (k*block_size)*vec multiply before the tid*vec multiply.
|
|
* Hoist to temporaries to pin that order in C. */
|
|
mul_kbs = rocke_b_mul(b, rocke_b_const_i32(b, (int64_t)k * (int64_t)block_size), c_vec);
|
|
mul_tid = rocke_b_mul(b, tid, c_vec);
|
|
n_off = rocke_b_add(b, mul_kbs, mul_tid);
|
|
|
|
/* x_scalars = cached_f32[k*vec:(k+1)*vec] when cache present, else []. */
|
|
if(num_cached_f32 > 0 && cached_f32 != NULL)
|
|
{
|
|
x_scalars = cached_f32 + (size_t)k * (size_t)vec;
|
|
num_x = vec;
|
|
}
|
|
else
|
|
{
|
|
x_scalars = NULL;
|
|
num_x = 0;
|
|
}
|
|
|
|
/* out = body(n_off, k, x_scalars); the callback fills out[0..vec). */
|
|
body(b, n_off, k, x_scalars, num_x, out, vec, user);
|
|
|
|
/* tile.store_vec_from_f32(b, b.const_i32(0), n_off, values=out)
|
|
* b.const_i32(0) allocated at the call site (NOT hoisted) so the
|
|
* SSA-id counter advances exactly as in Python. */
|
|
local_indices[0] = rocke_b_const_i32(b, 0);
|
|
local_indices[1] = n_off;
|
|
rocke_tile_window_store_vec_from_f32(b, t, local_indices, 2, out, vec);
|
|
}
|
|
}
|