1 /*
   2  * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package com.oracle.graal.compiler.hsail.test.infra;
  24 
  25 import static org.junit.Assert.*;
  26 import static org.junit.Assume.*;
  27 
  28 import java.io.*;
  29 import java.lang.annotation.*;
  30 import java.lang.reflect.*;
  31 import java.nio.file.*;
  32 import java.util.*;
  33 import java.util.concurrent.atomic.*;
  34 import java.util.logging.*;
  35 
  36 import com.amd.okra.*;
  37 
  38 /**
  39  * Abstract class on which the HSAIL unit tests are built. Executes a method or lambda on both the
  40  * Java side and the Okra side and compares the results for fields that are annotated with
  41  * {@link KernelTester.Result}.
  42  */
  43 public abstract class KernelTester {
  44 
  45     /**
  46      * Denotes a field whose value is to be compared as part of computing the result of a test.
  47      */
  48     @Retention(RetentionPolicy.RUNTIME)
  49     @Target(ElementType.FIELD)
  50     public @interface Result {
  51     }
  52 
  53     // Using these in case we want to compile with Java 7.
  54     public interface MyIntConsumer {
  55 
  56         void accept(int value);
  57     }
  58 
  59     public interface MyObjConsumer {
  60 
  61         void accept(Object obj);
  62     }
  63 
  64     public enum DispatchMode {
  65         SEQ, JTP, OKRA
  66     }
  67 
  68     public enum HsailMode {
  69         COMPILED, INJECT_HSAIL, INJECT_OCL
  70     }
  71 
  72     private DispatchMode dispatchMode;
  73     // Where the hsail comes from.
  74     private HsailMode hsailMode;
  75     private Method testMethod;
  76     // What type of okra dispatch to use when client calls.
  77     private boolean useLambdaMethod;
  78     private Class<?>[] testMethodParams = null;
  79     private int id = nextId.incrementAndGet();
  80     static AtomicInteger nextId = new AtomicInteger(0);
  81     public static Logger logger;
  82     private OkraContext okraContext;
  83     private OkraKernel okraKernel;
  84     private static final String propPkgName = KernelTester.class.getPackage().getName();
  85     private static Level logLevel;
  86     private static ConsoleHandler consoleHandler;
  87 
  88     static {
  89         logger = Logger.getLogger(propPkgName);
  90         logLevel = Level.parse(System.getProperty("kerneltester.logLevel", "SEVERE"));
  91 
  92         // This block configure the logger with handler and formatter.
  93         consoleHandler = new ConsoleHandler();
  94         logger.addHandler(consoleHandler);
  95         logger.setUseParentHandlers(false);
  96         SimpleFormatter formatter = new SimpleFormatter() {
  97 
  98             @SuppressWarnings("sync-override")
  99             @Override
 100             public String format(LogRecord record) {
 101                 return (record.getMessage() + "\n");
 102             }
 103         };
 104         consoleHandler.setFormatter(formatter);
 105         setLogLevel(logLevel);
 106     }
 107 
 108     private static boolean gaveNoOkraWarning = false;
 109     private boolean onSimulator;
 110     private boolean okraLibExists;
 111 
 112     public boolean runningOnSimulator() {
 113         return onSimulator;
 114     }
 115 
 116     public KernelTester() {
 117         okraLibExists = OkraUtil.okraLibExists();
 118         dispatchMode = DispatchMode.SEQ;
 119         hsailMode = HsailMode.COMPILED;
 120         useLambdaMethod = false;
 121     }
 122 
 123     public abstract void runTest();
 124 
 125     // Default comparison is to compare all things marked @Result.
 126     public boolean compareResults(KernelTester base) {
 127         Class<?> clazz = this.getClass();
 128         while (clazz != null && clazz != KernelTester.class) {
 129             for (Field f : clazz.getDeclaredFields()) {
 130                 if (!Modifier.isStatic(f.getModifiers())) {
 131                     Result annos = f.getAnnotation(Result.class);
 132                     if (annos != null) {
 133                         logger.fine("@Result field = " + f);
 134                         Object myResult = getFieldFromObject(f, this);
 135                         Object otherResult = getFieldFromObject(f, base);
 136                         boolean same = compareObjects(myResult, otherResult);
 137                         logger.fine("comparing " + myResult + ", " + otherResult + ", match=" + same);
 138                         if (!same) {
 139                             logger.severe("mismatch comparing " + f + ", " + myResult + " vs. " + otherResult);
 140                             logSevere("FAILED!!! " + this.getClass());
 141                             return false;
 142                         }
 143                     }
 144                 }
 145             }
 146             clazz = clazz.getSuperclass();
 147         }
 148         logInfo("PASSED: " + this.getClass());
 149         return true;
 150     }
 151 
 152     private boolean compareObjects(Object first, Object second) {
 153         if (first == null) {
 154             return (second == null);
 155         }
 156         if (second == null) {
 157             return (first == null);
 158         }
 159         Class<?> clazz = first.getClass();
 160         if (clazz != second.getClass()) {
 161             return false;
 162         }
 163         if (!clazz.isArray()) {
 164             // Non arrays.
 165             if (clazz.equals(float.class) || clazz.equals(double.class)) {
 166                 return isEqualsFP((double) first, (double) second);
 167             } else {
 168                 return first.equals(second);
 169             }
 170         } else {
 171             // Handle the case where Objects are arrays.
 172             ArrayComparer comparer;
 173             if (clazz.equals(float[].class) || clazz.equals(double[].class)) {
 174                 comparer = new FPArrayComparer();
 175             } else if (clazz.equals(long[].class) || clazz.equals(int[].class) || clazz.equals(byte[].class)) {
 176                 comparer = new IntArrayComparer();
 177             } else if (clazz.equals(boolean[].class)) {
 178                 comparer = new BooleanArrayComparer();
 179             } else {
 180                 comparer = new ObjArrayComparer();
 181             }
 182             return comparer.compareArrays(first, second);
 183         }
 184     }
 185 
 186     static final int MISMATCHLIMIT = 10;
 187     static final int ELEMENTDISPLAYLIMIT = 20;
 188 
 189     public int getMisMatchLimit() {
 190         return MISMATCHLIMIT;
 191     }
 192 
 193     public int getElementDisplayLimit() {
 194         return ELEMENTDISPLAYLIMIT;
 195     }
 196 
 197     abstract class ArrayComparer {
 198 
 199         abstract Object getElement(Object ary, int index);
 200 
 201         // Equality test, can be overridden
 202         boolean isEquals(Object firstElement, Object secondElement) {
 203             return firstElement.equals(secondElement);
 204         }
 205 
 206         boolean compareArrays(Object first, Object second) {
 207             int len = Array.getLength(first);
 208             if (len != Array.getLength(second)) {
 209                 return false;
 210             }
 211             // If info logLevel, build string of first few elements from first array.
 212             if (logLevel.intValue() <= Level.INFO.intValue()) {
 213                 StringBuilder sb = new StringBuilder();
 214                 for (int i = 0; i < Math.min(len, getElementDisplayLimit()); i++) {
 215                     sb.append(getElement(first, i));
 216                     sb.append(", ");
 217                 }
 218                 logger.info(sb.toString());
 219             }
 220             boolean success = true;
 221             int mismatches = 0;
 222             for (int i = 0; i < len; i++) {
 223                 Object firstElement = getElement(first, i);
 224                 Object secondElement = getElement(second, i);
 225                 if (!isEquals(firstElement, secondElement)) {
 226                     logSevere("mismatch at index " + i + ", expected " + secondElement + ", saw " + firstElement);
 227                     success = false;
 228                     mismatches++;
 229                     if (mismatches >= getMisMatchLimit()) {
 230                         logSevere("...Truncated");
 231                         break;
 232                     }
 233                 }
 234             }
 235             return success;
 236         }
 237     }
 238 
 239     class FPArrayComparer extends ArrayComparer {
 240 
 241         @Override
 242         Object getElement(Object ary, int index) {
 243             return Array.getDouble(ary, index);
 244         }
 245 
 246         @Override
 247         boolean isEquals(Object firstElement, Object secondElement) {
 248             return isEqualsFP((double) firstElement, (double) secondElement);
 249         }
 250     }
 251 
 252     class IntArrayComparer extends ArrayComparer {
 253 
 254         @Override
 255         Object getElement(Object ary, int index) {
 256             return Array.getLong(ary, index);
 257         }
 258     }
 259 
 260     class BooleanArrayComparer extends ArrayComparer {
 261 
 262         @Override
 263         Object getElement(Object ary, int index) {
 264             return Array.getBoolean(ary, index);
 265         }
 266     }
 267 
 268     class ObjArrayComparer extends ArrayComparer {
 269 
 270         @Override
 271         Object getElement(Object ary, int index) {
 272             return Array.get(ary, index);
 273         }
 274 
 275         @Override
 276         boolean isEquals(Object firstElement, Object secondElement) {
 277             return compareObjects(firstElement, secondElement);
 278         }
 279     }
 280 
 281     /**
 282      * This isEqualsFP method allows subclass to override what FP equality means for this particular
 283      * unit test.
 284      */
 285     public boolean isEqualsFP(double first, double second) {
 286         return first == second;
 287     }
 288 
 289     public void setDispatchMode(DispatchMode dispatchMode) {
 290         this.dispatchMode = dispatchMode;
 291     }
 292 
 293     public void setHsailMode(HsailMode hsailMode) {
 294         this.hsailMode = hsailMode;
 295     }
 296 
 297     /**
 298      * Return a clone of this instance unless overridden, we just call the null constructor.
 299      */
 300     public KernelTester newInstance() {
 301         try {
 302             return this.getClass().getConstructor((Class<?>[]) null).newInstance();
 303         } catch (Throwable t) {
 304             fail("Unexpected exception " + t);
 305             return null;
 306         }
 307     }
 308 
 309     public Method getMethodFromMethodName(String methName, Class<?> clazz) {
 310         Class<?> clazz2 = clazz;
 311         while (clazz2 != null) {
 312             for (Method m : clazz2.getDeclaredMethods()) {
 313                 logger.fine(" in " + clazz2 + ", trying to match " + m);
 314                 if (m.getName().equals(methName)) {
 315                     testMethodParams = m.getParameterTypes();
 316                     if (logLevel.intValue() <= Level.FINE.intValue()) {
 317                         logger.fine(" in " + clazz2 + ", matched " + m);
 318                         logger.fine("parameter types are...");
 319                         int paramNum = 0;
 320                         for (Class<?> pclazz : testMethodParams) {
 321                             logger.fine(paramNum++ + ") " + pclazz.toString());
 322                         }
 323                     }
 324                     return m;
 325                 }
 326             }
 327             // Didn't find it in current clazz, try superclass.
 328             clazz2 = clazz2.getSuperclass();
 329         }
 330         // If we got this far, no match.
 331         return null;
 332     }
 333 
 334     private void setTestMethod(String methName, Class<?> inClazz) {
 335         testMethod = getMethodFromMethodName(methName, inClazz);
 336         if (testMethod == null) {
 337             fail("cannot find method " + methName + " in class " + inClazz);
 338         } else {
 339             // Print info but only for first such class.
 340             if (id == 1) {
 341                 logger.fine("testMethod to be compiled is \n   " + testMethod);
 342             }
 343         }
 344     }
 345 
 346     // Default is method name "run", but could be overridden.
 347     private final String defaultMethodName = "run";
 348 
 349     public String getTestMethodName() {
 350         return defaultMethodName;
 351     }
 352 
 353     /**
 354      * The dispatchMethodKernel dispatches a non-lambda method. All the parameters of the compiled
 355      * method are supplied as parameters to this call.
 356      */
 357     public void dispatchMethodKernel(int range, Object... args) {
 358         if (testMethod == null) {
 359             setTestMethod(getTestMethodName(), this.getClass());
 360         }
 361         if (dispatchMode == DispatchMode.SEQ) {
 362             dispatchMethodKernelSeq(range, args);
 363         } else if (dispatchMode == DispatchMode.OKRA) {
 364             dispatchMethodKernelOkra(range, args);
 365         }
 366     }
 367 
 368     /**
 369      * This dispatchLambdaMethodKernel dispatches the lambda version of a kernel where the "kernel"
 370      * is for the lambda method itself (like lambda$0).
 371      */
 372     public void dispatchLambdaMethodKernel(int range, MyIntConsumer consumer) {
 373         if (testMethod == null) {
 374             setTestMethod(findLambdaMethodName(), this.getClass());
 375         }
 376         if (dispatchMode == DispatchMode.SEQ) {
 377             dispatchLambdaKernelSeq(range, consumer);
 378         } else if (dispatchMode == DispatchMode.OKRA) {
 379             dispatchLambdaMethodKernelOkra(range, consumer);
 380         }
 381     }
 382 
 383     public void dispatchLambdaMethodKernel(Object[] ary, MyObjConsumer consumer) {
 384         if (testMethod == null) {
 385             setTestMethod(findLambdaMethodName(), this.getClass());
 386         }
 387         if (dispatchMode == DispatchMode.SEQ) {
 388             dispatchLambdaKernelSeq(ary, consumer);
 389         } else if (dispatchMode == DispatchMode.OKRA) {
 390             dispatchLambdaMethodKernelOkra(ary, consumer);
 391         }
 392     }
 393 
 394     /**
 395      * The dispatchLambdaKernel dispatches the lambda version of a kernel where the "kernel" is for
 396      * the xxx$$Lambda.accept method in the wrapper for the lambda. Note that the useLambdaMethod
 397      * boolean provides a way of actually invoking dispatchLambdaMethodKernel from this API.
 398      */
 399     public void dispatchLambdaKernel(int range, MyIntConsumer consumer) {
 400         if (useLambdaMethod) {
 401             dispatchLambdaMethodKernel(range, consumer);
 402             return;
 403         }
 404         if (testMethod == null) {
 405             setTestMethod("accept", consumer.getClass());
 406         }
 407         if (dispatchMode == DispatchMode.SEQ) {
 408             dispatchLambdaKernelSeq(range, consumer);
 409         } else if (dispatchMode == DispatchMode.OKRA) {
 410             dispatchLambdaKernelOkra(range, consumer);
 411         }
 412     }
 413 
 414     public void dispatchLambdaKernel(Object[] ary, MyObjConsumer consumer) {
 415         if (useLambdaMethod) {
 416             dispatchLambdaMethodKernel(ary, consumer);
 417             return;
 418         }
 419         if (testMethod == null) {
 420             setTestMethod("accept", consumer.getClass());
 421         }
 422         if (dispatchMode == DispatchMode.SEQ) {
 423             dispatchLambdaKernelSeq(ary, consumer);
 424         } else if (dispatchMode == DispatchMode.OKRA) {
 425             dispatchLambdaKernelOkra(ary, consumer);
 426         }
 427     }
 428 
 429     private ArrayList<String> getLambdaMethodNames() {
 430         Class<?> clazz = this.getClass();
 431         ArrayList<String> lambdaNames = new ArrayList<>();
 432         while (clazz != null && (lambdaNames.size() == 0)) {
 433             for (Method m : clazz.getDeclaredMethods()) {
 434                 logger.fine(" in " + clazz + ", trying to match " + m);
 435                 if (m.getName().startsWith("lambda$")) {
 436                     lambdaNames.add(m.getName());
 437                 }
 438             }
 439             // Didn't find it in current clazz, try superclass.
 440             clazz = clazz.getSuperclass();
 441         }
 442         return lambdaNames;
 443     }
 444 
 445     /**
 446      * findLambdaMethodName finds a name in the class starting with lambda$. If we find more than
 447      * one, throw an error, and tell user to override explicitly
 448      */
 449     private String findLambdaMethodName() {
 450         // If user overrode getTestMethodName, use that name.
 451         if (!getTestMethodName().equals(defaultMethodName)) {
 452             return getTestMethodName();
 453         } else {
 454             ArrayList<String> lambdaNames = getLambdaMethodNames();
 455             switch (lambdaNames.size()) {
 456                 case 1:
 457                     return lambdaNames.get(0);
 458                 case 0:
 459                     fail("No lambda method found in " + this.getClass());
 460                     return null;
 461                 default:
 462                     // More than one lambda.
 463                     String msg = "Multiple lambda methods found in " + this.getClass() + "\nYou should override getTestMethodName with one of the following\n";
 464                     for (String name : lambdaNames) {
 465                         msg = msg + name + "\n";
 466                     }
 467                     fail(msg);
 468                     return null;
 469             }
 470         }
 471     }
 472 
 473     /**
 474      * The getCompiledHSAILSource returns the string of HSAIL code for the compiled method. By
 475      * default, throws an error. In graal for instance, this would be overridden in
 476      * GraalKernelTester.
 477      */
 478     public String getCompiledHSAILSource(Method testMethod1) {
 479         fail("no compiler connected so unable to compile " + testMethod1 + "\nYou could try injecting HSAIL or OpenCL");
 480         return null;
 481     }
 482 
 483     public String getHSAILSource(Method testMethod1) {
 484         switch (hsailMode) {
 485             case COMPILED:
 486                 return getCompiledHSAILSource(testMethod1);
 487             case INJECT_HSAIL:
 488                 return getHsailFromClassnameHsailFile();
 489             case INJECT_OCL:
 490                 return getHsailFromClassnameOclFile();
 491             default:
 492                 fail("unknown hsailMode = " + hsailMode);
 493                 return null;
 494         }
 495     }
 496 
 497     /**
 498      * The getHSAILKernelName returns the name of the hsail kernel. By default we use 'run'. unless
 499      * coming from opencl injection. Could be overridden by the junit test.
 500      */
 501     public String getHSAILKernelName() {
 502         return (hsailMode != HsailMode.INJECT_OCL ? "&run" : "&__OpenCL_run_kernel");
 503     }
 504 
 505     private void createOkraKernel() {
 506         // Call routines in the derived class to get the hsail code and kernel name.
 507         String hsailSource = getHSAILSource(testMethod);
 508         if (!okraLibExists) {
 509             if (!gaveNoOkraWarning) {
 510                 logger.severe("No Okra library detected, skipping all KernelTester tests in " + this.getClass().getPackage().getName());
 511                 gaveNoOkraWarning = true;
 512             }
 513         }
 514         // Ignore any kerneltester test if okra does not exist.
 515         assumeTrue(okraLibExists);
 516         // Control which okra instances can run the tests.
 517         onSimulator = OkraContext.isSimulator();
 518         okraContext = new OkraContext();
 519         if (!okraContext.isValid()) {
 520             fail("...unable to create context");
 521         }
 522         // Control verbosity in okra from our logLevel.
 523         if (logLevel.intValue() <= Level.INFO.intValue()) {
 524             okraContext.setVerbose(true);
 525         }
 526         okraKernel = new OkraKernel(okraContext, hsailSource, getHSAILKernelName());
 527         if (!okraKernel.isValid()) {
 528             fail("...unable to create kernel");
 529         }
 530     }
 531 
 532     private void dispatchKernelOkra(int range, Object... args) {
 533         if (okraKernel == null) {
 534             createOkraKernel();
 535         }
 536         if (logLevel.intValue() <= Level.FINE.intValue()) {
 537             logger.fine("Arguments passed to okra...");
 538             for (Object arg : args) {
 539                 logger.fine("  " + arg);
 540             }
 541         }
 542         okraKernel.setLaunchAttributes(range);
 543         okraKernel.dispatchWithArgs(args);
 544     }
 545 
 546     private void dispatchMethodKernelSeq(int range, Object... args) {
 547         Object[] invokeArgs = new Object[args.length + 1];
 548         // Need space on the end for the gid parameter.
 549         System.arraycopy(args, 0, invokeArgs, 0, args.length);
 550         int gidArgIndex = invokeArgs.length - 1;
 551         if (logLevel.intValue() <= Level.FINE.intValue()) {
 552             for (Object arg : args) {
 553                 logger.fine(arg.toString());
 554             }
 555         }
 556         for (int rangeIndex = 0; rangeIndex < range; rangeIndex++) {
 557             invokeArgs[gidArgIndex] = rangeIndex;
 558             try {
 559                 testMethod.invoke(this, invokeArgs);
 560             } catch (IllegalAccessException e) {
 561                 fail("could not invoke " + testMethod + ", make sure it is public");
 562             } catch (IllegalArgumentException e) {
 563                 fail("wrong arguments invoking " + testMethod + ", check number and type of args passed to dispatchMethodKernel");
 564             } catch (InvocationTargetException e) {
 565                 Throwable cause = e.getCause();
 566                 /**
 567                  * We will ignore ArrayIndexOutOfBoundsException because the graal okra target
 568                  * doesn't really handle it yet (basically returns early if it sees one).
 569                  */
 570                 if (cause instanceof ArrayIndexOutOfBoundsException) {
 571                     logger.severe("ignoring ArrayIndexOutOfBoundsException for index " + rangeIndex);
 572                 } else {
 573                     // Other exceptions.
 574                     String errstr = testMethod + " threw an exception on gid=" + rangeIndex + ", exception was " + cause;
 575                     fail(errstr);
 576                 }
 577             } catch (Exception e) {
 578                 fail("Unknown exception " + e + " invoking " + testMethod);
 579             }
 580         }
 581     }
 582 
 583     private void dispatchMethodKernelOkra(int range, Object... args) {
 584         Object[] fixedArgs = fixArgTypes(args);
 585         if (Modifier.isStatic(testMethod.getModifiers())) {
 586             dispatchKernelOkra(range, fixedArgs);
 587         } else {
 588             // If it is a non-static method we have to push "this" as the first argument.
 589             Object[] newFixedArgs = new Object[fixedArgs.length + 1];
 590             System.arraycopy(fixedArgs, 0, newFixedArgs, 1, fixedArgs.length);
 591             newFixedArgs[0] = this;
 592             dispatchKernelOkra(range, newFixedArgs);
 593         }
 594     }
 595 
 596     /**
 597      * For primitive arg parameters, make sure arg types are cast to whatever the testMethod
 598      * signature says they should be.
 599      */
 600     private Object[] fixArgTypes(Object[] args) {
 601         Object[] fixedArgs = new Object[args.length];
 602         for (int i = 0; i < args.length; i++) {
 603             Class<?> paramClass = testMethodParams[i];
 604             if (paramClass.equals(Float.class) || paramClass.equals(float.class)) {
 605                 fixedArgs[i] = ((Number) args[i]).floatValue();
 606             } else if (paramClass.equals(Integer.class) || paramClass.equals(int.class)) {
 607                 fixedArgs[i] = ((Number) args[i]).intValue();
 608             } else if (paramClass.equals(Long.class) || paramClass.equals(long.class)) {
 609                 fixedArgs[i] = ((Number) args[i]).longValue();
 610             } else if (paramClass.equals(Double.class) || paramClass.equals(double.class)) {
 611                 fixedArgs[i] = ((Number) args[i]).doubleValue();
 612             } else if (paramClass.equals(Byte.class) || paramClass.equals(byte.class)) {
 613                 fixedArgs[i] = ((Number) args[i]).byteValue();
 614             } else if (paramClass.equals(Boolean.class) || paramClass.equals(boolean.class)) {
 615                 fixedArgs[i] = (boolean) args[i];
 616             } else {
 617                 // All others just move unchanged.
 618                 fixedArgs[i] = args[i];
 619             }
 620         }
 621         return fixedArgs;
 622     }
 623 
 624     /**
 625      * Dispatching a lambda on the java side is simple.
 626      */
 627     @SuppressWarnings("static-method")
 628     private void dispatchLambdaKernelSeq(int range, MyIntConsumer consumer) {
 629         for (int i = 0; i < range; i++) {
 630             consumer.accept(i);
 631         }
 632     }
 633 
 634     @SuppressWarnings("static-method")
 635     private void dispatchLambdaKernelSeq(Object[] ary, MyObjConsumer consumer) {
 636         for (Object obj : ary) {
 637             consumer.accept(obj);
 638         }
 639     }
 640 
 641     /**
 642      * The dispatchLambdaMethodKernelOkra dispatches in the case where the hsail kernel implements
 643      * the lambda method itself as opposed to the wrapper that calls the lambda method. From the
 644      * consumer object, we need to find the fields and pass them to the kernel.
 645      */
 646     private void dispatchLambdaMethodKernelOkra(int range, MyIntConsumer consumer) {
 647         logger.info("To determine parameters to pass to hsail kernel, we will examine   " + consumer.getClass());
 648         Field[] fields = consumer.getClass().getDeclaredFields();
 649         Object[] args = new Object[fields.length];
 650         int argIndex = 0;
 651         for (Field f : fields) {
 652             logger.info("... " + f);
 653             args[argIndex++] = getFieldFromObject(f, consumer);
 654         }
 655         dispatchKernelOkra(range, args);
 656     }
 657 
 658     private void dispatchLambdaMethodKernelOkra(Object[] ary, MyObjConsumer consumer) {
 659         logger.info("To determine parameters to pass to hsail kernel, we will examine   " + consumer.getClass());
 660         Field[] fields = consumer.getClass().getDeclaredFields();
 661         Object[] args = new Object[fields.length];
 662         int argIndex = 0;
 663         for (Field f : fields) {
 664             logger.info("... " + f);
 665             args[argIndex++] = getFieldFromObject(f, consumer);
 666         }
 667         dispatchKernelOkra(ary.length, args);
 668     }
 669 
 670     /**
 671      * The dispatchLambdaKernelOkra dispatches in the case where the hsail kernel where the hsail
 672      * kernel implements the accept method of the wrapper that calls the lambda method as opposed to
 673      * the actual lambda method itself.
 674      */
 675     private void dispatchLambdaKernelOkra(int range, MyIntConsumer consumer) {
 676         // The "wrapper" method always has only one arg consisting of the consumer.
 677         Object[] args = new Object[1];
 678         args[0] = consumer;
 679         dispatchKernelOkra(range, args);
 680     }
 681 
 682     private void dispatchLambdaKernelOkra(Object[] ary, MyObjConsumer consumer) {
 683         // The "wrapper" method always has only one arg consisting of the consumer.
 684         Object[] args = new Object[1];
 685         args[0] = consumer;
 686         dispatchKernelOkra(ary.length, args);
 687     }
 688 
 689     private void disposeKernelOkra() {
 690         if (okraContext != null) {
 691             okraContext.dispose();
 692         }
 693     }
 694 
 695     private void compareOkraToSeq(HsailMode hsailMode1) {
 696         compareOkraToSeq(hsailMode1, false);
 697     }
 698 
 699     /**
 700      * Runs this instance on OKRA, and as SEQ and compares the output of the two executions.
 701      */
 702     private void compareOkraToSeq(HsailMode hsailMode1, boolean useLambda) {
 703         // Create and run sequential instance.
 704         KernelTester testerSeq = newInstance();
 705         testerSeq.setDispatchMode(DispatchMode.SEQ);
 706         testerSeq.runTest();
 707         // Now do this object.
 708         this.setHsailMode(hsailMode1);
 709         this.setDispatchMode(DispatchMode.OKRA);
 710         this.useLambdaMethod = useLambda;
 711         this.runTest();
 712         this.disposeKernelOkra();
 713         assertTrue("failed comparison to SEQ", compareResults(testerSeq));
 714     }
 715 
 716     public void testGeneratedHsail() {
 717         compareOkraToSeq(HsailMode.COMPILED);
 718     }
 719 
 720     public void testGeneratedHsailUsingLambdaMethod() {
 721         compareOkraToSeq(HsailMode.COMPILED, true);
 722     }
 723 
 724     public void testInjectedHsail() {
 725         newInstance().compareOkraToSeq(HsailMode.INJECT_HSAIL);
 726     }
 727 
 728     public void testInjectedOpencl() {
 729         newInstance().compareOkraToSeq(HsailMode.INJECT_OCL);
 730     }
 731 
 732     private static Object getFieldFromObject(Field f, Object fromObj) {
 733         try {
 734             f.setAccessible(true);
 735             Type type = f.getType();
 736             logger.info("type = " + type);
 737             if (type == double.class) {
 738                 return f.getDouble(fromObj);
 739             } else if (type == float.class) {
 740                 return f.getFloat(fromObj);
 741             } else if (type == long.class) {
 742                 return f.getLong(fromObj);
 743             } else if (type == int.class) {
 744                 return f.getInt(fromObj);
 745             } else if (type == byte.class) {
 746                 return f.getByte(fromObj);
 747             } else if (type == boolean.class) {
 748                 return f.getBoolean(fromObj);
 749             } else {
 750                 return f.get(fromObj);
 751             }
 752         } catch (Exception e) {
 753             fail("unable to get field " + f + " from " + fromObj);
 754             return null;
 755         }
 756     }
 757 
 758     public static void checkFileExists(String fileName) {
 759         assertTrue(fileName + " does not exist", fileExists(fileName));
 760     }
 761 
 762     public static boolean fileExists(String fileName) {
 763         return new File(fileName).exists();
 764     }
 765 
 766     public static String getFileAsString(String sourceFileName) {
 767         String source = null;
 768         try {
 769             checkFileExists(sourceFileName);
 770             source = new String(Files.readAllBytes(FileSystems.getDefault().getPath(sourceFileName)));
 771         } catch (IOException e) {
 772             fail("could not open file " + sourceFileName);
 773             return null;
 774         }
 775         return source;
 776     }
 777 
 778     public static String getHsailFromFile(String sourceFileName) {
 779         logger.severe("... getting hsail from file " + sourceFileName);
 780         return getFileAsString(sourceFileName);
 781     }
 782 
 783     private static void executeCmd(String... cmd) {
 784         logger.info("spawning" + Arrays.toString(cmd));
 785         try {
 786             ProcessBuilder pb = new ProcessBuilder(cmd);
 787             Process p = pb.start();
 788             if (logLevel.intValue() <= Level.INFO.intValue()) {
 789                 InputStream in = p.getInputStream();
 790                 BufferedInputStream buf = new BufferedInputStream(in);
 791                 InputStreamReader inread = new InputStreamReader(buf);
 792                 BufferedReader bufferedreader = new BufferedReader(inread);
 793                 String line;
 794                 while ((line = bufferedreader.readLine()) != null) {
 795                     logger.info(line);
 796                 }
 797             }
 798             p.waitFor();
 799         } catch (Exception e) {
 800             fail("could not execute <" + Arrays.toString(cmd) + ">");
 801         }
 802     }
 803 
 804     public static String getHsailFromOpenCLFile(String openclFileName) {
 805         String openclHsailFile = "opencl_out.hsail";
 806         String tmpTahitiFile = "_temp_0_Tahiti.txt";
 807         checkFileExists(openclFileName);
 808         logger.severe("...converting " + openclFileName + " to HSAIL...");
 809         executeCmd("aoc2", "-m64", "-I./", "-march=hsail", openclFileName);
 810         if (fileExists(tmpTahitiFile)) {
 811             return getFileAsString(tmpTahitiFile);
 812         } else {
 813             executeCmd("HSAILasm", "-disassemble", "-o", openclHsailFile, openclFileName.replace(".cl", ".bin"));
 814             checkFileExists(openclHsailFile);
 815             return getFileAsString(openclHsailFile);
 816         }
 817     }
 818 
 819     public String getHsailFromClassnameHsailFile() {
 820         return (getHsailFromFile(this.getClass().getSimpleName() + ".hsail"));
 821     }
 822 
 823     public String getHsailFromClassnameOclFile() {
 824         return (getHsailFromOpenCLFile(this.getClass().getSimpleName() + ".cl"));
 825     }
 826 
 827     public static void logInfo(String msg) {
 828         logger.info(msg);
 829     }
 830 
 831     public static void logSevere(String msg) {
 832         logger.severe(msg);
 833     }
 834 
 835     public static void setLogLevel(Level level) {
 836         logLevel = level;
 837         logger.setLevel(level);
 838         consoleHandler.setLevel(level);
 839     }
 840 }