MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
The gpu Plugin

See also
mim::plug::gpu

Dependencies

plugin core;
plugin mem;
import compile;

Types

Address Spaces

Address space numbers correlate to the ones used by LLVM.

See also
let %gpu.addr_space_global = 1;
let %gpu.addr_space_shared = 3;
let %gpu.addr_space_const = 4;
let %gpu.addr_space_local = 5;

Memory Types

let %gpu.GlobalM = %mem.M %gpu.addr_space_global;
let %gpu.SharedM = %mem.M %gpu.addr_space_shared;
let %gpu.ConstM = %mem.M %gpu.addr_space_const;
let %gpu.LocalM = %mem.M %gpu.addr_space_local;

Pointer Types

lam %gpu.GlobalPtr(T: *): * = %mem.Ptr (T, %gpu.addr_space_global);
lam %gpu.SharedPtr(T: *): * = %mem.Ptr (T, %gpu.addr_space_shared);
lam %gpu.ConstPtr(T: *): * = %mem.Ptr (T, %gpu.addr_space_const);
lam %gpu.LocalPtr(T: *): * = %mem.Ptr (T, %gpu.addr_space_local);

Streams

axm %gpu.Stream: *;
axm %gpu.default_stream: %gpu.Stream;

Operations

Context

%gpu.init

Initializes the GPU runtime.

// TODO: consider allowing multiple contexts and configuration of device number or context flags
axm %gpu.init: %mem.M 0 → [%mem.M 0, %gpu.GlobalM, %gpu.ConstM];

%gpu.deinit

Deinitializes the GPU runtime.

axm %gpu.deinit: [%mem.M 0, %gpu.GlobalM, %gpu.ConstM] → %mem.M 0;

Streams

%gpu.stream_init

Initializes a stream.

axm %gpu.stream_init: [%mem.M 0, %gpu.GlobalM, %mem.Ptr0 %gpu.Stream] → [%mem.M 0, %gpu.GlobalM];

%gpu.stream_deinit

Deinitializes a stream.

axm %gpu.stream_deinit: [%mem.M 0, %gpu.GlobalM, %gpu.Stream] → [%mem.M 0, %gpu.GlobalM];

%gpu.stream_sync

Waits for a stream to finish all its work.

axm %gpu.stream_sync: [%mem.M 0, %gpu.GlobalM, %gpu.Stream] → [%mem.M 0, %gpu.GlobalM];

%gpu.with_streams

Provides n streams to func for executing asynchronous GPU operations, managing their initialization and teardown.

fun %gpu.with_streams (n: Nat, func: Fn [%mem.M 0, %gpu.GlobalM, «n; %gpu.Stream»] → [%mem.M 0, %gpu.GlobalM])
(m0: %mem.M 0, m1: %gpu.GlobalM)
: [%mem.M 0, %gpu.GlobalM] =
let (m0, streams_ptr) = %mem.alloc («n; %gpu.Stream», 0) m0;
init (m0, m1)
where
con init (m0: %mem.M 0, m1: %gpu.GlobalM) =
loop (m0, m1, 0)
where
con loop (m0: %mem.M 0, m1: %gpu.GlobalM, i: Nat) =
let cond = %core.ncmp.l (i, n);
(run, body)#cond (m0, m1)
where
con body (m0: %mem.M 0, m1: %gpu.GlobalM) =
let lea = %mem.lea (streams_ptr, %core.bitcast (Idx n) i);
let (m0, m1) = %gpu.stream_init (m0, m1, lea);
let inc = %core.nat.add (i, 1);
loop (m0, m1, inc);
end;
end;
con run (m0: %mem.M 0, m1: %gpu.GlobalM) =
let (m0, streams) = %mem.load (m0, streams_ptr);
ret (m0, m1) = func $ (m0, m1, streams);
deinit (m0, m1);
con deinit (m0: %mem.M 0, m1: %gpu.GlobalM) =
let (m0, streams) = %mem.load (m0, streams_ptr);
loop (m0, m1, 0)
where
con loop (m0: %mem.M 0, m1: %gpu.GlobalM, i: Nat) =
let cond = %core.ncmp.l (i, n);
(return, body)#cond (m0, m1)
where
con body (m0: %mem.M 0, m1: %gpu.GlobalM) =
let idx = %core.bitcast (Idx n) i;
let (m0, m1) = %gpu.stream_deinit (m0, m1, streams#idx);
let inc = %core.nat.add (i, 1);
loop (m0, m1, inc);
end;
end;
end;

Blocking Memory Operations

%gpu.alloc(block)

Allocates memory on the device (in global address space).

axm %gpu.alloc(block): [T: *] → %gpu.GlobalM → [%gpu.GlobalM, %gpu.GlobalPtr T];

%gpu.free(block)

Frees memory on the device (in global address space).

axm %gpu.free(block): {T: *} → [%gpu.GlobalM, %gpu.GlobalPtr T] → %gpu.GlobalM;

%gpu.copy_to_device(block)

Copies data from host memory to device memory.

axm %gpu.copy_to_device(block): {T: *}
→ [%mem.M 0, %gpu.GlobalM, %mem.Ptr0 T, %gpu.GlobalPtr T]
→ [%mem.M 0, %gpu.GlobalM];

%gpu.copy_to_host(block)

Copies data from device memory to host memory.

axm %gpu.copy_to_host(block): {T: *}
→ [%mem.M 0, %gpu.GlobalM, %gpu.GlobalPtr T, %mem.Ptr0 T]
→ [%mem.M 0, %gpu.GlobalM];

%gpu.alloc_copy(block)

Allocates memory on the device (in global address space) for the provided host data and copies the host data to that memory.

let %gpu.alloc_copy.block = lm {T: *}
(m0: %mem.M 0, m1: %gpu.GlobalM, ptr: %mem.Ptr0 T)
: [%mem.M 0, %gpu.GlobalM, %gpu.GlobalPtr T] =
let (m1, d_ptr) = %gpu.alloc.block T m1;
let (m0, m1) = %gpu.copy_to_device.block (m0, m1, ptr, d_ptr);
(m0, m1, d_ptr);

Asynchronous Memory Operations

%gpu.alloc(asyn)

Asynchronously allocates memory on the device (in global address space).

axm %gpu.alloc(asyn): [T: *] → [%gpu.GlobalM, %gpu.Stream] → [%gpu.GlobalM, %gpu.GlobalPtr T];

%gpu.free(asyn)

Asynchronously frees memory on the device (in global address space).

axm %gpu.free(asyn): {T: *} → [%gpu.GlobalM, %gpu.GlobalPtr T, %gpu.Stream] → %gpu.GlobalM;

%gpu.copy_to_device(asyn)

Asynchronously copies data from host memory to device memory.

axm %gpu.copy_to_device(asyn): {T: *}
→ [%mem.M 0, %gpu.GlobalM, %mem.Ptr0 T, %gpu.GlobalPtr T, %gpu.Stream]
→ [%mem.M 0, %gpu.GlobalM];

%gpu.copy_to_host(asyn)

Asynchronously copies data from device memory to host memory.

axm %gpu.copy_to_host(asyn): {T: *}
→ [%mem.M 0, %gpu.GlobalM, %gpu.GlobalPtr T, %mem.Ptr0 T, %gpu.Stream]
→ [%mem.M 0, %gpu.GlobalM];

%gpu.alloc_copy(asyn)

Asynchronously allocates memory on the device (in global address space) for the provided host data and copies the host data to that memory.

let %gpu.alloc_copy.asyn = lm {T: *}
(m0: %mem.M 0, m1: %gpu.GlobalM, ptr: %mem.Ptr0 T, stream: %gpu.Stream)
: [%mem.M 0, %gpu.GlobalM, %gpu.GlobalPtr T] =
let (m1, d_ptr) = %gpu.alloc.asyn T (m1, stream);
let (m0, m1) = %gpu.copy_to_device.asyn (m0, m1, ptr, d_ptr, stream);
(m0, m1, d_ptr);

Kernel Launch

%gpu.launch

Launches a kernel function on the device with dynamic shared memory and module symbols.

// TODO: refactor to {n m: N, Ts: «n; *»} as soon as type inference bug is fixed
axm %gpu.launch: {n: Nat, Ts: «n; *»}
→ [n_groups: Nat, n_items: Nat, %gpu.Stream, m: Bool, MT: «%core.bitcast Nat m; *»]
→ Cn [%gpu.GlobalM, %gpu.SharedM, %gpu.ConstM, %gpu.LocalM, Idx n_groups, Idx n_items,
«i: %core.bitcast Nat m; %gpu.SharedPtr MT#i», «i: n; Ts#i»,
Cn [%gpu.GlobalM, %gpu.SharedM, %gpu.ConstM, %gpu.LocalM]]
→ «i: n; Ts#i»
→ Fn [%mem.M 0, %gpu.GlobalM, %gpu.ConstM]
→ [%mem.M 0, %gpu.GlobalM, %gpu.ConstM];

Work-Item Synchronization

%gpu.sync_work_items

Low-level operation to synchronize all work-items in the same work-group.

axm %gpu.sync_work_items: [%gpu.GlobalM, %gpu.SharedM] → [%gpu.GlobalM, %gpu.SharedM];

%gpu.synced_scope

Runs scope bracketed by work-item synchronization: syncs, runs scope, and syncs again.

// TODO: consider variadic arguments and returns values for the scope
lam %gpu.synced_scope (m1: %gpu.GlobalM, m3: %gpu.SharedM,
scope: [%gpu.GlobalM, %gpu.SharedM] → [%gpu.GlobalM, %gpu.SharedM])
: [%gpu.GlobalM, %gpu.SharedM] =
let (m1, m3) = %gpu.sync_work_items (m1, m3);
let (m1, m3) = scope (m1, m3);
let (m1, m3) = %gpu.sync_work_items (m1, m3);
(m1, m3);

Phases

axm %gpu.check_addr_spaces_repl: %compile.Phase;
axm %gpu.host_malloc2gpualloc_repl: %compile.Phase;
axm %gpu.mem_checks: %compile.Phase;
axm %gpu.remove_double_syncs: %compile.Phase;
axm %gpu.split_apply: %compile.Phase;

Pipelines

lam %gpu.host_specific_phases () = %gpu.host_malloc2gpualloc_repl;
lam %gpu.device_specific_phases () = %gpu.remove_double_syncs;