sourceextensions/gpu/lib/Gpu.xtlm
1⍝# Gpu.xtlm: the accelerate macro: a function's text handed to the GPU runner
2⍝#
3⍝# Import it with the facade: "gp:" u_se< "Gpu" brings Gpu.xtl (the
4⍝# functions) and this file (the macro) under one alias. The program
5⍝# must import the facade as gp:, since the expansion calls gp:l_ower,
6⍝# gp:b_ind, gp:r_un and gp:o_utput.
7⍝#
8⍝# u:f_ := KINDS gp:a_ccel< "{ x w -> ... }" defines a function of the same
9⍝# parameters that hands the text to the gpu extension, which lowers it
10⍝# through X_eTaL's own crates for the arguments' shapes when they are
11⍝# bound (once per set of shapes), runs it on opencl:0 with the
12⍝# arguments uploaded, and reads the result back. KINDS says how each
13⍝# argument is taken, "float" or "int" per parameter ("" for all
14⍝# Floats); an Int array must be bound as int to be looked up by. The
15⍝# function is one or two named arrays to one array, written on one
16⍝# line without quotes; its libraries are the ones the calling file
17⍝# imports (the expansion names the file, so the runner reads its u_se<
18⍝# lines). The same macro, with a runner that has load-from-source,
19⍝# bind and run, would accelerate on anything.
20
21⍝## Private helpers (text)
22
23⍝# The words of a text (split on spaces).
24ʰw̲ords ← { s → (n̲ot s = f̲irst " ") p̲artition s }
25ʰw̲ord ← { i w → d̲isclose i s̲elect w }
26
27⍝# The text as a string literal (no quotes or backslashes inside).
28ʰq̲uote ← { t → "\"" c̲at t c̲at "\"" }
29
30⍝# The second parameter's name, or "" when the function takes one.
31ʰs̲econd ← { w →
32 (3 ʰw̲ord w) m̲atch "->" ? ""
33 3 ʰw̲ord w
34}
35
36⍝# The parameter list and the bind calls, for one or two parameters
37⍝# (read from the right: the last bind happens first).
38ʰh̲ead ← { a b →
39 b m̲atch "" ? a
40 a c̲at " " c̲at b
41}
42ʰb̲inds ← { a b →
43 one ← "1 gp:b_ind " c̲at a c̲at " gp:a_fter "
44 b m̲atch "" ? one
45 "2 gp:b_ind " c̲at b c̲at " gp:a_fter " c̲at one
46}
47
48⍝## The macro
49
50⍝# KINDS gp:a_ccel< "{ x w -> body }": see the file's header. A
51⍝# function that calls a library's functions (ac:d_ense) is written in
52⍝# a file that imports that library; here, a product of primitives.
53⍝# >> "gp:" u_se< "Gpu"
54⍝# >> u:s_cores := "float float" gp:a_ccel< "{ q k -> q '+ '* i_nner 2 1 t_ranspose k }"
55⍝# >> q := 2 3 r_eshape 1.0 0.5 -1.0 0.0 2.0 0.25
56⍝# >> k := 4 3 r_eshape 0.5 1.0 0.0 -1.0 0.5 2.0 1.5 -0.5 1.0 0.0 0.0 1.0
57⍝# >> (q '+ '* i_nner 2 1 t_ranspose k) gp:n_ear q u:s_cores k
58⍝# ok
59ᵐa̲ccel< ← { kinds f →
60 (f̲irst "\"") m̲ember? f ? "bad-macro-argument right" ⎕R̲EJECT "the function's text holds a quote; write it without strings"
61 (f̲irst "\n") m̲ember? f ? "bad-macro-argument right" ⎕R̲EJECT "the function's text spans lines; write it on one"
62 w ← ʰw̲ords f
63 n̲ot (1 ʰw̲ord w) m̲atch "{" ? "bad-macro-argument right" ⎕R̲EJECT "the function must be written { x -> ... } or { x w -> ... }"
64 p1 ← 2 ʰw̲ord w
65 p2 ← ʰs̲econd w
66 spec ← (⎕F̲ILE @) c̲at "|" c̲at kinds
67 "{ " c̲at (p1 ʰh̲ead p2) c̲at " -> gp:o_utput 1 gp:a_fter gp:r_un 0 gp:a_fter " c̲at (p1 ʰb̲inds p2) c̲at (ʰq̲uote f) c̲at " gp:l_ower " c̲at (ʰq̲uote spec) c̲at " }"
68}