E6032
E6032 — Anacrossloop that the topology or its place cannot have: a level the topology does not name, a loop outside aspawn onregion or in the wrong order, a size that is not known when the kernel is compiled or is over the limit, or a jump out of the loop.
What the compiler reports
Error[E6032] at 23:{{.*}}the lanes of a block share a tensor that the code of the block makes
Error[E6032] at 36:{{.*}}the lanes of a block share a tensor that the code of the block makes
Error[E6032] at 50:{{.*}}a subgroup has no memory of its own
Error[E6032] at 67:{{.*}}So the size may not call a function
The fragments the test suite holds the compiler to. An ellipsis marks text the test does not constrain; the emitted message also carries a source location.
A program that triggers it
//
// Part of the Vx Project, under the Apache License v2.0 with LLVM Exceptions.
// See LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Code of a block outside its lane loops runs once for the block, so the lanes of the block
// share a tensor that it makes. Such a tensor must be in a memory with `scope: cta` or
// `scope: sm`, such as `Memory::SMEM` of the H100 file. A subgroup has no memory of its own.
// The host computes the size of a loop of blocks before the launch, and the kernel computes it
// again, so the size may not call a function. Each function gets E6032.
//
pub fn tensor_in_default_memory(o : &mut Tensor<f32, [?, 8], Memory::HBM>, n : i32) -> void {
spawn on(Topology::Device) {
for b in 0..n across blocks {
let mut t = Tensor<f32, [8]>::new();
t[0] = 2.0;
for l in 0..8 across lanes {
o[b][l] = t[0];
}
}
}
return;
}
pub fn tensor_in_device_memory(o : &mut Tensor<f32, [?, 8], Memory::HBM>, n : i32) -> void {
spawn on(Topology::Device) {
for b in 0..n across blocks {
let mut t = unsafe { Tensor<f32, [8], Memory::HBM>::uninit() };
for l in 0..8 across lanes {
t[l] = 1.0;
o[b][l] = t[l];
}
}
}
return;
}
pub fn tensor_in_subgroup_code(o : &mut Tensor<f32, [?, 32], Memory::HBM>, n : i32) -> void {
spawn on(Topology::Device) {
for b in 0..n across blocks {
for w in 0..4 across subgroups {
let mut t = unsafe { Tensor<f32, [32], Memory::SMEM>::uninit() };
for l in 0..32 across lanes {
t[l] = 1.0;
o[b * 4 + w][l] = t[l];
}
}
}
}
return;
}
fn half(n : i32) on Topology::Device -> i32 {
return n / 2;
}
pub fn size_that_calls(o : &mut Tensor<f32, [?, 8], Memory::HBM>, n : i32) -> void {
spawn on(Topology::Device) {
for b in 0..half(n) across blocks {
for l in 0..8 across lanes {
o[b][l] = 1.0;
}
}
}
return;
}
From tests/frontend/fail/across_block_tensors_and_sizes_that_are_not_allowed.vx, which asserts this diagnostic on every commit.
Related
Topologies and memoryThe chapter covering the rule this code enforces.
All diagnosticsEvery code the compiler can emit, grouped by stage.
E6031A tensor shape that folds to a negative extent, or whose size is more than 2^63 - 1 bytes, the most an allocation can ask for. The size is known, and it is not one the generated code can allocate: it would wrap.
E6033An index into a tensor in a
scope: thread space that is not known when compiling. Such a tensor stays in registers, and a register has no address. So each index must be a constant, or made of constants and the variables of for loops with constant bounds, at most 1024 passes each. The code generator unrolls those loops.