package de.gaalop.gaalet; import de.gaalop.NameTable; import de.gaalop.CheckGAVisitor; import de.gaalop.cfg.*; import de.gaalop.UsedVariablesVisitor; import de.gaalop.dfg.Expression; import de.gaalop.dfg.MacroCall; import de.gaalop.dfg.MultivectorComponent; import de.gaalop.dfg.Variable; import de.gaalop.dfg.MathFunction; import de.gaalop.dfg.MathFunctionCall; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * This is a utility class used by the CluCalcTransformer to build a control flow graph while parsing the CluCalc AST. */ public final class GraphBuilder { private class SetLocalAndInputVariables implements ControlFlowVisitor { SetLocalAndInputVariables() { // empty non-private constructor (prevent synthetic accessors) } /** * Searches the expression for variable references. If an undeclared reference is found, it is added to the * input variables of the graph. * * @param expression The expression to search in. * @param inMacro */ private void findUndeclaredVariables(Expression expression) { UsedVariablesVisitor visitor = new UsedVariablesVisitor(); expression.accept(visitor); for (Variable usedVariable : visitor.getVariables()) { checkIllegalVariable(usedVariable); if (!graph.getLocalVariables().contains(usedVariable)) { // in case we have pragmas giving ranges for the variable, add them if (graph.getPragmaMinValue().containsKey(usedVariable.getName())) { usedVariable.setMinValue(graph.getPragmaMinValue().get(usedVariable.getName())); } if (graph.getPragmaMaxValue().containsKey(usedVariable.getName())) { usedVariable.setMaxValue(graph.getPragmaMaxValue().get(usedVariable.getName())); } graph.addInputVariable(usedVariable); } } } @Override public void visit(StartNode node) { node.getSuccessor().accept(this); } @Override public void visit(AssignmentNode node) { findUndeclaredVariables(node.getValue()); //graph.addLocalVariable(node.getVariable()); node.getSuccessor().accept(this); } @Override public void visit(StoreResultNode node) { findUndeclaredVariables(node.getValue()); node.getSuccessor().accept(this); } @Override public void visit(IfThenElseNode node) { findUndeclaredVariables(node.getCondition()); node.getPositive().accept(this); node.getNegative().accept(this); node.getSuccessor().accept(this); } @Override public void visit(BlockEndNode node) { } @Override public void visit(LoopNode node) { node.getBody().accept(this); node.getSuccessor().accept(this); } @Override public void visit(BreakNode node) { } @Override public void visit(Macro node) { // ignore body of macro node.getSuccessor().accept(this); } @Override public void visit(ExpressionStatement node) { findUndeclaredVariables(node.getExpression()); node.getSuccessor().accept(this); } @Override public void visit(EndNode node) { } @Override public void visit(ColorNode node) { node.getSuccessor().accept(this); } } private final ControlFlowGraph graph; private SequentialNode lastNode; private HashMap<String, GaaletMultiVector> vectorSet = new HashMap<String, GaaletMultiVector>(); private int assignments; private Set<String> macros = new HashSet<String>(); private String currentMacroDefinition; private VariableScope currentScope = VariableScope.GLOBAL; private static final Map<String, String> illegalNames = new HashMap<String, String>(); static { // illegalNames = new HashMap<String, String>(); // illegalNames.put("B", "B is the metric matrix in Maple"); // illegalNames.put("condition_", "synthetic variable inserted by Gaalop"); // illegalNames.put("norm", "protected in Maple"); // illegalNames.put("normal", "protected in Maple"); // illegalNames.put("length", "protected in Maple"); // illegalNames.put("point", "protected in Maple"); } public GraphBuilder() { graph = new ControlFlowGraph(); lastNode = graph.getStartNode(); } void checkIllegalVariable(Variable variable) { String name = variable.getName(); String reason = illegalNames.get(name); if (reason != null) { throw new IllegalArgumentException("Illegal variable name '" + name + "' (" + reason + ")." + " Please use another variable."); } if (variable.getName().startsWith("re")) { throw new IllegalArgumentException("Variable '" + variable + "' cannot be used in Maple because of prefix 're' which is protected." + " Please choose another name."); } if (variable.getName().startsWith("condition_")) { throw new IllegalArgumentException("Variable '" + variable + "' cannot be used because of prefix 'condition_' which is used for conditional statements." + " Please choose another name."); } } public void beginNewScope() { currentScope = new VariableScope(currentScope); } public void endNewScope() { currentScope = currentScope.getParent(); } /** * Adds a variable name to the control flow graph as Pragma output marked. * * @param variable */ public void addPragmaOutputVariable(String variable) { graph.addPragmaOutputVariable(variable); } /** * Adds a pragma hint for a variable, which defines value range for it. The pragma must be set before the variable is added to * the input variables, i.e. the pragma must appear for the use of the variable */ public void addPragmaMinMaxValues(String variable, String min, String max) { graph.addPragmaMinMaxValues(variable, min, max); } /** * Adds a node to the end of the graph. * * @param node The node that should be added. */ private void addNode(SequentialNode node) { lastNode.insertAfter(node); lastNode = node; } /** * Adds a control variable for loops to the control flow graph. * * @param variable */ public void addIgnoreVariable(Variable variable) { //graph.addIgnoreVariable(variable); graph.addScalarVariable(variable); } public ExpressionStatement processExpressionStatement(Expression e) { ExpressionStatement statement = new ExpressionStatement(graph, e); addNode(statement); return statement; } public ControlFlowGraph getGraph() { return graph; } /** * Handles a loop statement. A CluCalc loop consists of a body of statements, typically containing the break * keyword. * * @param body list of statements belonging to body * @param iterations (optional) number of iterations for loop unrolling * @param counter (optional) counter variable * @return new {@link LoopNode} representing this statement. */ public LoopNode handleLoop(List<SequentialNode> body, String iterations, Variable counter) { LoopNode loop = new LoopNode(graph); addNode(loop); rewireNodes(body, loop); loop.setBody((body != null && body.size() > 0) ? body.get(0) : new BlockEndNode(graph, loop)); // save number of iterations and reset value for next loops if (iterations != null) { loop.setIterations(Integer.parseInt(iterations)); } // save counter variable for this loop if (counter != null) { if (CheckGAVisitor.isGAVariable(counter)) { throw new IllegalArgumentException("Counter variable " + counter + " is not scalar. Please use " + counter + " only as counter variable and do not assign Geometric Algebra expressions to it."); } loop.setCounterVariable(counter); graph.removeLocalVariable(counter); graph.addScalarVariable(counter); //graph.addIgnoreVariable(counter); } return loop; } /** * Handles the break keyword in a loop statement. * * @see #handleLoop(List) * @return new {@link BreakNode} representing this statement. */ public BreakNode handleBreak() { BreakNode brk = new BreakNode(graph); addNode(brk); return brk; } public Macro handleMacroDefinition(String id, List<SequentialNode> body, Expression ret) { // reset current macro name to allow further calls currentMacroDefinition = ""; Macro macro = new Macro(graph, id, body, ret); addNode(macro); graph.addMacro(macro); rewireNodes(body, macro); return macro; } public void addMacroName(String name) { String hashname = NameTable.getInstance().add(name); macros.add(hashname); currentMacroDefinition = hashname; } /** * Removes the nodes given by <code>list</code> from the control flow graph and rewires them to be a sequence of * separate nodes, e.g. in the body of an if-statement. The first node has <code>base</code> as predecessor. For the * last node in the list, base's successor will be set as successor, e.g. the first statement after an if-then-else * statement. * * @param list list of nodes from a block * @param base basis of block, e.g. an if-statement */ private void rewireNodes(List<SequentialNode> list, SequentialNode base) { if (list == null || list.size() == 0) { return; } Iterator<SequentialNode> it = list.iterator(); SequentialNode current = it.next(); graph.removeNode(current); current.addPredecessor(base); // first node has if-then-else node as predecessor while (it.hasNext()) { SequentialNode next = it.next(); graph.removeNode(next); current.replaceSuccessor(current.getSuccessor(), next); // could be redundant next.addPredecessor(current); current = next; } current.replaceSuccessor(current.getSuccessor(), new BlockEndNode(graph, base)); // mark the end of this block } protected int getNumberOfAssignments() { return assignments; } /** * Add an assignment node to the end of this graph. * * @param variable The variable that is assigned to. * @param expression The expression that is assigned to the variable. */ public AssignmentNode handleAssignment(Variable variable, Expression expression) { checkIllegalVariable(variable); CheckGAVisitor gaVisitor = new CheckGAVisitor(); expression.accept(gaVisitor); if (gaVisitor.isGA()) { //gaVisitor.addGAVariable(variable); } AssignmentNode assignment = new AssignmentNode(graph, variable, expression); addNode(assignment); return assignment; } /** * Add an assignment node to the end of this graph. * * @param name The name of the variable that is assigned to. * @param expression The expression that is assigned to the variable. */ public AssignmentNode handleAssignment(String name, Expression expression) { return handleAssignment(new Variable(name), expression); } /** * Handle a proecedure call. * * @param name The name of the procedure that was called. */ public void handleProcedure(String name) { /*//@changedalgebra String hashname = NameTable.getInstance().add(name); // Initialization of an algebra mode? for (AlgebraMode mode : ALGEBRA_MODES) { if (mode.getDefinitionMethod().equals(hashname)) { setMode(mode); return; } } throw new IllegalArgumentException("Unknown procedure: " + name + " "+ hashname); */ } /** * Handle the CluCalc print operator '?'. * * This only works for variables. * * @param variable The variable that should be printed. */ public StoreResultNode handlePrint(Expression variable) { if (variable instanceof Variable) { StoreResultNode storeResult = new StoreResultNode(graph, (Variable) variable); addNode(storeResult); return storeResult; } else { throw new IllegalArgumentException("Only variables can be marked for optimization."); } } public IfThenElseNode handleIfStatement(Expression condition, List<SequentialNode> then_part, List<SequentialNode> else_part) { IfThenElseNode ifthenelse = new IfThenElseNode(graph, condition); addNode(ifthenelse); rewireNodes(then_part, ifthenelse); rewireNodes(else_part, ifthenelse); ifthenelse.setPositive(then_part.get(0)); ifthenelse.setNegative((else_part != null && else_part.size() > 0) ? else_part.get(0) : new BlockEndNode(graph, lastNode)); return ifthenelse; } /** * Removes the nodes given by <code>list</code> from the control flow graph and rewires them to be a sequence of separate * nodes, e.g. in the body of an if-statement. The first node has <code>base</code> as predecessor. For the last node in the * list, base's successor will be set as successor, e.g. the first statement after an if-then-else statement. * * @param list list of nodes from a block * @param base basis of block, e.g. an if-statement */ private void rewireNodes(List<SequentialNode> list, IfThenElseNode base) { if (list == null || list.size() == 0) { return; } Iterator<SequentialNode> it = list.iterator(); SequentialNode current = it.next(); graph.removeNode(current); current.addPredecessor(base); // first node has if-then-else node as predecessor while (it.hasNext()) { SequentialNode next = it.next(); graph.removeNode(next); current.replaceSuccessor(current.getSuccessor(), next); // could be redundant next.addPredecessor(current); current = next; } current.replaceSuccessor(current.getSuccessor(), new BlockEndNode(graph, current)); // mark the end of this block } /** * Searches the expression for variable references. If an undeclared reference is found, it is added to the input variables of * the graph. * * @param expression The expression to search in. */ private void findUndeclaredVariables(Expression expression) { UsedVariablesVisitor visitor = new UsedVariablesVisitor(); expression.accept(visitor); for (Variable usedVariable : visitor.getVariables()) { if (!graph.getLocalVariables().contains(usedVariable)) { // in case we have pragmas giving ranges for the variable, add them if (graph.getPragmaMinValue().containsKey(usedVariable.getName())) { usedVariable.setMinValue(graph.getPragmaMinValue().get(usedVariable.getName())); } if (graph.getPragmaMaxValue().containsKey(usedVariable.getName())) { usedVariable.setMaxValue(graph.getPragmaMaxValue().get(usedVariable.getName())); } graph.addInputVariable(usedVariable); } } } // Creates an expression from an identifier and takes constants into account public Expression processIdentifier(String name) { /*//@changedalgebra String hashname = NameTable.getInstance().add(name); if (mode != null && mode.isConstant(hashname)) { return mode.getConstant(hashname); } else { return new Variable(hashname); } */ return new Variable(name); } public Expression processFunction(String name, List<Expression> args) { /*//@changedalgebra for (AlgebraMode mode : ALGEBRA_MODES) { if (mode.getDefinitionMethod().equals(name)) { setMode(mode); return null; } } // if (functionFactory.isDefined(name)) { // Expression[] argsArray = args.toArray(new Expression[args.size()]); // return functionFactory.createExpression(name, argsArray); // } for (MathFunction mathFunction : MathFunction.values()) { if (mathFunction.toString().toLowerCase().equals(name)) { if (args.size() == 1) { return new MathFunctionCall(args.get(0), mathFunction); } else { throw new IllegalArgumentException("Calling math function " + mathFunction + " with more than one" + " argument: " + args); } } } if (name.startsWith("::")) { name = name.substring(2); } String hashname = NameTable.getInstance().add(name); if (macros.contains(hashname)) { if (name.equals(currentMacroDefinition)) { throw new IllegalArgumentException("Recursive macro calls are not supported: " + name); } else { return new MacroCall(hashname, args); } } throw new IllegalArgumentException("Call to undefined function " + name + "(" + args + ").\n" + "Maybe this function is not defined in " + mode + "\n" + "Also make sure that macros are defined before they are called."); * */ return new MacroCall(name, args); } public SequentialNode handleDefGaalet(String name,ArrayList<String> blades, ArrayList<Expression> expression) { assignments++; String hashname = NameTable.getInstance().add(name); GaaletMultiVector muVec = new GaaletMultiVector(hashname); // we guess that blades and expression have the same size. for (int n=0; n<expression.size();n++) { findUndeclaredVariables(expression.get(n)); String hex = blades.get(n); int number; if ((hex.length()>1)&&(hex.charAt(1) =='x')) { hex = hex.substring(2); number = Integer.valueOf(hex,16); } else number = new Integer(hex); muVec.addGaaletTupel(number,expression.get(n)); } Variable variable; variable = new Variable(hashname); AssignmentNode assignment = new AssignmentNode(graph, variable, muVec.getExpression()); addNode(assignment); // assignmentNode cares about hashnames by itself vectorSet.put(hashname, muVec); wasDefined(hashname); System.out.println(muVec.getExpression()); return assignment; } public SequentialNode bladeAssignment(String name, String index, Expression expression) { assignments++; findUndeclaredVariables(expression); System.out.println("Blade Assignment"); Integer number = new Integer(index); // check needed. String hashname = NameTable.getInstance().add(name); if (vectorSet.get(hashname) == null) { System.err.println("Definition of variable " + name + " needed!!!"); } int gealgBlade = vectorSet.get(hashname).get(number); Variable variable = new MultivectorComponent(hashname, gealgBlade); AssignmentNode assignment = new AssignmentNode(graph, variable, expression); addNode(assignment); return assignment; } public void defineMV(String name, ArrayList<String> blades) { String hashname = NameTable.getInstance().add(name); GaaletMultiVector variable = new GaaletMultiVector(hashname); vectorSet.put(hashname, variable); for (String blade: blades) { vectorSet.get(hashname).addGaaletBlades(blade); } wasDefined(hashname); } public MultivectorComponent blade(String name, String blade) { int index = new Integer(blade); String hashname = NameTable.getInstance().add(name); int gealgBlade = vectorSet.get(hashname).get(index); return new MultivectorComponent(hashname, gealgBlade); } /** * Should be called to notify the graph builder that the parsing process has finished. If needed, post-processing of * the graph can be performed here. */ public void finish() { // for (Variable v : graph.getIgnoreVariables()) { // graph.removeLocalVariable(v); // } FindStoreOutputNodes outputNodes = new FindStoreOutputNodes(); graph.accept(outputNodes); //if (outputNodes.getNodes().isEmpty()) { // throw new RuntimeException("There are no lines marked for optimization ('?')"); //} SetCallerVisitor visitor = new SetCallerVisitor(); graph.accept(visitor); SetLocalAndInputVariables inputFinder = new SetLocalAndInputVariables(); graph.accept(inputFinder); } /** * Whenever a variable is defined, we want it to be also defined in the generated output. * * @param variableName The identifier of the variable. NOT the hashname given be NameTable. */ public void wasDefined(String variableName) { //String hashname = NameTable.getInstance().add(variableName); graph.addLocalVariable(new Variable(variableName)); } /** * Whenever a variable is defined, we want it to be also defined in the generated output. * * @param variableName The identifier of the variable. NOT the hashname given be NameTable. */ public void wasDefined(Variable variable) { wasDefined(variable.getName()); } }