package de.gaalop.cpp; import de.gaalop.Notifications; import de.gaalop.cfg.*; import de.gaalop.dfg.*; import java.util.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * This visitor traverses the control and data flow graphs and generates C/C++ code. */ public class CppVisitor implements ControlFlowVisitor, ExpressionVisitor { protected Log log = LogFactory.getLog(CppVisitor.class); protected boolean standalone = true; protected final String suffix = "_opt"; protected StringBuilder code = new StringBuilder(); protected ControlFlowGraph graph; protected int indentation = 0; protected Set<String> assigned = new HashSet<String>(); protected String variableType = "float"; public CppVisitor(boolean standalone) { this.standalone = standalone; } public CppVisitor(boolean standalone, boolean useDouble) { this.standalone = standalone; if (useDouble) variableType = "double"; } public CppVisitor(String variableType) { this.standalone = false; this.variableType = variableType; } public void setStandalone(boolean standalone) { this.standalone = standalone; } public String getCode() { return code.toString(); } protected void appendIndentation() { for (int i = 0; i < indentation; ++i) { code.append('\t'); } } @Override public void visit(StartNode node) { graph = node.getGraph(); int bladeCount = graph.getAlgebraDefinitionFile().getBladeCount(); List<Variable> localVariables = sortVariables(graph.getLocalVariables()); if (standalone) { code.append("void calculate("); // Input Parameters List<Variable> inputParameters = sortVariables(graph.getInputVariables()); for (Variable var : inputParameters) { code.append(variableType).append(" "); // The assumption here is that they all are normal scalars code.append(var.getName()); code.append(", "); } for (Variable var : localVariables) { code.append(variableType).append(" "); code.append(var.getName()); code.append("["+bladeCount+"], "); } if (graph.getLocalVariables().size() > 0) { code.setLength(code.length() - 2); } code.append(") {\n"); indentation++; } else { for (Variable var : localVariables) { appendIndentation(); code.append(variableType).append(" "); code.append(var.getName()); code.append("["+bladeCount+"] = { 0.0 };\n"); } } if (graph.getScalarVariables().size() > 0) { appendIndentation(); code.append(variableType).append(" "); for (Variable tmp : graph.getScalarVariables()) { code.append(tmp.getName()); code.append(", "); } code.delete(code.length() - 2, code.length()); code.append(";\n"); } if (!graph.getLocalVariables().isEmpty()) { code.append("\n"); } node.getSuccessor().accept(this); } /** * Sorts a set of variables by name to make the order deterministic. * * @param inputVariables * @return */ protected List<Variable> sortVariables(Set<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; } @Override public void visit(AssignmentNode node) { String variable = node.getVariable().getName(); if (assigned.contains(variable)) { String message = "Variable " + variable + " has been reset for reuse."; log.warn(message); Notifications.addWarning(message); code.append("\n"); appendIndentation(); code.append("memset("); code.append(variable); code.append(", 0, sizeof("); code.append(variable); code.append(")); // Reset variable for reuse.\n"); assigned.remove(variable); } appendIndentation(); node.getVariable().accept(this); code.append(" = "); node.getValue().accept(this); code.append(";"); if (node.getVariable() instanceof MultivectorComponent) { code.append(" // "); MultivectorComponent component = (MultivectorComponent) node.getVariable(); code.append(node.getGraph().getAlgebraDefinitionFile().getBladeString(component.getBladeIndex())); } code.append('\n'); node.getSuccessor().accept(this); } @Override public void visit(ExpressionStatement node) { appendIndentation(); node.getExpression().accept(this); code.append(";\n"); node.getSuccessor().accept(this); } @Override public void visit(StoreResultNode node) { assigned.add(node.getValue().getName() + suffix); node.getSuccessor().accept(this); } @Override public void visit(IfThenElseNode node) { Expression condition = node.getCondition(); appendIndentation(); code.append("if ("); condition.accept(this); code.append(") {\n"); indentation++; node.getPositive().accept(this); indentation--; appendIndentation(); code.append("}"); if (node.getNegative() instanceof BlockEndNode) { code.append("\n"); } else { code.append(" else "); boolean isElseIf = false; if (node.getNegative() instanceof IfThenElseNode) { IfThenElseNode ifthenelse = (IfThenElseNode) node.getNegative(); isElseIf = ifthenelse.isElseIf(); } if (!isElseIf) { code.append("{\n"); indentation++; } node.getNegative().accept(this); if (!isElseIf) { indentation--; appendIndentation(); code.append("}\n"); } } node.getSuccessor().accept(this); } @Override public void visit(LoopNode node) { appendIndentation(); code.append("while(true) {\n"); indentation++; node.getBody().accept(this); indentation--; appendIndentation(); code.append("}\n"); node.getSuccessor().accept(this); } @Override public void visit(BreakNode breakNode) { appendIndentation(); code.append("break;\n"); } @Override public void visit(BlockEndNode node) { // nothing to do } @Override public void visit(EndNode node) { if (standalone) { indentation--; code.append("}\n"); } } @Override public void visit(ColorNode node) { node.getSuccessor().accept(this); } protected void addBinaryInfix(BinaryOperation op, String operator) { addChild(op, op.getLeft()); code.append(operator); addChild(op, op.getRight()); } protected void addChild(Expression parent, Expression child) { if (OperatorPriority.hasLowerPriority(parent, child)) { code.append('('); child.accept(this); code.append(')'); } else { child.accept(this); } } @Override public void visit(Subtraction subtraction) { addBinaryInfix(subtraction, " - "); } @Override public void visit(Addition addition) { addBinaryInfix(addition, " + "); } @Override public void visit(Division division) { addBinaryInfix(division, " / "); } @Override public void visit(InnerProduct innerProduct) { throw new UnsupportedOperationException("The C/C++ backend does not support the inner product."); } @Override public void visit(Multiplication multiplication) { addBinaryInfix(multiplication, " * "); } @Override public void visit(MathFunctionCall mathFunctionCall) { String funcName; switch (mathFunctionCall.getFunction()) { case ABS: funcName = "fabs"; break; case SQRT: funcName = "sqrtf"; break; default: funcName = mathFunctionCall.getFunction().toString().toLowerCase(); } code.append(funcName); code.append('('); mathFunctionCall.getOperand().accept(this); code.append(')'); } @Override public void visit(Variable variable) { // usually there are no code.append(variable.getName()); } @Override public void visit(MultivectorComponent component) { code.append(component.getName().replace(suffix, "")); code.append('['); code.append(component.getBladeIndex()); code.append(']'); } @Override public void visit(Exponentiation exponentiation) { if (isSquare(exponentiation)) { Multiplication m = new Multiplication(exponentiation.getLeft(), exponentiation.getLeft()); m.accept(this); } else { code.append("pow("); exponentiation.getLeft().accept(this); code.append(','); exponentiation.getRight().accept(this); code.append(')'); } } protected boolean isSquare(Exponentiation exponentiation) { final FloatConstant two = new FloatConstant(2.0f); return two.equals(exponentiation.getRight()); } @Override public void visit(FloatConstant floatConstant) { code.append(Double.toString(floatConstant.getValue())); //code.append('d'); } @Override public void visit(OuterProduct outerProduct) { throw new UnsupportedOperationException("The C/C++ backend does not support the outer product."); } @Override public void visit(BaseVector baseVector) { throw new UnsupportedOperationException("The C/C++ backend does not support base vectors."); } @Override public void visit(Negation negation) { code.append('('); code.append('-'); addChild(negation, negation.getOperand()); code.append(')'); } @Override public void visit(Reverse node) { throw new UnsupportedOperationException("The C/C++ backend does not support the reverse operation."); } @Override public void visit(LogicalOr node) { addBinaryInfix(node, " || "); } @Override public void visit(LogicalAnd node) { addBinaryInfix(node, " && "); } @Override public void visit(LogicalNegation node) { code.append('!'); addChild(node, node.getOperand()); } @Override public void visit(Equality node) { addBinaryInfix(node, " == "); } @Override public void visit(Inequality node) { addBinaryInfix(node, " != "); } @Override public void visit(Relation relation) { addBinaryInfix(relation, relation.getTypeString()); } @Override public void visit(Macro node) { throw new IllegalStateException("Macros should have been inlined and removed from the graph."); } @Override public void visit(FunctionArgument node) { throw new IllegalStateException("Macros should have been inlined and no function arguments should be the graph."); } @Override public void visit(MacroCall node) { throw new IllegalStateException("Macros should have been inlined and no macro calls should be in the graph."); } }