E6041

E6041 — A function that makes a channel of tensors in a memory with a capacity, and returns an end of that channel. The room of the channel is counted in the function that calls channel, only until that function returns.

What the compiler reports

Error[E6041] at {{.*}}.vx:[[@LINE+1]]:10: 'make' makes a channel of tensors in a memory with a capacity, and returns an end of a channel. The room of the channel is counted in the function that calls `channel`, only until it returns. Make the channel in the function that uses its ends
{{^}}Semantic check failed on {{.*}}: 4 error(s) emitted

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
//
// A channel of tensors in the GPU memory `W` can hold `n` of them at once. So the capacity check
// counts `n` tensors from the `channel(n)` call until the function that calls it returns. Each
// tensor here is 512 bytes, and `W` holds 4096.
//
// - `too_big`: a room of 9 tensors is 4608 bytes, more than all of `W`.
// - `with_a_call`: a room of 7 tensors, 3584 bytes, stays while `place_two` places 1024 more.
// - `room_not_known`: the room is a parameter, so the check has no bound.
// - `make`: the room is counted in `make`, so `make` cannot give the ends to its caller.
//

import std::sync::channel;
import core::mem;

Memory CPU_DRAM {}
Memory W {
  within: Memory::CPU_DRAM, capacity: 4 KiB, managed: explicit, scope: device
}
Topology Dev {
  arch: nvptx64,
  memory: Memory::W,
  visible: [Memory::W],
  transfer Memory::CPU_DRAM -> Memory::W : 10,
  transfer Memory::W -> Memory::CPU_DRAM : 10
}

fn too_big() -> void {
  let (tx, rx) = channel<Tensor<f32, [128], Memory::W>>(9);
}

fn place_two() -> f32 {
  let a = transfer(Tensor<f32, [128]>::fill(1.0), Memory::W);
  let b = transfer(Tensor<f32, [128]>::fill(2.0), Memory::W);
  let c = transfer(a, Memory::CPU_DRAM);
  let d = transfer(b, Memory::CPU_DRAM);
  return c[0] + d[0];
}

fn with_a_call() -> f32 {
  let (tx, rx) = channel<Tensor<f32, [128], Memory::W>>(7);
  return place_two();
}

fn room_not_known(n : i64) -> void {
  let (tx, rx) = channel<Tensor<f32, [128], Memory::W>>(n);
}

fn make() -> (Sender<Tensor<f32, [128], Memory::W>>, Receiver<Tensor<f32, [128], Memory::W>>) {
  return channel<Tensor<f32, [128], Memory::W>>(2);
}

fn main() -> i32 {
  too_big();
  let _x = with_a_call();
  room_not_known(2);
  let (_tx, _rx) = make();
  return 0;
}

From tests/frontend/fail/the_room_of_a_channel_of_gpu_tensors_counts.vx, which asserts this diagnostic on every commit.

Related