package de.gaalop.cfg; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.LinkedList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.gaalop.InputFile; import de.gaalop.dfg.Expression; import de.gaalop.dfg.MacroCall; import de.gaalop.dfg.Variable; import java.util.*; /** * This class models a control dataflow graph. * <p/> * It is usually created by a <code>CodeParser</code> and then processed by an <code>OptimizationStrategy</code> before * it is again converted to source code by a <code>CodeGenerator</code>. * <p/> * A ControlFlowGraph instance only holds references to the first and last node of the graph, which are modeled by the * classes StartNode and EndNode. * * @author Sebastian Hartte * @author Christian Schwinn * @see de.gaalop.CodeGenerator * @see de.gaalop.CodeParser * @see de.gaalop.OptimizationStrategy * @see de.gaalop.cfg.StartNode * @see de.gaalop.cfg.EndNode */ public final class ControlFlowGraph { private Log log = LogFactory.getLog(ControlFlowGraph.class); private Set<Variable> localVariables = new HashSet<Variable>(); private Set<Variable> scalarVariables = new HashSet<Variable>(); private Set<Variable> inputVariables = new HashSet<Variable>(); private AlgebraDefinitionFile algebraDefinitionFile = new AlgebraDefinitionFile(); private final StartNode startNode; private final EndNode endNode; private InputFile source; private Map<String, Macro> macros = new HashMap<String, Macro>(); private Set<Slider> sliders = new HashSet<Slider>(); private ColorNode bgColor; /* store information about the pragmas */ private Set<String> pragmaOutputVariables = new HashSet<String>(); private Set<String> pragmaOnlyEvaluateVariables = new HashSet<String>(); private HashMap<String, String> pragmaMinValue = new HashMap<String, String>(); private HashMap<String, String> pragmaMaxValue = new HashMap<String, String>(); public LinkedList<ExpressionStatement> visualizerExpressions = new LinkedList<ExpressionStatement>(); private HashMap<String, Expression> renderingExpressions = new HashMap<String, Expression>(); public GlobalSettings globalSettings = new GlobalSettings(); public LinkedList<UnknownMacroCall> unknownMacros = new LinkedList<UnknownMacroCall>(); public String algebraName; public boolean asRessource; public String algebraBaseDirectory; public HashMap<String, Expression> getRenderingExpressions() { return renderingExpressions; } public void setRenderingExpressions(HashMap<String, Expression> renderingExpressions) { this.renderingExpressions = renderingExpressions; } public HashMap<String, String> getPragmaMaxValue() { return pragmaMaxValue; } public HashMap<String, String> getPragmaMinValue() { return pragmaMinValue; } public Set<String> getPragmaOutputVariables() { return pragmaOutputVariables; } public Set<String> getPragmaOnlyEvaluateVariables() { return pragmaOnlyEvaluateVariables; } public void addPragmaOutputVariable(String name) { pragmaOutputVariables.add(name); } public void addPragmaOnlyEvaluateVariable(String name) { pragmaOnlyEvaluateVariables.add(name); } public void addScalarVariable(Variable tempVariable) { scalarVariables.add(tempVariable); } public void removeScalarVariable(String name) { scalarVariables.remove(new Variable(name)); } public Set<Variable> getScalarVariables() { return scalarVariables; } /** * 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) { pragmaMaxValue.put(variable, max); pragmaMinValue.put(variable, min); } /** * Adds a new macro definition. * * @param macro new macro definition */ public void addMacro(Macro macro) { macros.put(macro.getName(), macro); } public void addSlider(Slider slider) { sliders .add(slider); } public Set<Slider> getSliders() { return sliders; } public void setBGColor(ColorNode color) { bgColor = color; } public ColorNode getBGColor() { return bgColor; } /** * Returns the macro definition of a macro with given name, if available. * * @param name name of macro to be returned * @return macro definition according to name or null, if not found */ public Macro getMacro(String name) { return macros.get(name); } /** * Returns a list of macro definitions that are contained in this graph. * * @return list of macro definitions */ public Set<Macro> getMacros() { return new HashSet<Macro>(macros.values()); } /** * Constructs a new control flow graph. * <p/> * A new graph contains the start and end node and the connection between them. */ public ControlFlowGraph() { startNode = new StartNode(this); endNode = new EndNode(this); startNode.setSuccessor(endNode); endNode.addPredecessor(startNode); } /** * Gets the last node in this control flow graph. * * @return The last node in the graph. */ public EndNode getEndNode() { return endNode; } /** * Gets the start node in this control flow graph. * * @return The first node of the control flow graph. */ public StartNode getStartNode() { return startNode; } /** * Gets the input file this graph was created from. * * @return The input file this graph was created from or null if it is unknown. */ public InputFile getSource() { return source; } /** * Sets the input file this graph was parsed from. * * @param source The input file this graph resulted from. */ public void setSource(InputFile source) { this.source = source; } /** * Gets the locally declared variables. * * @return An unmodifiable set containing the locally declared variables in this cfg. * @see #getInputVariables() */ public Set<Variable> getLocalVariables() { return Collections.unmodifiableSet(localVariables); } public Set<Variable> getLocalVariablesModifiable() { return localVariables; } /** * Gets the set of variables that are expected as input parameters for the algorithm modeled by this graph. * * @return An unmodifiable set containing the input variables for this graph. */ public Set<Variable> getInputVariables() { return Collections.unmodifiableSet(inputVariables); } /** * Adds a new local variable. * * @param variable The new local variable. * @throws IllegalArgumentException If <code>variable</code> is an input variable in this graph. */ public void addLocalVariable(Variable variable) { // Check that the given variable is not part of the inputVariables set if (inputVariables.contains(variable)) { throw new IllegalArgumentException("The Variable " + variable.getName() + " cannot be a local variable and an input variable at the same time."); } localVariables.add(variable); } /** * Removes a local variable from this graph. * <p/> * If <code>variable</code> is also an output variable, it is removed from that set as well. * * @param variable The variable that should be removed. */ public void removeLocalVariable(Variable variable) { localVariables.remove(variable); } /** * Determines whether there is a local variable with given name defined in this graph. * * @param name name of variable * @return true, if a local variable with given name exists, false otherwise */ public boolean containsLocalVariable(String name) { for (Variable v : localVariables) { if (v.getName().equals(name)) { return true; } } return false; } /** * Adds an input parameter. * * @param variable The new input parameter. * @throws IllegalArgumentException If <code>variable</code> is a local variable in this graph. */ public void addInputVariable(Variable variable) { String name = variable.getName(); if ("true".equals(name) || "false".equals(name)) { // ignore these keywords return; } // Check that the given variable is not part of the localVariables set if (localVariables.contains(variable)) { throw new IllegalArgumentException("The Variable " + name + " cannot be a local variable and an input variable at the same time."); } // if (name.contains("e")) { // throw new IllegalArgumentException("Input variable " + variable // + " contains 'e' which is not supported by Maple."); // } inputVariables.add(variable); } /** * Removes an input variable. * * @param variable The variable that should be removed. */ public void removeInputVariable(Variable variable) { inputVariables.remove(variable); } /** * Starts the traversal of this control flow graph using a visitor. * <p/> * Since the first node of the graph will always be a StartNode object, this method forwards the call to * {@link StartNode#accept(ControlFlowVisitor)}. * * @param visitor The visitor that provides the callback methods. */ public void accept(ControlFlowVisitor visitor) { startNode.accept(visitor); } /** * Removes the given node from the control flow graph. The graph will be traversed in order to find the node to be * removed. Once the desired node is found, its predecessors are rewired to have the old node's successor as direct * successors. * * @param node node to be removed */ public void removeNode(SequentialNode node) { Node successor = node.getSuccessor(); successor.removePredecessor(node); Set<Node> predecessors = new HashSet<Node>(node.getPredecessors()); for (Node predecessor : predecessors) { successor.addPredecessor(predecessor); predecessor.replaceSuccessor(node, successor); } } @Override public String toString() { SequentialNode curr = startNode; StringBuilder sb = new StringBuilder(); sb.append("CFG:\n"); sb.append(startNode); do { sb.append("\n--> "); if (curr.getSuccessor() instanceof SequentialNode) { curr = (SequentialNode) curr.getSuccessor(); sb.append(curr); } else { Node end = curr.getSuccessor(); sb.append(end); break; } } while (true); return sb.toString(); } public String prettyPrint() { Printer printer = new Printer(); accept(printer); return printer.getCode(); } private static class Printer implements ControlFlowVisitor { private int indent = 0; private StringBuilder code = new StringBuilder(); Printer() { // empty non-private constructor } String getCode() { return code.toString(); } private void appendIndent() { for (int i = 0; i < indent; i++) { code.append('\t'); } } @Override public void visit(StartNode node) { code.append("START\n"); node.getSuccessor().accept(this); } @Override public void visit(AssignmentNode node) { appendIndent(); code.append(node.getVariable()); code.append(" = "); code.append(node.getValue()); code.append(";\n"); node.getSuccessor().accept(this); } @Override public void visit(StoreResultNode node) { appendIndent(); code.append(node); code.append(";\n"); node.getSuccessor().accept(this); } @Override public void visit(IfThenElseNode node) { appendIndent(); code.append("if ("); code.append(node.getCondition()); code.append(") {\n"); indent++; node.getPositive().accept(this); indent--; appendIndent(); code.append("} else {"); indent++; node.getNegative().accept(this); indent--; appendIndent(); code.append("}\n"); node.getSuccessor().accept(this); } @Override public void visit(BlockEndNode node) { } @Override public void visit(LoopNode node) { appendIndent(); code.append("loop {\n"); indent++; node.getBody().accept(this); indent--; appendIndent(); code.append("}\n"); node.getSuccessor().accept(this); } @Override public void visit(BreakNode node) { appendIndent(); code.append("break;\n"); } @Override public void visit(Macro node) { appendIndent(); code.append(node.getName()); code.append(" = {\n"); indent++; if (node.getBody().size() > 0) { node.getBody().get(0).accept(this); } if (node.getReturnValue() != null) { appendIndent(); code.append(node.getReturnValue()); } indent--; appendIndent(); code.append("}\n"); node.getSuccessor().accept(this); } @Override public void visit(ExpressionStatement node) { appendIndent(); code.append(node); node.getSuccessor().accept(this); } @Override public void visit(EndNode node) { code.append("END"); } @Override public void visit(ColorNode node) { appendIndent(); code.append(":"); code.append(node); } } public AlgebraDefinitionFile getAlgebraDefinitionFile() { return algebraDefinitionFile; } public void setAlgebraDefinitionFile(AlgebraDefinitionFile algebraDefinitionFile) { this.algebraDefinitionFile = algebraDefinitionFile; } }