1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <iomanip>
18 #include <iostream>
19 #include <cmath>
20 #include <sstream>
21
22 #include "Generator.h"
23 #include "Specification.h"
24 #include "Utilities.h"
25
26 using namespace std;
27
28 // Converts float2 to FLOAT_32 and 2, etc.
convertToRsType(const string & name,string * dataType,char * vectorSize)29 static void convertToRsType(const string& name, string* dataType, char* vectorSize) {
30 string s = name;
31 int last = s.size() - 1;
32 char lastChar = s[last];
33 if (lastChar >= '1' && lastChar <= '4') {
34 s.erase(last);
35 *vectorSize = lastChar;
36 } else {
37 *vectorSize = '1';
38 }
39 dataType->clear();
40 for (int i = 0; i < NUM_TYPES; i++) {
41 if (s == TYPES[i].cType) {
42 *dataType = TYPES[i].rsDataType;
43 break;
44 }
45 }
46 }
47
48 // Returns true if any permutation of the function have tests to b
needTestFiles(const Function & function,unsigned int versionOfTestFiles)49 static bool needTestFiles(const Function& function, unsigned int versionOfTestFiles) {
50 for (auto spec : function.getSpecifications()) {
51 if (spec->hasTests(versionOfTestFiles)) {
52 return true;
53 }
54 }
55 return false;
56 }
57
58 /* One instance of this class is generated for each permutation of a function for which
59 * we are generating test code. This instance will generate both the script and the Java
60 * section of the test files for this permutation. The class is mostly used to keep track
61 * of the various names shared between script and Java files.
62 * WARNING: Because the constructor keeps a reference to the FunctionPermutation, PermutationWriter
63 * should not exceed the lifetime of FunctionPermutation.
64 */
65 class PermutationWriter {
66 private:
67 FunctionPermutation& mPermutation;
68
69 string mRsKernelName;
70 string mJavaArgumentsClassName;
71 string mJavaArgumentsNClassName;
72 string mJavaVerifierComputeMethodName;
73 string mJavaVerifierVerifyMethodName;
74 string mJavaCheckMethodName;
75 string mJavaVerifyMethodName;
76
77 // Pointer to the files we are generating. Handy to avoid always passing them in the calls.
78 GeneratedFile* mRs;
79 GeneratedFile* mJava;
80
81 /* Shortcuts to the return parameter and the first input parameter of the function
82 * specification.
83 */
84 const ParameterDefinition* mReturnParam; // Can be nullptr. NOT OWNED.
85 const ParameterDefinition* mFirstInputParam; // Can be nullptr. NOT OWNED.
86
87 /* All the parameters plus the return param, if present. Collecting them together
88 * simplifies code generation. NOT OWNED.
89 */
90 vector<const ParameterDefinition*> mAllInputsAndOutputs;
91
92 /* We use a class to pass the arguments between the generated code and the CoreVerifier. This
93 * method generates this class. The set keeps track if we've generated this class already
94 * for this test file, as more than one permutation may use the same argument class.
95 */
96 void writeJavaArgumentClass(bool scalar, set<string>* javaGeneratedArgumentClasses) const;
97
98 // Generate the Check* method that invokes the script and calls the verifier.
99 void writeJavaCheckMethod(bool generateCallToVerifier) const;
100
101 // Generate code to define and randomly initialize the input allocation.
102 void writeJavaInputAllocationDefinition(const ParameterDefinition& param) const;
103
104 /* Generate code that instantiate an allocation of floats or integers and fills it with
105 * random data. This random data must be compatible with the specified type. This is
106 * used for the convert_* tests, as converting values that don't fit yield undefined results.
107 */
108 void writeJavaRandomCompatibleFloatAllocation(const string& dataType, const string& seed,
109 char vectorSize,
110 const NumericalType& compatibleType,
111 const NumericalType& generatedType) const;
112 void writeJavaRandomCompatibleIntegerAllocation(const string& dataType, const string& seed,
113 char vectorSize,
114 const NumericalType& compatibleType,
115 const NumericalType& generatedType) const;
116
117 // Generate code that defines an output allocation.
118 void writeJavaOutputAllocationDefinition(const ParameterDefinition& param) const;
119
120 /* Generate the code that verifies the results for RenderScript functions where each entry
121 * of a vector is evaluated independently. If verifierValidates is true, CoreMathVerifier
122 * does the actual validation instead of more commonly returning the range of acceptable values.
123 */
124 void writeJavaVerifyScalarMethod(bool verifierValidates) const;
125
126 /* Generate the code that verify the results for a RenderScript function where a vector
127 * is a point in n-dimensional space.
128 */
129 void writeJavaVerifyVectorMethod() const;
130
131 // Generate the line that creates the Target.
132 void writeJavaCreateTarget() const;
133
134 // Generate the method header of the verify function.
135 void writeJavaVerifyMethodHeader() const;
136
137 // Generate codes that copies the content of an allocation to an array.
138 void writeJavaArrayInitialization(const ParameterDefinition& p) const;
139
140 // Generate code that tests one value returned from the script.
141 void writeJavaTestAndSetValid(const ParameterDefinition& p, const string& argsIndex,
142 const string& actualIndex) const;
143 void writeJavaTestOneValue(const ParameterDefinition& p, const string& argsIndex,
144 const string& actualIndex) const;
145 // For test:vector cases, generate code that compares returned vector vs. expected value.
146 void writeJavaVectorComparison(const ParameterDefinition& p) const;
147
148 // Muliple functions that generates code to build the error message if an error is found.
149 void writeJavaAppendOutputToMessage(const ParameterDefinition& p, const string& argsIndex,
150 const string& actualIndex, bool verifierValidates) const;
151 void writeJavaAppendInputToMessage(const ParameterDefinition& p, const string& actual) const;
152 void writeJavaAppendNewLineToMessage() const;
153 void writeJavaAppendVectorInputToMessage(const ParameterDefinition& p) const;
154 void writeJavaAppendVectorOutputToMessage(const ParameterDefinition& p) const;
155
156 // Generate the set of instructions to call the script.
157 void writeJavaCallToRs(bool relaxed, bool generateCallToVerifier) const;
158
159 // Write an allocation definition if not already emitted in the .rs file.
160 void writeRsAllocationDefinition(const ParameterDefinition& param,
161 set<string>* rsAllocationsGenerated) const;
162
163 public:
164 /* NOTE: We keep pointers to the permutation and the files. This object should not
165 * outlive the arguments.
166 */
167 PermutationWriter(FunctionPermutation& permutation, GeneratedFile* rsFile,
168 GeneratedFile* javaFile);
getJavaCheckMethodName() const169 string getJavaCheckMethodName() const { return mJavaCheckMethodName; }
170
171 // Write the script test function for this permutation.
172 void writeRsSection(set<string>* rsAllocationsGenerated) const;
173 // Write the section of the Java code that calls the script and validates the results
174 void writeJavaSection(set<string>* javaGeneratedArgumentClasses) const;
175 };
176
PermutationWriter(FunctionPermutation & permutation,GeneratedFile * rsFile,GeneratedFile * javaFile)177 PermutationWriter::PermutationWriter(FunctionPermutation& permutation, GeneratedFile* rsFile,
178 GeneratedFile* javaFile)
179 : mPermutation(permutation),
180 mRs(rsFile),
181 mJava(javaFile),
182 mReturnParam(nullptr),
183 mFirstInputParam(nullptr) {
184 mRsKernelName = "test" + capitalize(permutation.getName());
185
186 mJavaArgumentsClassName = "Arguments";
187 mJavaArgumentsNClassName = "Arguments";
188 const string trunk = capitalize(permutation.getNameTrunk());
189 mJavaCheckMethodName = "check" + trunk;
190 mJavaVerifyMethodName = "verifyResults" + trunk;
191
192 for (auto p : permutation.getParams()) {
193 mAllInputsAndOutputs.push_back(p);
194 if (mFirstInputParam == nullptr && !p->isOutParameter) {
195 mFirstInputParam = p;
196 }
197 }
198 mReturnParam = permutation.getReturn();
199 if (mReturnParam) {
200 mAllInputsAndOutputs.push_back(mReturnParam);
201 }
202
203 for (auto p : mAllInputsAndOutputs) {
204 const string capitalizedRsType = capitalize(p->rsType);
205 const string capitalizedBaseType = capitalize(p->rsBaseType);
206 mRsKernelName += capitalizedRsType;
207 mJavaArgumentsClassName += capitalizedBaseType;
208 mJavaArgumentsNClassName += capitalizedBaseType;
209 if (p->mVectorSize != "1") {
210 mJavaArgumentsNClassName += "N";
211 }
212 mJavaCheckMethodName += capitalizedRsType;
213 mJavaVerifyMethodName += capitalizedRsType;
214 }
215 mJavaVerifierComputeMethodName = "compute" + trunk;
216 mJavaVerifierVerifyMethodName = "verify" + trunk;
217 }
218
writeJavaSection(set<string> * javaGeneratedArgumentClasses) const219 void PermutationWriter::writeJavaSection(set<string>* javaGeneratedArgumentClasses) const {
220 // By default, we test the results using item by item comparison.
221 const string test = mPermutation.getTest();
222 if (test == "scalar" || test == "limited") {
223 writeJavaArgumentClass(true, javaGeneratedArgumentClasses);
224 writeJavaCheckMethod(true);
225 writeJavaVerifyScalarMethod(false);
226 } else if (test == "custom") {
227 writeJavaArgumentClass(true, javaGeneratedArgumentClasses);
228 writeJavaCheckMethod(true);
229 writeJavaVerifyScalarMethod(true);
230 } else if (test == "vector") {
231 writeJavaArgumentClass(false, javaGeneratedArgumentClasses);
232 writeJavaCheckMethod(true);
233 writeJavaVerifyVectorMethod();
234 } else if (test == "noverify") {
235 writeJavaCheckMethod(false);
236 }
237 }
238
writeJavaArgumentClass(bool scalar,set<string> * javaGeneratedArgumentClasses) const239 void PermutationWriter::writeJavaArgumentClass(bool scalar,
240 set<string>* javaGeneratedArgumentClasses) const {
241 string name;
242 if (scalar) {
243 name = mJavaArgumentsClassName;
244 } else {
245 name = mJavaArgumentsNClassName;
246 }
247
248 // Make sure we have not generated the argument class already.
249 if (!testAndSet(name, javaGeneratedArgumentClasses)) {
250 mJava->indent() << "public class " << name;
251 mJava->startBlock();
252
253 for (auto p : mAllInputsAndOutputs) {
254 bool isFieldArray = !scalar && p->mVectorSize != "1";
255 bool isFloatyField = p->isOutParameter && p->isFloatType && mPermutation.getTest() != "custom";
256
257 mJava->indent() << "public ";
258 if (isFloatyField) {
259 *mJava << "Target.Floaty";
260 } else {
261 *mJava << p->javaBaseType;
262 }
263 if (isFieldArray) {
264 *mJava << "[]";
265 }
266 *mJava << " " << p->variableName << ";\n";
267
268 // For Float16 parameters, add an extra 'double' field in the class
269 // to hold the Double value converted from the input.
270 if (p->isFloat16Parameter() && !isFloatyField) {
271 mJava->indent() << "public double";
272 if (isFieldArray) {
273 *mJava << "[]";
274 }
275 *mJava << " " + p->variableName << "Double;\n";
276 }
277 }
278 mJava->endBlock();
279 *mJava << "\n";
280 }
281 }
282
writeJavaCheckMethod(bool generateCallToVerifier) const283 void PermutationWriter::writeJavaCheckMethod(bool generateCallToVerifier) const {
284 mJava->indent() << "private void " << mJavaCheckMethodName << "()";
285 mJava->startBlock();
286
287 // Generate the input allocations and initialization.
288 for (auto p : mAllInputsAndOutputs) {
289 if (!p->isOutParameter) {
290 writeJavaInputAllocationDefinition(*p);
291 }
292 }
293 // Generate code to enforce ordering between two allocations if needed.
294 for (auto p : mAllInputsAndOutputs) {
295 if (!p->isOutParameter && !p->smallerParameter.empty()) {
296 string smallerAlloc = "in" + capitalize(p->smallerParameter);
297 mJava->indent() << "enforceOrdering(" << smallerAlloc << ", " << p->javaAllocName
298 << ");\n";
299 }
300 }
301
302 // Generate code to check the full and relaxed scripts.
303 writeJavaCallToRs(false, generateCallToVerifier);
304 writeJavaCallToRs(true, generateCallToVerifier);
305
306 // Generate code to destroy input Allocations.
307 for (auto p : mAllInputsAndOutputs) {
308 if (!p->isOutParameter) {
309 mJava->indent() << p->javaAllocName << ".destroy();\n";
310 }
311 }
312
313 mJava->endBlock();
314 *mJava << "\n";
315 }
316
writeJavaInputAllocationDefinition(const ParameterDefinition & param) const317 void PermutationWriter::writeJavaInputAllocationDefinition(const ParameterDefinition& param) const {
318 string dataType;
319 char vectorSize;
320 convertToRsType(param.rsType, &dataType, &vectorSize);
321
322 const string seed = hashString(mJavaCheckMethodName + param.javaAllocName);
323 mJava->indent() << "Allocation " << param.javaAllocName << " = ";
324 if (param.compatibleTypeIndex >= 0) {
325 if (TYPES[param.typeIndex].kind == FLOATING_POINT) {
326 writeJavaRandomCompatibleFloatAllocation(dataType, seed, vectorSize,
327 TYPES[param.compatibleTypeIndex],
328 TYPES[param.typeIndex]);
329 } else {
330 writeJavaRandomCompatibleIntegerAllocation(dataType, seed, vectorSize,
331 TYPES[param.compatibleTypeIndex],
332 TYPES[param.typeIndex]);
333 }
334 } else if (!param.minValue.empty()) {
335 *mJava << "createRandomFloatAllocation(mRS, Element.DataType." << dataType << ", "
336 << vectorSize << ", " << seed << ", " << param.minValue << ", " << param.maxValue
337 << ")";
338 } else {
339 /* TODO Instead of passing always false, check whether we are doing a limited test.
340 * Use instead: (mPermutation.getTest() == "limited" ? "false" : "true")
341 */
342 *mJava << "createRandomAllocation(mRS, Element.DataType." << dataType << ", " << vectorSize
343 << ", " << seed << ", false)";
344 }
345 *mJava << ";\n";
346 }
347
writeJavaRandomCompatibleFloatAllocation(const string & dataType,const string & seed,char vectorSize,const NumericalType & compatibleType,const NumericalType & generatedType) const348 void PermutationWriter::writeJavaRandomCompatibleFloatAllocation(
349 const string& dataType, const string& seed, char vectorSize,
350 const NumericalType& compatibleType, const NumericalType& generatedType) const {
351 *mJava << "createRandomFloatAllocation"
352 << "(mRS, Element.DataType." << dataType << ", " << vectorSize << ", " << seed << ", ";
353 double minValue = 0.0;
354 double maxValue = 0.0;
355 switch (compatibleType.kind) {
356 case FLOATING_POINT: {
357 // We're generating floating point values. We just worry about the exponent.
358 // Subtract 1 for the exponent sign.
359 int bits = min(compatibleType.exponentBits, generatedType.exponentBits) - 1;
360 maxValue = ldexp(0.95, (1 << bits) - 1);
361 minValue = -maxValue;
362 break;
363 }
364 case UNSIGNED_INTEGER:
365 maxValue = maxDoubleForInteger(compatibleType.significantBits,
366 generatedType.significantBits);
367 minValue = 0.0;
368 break;
369 case SIGNED_INTEGER:
370 maxValue = maxDoubleForInteger(compatibleType.significantBits,
371 generatedType.significantBits);
372 minValue = -maxValue - 1.0;
373 break;
374 }
375 *mJava << scientific << std::setprecision(19);
376 *mJava << minValue << ", " << maxValue << ")";
377 mJava->unsetf(ios_base::floatfield);
378 }
379
writeJavaRandomCompatibleIntegerAllocation(const string & dataType,const string & seed,char vectorSize,const NumericalType & compatibleType,const NumericalType & generatedType) const380 void PermutationWriter::writeJavaRandomCompatibleIntegerAllocation(
381 const string& dataType, const string& seed, char vectorSize,
382 const NumericalType& compatibleType, const NumericalType& generatedType) const {
383 *mJava << "createRandomIntegerAllocation"
384 << "(mRS, Element.DataType." << dataType << ", " << vectorSize << ", " << seed << ", ";
385
386 if (compatibleType.kind == FLOATING_POINT) {
387 // Currently, all floating points can take any number we generate.
388 bool isSigned = generatedType.kind == SIGNED_INTEGER;
389 *mJava << (isSigned ? "true" : "false") << ", " << generatedType.significantBits;
390 } else {
391 bool isSigned =
392 compatibleType.kind == SIGNED_INTEGER && generatedType.kind == SIGNED_INTEGER;
393 *mJava << (isSigned ? "true" : "false") << ", "
394 << min(compatibleType.significantBits, generatedType.significantBits);
395 }
396 *mJava << ")";
397 }
398
writeJavaOutputAllocationDefinition(const ParameterDefinition & param) const399 void PermutationWriter::writeJavaOutputAllocationDefinition(
400 const ParameterDefinition& param) const {
401 string dataType;
402 char vectorSize;
403 convertToRsType(param.rsType, &dataType, &vectorSize);
404 mJava->indent() << "Allocation " << param.javaAllocName << " = Allocation.createSized(mRS, "
405 << "getElement(mRS, Element.DataType." << dataType << ", " << vectorSize
406 << "), INPUTSIZE);\n";
407 }
408
writeJavaVerifyScalarMethod(bool verifierValidates) const409 void PermutationWriter::writeJavaVerifyScalarMethod(bool verifierValidates) const {
410 writeJavaVerifyMethodHeader();
411 mJava->startBlock();
412
413 string vectorSize = "1";
414 for (auto p : mAllInputsAndOutputs) {
415 writeJavaArrayInitialization(*p);
416 if (p->mVectorSize != "1" && p->mVectorSize != vectorSize) {
417 if (vectorSize == "1") {
418 vectorSize = p->mVectorSize;
419 } else {
420 cerr << "Error. Had vector " << vectorSize << " and " << p->mVectorSize << "\n";
421 }
422 }
423 }
424
425 mJava->indent() << "StringBuilder message = new StringBuilder();\n";
426 mJava->indent() << "boolean errorFound = false;\n";
427 mJava->indent() << "for (int i = 0; i < INPUTSIZE; i++)";
428 mJava->startBlock();
429
430 mJava->indent() << "for (int j = 0; j < " << vectorSize << " ; j++)";
431 mJava->startBlock();
432
433 mJava->indent() << "// Extract the inputs.\n";
434 mJava->indent() << mJavaArgumentsClassName << " args = new " << mJavaArgumentsClassName
435 << "();\n";
436 for (auto p : mAllInputsAndOutputs) {
437 if (!p->isOutParameter) {
438 mJava->indent() << "args." << p->variableName << " = " << p->javaArrayName << "[i";
439 if (p->vectorWidth != "1") {
440 *mJava << " * " << p->vectorWidth << " + j";
441 }
442 *mJava << "];\n";
443
444 // Convert the Float16 parameter to double and store it in the appropriate field in the
445 // Arguments class.
446 if (p->isFloat16Parameter()) {
447 mJava->indent() << "args." << p->doubleVariableName
448 << " = Float16Utils.convertFloat16ToDouble(args."
449 << p->variableName << ");\n";
450 }
451 }
452 }
453 const bool hasFloat = mPermutation.hasFloatAnswers();
454 if (verifierValidates) {
455 mJava->indent() << "// Extract the outputs.\n";
456 for (auto p : mAllInputsAndOutputs) {
457 if (p->isOutParameter) {
458 mJava->indent() << "args." << p->variableName << " = " << p->javaArrayName
459 << "[i * " << p->vectorWidth << " + j];\n";
460 if (p->isFloat16Parameter()) {
461 mJava->indent() << "args." << p->doubleVariableName
462 << " = Float16Utils.convertFloat16ToDouble(args."
463 << p->variableName << ");\n";
464 }
465 }
466 }
467 mJava->indent() << "// Ask the CoreMathVerifier to validate.\n";
468 if (hasFloat) {
469 writeJavaCreateTarget();
470 }
471 mJava->indent() << "String errorMessage = CoreMathVerifier."
472 << mJavaVerifierVerifyMethodName << "(args";
473 if (hasFloat) {
474 *mJava << ", target";
475 }
476 *mJava << ");\n";
477 mJava->indent() << "boolean valid = errorMessage == null;\n";
478 } else {
479 mJava->indent() << "// Figure out what the outputs should have been.\n";
480 if (hasFloat) {
481 writeJavaCreateTarget();
482 }
483 mJava->indent() << "CoreMathVerifier." << mJavaVerifierComputeMethodName << "(args";
484 if (hasFloat) {
485 *mJava << ", target";
486 }
487 *mJava << ");\n";
488 mJava->indent() << "// Validate the outputs.\n";
489 mJava->indent() << "boolean valid = true;\n";
490 for (auto p : mAllInputsAndOutputs) {
491 if (p->isOutParameter) {
492 writeJavaTestAndSetValid(*p, "", "[i * " + p->vectorWidth + " + j]");
493 }
494 }
495 }
496
497 mJava->indent() << "if (!valid)";
498 mJava->startBlock();
499 mJava->indent() << "if (!errorFound)";
500 mJava->startBlock();
501 mJava->indent() << "errorFound = true;\n";
502
503 for (auto p : mAllInputsAndOutputs) {
504 if (p->isOutParameter) {
505 writeJavaAppendOutputToMessage(*p, "", "[i * " + p->vectorWidth + " + j]",
506 verifierValidates);
507 } else {
508 writeJavaAppendInputToMessage(*p, "args." + p->variableName);
509 }
510 }
511 if (verifierValidates) {
512 mJava->indent() << "message.append(errorMessage);\n";
513 }
514 mJava->indent() << "message.append(\"Errors at\");\n";
515 mJava->endBlock();
516
517 mJava->indent() << "message.append(\" [\");\n";
518 mJava->indent() << "message.append(Integer.toString(i));\n";
519 mJava->indent() << "message.append(\", \");\n";
520 mJava->indent() << "message.append(Integer.toString(j));\n";
521 mJava->indent() << "message.append(\"]\");\n";
522
523 mJava->endBlock();
524 mJava->endBlock();
525 mJava->endBlock();
526
527 mJava->indent() << "assertFalse(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n";
528 mJava->indentPlus()
529 << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), errorFound);\n";
530
531 mJava->endBlock();
532 *mJava << "\n";
533 }
534
writeJavaVerifyVectorMethod() const535 void PermutationWriter::writeJavaVerifyVectorMethod() const {
536 writeJavaVerifyMethodHeader();
537 mJava->startBlock();
538
539 for (auto p : mAllInputsAndOutputs) {
540 writeJavaArrayInitialization(*p);
541 }
542 mJava->indent() << "StringBuilder message = new StringBuilder();\n";
543 mJava->indent() << "boolean errorFound = false;\n";
544 mJava->indent() << "for (int i = 0; i < INPUTSIZE; i++)";
545 mJava->startBlock();
546
547 mJava->indent() << mJavaArgumentsNClassName << " args = new " << mJavaArgumentsNClassName
548 << "();\n";
549
550 mJava->indent() << "// Create the appropriate sized arrays in args\n";
551 for (auto p : mAllInputsAndOutputs) {
552 if (p->mVectorSize != "1") {
553 string type = p->javaBaseType;
554 if (p->isOutParameter && p->isFloatType) {
555 type = "Target.Floaty";
556 }
557 mJava->indent() << "args." << p->variableName << " = new " << type << "["
558 << p->mVectorSize << "];\n";
559 if (p->isFloat16Parameter() && !p->isOutParameter) {
560 mJava->indent() << "args." << p->variableName << "Double = new double["
561 << p->mVectorSize << "];\n";
562 }
563 }
564 }
565
566 mJava->indent() << "// Fill args with the input values\n";
567 for (auto p : mAllInputsAndOutputs) {
568 if (!p->isOutParameter) {
569 if (p->mVectorSize == "1") {
570 mJava->indent() << "args." << p->variableName << " = " << p->javaArrayName << "[i]"
571 << ";\n";
572 // Convert the Float16 parameter to double and store it in the appropriate field in
573 // the Arguments class.
574 if (p->isFloat16Parameter()) {
575 mJava->indent() << "args." << p->doubleVariableName << " = "
576 << "Float16Utils.convertFloat16ToDouble(args."
577 << p->variableName << ");\n";
578 }
579 } else {
580 mJava->indent() << "for (int j = 0; j < " << p->mVectorSize << " ; j++)";
581 mJava->startBlock();
582 mJava->indent() << "args." << p->variableName << "[j] = "
583 << p->javaArrayName << "[i * " << p->vectorWidth << " + j]"
584 << ";\n";
585
586 // Convert the Float16 parameter to double and store it in the appropriate field in
587 // the Arguments class.
588 if (p->isFloat16Parameter()) {
589 mJava->indent() << "args." << p->doubleVariableName << "[j] = "
590 << "Float16Utils.convertFloat16ToDouble(args."
591 << p->variableName << "[j]);\n";
592 }
593 mJava->endBlock();
594 }
595 }
596 }
597 writeJavaCreateTarget();
598 mJava->indent() << "CoreMathVerifier." << mJavaVerifierComputeMethodName
599 << "(args, target);\n\n";
600
601 mJava->indent() << "// Compare the expected outputs to the actual values returned by RS.\n";
602 mJava->indent() << "boolean valid = true;\n";
603 for (auto p : mAllInputsAndOutputs) {
604 if (p->isOutParameter) {
605 writeJavaVectorComparison(*p);
606 }
607 }
608
609 mJava->indent() << "if (!valid)";
610 mJava->startBlock();
611 mJava->indent() << "if (!errorFound)";
612 mJava->startBlock();
613 mJava->indent() << "errorFound = true;\n";
614
615 for (auto p : mAllInputsAndOutputs) {
616 if (p->isOutParameter) {
617 writeJavaAppendVectorOutputToMessage(*p);
618 } else {
619 writeJavaAppendVectorInputToMessage(*p);
620 }
621 }
622 mJava->indent() << "message.append(\"Errors at\");\n";
623 mJava->endBlock();
624
625 mJava->indent() << "message.append(\" [\");\n";
626 mJava->indent() << "message.append(Integer.toString(i));\n";
627 mJava->indent() << "message.append(\"]\");\n";
628
629 mJava->endBlock();
630 mJava->endBlock();
631
632 mJava->indent() << "assertFalse(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n";
633 mJava->indentPlus()
634 << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), errorFound);\n";
635
636 mJava->endBlock();
637 *mJava << "\n";
638 }
639
640
writeJavaCreateTarget() const641 void PermutationWriter::writeJavaCreateTarget() const {
642 string name = mPermutation.getName();
643
644 const char* functionType = "NORMAL";
645 size_t end = name.find('_');
646 if (end != string::npos) {
647 if (name.compare(0, end, "native") == 0) {
648 functionType = "NATIVE";
649 } else if (name.compare(0, end, "half") == 0) {
650 functionType = "HALF";
651 } else if (name.compare(0, end, "fast") == 0) {
652 functionType = "FAST";
653 }
654 }
655
656 string floatType = mReturnParam->specType;
657 const char* precisionStr = "";
658 if (floatType.compare("f16") == 0) {
659 precisionStr = "HALF";
660 } else if (floatType.compare("f32") == 0) {
661 precisionStr = "FLOAT";
662 } else if (floatType.compare("f64") == 0) {
663 precisionStr = "DOUBLE";
664 } else {
665 cerr << "Error. Unreachable. Return type is not floating point\n";
666 }
667
668 mJava->indent() << "Target target = new Target(Target.FunctionType." <<
669 functionType << ", Target.ReturnType." << precisionStr <<
670 ", relaxed);\n";
671 }
672
writeJavaVerifyMethodHeader() const673 void PermutationWriter::writeJavaVerifyMethodHeader() const {
674 mJava->indent() << "private void " << mJavaVerifyMethodName << "(";
675 for (auto p : mAllInputsAndOutputs) {
676 *mJava << "Allocation " << p->javaAllocName << ", ";
677 }
678 *mJava << "boolean relaxed)";
679 }
680
writeJavaArrayInitialization(const ParameterDefinition & p) const681 void PermutationWriter::writeJavaArrayInitialization(const ParameterDefinition& p) const {
682 mJava->indent() << p.javaBaseType << "[] " << p.javaArrayName << " = new " << p.javaBaseType
683 << "[INPUTSIZE * " << p.vectorWidth << "];\n";
684
685 /* For basic types, populate the array with values, to help understand failures. We have had
686 * bugs where the output buffer was all 0. We were not sure if there was a failed copy or
687 * the GPU driver was copying zeroes.
688 */
689 if (p.typeIndex >= 0) {
690 mJava->indent() << "Arrays.fill(" << p.javaArrayName << ", (" << TYPES[p.typeIndex].javaType
691 << ") 42);\n";
692 }
693
694 mJava->indent() << p.javaAllocName << ".copyTo(" << p.javaArrayName << ");\n";
695 }
696
writeJavaTestAndSetValid(const ParameterDefinition & p,const string & argsIndex,const string & actualIndex) const697 void PermutationWriter::writeJavaTestAndSetValid(const ParameterDefinition& p,
698 const string& argsIndex,
699 const string& actualIndex) const {
700 writeJavaTestOneValue(p, argsIndex, actualIndex);
701 mJava->startBlock();
702 mJava->indent() << "valid = false;\n";
703 mJava->endBlock();
704 }
705
writeJavaTestOneValue(const ParameterDefinition & p,const string & argsIndex,const string & actualIndex) const706 void PermutationWriter::writeJavaTestOneValue(const ParameterDefinition& p, const string& argsIndex,
707 const string& actualIndex) const {
708 string actualOut;
709 if (p.isFloat16Parameter()) {
710 // For Float16 values, the output needs to be converted to Double.
711 actualOut = "Float16Utils.convertFloat16ToDouble(" + p.javaArrayName + actualIndex + ")";
712 } else {
713 actualOut = p.javaArrayName + actualIndex;
714 }
715
716 mJava->indent() << "if (";
717 if (p.isFloatType) {
718 *mJava << "!args." << p.variableName << argsIndex << ".couldBe(" << actualOut;
719 const string s = mPermutation.getPrecisionLimit();
720 if (!s.empty()) {
721 *mJava << ", " << s;
722 }
723 *mJava << ")";
724 } else {
725 *mJava << "args." << p.variableName << argsIndex << " != " << p.javaArrayName
726 << actualIndex;
727 }
728
729 if (p.undefinedIfOutIsNan && mReturnParam) {
730 *mJava << " && !args." << mReturnParam->variableName << argsIndex << ".isNaN()";
731 }
732 *mJava << ")";
733 }
734
writeJavaVectorComparison(const ParameterDefinition & p) const735 void PermutationWriter::writeJavaVectorComparison(const ParameterDefinition& p) const {
736 if (p.mVectorSize == "1") {
737 writeJavaTestAndSetValid(p, "", "[i]");
738 } else {
739 mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
740 mJava->startBlock();
741 writeJavaTestAndSetValid(p, "[j]", "[i * " + p.vectorWidth + " + j]");
742 mJava->endBlock();
743 }
744 }
745
writeJavaAppendOutputToMessage(const ParameterDefinition & p,const string & argsIndex,const string & actualIndex,bool verifierValidates) const746 void PermutationWriter::writeJavaAppendOutputToMessage(const ParameterDefinition& p,
747 const string& argsIndex,
748 const string& actualIndex,
749 bool verifierValidates) const {
750 if (verifierValidates) {
751 mJava->indent() << "message.append(\"Output " << p.variableName << ": \");\n";
752 mJava->indent() << "appendVariableToMessage(message, args." << p.variableName << argsIndex
753 << ");\n";
754 writeJavaAppendNewLineToMessage();
755 if (p.isFloat16Parameter()) {
756 writeJavaAppendNewLineToMessage();
757 mJava->indent() << "message.append(\"Output " << p.variableName
758 << " (in double): \");\n";
759 mJava->indent() << "appendVariableToMessage(message, args." << p.doubleVariableName
760 << ");\n";
761 writeJavaAppendNewLineToMessage();
762 }
763 } else {
764 mJava->indent() << "message.append(\"Expected output " << p.variableName << ": \");\n";
765 mJava->indent() << "appendVariableToMessage(message, args." << p.variableName << argsIndex
766 << ");\n";
767 writeJavaAppendNewLineToMessage();
768
769 mJava->indent() << "message.append(\"Actual output " << p.variableName << ": \");\n";
770 mJava->indent() << "appendVariableToMessage(message, " << p.javaArrayName << actualIndex
771 << ");\n";
772
773 if (p.isFloat16Parameter()) {
774 writeJavaAppendNewLineToMessage();
775 mJava->indent() << "message.append(\"Actual output " << p.variableName
776 << " (in double): \");\n";
777 mJava->indent() << "appendVariableToMessage(message, Float16Utils.convertFloat16ToDouble("
778 << p.javaArrayName << actualIndex << "));\n";
779 }
780
781 writeJavaTestOneValue(p, argsIndex, actualIndex);
782 mJava->startBlock();
783 mJava->indent() << "message.append(\" FAIL\");\n";
784 mJava->endBlock();
785 writeJavaAppendNewLineToMessage();
786 }
787 }
788
writeJavaAppendInputToMessage(const ParameterDefinition & p,const string & actual) const789 void PermutationWriter::writeJavaAppendInputToMessage(const ParameterDefinition& p,
790 const string& actual) const {
791 mJava->indent() << "message.append(\"Input " << p.variableName << ": \");\n";
792 mJava->indent() << "appendVariableToMessage(message, " << actual << ");\n";
793 writeJavaAppendNewLineToMessage();
794 }
795
writeJavaAppendNewLineToMessage() const796 void PermutationWriter::writeJavaAppendNewLineToMessage() const {
797 mJava->indent() << "message.append(\"\\n\");\n";
798 }
799
writeJavaAppendVectorInputToMessage(const ParameterDefinition & p) const800 void PermutationWriter::writeJavaAppendVectorInputToMessage(const ParameterDefinition& p) const {
801 if (p.mVectorSize == "1") {
802 writeJavaAppendInputToMessage(p, p.javaArrayName + "[i]");
803 } else {
804 mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
805 mJava->startBlock();
806 writeJavaAppendInputToMessage(p, p.javaArrayName + "[i * " + p.vectorWidth + " + j]");
807 mJava->endBlock();
808 }
809 }
810
writeJavaAppendVectorOutputToMessage(const ParameterDefinition & p) const811 void PermutationWriter::writeJavaAppendVectorOutputToMessage(const ParameterDefinition& p) const {
812 if (p.mVectorSize == "1") {
813 writeJavaAppendOutputToMessage(p, "", "[i]", false);
814 } else {
815 mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
816 mJava->startBlock();
817 writeJavaAppendOutputToMessage(p, "[j]", "[i * " + p.vectorWidth + " + j]", false);
818 mJava->endBlock();
819 }
820 }
821
writeJavaCallToRs(bool relaxed,bool generateCallToVerifier) const822 void PermutationWriter::writeJavaCallToRs(bool relaxed, bool generateCallToVerifier) const {
823 string script = "script";
824 if (relaxed) {
825 script += "Relaxed";
826 }
827
828 mJava->indent() << "try";
829 mJava->startBlock();
830
831 for (auto p : mAllInputsAndOutputs) {
832 if (p->isOutParameter) {
833 writeJavaOutputAllocationDefinition(*p);
834 }
835 }
836
837 for (auto p : mPermutation.getParams()) {
838 if (p != mFirstInputParam) {
839 mJava->indent() << script << ".set_" << p->rsAllocName << "(" << p->javaAllocName
840 << ");\n";
841 }
842 }
843
844 mJava->indent() << script << ".forEach_" << mRsKernelName << "(";
845 bool needComma = false;
846 if (mFirstInputParam) {
847 *mJava << mFirstInputParam->javaAllocName;
848 needComma = true;
849 }
850 if (mReturnParam) {
851 if (needComma) {
852 *mJava << ", ";
853 }
854 *mJava << mReturnParam->variableName << ");\n";
855 }
856
857 if (generateCallToVerifier) {
858 mJava->indent() << mJavaVerifyMethodName << "(";
859 for (auto p : mAllInputsAndOutputs) {
860 *mJava << p->variableName << ", ";
861 }
862
863 if (relaxed) {
864 *mJava << "true";
865 } else {
866 *mJava << "false";
867 }
868 *mJava << ");\n";
869 }
870
871 // Generate code to destroy output Allocations.
872 for (auto p : mAllInputsAndOutputs) {
873 if (p->isOutParameter) {
874 mJava->indent() << p->javaAllocName << ".destroy();\n";
875 }
876 }
877
878 mJava->decreaseIndent();
879 mJava->indent() << "} catch (Exception e) {\n";
880 mJava->increaseIndent();
881 mJava->indent() << "throw new RSRuntimeException(\"RenderScript. Can't invoke forEach_"
882 << mRsKernelName << ": \" + e.toString());\n";
883 mJava->endBlock();
884 }
885
886 /* Write the section of the .rs file for this permutation.
887 *
888 * We communicate the extra input and output parameters via global allocations.
889 * For example, if we have a function that takes three arguments, two for input
890 * and one for output:
891 *
892 * start:
893 * name: gamn
894 * ret: float3
895 * arg: float3 a
896 * arg: int b
897 * arg: float3 *c
898 * end:
899 *
900 * We'll produce:
901 *
902 * rs_allocation gAllocInB;
903 * rs_allocation gAllocOutC;
904 *
905 * float3 __attribute__((kernel)) test_gamn_float3_int_float3(float3 inA, unsigned int x) {
906 * int inB;
907 * float3 outC;
908 * float2 out;
909 * inB = rsGetElementAt_int(gAllocInB, x);
910 * out = gamn(a, in_b, &outC);
911 * rsSetElementAt_float4(gAllocOutC, &outC, x);
912 * return out;
913 * }
914 *
915 * We avoid re-using x and y from the definition because these have reserved
916 * meanings in a .rs file.
917 */
writeRsSection(set<string> * rsAllocationsGenerated) const918 void PermutationWriter::writeRsSection(set<string>* rsAllocationsGenerated) const {
919 // Write the allocation declarations we'll need.
920 for (auto p : mPermutation.getParams()) {
921 // Don't need allocation for one input and one return value.
922 if (p != mFirstInputParam) {
923 writeRsAllocationDefinition(*p, rsAllocationsGenerated);
924 }
925 }
926 *mRs << "\n";
927
928 // Write the function header.
929 if (mReturnParam) {
930 *mRs << mReturnParam->rsType;
931 } else {
932 *mRs << "void";
933 }
934 *mRs << " __attribute__((kernel)) " << mRsKernelName;
935 *mRs << "(";
936 bool needComma = false;
937 if (mFirstInputParam) {
938 *mRs << mFirstInputParam->rsType << " " << mFirstInputParam->variableName;
939 needComma = true;
940 }
941 if (mPermutation.getOutputCount() > 1 || mPermutation.getInputCount() > 1) {
942 if (needComma) {
943 *mRs << ", ";
944 }
945 *mRs << "unsigned int x";
946 }
947 *mRs << ")";
948 mRs->startBlock();
949
950 // Write the local variable declarations and initializations.
951 for (auto p : mPermutation.getParams()) {
952 if (p == mFirstInputParam) {
953 continue;
954 }
955 mRs->indent() << p->rsType << " " << p->variableName;
956 if (p->isOutParameter) {
957 *mRs << " = 0;\n";
958 } else {
959 *mRs << " = rsGetElementAt_" << p->rsType << "(" << p->rsAllocName << ", x);\n";
960 }
961 }
962
963 // Write the function call.
964 if (mReturnParam) {
965 if (mPermutation.getOutputCount() > 1) {
966 mRs->indent() << mReturnParam->rsType << " " << mReturnParam->variableName << " = ";
967 } else {
968 mRs->indent() << "return ";
969 }
970 }
971 *mRs << mPermutation.getName() << "(";
972 needComma = false;
973 for (auto p : mPermutation.getParams()) {
974 if (needComma) {
975 *mRs << ", ";
976 }
977 if (p->isOutParameter) {
978 *mRs << "&";
979 }
980 *mRs << p->variableName;
981 needComma = true;
982 }
983 *mRs << ");\n";
984
985 if (mPermutation.getOutputCount() > 1) {
986 // Write setting the extra out parameters into the allocations.
987 for (auto p : mPermutation.getParams()) {
988 if (p->isOutParameter) {
989 mRs->indent() << "rsSetElementAt_" << p->rsType << "(" << p->rsAllocName << ", ";
990 // Check if we need to use '&' for this type of argument.
991 char lastChar = p->variableName.back();
992 if (lastChar >= '0' && lastChar <= '9') {
993 *mRs << "&";
994 }
995 *mRs << p->variableName << ", x);\n";
996 }
997 }
998 if (mReturnParam) {
999 mRs->indent() << "return " << mReturnParam->variableName << ";\n";
1000 }
1001 }
1002 mRs->endBlock();
1003 }
1004
writeRsAllocationDefinition(const ParameterDefinition & param,set<string> * rsAllocationsGenerated) const1005 void PermutationWriter::writeRsAllocationDefinition(const ParameterDefinition& param,
1006 set<string>* rsAllocationsGenerated) const {
1007 if (!testAndSet(param.rsAllocName, rsAllocationsGenerated)) {
1008 *mRs << "rs_allocation " << param.rsAllocName << ";\n";
1009 }
1010 }
1011
1012 // Open the mJavaFile and writes the header.
startJavaFile(GeneratedFile * file,const string & directory,const string & testName,const string & relaxedTestName)1013 static bool startJavaFile(GeneratedFile* file, const string& directory,
1014 const string& testName,
1015 const string& relaxedTestName) {
1016 const string fileName = testName + ".java";
1017 if (!file->start(directory, fileName)) {
1018 return false;
1019 }
1020 file->writeNotices();
1021
1022 *file << "package android.renderscript.cts;\n\n";
1023
1024 *file << "import android.renderscript.Allocation;\n";
1025 *file << "import android.renderscript.RSRuntimeException;\n";
1026 *file << "import android.renderscript.Element;\n";
1027 *file << "import android.renderscript.cts.Target;\n\n";
1028 *file << "import java.util.Arrays;\n\n";
1029
1030 *file << "public class " << testName << " extends RSBaseCompute";
1031 file->startBlock(); // The corresponding endBlock() is in finishJavaFile()
1032 *file << "\n";
1033
1034 file->indent() << "private ScriptC_" << testName << " script;\n";
1035 file->indent() << "private ScriptC_" << relaxedTestName << " scriptRelaxed;\n\n";
1036
1037 file->indent() << "@Override\n";
1038 file->indent() << "protected void setUp() throws Exception";
1039 file->startBlock();
1040
1041 file->indent() << "super.setUp();\n";
1042 file->indent() << "script = new ScriptC_" << testName << "(mRS);\n";
1043 file->indent() << "scriptRelaxed = new ScriptC_" << relaxedTestName << "(mRS);\n";
1044
1045 file->endBlock();
1046 *file << "\n";
1047
1048 file->indent() << "@Override\n";
1049 file->indent() << "protected void tearDown() throws Exception";
1050 file->startBlock();
1051
1052 file->indent() << "script.destroy();\n";
1053 file->indent() << "scriptRelaxed.destroy();\n";
1054 file->indent() << "super.tearDown();\n";
1055
1056 file->endBlock();
1057 *file << "\n";
1058
1059 return true;
1060 }
1061
1062 // Write the test method that calls all the generated Check methods.
finishJavaFile(GeneratedFile * file,const Function & function,const vector<string> & javaCheckMethods)1063 static void finishJavaFile(GeneratedFile* file, const Function& function,
1064 const vector<string>& javaCheckMethods) {
1065 file->indent() << "public void test" << function.getCapitalizedName() << "()";
1066 file->startBlock();
1067 for (auto m : javaCheckMethods) {
1068 file->indent() << m << "();\n";
1069 }
1070 file->endBlock();
1071
1072 file->endBlock();
1073 }
1074
1075 // Open the script file and write its header.
startRsFile(GeneratedFile * file,const string & directory,const string & testName)1076 static bool startRsFile(GeneratedFile* file, const string& directory,
1077 const string& testName) {
1078 string fileName = testName + ".rs";
1079 if (!file->start(directory, fileName)) {
1080 return false;
1081 }
1082 file->writeNotices();
1083
1084 *file << "#pragma version(1)\n";
1085 *file << "#pragma rs java_package_name(android.renderscript.cts)\n\n";
1086 return true;
1087 }
1088
1089 // Write the entire *Relaxed.rs test file, as it only depends on the name.
writeRelaxedRsFile(const string & directory,const string & testName,const string & relaxedTestName)1090 static bool writeRelaxedRsFile(const string& directory, const string& testName,
1091 const string& relaxedTestName) {
1092 string name = relaxedTestName + ".rs";
1093
1094 GeneratedFile file;
1095 if (!file.start(directory, name)) {
1096 return false;
1097 }
1098 file.writeNotices();
1099
1100 file << "#include \"" << testName << ".rs\"\n";
1101 file << "#pragma rs_fp_relaxed\n";
1102 file.close();
1103 return true;
1104 }
1105
1106 /* Write the .java and the two .rs test files. versionOfTestFiles is used to restrict which API
1107 * to test.
1108 */
writeTestFilesForFunction(const Function & function,const string & directory,unsigned int versionOfTestFiles)1109 static bool writeTestFilesForFunction(const Function& function, const string& directory,
1110 unsigned int versionOfTestFiles) {
1111 // Avoid creating empty files if we're not testing this function.
1112 if (!needTestFiles(function, versionOfTestFiles)) {
1113 return true;
1114 }
1115
1116 const string testName = "Test" + function.getCapitalizedName();
1117 const string relaxedTestName = testName + "Relaxed";
1118
1119 if (!writeRelaxedRsFile(directory, testName, relaxedTestName)) {
1120 return false;
1121 }
1122
1123 GeneratedFile rsFile; // The Renderscript test file we're generating.
1124 GeneratedFile javaFile; // The Jave test file we're generating.
1125 if (!startRsFile(&rsFile, directory, testName)) {
1126 return false;
1127 }
1128
1129 if (!startJavaFile(&javaFile, directory, testName, relaxedTestName)) {
1130 return false;
1131 }
1132
1133 /* We keep track of the allocations generated in the .rs file and the argument classes defined
1134 * in the Java file, as we share these between the functions created for each specification.
1135 */
1136 set<string> rsAllocationsGenerated;
1137 set<string> javaGeneratedArgumentClasses;
1138 // Lines of Java code to invoke the check methods.
1139 vector<string> javaCheckMethods;
1140
1141 for (auto spec : function.getSpecifications()) {
1142 if (spec->hasTests(versionOfTestFiles)) {
1143 for (auto permutation : spec->getPermutations()) {
1144 PermutationWriter w(*permutation, &rsFile, &javaFile);
1145 w.writeRsSection(&rsAllocationsGenerated);
1146 w.writeJavaSection(&javaGeneratedArgumentClasses);
1147
1148 // Store the check method to be called.
1149 javaCheckMethods.push_back(w.getJavaCheckMethodName());
1150 }
1151 }
1152 }
1153
1154 finishJavaFile(&javaFile, function, javaCheckMethods);
1155 // There's no work to wrap-up in the .rs file.
1156
1157 rsFile.close();
1158 javaFile.close();
1159 return true;
1160 }
1161
generateTestFiles(const string & directory,unsigned int versionOfTestFiles)1162 bool generateTestFiles(const string& directory, unsigned int versionOfTestFiles) {
1163 bool success = true;
1164 for (auto f : systemSpecification.getFunctions()) {
1165 if (!writeTestFilesForFunction(*f.second, directory, versionOfTestFiles)) {
1166 success = false;
1167 }
1168 }
1169 return success;
1170 }
1171