• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,int versionOfTestFiles)49 static bool needTestFiles(const Function& function, 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 method header of the verify function.
132     void writeJavaVerifyMethodHeader() const;
133 
134     // Generate codes that copies the content of an allocation to an array.
135     void writeJavaArrayInitialization(const ParameterDefinition& p) const;
136 
137     // Generate code that tests one value returned from the script.
138     void writeJavaTestAndSetValid(const ParameterDefinition& p, const string& argsIndex,
139                                   const string& actualIndex) const;
140     void writeJavaTestOneValue(const ParameterDefinition& p, const string& argsIndex,
141                                const string& actualIndex) const;
142     // For test:vector cases, generate code that compares returned vector vs. expected value.
143     void writeJavaVectorComparison(const ParameterDefinition& p) const;
144 
145     // Muliple functions that generates code to build the error message if an error is found.
146     void writeJavaAppendOutputToMessage(const ParameterDefinition& p, const string& argsIndex,
147                                         const string& actualIndex, bool verifierValidates) const;
148     void writeJavaAppendInputToMessage(const ParameterDefinition& p, const string& actual) const;
149     void writeJavaAppendNewLineToMessage() const;
150     void writeJavaAppendVectorInputToMessage(const ParameterDefinition& p) const;
151     void writeJavaAppendVectorOutputToMessage(const ParameterDefinition& p) const;
152 
153     // Generate the set of instructions to call the script.
154     void writeJavaCallToRs(bool relaxed, bool generateCallToVerifier) const;
155 
156     // Write an allocation definition if not already emitted in the .rs file.
157     void writeRsAllocationDefinition(const ParameterDefinition& param,
158                                      set<string>* rsAllocationsGenerated) const;
159 
160 public:
161     /* NOTE: We keep pointers to the permutation and the files.  This object should not
162      * outlive the arguments.
163      */
164     PermutationWriter(FunctionPermutation& permutation, GeneratedFile* rsFile,
165                       GeneratedFile* javaFile);
getJavaCheckMethodName() const166     string getJavaCheckMethodName() const { return mJavaCheckMethodName; }
167 
168     // Write the script test function for this permutation.
169     void writeRsSection(set<string>* rsAllocationsGenerated) const;
170     // Write the section of the Java code that calls the script and validates the results
171     void writeJavaSection(set<string>* javaGeneratedArgumentClasses) const;
172 };
173 
PermutationWriter(FunctionPermutation & permutation,GeneratedFile * rsFile,GeneratedFile * javaFile)174 PermutationWriter::PermutationWriter(FunctionPermutation& permutation, GeneratedFile* rsFile,
175                                      GeneratedFile* javaFile)
176     : mPermutation(permutation),
177       mRs(rsFile),
178       mJava(javaFile),
179       mReturnParam(nullptr),
180       mFirstInputParam(nullptr) {
181     mRsKernelName = "test" + capitalize(permutation.getName());
182 
183     mJavaArgumentsClassName = "Arguments";
184     mJavaArgumentsNClassName = "Arguments";
185     const string trunk = capitalize(permutation.getNameTrunk());
186     mJavaCheckMethodName = "check" + trunk;
187     mJavaVerifyMethodName = "verifyResults" + trunk;
188 
189     for (auto p : permutation.getParams()) {
190         mAllInputsAndOutputs.push_back(p);
191         if (mFirstInputParam == nullptr && !p->isOutParameter) {
192             mFirstInputParam = p;
193         }
194     }
195     mReturnParam = permutation.getReturn();
196     if (mReturnParam) {
197         mAllInputsAndOutputs.push_back(mReturnParam);
198     }
199 
200     for (auto p : mAllInputsAndOutputs) {
201         const string capitalizedRsType = capitalize(p->rsType);
202         const string capitalizedBaseType = capitalize(p->rsBaseType);
203         mRsKernelName += capitalizedRsType;
204         mJavaArgumentsClassName += capitalizedBaseType;
205         mJavaArgumentsNClassName += capitalizedBaseType;
206         if (p->mVectorSize != "1") {
207             mJavaArgumentsNClassName += "N";
208         }
209         mJavaCheckMethodName += capitalizedRsType;
210         mJavaVerifyMethodName += capitalizedRsType;
211     }
212     mJavaVerifierComputeMethodName = "compute" + trunk;
213     mJavaVerifierVerifyMethodName = "verify" + trunk;
214 }
215 
writeJavaSection(set<string> * javaGeneratedArgumentClasses) const216 void PermutationWriter::writeJavaSection(set<string>* javaGeneratedArgumentClasses) const {
217     // By default, we test the results using item by item comparison.
218     const string test = mPermutation.getTest();
219     if (test == "scalar" || test == "limited") {
220         writeJavaArgumentClass(true, javaGeneratedArgumentClasses);
221         writeJavaCheckMethod(true);
222         writeJavaVerifyScalarMethod(false);
223     } else if (test == "custom") {
224         writeJavaArgumentClass(true, javaGeneratedArgumentClasses);
225         writeJavaCheckMethod(true);
226         writeJavaVerifyScalarMethod(true);
227     } else if (test == "vector") {
228         writeJavaArgumentClass(false, javaGeneratedArgumentClasses);
229         writeJavaCheckMethod(true);
230         writeJavaVerifyVectorMethod();
231     } else if (test == "noverify") {
232         writeJavaCheckMethod(false);
233     }
234 }
235 
writeJavaArgumentClass(bool scalar,set<string> * javaGeneratedArgumentClasses) const236 void PermutationWriter::writeJavaArgumentClass(bool scalar,
237                                                set<string>* javaGeneratedArgumentClasses) const {
238     string name;
239     if (scalar) {
240         name = mJavaArgumentsClassName;
241     } else {
242         name = mJavaArgumentsNClassName;
243     }
244 
245     // Make sure we have not generated the argument class already.
246     if (!testAndSet(name, javaGeneratedArgumentClasses)) {
247         mJava->indent() << "public class " << name;
248         mJava->startBlock();
249 
250         for (auto p : mAllInputsAndOutputs) {
251             mJava->indent() << "public ";
252             if (p->isOutParameter && p->isFloatType && mPermutation.getTest() != "custom") {
253                 *mJava << "Target.Floaty";
254             } else {
255                 *mJava << p->javaBaseType;
256             }
257             if (!scalar && p->mVectorSize != "1") {
258                 *mJava << "[]";
259             }
260             *mJava << " " << p->variableName << ";\n";
261         }
262         mJava->endBlock();
263         *mJava << "\n";
264     }
265 }
266 
writeJavaCheckMethod(bool generateCallToVerifier) const267 void PermutationWriter::writeJavaCheckMethod(bool generateCallToVerifier) const {
268     mJava->indent() << "private void " << mJavaCheckMethodName << "()";
269     mJava->startBlock();
270 
271     // Generate the input allocations and initialization.
272     for (auto p : mAllInputsAndOutputs) {
273         if (!p->isOutParameter) {
274             writeJavaInputAllocationDefinition(*p);
275         }
276     }
277     // Generate code to enforce ordering between two allocations if needed.
278     for (auto p : mAllInputsAndOutputs) {
279         if (!p->isOutParameter && !p->smallerParameter.empty()) {
280             string smallerAlloc = "in" + capitalize(p->smallerParameter);
281             mJava->indent() << "enforceOrdering(" << smallerAlloc << ", " << p->javaAllocName
282                             << ");\n";
283         }
284     }
285 
286     // Generate code to check the full and relaxed scripts.
287     writeJavaCallToRs(false, generateCallToVerifier);
288     writeJavaCallToRs(true, generateCallToVerifier);
289 
290     mJava->endBlock();
291     *mJava << "\n";
292 }
293 
writeJavaInputAllocationDefinition(const ParameterDefinition & param) const294 void PermutationWriter::writeJavaInputAllocationDefinition(const ParameterDefinition& param) const {
295     string dataType;
296     char vectorSize;
297     convertToRsType(param.rsType, &dataType, &vectorSize);
298 
299     const string seed = hashString(mJavaCheckMethodName + param.javaAllocName);
300     mJava->indent() << "Allocation " << param.javaAllocName << " = ";
301     if (param.compatibleTypeIndex >= 0) {
302         if (TYPES[param.typeIndex].kind == FLOATING_POINT) {
303             writeJavaRandomCompatibleFloatAllocation(dataType, seed, vectorSize,
304                                                      TYPES[param.compatibleTypeIndex],
305                                                      TYPES[param.typeIndex]);
306         } else {
307             writeJavaRandomCompatibleIntegerAllocation(dataType, seed, vectorSize,
308                                                        TYPES[param.compatibleTypeIndex],
309                                                        TYPES[param.typeIndex]);
310         }
311     } else if (!param.minValue.empty()) {
312         *mJava << "createRandomFloatAllocation(mRS, Element.DataType." << dataType << ", "
313                << vectorSize << ", " << seed << ", " << param.minValue << ", " << param.maxValue
314                << ")";
315     } else {
316         /* TODO Instead of passing always false, check whether we are doing a limited test.
317          * Use instead: (mPermutation.getTest() == "limited" ? "false" : "true")
318          */
319         *mJava << "createRandomAllocation(mRS, Element.DataType." << dataType << ", " << vectorSize
320                << ", " << seed << ", false)";
321     }
322     *mJava << ";\n";
323 }
324 
writeJavaRandomCompatibleFloatAllocation(const string & dataType,const string & seed,char vectorSize,const NumericalType & compatibleType,const NumericalType & generatedType) const325 void PermutationWriter::writeJavaRandomCompatibleFloatAllocation(
326             const string& dataType, const string& seed, char vectorSize,
327             const NumericalType& compatibleType, const NumericalType& generatedType) const {
328     *mJava << "createRandomFloatAllocation"
329            << "(mRS, Element.DataType." << dataType << ", " << vectorSize << ", " << seed << ", ";
330     double minValue = 0.0;
331     double maxValue = 0.0;
332     switch (compatibleType.kind) {
333         case FLOATING_POINT: {
334             // We're generating floating point values.  We just worry about the exponent.
335             // Subtract 1 for the exponent sign.
336             int bits = min(compatibleType.exponentBits, generatedType.exponentBits) - 1;
337             maxValue = ldexp(0.95, (1 << bits) - 1);
338             minValue = -maxValue;
339             break;
340         }
341         case UNSIGNED_INTEGER:
342             maxValue = maxDoubleForInteger(compatibleType.significantBits,
343                                            generatedType.significantBits);
344             minValue = 0.0;
345             break;
346         case SIGNED_INTEGER:
347             maxValue = maxDoubleForInteger(compatibleType.significantBits,
348                                            generatedType.significantBits);
349             minValue = -maxValue - 1.0;
350             break;
351     }
352     *mJava << scientific << std::setprecision(19);
353     *mJava << minValue << ", " << maxValue << ")";
354     mJava->unsetf(ios_base::floatfield);
355 }
356 
writeJavaRandomCompatibleIntegerAllocation(const string & dataType,const string & seed,char vectorSize,const NumericalType & compatibleType,const NumericalType & generatedType) const357 void PermutationWriter::writeJavaRandomCompatibleIntegerAllocation(
358             const string& dataType, const string& seed, char vectorSize,
359             const NumericalType& compatibleType, const NumericalType& generatedType) const {
360     *mJava << "createRandomIntegerAllocation"
361            << "(mRS, Element.DataType." << dataType << ", " << vectorSize << ", " << seed << ", ";
362 
363     if (compatibleType.kind == FLOATING_POINT) {
364         // Currently, all floating points can take any number we generate.
365         bool isSigned = generatedType.kind == SIGNED_INTEGER;
366         *mJava << (isSigned ? "true" : "false") << ", " << generatedType.significantBits;
367     } else {
368         bool isSigned =
369                     compatibleType.kind == SIGNED_INTEGER && generatedType.kind == SIGNED_INTEGER;
370         *mJava << (isSigned ? "true" : "false") << ", "
371                << min(compatibleType.significantBits, generatedType.significantBits);
372     }
373     *mJava << ")";
374 }
375 
writeJavaOutputAllocationDefinition(const ParameterDefinition & param) const376 void PermutationWriter::writeJavaOutputAllocationDefinition(
377             const ParameterDefinition& param) const {
378     string dataType;
379     char vectorSize;
380     convertToRsType(param.rsType, &dataType, &vectorSize);
381     mJava->indent() << "Allocation " << param.javaAllocName << " = Allocation.createSized(mRS, "
382                     << "getElement(mRS, Element.DataType." << dataType << ", " << vectorSize
383                     << "), INPUTSIZE);\n";
384 }
385 
writeJavaVerifyScalarMethod(bool verifierValidates) const386 void PermutationWriter::writeJavaVerifyScalarMethod(bool verifierValidates) const {
387     writeJavaVerifyMethodHeader();
388     mJava->startBlock();
389 
390     string vectorSize = "1";
391     for (auto p : mAllInputsAndOutputs) {
392         writeJavaArrayInitialization(*p);
393         if (p->mVectorSize != "1" && p->mVectorSize != vectorSize) {
394             if (vectorSize == "1") {
395                 vectorSize = p->mVectorSize;
396             } else {
397                 cerr << "Error.  Had vector " << vectorSize << " and " << p->mVectorSize << "\n";
398             }
399         }
400     }
401 
402     mJava->indent() << "StringBuilder message = new StringBuilder();\n";
403     mJava->indent() << "boolean errorFound = false;\n";
404     mJava->indent() << "for (int i = 0; i < INPUTSIZE; i++)";
405     mJava->startBlock();
406 
407     mJava->indent() << "for (int j = 0; j < " << vectorSize << " ; j++)";
408     mJava->startBlock();
409 
410     mJava->indent() << "// Extract the inputs.\n";
411     mJava->indent() << mJavaArgumentsClassName << " args = new " << mJavaArgumentsClassName
412                     << "();\n";
413     for (auto p : mAllInputsAndOutputs) {
414         if (!p->isOutParameter) {
415             mJava->indent() << "args." << p->variableName << " = " << p->javaArrayName << "[i";
416             if (p->vectorWidth != "1") {
417                 *mJava << " * " << p->vectorWidth << " + j";
418             }
419             *mJava << "];\n";
420         }
421     }
422     const bool hasFloat = mPermutation.hasFloatAnswers();
423     if (verifierValidates) {
424         mJava->indent() << "// Extract the outputs.\n";
425         for (auto p : mAllInputsAndOutputs) {
426             if (p->isOutParameter) {
427                 mJava->indent() << "args." << p->variableName << " = " << p->javaArrayName
428                                 << "[i * " << p->vectorWidth << " + j];\n";
429             }
430         }
431         mJava->indent() << "// Ask the CoreMathVerifier to validate.\n";
432         if (hasFloat) {
433             mJava->indent() << "Target target = new Target(relaxed);\n";
434         }
435         mJava->indent() << "String errorMessage = CoreMathVerifier."
436                         << mJavaVerifierVerifyMethodName << "(args";
437         if (hasFloat) {
438             *mJava << ", target";
439         }
440         *mJava << ");\n";
441         mJava->indent() << "boolean valid = errorMessage == null;\n";
442     } else {
443         mJava->indent() << "// Figure out what the outputs should have been.\n";
444         if (hasFloat) {
445             mJava->indent() << "Target target = new Target(relaxed);\n";
446         }
447         mJava->indent() << "CoreMathVerifier." << mJavaVerifierComputeMethodName << "(args";
448         if (hasFloat) {
449             *mJava << ", target";
450         }
451         *mJava << ");\n";
452         mJava->indent() << "// Validate the outputs.\n";
453         mJava->indent() << "boolean valid = true;\n";
454         for (auto p : mAllInputsAndOutputs) {
455             if (p->isOutParameter) {
456                 writeJavaTestAndSetValid(*p, "", "[i * " + p->vectorWidth + " + j]");
457             }
458         }
459     }
460 
461     mJava->indent() << "if (!valid)";
462     mJava->startBlock();
463     mJava->indent() << "if (!errorFound)";
464     mJava->startBlock();
465     mJava->indent() << "errorFound = true;\n";
466 
467     for (auto p : mAllInputsAndOutputs) {
468         if (p->isOutParameter) {
469             writeJavaAppendOutputToMessage(*p, "", "[i * " + p->vectorWidth + " + j]",
470                                            verifierValidates);
471         } else {
472             writeJavaAppendInputToMessage(*p, "args." + p->variableName);
473         }
474     }
475     if (verifierValidates) {
476         mJava->indent() << "message.append(errorMessage);\n";
477     }
478     mJava->indent() << "message.append(\"Errors at\");\n";
479     mJava->endBlock();
480 
481     mJava->indent() << "message.append(\" [\");\n";
482     mJava->indent() << "message.append(Integer.toString(i));\n";
483     mJava->indent() << "message.append(\", \");\n";
484     mJava->indent() << "message.append(Integer.toString(j));\n";
485     mJava->indent() << "message.append(\"]\");\n";
486 
487     mJava->endBlock();
488     mJava->endBlock();
489     mJava->endBlock();
490 
491     mJava->indent() << "assertFalse(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n";
492     mJava->indentPlus()
493                 << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), errorFound);\n";
494 
495     mJava->endBlock();
496     *mJava << "\n";
497 }
498 
writeJavaVerifyVectorMethod() const499 void PermutationWriter::writeJavaVerifyVectorMethod() const {
500     writeJavaVerifyMethodHeader();
501     mJava->startBlock();
502 
503     for (auto p : mAllInputsAndOutputs) {
504         writeJavaArrayInitialization(*p);
505     }
506     mJava->indent() << "StringBuilder message = new StringBuilder();\n";
507     mJava->indent() << "boolean errorFound = false;\n";
508     mJava->indent() << "for (int i = 0; i < INPUTSIZE; i++)";
509     mJava->startBlock();
510 
511     mJava->indent() << mJavaArgumentsNClassName << " args = new " << mJavaArgumentsNClassName
512                     << "();\n";
513 
514     mJava->indent() << "// Create the appropriate sized arrays in args\n";
515     for (auto p : mAllInputsAndOutputs) {
516         if (p->mVectorSize != "1") {
517             string type = p->javaBaseType;
518             if (p->isOutParameter && p->isFloatType) {
519                 type = "Target.Floaty";
520             }
521             mJava->indent() << "args." << p->variableName << " = new " << type << "["
522                             << p->mVectorSize << "];\n";
523         }
524     }
525 
526     mJava->indent() << "// Fill args with the input values\n";
527     for (auto p : mAllInputsAndOutputs) {
528         if (!p->isOutParameter) {
529             if (p->mVectorSize == "1") {
530                 mJava->indent() << "args." << p->variableName << " = " << p->javaArrayName << "[i]"
531                                 << ";\n";
532             } else {
533                 mJava->indent() << "for (int j = 0; j < " << p->mVectorSize << " ; j++)";
534                 mJava->startBlock();
535                 mJava->indent() << "args." << p->variableName << "[j] = "
536                                 << p->javaArrayName << "[i * " << p->vectorWidth << " + j]"
537                                 << ";\n";
538                 mJava->endBlock();
539             }
540         }
541     }
542     mJava->indent() << "Target target = new Target(relaxed);\n";
543     mJava->indent() << "CoreMathVerifier." << mJavaVerifierComputeMethodName
544                     << "(args, target);\n\n";
545 
546     mJava->indent() << "// Compare the expected outputs to the actual values returned by RS.\n";
547     mJava->indent() << "boolean valid = true;\n";
548     for (auto p : mAllInputsAndOutputs) {
549         if (p->isOutParameter) {
550             writeJavaVectorComparison(*p);
551         }
552     }
553 
554     mJava->indent() << "if (!valid)";
555     mJava->startBlock();
556     mJava->indent() << "if (!errorFound)";
557     mJava->startBlock();
558     mJava->indent() << "errorFound = true;\n";
559 
560     for (auto p : mAllInputsAndOutputs) {
561         if (p->isOutParameter) {
562             writeJavaAppendVectorOutputToMessage(*p);
563         } else {
564             writeJavaAppendVectorInputToMessage(*p);
565         }
566     }
567     mJava->indent() << "message.append(\"Errors at\");\n";
568     mJava->endBlock();
569 
570     mJava->indent() << "message.append(\" [\");\n";
571     mJava->indent() << "message.append(Integer.toString(i));\n";
572     mJava->indent() << "message.append(\"]\");\n";
573 
574     mJava->endBlock();
575     mJava->endBlock();
576 
577     mJava->indent() << "assertFalse(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n";
578     mJava->indentPlus()
579                 << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), errorFound);\n";
580 
581     mJava->endBlock();
582     *mJava << "\n";
583 }
584 
writeJavaVerifyMethodHeader() const585 void PermutationWriter::writeJavaVerifyMethodHeader() const {
586     mJava->indent() << "private void " << mJavaVerifyMethodName << "(";
587     for (auto p : mAllInputsAndOutputs) {
588         *mJava << "Allocation " << p->javaAllocName << ", ";
589     }
590     *mJava << "boolean relaxed)";
591 }
592 
writeJavaArrayInitialization(const ParameterDefinition & p) const593 void PermutationWriter::writeJavaArrayInitialization(const ParameterDefinition& p) const {
594     mJava->indent() << p.javaBaseType << "[] " << p.javaArrayName << " = new " << p.javaBaseType
595                     << "[INPUTSIZE * " << p.vectorWidth << "];\n";
596 
597     /* For basic types, populate the array with values, to help understand failures.  We have had
598      * bugs where the output buffer was all 0.  We were not sure if there was a failed copy or
599      * the GPU driver was copying zeroes.
600      */
601     if (p.typeIndex >= 0) {
602         mJava->indent() << "Arrays.fill(" << p.javaArrayName << ", (" << TYPES[p.typeIndex].javaType
603                         << ") 42);\n";
604     }
605 
606     mJava->indent() << p.javaAllocName << ".copyTo(" << p.javaArrayName << ");\n";
607 }
608 
writeJavaTestAndSetValid(const ParameterDefinition & p,const string & argsIndex,const string & actualIndex) const609 void PermutationWriter::writeJavaTestAndSetValid(const ParameterDefinition& p,
610                                                  const string& argsIndex,
611                                                  const string& actualIndex) const {
612     writeJavaTestOneValue(p, argsIndex, actualIndex);
613     mJava->startBlock();
614     mJava->indent() << "valid = false;\n";
615     mJava->endBlock();
616 }
617 
writeJavaTestOneValue(const ParameterDefinition & p,const string & argsIndex,const string & actualIndex) const618 void PermutationWriter::writeJavaTestOneValue(const ParameterDefinition& p, const string& argsIndex,
619                                               const string& actualIndex) const {
620     mJava->indent() << "if (";
621     if (p.isFloatType) {
622         *mJava << "!args." << p.variableName << argsIndex << ".couldBe(" << p.javaArrayName
623                << actualIndex;
624         const string s = mPermutation.getPrecisionLimit();
625         if (!s.empty()) {
626             *mJava << ", " << s;
627         }
628         *mJava << ")";
629     } else {
630         *mJava << "args." << p.variableName << argsIndex << " != " << p.javaArrayName
631                << actualIndex;
632     }
633 
634     if (p.undefinedIfOutIsNan && mReturnParam) {
635         *mJava << " && !args." << mReturnParam->variableName << argsIndex << ".isNaN()";
636     }
637     *mJava << ")";
638 }
639 
writeJavaVectorComparison(const ParameterDefinition & p) const640 void PermutationWriter::writeJavaVectorComparison(const ParameterDefinition& p) const {
641     if (p.mVectorSize == "1") {
642         writeJavaTestAndSetValid(p, "", "[i]");
643     } else {
644         mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
645         mJava->startBlock();
646         writeJavaTestAndSetValid(p, "[j]", "[i * " + p.vectorWidth + " + j]");
647         mJava->endBlock();
648     }
649 }
650 
writeJavaAppendOutputToMessage(const ParameterDefinition & p,const string & argsIndex,const string & actualIndex,bool verifierValidates) const651 void PermutationWriter::writeJavaAppendOutputToMessage(const ParameterDefinition& p,
652                                                        const string& argsIndex,
653                                                        const string& actualIndex,
654                                                        bool verifierValidates) const {
655     if (verifierValidates) {
656         mJava->indent() << "message.append(\"Output " << p.variableName << ": \");\n";
657         mJava->indent() << "appendVariableToMessage(message, args." << p.variableName << argsIndex
658                         << ");\n";
659         writeJavaAppendNewLineToMessage();
660     } else {
661         mJava->indent() << "message.append(\"Expected output " << p.variableName << ": \");\n";
662         mJava->indent() << "appendVariableToMessage(message, args." << p.variableName << argsIndex
663                         << ");\n";
664         writeJavaAppendNewLineToMessage();
665 
666         mJava->indent() << "message.append(\"Actual   output " << p.variableName << ": \");\n";
667         mJava->indent() << "appendVariableToMessage(message, " << p.javaArrayName << actualIndex
668                         << ");\n";
669 
670         writeJavaTestOneValue(p, argsIndex, actualIndex);
671         mJava->startBlock();
672         mJava->indent() << "message.append(\" FAIL\");\n";
673         mJava->endBlock();
674         writeJavaAppendNewLineToMessage();
675     }
676 }
677 
writeJavaAppendInputToMessage(const ParameterDefinition & p,const string & actual) const678 void PermutationWriter::writeJavaAppendInputToMessage(const ParameterDefinition& p,
679                                                       const string& actual) const {
680     mJava->indent() << "message.append(\"Input " << p.variableName << ": \");\n";
681     mJava->indent() << "appendVariableToMessage(message, " << actual << ");\n";
682     writeJavaAppendNewLineToMessage();
683 }
684 
writeJavaAppendNewLineToMessage() const685 void PermutationWriter::writeJavaAppendNewLineToMessage() const {
686     mJava->indent() << "message.append(\"\\n\");\n";
687 }
688 
writeJavaAppendVectorInputToMessage(const ParameterDefinition & p) const689 void PermutationWriter::writeJavaAppendVectorInputToMessage(const ParameterDefinition& p) const {
690     if (p.mVectorSize == "1") {
691         writeJavaAppendInputToMessage(p, p.javaArrayName + "[i]");
692     } else {
693         mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
694         mJava->startBlock();
695         writeJavaAppendInputToMessage(p, p.javaArrayName + "[i * " + p.vectorWidth + " + j]");
696         mJava->endBlock();
697     }
698 }
699 
writeJavaAppendVectorOutputToMessage(const ParameterDefinition & p) const700 void PermutationWriter::writeJavaAppendVectorOutputToMessage(const ParameterDefinition& p) const {
701     if (p.mVectorSize == "1") {
702         writeJavaAppendOutputToMessage(p, "", "[i]", false);
703     } else {
704         mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
705         mJava->startBlock();
706         writeJavaAppendOutputToMessage(p, "[j]", "[i * " + p.vectorWidth + " + j]", false);
707         mJava->endBlock();
708     }
709 }
710 
writeJavaCallToRs(bool relaxed,bool generateCallToVerifier) const711 void PermutationWriter::writeJavaCallToRs(bool relaxed, bool generateCallToVerifier) const {
712     string script = "script";
713     if (relaxed) {
714         script += "Relaxed";
715     }
716 
717     mJava->indent() << "try";
718     mJava->startBlock();
719 
720     for (auto p : mAllInputsAndOutputs) {
721         if (p->isOutParameter) {
722             writeJavaOutputAllocationDefinition(*p);
723         }
724     }
725 
726     for (auto p : mPermutation.getParams()) {
727         if (p != mFirstInputParam) {
728             mJava->indent() << script << ".set_" << p->rsAllocName << "(" << p->javaAllocName
729                             << ");\n";
730         }
731     }
732 
733     mJava->indent() << script << ".forEach_" << mRsKernelName << "(";
734     bool needComma = false;
735     if (mFirstInputParam) {
736         *mJava << mFirstInputParam->javaAllocName;
737         needComma = true;
738     }
739     if (mReturnParam) {
740         if (needComma) {
741             *mJava << ", ";
742         }
743         *mJava << mReturnParam->variableName << ");\n";
744     }
745 
746     if (generateCallToVerifier) {
747         mJava->indent() << mJavaVerifyMethodName << "(";
748         for (auto p : mAllInputsAndOutputs) {
749             *mJava << p->variableName << ", ";
750         }
751 
752         if (relaxed) {
753             *mJava << "true";
754         } else {
755             *mJava << "false";
756         }
757         *mJava << ");\n";
758     }
759     mJava->decreaseIndent();
760     mJava->indent() << "} catch (Exception e) {\n";
761     mJava->increaseIndent();
762     mJava->indent() << "throw new RSRuntimeException(\"RenderScript. Can't invoke forEach_"
763                     << mRsKernelName << ": \" + e.toString());\n";
764     mJava->endBlock();
765 }
766 
767 /* Write the section of the .rs file for this permutation.
768  *
769  * We communicate the extra input and output parameters via global allocations.
770  * For example, if we have a function that takes three arguments, two for input
771  * and one for output:
772  *
773  * start:
774  * name: gamn
775  * ret: float3
776  * arg: float3 a
777  * arg: int b
778  * arg: float3 *c
779  * end:
780  *
781  * We'll produce:
782  *
783  * rs_allocation gAllocInB;
784  * rs_allocation gAllocOutC;
785  *
786  * float3 __attribute__((kernel)) test_gamn_float3_int_float3(float3 inA, unsigned int x) {
787  *    int inB;
788  *    float3 outC;
789  *    float2 out;
790  *    inB = rsGetElementAt_int(gAllocInB, x);
791  *    out = gamn(a, in_b, &outC);
792  *    rsSetElementAt_float4(gAllocOutC, &outC, x);
793  *    return out;
794  * }
795  *
796  * We avoid re-using x and y from the definition because these have reserved
797  * meanings in a .rs file.
798  */
writeRsSection(set<string> * rsAllocationsGenerated) const799 void PermutationWriter::writeRsSection(set<string>* rsAllocationsGenerated) const {
800     // Write the allocation declarations we'll need.
801     for (auto p : mPermutation.getParams()) {
802         // Don't need allocation for one input and one return value.
803         if (p != mFirstInputParam) {
804             writeRsAllocationDefinition(*p, rsAllocationsGenerated);
805         }
806     }
807     *mRs << "\n";
808 
809     // Write the function header.
810     if (mReturnParam) {
811         *mRs << mReturnParam->rsType;
812     } else {
813         *mRs << "void";
814     }
815     *mRs << " __attribute__((kernel)) " << mRsKernelName;
816     *mRs << "(";
817     bool needComma = false;
818     if (mFirstInputParam) {
819         *mRs << mFirstInputParam->rsType << " " << mFirstInputParam->variableName;
820         needComma = true;
821     }
822     if (mPermutation.getOutputCount() > 1 || mPermutation.getInputCount() > 1) {
823         if (needComma) {
824             *mRs << ", ";
825         }
826         *mRs << "unsigned int x";
827     }
828     *mRs << ")";
829     mRs->startBlock();
830 
831     // Write the local variable declarations and initializations.
832     for (auto p : mPermutation.getParams()) {
833         if (p == mFirstInputParam) {
834             continue;
835         }
836         mRs->indent() << p->rsType << " " << p->variableName;
837         if (p->isOutParameter) {
838             *mRs << " = 0;\n";
839         } else {
840             *mRs << " = rsGetElementAt_" << p->rsType << "(" << p->rsAllocName << ", x);\n";
841         }
842     }
843 
844     // Write the function call.
845     if (mReturnParam) {
846         if (mPermutation.getOutputCount() > 1) {
847             mRs->indent() << mReturnParam->rsType << " " << mReturnParam->variableName << " = ";
848         } else {
849             mRs->indent() << "return ";
850         }
851     }
852     *mRs << mPermutation.getName() << "(";
853     needComma = false;
854     for (auto p : mPermutation.getParams()) {
855         if (needComma) {
856             *mRs << ", ";
857         }
858         if (p->isOutParameter) {
859             *mRs << "&";
860         }
861         *mRs << p->variableName;
862         needComma = true;
863     }
864     *mRs << ");\n";
865 
866     if (mPermutation.getOutputCount() > 1) {
867         // Write setting the extra out parameters into the allocations.
868         for (auto p : mPermutation.getParams()) {
869             if (p->isOutParameter) {
870                 mRs->indent() << "rsSetElementAt_" << p->rsType << "(" << p->rsAllocName << ", ";
871                 // Check if we need to use '&' for this type of argument.
872                 char lastChar = p->variableName.back();
873                 if (lastChar >= '0' && lastChar <= '9') {
874                     *mRs << "&";
875                 }
876                 *mRs << p->variableName << ", x);\n";
877             }
878         }
879         if (mReturnParam) {
880             mRs->indent() << "return " << mReturnParam->variableName << ";\n";
881         }
882     }
883     mRs->endBlock();
884 }
885 
writeRsAllocationDefinition(const ParameterDefinition & param,set<string> * rsAllocationsGenerated) const886 void PermutationWriter::writeRsAllocationDefinition(const ParameterDefinition& param,
887                                                     set<string>* rsAllocationsGenerated) const {
888     if (!testAndSet(param.rsAllocName, rsAllocationsGenerated)) {
889         *mRs << "rs_allocation " << param.rsAllocName << ";\n";
890     }
891 }
892 
893 // Open the mJavaFile and writes the header.
startJavaFile(GeneratedFile * file,const Function & function,const string & directory,const string & testName,const string & relaxedTestName)894 static bool startJavaFile(GeneratedFile* file, const Function& function, const string& directory,
895                           const string& testName, const string& relaxedTestName) {
896     const string fileName = testName + ".java";
897     if (!file->start(directory, fileName)) {
898         return false;
899     }
900     file->writeNotices();
901 
902     *file << "package android.renderscript.cts;\n\n";
903 
904     *file << "import android.renderscript.Allocation;\n";
905     *file << "import android.renderscript.RSRuntimeException;\n";
906     *file << "import android.renderscript.Element;\n\n";
907     *file << "import java.util.Arrays;\n\n";
908 
909     *file << "public class " << testName << " extends RSBaseCompute";
910     file->startBlock();  // The corresponding endBlock() is in finishJavaFile()
911     *file << "\n";
912 
913     file->indent() << "private ScriptC_" << testName << " script;\n";
914     file->indent() << "private ScriptC_" << relaxedTestName << " scriptRelaxed;\n\n";
915 
916     file->indent() << "@Override\n";
917     file->indent() << "protected void setUp() throws Exception";
918     file->startBlock();
919 
920     file->indent() << "super.setUp();\n";
921     file->indent() << "script = new ScriptC_" << testName << "(mRS);\n";
922     file->indent() << "scriptRelaxed = new ScriptC_" << relaxedTestName << "(mRS);\n";
923 
924     file->endBlock();
925     *file << "\n";
926     return true;
927 }
928 
929 // Write the test method that calls all the generated Check methods.
finishJavaFile(GeneratedFile * file,const Function & function,const vector<string> & javaCheckMethods)930 static void finishJavaFile(GeneratedFile* file, const Function& function,
931                            const vector<string>& javaCheckMethods) {
932     file->indent() << "public void test" << function.getCapitalizedName() << "()";
933     file->startBlock();
934     for (auto m : javaCheckMethods) {
935         file->indent() << m << "();\n";
936     }
937     file->endBlock();
938 
939     file->endBlock();
940 }
941 
942 // Open the script file and write its header.
startRsFile(GeneratedFile * file,const Function & function,const string & directory,const string & testName)943 static bool startRsFile(GeneratedFile* file, const Function& function, const string& directory,
944                         const string& testName) {
945     string fileName = testName + ".rs";
946     if (!file->start(directory, fileName)) {
947         return false;
948     }
949     file->writeNotices();
950 
951     *file << "#pragma version(1)\n";
952     *file << "#pragma rs java_package_name(android.renderscript.cts)\n\n";
953     return true;
954 }
955 
956 // Write the entire *Relaxed.rs test file, as it only depends on the name.
writeRelaxedRsFile(const Function & function,const string & directory,const string & testName,const string & relaxedTestName)957 static bool writeRelaxedRsFile(const Function& function, const string& directory,
958                                const string& testName, const string& relaxedTestName) {
959     string name = relaxedTestName + ".rs";
960 
961     GeneratedFile file;
962     if (!file.start(directory, name)) {
963         return false;
964     }
965     file.writeNotices();
966 
967     file << "#include \"" << testName << ".rs\"\n";
968     file << "#pragma rs_fp_relaxed\n";
969     file.close();
970     return true;
971 }
972 
973 /* Write the .java and the two .rs test files.  versionOfTestFiles is used to restrict which API
974  * to test.
975  */
writeTestFilesForFunction(const Function & function,const string & directory,int versionOfTestFiles)976 static bool writeTestFilesForFunction(const Function& function, const string& directory,
977                                       int versionOfTestFiles) {
978     // Avoid creating empty files if we're not testing this function.
979     if (!needTestFiles(function, versionOfTestFiles)) {
980         return true;
981     }
982 
983     const string testName = "Test" + function.getCapitalizedName();
984     const string relaxedTestName = testName + "Relaxed";
985 
986     if (!writeRelaxedRsFile(function, directory, testName, relaxedTestName)) {
987         return false;
988     }
989 
990     GeneratedFile rsFile;    // The Renderscript test file we're generating.
991     GeneratedFile javaFile;  // The Jave test file we're generating.
992     if (!startRsFile(&rsFile, function, directory, testName)) {
993         return false;
994     }
995 
996     if (!startJavaFile(&javaFile, function, directory, testName, relaxedTestName)) {
997         return false;
998     }
999 
1000     /* We keep track of the allocations generated in the .rs file and the argument classes defined
1001      * in the Java file, as we share these between the functions created for each specification.
1002      */
1003     set<string> rsAllocationsGenerated;
1004     set<string> javaGeneratedArgumentClasses;
1005     // Lines of Java code to invoke the check methods.
1006     vector<string> javaCheckMethods;
1007 
1008     for (auto spec : function.getSpecifications()) {
1009         if (spec->hasTests(versionOfTestFiles)) {
1010             for (auto permutation : spec->getPermutations()) {
1011                 PermutationWriter w(*permutation, &rsFile, &javaFile);
1012                 w.writeRsSection(&rsAllocationsGenerated);
1013                 w.writeJavaSection(&javaGeneratedArgumentClasses);
1014 
1015                 // Store the check method to be called.
1016                 javaCheckMethods.push_back(w.getJavaCheckMethodName());
1017             }
1018         }
1019     }
1020 
1021     finishJavaFile(&javaFile, function, javaCheckMethods);
1022     // There's no work to wrap-up in the .rs file.
1023 
1024     rsFile.close();
1025     javaFile.close();
1026     return true;
1027 }
1028 
generateTestFiles(const string & directory,int versionOfTestFiles)1029 bool generateTestFiles(const string& directory, int versionOfTestFiles) {
1030     bool success = true;
1031     for (auto f : systemSpecification.getFunctions()) {
1032         if (!writeTestFilesForFunction(*f.second, directory, versionOfTestFiles)) {
1033             success = false;
1034         }
1035     }
1036     return success;
1037 }
1038