package de.gaalop.maple;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.gaalop.Notifications;
import de.gaalop.cfg.AssignmentNode;
import de.gaalop.cfg.BlockEndNode;
import de.gaalop.cfg.BreakNode;
import de.gaalop.cfg.ColorNode;
import de.gaalop.cfg.ControlFlowGraph;
import de.gaalop.cfg.ControlFlowVisitor;
import de.gaalop.cfg.EmptyControlFlowVisitor;
import de.gaalop.cfg.EndNode;
import de.gaalop.cfg.ExpressionStatement;
import de.gaalop.cfg.IfThenElseNode;
import de.gaalop.cfg.LoopNode;
import de.gaalop.cfg.Macro;
import de.gaalop.cfg.Node;
import de.gaalop.cfg.SequentialNode;
import de.gaalop.cfg.StartNode;
import de.gaalop.cfg.StoreResultNode;
import de.gaalop.dfg.BinaryOperation;
import de.gaalop.dfg.EmptyExpressionVisitor;
import de.gaalop.dfg.Equality;
import de.gaalop.dfg.Expression;
import de.gaalop.dfg.ExpressionFactory;
import de.gaalop.dfg.FloatConstant;
import de.gaalop.dfg.Inequality;
import de.gaalop.dfg.Multiplication;
import de.gaalop.dfg.MultivectorComponent;
import de.gaalop.dfg.Relation;
import de.gaalop.dfg.Subtraction;
import de.gaalop.dfg.Variable;
import de.gaalop.maple.engine.MapleEngine;
import de.gaalop.maple.engine.MapleEngineException;
import de.gaalop.maple.parser.MapleLexer;
import de.gaalop.maple.parser.MapleParser;
import de.gaalop.maple.parser.MapleTransformer;
/**
* This visitor creates code for Maple.
*/
public class MapleCfgVisitor implements ControlFlowVisitor {
/**
* This visitor removes assignments to unused helper variables, e.g. resulting from inlined macros or loops.
*
* @author Christian Schwinn
*
*/
private class RemoveUnusedAssignmentsVisitor extends EmptyControlFlowVisitor {
private final Set<String> unusedNames = new HashSet<String>();
public RemoveUnusedAssignmentsVisitor(Set<MultivectorComponent> components) {
for (MultivectorComponent comp : components) {
unusedNames.add(getTempVarName(comp));
}
}
@Override
public void visit(AssignmentNode node) {
Node successor = node.getSuccessor();
String name = node.getVariable().getName();
if (unusedNames.contains(name)) {
node.getGraph().removeNode(node);
node.getGraph().removeScalarVariable(name);
}
successor.accept(this);
}
}
/**
* This visitor removes empty nodes like {@link IfThenElseNode} from the graph. During loop unrolling, conditional
* statements could have become empty because of the removal of <code>break</code> statements.
*
* @author Christian Schwinn
*
*/
private static class RemoveEmptyNodesVisitor extends EmptyControlFlowVisitor {
public RemoveEmptyNodesVisitor() {
}
@Override
public void visit(IfThenElseNode node) {
Node successor = node.getSuccessor();
SequentialNode positive = node.getPositive();
SequentialNode negative = node.getNegative();
if (positive instanceof BlockEndNode && negative instanceof BlockEndNode) {
node.getGraph().removeNode(node);
} else {
positive.accept(this);
negative.accept(this);
}
successor.accept(this);
}
}
/**
* This class checks variables for reuse after conditional statements or loops. The detection of these variables is
* necessary to trigger a forced optimization for assignments within an {@link IfThenElseNode} or {@link LoopNode}.
* For each assignment to a variable within a {@link Block} the current hierarchy of blocks is saved in a stack,
* representing the current scope. If the assigned variable is reused later, the scope of the variable reuse is
* compared with the last scope of the assignment. If the variable is identified to be reused, it is saved in the
* list of reused variables, which can be queried by {@link CheckDependencyVisitor#getDependentVariables()} after
* the graph has been traversed.
*
* @author Christian Schwinn
*
*/
private static class CheckDependencyVisitor extends EmptyControlFlowVisitor {
private enum Branch {
POSITIVE, NEGATIVE, LOOP
}
private static class Block {
private final SequentialNode node;
private final Branch branch;
public Block(IfThenElseNode node, Branch branch) {
this.node = node;
this.branch = branch;
}
public Block(LoopNode node) {
this.node = node;
this.branch = Branch.LOOP;
}
public SequentialNode getNode() {
return node;
}
public Branch getBranch() {
return branch;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Block) {
Block block = (Block) obj;
if (node == block.getNode() && branch == block.getBranch()) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return node.hashCode() + branch.hashCode();
}
@Override
public String toString() {
return "BLOCK[" + node + ":" + branch + "]";
}
}
private Set<Variable> optVariables = new HashSet<Variable>();
private Stack<Block> hierarchy = new Stack<Block>();
private Map<Variable, List<Block>> currentStack = new HashMap<Variable, List<Block>>();
private boolean loopMode = false;
public CheckDependencyVisitor() {
}
public Set<Variable> getDependentVariables() {
return optVariables;
}
@Override
public void visit(IfThenElseNode node) {
hierarchy.push(new Block(node, Branch.POSITIVE));
node.getPositive().accept(this);
hierarchy.pop();
hierarchy.push(new Block(node, Branch.NEGATIVE));
node.getNegative().accept(this);
hierarchy.pop();
node.getSuccessor().accept(this);
}
@Override
public void visit(LoopNode node) {
boolean oldLoopMode = loopMode;
loopMode = true;
hierarchy.push(new Block(node));
node.getBody().accept(this);
hierarchy.pop();
loopMode = oldLoopMode;
node.getSuccessor().accept(this);
}
@Override
public void visit(AssignmentNode node) {
Variable variable = node.getVariable();
Set<Variable> usedVariables = checkVariables(node.getValue());
boolean recursive = usedVariables.contains(variable);
if (loopMode && recursive) {
optVariables.add(variable);
}
if (!hierarchy.empty()) {
currentStack.put(variable, new ArrayList<Block>(hierarchy));
}
node.getSuccessor().accept(this);
}
@Override
public void visit(StoreResultNode node) {
Variable variable = node.getValue();
checkVariable(variable);
node.getSuccessor().accept(this);
}
@Override
public void visit(ExpressionStatement node) {
checkVariables(node.getExpression());
node.getSuccessor().accept(this);
}
private Set<Variable> checkVariables(Expression expression) {
de.gaalop.UsedVariablesVisitor usedVariables = new de.gaalop.UsedVariablesVisitor();
expression.accept(usedVariables);
// check for common hierarchy
for (Variable v : usedVariables.getVariables()) {
checkVariable(v);
}
return usedVariables.getVariables();
}
private void checkVariable(Variable v) {
if (checkHierarchy(v)) {
optVariables.add(v);
}
}
private boolean checkHierarchy(Variable v) {
List<Block> vStack = currentStack.get(v);
if (vStack == null) {
return false;
}
List<Block> variableList = new ArrayList<Block>(vStack);
List<Block> currentList = new ArrayList<Block>(hierarchy);
int min = Math.min(variableList.size(), currentList.size());
if (min == 0) {
// found reuse in global space
return true;
}
for (int i = 0; i < min; i++) {
Block variableBlock = variableList.get(i);
Block currentBlock = currentList.get(i);
if (variableBlock.getNode() != currentBlock.getNode()) {
return true;
} else if (variableBlock.getBranch() != currentBlock.getBranch()) {
// reuse in different branches of if-statement
return false;
}
}
if (variableList.size() > currentList.size()) {
return true;
}
return false;
}
}
/**
* Simple visitor used to inline the relevant branch of an if-statement. The other branch is cut off and the
* original {@link IfThenElseNode} is removed from the graph.
*
* @author Christian Schwinn
*
*/
private class InlineBlockVisitor extends EmptyControlFlowVisitor {
private final IfThenElseNode root;
private final Node branch;
private final Node successor;
/**
* Creates a new visitor with given root and branch.
*
* @param root root node from which to inline a branch
* @param branch first node of branch to be inlined
*/
public InlineBlockVisitor(IfThenElseNode root, Node branch) {
this.root = root;
this.branch = branch;
successor = root.getSuccessor();
}
private void replaceSuccessor(Node oldSuccessor, Node newSuccessor) {
Set<Node> predecessors = new HashSet<Node>(oldSuccessor.getPredecessors());
for (Node p : predecessors) {
p.replaceSuccessor(oldSuccessor, newSuccessor);
}
}
@Override
public void visit(IfThenElseNode node) {
// we peek only to next level of nested statements
if (node == root) {
if (node.getPositive() == branch) {
if (!(branch instanceof BlockEndNode)) {
replaceSuccessor(node, branch);
}
node.getPositive().accept(this);
} else if (node.getNegative() == branch) {
if (!(branch instanceof BlockEndNode)) {
replaceSuccessor(node, branch);
}
node.getNegative().accept(this);
}
graph.removeNode(node);
}
node.getSuccessor().accept(this);
}
@Override
public void visit(BlockEndNode node) {
// this relies on the fact that nested statements are being ignored in visit(IfThenElseNode),
// otherwise successor could be the wrong one
if (node.getBase() == root) {
replaceSuccessor(node, successor);
}
}
}
/**
* This visitor has to be called before a loop is processed by {@link MapleCfgVisitor}. It makes sure that assigned
* variables are initialized in front of the outermost loop. Although initialization is also performed in
* {@link MapleCfgVisitor#visit(AssignmentNode)}, this step is required to make sure variables are initialized with
* the full set of symbolic coefficients (linear combination) when they are reused within the loop. Otherwise, the
* structure of multivectors could change in the loop and previous assignments would only "see" parts of the
* non-zero coefficients. This is required because loops are executed more than one time and after the first
* iteration multivectors could consist of more components than before. This visitor guarantees that all
* coefficients are initialized right away.
*
* @author Christian Schwinn
*
*/
private class InitializeVariablesInLoopVisitor extends EmptyControlFlowVisitor {
private final LoopNode root;
InitializeVariablesInLoopVisitor(LoopNode node) {
this.root = node;
}
@Override
public void visit(StartNode node) {
throw new IllegalStateException("This visitor is intended to be called on loop nodes only.");
}
@Override
public void visit(AssignmentNode node) {
// optimize current value of variable and add variables for coefficients in front of block
Variable variable = node.getVariable();
// optimize current value of variable and add variables for coefficients in front of block
initializeCoefficients(variable);
// optimize value in a temporary variable and add missing initializations
initializeMissingCoefficients(node);
// reset Maple binding with linear combination of variables for coefficients
resetVariable(variable);
node.getSuccessor().accept(this);
}
@Override
public void visit(LoopNode node) {
node.getBody().accept(this);
if (node != root) {
node.getSuccessor().accept(this);
}
}
}
/**
* This visitor performs loop unrolling on-the-fly, i.e. every time a loop is visited which has the iterations
* property set to a value greater than zero. Unrolling means to copy the body of the loop as many times as
* specified by {@link LoopNode#getIterations()} and removing the corresponding {@link LoopNode} from the graph.
* Nested loops are not unrolled right away, instead they are copied like other statements, too. Unrolling of nested
* loops is performed in the context of {@link MapleCfgVisitor#visit(LoopNode)} when the inner loop is processed for
* optimization. The loop to be unrolled therefore has to be specified in the constructor.
*
* @author Christian Schwinn
*
*/
private class UnrollLoopsVisitor extends EmptyControlFlowVisitor {
private LoopNode root;
SequentialNode firstNewNode;
boolean showWarning;
public UnrollLoopsVisitor(LoopNode root) {
this.root = root;
}
/**
* Inserts a new node before the current node.
*
* @param newNode node to be inserted before the current node
*/
private void insertNewNode(SequentialNode newNode) {
if (firstNewNode == null) {
firstNewNode = newNode;
}
root.insertBefore(newNode);
}
@Override
public void visit(StartNode node) {
throw new IllegalStateException("This visitor should be invoked on a loop node only.");
}
@Override
public void visit(AssignmentNode node) {
insertNewNode(node.copy());
node.getSuccessor().accept(this);
}
@Override
public void visit(StoreResultNode node) {
insertNewNode(node.copy());
node.getSuccessor().accept(this);
}
@Override
public void visit(LoopNode node) {
if (node == root) {
for (int i = 0; i < node.getIterations(); i++) {
node.getBody().accept(this);
}
node.getGraph().removeNode(node);
} else {
insertNewNode(node.copy());
// ignore nested loops (process them later)
node.getSuccessor().accept(this);
}
// do not visit root's successor
}
@Override
public void visit(IfThenElseNode node) {
IfThenElseNode newNode = (IfThenElseNode) node.copy();
insertNewNode(newNode);
boolean endOfLoop = node.getSuccessor() instanceof BlockEndNode;
boolean startOfLoop = false;
for (Node pred : node.getPredecessors()) {
if (pred instanceof LoopNode) {
LoopNode loop = (LoopNode) pred;
if (loop.getBody() == node) {
startOfLoop = true;
}
}
}
if (!(endOfLoop || startOfLoop)) {
showWarning = true;
}
node.getSuccessor().accept(this);
}
@Override
public void visit(ExpressionStatement node) {
insertNewNode(node.copy());
node.getSuccessor().accept(this);
}
}
/**
* This visitor reorders relational operations to a left-hand side expression that is compared to 0. Equalities and
* inequalities are processed by optimizing both sides of the comparison and replacing the condition by a
* component-by-component comparison of each coefficient.
*
* @author Christian Schwinn
*
*/
private class ReorderConditionVisitor extends EmptyExpressionVisitor {
private IfThenElseNode root;
public ReorderConditionVisitor(IfThenElseNode root) {
this.root = root;
}
private void reorderToLeft(BinaryOperation node) {
Expression left = node.getLeft();
Expression right = node.getRight();
Subtraction lhs = ExpressionFactory.subtract(left.copy(), right.copy());
Variable condition = new Variable(CONDITION_PREFIX + conditionSuffix++);
Expression newRight = new FloatConstant(0);
try {
String assignment = generateCode(condition) + ":=" + generateCode(lhs) + ";";
engine.evaluate(assignment);
String opt = simplify(condition);
List<AssignmentNode> newNodes = parseMapleCode(graph, opt);
graph.addScalarVariable(condition);
for (AssignmentNode newAssignment : newNodes) {
if (newAssignment.getVariable() instanceof MultivectorComponent) {
MultivectorComponent mc = (MultivectorComponent) newAssignment.getVariable();
if (mc.getBladeIndex() == 0) {
AssignmentNode scalarPart = new AssignmentNode(graph, condition, newAssignment.getValue());
root.insertBefore(scalarPart);
} else {
throw new IllegalArgumentException("Condition in if-statement '" + root.getCondition()
+ "' is not scalar and cannot be evaluated.");
}
}
}
} catch (MapleEngineException e) {
throw new RuntimeException("Unable to optimize condition " + lhs + " in Maple.", e);
}
node.replaceExpression(left, condition);
node.replaceExpression(right, newRight);
node.getLeft().accept(this);
}
private boolean containsTrueOrFalse(Expression expression) {
UsedVariablesVisitor visitor = new UsedVariablesVisitor();
expression.accept(visitor);
for (Variable v : visitor.getVariables()) {
if ("true".equals(v.getName()) || "false".equals(v.getName())) {
return true;
}
}
return false;
}
/**
* Compares equalities and inequalities component-wise. Both multivectors are optimized and subtracted to get
* the components which are different. For these components, a comparison is added to the condition.
*
* @param node equality or inequality node
* @param equality whether to compare for equality or inequality (convenience to avoid instanceof checks)
*/
private void compareComponents(BinaryOperation node, boolean equality) {
Expression left = node.getLeft();
Expression right = node.getRight();
if (containsTrueOrFalse(left) || containsTrueOrFalse(right)) {
return;
}
Subtraction difference = ExpressionFactory.subtract(left.copy(), right.copy());
Variable lVar = new Variable(CONDITION_PREFIX + conditionSuffix++);
Variable rVar = new Variable(CONDITION_PREFIX + conditionSuffix++);
Variable diff = new Variable("__temp__");
try {
engine.evaluate(generateCode(lVar) + ":=" + generateCode(left) + ";");
engine.evaluate(generateCode(rVar) + ":=" + generateCode(right) + ";");
engine.evaluate(generateCode(diff) + ":=" + generateCode(difference) + ";");
initializeCoefficients(lVar);
initializeCoefficients(rVar);
Set<Integer> lVarCoeffs = new HashSet<Integer>();
Set<Integer> rVarCoeffs = new HashSet<Integer>();
for (MultivectorComponent comp : initializedVariables.get(lVar)) {
lVarCoeffs.add(comp.getBladeIndex());
}
for (MultivectorComponent comp : initializedVariables.get(rVar)) {
rVarCoeffs.add(comp.getBladeIndex());
}
BinaryOperation newCondition = null;
for (AssignmentNode diffOpt : optimizeVariable(graph, diff)) {
if (diffOpt.getVariable() instanceof MultivectorComponent) {
MultivectorComponent comp = (MultivectorComponent) diffOpt.getVariable();
int index = comp.getBladeIndex();
Expression lVarNew = getNewExpression(lVar, lVarCoeffs, index);
Expression rVarNew = getNewExpression(rVar, rVarCoeffs, index);
BinaryOperation comparison;
if (equality) {
comparison = new Equality(lVarNew, rVarNew);
} else {
comparison = new Inequality(lVarNew, rVarNew);
}
if (newCondition == null) {
newCondition = comparison;
} else {
newCondition = ExpressionFactory.and(newCondition, comparison);
}
}
}
root.replaceExpression(node, newCondition);
} catch (MapleEngineException e) {
throw new RuntimeException("Could not optimize (in)equality " + node + " in condition "
+ root.getCondition(), e);
}
}
private Expression getNewExpression(Variable var, Set<Integer> coeffs, int index) {
Expression newExp;
if (coeffs.contains(index)) {
newExp = new Variable(getTempVarName(var.getName(), index));
} else {
newExp = new FloatConstant(0.0f);
}
return newExp;
}
@Override
public void visit(Equality node) {
compareComponents(node, true);
}
@Override
public void visit(Inequality node) {
compareComponents(node, false);
}
@Override
public void visit(Relation node) {
reorderToLeft(node);
}
}
private Log log = LogFactory.getLog(MapleCfgVisitor.class);
static final String CONDITION_PREFIX = "condition_";
static int conditionSuffix = 0;
private static final String suffix = "_opt";
MapleEngine engine;
private HashMap<String, String> oldMinVal;
private HashMap<String, String> oldMaxVal;
Plugin plugin;
/** Used to distinguish normal assignments and such from a loop or if-statement where GA must be eliminated. */
private int blockDepth = 0;
private boolean loopMode = false;
/** Used to insert synthetic variables in front of this {@link IfThenElseNode} or {@link LoopNode} **/
private SequentialNode currentRoot;
/** Keeps track of which synthetic variables for multivector coefficients already have been initialized. **/
Map<Variable, Set<MultivectorComponent>> initializedVariables = new HashMap<Variable, Set<MultivectorComponent>>();
/** Used to detect unknown variables which would make an if-condition undecidable at compile-time. **/
private final List<Variable> unknownVariables = new ArrayList<Variable>();
/** Variables determined to be optimized in any case. **/
final Set<Variable> optVariables = new HashSet<Variable>();
/** Keeps track of synthetic variables which were initialized but can be removed afterwards. **/
private final Set<MultivectorComponent> unusedInitializations = new HashSet<MultivectorComponent>();
/** Current mapping of new variable names used for renaming in branches to avoid backtracking after then-part. **/
private Map<Variable, Variable> newNames = new HashMap<Variable, Variable>();
/** Suffix to be appended to temporary variables for renaming in branches. **/
private static int tempSuffix = 1;
ControlFlowGraph graph;
public MapleCfgVisitor(MapleEngine engine, Plugin plugin) {
this.engine = engine;
this.plugin = plugin;
}
@Override
public void visit(StartNode startNode) {
graph = startNode.getGraph();
CheckDependencyVisitor visitor = new CheckDependencyVisitor();
graph.accept(visitor);
optVariables.addAll(visitor.getDependentVariables());
unknownVariables.addAll(graph.getInputVariables());
plugin.notifyStart();
startNode.getSuccessor().accept(this);
}
@Override
public void visit(AssignmentNode node) {
Variable variable = node.getVariable();
Expression value = node.getValue();
Node successor = node.getSuccessor();
UsedVariablesVisitor usedVariables = new UsedVariablesVisitor();
value.accept(usedVariables);
for (Variable var : usedVariables.getVariables()) {
if (unknownVariables.contains(var)) {
unknownVariables.add(variable);
break;
}
}
boolean optimize = optVariables.contains(variable);
if (blockDepth > 0) {
if (!optimize && successor instanceof StoreResultNode) {
optimize = true;
}
// replace each variable with new name, if existing
for (Variable var : usedVariables.getVariables()) {
Variable newName = newNames.get(var);
if (newName != null) {
value.replaceExpression(var, newName);
}
}
if (optimize) {
// optimize current value of variable and add variables for coefficients in front of block
initializeCoefficients(node.getVariable());
// optimize value in a temporary variable and add missing initializations
initializeMissingCoefficients(node);
// reset Maple binding with linear combination of variables for coefficients
resetVariable(variable);
} else {
// create new variable name for this variable in current block
Variable newVariable = generateTempVariable(variable);
node.replaceExpression(variable, newVariable);
// add new variable (lhs) to map for use in subsequent assignments
// -> prevent recursive assignments from producing wrong values
newNames.put(variable, newVariable);
}
// update local variables with new expressions
variable = node.getVariable();
value = node.getValue();
}
// perform actual calculation
assignVariable(variable, value);
if (blockDepth > 0 && optimize) {
// optimize new value and reset Maple binding with linear combination of new value
assignCoefficients(node, variable);
}
// notify observers about progress (must be called before successor.accept(this))
plugin.notifyProgress();
graph.removeNode(node);
successor.accept(this);
}
/**
* Initializes synthetic variables representing the current coefficients of the given variable.
*
* @param variable Variable to be initialized in front of the current root.
*/
void initializeCoefficients(Variable variable) {
List<AssignmentNode> coefficients = optimizeVariable(graph, variable);
for (AssignmentNode coefficient : coefficients) {
if (coefficient.getVariable() instanceof MultivectorComponent) {
MultivectorComponent component = (MultivectorComponent) coefficient.getVariable();
if (component.getBladeIndex() == 0 && coefficients.size() == 1) {
// check that Maple result is not of type x := 'x'
String optName = coefficient.getVariable().getName();
Variable coeffVariable = new Variable(optName.substring(0, optName.lastIndexOf(suffix)));
if (coeffVariable.equals(coefficient.getValue())) {
coefficient.setValue(new FloatConstant(0));
// throw new RuntimeException("Variable " + variable + " is not initialized in global space.");
}
}
initializeCoefficient(variable, coefficient.getValue(), component);
}
}
}
/**
* Helper method for {@link #initializeCoefficients(Variable)} to initialize a single coefficient.
*
* @param variable Variable to be initialized.
* @param value Current value of variable.
* @param component Component to be assigned, resulting from previous optimization.
*/
private void initializeCoefficient(Variable variable, Expression value, MultivectorComponent component) {
Variable tempVar = new Variable(getTempVarName(component));
if (initializedVariables.get(variable) == null) {
initializedVariables.put(variable, new HashSet<MultivectorComponent>());
}
Set<MultivectorComponent> initCoefficients = initializedVariables.get(variable);
if (!initCoefficients.contains(component)) {
graph.addScalarVariable(tempVar);
AssignmentNode initialization = new AssignmentNode(graph, tempVar, value);
if (!tempVar.equals(value)) {
currentRoot.insertBefore(initialization);
checkUnusedInitialization(component);
}
initCoefficients.add(component);
}
}
/**
* Initializes synthetic variables in front of the current root which have not been initialized before. This has to
* be done when a variable is reassigned in a branch, resulting in new multivector components.
*
* @param node Assignment which potentially changes the structure of the assigned multivector.
*/
void initializeMissingCoefficients(AssignmentNode node) {
Variable temp = new Variable("__temp__");
assignVariable(temp, node.getValue());
List<MultivectorComponent> coefficients = getComponents(optimizeVariable(graph, temp));
for (MultivectorComponent coefficient : coefficients) {
Variable originalVariable = node.getVariable();
String optVarName = originalVariable.getName() + suffix;
MultivectorComponent originalComp = new MultivectorComponent(optVarName, coefficient.getBladeIndex());
Variable tempVar = new Variable(getTempVarName(originalComp));
if (initializedVariables.get(originalVariable) == null) {
initializedVariables.put(originalVariable, new HashSet<MultivectorComponent>());
}
Set<MultivectorComponent> initCoefficients = initializedVariables.get(originalVariable);
if (!initCoefficients.contains(originalComp)) {
graph.addScalarVariable(tempVar);
AssignmentNode initialization = new AssignmentNode(graph, tempVar, new FloatConstant(0));
currentRoot.insertBefore(initialization);
initCoefficients.add(originalComp);
checkUnusedInitialization(originalComp);
}
}
}
/**
* Assigns new values to synthetic variables representing the multivector's coefficients. The given node is used as
* the basis in front of which these assignments are inserted into the graph.
*
* @param base Basis in front of which to insert new assignments.
* @param variable Variable to be optimized.
*/
private void assignCoefficients(AssignmentNode base, Variable variable) {
List<AssignmentNode> coefficients = optimizeVariable(graph, variable);
List<MultivectorComponent> newValues = new ArrayList<MultivectorComponent>();
for (AssignmentNode coefficient : coefficients) {
if (coefficient.getVariable() instanceof MultivectorComponent) {
MultivectorComponent mc = (MultivectorComponent) coefficient.getVariable();
newValues.add(mc);
Variable newVariable = new Variable(getTempVarName(mc));
Expression newValue = coefficient.getValue();
if (!newVariable.equals(newValue)) {
AssignmentNode newAssignment = new AssignmentNode(graph, newVariable, newValue);
base.insertBefore(newAssignment);
}
}
}
resetZeroCoefficients(base, variable, newValues);
resetVariable(variable);
}
/**
* Resets synthetic variables to zero which are not part of the multivector anymore.
*
* @param base Basis in front of which to insert the reset statements.
* @param variable Variable which has been reassigned.
* @param newValues Multivector components resulting from current optimization.
*/
private void resetZeroCoefficients(AssignmentNode base, Variable variable, List<MultivectorComponent> newValues) {
List<MultivectorComponent> zeroCoefficients = new ArrayList<MultivectorComponent>();
for (MultivectorComponent initCoeff : initializedVariables.get(variable)) {
if (!newValues.contains(initCoeff)) {
// this component is not part of the multivector anymore
zeroCoefficients.add(initCoeff);
}
}
for (MultivectorComponent zeroCoeff : zeroCoefficients) {
Variable newVariable = new Variable(getTempVarName(zeroCoeff));
AssignmentNode resetToZero = new AssignmentNode(graph, newVariable, new FloatConstant(0));
base.insertBefore(resetToZero);
}
}
/**
* Resets the Maple binding of the given variable to a linear combination of symbolic coefficients. Concrete
* coefficients are the synthetic variables which have been initialized in front of the current root and have been
* assigned in the current branch.
*
* @param variable Variable to be reset as a linear combination using previously assigned synthetic variables.
*/
void resetVariable(Variable variable) {
Set<MultivectorComponent> components = initializedVariables.get(variable);
if (components == null || components.size() == 0) {
throw new IllegalStateException("No components to reset for variable " + variable);
}
Expression[] products = new Expression[components.size()];
int i = 0;
for (MultivectorComponent mc : components) {
Variable coefficient = new Variable(getTempVarName(mc));
Expression blade = graph.getAlgebraDefinitionFile().getBladeExpression(mc.getBladeIndex());
Multiplication product = ExpressionFactory.product(coefficient, blade);
products[i++] = product;
}
Expression sum;
if (products.length > 1) {
sum = ExpressionFactory.sum(products);
} else {
sum = products[0];
}
assignVariable(variable, sum);
}
/**
* Extracts the {@link MultivectorComponent}s from a list of coefficients.
*
* @param coefficients nodes from the Maple parser
* @return list of multivector components
*/
private List<MultivectorComponent> getComponents(List<AssignmentNode> coefficients) {
List<MultivectorComponent> components = new ArrayList<MultivectorComponent>();
for (AssignmentNode coefficient : coefficients) {
if (coefficient.getVariable() instanceof MultivectorComponent) {
components.add((MultivectorComponent) coefficient.getVariable());
}
}
return components;
}
/**
* Translates the given variable and value to Maple syntax and executes it.
*
* @param variable
* @param value
*/
private void assignVariable(Variable variable, Expression value) {
String variableCode = generateCode(variable);
StringBuilder codeBuffer = new StringBuilder();
codeBuffer.append(variableCode);
codeBuffer.append(" := ");
codeBuffer.append(generateCode(value));
codeBuffer.append(";\n");
try {
engine.evaluate(codeBuffer.toString());
} catch (MapleEngineException e) {
throw new RuntimeException("Unable to process assignment " + variable + " := " + value + " in Maple.", e);
}
}
/**
* Generates the Maple equivalent code for the given expression.
*
* @param expression Expression to be translated.
* @return the corresponding Maple code
*/
String generateCode(Expression expression) {
MapleDfgVisitor visitor = new MapleDfgVisitor();
expression.accept(visitor);
return visitor.getCode();
}
/**
* Convenience method for {@link #generateTempVariable(Variable)}.
*
* @param component
* @return the name of the corresponding synthetic variable
*/
String getTempVarName(MultivectorComponent component) {
return getTempVarName(component.getName(), component.getBladeIndex());
}
/**
* Generates the name of the synthetic variable representing the coefficient at given index for the given variable.
*
* @param variable Variable name.
* @param index Index of coefficient.
* @return <code>variable__index</code>
*/
String getTempVarName(String variable, int index) {
// attention: must not collide with renamed variables from macro inlining
return variable.replace('e', 'E').replace(suffix, "") + "__" + index;
}
/**
* Generates a temporary variable as a replacement from variables in a branch.
*
* @param v original variable
* @return variable including __tmp__
*/
private Variable generateTempVariable(Variable v) {
Variable newVariable = new Variable(v + "__tmp__" + tempSuffix++);
return newVariable;
}
/**
* Checks if the initialization of a synthetic variable has to be kept in the code.
*
* @param component Name and index of the synthetic variable to be checked.
*/
private void checkUnusedInitialization(MultivectorComponent component) {
String variableName = component.getName().replace(suffix, "");
if (!optVariables.contains(new Variable(variableName)) && !variableName.startsWith(CONDITION_PREFIX)) {
unusedInitializations.add(component);
}
}
@Override
public void visit(ExpressionStatement node) {
String command = generateCode(node.getExpression());
command += ";\n";
try {
engine.evaluate(command);
} catch (MapleEngineException e) {
throw new RuntimeException("Unable to simplify statement " + node + " in Maple.", e);
}
graph.removeNode(node);
node.getSuccessor().accept(this);
}
@Override
public void visit(StoreResultNode node) {
List<AssignmentNode> newNodes = optimizeVariable(graph, node.getValue());
for (AssignmentNode newNode : newNodes) {
node.insertBefore(newNode);
}
if (blockDepth > 0) {
// do not reuse coefficients in form v[i] but with helper variables, e.g. v__0
resetVariable(node.getValue());
}
node.getSuccessor().accept(this);
}
/**
* Simplifies the given variable and parses the Maple code to return the new nodes.
*/
List<AssignmentNode> optimizeVariable(ControlFlowGraph graph, Variable v) {
String simplification = simplify(v);
log.debug("Maple simplification of " + v + ": " + simplification);
return parseMapleCode(graph, simplification);
}
/**
* Simplifies a single Expression node.
*
* @param expression The data flow graph that should be simplified
* @return The code Maple returned as the simplification.
*/
String simplify(Expression expression) {
StringBuilder codeBuffer = new StringBuilder();
codeBuffer.append("gaalop(");
codeBuffer.append(generateCode(expression));
codeBuffer.append(");\n");
try {
return engine.evaluate(codeBuffer.toString());
} catch (MapleEngineException e) {
throw new RuntimeException("Unable to apply gaalop() function on expression " + expression + " in Maple.",
e);
}
}
/**
* Parses a snippet of maple code and returns a list of CFG nodes that implement the returned maple expressions.
*
* @param graph The control flow graph the new nodes should be created in.
* @param mapleCode The code returned by Maple.
* @return A list of control flow nodes modeling the returned code.
*/
List<AssignmentNode> parseMapleCode(ControlFlowGraph graph, String mapleCode) {
oldMinVal = new HashMap<String, String>();
oldMaxVal = new HashMap<String, String>();
/* fill the Maps with the min and maxvalues from the nodes */
for (Variable v : graph.getInputVariables()) {
if (v.getMinValue() != null)
oldMinVal.put(v.getName(), v.getMinValue());
if (v.getMaxValue() != null)
oldMaxVal.put(v.getName(), v.getMaxValue());
}
MapleLexer lexer = new MapleLexer(new ANTLRStringStream(mapleCode));
MapleParser parser = new MapleParser(new CommonTokenStream(lexer));
try {
MapleParser.program_return result = parser.program();
MapleTransformer transformer = new MapleTransformer(new CommonTreeNodeStream(result.getTree()));
return transformer.script(graph, oldMinVal, oldMaxVal);
} catch (RecognitionException e) {
throw new RuntimeException(e);
}
}
@Override
public void visit(IfThenElseNode node) {
Expression condition = node.getCondition();
UsedVariablesVisitor usedVariables = new UsedVariablesVisitor();
condition.accept(usedVariables);
boolean unknown = false;
for (Variable var : usedVariables.getVariables()) {
if (unknownVariables.contains(var)) {
unknown = true;
break;
}
}
for (Variable var : usedVariables.getVariables()) {
Variable newName = newNames.get(var);
if (newName != null) {
condition.replaceExpression(var, newName);
}
}
if (loopMode) {
unknown = true;
}
if (!unknown) {
// try to evaluate condition to true or false
String evalb = "evalb(" + generateCode(condition) + ");";
try {
String result = engine.evaluate(evalb);
log.debug("evalb result for condition " + condition + ": " + result);
if ("true\n".equals(result)) {
InlineBlockVisitor inliner = new InlineBlockVisitor(node, node.getPositive());
node.accept(inliner);
node.getPositive().accept(this);
} else if ("false\n".equals(result)) {
InlineBlockVisitor inliner = new InlineBlockVisitor(node, node.getNegative());
node.accept(inliner);
node.getNegative().accept(this);
} else {
unknown = true;
}
} catch (MapleEngineException e) {
e.printStackTrace();
unknown = true;
}
}
if (unknown) {
// condition contains unknown variables or evalb has failed to evaluate to true or false
handleUnknownBranches(node, condition);
node.getSuccessor().accept(this);
}
}
/**
* Performs the "regular" handling of {@link IfThenElseNode} which cannot be inlined due to an undecidable
* condition.
*
* @param node {@link IfThenElseNode} to be processed.
* @param condition Condition which has to be reordered and checked for GA.
*/
private void handleUnknownBranches(IfThenElseNode node, Expression condition) {
if (blockDepth == 0) {
currentRoot = node;
}
ReorderConditionVisitor reorder = new ReorderConditionVisitor(node);
condition.accept(reorder);
blockDepth++;
Map<Variable, Variable> currentMapping = new HashMap<Variable, Variable>(newNames);
node.getPositive().accept(this);
// restore previous mapping for variables
newNames = currentMapping;
node.getNegative().accept(this);
// restore previous mapping for variables
newNames = currentMapping;
blockDepth--;
if (blockDepth == 0) {
initializedVariables.clear();
}
}
@Override
public void visit(LoopNode node) {
if (node.getIterations() > 0) {
UnrollLoopsVisitor ulv = new UnrollLoopsVisitor(node);
node.accept(ulv);
if (ulv.showWarning) {
Notifications.addWarning("Make sure that termination conditions for unroll-loops are "
+ "always at beginning or end of a loop.\n"
+ "Otherwise, partial loop bodies cannot be unrolled.");
}
ulv.firstNewNode.accept(this);
} else {
if (blockDepth == 0) {
currentRoot = node;
}
/*
* Initialize synthetic variables of assigned values in front of the current root. Additional to the normal
* initialization in visit(AssignmentNode), synthetic variables have to be present for every potential
* coefficient which might result from an assignment anywhere in the loop. This is necessary since the structure
* of multivectors can change in the loop body and the loop is executed more than one time (in contrast to
* if-statements, which are processed only once).
*/
InitializeVariablesInLoopVisitor visitor = new InitializeVariablesInLoopVisitor(node);
node.accept(visitor);
blockDepth++;
boolean oldMode = loopMode;
loopMode = true;
Map<Variable, Variable> currentMapping = new HashMap<Variable, Variable>(newNames);
node.getBody().accept(this);
// restore previous mapping for variables
newNames = currentMapping;
loopMode = oldMode;
blockDepth--;
if (blockDepth == 0) {
initializedVariables.clear();
}
node.getSuccessor().accept(this);
}
}
@Override
public void visit(BreakNode node) {
Node successor = node.getSuccessor();
if (!loopMode) {
node.getGraph().removeNode(node);
}
successor.accept(this);
}
@Override
public void visit(BlockEndNode node) {
// nothing to do
}
@Override
public void visit(EndNode endNode) {
RemoveUnusedAssignmentsVisitor v1 = new RemoveUnusedAssignmentsVisitor(unusedInitializations);
graph.accept(v1);
RemoveEmptyNodesVisitor v2 = new RemoveEmptyNodesVisitor();
graph.accept(v2);
}
@Override
public void visit(Macro node) {
throw new IllegalStateException("Macros should have been inlined.");
}
@Override
public void visit(ColorNode node) {
node.getSuccessor().accept(this);
}
}