package de.gaalop.testbench; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Scanner; import de.gaalop.CodeParserException; import de.gaalop.InputFile; import de.gaalop.OptimizationException; import de.gaalop.OptimizationStrategy; import de.gaalop.cfg.ControlFlowGraph; import de.gaalop.cfg.FindStoreOutputNodes; import de.gaalop.cfg.StoreResultNode; import de.gaalop.clucalc.input.CluCalcCodeParser; import de.gaalop.clucalc.output.ExtendedCLUCalcGenerator; import de.gaalop.cpp.CppVisitor; import de.gaalop.dfg.Variable; public class TestbenchGenerator { private final int MAX_MV_LENGTH = 2048; private final String CLU_BLADES = "string CLU_BLADES[32] = {\n" + "\t\"1\",\n" + "\t\"e1\", \"e2\", \"e3\", \"e\", \"e0\",\n" + "\t\"e12\", \"e31\", \"(e1^e)\", \"(e1^e0)\", \"e23\", \"(e2^e)\", \"(e2^e0)\", \"(e3^e)\", \"(e3^e0)\", \"E\",\n" + "\t\"e123\", \"(e12^e)\", \"(e12^e0)\", \"(e31^e)\", \"(e31^e0)\", \"(e1^E)\", \"(e23^e)\", \"(e23^e0)\", \"(e2^E)\", \"(e3^E)\",\n" + "\t\"(e123^e)\", \"(e123^e0)\", \"(e12^E)\", \"(e31^E)\", \"(e23^E)\",\n" + "\t\"I\" \n" + "};\n\n"; private File path; private String fileName; private String originalFileName; private String optFileName; private String cppFileName; private String compileScriptName; private InputFile inputFile; private final CluCalcCodeParser parser = CluCalcCodeParser.INSTANCE; private final OptimizationStrategy optimizer; private List<Variable> inputVariables; private List<Variable> outputVariables; private final Map<String, Float> inputValues; /** * Creates a new testbench generator for given file, path and input values. * * @param fileName * @param path * @param inputValues */ public TestbenchGenerator(String fileName, String path, Map<String, Float> inputValues) { optimizer = new de.gaalop.maple.Plugin().createOptimizationStrategy(); this.path = new File(path); this.inputValues = inputValues; try { File file = new File(fileName); System.out.println("Generating testbench for file " + file); this.fileName = file.getName().substring(0, file.getName().indexOf(".clu")); inputFile = readFile(file); } catch (IOException e) { e.printStackTrace(); } } /** * Creates a new testbench generator for given file name and contents, path and input variables. No file is read. * * @param fileName * @param content * @param path * @param inputValues */ public TestbenchGenerator(String fileName, String content, String path, Map<String, Float> inputValues) { optimizer = new de.gaalop.maple.Plugin().createOptimizationStrategy(); this.path = new File(path); this.inputValues = inputValues; File file = new File(fileName); System.out.println("Generating testbench for file " + file); this.fileName = file.getName().substring(0, file.getName().indexOf(".clu")); inputFile = new InputFile(file.getName(), content); } public static void main(String[] args) throws CodeParserException, OptimizationException { if (args.length < 2) { throw new IllegalArgumentException("Must provide file name and path to scripts"); } TestbenchGenerator generator = new TestbenchGenerator(args[0], args[1], null); generator.run(); } public void run() throws CodeParserException, OptimizationException { ControlFlowGraph graph = parser.parseFile(inputFile); originalFileName = generateExtendedCluFile(graph, false); optimizer.transform(graph); optFileName = generateExtendedCluFile(graph, true); cppFileName = generateTestbench(graph); System.out.println("Testbench has been generated successfully."); } /** * Generates a batch script to compile the testbench. To set the environment variables for the Visual Studio C++ * compiler, the include and library paths have to be specified. These will be used in the script. Additionally, the * environment variable <code>VCINSTALLDIR</code> is expected to be set to the Visual Studio installation. * * @param include * @param libpath */ public void createCompileScript(String include, String libpath) { StringBuilder code = new StringBuilder(); String linesep = System.getProperty("line.separator"); code.append("call \"%VCINSTALLDIR%\\vcvarsall.bat\"" + linesep); code.append("echo Compiling testbench..." + linesep); code.append("cl /I\""); code.append(include); code.append("\" "); code.append(cppFileName); code.append(" /link /LIBPATH:\""); code.append(libpath); code.append("\" CLUViz.lib" + linesep); compileScriptName = "compile-" + fileName + ".bat"; writeFile(code.toString(), compileScriptName); } /** * Compiles the testbench using the batch script created by {@link #createCompileScript(String, String)}. * * @return the executable file for the testbench * @see #createCompileScript(String, String) */ public File compile() { File script = new File(path + System.getProperty("file.separator") + compileScriptName); ProcessBuilder pb = new ProcessBuilder(script.getAbsolutePath()); pb.directory(script.getParentFile()); try { System.out.println("Calling " + script); Process p = pb.start(); Scanner scanner = new Scanner(p.getInputStream()); while (scanner.hasNextLine()) { System.out.println(scanner.nextLine()); } scanner.close(); } catch (IOException e) { e.printStackTrace(); } return new File(path + System.getProperty("file.separator") + fileName + ".exe"); } private InputFile readFile(File file) throws IOException { StringBuilder result = new StringBuilder(); BufferedReader in = new BufferedReader(new FileReader(file)); try { String line; while ((line = in.readLine()) != null) { result.append(line); result.append('\n'); } } finally { in.close(); } return new InputFile(file.getName(), result.toString()); } private String generateExtendedCluFile(ControlFlowGraph graph, boolean opt) { ExtendedCLUCalcGenerator generator = new ExtendedCLUCalcGenerator("_opt", opt); graph.accept(generator); String code = generator.getCode(); String suffix = ".clu"; if (opt) { suffix = "_opt" + suffix; } String name = fileName + suffix; writeFile(code, name); return name; } private void writeFile(String code, String fileName) { File file = new File(path.getAbsolutePath() + System.getProperty("file.separator") + fileName); try { PrintWriter writer = new PrintWriter(file); writer.print(code); writer.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } System.out.println("File " + file + " has been generated successfully."); } private String generateTestbench(ControlFlowGraph graph) { StringBuilder code = new StringBuilder(); // include directives code.append("#include \"cluviz/CLUVizDLL.h\"\n"); code.append("#include <iostream>\n"); code.append("#include <string>\n"); code.append("\nusing namespace std;\n\n"); code.append("const char* dir = \""); code.append(path.getAbsolutePath().replace(System.getProperty("file.separator"), "/")); code.append("/\";\n\n"); code.append(CLU_BLADES); addPrint(code); addCppCalculate(code, graph); addCluCalculate(code, graph); addMain(code, graph); String name = fileName + ".cpp"; writeFile(code.toString(), name); return name; } private void addPrint(StringBuilder code) { code.append("void printCluMV(float mv[32]) {\n"); code.append("\tbool firstEntry = true;\n"); code.append("\tfor (int i = 0; i < 32; i++) {\n"); code.append("\t\tfloat value = mv[i];\n"); code.append("\t\tif (value != 0) {\n"); code.append("\t\t\tstring blade = i == 0 ? \"\" : \"^\" + CLU_BLADES[i];\n"); code.append("\t\t\tif (i == 7 || i == 19 || i == 20 || i == 29) {\n"); code.append("\t\t\t\tvalue = -value; // special handling for CLUCalc's handling of e1^e3 which is -e31\n"); code.append("\t\t\t}\n"); code.append("\t\t\tif (firstEntry) {\n"); code.append("\t\t\t\tcout << value << blade;\n"); code.append("\t\t\t\tfirstEntry = false;\n"); code.append("\t\t\t} else {\n"); code.append("\t\t\t\tif (value > 0) {\n"); code.append("\t\t\t\t\tcout << \" + \"; \n"); code.append("\t\t\t\t} else {\n"); code.append("\t\t\t\t\tcout << \" - \"; \n"); code.append("\t\t\t\t}\n"); code.append("\t\t\t\tcout << fabs(value) << blade;\n"); code.append("\t\t\t}\n"); code.append("\t\t}\n"); code.append("\t}\n"); code.append("\tif (firstEntry) {\n"); code.append("\t\tcout << 0;\n"); code.append("\t}\n"); code.append("\tcout << endl;\n"); code.append("}\n\n"); } private void addCppCalculate(StringBuilder code, ControlFlowGraph graph) { CppVisitor visitor = new CppVisitor(true); graph.accept(visitor); code.append(visitor.getCode()); code.append("\n"); } private List<Variable> sortVariables(Collection<Variable> inputVariables) { List<Variable> variables = new ArrayList<Variable>(inputVariables); Comparator<Variable> comparator = new Comparator<Variable>() { @Override public int compare(Variable o1, Variable o2) { return o1.getName().compareToIgnoreCase(o2.getName()); } }; Collections.sort(variables, comparator); return variables; } private void addCluCalculate(StringBuilder code, ControlFlowGraph graph) { code.append("void calculateCLU(char* fileName"); // create list to keep ordering consistent inputVariables = sortVariables(graph.getInputVariables()); for (Variable v : inputVariables) { code.append(", float "); code.append(v.getName()); } code.append(") {\n"); code.append("\tchar pcDir[1024], *pcPos;\n"); code.append("\tstrncpy_s( pcDir, (const char *) dir, 1023 );\n"); code.append("\tpcPos = strrchr( pcDir, '/' );\n"); code.append("\t*pcPos = 0; \n\n"); code.append("\tint iVizHandle = 0;\n"); code.append("\tCLUViz::Start();\n"); code.append("\tCLUViz::SetScriptPath(pcDir);\n"); code.append("\tCLUViz::Create( iVizHandle, 0, 0, 0, 0, \"Gaalop Testbench\" );\n"); code.append("\tCLUViz::LoadScript( iVizHandle, fileName );\n\n"); for (Variable v : inputVariables) { code.append("\tCLUViz::SetVarNumber(iVizHandle, "); code.append("\""); code.append(v.getName()); code.append("\", "); code.append(v.getName()); code.append(");\n"); } code.append("\t\n"); code.append("\tCLUViz::ExecUser(iVizHandle, \"calculate\");\n"); code.append("\tCLUViz::ExecUser(iVizHandle, \"toString\");\n\n"); FindStoreOutputNodes visitor = new FindStoreOutputNodes(); graph.accept(visitor); List<StoreResultNode> nodes = visitor.getNodes(); outputVariables = new ArrayList<Variable>(); for (StoreResultNode node : nodes) { outputVariables.add(node.getValue()); } for (Variable mv : outputVariables) { code.append("\tchar "); String name = mv.getName(); code.append(name); code.append("["); code.append(MAX_MV_LENGTH + 1); code.append("];\n"); code.append("\tCLUViz::GetVarString( iVizHandle, \""); code.append(name); code.append("\", "); code.append(name); code.append(", "); code.append(MAX_MV_LENGTH); code.append(");\n"); code.append("\tprintf(\"%s\\n\", "); code.append(name); code.append(");\n"); } code.append("\tCLUViz::Destroy(iVizHandle);\n"); code.append("}\n\n"); } private void addMain(StringBuilder code, ControlFlowGraph graph) { code.append("int main(int argc, char* argv[]) {\n"); for (Variable v : inputVariables) { code.append("\tfloat "); String name = v.getName(); code.append(name); code.append(" = "); float value = 0.0f; if (inputValues != null && inputValues.get(name) != null) { value = inputValues.get(name); } code.append(value); code.append(";\n"); } for (Variable mv : outputVariables) { code.append("\tfloat "); code.append(mv.getName()); code.append("[32] = { 0.0f };\n"); } code.append("\n"); code.append("\tcout << \"C++ output:\" << endl;\n"); code.append("\tcalculate("); for (Variable v : inputVariables) { code.append(v.getName()); code.append(", "); } // keep consistent with method declaration, so use a sorted list List<Variable> sortedOutputVariables = sortVariables(outputVariables); for (Variable mv : sortedOutputVariables) { code.append(mv.getName()); code.append(", "); } code.delete(code.length() - 2, code.length()); code.append(");\n"); for (Variable mv : outputVariables) { code.append("\tprintCluMV("); code.append(mv.getName()); code.append(");\n\n"); } code.append("\tcout << \"Original file output:\" << endl;\n"); code.append("\tcalculateCLU(\""); code.append(originalFileName); code.append("\""); for (Variable v : inputVariables) { code.append(", "); code.append(v.getName()); } code.append(");\n"); code.append("\tcout << \"Optimized CLUCalc file output:\" << endl;\n"); code.append("\tcalculateCLU(\""); code.append(optFileName); code.append("\""); for (Variable v : inputVariables) { code.append(", "); code.append(v.getName()); } code.append(");\n\n"); code.append("\treturn 0;\n"); code.append("}\n"); } }