E6038
E6038 — Aspawn onbody for a device that uses a channel end, a lock, or a value that holds one. The standard library marks these types with the traitHostOnly. A wait on the device for the host can wait forever, because a GPU does not promise that its threads make progress. Send or receive before thespawn on, or after it. Aspawn on(Topology::CPU)body runs on the host, so it can use them.
What the compiler reports
Error[E6038] at {{.*}}.vx:[[@LINE+1]]:5: the body of the `spawn on` at line [[@LINE-2]] runs on GPU[0], and it uses 'tx': 'Sender<i64>' can wait for the host. A channel end and a lock stay on the host, because a wait on the device for the host can wait forever. Use 'tx' before the `spawn on`, or after it
Error[E6038] at {{.*}}.vx:[[@LINE+1]]:11: {{.*}} it uses 'rx': 'Receiver<i64>' can wait for the host.
Error[E6038] at {{.*}}.vx:[[@LINE+1]]:11: {{.*}} it uses 'w': field 'rx' of 'Waiter' has type 'Receiver<i64>', and 'Receiver<i64>' can wait for the host.
Error[E6038] at {{.*}}.vx:[[@LINE+1]]:14: {{.*}} it uses 'm': 'Mutex<i64>' can wait for the host.
Error[E6038] at {{.*}}.vx:[[@LINE+1]]:14: {{.*}} it uses 'g': 'MutexGuard<i64>' can wait for the host.
{{^}}Semantic check failed on {{.*}}: 5 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 `spawn on` body for a device must not use a channel end or a lock, because a wait on the
// device for the host can wait forever. A body on the CPU can use them, and so can the code
// before and after a `spawn on`.
//
import std::sync::channel;
import std::sync::mutex;
struct Waiter {
rx : Receiver<i64>,
}
fn send_on_the_gpu(tx : Sender<i64>) -> void {
let mut r = unsafe { Tensor<f32, [4], Memory::GPU_HBM>::uninit() };
spawn on(Topology::GPU) {
r[0] = 1.0;
tx.send(7);
}
}
fn receive_through_a_reference_on_the_gpu(rx : &Receiver<i64>) -> void {
let mut r = unsafe { Tensor<f32, [4], Memory::GPU_HBM>::uninit() };
spawn on(Topology::GPU) {
r[0] = 1.0;
match rx.recv() {
Result<i64, RecvError>::Ok(v) => {
r[1] = v as f32;
}
Result<i64, RecvError>::Err(_e) => {
r[1] = 0.0;
}
}
r[2] = 2.0;
}
}
fn a_struct_that_holds_a_channel_end(w : &Waiter) -> void {
let mut r = unsafe { Tensor<f32, [4], Memory::GPU_HBM>::uninit() };
spawn on(Topology::GPU) {
r[0] = 1.0;
match w.rx.recv() {
Result<i64, RecvError>::Ok(v) => {
r[1] = v as f32;
}
Result<i64, RecvError>::Err(_e) => {
r[1] = 0.0;
}
}
r[2] = 2.0;
}
}
fn lock_on_the_gpu(m : &Mutex<i64>) -> void {
let mut r = unsafe { Tensor<f32, [4], Memory::GPU_HBM>::uninit() };
spawn on(Topology::GPU) {
r[0] = 1.0;
let _g = m.lock();
}
}
fn a_guard_on_the_gpu(m : &Mutex<i64>) -> void {
let mut g = m.lock();
let mut r = unsafe { Tensor<f32, [4], Memory::GPU_HBM>::uninit() };
spawn on(Topology::GPU) {
r[0] = 1.0;
let _v = g.get();
}
}
// A `spawn on(Topology::CPU)` body runs on the host, so it can use a channel end.
fn send_on_the_cpu(tx : &Sender<i64>) -> void {
spawn on(Topology::CPU) {
tx.send(8);
}
}
// The code before and after a `spawn on` runs on the host.
fn send_around_the_gpu(tx : &Sender<i64>) -> void {
tx.send(1);
let mut r = unsafe { Tensor<f32, [4], Memory::GPU_HBM>::uninit() };
spawn on(Topology::GPU) {
r[0] = 1.0;
}
tx.send(2);
}
fn main() -> i32 {
return 0;
}
// The five errors above are the only ones. `--implicit-check-not` does not look past the last
// `CHECK-DAG` line, so this line counts them.
From tests/frontend/fail/channel_ends_and_locks_in_a_device_body.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.
E6037A placement whose memory is not built in and has no
scope:. Such a memory has no address space on the target. Declare the memory with scope: device, sm, cta or thread.
E6039A spawn in a loop of its tasks block, with a count of passes that is not known at compile time, and tasks that use a memory with a capacity. Each pass starts a task, and the tasks run at the same time, so their working set has no bound. Use a for loop over a constant range, or join each task in its pass. Downgraded to W1028 when the space is declared overcommit.