Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ test_categories:
quick:
description: "rocKE pre-registered smoke/golden/unit checks (C++ + Python)"
test_patterns:
- "rocke_optimization_barrier"
- "rocke_arch_target_identity"
- "rocke_ir_serialize_roundtrip"
- "rocke_mma_frag_ssot"
Expand All @@ -47,6 +48,7 @@ test_categories:
ffm-quick:
description: "Fast-feedback mechanism (FFM) rocKE smoke/golden/unit checks"
test_patterns:
- "rocke_optimization_barrier"
- "rocke_arch_target_identity"
- "rocke_ir_serialize_roundtrip"
- "rocke_mma_frag_ssot"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,39 @@ rocke_value_t* rocke_b_register_p_from_qk_c(rocke_ir_builder_t* b,
/* inline asm */
/* ===================================================================== */

rocke_value_t* rocke_b_optimization_barrier(rocke_ir_builder_t* b, rocke_value_t* value)
{
if(!rocke_i_live(b))
{
return NULL;
}
if(!value || !value->type || value->type->kind != ROCKE_TYPE_SCALAR
|| value->type->scalar < ROCKE_SCALAR_I1 || value->type->scalar > ROCKE_SCALAR_BF8E5M2)
{
return (rocke_value_t*)rocke_i_set_err(
b, ROCKE_ERR_VALUE, "optimization_barrier requires a directly lowerable scalar or i1");
}
if(value->type->scalar == ROCKE_SCALAR_I1 || value->type->scalar == ROCKE_SCALAR_I8
|| value->type->scalar == ROCKE_SCALAR_FP8E4M3
|| value->type->scalar == ROCKE_SCALAR_BF8E5M2)
{
bool encoded_float = value->type->scalar == ROCKE_SCALAR_FP8E4M3
|| value->type->scalar == ROCKE_SCALAR_BF8E5M2;
rocke_value_t* raw = encoded_float ? rocke_b_bitcast(b, value, rocke_i8()) : value;
rocke_value_t* wide = rocke_b_zext(b, raw, rocke_i32());
rocke_value_t* opaque = rocke_b_optimization_barrier(b, wide);
rocke_value_t* narrow = rocke_b_trunc(b, opaque, raw->type);
return encoded_float ? rocke_b_bitcast(b, narrow, value->type) : narrow;
}
rocke_inline_asm_opts_t opts = {};
opts.sideeffect_set = true;
opts.sideeffect = false;
rocke_value_t* operands[] = {value};
const rocke_type_t* types[] = {value->type};
rocke_op_t* op = rocke_b_inline_asm(b, "", "=v,0", operands, 1, types, 1, &opts);
Comment thread
tenpercent marked this conversation as resolved.
Resolved
return op ? op->results[0] : NULL;
}

rocke_op_t* rocke_b_inline_asm(rocke_ir_builder_t* b,
const char* asm_template,
const char* constraints,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,14 @@ rocke_value_t* rocke_b_mma(rocke_ir_builder_t* b,
int num_extra);

/* ----- inline asm ----- */
/* Value-preserving optimization boundary for i1/i8/i16/i32/i64/bf16/f16/f32/
* fp8e4m3/bf8e5m2. Packed FP4/FP6 and scale formats use integer storage/carriers.
* Use the returned value; unused results may be eliminated. Empty tied
* VGPR asm hides the producer from consumers without memory/thread ordering.
* Predicates and byte storage bridge through i32. Pointers/vectors are rejected. Register moves
* may still be required. */
rocke_value_t* rocke_b_optimization_barrier(rocke_ir_builder_t* b, rocke_value_t* value);

/* operands/result_types are explicit arrays; constraints/template are strings.
* Returns the op (results accessible via op->results) since asm may be 0/1/N
* results. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,68 @@

This page documents the DSL primitives that map closely to GPU hardware behavior. Everything below is verified against `helpers/atoms.py`, `helpers/loads.py`, `helpers/layouts.py`, `helpers/schedule.py`, `helpers/pipeline.py`, `helpers/epilogues.py`, `helpers/reduction.py`, `core/ir.py`, and `core/lower_llvm.py`.

## Keeping operations separate

`IRBuilder.optimization_barrier(value)` returns the same scalar value and type.
It stops the compiler from combining the operation that produces the value
with an operation that uses the returned value. Use it when combining those
operations would change rounding:

```python
product = ir.optimization_barrier(ir.fmul(a, b))
result = ir.fadd(product, c)
```

In this example, the multiplication rounds to the value's type before the
addition. The compiler cannot replace the pair with a fused multiply-add that
rounds only once. The same tool can keep FP32 multiplication separate from FP16
conversion. **Use the returned value** in the next operation; calling the
barrier and then using the original value does not protect that operation.

This feature is covered through AMDGPU LLVM IR and COMGR compilation. Native
HIP-source lowering of the barrier is unsupported.

The C API is `rocke_b_optimization_barrier(builder, value)`. Both APIs use the
existing inline-assembly operation. The assembly text is empty, and its output
uses the same vector register as its input. This preserves the value while
preventing the compiler from looking through the barrier.

Supported scalar types are `i8`, `i16`, `i32`, `i64`, `f16`, `bf16`, `f32`,
`fp8e4m3`, and `bf8e5m2`. The stored bits of the low-bit float types are preserved.
Booleans (`i1`) are supported too. A boolean can be stored as a lane mask, and
an 8-bit value cannot directly use the required vector-register constraint.
For these types, the helper widens the value to `i32`, applies the barrier,
then restores the original type. Pointers and vectors are rejected. For a
vector, extract its elements and apply the barrier to each element as needed.

FP4/FP6/BF6 and E8M0/E5M3 have logical dtype identities, but do not have
representations for direct scalar lowering. Apply the barrier to their integer
storage bytes or packed carrier words. For example, after loading a scale byte:

```python
scale_bits = ir.optimization_barrier(ir.global_load_i8(scale_ptr, index))
```

This preserves the encoded byte; it does not decode the scale or introduce a
floating-point rounding step. The C API admits the same types as Python.

### How this differs from `sched_barrier`

| API | What it controls |
| --- | --- |
| `optimization_barrier(value)` | Whether the compiler can combine operations across a particular value. |
| `sched_barrier(mask)` | Which machine-instruction classes can move across a point during scheduling. |

Fusion can happen before instruction scheduling. A scheduling barrier can
control where a fused instruction runs, but it does not restore the intermediate
result that fusion removed. Use the value barrier to keep that result.

The value barrier does not order memory accesses or synchronize threads. It
also does not fix instruction order or disable optimizations within either
neighboring expression. The compiler can remove it if its result is unused.
The empty assembly adds no instruction itself, but register moves and keeping
operations separate can still have a cost.

## MFMA Atoms

`helpers/atoms.py::MfmaAtom` packages one MFMA intrinsic's shape, per-lane widths, accumulator width, dispatch to `IRBuilder`, and lane-to-output mapping.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ analyze_hsaco, analyze_llvm_ir, compare_variant_reports
parse_isa, parse_resources
```

## Keeping arithmetic operations separate

`IRBuilder.optimization_barrier(value)` returns the same scalar value and type
and prevents the compiler from combining operations across that value. Use
the returned value when an intermediate result must round before the next
operation. The C API is `rocke_b_optimization_barrier(builder, value)`.
The supported compilation path is AMDGPU LLVM IR through COMGR; native
HIP-source lowering is unsupported.

See [the value-barrier guide](../primitives/intrinsics_and_primitives.md#keeping-operations-separate)
for examples, supported types, and the difference from `sched_barrier`.

## `from rocke.helpers import ...` (extras not in top-level)

```text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,17 @@ Other scores and source line numbers retain their stated historical basis.

## 1. The canonical dtype set

The original basis snapshot listed ten scalar IR types. The table below also
includes the subsequently added logical TF32 type. For the newer logical
low-bit and scale types, see [dtype storage and packing](../architecture/dtype_storage_packing.md).
**Current clarification (2026-10-07).** The original basis snapshot listed
ten directly lowerable scalar representations. The table below also includes
the subsequently added logical TF32 type with its I32 carrier. The registry
recognizes `fp4e2m1`, `fp6e2m3`, `fp6e3m2`, `e8m0`, and `e5m3` as logical `Type`
identities preserved by serialization; their packed-data paths use `i8` storage
and integer carrier registers. See [dtype storage and packing](../architecture/dtype_storage_packing.md).
Logical recognition does not provide generic scalar arithmetic or conversion.
The optimization-barrier API admits the original ten representations; use an
I32 carrier for TF32 and integer storage/carriers for packed formats.
Other coverage scores and source line numbers retain their historical basis.

The definitions in both engines are:

- Python: `platform/python/rocke/core/ir.py:43-52`
Expand All @@ -59,9 +67,10 @@ LLVM mapping: `lower_llvm.py:1093-1129` (Python, `_llvm_type`) ↔
storage and materialized through `llvm.amdgcn.cvt.*` intrinsics — there is no
native `<8 x fp8>` LLVM type in the emitter.

**Sub-byte / block formats in the original snapshot.** The basis tree treated
`i4`, `fp4`, `fp6`, and the `e8m0` MX scale as packed encodings consumed by
dedicated helpers/atoms. Those historical scores do not describe the newer
**Packed formats use a separate storage representation.** FP4/FP6/BF6 and
E8M0/E5M3 have logical `Type` identities; dedicated helpers/atoms consume their
encoded bytes or packed integer carriers (see §4). The original basis treated
these as packed encodings, so its historical scores do not describe the newer
logical-type and storage APIs linked above.

> CK-Tile gives these formats named logical types backed by a packed storage
Expand All @@ -72,8 +81,8 @@ logical-type and storage APIs linked above.
> `e5m3.hpp:37`), and `tf32_t = tfloat32_t` (`tfloat32.hpp:68`) — each with a
> `native_t<>` specialization mapping the logical type to its storage. This
> separates the logical format named in an API from its storage representation.
> rocKE's dedicated packed-format paths can provide support without a first-class
> IR `Type`; C5 separately measures whether kernel families accept the format.
> rocKE separates logical dtype identity from packed storage and carrier types;
> C5 separately measures whether kernel families accept the format.

**TF32 is a logical dtype with a 32-bit I32 carrier.** `tf32` and its `xf32`
alias select two native gfx942 XF32 atoms independently of ordinary FP32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2210,6 +2210,35 @@ def mfma_f32_32x32x16_fp8(self, a: Value, b: Value, c: Value) -> Value:
def mfma_f32_32x32x16_bf8(self, a: Value, b: Value, c: Value) -> Value:
return self.mma("mfma_f32_32x32x16_bf8", a, b, c)

def optimization_barrier(self, value: Value) -> Value:
"""Preserve a scalar value while hiding its definition from consumers.

Use the returned value to prevent arithmetic contraction/reassociation
across this dependency, e.g. ``fadd(optimization_barrier(fmul(a,b)),c)``.
This preserves an intermediate rounding boundary; it does not disable
optimization within the producer or consumer expression.

Supports directly lowerable numeric scalars and i1 predicates. Packed
FP4/FP6 and scale formats use their integer storage/carrier values.
AMDGPU uses an empty
tied-VGPR asm: no instruction is emitted by the barrier itself, but it
can require register moves and prevent profitable instruction combines.
It has no memory/thread ordering or side effects; unused results may
be eliminated. Pointers and vectors are rejected; apply it to extracted
vector elements when needed.
"""
if value.type in (I1, I8, FP8E4M3, BF8E5M2):
# Lane predicates and byte values cannot directly tie a VGPR.
raw = value if value.type in (I1, I8) else self.bitcast(value, I8)
wide = self.optimization_barrier(self.zext(raw, I32))
narrow = self.trunc(wide, raw.type)
return narrow if raw is value else self.bitcast(narrow, value.type)
if value.type not in (I8, I16, I32, I64, BF16, F16, F32, FP8E4M3, BF8E5M2):
raise ValueError(
"optimization_barrier requires a directly lowerable scalar or i1"
)
return self.inline_asm("", "=v,0", [value], value.type, sideeffect=False)

def inline_asm(
self,
template: str,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/core/smoke.cpp")
endif()

if(BUILD_TESTING)
add_executable(rocke_optimization_barrier core/test_optimization_barrier.cpp)
target_link_libraries(rocke_optimization_barrier PRIVATE rocke_core)
add_test(NAME rocke_optimization_barrier COMMAND rocke_optimization_barrier)
set_tests_properties(rocke_optimization_barrier
PROPERTIES LABELS "ckc;unit;host")
rocke_cxx_test_installed_name(_rocke_installed_name
rocke_optimization_barrier)
set_target_properties(rocke_optimization_barrier
PROPERTIES OUTPUT_NAME "${_rocke_installed_name}")
install(TARGETS rocke_optimization_barrier
RUNTIME DESTINATION ${ROCKE_CXX_TEST_INSTALL_DIR})
set_property(GLOBAL APPEND PROPERTY ROCKE_INSTALL_TEST_TARGETS
rocke_optimization_barrier)

add_executable(rocke_tf32 core/test_tf32.cpp)
target_link_libraries(rocke_tf32 PRIVATE rocke_core)
add_test(NAME rocke_tf32 COMMAND rocke_tf32)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,19 @@ the configured build. With no configured build or explicit override, the runner
reports that parity as skipped; direct pytest invocations can use the same
overrides.

### Optimization-barrier coverage

The standalone `rocke_optimization_barrier` CTest checks native scalar admission,
rejection, serialization round trips, and LLVM lowering. It is installed with the
other native tests and does not require a pytest executable-discovery fixture.

`core/test_optimization_barrier.py` checks the Python builder and serialized LLVM
output. The dedicated Python/C parity emitters cover LLVM, canonical IR, and
verification; committed golden entries anchor the existing configurations.
`instances/test_optimization_barrier_numeric.py` compiles through COMGR and
checks scalar payloads, lane predicates, and intermediate rounding on a GPU.
Native HIP-source lowering is outside this feature's supported path.

## Installed pinned-reference suites

These suites are registered by platform CMake for the installed provider, with
Expand Down
Loading
Loading