blob: 7cf10992f4424f02ee64dc3425d8e51c02f705b8 [file] [log] [blame]
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -07001/*
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
26using namespace std;
27
28// Converts float2 to FLOAT_32 and 2, etc.
29static 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
49static 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 */
65class PermutationWriter {
66private:
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 writeJavaAppendVariableToMessage(const ParameterDefinition& p, const string& value) const;
151 void writeJavaAppendFloatVariableToMessage(const string& value, bool regularFloat) const;
152 void writeJavaAppendVectorInputToMessage(const ParameterDefinition& p) const;
153 void writeJavaAppendVectorOutputToMessage(const ParameterDefinition& p) const;
154
155 // Generate the set of instructions to call the script.
156 void writeJavaCallToRs(bool relaxed, bool generateCallToVerifier) const;
157
158 // Write an allocation definition if not already emitted in the .rs file.
159 void writeRsAllocationDefinition(const ParameterDefinition& param,
160 set<string>* rsAllocationsGenerated) const;
161
162public:
163 /* NOTE: We keep pointers to the permutation and the files. This object should not
164 * outlive the arguments.
165 */
166 PermutationWriter(FunctionPermutation& permutation, GeneratedFile* rsFile,
167 GeneratedFile* javaFile);
168 string getJavaCheckMethodName() const { return mJavaCheckMethodName; }
169
170 // Write the script test function for this permutation.
171 void writeRsSection(set<string>* rsAllocationsGenerated) const;
172 // Write the section of the Java code that calls the script and validates the results
173 void writeJavaSection(set<string>* javaGeneratedArgumentClasses) const;
174};
175
176PermutationWriter::PermutationWriter(FunctionPermutation& permutation, GeneratedFile* rsFile,
177 GeneratedFile* javaFile)
178 : mPermutation(permutation),
179 mRs(rsFile),
180 mJava(javaFile),
181 mReturnParam(nullptr),
182 mFirstInputParam(nullptr) {
183 mRsKernelName = "test" + capitalize(permutation.getName());
184
185 mJavaArgumentsClassName = "Arguments";
186 mJavaArgumentsNClassName = "Arguments";
187 const string trunk = capitalize(permutation.getNameTrunk());
188 mJavaCheckMethodName = "check" + trunk;
189 mJavaVerifyMethodName = "verifyResults" + trunk;
190
191 for (auto p : permutation.getParams()) {
192 mAllInputsAndOutputs.push_back(p);
193 if (mFirstInputParam == nullptr && !p->isOutParameter) {
194 mFirstInputParam = p;
195 }
196 }
197 mReturnParam = permutation.getReturn();
198 if (mReturnParam) {
199 mAllInputsAndOutputs.push_back(mReturnParam);
200 }
201
202 for (auto p : mAllInputsAndOutputs) {
203 const string capitalizedRsType = capitalize(p->rsType);
204 const string capitalizedBaseType = capitalize(p->rsBaseType);
205 mRsKernelName += capitalizedRsType;
206 mJavaArgumentsClassName += capitalizedBaseType;
207 mJavaArgumentsNClassName += capitalizedBaseType;
208 if (p->mVectorSize != "1") {
209 mJavaArgumentsNClassName += "N";
210 }
211 mJavaCheckMethodName += capitalizedRsType;
212 mJavaVerifyMethodName += capitalizedRsType;
213 }
214 mJavaVerifierComputeMethodName = "compute" + trunk;
215 mJavaVerifierVerifyMethodName = "verify" + trunk;
216}
217
218void PermutationWriter::writeJavaSection(set<string>* javaGeneratedArgumentClasses) const {
219 // By default, we test the results using item by item comparison.
220 const string test = mPermutation.getTest();
221 if (test == "scalar" || test == "limited") {
222 writeJavaArgumentClass(true, javaGeneratedArgumentClasses);
223 writeJavaCheckMethod(true);
224 writeJavaVerifyScalarMethod(false);
225 } else if (test == "custom") {
226 writeJavaArgumentClass(true, javaGeneratedArgumentClasses);
227 writeJavaCheckMethod(true);
228 writeJavaVerifyScalarMethod(true);
229 } else if (test == "vector") {
230 writeJavaArgumentClass(false, javaGeneratedArgumentClasses);
231 writeJavaCheckMethod(true);
232 writeJavaVerifyVectorMethod();
233 } else if (test == "noverify") {
234 writeJavaCheckMethod(false);
235 }
236}
237
238void PermutationWriter::writeJavaArgumentClass(bool scalar,
239 set<string>* javaGeneratedArgumentClasses) const {
240 string name;
241 if (scalar) {
242 name = mJavaArgumentsClassName;
243 } else {
244 name = mJavaArgumentsNClassName;
245 }
246
247 // Make sure we have not generated the argument class already.
248 if (!testAndSet(name, javaGeneratedArgumentClasses)) {
249 mJava->indent() << "public class " << name;
250 mJava->startBlock();
251
252 for (auto p : mAllInputsAndOutputs) {
253 mJava->indent() << "public ";
254 if (p->isOutParameter && p->isFloatType && mPermutation.getTest() != "custom") {
255 *mJava << "Target.Floaty";
256 } else {
257 *mJava << p->javaBaseType;
258 }
259 if (!scalar && p->mVectorSize != "1") {
260 *mJava << "[]";
261 }
262 *mJava << " " << p->variableName << ";\n";
263 }
264 mJava->endBlock();
265 *mJava << "\n";
266 }
267}
268
269void PermutationWriter::writeJavaCheckMethod(bool generateCallToVerifier) const {
270 mJava->indent() << "private void " << mJavaCheckMethodName << "()";
271 mJava->startBlock();
272
273 // Generate the input allocations and initialization.
274 for (auto p : mAllInputsAndOutputs) {
275 if (!p->isOutParameter) {
276 writeJavaInputAllocationDefinition(*p);
277 }
278 }
279 // Generate code to enforce ordering between two allocations if needed.
280 for (auto p : mAllInputsAndOutputs) {
281 if (!p->isOutParameter && !p->smallerParameter.empty()) {
282 string smallerAlloc = "in" + capitalize(p->smallerParameter);
283 mJava->indent() << "enforceOrdering(" << smallerAlloc << ", " << p->javaAllocName
284 << ");\n";
285 }
286 }
287
288 // Generate code to check the full and relaxed scripts.
289 writeJavaCallToRs(false, generateCallToVerifier);
290 writeJavaCallToRs(true, generateCallToVerifier);
291
292 mJava->endBlock();
293 *mJava << "\n";
294}
295
296void PermutationWriter::writeJavaInputAllocationDefinition(const ParameterDefinition& param) const {
297 string dataType;
298 char vectorSize;
299 convertToRsType(param.rsType, &dataType, &vectorSize);
300
301 const string seed = hashString(mJavaCheckMethodName + param.javaAllocName);
302 mJava->indent() << "Allocation " << param.javaAllocName << " = ";
303 if (param.compatibleTypeIndex >= 0) {
304 if (TYPES[param.typeIndex].kind == FLOATING_POINT) {
305 writeJavaRandomCompatibleFloatAllocation(dataType, seed, vectorSize,
306 TYPES[param.compatibleTypeIndex],
307 TYPES[param.typeIndex]);
308 } else {
309 writeJavaRandomCompatibleIntegerAllocation(dataType, seed, vectorSize,
310 TYPES[param.compatibleTypeIndex],
311 TYPES[param.typeIndex]);
312 }
313 } else if (!param.minValue.empty()) {
314 *mJava << "createRandomFloatAllocation(mRS, Element.DataType." << dataType << ", "
315 << vectorSize << ", " << seed << ", " << param.minValue << ", " << param.maxValue
316 << ")";
317 } else {
318 /* TODO Instead of passing always false, check whether we are doing a limited test.
319 * Use instead: (mPermutation.getTest() == "limited" ? "false" : "true")
320 */
321 *mJava << "createRandomAllocation(mRS, Element.DataType." << dataType << ", " << vectorSize
322 << ", " << seed << ", false)";
323 }
324 *mJava << ";\n";
325}
326
327void PermutationWriter::writeJavaRandomCompatibleFloatAllocation(
328 const string& dataType, const string& seed, char vectorSize,
329 const NumericalType& compatibleType, const NumericalType& generatedType) const {
330 *mJava << "createRandomFloatAllocation"
331 << "(mRS, Element.DataType." << dataType << ", " << vectorSize << ", " << seed << ", ";
332 double minValue = 0.0;
333 double maxValue = 0.0;
334 switch (compatibleType.kind) {
335 case FLOATING_POINT: {
336 // We're generating floating point values. We just worry about the exponent.
337 // Subtract 1 for the exponent sign.
338 int bits = min(compatibleType.exponentBits, generatedType.exponentBits) - 1;
339 maxValue = ldexp(0.95, (1 << bits) - 1);
340 minValue = -maxValue;
341 break;
342 }
343 case UNSIGNED_INTEGER:
344 maxValue = maxDoubleForInteger(compatibleType.significantBits,
345 generatedType.significantBits);
346 minValue = 0.0;
347 break;
348 case SIGNED_INTEGER:
349 maxValue = maxDoubleForInteger(compatibleType.significantBits,
350 generatedType.significantBits);
351 minValue = -maxValue - 1.0;
352 break;
353 }
354 *mJava << scientific << std::setprecision(19);
355 *mJava << minValue << ", " << maxValue << ")";
356 mJava->unsetf(ios_base::floatfield);
357}
358
359void PermutationWriter::writeJavaRandomCompatibleIntegerAllocation(
360 const string& dataType, const string& seed, char vectorSize,
361 const NumericalType& compatibleType, const NumericalType& generatedType) const {
362 *mJava << "createRandomIntegerAllocation"
363 << "(mRS, Element.DataType." << dataType << ", " << vectorSize << ", " << seed << ", ";
364
365 if (compatibleType.kind == FLOATING_POINT) {
366 // Currently, all floating points can take any number we generate.
367 bool isSigned = generatedType.kind == SIGNED_INTEGER;
368 *mJava << (isSigned ? "true" : "false") << ", " << generatedType.significantBits;
369 } else {
370 bool isSigned =
371 compatibleType.kind == SIGNED_INTEGER && generatedType.kind == SIGNED_INTEGER;
372 *mJava << (isSigned ? "true" : "false") << ", "
373 << min(compatibleType.significantBits, generatedType.significantBits);
374 }
375 *mJava << ")";
376}
377
378void PermutationWriter::writeJavaOutputAllocationDefinition(
379 const ParameterDefinition& param) const {
380 string dataType;
381 char vectorSize;
382 convertToRsType(param.rsType, &dataType, &vectorSize);
383 mJava->indent() << "Allocation " << param.javaAllocName << " = Allocation.createSized(mRS, "
384 << "getElement(mRS, Element.DataType." << dataType << ", " << vectorSize
385 << "), INPUTSIZE);\n";
386}
387
388void PermutationWriter::writeJavaVerifyScalarMethod(bool verifierValidates) const {
389 writeJavaVerifyMethodHeader();
390 mJava->startBlock();
391
392 string vectorSize = "1";
393 for (auto p : mAllInputsAndOutputs) {
394 writeJavaArrayInitialization(*p);
395 if (p->mVectorSize != "1" && p->mVectorSize != vectorSize) {
396 if (vectorSize == "1") {
397 vectorSize = p->mVectorSize;
398 } else {
399 cerr << "Error. Had vector " << vectorSize << " and " << p->mVectorSize << "\n";
400 }
401 }
402 }
403
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
464 mJava->indent() << "StringBuilder message = new StringBuilder();\n";
465 for (auto p : mAllInputsAndOutputs) {
466 if (p->isOutParameter) {
467 writeJavaAppendOutputToMessage(*p, "", "[i * " + p->vectorWidth + " + j]",
468 verifierValidates);
469 } else {
470 writeJavaAppendInputToMessage(*p, "args." + p->variableName);
471 }
472 }
473 if (verifierValidates) {
474 mJava->indent() << "message.append(errorMessage);\n";
475 }
476
477 mJava->indent() << "assertTrue(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n";
478 mJava->indentPlus()
479 << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), valid);\n";
480
481 mJava->endBlock();
482 mJava->endBlock();
483 mJava->endBlock();
484 mJava->endBlock();
485 *mJava << "\n";
486}
487
488void PermutationWriter::writeJavaVerifyVectorMethod() const {
489 writeJavaVerifyMethodHeader();
490 mJava->startBlock();
491
492 for (auto p : mAllInputsAndOutputs) {
493 writeJavaArrayInitialization(*p);
494 }
495 mJava->indent() << "for (int i = 0; i < INPUTSIZE; i++)";
496 mJava->startBlock();
497
498 mJava->indent() << mJavaArgumentsNClassName << " args = new " << mJavaArgumentsNClassName
499 << "();\n";
500
501 mJava->indent() << "// Create the appropriate sized arrays in args\n";
502 for (auto p : mAllInputsAndOutputs) {
503 if (p->mVectorSize != "1") {
504 string type = p->javaBaseType;
505 if (p->isOutParameter && p->isFloatType) {
506 type = "Target.Floaty";
507 }
508 mJava->indent() << "args." << p->variableName << " = new " << type << "["
509 << p->mVectorSize << "];\n";
510 }
511 }
512
513 mJava->indent() << "// Fill args with the input values\n";
514 for (auto p : mAllInputsAndOutputs) {
515 if (!p->isOutParameter) {
516 if (p->mVectorSize == "1") {
517 mJava->indent() << "args." << p->variableName << " = " << p->javaArrayName + "[i]"
518 << ";\n";
519 } else {
520 mJava->indent() << "for (int j = 0; j < " << p->mVectorSize << " ; j++)";
521 mJava->startBlock();
522 mJava->indent() << "args." << p->variableName + "[j] = "
523 << p->javaArrayName + "[i * " + p->vectorWidth + " + j]"
524 << ";\n";
525 mJava->endBlock();
526 }
527 }
528 }
529 mJava->indent() << "Target target = new Target(relaxed);\n";
530 mJava->indent() << "CoreMathVerifier." << mJavaVerifierComputeMethodName
531 << "(args, target);\n\n";
532
533 mJava->indent() << "// Compare the expected outputs to the actual values returned by RS.\n";
534 mJava->indent() << "boolean valid = true;\n";
535 for (auto p : mAllInputsAndOutputs) {
536 if (p->isOutParameter) {
537 writeJavaVectorComparison(*p);
538 }
539 }
540
541 mJava->indent() << "if (!valid)";
542 mJava->startBlock();
543
544 mJava->indent() << "StringBuilder message = new StringBuilder();\n";
545 for (auto p : mAllInputsAndOutputs) {
546 if (p->isOutParameter) {
547 writeJavaAppendVectorOutputToMessage(*p);
548 } else {
549 writeJavaAppendVectorInputToMessage(*p);
550 }
551 }
552
553 mJava->indent() << "assertTrue(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n";
554 mJava->indentPlus()
555 << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), valid);\n";
556
557 mJava->endBlock();
558 mJava->endBlock();
559 mJava->endBlock();
560 *mJava << "\n";
561}
562
563void PermutationWriter::writeJavaVerifyMethodHeader() const {
564 mJava->indent() << "private void " << mJavaVerifyMethodName << "(";
565 for (auto p : mAllInputsAndOutputs) {
566 *mJava << "Allocation " << p->javaAllocName << ", ";
567 }
568 *mJava << "boolean relaxed)";
569}
570
571void PermutationWriter::writeJavaArrayInitialization(const ParameterDefinition& p) const {
572 mJava->indent() << p.javaBaseType << "[] " << p.javaArrayName << " = new " << p.javaBaseType
573 << "[INPUTSIZE * " << p.vectorWidth << "];\n";
574 mJava->indent() << p.javaAllocName << ".copyTo(" << p.javaArrayName << ");\n";
575}
576
577void PermutationWriter::writeJavaTestAndSetValid(const ParameterDefinition& p,
578 const string& argsIndex,
579 const string& actualIndex) const {
580 writeJavaTestOneValue(p, argsIndex, actualIndex);
581 mJava->startBlock();
582 mJava->indent() << "valid = false;\n";
583 mJava->endBlock();
584}
585
586void PermutationWriter::writeJavaTestOneValue(const ParameterDefinition& p, const string& argsIndex,
587 const string& actualIndex) const {
588 mJava->indent() << "if (";
589 if (p.isFloatType) {
590 *mJava << "!args." << p.variableName << argsIndex << ".couldBe(" << p.javaArrayName
591 << actualIndex;
592 const string s = mPermutation.getPrecisionLimit();
593 if (!s.empty()) {
594 *mJava << ", " << s;
595 }
596 *mJava << ")";
597 } else {
598 *mJava << "args." << p.variableName << argsIndex << " != " << p.javaArrayName
599 << actualIndex;
600 }
601
602 if (p.undefinedIfOutIsNan && mReturnParam) {
603 *mJava << " && !args." << mReturnParam->variableName << argsIndex << ".isNaN()";
604 }
605 *mJava << ")";
606}
607
608void PermutationWriter::writeJavaVectorComparison(const ParameterDefinition& p) const {
609 if (p.mVectorSize == "1") {
610 writeJavaTestAndSetValid(p, "", "[i]");
611 } else {
612 mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
613 mJava->startBlock();
614 writeJavaTestAndSetValid(p, "[j]", "[i * " + p.vectorWidth + " + j]");
615 mJava->endBlock();
616 }
617}
618
619void PermutationWriter::writeJavaAppendOutputToMessage(const ParameterDefinition& p,
620 const string& argsIndex,
621 const string& actualIndex,
622 bool verifierValidates) const {
623 if (verifierValidates) {
624 const string actual = "args." + p.variableName + argsIndex;
625 mJava->indent() << "message.append(\"Output " + p.variableName + ": \");\n";
626 if (p.isFloatType) {
627 writeJavaAppendFloatVariableToMessage(actual, true);
628 } else {
629 writeJavaAppendVariableToMessage(p, actual);
630 }
631 writeJavaAppendNewLineToMessage();
632 } else {
633 const string expected = "args." + p.variableName + argsIndex;
634 const string actual = p.javaArrayName + actualIndex;
635 mJava->indent() << "message.append(\"Expected output " + p.variableName + ": \");\n";
636 if (p.isFloatType) {
637 writeJavaAppendFloatVariableToMessage(expected, false);
638 } else {
639 writeJavaAppendVariableToMessage(p, expected);
640 }
641 writeJavaAppendNewLineToMessage();
642 mJava->indent() << "message.append(\"Actual output " + p.variableName + ": \");\n";
643 writeJavaAppendVariableToMessage(p, actual);
644
645 writeJavaTestOneValue(p, argsIndex, actualIndex);
646 mJava->startBlock();
647 mJava->indent() << "message.append(\" FAIL\");\n";
648 mJava->endBlock();
649 writeJavaAppendNewLineToMessage();
650 }
651}
652
653void PermutationWriter::writeJavaAppendInputToMessage(const ParameterDefinition& p,
654 const string& actual) const {
655 mJava->indent() << "message.append(\"Input " + p.variableName + ": \");\n";
656 writeJavaAppendVariableToMessage(p, actual);
657 writeJavaAppendNewLineToMessage();
658}
659
660void PermutationWriter::writeJavaAppendNewLineToMessage() const {
661 mJava->indent() << "message.append(\"\\n\");\n";
662}
663
664void PermutationWriter::writeJavaAppendVariableToMessage(const ParameterDefinition& p,
665 const string& value) const {
666 if (p.specType == "f16" || p.specType == "f32") {
667 mJava->indent() << "message.append(String.format(\"%14.8g {%8x} %15a\",\n";
668 mJava->indentPlus() << value << ", "
669 << "Float.floatToRawIntBits(" << value << "), " << value << "));\n";
670 } else if (p.specType == "f64") {
671 mJava->indent() << "message.append(String.format(\"%24.8g {%16x} %31a\",\n";
672 mJava->indentPlus() << value << ", "
673 << "Double.doubleToRawLongBits(" << value << "), " << value << "));\n";
674 } else if (p.specType[0] == 'u') {
675 mJava->indent() << "message.append(String.format(\"0x%x\", " << value << "));\n";
676 } else {
677 mJava->indent() << "message.append(String.format(\"%d\", " << value << "));\n";
678 }
679}
680
681void PermutationWriter::writeJavaAppendFloatVariableToMessage(const string& value,
682 bool regularFloat) const {
683 mJava->indent() << "message.append(";
684 if (regularFloat) {
685 *mJava << "Float.toString(" << value << ")";
686 } else {
687 *mJava << value << ".toString()";
688 }
689 *mJava << ");\n";
690}
691
692void PermutationWriter::writeJavaAppendVectorInputToMessage(const ParameterDefinition& p) const {
693 if (p.mVectorSize == "1") {
694 writeJavaAppendInputToMessage(p, p.javaArrayName + "[i]");
695 } else {
696 mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
697 mJava->startBlock();
698 writeJavaAppendInputToMessage(p, p.javaArrayName + "[i * " + p.vectorWidth + " + j]");
699 mJava->endBlock();
700 }
701}
702
703void PermutationWriter::writeJavaAppendVectorOutputToMessage(const ParameterDefinition& p) const {
704 if (p.mVectorSize == "1") {
705 writeJavaAppendOutputToMessage(p, "", "[i]", false);
706 } else {
707 mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
708 mJava->startBlock();
709 writeJavaAppendOutputToMessage(p, "[j]", "[i * " + p.vectorWidth + " + j]", false);
710 mJava->endBlock();
711 }
712}
713
714void PermutationWriter::writeJavaCallToRs(bool relaxed, bool generateCallToVerifier) const {
715 string script = "script";
716 if (relaxed) {
717 script += "Relaxed";
718 }
719
720 mJava->indent() << "try";
721 mJava->startBlock();
722
723 for (auto p : mAllInputsAndOutputs) {
724 if (p->isOutParameter) {
725 writeJavaOutputAllocationDefinition(*p);
726 }
727 }
728
729 for (auto p : mPermutation.getParams()) {
730 if (p != mFirstInputParam) {
731 mJava->indent() << script << ".set_" << p->rsAllocName << "(" << p->javaAllocName
732 << ");\n";
733 }
734 }
735
736 mJava->indent() << script << ".forEach_" << mRsKernelName << "(";
737 bool needComma = false;
738 if (mFirstInputParam) {
739 *mJava << mFirstInputParam->javaAllocName;
740 needComma = true;
741 }
742 if (mReturnParam) {
743 if (needComma) {
744 *mJava << ", ";
745 }
746 *mJava << mReturnParam->variableName << ");\n";
747 }
748
749 if (generateCallToVerifier) {
750 mJava->indent() << mJavaVerifyMethodName << "(";
751 for (auto p : mAllInputsAndOutputs) {
752 *mJava << p->variableName << ", ";
753 }
754
755 if (relaxed) {
756 *mJava << "true";
757 } else {
758 *mJava << "false";
759 }
760 *mJava << ");\n";
761 }
762 mJava->decreaseIndent();
763 mJava->indent() << "} catch (Exception e) {\n";
764 mJava->increaseIndent();
765 mJava->indent() << "throw new RSRuntimeException(\"RenderScript. Can't invoke forEach_"
766 << mRsKernelName << ": \" + e.toString());\n";
767 mJava->endBlock();
768}
769
770/* Write the section of the .rs file for this permutation.
771 *
772 * We communicate the extra input and output parameters via global allocations.
773 * For example, if we have a function that takes three arguments, two for input
774 * and one for output:
775 *
776 * start:
777 * name: gamn
778 * ret: float3
779 * arg: float3 a
780 * arg: int b
781 * arg: float3 *c
782 * end:
783 *
784 * We'll produce:
785 *
786 * rs_allocation gAllocInB;
787 * rs_allocation gAllocOutC;
788 *
789 * float3 __attribute__((kernel)) test_gamn_float3_int_float3(float3 inA, unsigned int x) {
790 * int inB;
791 * float3 outC;
792 * float2 out;
793 * inB = rsGetElementAt_int(gAllocInB, x);
794 * out = gamn(a, in_b, &outC);
795 * rsSetElementAt_float4(gAllocOutC, &outC, x);
796 * return out;
797 * }
798 *
799 * We avoid re-using x and y from the definition because these have reserved
800 * meanings in a .rs file.
801 */
802void PermutationWriter::writeRsSection(set<string>* rsAllocationsGenerated) const {
803 // Write the allocation declarations we'll need.
804 for (auto p : mPermutation.getParams()) {
805 // Don't need allocation for one input and one return value.
806 if (p != mFirstInputParam) {
807 writeRsAllocationDefinition(*p, rsAllocationsGenerated);
808 }
809 }
810 *mRs << "\n";
811
812 // Write the function header.
813 if (mReturnParam) {
814 *mRs << mReturnParam->rsType;
815 } else {
816 *mRs << "void";
817 }
818 *mRs << " __attribute__((kernel)) " << mRsKernelName;
819 *mRs << "(";
820 bool needComma = false;
821 if (mFirstInputParam) {
822 *mRs << mFirstInputParam->rsType << " " << mFirstInputParam->variableName;
823 needComma = true;
824 }
825 if (mPermutation.getOutputCount() > 1 || mPermutation.getInputCount() > 1) {
826 if (needComma) {
827 *mRs << ", ";
828 }
829 *mRs << "unsigned int x";
830 }
831 *mRs << ")";
832 mRs->startBlock();
833
834 // Write the local variable declarations and initializations.
835 for (auto p : mPermutation.getParams()) {
836 if (p == mFirstInputParam) {
837 continue;
838 }
839 mRs->indent() << p->rsType << " " << p->variableName;
840 if (p->isOutParameter) {
841 *mRs << " = 0;\n";
842 } else {
843 *mRs << " = rsGetElementAt_" << p->rsType << "(" << p->rsAllocName << ", x);\n";
844 }
845 }
846
847 // Write the function call.
848 if (mReturnParam) {
849 if (mPermutation.getOutputCount() > 1) {
850 mRs->indent() << mReturnParam->rsType << " " << mReturnParam->variableName << " = ";
851 } else {
852 mRs->indent() << "return ";
853 }
854 }
855 *mRs << mPermutation.getName() << "(";
856 needComma = false;
857 for (auto p : mPermutation.getParams()) {
858 if (needComma) {
859 *mRs << ", ";
860 }
861 if (p->isOutParameter) {
862 *mRs << "&";
863 }
864 *mRs << p->variableName;
865 needComma = true;
866 }
867 *mRs << ");\n";
868
869 if (mPermutation.getOutputCount() > 1) {
870 // Write setting the extra out parameters into the allocations.
871 for (auto p : mPermutation.getParams()) {
872 if (p->isOutParameter) {
873 mRs->indent() << "rsSetElementAt_" << p->rsType << "(" << p->rsAllocName << ", ";
874 // Check if we need to use '&' for this type of argument.
875 char lastChar = p->variableName.back();
876 if (lastChar >= '0' && lastChar <= '9') {
877 *mRs << "&";
878 }
879 *mRs << p->variableName << ", x);\n";
880 }
881 }
882 if (mReturnParam) {
883 mRs->indent() << "return " << mReturnParam->variableName << ";\n";
884 }
885 }
886 mRs->endBlock();
887}
888
889void PermutationWriter::writeRsAllocationDefinition(const ParameterDefinition& param,
890 set<string>* rsAllocationsGenerated) const {
891 if (!testAndSet(param.rsAllocName, rsAllocationsGenerated)) {
892 *mRs << "rs_allocation " << param.rsAllocName << ";\n";
893 }
894}
895
896// Open the mJavaFile and writes the header.
897static bool startJavaFile(GeneratedFile* file, const Function& function, const string& testName,
898 const string& relaxedTestName) {
899 const string fileName = testName + ".java";
900 if (!file->start(fileName)) {
901 return false;
902 }
903 file->writeNotices();
904
905 *file << "package android.renderscript.cts;\n\n";
906
907 *file << "import android.renderscript.Allocation;\n";
908 *file << "import android.renderscript.RSRuntimeException;\n";
909 *file << "import android.renderscript.Element;\n\n";
910
911 *file << "public class " << testName << " extends RSBaseCompute";
912 file->startBlock(); // The corresponding endBlock() is in finishJavaFile()
913 *file << "\n";
914
915 file->indent() << "private ScriptC_" << testName << " script;\n";
916 file->indent() << "private ScriptC_" << relaxedTestName << " scriptRelaxed;\n\n";
917
918 file->indent() << "@Override\n";
919 file->indent() << "protected void setUp() throws Exception";
920 file->startBlock();
921
922 file->indent() << "super.setUp();\n";
923 file->indent() << "script = new ScriptC_" << testName << "(mRS);\n";
924 file->indent() << "scriptRelaxed = new ScriptC_" << relaxedTestName << "(mRS);\n";
925
926 file->endBlock();
927 *file << "\n";
928 return true;
929}
930
931// Write the test method that calls all the generated Check methods.
932static void finishJavaFile(GeneratedFile* file, const Function& function,
933 const vector<string>& javaCheckMethods) {
934 file->indent() << "public void test" << function.getCapitalizedName() << "()";
935 file->startBlock();
936 for (auto m : javaCheckMethods) {
937 file->indent() << m << "();\n";
938 }
939 file->endBlock();
940
941 file->endBlock();
942}
943
944// Open the script file and write its header.
945static bool startRsFile(GeneratedFile* file, const Function& function, const string& testName) {
946 string fileName = testName + ".rs";
947 if (!file->start(fileName)) {
948 return false;
949 }
950 file->writeNotices();
951
952 *file << "#pragma version(1)\n";
953 *file << "#pragma rs java_package_name(android.renderscript.cts)\n\n";
954 return true;
955}
956
957// Write the entire *Relaxed.rs test file, as it only depends on the name.
958static bool writeRelaxedRsFile(const Function& function, const string& testName,
959 const string& relaxedTestName) {
960 string name = relaxedTestName + ".rs";
961
962 GeneratedFile file;
963 if (!file.start(name)) {
964 return false;
965 }
966 file.writeNotices();
967
968 file << "#include \"" << testName << ".rs\"\n";
969 file << "#pragma rs_fp_relaxed\n";
970 file.close();
971 return true;
972}
973
974/* Write the .java and the two .rs test files. versionOfTestFiles is used to restrict which API
975 * to test.
976 */
977static bool writeTestFilesForFunction(const Function& function, 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 = "GeneratedTest" + function.getCapitalizedName();
984 const string relaxedTestName = testName + "Relaxed";
985
986 if (!writeRelaxedRsFile(function, 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, testName)) {
993 return false;
994 }
995
996 if (!startJavaFile(&javaFile, function, 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
1029bool GenerateTestFiles(int versionOfTestFiles) {
1030 bool success = true;
1031 for (auto specFile : systemSpecification.getSpecFiles()) {
1032 for (auto f : specFile->getFunctionsMap()) {
1033 if (!writeTestFilesForFunction(*f.second, versionOfTestFiles)) {
1034 success = false;
1035 }
1036 }
1037 }
1038 return success;
1039}