1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//! This is not the main documentation for Emu. Go to [docs.rs/em](https://docs.rs/em)
//! for the main documentation of Emu.
// for generating Rust
extern crate quote;
// for procedural macros
extern crate proc_macro;
use TokenStream;
use Span;
// for parsing Rust
extern crate syn;
use Fold;
use *;
// THE TABLE OF CONTENTS
//
// these modules are the main modules Emu uses
// for looking through code for gpu_do!() and using the GPU appropriately
// for passing around a reference to the GPU from function to function
// these modules are more linke utilities for Emu
// for generating OpenCL from Rust
// for identifying a for loop as potentially something we can work with
// for inspecting a function for more info
use *;
use *;
use *;
// error represents an error in compilation that makes it more confusing to user to proceed
// if e is an error, we just stop the proc macro execution and just return what was already there + errors
//
// we append errors because it's a bit of a hack to make them show up as compile-time errors to macro users
/// A procedural macro for using the GPU to store data and accelerate parts of code in the tagged function.
///
/// While this is technically what looks through your code for `gpu_do!()`
/// declarations and expands them in ways that potentially accelerate your
/// code, you should think about this more as the "passing" part of Emu rather
/// than the "acceleration" part.
///
/// Well, what do I mean by passing? I've talked in other parts of the docs
/// about the "GPU being in scope" or "passing the GPU around". I literally
/// mean an identifier `gpu` that references an instance of `Gpu` and holds
/// all information about the physical GPU that Emu needs to know. Why do we
/// need to "pass" this `Gpu` instance from function to function? Well, we
/// would like only 1 instance of `Gpu` to exist throughout an entire
/// application. Even if you are using other libaries, those libraries should
/// be using this 1 instance of `Gpu`. The way we accomplish this is by
/// modifying functions (that use the GPU) to accept as input, return as
/// output, pass around the `Gpu` instance. That "modification" of functions
/// is done by this macro.
///
/// So, how do I do passing? If you correctly tag your function with
/// `#[gpu_use]`, it's function signature will be modified and its contents
/// will be modified so as to allow `gpu`, a `Gpu` instance, to be used in
/// the function body. To correctly tag your function with `#[gpu_use]`, you
/// must list the "helper functions" of the tagged function.
/// ```
/// # extern crate em;
/// # use em::*;
/// # #[gpu_use(multiply)]
/// # fn multiply(data: Vec<f32>, scalar: f32) -> Vec<f32> {data}
/// # #[gpu_use(add_one_then_double)]
/// # fn add_one_then_double(data: Vec<f32>) -> Vec<f32> {data}
/// #[gpu_use(multiply, add_one_then_double)]
/// fn main() {
/// let mut data = vec![0.1; 1000];
///
/// gpu_do!(load(data));
/// data = multiply(data, 10.0);
/// data = add_one_then_double(data);
/// gpu_do!(read(data));
///
/// println!("{:?}", data);
/// }
/// ```
/// In this above example, the helper functions of the tagged function are `multiply` and `add_one_then_double`. What would those helper functions look like themselves?
/// ```
/// # extern crate em;
/// # use em::*;
/// #[gpu_use(multiply)]
/// fn multiply(mut data: Vec<f32>, scalar: f32) -> Vec<f32> {
/// gpu_do!(launch());
/// for i in 0..1000 {
/// data[i] = data[i] * scalar;
/// }
///
/// data
/// }
///
/// #[gpu_use(add_one_then_double, multiply)]
/// fn add_one_then_double(mut data: Vec<f32>) -> Vec<f32> {
/// gpu_do!(launch());
/// for i in 0..1000 {
/// data[i] = data[i] + 1.0;
/// }
/// data = multiply(data, 2.0);
///
/// data
/// }
///
/// #[gpu_use(multiply, add_one_then_double)]
/// fn main() {
/// let mut data = vec![0.1; 1000];
///
/// gpu_do!(load(data));
/// data = multiply(data, 10.0);
/// data = add_one_then_double(data);
/// gpu_do!(read(data));
/// }
/// ```
/// There is, admittedly, a lot going on here. But there is just one thing I want you to focus
/// on - the `#[gpu_use()]` invocations/tags. Focus on that. See how different
/// functions have different helper functions. First look at the main function and its helper functions and then look at the other two functions. You will see that the helper functions of a
/// function are each only one of 2 things.
/// 1. A function called from inside the function body that uses the GPU (is tagged with `#[gpu_use]`)
/// 2. The function itself, if it can be a helper function to another function
///
/// Looking at the above example you should be able to justify each helper
/// function listed for each function, using the above 2 cases. Note that the `main` function doesn't list itself as a helper function and that is because
/// it doesn't need the GPU passed to it ever.