1 /*
2 * Copyright 2019 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkM44.h"
9 #include "src/sksl/SkSLCompiler.h"
10 #include "src/sksl/codegen/SkSLVMCodeGenerator.h"
11 #include "src/sksl/ir/SkSLExternalFunction.h"
12 #include "src/utils/SkJSON.h"
13
14 #include "tests/Test.h"
15
16 struct ProgramBuilder {
ProgramBuilderProgramBuilder17 ProgramBuilder(skiatest::Reporter* r, const char* src)
18 : fCompiler(&fCaps) {
19 SkSL::Program::Settings settings;
20 // The SkSL inliner is well tested in other contexts. Here, we disable inlining entirely,
21 // to stress-test the VM generator's handling of function calls with varying signatures.
22 settings.fInlineThreshold = 0;
23 // For convenience, so we can test functions other than (and not called by) main.
24 settings.fRemoveDeadFunctions = false;
25
26 fProgram = fCompiler.convertProgram(SkSL::ProgramKind::kGeneric, SkSL::String(src),
27 settings);
28 if (!fProgram) {
29 ERRORF(r, "Program failed to compile:\n%s\n%s\n", src, fCompiler.errorText().c_str());
30 }
31 }
32
operator boolProgramBuilder33 operator bool() const { return fProgram != nullptr; }
operator *ProgramBuilder34 SkSL::Program& operator*() { return *fProgram; }
35
36 GrShaderCaps fCaps;
37 SkSL::Compiler fCompiler;
38 std::unique_ptr<SkSL::Program> fProgram;
39 };
40
verify_values(skiatest::Reporter * r,const char * src,const float * actual,const float * expected,int N,bool exactCompare)41 static void verify_values(skiatest::Reporter* r,
42 const char* src,
43 const float* actual,
44 const float* expected,
45 int N,
46 bool exactCompare) {
47 auto exact_equiv = [](float x, float y) {
48 return x == y
49 || (isnan(x) && isnan(y));
50 };
51
52 bool valid = true;
53 for (int i = 0; i < N; ++i) {
54 if (exactCompare && !exact_equiv(actual[i], expected[i])) {
55 valid = false;
56 }
57 if (!exactCompare && !SkScalarNearlyEqual(actual[i], expected[i])) {
58 valid = false;
59 }
60 }
61
62 if (!valid) {
63 printf("for program: %s\n", src);
64 printf(" expected (");
65 const char* separator = "";
66 for (int i = 0; i < N; ++i) {
67 printf("%s%f", separator, expected[i]);
68 separator = ", ";
69 }
70 printf("), but received (");
71 separator = "";
72 for (int i = 0; i < N; ++i) {
73 printf("%s%f", separator, actual[i]);
74 separator = ", ";
75 }
76 printf(")\n");
77 }
78 REPORTER_ASSERT(r, valid);
79 }
80
test(skiatest::Reporter * r,const char * src,float * in,const float * expected,bool exactCompare=true)81 void test(skiatest::Reporter* r, const char* src, float* in, const float* expected,
82 bool exactCompare = true) {
83 ProgramBuilder program(r, src);
84 if (!program) { return; }
85
86 const SkSL::FunctionDefinition* main = SkSL::Program_GetFunction(*program, "main");
87 REPORTER_ASSERT(r, main);
88
89 skvm::Builder b;
90 SkSL::SkVMSignature sig;
91 SkSL::ProgramToSkVM(*program, *main, &b, /*debugInfo=*/nullptr, /*uniforms=*/{}, &sig);
92 skvm::Program p = b.done();
93
94 REPORTER_ASSERT(r, p.nargs() == (int)(sig.fParameterSlots + sig.fReturnSlots));
95
96 auto out = std::make_unique<float[]>(sig.fReturnSlots);
97 auto args = std::make_unique<void*[]>(sig.fParameterSlots + sig.fReturnSlots);
98 for (size_t i = 0; i < sig.fParameterSlots; ++i) {
99 args[i] = in + i;
100 }
101 for (size_t i = 0; i < sig.fReturnSlots; ++i) {
102 args[sig.fParameterSlots + i] = out.get() + i;
103 }
104
105 // TODO: Test with and without JIT?
106 p.eval(1, args.get());
107
108 verify_values(r, src, out.get(), expected, sig.fReturnSlots, exactCompare);
109 }
110
test(skiatest::Reporter * r,const char * src,float inR,float inG,float inB,float inA,float exR,float exG,float exB,float exA)111 void test(skiatest::Reporter* r, const char* src,
112 float inR, float inG, float inB, float inA,
113 float exR, float exG, float exB, float exA) {
114 ProgramBuilder program(r, src);
115 if (!program) { return; }
116
117 const SkSL::FunctionDefinition* main = SkSL::Program_GetFunction(*program, "main");
118 REPORTER_ASSERT(r, main);
119
120 skvm::Builder b;
121 SkSL::ProgramToSkVM(*program, *main, &b, /*debugInfo=*/nullptr, /*uniforms=*/{});
122 skvm::Program p = b.done();
123
124 // TODO: Test with and without JIT?
125 p.eval(1, &inR, &inG, &inB, &inA);
126
127 float actual[4] = { inR, inG, inB, inA };
128 float expected[4] = { exR, exG, exB, exA };
129
130 verify_values(r, src, actual, expected, 4, /*exactCompare=*/true);
131
132 // TODO: vec_test with skvm
133 }
134
DEF_TEST(SkSLInterpreterAdd,r)135 DEF_TEST(SkSLInterpreterAdd, r) {
136 test(r, "void main(inout half4 color) { color.r = color.r + color.g; }", 0.25, 0.75, 0, 0, 1,
137 0.75, 0, 0);
138 test(r, "void main(inout half4 color) { color += half4(1, 2, 3, 4); }", 4, 3, 2, 1, 5, 5, 5, 5);
139 test(r, "void main(inout half4 color) { half4 c = color; color += c; }", 0.25, 0.5, 0.75, 1,
140 0.5, 1, 1.5, 2);
141 test(r, "void main(inout half4 color) { color.r = half(int(color.r) + int(color.g)); }", 1, 3, 0, 0,
142 4, 3, 0, 0);
143 }
144
DEF_TEST(SkSLInterpreterSubtract,r)145 DEF_TEST(SkSLInterpreterSubtract, r) {
146 test(r, "void main(inout half4 color) { color.r = color.r - color.g; }", 1, 0.75, 0, 0, 0.25,
147 0.75, 0, 0);
148 test(r, "void main(inout half4 color) { color -= half4(1, 2, 3, 4); }", 5, 5, 5, 5, 4, 3, 2, 1);
149 test(r, "void main(inout half4 color) { half4 c = color; color -= c; }", 4, 3, 2, 1,
150 0, 0, 0, 0);
151 test(r, "void main(inout half4 color) { color.x = -color.x; }", 4, 3, 2, 1, -4, 3, 2, 1);
152 test(r, "void main(inout half4 color) { color = -color; }", 4, 3, 2, 1, -4, -3, -2, -1);
153 test(r, "void main(inout half4 color) { color.r = half(int(color.r) - int(color.g)); }", 3, 1, 0, 0,
154 2, 1, 0, 0);
155 }
156
DEF_TEST(SkSLInterpreterMultiply,r)157 DEF_TEST(SkSLInterpreterMultiply, r) {
158 test(r, "void main(inout half4 color) { color.r = color.r * color.g; }", 2, 3, 0, 0, 6, 3, 0,
159 0);
160 test(r, "void main(inout half4 color) { color *= half4(1, 2, 3, 4); }", 2, 3, 4, 5, 2, 6, 12,
161 20);
162 test(r, "void main(inout half4 color) { half4 c = color; color *= c; }", 4, 3, 2, 1,
163 16, 9, 4, 1);
164 test(r, "void main(inout half4 color) { color.r = half(int(color.r) * int(color.g)); }", 3, -2, 0, 0,
165 -6, -2, 0, 0);
166 }
167
DEF_TEST(SkSLInterpreterDivide,r)168 DEF_TEST(SkSLInterpreterDivide, r) {
169 test(r, "void main(inout half4 color) { color.r = color.r / color.g; }", 1, 2, 0, 0, 0.5, 2, 0,
170 0);
171 test(r, "void main(inout half4 color) { color /= half4(1, 2, 3, 4); }", 12, 12, 12, 12, 12, 6,
172 4, 3);
173 test(r, "void main(inout half4 color) { half4 c = color; color /= c; }", 4, 3, 2, 1,
174 1, 1, 1, 1);
175 test(r, "void main(inout half4 color) { color.r = half(int(color.r) / int(color.g)); }", 8, -2, 0, 0,
176 -4, -2, 0, 0);
177 }
178
DEF_TEST(SkSLInterpreterAnd,r)179 DEF_TEST(SkSLInterpreterAnd, r) {
180 test(r, "void main(inout half4 color) { if (color.r > color.g && color.g > color.b) "
181 "color = half4(color.a); }", 2, 1, 0, 3, 3, 3, 3, 3);
182 test(r, "void main(inout half4 color) { if (color.r > color.g && color.g > color.b) "
183 "color = half4(color.a); }", 1, 1, 0, 3, 1, 1, 0, 3);
184 test(r, "void main(inout half4 color) { if (color.r > color.g && color.g > color.b) "
185 "color = half4(color.a); }", 2, 1, 1, 3, 2, 1, 1, 3);
186 test(r, "half global; bool update() { global = 123; return true; }"
187 "void main(inout half4 color) { global = 0; if (color.r > color.g && update()) "
188 "color = half4(color.a); color.a = global; }", 2, 1, 1, 3, 3, 3, 3, 123);
189 test(r, "half global; bool update() { global = 123; return true; }"
190 "void main(inout half4 color) { global = 0; if (color.r > color.g && update()) "
191 "color = half4(color.a); color.a = global; }", 1, 1, 1, 3, 1, 1, 1, 0);
192 }
193
DEF_TEST(SkSLInterpreterOr,r)194 DEF_TEST(SkSLInterpreterOr, r) {
195 test(r, "void main(inout half4 color) { if (color.r > color.g || color.g > color.b) "
196 "color = half4(color.a); }", 2, 1, 0, 3, 3, 3, 3, 3);
197 test(r, "void main(inout half4 color) { if (color.r > color.g || color.g > color.b) "
198 "color = half4(color.a); }", 1, 1, 0, 3, 3, 3, 3, 3);
199 test(r, "void main(inout half4 color) { if (color.r > color.g || color.g > color.b) "
200 "color = half4(color.a); }", 1, 1, 1, 3, 1, 1, 1, 3);
201 test(r, "half global; bool update() { global = 123; return true; }"
202 "void main(inout half4 color) { global = 0; if (color.r > color.g || update()) "
203 "color = half4(color.a); color.a = global; }", 1, 1, 1, 3, 3, 3, 3, 123);
204 test(r, "half global; bool update() { global = 123; return true; }"
205 "void main(inout half4 color) { global = 0; if (color.r > color.g || update()) "
206 "color = half4(color.a); color.a = global; }", 2, 1, 1, 3, 3, 3, 3, 0);
207 }
208
DEF_TEST(SkSLInterpreterMatrix,r)209 DEF_TEST(SkSLInterpreterMatrix, r) {
210 float in[16];
211 float expected[16];
212
213 // Constructing matrix from scalar produces a diagonal matrix
214 in[0] = 2.0f;
215 expected[0] = 4.0f;
216 test(r, "float main(float x) { float4x4 m = float4x4(x); return m[1][1] + m[1][2] + m[2][2]; }",
217 in, expected);
218
219 // Constructing from a different-sized matrix fills the remaining space with the identity matrix
220 expected[0] = 3.0f;
221 test(r, "float main(float x) {"
222 "float2x2 m = float2x2(x);"
223 "float4x4 m2 = float4x4(m);"
224 "return m2[0][0] + m2[3][3]; }",
225 in, expected);
226
227 // Constructing a matrix from vectors or scalars fills in values in column-major order
228 in[0] = 1.0f;
229 in[1] = 2.0f;
230 in[2] = 4.0f;
231 in[3] = 8.0f;
232 expected[0] = 6.0f;
233 test(r, "float main(float4 v) { float2x2 m = float2x2(v); return m[0][1] + m[1][0]; }",
234 in, expected);
235
236 expected[0] = 10.0f;
237 test(r, "float main(float4 v) {"
238 "float2x2 m = float2x2(v.x, v.y, v.w, v.z);"
239 "return m[0][1] + m[1][0]; }",
240 in, expected);
241
242 // Initialize 16 values to be used as inputs to matrix tests
243 for (int i = 0; i < 16; ++i) { in[i] = (float)i; }
244
245 // M+M, M-S, S-M
246 for (int i = 0; i < 16; ++i) { expected[i] = (float)(2 * i); }
247 test(r, "float4x4 main(float4x4 m) { return m + m; }", in, expected);
248 for (int i = 0; i < 16; ++i) { expected[i] = (float)(i + 3); }
249 test(r, "float4x4 main(float4x4 m) { return m + 3.0; }", in, expected);
250 test(r, "float4x4 main(float4x4 m) { return 3.0 + m; }", in, expected);
251
252 // M-M, M-S, S-M
253 for (int i = 0; i < 4; ++i) { expected[i] = 4.0f; }
254 test(r, "float2x2 main(float2x2 m1, float2x2 m2) { return m2 - m1; }", in, expected);
255 for (int i = 0; i < 16; ++i) { expected[i] = (float)(i - 3); }
256 test(r, "float4x4 main(float4x4 m) { return m - 3.0; }", in, expected);
257 for (int i = 0; i < 16; ++i) { expected[i] = (float)(3 - i); }
258 test(r, "float4x4 main(float4x4 m) { return 3.0 - m; }", in, expected);
259
260 // M*S, S*M, M/S, S/M
261 for (int i = 0; i < 16; ++i) { expected[i] = (float)(i * 3); }
262 test(r, "float4x4 main(float4x4 m) { return m * 3.0; }", in, expected);
263 test(r, "float4x4 main(float4x4 m) { return 3.0 * m; }", in, expected);
264 for (int i = 0; i < 16; ++i) { expected[i] = (float)(i) / 2.0f; }
265 test(r, "float4x4 main(float4x4 m) { return m / 2.0; }", in, expected);
266 for (int i = 0; i < 16; ++i) { expected[i] = 1.0f / (float)(i + 1); }
267 test(r, "float4x4 main(float4x4 m) { return 1.0 / (m + 1); }", in, expected);
268
269 // Matrix negation
270 for (int i = 0; i < 16; ++i) { expected[i] = (float)(-i); }
271 test(r, "float4x4 main(float4x4 m) { return -m; }", in, expected);
272
273 // M*V, V*M
274 for (int i = 0; i < 3; ++i) {
275 expected[i] = 9.0f*i + 10.0f*(i+3) + 11.0f*(i+6);
276 }
277 test(r, "float3 main(float3x3 m, float3 v) { return m * v; }", in, expected);
278 for (int i = 0; i < 3; ++i) {
279 expected[i] = 9.0f*(3*i) + 10.0f*(3*i+1) + 11.0f*(3*i+2);
280 }
281 test(r, "float3 main(float3x3 m, float3 v) { return v * m; }", in, expected);
282
283 // M*M
284 {
285 SkM44 m = SkM44::ColMajor(in);
286 SkM44 m2;
287 float in2[16];
288 for (int i = 0; i < 16; ++i) {
289 in2[i] = (i + 4) % 16;
290 }
291 m2 = SkM44::ColMajor(in2);
292 m.setConcat(m, m2);
293 // Rearrange the columns on the RHS so we detect left-hand/right-hand errors
294 test(r, "float4x4 main(float4x4 m) { return m * float4x4(m[1], m[2], m[3], m[0]); }",
295 in, (float*)&m);
296 }
297 }
298
DEF_TEST(SkSLInterpreterTernary,r)299 DEF_TEST(SkSLInterpreterTernary, r) {
300 test(r, "void main(inout half4 color) { color.r = color.g > color.b ? color.g : color.b; }",
301 0, 1, 2, 0, 2, 1, 2, 0);
302 test(r, "void main(inout half4 color) { color.r = color.g > color.b ? color.g : color.b; }",
303 0, 3, 2, 0, 3, 3, 2, 0);
304 }
305
DEF_TEST(SkSLInterpreterCast,r)306 DEF_TEST(SkSLInterpreterCast, r) {
307 union Val {
308 float f;
309 int32_t s;
310 };
311
312 Val input[2];
313 Val expected[2];
314
315 input[0].s = 3;
316 input[1].s = -5;
317 expected[0].f = 3.0f;
318 expected[1].f = -5.0f;
319 test(r, "float main(int x) { return float (x); }", (float*)input, (float*)expected);
320 test(r, "float2 main(int2 x) { return float2(x); }", (float*)input, (float*)expected);
321
322 input[0].f = 3.0f;
323 input[1].f = -5.0f;
324 expected[0].s = 3;
325 expected[1].s = -5;
326 test(r, "int main(float x) { return int (x); }", (float*)input, (float*)expected);
327 test(r, "int2 main(float2 x) { return int2(x); }", (float*)input, (float*)expected);
328
329 input[0].s = 3;
330 expected[0].f = 3.0f;
331 expected[1].f = 3.0f;
332 test(r, "float2 main(int x) { return float2(x); }", (float*)input, (float*)expected);
333 }
334
DEF_TEST(SkSLInterpreterIf,r)335 DEF_TEST(SkSLInterpreterIf, r) {
336 test(r, "void main(inout half4 color) { if (color.r > color.g) color.a = 1; }", 5, 3, 0, 0,
337 5, 3, 0, 1);
338 test(r, "void main(inout half4 color) { if (color.r > color.g) color.a = 1; }", 5, 5, 0, 0,
339 5, 5, 0, 0);
340 test(r, "void main(inout half4 color) { if (color.r > color.g) color.a = 1; }", 5, 6, 0, 0,
341 5, 6, 0, 0);
342 test(r, "void main(inout half4 color) { if (color.r < color.g) color.a = 1; }", 3, 5, 0, 0,
343 3, 5, 0, 1);
344 test(r, "void main(inout half4 color) { if (color.r < color.g) color.a = 1; }", 5, 5, 0, 0,
345 5, 5, 0, 0);
346 test(r, "void main(inout half4 color) { if (color.r < color.g) color.a = 1; }", 6, 5, 0, 0,
347 6, 5, 0, 0);
348 test(r, "void main(inout half4 color) { if (color.r >= color.g) color.a = 1; }", 5, 3, 0, 0,
349 5, 3, 0, 1);
350 test(r, "void main(inout half4 color) { if (color.r >= color.g) color.a = 1; }", 5, 5, 0, 0,
351 5, 5, 0, 1);
352 test(r, "void main(inout half4 color) { if (color.r >= color.g) color.a = 1; }", 5, 6, 0, 0,
353 5, 6, 0, 0);
354 test(r, "void main(inout half4 color) { if (color.r <= color.g) color.a = 1; }", 3, 5, 0, 0,
355 3, 5, 0, 1);
356 test(r, "void main(inout half4 color) { if (color.r <= color.g) color.a = 1; }", 5, 5, 0, 0,
357 5, 5, 0, 1);
358 test(r, "void main(inout half4 color) { if (color.r <= color.g) color.a = 1; }", 6, 5, 0, 0,
359 6, 5, 0, 0);
360 test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; }", 2, 2, 0, 0,
361 2, 2, 0, 1);
362 test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; }", 2, -2, 0, 0,
363 2, -2, 0, 0);
364 test(r, "void main(inout half4 color) { if (color.r != color.g) color.a = 1; }", 2, 2, 0, 0,
365 2, 2, 0, 0);
366 test(r, "void main(inout half4 color) { if (color.r != color.g) color.a = 1; }", 2, -2, 0, 0,
367 2, -2, 0, 1);
368 test(r, "void main(inout half4 color) { if (!(color.r == color.g)) color.a = 1; }", 2, 2, 0, 0,
369 2, 2, 0, 0);
370 test(r, "void main(inout half4 color) { if (!(color.r == color.g)) color.a = 1; }", 2, -2, 0, 0,
371 2, -2, 0, 1);
372 test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; else "
373 "color.a = 2; }", 1, 1, 0, 0, 1, 1, 0, 1);
374 test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; else "
375 "color.a = 2; }", 2, -2, 0, 0, 2, -2, 0, 2);
376 }
377
DEF_TEST(SkSLInterpreterIfVector,r)378 DEF_TEST(SkSLInterpreterIfVector, r) {
379 test(r, "void main(inout half4 color) { if (color.rg == color.ba) color.a = 1; }",
380 1, 2, 1, 2, 1, 2, 1, 1);
381 test(r, "void main(inout half4 color) { if (color.rg == color.ba) color.a = 1; }",
382 1, 2, 3, 2, 1, 2, 3, 2);
383 test(r, "void main(inout half4 color) { if (color.rg != color.ba) color.a = 1; }",
384 1, 2, 1, 2, 1, 2, 1, 2);
385 test(r, "void main(inout half4 color) { if (color.rg != color.ba) color.a = 1; }",
386 1, 2, 3, 2, 1, 2, 3, 1);
387 }
388
DEF_TEST(SkSLInterpreterFor,r)389 DEF_TEST(SkSLInterpreterFor, r) {
390 test(r, "void main(inout half4 color) { for (int i = 1; i <= 10; ++i) color.r += half(i); }",
391 0, 0, 0, 0,
392 55, 0, 0, 0);
393 test(r,
394 "void main(inout half4 color) {"
395 " for (int i = 1; i <= 10; ++i)"
396 " for (int j = 1; j <= 10; ++j)"
397 " if (j >= i) { color.r += half(j); }"
398 "}",
399 0, 0, 0, 0,
400 385, 0, 0, 0);
401 test(r,
402 "void main(inout half4 color) {"
403 " for (int i = 1; i <= 10; ++i)"
404 " for (int j = 1; j < 20 ; ++j) {"
405 " if (i == j) continue;"
406 " if (j > 10) break;"
407 " color.r += half(j);"
408 " }"
409 "}",
410 0, 0, 0, 0,
411 495, 0, 0, 0);
412 }
413
DEF_TEST(SkSLInterpreterPrefixPostfix,r)414 DEF_TEST(SkSLInterpreterPrefixPostfix, r) {
415 test(r, "void main(inout half4 color) { color.r = ++color.g; }", 1, 2, 3, 4, 3, 3, 3, 4);
416 test(r, "void main(inout half4 color) { color.r = color.g++; }", 1, 2, 3, 4, 2, 3, 3, 4);
417 }
418
DEF_TEST(SkSLInterpreterSwizzle,r)419 DEF_TEST(SkSLInterpreterSwizzle, r) {
420 test(r, "void main(inout half4 color) { color = color.abgr; }", 1, 2, 3, 4, 4, 3, 2, 1);
421 test(r, "void main(inout half4 color) { color.rgb = half4(5, 6, 7, 8).bbg; }", 1, 2, 3, 4, 7, 7,
422 6, 4);
423 test(r, "void main(inout half4 color) { color.bgr = half3(5, 6, 7); }", 1, 2, 3, 4, 7, 6,
424 5, 4);
425 }
426
DEF_TEST(SkSLInterpreterGlobal,r)427 DEF_TEST(SkSLInterpreterGlobal, r) {
428 test(r, "int x; void main(inout half4 color) { x = 10; color.b = half(x); }", 1, 2, 3, 4, 1, 2,
429 10, 4);
430 test(r, "float4 x; void main(inout float4 color) { x = color * 2; color = x; }",
431 1, 2, 3, 4, 2, 4, 6, 8);
432 test(r, "float4 x; void main(inout float4 color) { x = float4(5, 6, 7, 8); color = x.wzyx; }",
433 1, 2, 3, 4, 8, 7, 6, 5);
434 test(r, "float4 x; void main(inout float4 color) { x.wzyx = float4(5, 6, 7, 8); color = x; }",
435 1, 2, 3, 4, 8, 7, 6, 5);
436 }
437
DEF_TEST(SkSLInterpreterGeneric,r)438 DEF_TEST(SkSLInterpreterGeneric, r) {
439 float value1 = 5;
440 float expected1 = 25;
441 test(r, "float main(float x) { return x * x; }", &value1, &expected1);
442 float value2[2] = { 5, 25 };
443 float expected2[2] = { 25, 625 };
444 test(r, "float2 main(float x, float y) { return float2(x * x, y * y); }", value2, expected2);
445 }
446
DEF_TEST(SkSLInterpreterFieldAccessComplex,r)447 DEF_TEST(SkSLInterpreterFieldAccessComplex, r) {
448 const char* src = R"(
449 struct P { float x; float y; };
450 P make_point() { P p; p.x = 7; p.y = 3; return p; }
451 float main() { return make_point().y; }
452 )";
453
454 float expected = 3.0f;
455 test(r, src, /*in=*/nullptr, &expected);
456 }
457
DEF_TEST(SkSLInterpreterIndexComplex,r)458 DEF_TEST(SkSLInterpreterIndexComplex, r) {
459 const char* src = R"(
460 float2x2 make_mtx() { return float2x2(1, 2, 3, 4); }
461 float main() { return make_mtx()[1][0]; }
462 )";
463
464 float expected = 3.0f;
465 test(r, src, /*in=*/nullptr, &expected);
466 }
467
DEF_TEST(SkSLInterpreterCompound,r)468 DEF_TEST(SkSLInterpreterCompound, r) {
469 struct RectAndColor { SkIRect fRect; SkColor4f fColor; };
470 struct ManyRects { int fNumRects; RectAndColor fRects[4]; };
471
472 const char* src =
473 // Some struct definitions
474 "struct Point { int x; int y; };\n"
475 "struct Rect { Point p0; Point p1; };\n"
476 "struct RectAndColor { Rect r; float4 color; };\n"
477
478 // Structs as globals, parameters, return values
479 "RectAndColor temp;\n"
480 "int rect_height(Rect r) { return r.p1.y - r.p0.y; }\n"
481 "RectAndColor make_blue_rect(int w, int h) {\n"
482 " temp.r.p0.x = temp.r.p0.y = 0;\n"
483 " temp.r.p1.x = w; temp.r.p1.y = h;\n"
484 " temp.color = float4(0, 1, 0, 1);\n"
485 " return temp;\n"
486 "}\n"
487
488 // Initialization and assignment of types larger than 4 slots
489 "RectAndColor init_big(RectAndColor r) { RectAndColor s = r; return s; }\n"
490 "RectAndColor copy_big(RectAndColor r) { RectAndColor s; s = r; return s; }\n"
491
492 // Same for arrays, including some non-constant indexing
493 "int median(int a[15]) { return a[7]; }\n"
494
495 "float tempFloats[8];\n"
496 "float sums(float a[8]) {\n"
497 " tempFloats[0] = a[0];\n"
498 " for (int i = 1; i < 8; ++i) { tempFloats[i] = tempFloats[i - 1] + a[i]; }\n"
499 " return tempFloats[7];\n"
500 "}\n"
501
502 // Uniforms, array-of-structs
503 "uniform Rect gRects[4];\n"
504 "Rect get_rect_2() { return gRects[2]; }\n"
505
506 // Kitchen sink (swizzles, inout, SoAoS)
507 "struct ManyRects { int numRects; RectAndColor rects[4]; };\n"
508 "void fill_rects(inout ManyRects mr) {\n"
509 " for (int i = 0; i < 4; ++i) {\n"
510 " if (i >= mr.numRects) { break; }\n"
511 " mr.rects[i].r = gRects[i];\n"
512 " float b = float(mr.rects[i].r.p1.y);\n"
513 " mr.rects[i].color = float4(b, b, b, b);\n"
514 " }\n"
515 "}\n";
516
517 ProgramBuilder program(r, src);
518
519 auto rect_height = SkSL::Program_GetFunction(*program, "rect_height"),
520 make_blue_rect = SkSL::Program_GetFunction(*program, "make_blue_rect"),
521 median = SkSL::Program_GetFunction(*program, "median"),
522 sums = SkSL::Program_GetFunction(*program, "sums"),
523 get_rect_2 = SkSL::Program_GetFunction(*program, "get_rect_2"),
524 fill_rects = SkSL::Program_GetFunction(*program, "fill_rects");
525
526 SkIRect gRects[4] = { { 1,2,3,4 }, { 5,6,7,8 }, { 9,10,11,12 }, { 13,14,15,16 } };
527
528 auto build = [&](const SkSL::FunctionDefinition* fn) {
529 skvm::Builder b;
530 skvm::UPtr uniformPtr = b.uniform();
531 skvm::Val uniforms[16];
532 for (int i = 0; i < 16; ++i) {
533 uniforms[i] = b.uniform32(uniformPtr, i * sizeof(int)).id;
534 }
535 SkSL::ProgramToSkVM(*program, *fn, &b, /*debugInfo=*/nullptr, SkMakeSpan(uniforms));
536 return b.done();
537 };
538
539 struct Args {
540 Args(void* uniformData) { fArgs.push_back(uniformData); }
541 void add(void* base, int n) {
542 for (int i = 0; i < n; ++i) {
543 fArgs.push_back(SkTAddOffset<void>(base, i * sizeof(float)));
544 }
545 }
546 std::vector<void*> fArgs;
547 };
548
549 {
550 SkIRect in = SkIRect::MakeXYWH(10, 10, 20, 30);
551 int out = 0;
552 skvm::Program p = build(rect_height);
553 Args args(gRects);
554 args.add(&in, 4);
555 args.add(&out, 1);
556 p.eval(1, args.fArgs.data());
557 REPORTER_ASSERT(r, out == 30);
558 }
559
560 {
561 int in[2] = { 15, 25 };
562 RectAndColor out;
563 skvm::Program p = build(make_blue_rect);
564 Args args(gRects);
565 args.add(&in, 2);
566 args.add(&out, 8);
567 p.eval(1, args.fArgs.data());
568 REPORTER_ASSERT(r, out.fRect.width() == 15);
569 REPORTER_ASSERT(r, out.fRect.height() == 25);
570 SkColor4f blue = { 0.0f, 1.0f, 0.0f, 1.0f };
571 REPORTER_ASSERT(r, out.fColor == blue);
572 }
573
574 {
575 int in[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
576 int out = 0;
577 skvm::Program p = build(median);
578 Args args(gRects);
579 args.add(&in, 15);
580 args.add(&out, 1);
581 p.eval(1, args.fArgs.data());
582 REPORTER_ASSERT(r, out == 8);
583 }
584
585 {
586 float in[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
587 float out = 0;
588 skvm::Program p = build(sums);
589 Args args(gRects);
590 args.add(&in, 8);
591 args.add(&out, 1);
592 p.eval(1, args.fArgs.data());
593 REPORTER_ASSERT(r, out == static_cast<float>((7 + 1) * (7 + 2) / 2));
594 }
595
596 {
597 SkIRect out = SkIRect::MakeEmpty();
598 skvm::Program p = build(get_rect_2);
599 Args args(gRects);
600 args.add(&out, 4);
601 p.eval(1, args.fArgs.data());
602 REPORTER_ASSERT(r, out == gRects[2]);
603 }
604
605 {
606 ManyRects in;
607 memset(&in, 0, sizeof(in));
608 in.fNumRects = 2;
609 skvm::Program p = build(fill_rects);
610 Args args(gRects);
611 args.add(&in, 33);
612 p.eval(1, args.fArgs.data());
613 ManyRects expected;
614 memset(&expected, 0, sizeof(expected));
615 expected.fNumRects = 2;
616 for (int i = 0; i < 2; ++i) {
617 expected.fRects[i].fRect = gRects[i];
618 float c = gRects[i].fBottom;
619 expected.fRects[i].fColor = { c, c, c, c };
620 }
621 REPORTER_ASSERT(r, memcmp(&in, &expected, sizeof(in)) == 0);
622 }
623 }
624
expect_failure(skiatest::Reporter * r,const char * src)625 static void expect_failure(skiatest::Reporter* r, const char* src) {
626 GrShaderCaps caps;
627 SkSL::Compiler compiler(&caps);
628 SkSL::Program::Settings settings;
629 auto program = compiler.convertProgram(SkSL::ProgramKind::kGeneric,
630 SkSL::String(src), settings);
631 REPORTER_ASSERT(r, !program);
632 }
633
DEF_TEST(SkSLInterpreterRestrictLoops,r)634 DEF_TEST(SkSLInterpreterRestrictLoops, r) {
635 // while and do-while loops are not allowed
636 expect_failure(r, "void main(inout float x) { while (x < 1) { x++; } }");
637 expect_failure(r, "void main(inout float x) { do { x++; } while (x < 1); }");
638 }
639
DEF_TEST(SkSLInterpreterReturnThenCall,r)640 DEF_TEST(SkSLInterpreterReturnThenCall, r) {
641 // Test that early returns disable execution in subsequently called functions
642 const char* src = R"(
643 float y;
644 void inc () { ++y; }
645 void maybe_inc() { if (y < 0) return; inc(); }
646 void main(inout float x) { y = x; maybe_inc(); x = y; }
647 )";
648
649 ProgramBuilder program(r, src);
650 const SkSL::FunctionDefinition* main = SkSL::Program_GetFunction(*program, "main");
651 REPORTER_ASSERT(r, main);
652
653 skvm::Builder b;
654 SkSL::ProgramToSkVM(*program, *main, &b, /*debugInfo=*/nullptr, /*uniforms=*/{});
655 skvm::Program p = b.done();
656
657 float xs[] = { -2.0f, 0.0f, 3.0f, -1.0f };
658 p.eval(4, xs);
659
660 REPORTER_ASSERT(r, xs[0] == -2.0f);
661 REPORTER_ASSERT(r, xs[1] == 1.0f);
662 REPORTER_ASSERT(r, xs[2] == 4.0f);
663 REPORTER_ASSERT(r, xs[3] == -1.0f);
664 }
665
DEF_TEST(SkSLInterpreterEarlyReturn,r)666 DEF_TEST(SkSLInterpreterEarlyReturn, r) {
667 // Test early returns with divergent control flow
668 const char* src = "float main(float x, float y) { if (x < y) { return x; } return y; }";
669
670 ProgramBuilder program(r, src);
671
672 const SkSL::FunctionDefinition* main = SkSL::Program_GetFunction(*program, "main");
673 REPORTER_ASSERT(r, main);
674
675 skvm::Builder b;
676 SkSL::ProgramToSkVM(*program, *main, &b, /*debugInfo=*/nullptr, /*uniforms=*/{});
677 skvm::Program p = b.done();
678
679 float xs[] = { 1.0f, 3.0f },
680 ys[] = { 2.0f, 2.0f };
681 float rets[2];
682 p.eval(2, xs, ys, rets);
683
684 REPORTER_ASSERT(r, rets[0] == 1.0f);
685 REPORTER_ASSERT(r, rets[1] == 2.0f);
686 }
687
DEF_TEST(SkSLInterpreterFunctions,r)688 DEF_TEST(SkSLInterpreterFunctions, r) {
689 const char* src =
690 "float sqr(float x) { return x * x; }\n"
691 "float sub(float x, float y) { return x - y; }\n"
692 "float main(float x) { return sub(sqr(x), x); }\n"
693
694 // Different signatures
695 "float dot(float2 a, float2 b) { return a.x*b.x + a.y*b.y; }\n"
696 "float dot(float3 a, float3 b) { return a.x*b.x + a.y*b.y + a.z*b.z; }\n"
697 "float dot3_test(float x) { return dot(float3(x, x + 1, x + 2), float3(1, -1, 2)); }\n"
698 "float dot2_test(float x) { return dot(float2(x, x + 1), float2(1, -1)); }\n";
699
700 ProgramBuilder program(r, src);
701
702 auto sub = SkSL::Program_GetFunction(*program, "sub");
703 auto sqr = SkSL::Program_GetFunction(*program, "sqr");
704 auto main = SkSL::Program_GetFunction(*program, "main");
705 auto tan = SkSL::Program_GetFunction(*program, "tan");
706 auto dot3 = SkSL::Program_GetFunction(*program, "dot3_test");
707 auto dot2 = SkSL::Program_GetFunction(*program, "dot2_test");
708
709 REPORTER_ASSERT(r, sub);
710 REPORTER_ASSERT(r, sqr);
711 REPORTER_ASSERT(r, main);
712 REPORTER_ASSERT(r, !tan); // Getting a non-existent function should return nullptr
713 REPORTER_ASSERT(r, dot3);
714 REPORTER_ASSERT(r, dot2);
715
716 auto test_fn = [&](const SkSL::FunctionDefinition* fn, float in, float expected) {
717 skvm::Builder b;
718 SkSL::ProgramToSkVM(*program, *fn, &b, /*debugInfo=*/nullptr, /*uniforms=*/{});
719 skvm::Program p = b.done();
720
721 float out = 0.0f;
722 p.eval(1, &in, &out);
723 REPORTER_ASSERT(r, out == expected);
724 };
725
726 test_fn(main, 3.0f, 6.0f);
727 test_fn(dot3, 3.0f, 9.0f);
728 test_fn(dot2, 3.0f, -1.0f);
729 }
730
DEF_TEST(SkSLInterpreterOutParams,r)731 DEF_TEST(SkSLInterpreterOutParams, r) {
732 test(r,
733 "void oneAlpha(inout half4 color) { color.a = 1; }"
734 "void main(inout half4 color) { oneAlpha(color); }",
735 0, 0, 0, 0, 0, 0, 0, 1);
736 test(r,
737 "half2 tricky(half x, half y, inout half2 color, half z) {"
738 " color.xy = color.yx;"
739 " return half2(x + y, z);"
740 "}"
741 "void main(inout half4 color) {"
742 " half2 t = tricky(1, 2, color.rb, 5);"
743 " color.ga = t;"
744 "}",
745 1, 2, 3, 4, 3, 3, 1, 5);
746 }
747
DEF_TEST(SkSLInterpreterSwizzleSingleLvalue,r)748 DEF_TEST(SkSLInterpreterSwizzleSingleLvalue, r) {
749 test(r,
750 "void main(inout half4 color) { color.xywz = half4(1,2,3,4); }",
751 0, 0, 0, 0, 1, 2, 4, 3);
752 }
753
DEF_TEST(SkSLInterpreterSwizzleDoubleLvalue,r)754 DEF_TEST(SkSLInterpreterSwizzleDoubleLvalue, r) {
755 test(r,
756 "void main(inout half4 color) { color.xywz.yxzw = half4(1,2,3,4); }",
757 0, 0, 0, 0, 2, 1, 4, 3);
758 }
759
DEF_TEST(SkSLInterpreterSwizzleIndexLvalue,r)760 DEF_TEST(SkSLInterpreterSwizzleIndexLvalue, r) {
761 const char* src = R"(
762 void main(inout half4 color) {
763 for (int i = 0; i < 4; i++) {
764 color.wzyx[i] += half(i);
765 }
766 }
767 )";
768 test(r, src, 0, 0, 0, 0, 3, 2, 1, 0);
769 }
770
DEF_TEST(SkSLInterpreterMathFunctions,r)771 DEF_TEST(SkSLInterpreterMathFunctions, r) {
772 float value[4], expected[4];
773
774 value[0] = 0.0f; expected[0] = 0.0f;
775 test(r, "float main(float x) { return sin(x); }", value, expected);
776 test(r, "float main(float x) { return tan(x); }", value, expected);
777
778 value[0] = 0.0f; expected[0] = 1.0f;
779 test(r, "float main(float x) { return cos(x); }", value, expected);
780
781 value[0] = 25.0f; expected[0] = 5.0f;
782 test(r, "float main(float x) { return sqrt(x); }", value, expected);
783
784 value[0] = 90.0f; expected[0] = sk_float_degrees_to_radians(value[0]);
785 test(r, "float main(float x) { return radians(x); }", value, expected);
786
787 value[0] = 1.0f; value[1] = -1.0f;
788 expected[0] = 1.0f / SK_FloatSqrt2; expected[1] = -1.0f / SK_FloatSqrt2;
789 test(r, "float2 main(float2 x) { return normalize(x); }", value, expected);
790 }
791
DEF_TEST(SkSLInterpreterVoidFunction,r)792 DEF_TEST(SkSLInterpreterVoidFunction, r) {
793 test(r,
794 "half x; void foo() { x = 1.0; }"
795 "void main(inout half4 color) { foo(); color.r = x; }",
796 0, 0, 0, 0, 1, 0, 0, 0);
797 }
798
DEF_TEST(SkSLInterpreterMix,r)799 DEF_TEST(SkSLInterpreterMix, r) {
800 float value, expected;
801
802 value = 0.5f; expected = 0.0f;
803 test(r, "float main(float x) { return mix(-10, 10, x); }", &value, &expected);
804 value = 0.75f; expected = 5.0f;
805 test(r, "float main(float x) { return mix(-10, 10, x); }", &value, &expected);
806 value = 2.0f; expected = 30.0f;
807 test(r, "float main(float x) { return mix(-10, 10, x); }", &value, &expected);
808
809 float valueVectors[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f },
810 expectedVector[] = { 3.0f, 4.0f, 5.0f, 6.0f };
811 test(r, "float4 main(float4 x, float4 y) { return mix(x, y, 0.5); }", valueVectors,
812 expectedVector);
813 }
814
DEF_TEST(SkSLInterpreterCross,r)815 DEF_TEST(SkSLInterpreterCross, r) {
816 float args[] = { 1.0f, 4.0f, -6.0f, -2.0f, 7.0f, -3.0f };
817 SkV3 cross = SkV3::Cross({args[0], args[1], args[2]},
818 {args[3], args[4], args[5]});
819 float expected[] = { cross.x, cross.y, cross.z };
820 test(r, "float3 main(float3 x, float3 y) { return cross(x, y); }", args, expected);
821 }
822
DEF_TEST(SkSLInterpreterInverse,r)823 DEF_TEST(SkSLInterpreterInverse, r) {
824 {
825 SkMatrix m;
826 m.setRotate(30).postScale(1, 2);
827 float args[4] = { m[0], m[3], m[1], m[4] };
828 SkAssertResult(m.invert(&m));
829 float expt[4] = { m[0], m[3], m[1], m[4] };
830 test(r, "float2x2 main(float2x2 m) { return inverse(m); }", args, expt, false);
831 }
832 {
833 SkMatrix m;
834 m.setRotate(30).postScale(1, 2).postTranslate(1, 2);
835 float args[9] = { m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8] };
836 SkAssertResult(m.invert(&m));
837 float expt[9] = { m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8] };
838 test(r, "float3x3 main(float3x3 m) { return inverse(m); }", args, expt, false);
839 }
840 {
841 float args[16], expt[16];
842 // just some crazy thing that is invertible
843 SkM44 m = {1, 2, 3, 4, 1, 2, 0, 3, 1, 0, 1, 4, 1, 3, 2, 0};
844 m.getColMajor(args);
845 SkAssertResult(m.invert(&m));
846 m.getColMajor(expt);
847 test(r, "float4x4 main(float4x4 m) { return inverse(m); }", args, expt, false);
848 }
849 }
850
DEF_TEST(SkSLInterpreterDot,r)851 DEF_TEST(SkSLInterpreterDot, r) {
852 float args[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
853 float expected = args[0] * args[2] +
854 args[1] * args[3];
855 test(r, "float main(float2 x, float2 y) { return dot(x, y); }", args, &expected);
856
857 expected = args[0] * args[3] +
858 args[1] * args[4] +
859 args[2] * args[5];
860 test(r, "float main(float3 x, float3 y) { return dot(x, y); }", args, &expected);
861
862 expected = args[0] * args[4] +
863 args[1] * args[5] +
864 args[2] * args[6] +
865 args[3] * args[7];
866 test(r, "float main(float4 x, float4 y) { return dot(x, y); }", args, &expected);
867 }
868
869 class ExternalSqrt : public SkSL::ExternalFunction {
870 public:
ExternalSqrt(const char * name,SkSL::Compiler & compiler)871 ExternalSqrt(const char* name, SkSL::Compiler& compiler)
872 : INHERITED(name, *compiler.context().fTypes.fFloat)
873 , fCompiler(compiler) {}
874
callParameterCount() const875 int callParameterCount() const override { return 1; }
876
getCallParameterTypes(const SkSL::Type ** outTypes) const877 void getCallParameterTypes(const SkSL::Type** outTypes) const override {
878 outTypes[0] = fCompiler.context().fTypes.fFloat.get();
879 }
880
call(skvm::Builder * b,skvm::F32 * arguments,skvm::F32 * outResult,skvm::I32 mask) const881 void call(skvm::Builder* b,
882 skvm::F32* arguments,
883 skvm::F32* outResult,
884 skvm::I32 mask) const override {
885 outResult[0] = sqrt(arguments[0]);
886 }
887
888 private:
889 SkSL::Compiler& fCompiler;
890 using INHERITED = SkSL::ExternalFunction;
891 };
892
DEF_TEST(SkSLInterpreterExternalFunction,r)893 DEF_TEST(SkSLInterpreterExternalFunction, r) {
894 GrShaderCaps caps;
895 SkSL::Compiler compiler(&caps);
896 SkSL::Program::Settings settings;
897 const char* src = "float main() { return externalSqrt(25); }";
898 std::vector<std::unique_ptr<SkSL::ExternalFunction>> externalFunctions;
899 externalFunctions.push_back(std::make_unique<ExternalSqrt>("externalSqrt", compiler));
900 settings.fExternalFunctions = &externalFunctions;
901 std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
902 SkSL::ProgramKind::kGeneric, SkSL::String(src), settings);
903 REPORTER_ASSERT(r, program);
904
905 const SkSL::FunctionDefinition* main = SkSL::Program_GetFunction(*program, "main");
906
907 skvm::Builder b;
908 SkSL::ProgramToSkVM(*program, *main, &b, /*debugInfo=*/nullptr, /*uniforms=*/{});
909 skvm::Program p = b.done();
910
911 float out;
912 p.eval(1, &out);
913 REPORTER_ASSERT(r, out == 5.0);
914 }
915
916 class ExternalTable : public SkSL::ExternalFunction {
917 public:
ExternalTable(const char * name,SkSL::Compiler & compiler,skvm::Uniforms * uniforms)918 ExternalTable(const char* name, SkSL::Compiler& compiler, skvm::Uniforms* uniforms)
919 : INHERITED(name, *compiler.context().fTypes.fFloat)
920 , fCompiler(compiler)
921 , fTable{1, 2, 4, 8} {
922 fAddr = uniforms->pushPtr(fTable);
923 }
924
callParameterCount() const925 int callParameterCount() const override { return 1; }
926
getCallParameterTypes(const SkSL::Type ** outTypes) const927 void getCallParameterTypes(const SkSL::Type** outTypes) const override {
928 outTypes[0] = fCompiler.context().fTypes.fFloat.get();
929 }
930
call(skvm::Builder * b,skvm::F32 * arguments,skvm::F32 * outResult,skvm::I32 mask) const931 void call(skvm::Builder* b,
932 skvm::F32* arguments,
933 skvm::F32* outResult,
934 skvm::I32 mask) const override {
935 skvm::I32 index = skvm::trunc(arguments[0] * 4);
936 index = max(0, min(index, 3));
937 outResult[0] = b->gatherF(fAddr, index);
938 }
939
940 private:
941 SkSL::Compiler& fCompiler;
942 skvm::Uniform fAddr;
943 float fTable[4];
944 using INHERITED = SkSL::ExternalFunction;
945 };
946
DEF_TEST(SkSLInterpreterExternalTable,r)947 DEF_TEST(SkSLInterpreterExternalTable, r) {
948 GrShaderCaps caps;
949 SkSL::Compiler compiler(&caps);
950 SkSL::Program::Settings settings;
951 const char* src =
952 "float4 main() { return float4(table(2), table(-1), table(0.4), table(0.6)); }";
953 std::vector<std::unique_ptr<SkSL::ExternalFunction>> externalFunctions;
954
955 skvm::Builder b;
956 skvm::Uniforms u(b.uniform(), 0);
957
958 externalFunctions.push_back(std::make_unique<ExternalTable>("table", compiler, &u));
959 settings.fExternalFunctions = &externalFunctions;
960 std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
961 SkSL::ProgramKind::kGeneric, SkSL::String(src), settings);
962 REPORTER_ASSERT(r, program);
963
964 const SkSL::FunctionDefinition* main = SkSL::Program_GetFunction(*program, "main");
965
966 SkSL::ProgramToSkVM(*program, *main, &b, /*debugInfo=*/nullptr, /*uniforms=*/{});
967 skvm::Program p = b.done();
968
969 float out[4];
970 p.eval(1, u.buf.data(), &out[0], &out[1], &out[2], &out[3]);
971 REPORTER_ASSERT(r, out[0] == 8.0);
972 REPORTER_ASSERT(r, out[1] == 1.0);
973 REPORTER_ASSERT(r, out[2] == 2.0);
974 REPORTER_ASSERT(r, out[3] == 4.0);
975 }
976