/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ExpressionAnalyzer.java
* Created: Jun 4, 2004Sep 12, 2003 5:42:14 PM
* By: rcypher
*/
package org.openquark.cal.compiler;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import org.openquark.cal.compiler.Expression.LetNonRec;
import org.openquark.cal.compiler.Expression.Let.LetDefn;
import org.openquark.cal.compiler.Expression.Switch.SwitchAlt;
import org.openquark.cal.internal.module.Cal.Core.CAL_Prelude_internal;
import org.openquark.cal.machine.MachineFunction;
import org.openquark.cal.machine.Module;
import org.openquark.cal.module.Cal.Collections.CAL_List;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.cal.util.ArrayStack;
import org.openquark.cal.util.Graph;
import org.openquark.cal.util.Vertex;
import org.openquark.cal.util.VertexBuilder;
import org.openquark.cal.util.VertexBuilderList;
/**
* Warning- this class should only be used by the CAL compiler implementation. It is not part of the
* external API of the CAL platform.
* <P>
* A class for doing analysis and transformations on expressions.
* @author rcypher
*/
public final class ExpressionAnalyzer {
// Flags used to run optimizations on/off for debugging purposes.
private static final boolean CONVERT_TAIL_RECURSION_TO_LOOPS = true;
private static final boolean INLINE_LETVARS = true;
private static final QualifiedName ifName = QualifiedName.make(CAL_Prelude.MODULE_NAME, "if");
private boolean hadUnsafeCoerce;
/** Flag indicating that the expression being analyzed has changed. */
private boolean changed;
private final ModuleTypeInfo currentModuleTypeInfo;
/** The MachineFunction instance corresponding to the function containing the expression being analyzed. */
private final MachineFunction function;
ExpressionAnalyzer (ModuleTypeInfo currentModuleTypeInfo, MachineFunction coreFunction) {
if (currentModuleTypeInfo == null || coreFunction == null) {
throw new NullPointerException();
}
this.currentModuleTypeInfo = currentModuleTypeInfo;
this.function = coreFunction;
}
/**
* If the expression is a let expression try to inline any letvariables.
* A let variable can be inlined if it is in one of the following categories:
* 1) the let variable is defined as a literal
* 2) the let variable is defined as a non-reducible variable
* @param e
* @return the transformed expression.
*/
private Expression inlineLetVars (Expression e) throws UnableToResolveForeignEntityException {
// We can only inline let variables for a non recursive let.
if (e.asLetNonRec() == null) {
return e;
}
// If the let is never used we can simply transform to the body.
if (e.asLetNonRec().getDefn().getUseCount() == 0) {
changed = true;
return e.asLetNonRec().getBody();
}
// Class to walk the expression tree substituting variable definitions for
// variable references wherever possible.
final class VarInliner extends PreOrderTransformVisitor {
// Name of the variable being inlined.
private final String varNameToInline;
// Definition of the variable being inlined.
private final Expression VarDefToInline;
VarInliner (String varNameToInline, Expression varDefToInline) {
this.varNameToInline = varNameToInline;
this.VarDefToInline = varDefToInline;
}
// If the expression is the variable being inlined return the
// definition. Otherwise return the original expression.
@Override
Expression transform (Expression expr) {
if (expr.asVar() != null) {
Expression.Var var = expr.asVar();
if (varNameToInline.equals(var.getName().getUnqualifiedName())) {
expr = VarDefToInline;
}
}
return expr;
}
}
// Determine if the variable in question can be inlined.
// Get the let variable definition.
Expression.Let.LetDefn def = e.asLetNonRec().getDefn();
// If the let var can't be inlined simply return.
// We inline letvars that are literals, simply defined as another local variable, or
// are defined as a 'safe' supercombinator. (by safe we mean SCs that are not zero arity
// foreign functions).
Expression eDef = def.getExpr();
if (!(eDef.asVar() != null && (eDef.asVar().getForeignFunctionInfo() == null || eDef.asVar().getForeignFunctionInfo().getNArguments() > 0)) &&
eDef.asLiteral() == null) {
return e;
}
// We're going to inline the let variable so set changed to true.
changed = true;
// Create the inliner and walk it over the expression tree.
VarInliner inliner = new VarInliner (def.getVar(), def.getExpr());
e.walk(inliner);
// Since we've just inlined the letvar definition into the letvar body we want to simply return
// the body, thus eliminating the declaration/definition of the letvar.
return e.asLetNonRec().getBody();
}
/**
* Apply the different transforms to the expression 'e' and all
* its parts.
* @param e
* @return the transformed expression.
*/
Expression transformExpression (Expression e) throws UnableToResolveForeignEntityException {
changed = true;
/**
* A RuntimeException subclass that wraps an UnableToResolveForeignEntityException.
*
* This is done so that the checked UnableToResolveForeignEntityException can tunnel through
* the visitation framework without forcing all visit methods to have a throws declaration.
* This wrapper exception is unwrapped by the try-catch block surrounding the walk() method call.
*/
final class WrapperExceptionForUnableToResolveForeignEntityException extends RuntimeException {
private static final long serialVersionUID = -8232282501855879381L;
WrapperExceptionForUnableToResolveForeignEntityException(UnableToResolveForeignEntityException cause) {
super(cause);
}
UnableToResolveForeignEntityException getUnderlyingException() {
return (UnableToResolveForeignEntityException)getCause();
}
}
// Visitor which simply calls doTransforms on each part of the expression.
PreOrderTransformVisitor basicTransformsVisitor = new PreOrderTransformVisitor() {
@Override
Expression transform (Expression expressionToTransform) {
// Inline let variables where possible.
if (ExpressionAnalyzer.INLINE_LETVARS) {
try {
return inlineLetVars(expressionToTransform);
} catch (UnableToResolveForeignEntityException e) {
// Wrap the UnableToResolveForeignEntityException so that it may bubble up the
// visitor's call stack wrapped in a RuntimeException subclass.
throw new WrapperExceptionForUnableToResolveForeignEntityException(e);
}
} else {
return expressionToTransform;
}
}
};
e = inlineSpecialPreludeFunctions( e );
while (changed) {
changed = false;
// Recalculate the let variable use counts since the expression may have
// changed since the last time this was done. This information is
// used in the various transformations.
letVariableUse(e);
// Inline let variables where possible.
e = ExpressionAnalyzer.INLINE_LETVARS ? inlineLetVars(e) : e;
// Apply the transforms to the parts of the expression.
try {
e.walk(basicTransformsVisitor);
} catch (WrapperExceptionForUnableToResolveForeignEntityException ex) {
// unwrap the wrapper exception, and throw the underlying UnableToResolveForeignEntityException
throw ex.getUnderlyingException();
}
// Change top level and/or to if-then-else if it exposes a recursive tail call
// or the right hand side calls a strongly connected component.
e = changeTopLevelAndOr(e);
// Look for tail recursive calls (i.e. a top level recursive calls an transform them.
// We don't want to do this for primitive functions since they always take the form fn x y = fn x y;
if (function.isCALFunction() && ExpressionAnalyzer.CONVERT_TAIL_RECURSION_TO_LOOPS) {
TailRecursionTransformer rt = new TailRecursionTransformer (function);
e = rt.transformTailRecursionCalls(e);
if (rt.changed) {
changed = true;
}
}
}
LetVarRescoper lvr = new LetVarRescoper();
e = lvr.rescopeLetVariables(e);
return e;
}
/**
* This function is idempotent assuming that for each sub-tree t of e that
* doTransformsOnlyOnce has been applied in a post-order traversal of t.
*
* The proof outline that this function is idempotent under the given assumption.
*
* For Case 1 and 2 this can be seen using the theorems.
* For Case 3 and 4 this can be seen by observing that the expression
* created is a two argument function call but Case 3 and 4 expect a three
* argument function call so the transformation could not apply again.
*
* @param e The expression to simplify
* @return The expression after simplification.
*
*/
private Expression doTransformsOnlyOnce (Expression e) {
// Case 1: Remove any functions that are operationally the identity.
e = inlineSimpleBodiedFunctions(e);
// Case 2: Change applications of negate to a literal to be the negated literal value.
e = collapseNegate(e);
// Case 3: Inline flip function
if (e.asAppl() != null){
// e = (a1 ...)
// a1 = (a11 a12)
// a11 = (v111 ...)
Expression.Appl a1 = e.asAppl().getE1().asAppl();
if (a1 != null){
Expression.Appl a11 = a1.asAppl().getE1().asAppl();
if (a11 != null){
Expression.Var v111 = a11.getE1().asVar();
if (v111 != null){
if (v111.getName().equals(CAL_Prelude.Functions.flip)){
e = doTransformsOnlyOnce(new Expression.Appl(
doTransformsOnlyOnce(new Expression.Appl(a11.getE2(), e.asAppl().getE2())),
a1.getE2()
));
}
}
}
}
}
// Case 4: Inline the compose function if possible.
e = inlineComposeFunction(e);
return e;
}
/**
* Converts top level and/or into and if-then-else under certain conditions.
* The conversion occurs if the right hand side of the and/or is tail recursive
* or contains a call to a closely connected component. This will expose tail recusive calls
* for optimization and avoid space usage issues.
*
* @param e
* @return the transformed expression.
*/
private Expression changeTopLevelAndOr (Expression e) {
if (e.asAppl() != null) {
Expression[] chain = appChain (e);
if (chain == null) {
return e;
}
QualifiedName varName = chain[0].asVar().getName();
if (chain.length == 4 && varName.equals(ifName)) {
Expression.Appl app = e.asAppl ();
app.setE2 (changeTopLevelAndOr (app.getE2 ()));
app = app.getE1().asAppl ();
if (app != null) {
app.setE2 (changeTopLevelAndOr (app.getE2 ()));
}
return e;
}
// If this is a fully saturated application of Prelude.and or Prelude.or and the right hand side
// depends on a strongly connected component we want to change it to an if-then-else.
// In the case of a tail recursive call the call is then eligible for optimization by being
// transformed to a loop. In the case of indirect recursion the this avoids an issue with using
// too much call stack space while still allowing for a more efficient representation than a call
// to Prelude.and or Prelude.or.
if (chain.length == 3 &&
(varName.equals(CAL_Prelude.Functions.or) || varName.equals(CAL_Prelude.Functions.and)) &&
dependsOnStronglyConnectedComponent(chain[2])) {
Expression.Var ifVar = new Expression.Var (ifName);
if (varName.equals(CAL_Prelude.Functions.and)) {
Expression.Appl app = new Expression.Appl (ifVar, chain[1]);
app = new Expression.Appl (app, chain[2]);
app = new Expression.Appl (app, new Expression.Literal (Boolean.FALSE));
changed = true;
return app;
} else
if (varName.equals(CAL_Prelude.Functions.or)) {
Expression.Appl app = new Expression.Appl (ifVar, chain[1]);
app = new Expression.Appl (app, new Expression.Literal (Boolean.TRUE));
app = new Expression.Appl (app, chain[2]);
changed = true;
return app;
}
}
} else if (e.asLet() != null) {
Expression.Let let = e.asLet();
let.setBody(changeTopLevelAndOr (let.getBody()));
} else if (e.asSwitch() != null) {
Expression.Switch sw = e.asSwitch();
Expression.Switch.SwitchAlt alts[] = sw.getAlts();
for (int i = 0; i < alts.length; ++i) {
alts[i].setAltExpr(changeTopLevelAndOr(alts[i].getAltExpr()));
}
}
return e;
}
/**
* Determine if the given expression is dependent on a strongly connected component of the containing
* function.
* @param e
* @return true or false
*/
private boolean dependsOnStronglyConnectedComponent(Expression e) {
return dependsOnStronglyConnectedComponent(e, function.getStronglyConnectedComponents(), function.getQualifiedName().getModuleName());
}
/**
* @param e
* @param stronglyConnectedComponents
* @param moduleName
* @return true if the expression depends on a function in the set of strongly connected components.
*/
public static boolean dependsOnStronglyConnectedComponent (Expression e, Set<String> stronglyConnectedComponents, ModuleName moduleName) {
if (stronglyConnectedComponents == null) {
return false;
}
for (final String name : stronglyConnectedComponents) {
if (ExpressionAnalyzer.isDependentOn(e, QualifiedName.make(moduleName, name))) {
return true;
}
}
return false;
}
/**
* If e is an application of the negateInt, negateDouble, negateLong, negateInteger to a
* literal value we change it to a literal of the negative value.
* @param e
* @return the collapsed expression.
*/
private Expression collapseNegate (Expression e) {
if (e instanceof Expression.Appl) {
Expression.Appl app = (Expression.Appl)e;
if (app.getE1().asVar() != null && app.getE2().asLiteral() != null) {
Expression.Var var = app.getE1().asVar();
Expression.Literal lit = app.getE2().asLiteral();
QualifiedName functionName = var.getName();
if (functionName.getModuleName().equals(CAL_Prelude.MODULE_NAME)) {
String unqualifiedFunctionName = functionName.getUnqualifiedName();
if (unqualifiedFunctionName.equals(CAL_Prelude_internal.Functions.negateInt.getUnqualifiedName())) {
e = new Expression.Literal(Integer.valueOf(-((Integer)lit.getLiteral()).intValue()));
changed = true;
} else if (unqualifiedFunctionName.equals(CAL_Prelude_internal.Functions.negateDouble.getUnqualifiedName())) {
e = new Expression.Literal(new Double (-((Double)lit.getLiteral()).doubleValue()));
changed = true;
} else if (unqualifiedFunctionName.equals(CAL_Prelude_internal.Functions.negateLong.getUnqualifiedName())) {
e = new Expression.Literal(Long.valueOf(-((Long)lit.getLiteral()).longValue()));
changed = true;
} else if (unqualifiedFunctionName.equals(CAL_Prelude_internal.Functions.negateInteger.getUnqualifiedName())) {
e = new Expression.Literal(((BigInteger)lit.getLiteral()).negate());
changed = true;
} else if (unqualifiedFunctionName.equals(CAL_Prelude_internal.Functions.negateShort.getUnqualifiedName())) {
e = new Expression.Literal(Short.valueOf(((short)-((Short)lit.getLiteral()).shortValue())));
changed = true;
} else if (unqualifiedFunctionName.equals(CAL_Prelude_internal.Functions.negateFloat.getUnqualifiedName())) {
e = new Expression.Literal( new Float(-((Float)lit.getLiteral()).floatValue()));
changed = true;
} else if (unqualifiedFunctionName.equals(CAL_Prelude_internal.Functions.negateByte.getUnqualifiedName())) {
e = new Expression.Literal(Byte.valueOf(((byte)-((Byte)lit.getLiteral()).byteValue())));
changed = true;
}
}
}
}
return e;
}
/**
* Inline a few special functions in the Prelude.
*
* In general, there are a few special functions whose defining expressions involve less graph than writing the
* application of the function itself.
*
* Some of these functions (such as unsafeCoerce and asTypeOf) are used primarily to persuade the type-checker to accept
* certain constructs, and it is important that they have no run-time impact for this sort of usage.
*
* (id, emptyString, emptyList) are instance method functions and so occur through the compile-time instance
* resolution optimization even though they don't naturally occur in user-written code.
*
* (fst, snd, field1, ... field7, list0) are just handy small optimizations.
*
* (compose, apply, flip, const) occur sometimes in fully saturated form when writing gems in the GemCutter. compose also
* can occur fully saturated as a translation of the "#" operator.
*
* 0 argument functions:
* emptyString :: String;
* emptyList :: [a];
* list0 :: [a];
*
* 1 argument functions: (note that id, unsafeCoerce and apply are all operationally the identity function).
* id :: a -> a;
* public id !x = x;
*
* primitive public unsafeCoerce :: a -> b;
*
* apply :: (a -> b) -> a -> b;
* public apply !functionToApply = functionToApply;
*
* public fst !pair = pair.#1;
* public snd !pair = pair.#2;
* public field1 !r = r.#1;
* ...
* public field7 !r = r.#7;
*
* 2 argument functions:
* asTypeOf :: a -> a -> a;
* public asTypeOf !valueToReturn valueToIgnore = valueToReturn;
*
* const :: a -> b -> a;
* public const !valueToReturn valueToIgnore = valueToReturn;
*
* 3 argument functions:
* compose :: (b -> c) -> (a -> b) -> (a -> c);
* public compose !f g x = f (g x);
*
* flip :: (a -> b -> c) -> b -> a -> c;
* public flip !f x y = f y x;
*
* @param e
* @return expression with inlining done of the top level application node
*/
/**
* Theorems about traversing a tree in post order
*
* p is a tree of nodes, n.
* f is a function from p to p.
*
* Tf(p) - T is a function that traverses p in post order and applies f to each subtree of p.
*
* Tf(p) is idempotent iff Tf(Tf(p)) = Tf(p).
*
* Theorem 1. If f and g are independent (if the subtrees of p have been traversed)
* and Tf and Tg are idempotent then T(f.g) is idempotent.
*
* Tf(p) = Tf(Tf(p)) AND Tg(p) = Tg(Tg(p)) AND g(f(p)) == f(g(p)) then T(f.g) == T(f.g) . T(f.g)
*
* Theorem 2. If f(p) returns a subtree of p, a leaf, or p for internal nodes and p for leaf nodes then Tf is idempotent.
*
* inlineOperationalIdentityFunction satisfies Theorem 2.
* collapseNegate satisfies Theorem 2.
* Theorem 1 applies to inlineOperationalIdentityFunction and collapseNegate.
*
* @param e - the expression to be transformed
* @return the transformed expression.
*/
private Expression inlineSpecialPreludeFunctions(Expression e) {
PostOrderTransformVisitor basicTransformsVisitor = new PostOrderTransformVisitor() {
@Override
Expression transform (Expression e) {return doTransformsOnlyOnce(e);}
};
e.walk(basicTransformsVisitor);
e = doTransformsOnlyOnce (e);
return e;
}
/**
* In general, there are a few special functions whose defining expressions involve less graph than writing the
* application of the function itself.
*
* Some of these functions (such as unsafeCoerce and asTypeOf) are used primarily to persuade the type-checker to accept
* certain constructs, and it is important that they have no run-time impact for this sort of usage.
*
* (id, emptyString, emptyList) are instance method functions and so occur through the compile-time instance
* resolution optimization even though they don't naturally occur in user-written code.
*
* (fst, snd, field1, ... field7, list0) are just handy small optimizations.
*
* (apply, const) occur sometimes in fully saturated form when writing gems in the GemCutter. compose also
* can occur fully saturated as a translation of the "#" operator.
*
* 0 argument functions:
* emptyString :: String;
* emptyList :: [a];
* list0 :: [a];
*
* 1 argument functions: (note that id, unsafeCoerce, outputJObject and apply are all operationally the identity function).
* id :: a -> a;
* public id !x = x;
*
* primitive public unsafeCoerce :: a -> b;
*
* outputJObject :: JObject -> JObject;
* private outputJObject !x = x;
*
* apply :: (a -> b) -> a -> b;
* public apply !functionToApply = functionToApply;
*
* public fst !pair = pair.#1;
* public snd !pair = pair.#2;
* public field1 !r = r.#1;
* ...
* public field7 !r = r.#7;
*
* 2 argument functions:
* asTypeOf :: a -> a -> a;
* public asTypeOf !valueToReturn valueToIgnore = valueToReturn;
*
* const :: a -> b -> a;
* public const !valueToReturn valueToIgnore = valueToReturn;
*
* @param e The expression to try the inlining operation on.
* @return expression with inlining done of the top level application node
*/
private Expression inlineSimpleBodiedFunctions(Expression e) {
if (e instanceof Expression.Var){
Expression.Var var = e.asVar();
QualifiedName functionName = var.getName();
if (functionName.getModuleName().equals(CAL_Prelude.MODULE_NAME)){
String unqualifiedFunctionName = functionName.getUnqualifiedName();
if (unqualifiedFunctionName.equals(CAL_Prelude_internal.Functions.emptyList.getUnqualifiedName())){
e = new Expression.Var(currentModuleTypeInfo.getVisibleDataConstructor(CAL_Prelude.DataConstructors.Nil));
}
else if (unqualifiedFunctionName.equals(CAL_Prelude_internal.Functions.emptyString.getUnqualifiedName())){
e = new Expression.Literal("");
}
} else if (functionName.equals(CAL_List.Functions.list0)) {
e = new Expression.Var(currentModuleTypeInfo.getVisibleDataConstructor(CAL_Prelude.DataConstructors.Nil));
}
}
else if (e instanceof Expression.Appl) {
Expression.Appl app = (Expression.Appl)e;
if (app.getE1().asVar() != null) {
Expression.Var var = app.getE1().asVar();
QualifiedName functionName = var.getName();
ForeignFunctionInfo foreignFunctionInfo;
if ((foreignFunctionInfo = var.getForeignFunctionInfo()) != null
&& foreignFunctionInfo.getJavaKind() == ForeignFunctionInfo.JavaKind.IDENTITY_CAST) {
//these functions are operationally the identity
e = app.getE2();
} else if (functionName.getModuleName().equals(CAL_Prelude.MODULE_NAME)) {
final String unqualifiedFunctionName = functionName.getUnqualifiedName();
if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.id.getUnqualifiedName()) ||
unqualifiedFunctionName.equals(CAL_Prelude.Functions.unsafeCoerce.getUnqualifiedName()) ||
unqualifiedFunctionName.equals(CAL_Prelude.Functions.toCalValue.getUnqualifiedName()) ||
unqualifiedFunctionName.equals(CAL_Prelude.Functions.unsafeFromCalValue.getUnqualifiedName()) ||
unqualifiedFunctionName.equals(CAL_Prelude_internal.Functions.outputJObject.getUnqualifiedName()) ||
unqualifiedFunctionName.equals(CAL_Prelude.Functions.apply.getUnqualifiedName())) {
if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.unsafeCoerce.getUnqualifiedName())){
hadUnsafeCoerce = true;
}
//these functions are all operationally the identity function
e = app.getE2();
}
else{
int ordinal = 0; // 0 is not a valid value and is used as a flag
if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.fst.getUnqualifiedName())){
ordinal = 1;
}
else if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.snd.getUnqualifiedName())){
ordinal = 2;
}
else if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.field1.getUnqualifiedName())){
ordinal = 1;
}
else if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.field2.getUnqualifiedName())){
ordinal = 2;
}
else if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.field3.getUnqualifiedName())){
ordinal = 3;
}
else if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.field4.getUnqualifiedName())){
ordinal = 4;
}
else if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.field5.getUnqualifiedName())){
ordinal = 5;
}
else if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.field6.getUnqualifiedName())){
ordinal = 6;
}
else if (unqualifiedFunctionName.equals(CAL_Prelude.Functions.field7.getUnqualifiedName())){
ordinal = 7;
}
if (ordinal > 0){
e = new Expression.RecordSelection(e.asAppl().getE2(), FieldName.makeOrdinalField(ordinal));
}
}
}
}
else if (e.asAppl().getE1() instanceof Expression.Appl){
Expression.Appl a1 = e.asAppl().getE1().asAppl();
if (a1 != null){
Expression.Var v1_1 = a1.asAppl().getE1().asVar();
if (v1_1 != null){
// public asTypeOf !valueToReturn valueToIgnore = valueToReturn;
if (v1_1.asVar().getName().equals(CAL_Prelude.Functions.asTypeOf)){
e = a1.getE2();
}
// public const !valueToReturn valueToIgnore = valueToReturn;
else if (v1_1.asVar().getName().equals(CAL_Prelude.Functions.const_)){
e = a1.getE2();
}
}
}
}
}
return e;
}
/**
* If the expression is of the form
*
* (((Prelude.compose expr1) expr2) expr3)
*
* then replace by (expr1 (expr2 expr3)).
*
* @param e the input expression
* @return the optimized expression
*/
private Expression inlineComposeFunction(Expression e) {
if (e instanceof Expression.Appl) {
Expression.Appl app = (Expression.Appl)e;
Expression e1 = app.getE1();
if (e1 instanceof Expression.Appl) {
Expression.Appl app1 = (Expression.Appl)e1;
Expression e11 = app1.getE1();
if (e11 instanceof Expression.Appl) {
Expression.Appl app11 = (Expression.Appl)e11;
if (app11.getE1().asVar() != null) {
Expression.Var var = app11.getE1().asVar();
QualifiedName functionName = var.getName();
if (functionName.equals(CAL_Prelude.Functions.compose)) {
e = new Expression.Appl(app11.getE2(), doTransformsOnlyOnce(new Expression.Appl(app1.getE2(), app.getE2())));
e = doTransformsOnlyOnce( e );
}
}
}
}
}
return e;
}
/**
* This function lifts the definitions of non-recursive let variables
* into their own functions.
* Lifting let variable definitions into their own functions is required by
* the LECC machine to work around a limitation on the size of individual
* methods in the Java bytecode format.
*
* @param mf
* @param module
* @return the transformed Expression and the lifted let variable definition functions.
*/
public static LiftedLetVarResults liftLetVariables (MachineFunction mf, Module module) {
// Create the LetVarLifter
LetVarLifter lvl = new LetVarLifter();
return lvl.liftLetVariables(mf, module);
}
/**
* Encapsulate the results of lifting let variable definitions.
* This class holds the transformed Expression and the
* information about the lifted let variables.
* @author rcypher
*/
public static final class LiftedLetVarResults {
/** The transformed Expression. */
Expression modifiedExpression;
/** Collection of LiftedLetVarInfo */
Collection<LiftedLetVarInfo> liftedVarInfo;
LiftedLetVarResults (Expression modifiedExpression, Collection<LiftedLetVarInfo> liftedVarInfo) {
this.modifiedExpression = modifiedExpression;
this.liftedVarInfo = liftedVarInfo;
}
public Expression getExpression() {return modifiedExpression;}
public Collection<LiftedLetVarInfo> getLiftedVarInfo() {return liftedVarInfo;}
}
/**
* This class encapsulates the functionality which does lifts
* the definitions of non-recursive let variables into their own
* functions.
* This is accomplished through a series of transformations.
*
* @author rcypher
*
*/
private static class LetVarLifter {
/**
* Transform expression e such that the definitions of non-recursive let
* variables are lifted out as separate functions.
* The original expression is not modified, instead a new Expression
* is produced.
* @param mf
* @param module
* @return the transformed expression and a Collection of LiftedLetVarInfo describing the lifted let variable functions
*/
LiftedLetVarResults liftLetVariables (MachineFunction mf, Module module) {
// Get the Expression for the function and make a copy.
// All transformations will be performed on the copy, leaving
// the original Expression untouched.
Expression e = mf.getExpressionForm();
ExpressionCopier<Void> copier = new ExpressionCopier<Void>();
e = (Expression)e.accept(copier, null);
// First we want to collect all the let variables in the expression.
// We use an extension of Visitor which simply builds up a Map
// of String -> Expression.Let.LetDefn.
final class LetVarFinder extends Visitor {
/** String -> Expression.Let.LetDefn */
Map<String, LetDefn> letVars = new HashMap<String, LetDefn>();
@Override
void enterLetNonRec (Expression.LetNonRec letNonRec) {
letVars.put(letNonRec.getDefn().getVar(), letNonRec.getDefn());
}
}
LetVarFinder letVarFinder = new LetVarFinder();
e.walk(letVarFinder);
Map<String, LetDefn> letVars = letVarFinder.letVars;
// Next we want to do the lifting of the let variables, which will generate the list of
// LiftedLetVarInfo. We use a PostOrderTransformVisitor.
/**
* The LetVarLiftTransformer is a post order transformer which lifts
* non-recursive let variable definitions into their own functions.
* For example:
*
* foo a b = let x = a + b; y = x + b; in if a > b then x else y;
*
* Would be transformed to:
*
* foo a b = let x = xDef a b; y = yDef x b; in if a > b then x else y;
* xDef a b = a + b;
* yDef x b = x + g;
*
*/
final class LetVarLiftTransformer extends PostOrderTransformVisitor {
/** The module containing the function being transformed. */
private final Module module;
/** The information describing the lifted letvar definition functions. */
private final List<LiftedLetVarInfo> liftedVarInfo = new ArrayList<LiftedLetVarInfo>();
/**
* Map of String -> StrictnessAndType. Used to track and hold
* information about local variables that are defined in
* this expression.
*/
Map<String, StrictnessAndType> definedVars = new HashMap<String, StrictnessAndType>();
/**
* Class used to record the strictness and type of local variables
* defined in the expression being transformed.
*/
final class StrictnessAndType {
boolean strictness;
TypeExpr type;
StrictnessAndType(boolean strictness, TypeExpr type) {
this.strictness = strictness;
this.type = type;
}
boolean getStrictness() {return strictness;}
TypeExpr getType() {return type;}
}
/**
* Map of String -> Expression.Let.LetDefn, for all the let variables in the
* function.
* */
private final Map<String, LetDefn> allLetVars;
LetVarLiftTransformer (Module module, Map<String, LetDefn> allLetVars) {
this.module = module;
this.allLetVars = allLetVars;
}
@Override
void enterLetRec(Expression.LetRec letRec) {
// Add the variables into the defined variables map.
for (int i = 0; i < letRec.getDefns().length; ++i) {
definedVars.put(letRec.getDefns()[i].getVar(), new StrictnessAndType(false, letRec.getDefns()[i].getVarType()));
}
}
@Override
void enterLetNonRec (Expression.LetNonRec letNonRec) {
// Add the variable into the definedVariables map.
// We can get the variable type from the definition.
// We can consider the variable strict if the defining
// expression is an application of Prelude.eager.
Expression varDef = letNonRec.getDefn().getExpr();
Expression[] chain = appChain(varDef);
boolean strict = false;
if (chain != null && chain.length == 2 && chain[0].asVar() != null && chain[0].asVar().getName().equals(CAL_Prelude.Functions.eager)) {
strict = true;
}
definedVars.put(letNonRec.getDefn().getVar(), new StrictnessAndType(strict, letNonRec.getDefn().getVarType()));
}
@Override
void enterSwitchAlt (Expression.Switch.SwitchAlt alt) {
if (alt.hasVars()) {
// Add the variables to the defined variables map.
// We want to mark as strict variables which correspond
// to a plinged field in the data constructor.
// NOTE: we know that we are dealing with a data constructor
// because the switch alt has variables.
// NOTE2: Because a switch alt can have multiple tags (i.e. data
// constructors) the field must be plinged in all the data
// constructors for the corresponding var to be considered strict.
String[] names = alt.getVarNames();
for (int i = 0; i < names.length; ++i) {
boolean strict = true;
TypeExpr type = null;
if (alt instanceof Expression.Switch.SwitchAlt.Matching) {
Map<FieldName, String> fieldNameToVarNameMap = ((SwitchAlt.Matching)alt).getFieldNameToVarNameMap();
for (final Map.Entry<FieldName, String> entry : fieldNameToVarNameMap.entrySet()) {
FieldName fn = entry.getKey();
String varName = entry.getValue();
if (varName.equals(names[i])) {
for(int j = 0, nTags = alt.getAltTags().size(); j < nTags; ++j) {
DataConstructor dc = (DataConstructor)alt.getAltTags().get(j);
if (!dc.isArgStrict(dc.getFieldIndex(fn))) {
strict = false;
}
if (type == null) {
TypeExpr dcType = dc.getTypeExpr();
TypeExpr[] tft = dcType.getTypePieces(dc.getArity());
type = tft[dc.getFieldIndex(fn)];
}
}
break;
}
}
} else {
SortedMap<Integer, String> positionToVarNameMap = ((SwitchAlt.Positional)alt).getPositionToVarNameMap();
for (final Map.Entry<Integer, String> entry : positionToVarNameMap.entrySet()) {
Integer indexInteger = entry.getKey();
String altVar = entry.getValue();
if (altVar.equals(names[i])) {
for(int j = 0, nTags = alt.getAltTags().size(); j < nTags; ++j) {
DataConstructor dc = (DataConstructor)alt.getAltTags().get(j);
if (!dc.isArgStrict(indexInteger.intValue())) {
strict =false;
break;
}
if (type == null) {
TypeExpr dcType = dc.getTypeExpr();
TypeExpr[] tft = dcType.getTypePieces(dc.getArity());
type = tft[indexInteger.intValue()];
}
}
break;
}
}
}
definedVars.put(names[i], new StrictnessAndType(strict, type));
}
}
}
@Override
void enterRecordCase(Expression.RecordCase recordCase){
// Add the variables to the definedVariables map.
if (recordCase.getFieldBindingVarMap().size() > 0) {
Map<FieldName, String> bindingMap = recordCase.getFieldBindingVarMap();
for (final Map.Entry<FieldName, String> entry : bindingMap.entrySet()) {
definedVars.put(entry.getValue(), new StrictnessAndType(false, null));
}
}
}
/**
* Do the post-order transformation. In this case we only
* transform non-recursive let expressions.
*/
@Override
Expression transform (Expression e) {
// If e is an Expression.LetNonRec we want to do the modification.
Expression.LetNonRec lnr = e.asLetNonRec();
if (lnr != null) {
// There are some cases where there is no point in
// lifting the let variable definition.
// If the letvar definition is a var there is
// no point in lifting.
if (lnr.getDefn().getExpr().asVar() != null) {
return e;
}
// Determine the free variables in the RHS of the let binding.
// These will be the arguments of the lifted function.
Set<QualifiedName> freeVarNameSet = ExpressionAnalyzer.findReferencedFreeVariables(lnr.getDefn().getExpr(), module);
// Some of the free variables found by the previous step may actually be
// Expression.Var instances referring to other lifted let variable definitions.
// The show up as free variables because there is no information about them
// in the module. We need to remove these.
for (int i = 0; i < liftedVarInfo.size(); i++) {
LiftedLetVarInfo lifted = liftedVarInfo.get(i);
freeVarNameSet.remove(lifted.getFunctionQualifiedName());
}
// Build up the names and types of the arguments to the lifted function.
String[] argNames = new String[freeVarNameSet.size()];
TypeExpr[] argTypes = new TypeExpr[argNames.length];
boolean[] argStrictness = new boolean[argNames.length];
// We can fill in the arg type for any args that are other let bindings.
int i = 0;
for (final QualifiedName qualifiedArgName : freeVarNameSet) {
String argName = qualifiedArgName.getUnqualifiedName();
TypeExpr argType = null;
Expression.Let.LetDefn letDef = allLetVars.get(argName);
if (letDef != null) {
argType = letDef.getVarType();
}
argNames[i] = argName;
argTypes[i] = argType;
StrictnessAndType st = definedVars.get(argName);
if (st != null) {
if (st.getStrictness()) {
argStrictness[i] = true;
}
if (st.getType() != null) {
argTypes[i] = st.getType();
}
}
i++;
}
String functionName = lnr.getDefn().getVar() + "$def";
// Create the lifted letvar def info.
liftedVarInfo.add(
new LiftedLetVarInfo(
module.getName(),
functionName,
argNames.length,
argNames,
argTypes,
argStrictness,
lnr.getDefn().getVarType(),
lnr.getDefn().getExpr()));
// Transform the LetNonRec expression.
// Basically we replace the right hand side of the let binding with an
// application of the lifted definition function.
Expression newRHS = new Expression.Var(QualifiedName.make(module.getName(), functionName));
for (i = 0; i < argNames.length; ++i) {
newRHS = new Expression.Appl(newRHS, new Expression.Var(QualifiedName.make(module.getName(), argNames[i])));
}
lnr.getDefn().setExpr(newRHS);
}
return e;
}
}
// Create the let variable lifter and transform the expression.
LetVarLiftTransformer lvlt = new LetVarLiftTransformer(module, letVars);
e.walk(lvlt);
e = lvlt.transform(e);
// Grab the information about the lifted variable definitions.
List<LiftedLetVarInfo> liftedLetVarInfo = lvlt.liftedVarInfo;
// Now that we've lifted the let variable definitions we want to float the
// let variables inward. Yet another post order transform visitor comes
// into play.
// Change top level if-then-else to switch.
// The transformation to re-scope let variables
// inward only applies to switch alts, since we
// know that these will always correspond to a
// scope in the Java code generated by the LECC
// machine.
// Top level if-then-else constructs will also
// translate to a Java scope. However, it is
// difficult to track which if-then-else is top
// level at the same time as floating the let variables.
// We solve this by transforming top level if-then-else
// into boolean switches. This is safe as the generated
// code will still generate a Java if-then-else.
e = changeTopLevelIfThenElse(e);
/**
* This class does an inward floating let variable transformation.
* For example:
* foo a b = let x = xDef a b; y = yDef x b; in if a < b then x else y;
*
* Can be transformed to:
* foo a b = let x = xDef a b; in if a < b then x else let y = yDef x b; in y;
*
* This transformation can result in more efficient code as the definition of a
* let variable will only be constructed if the path of execution requires it.
*/
final class LetVarRescoper2 extends PostOrderTransformVisitor {
/** Stack of Set of String. Tracks variables encountered for
* the first time in each scope.
*/
ArrayStack<Set<String>> scopes = ArrayStack.make();
/** Map of String -> Expression.LetNonRec. Tracks the set of
* currently visible let variables.
*/
Map<String, LetNonRec> letVariableDefs = new HashMap<String, LetNonRec>();
/**
* Names of defined let variables in the order they
* were encountered.
*/
Set<String> letVariables = new LinkedHashSet<String>();
/** Set of String. Names of moved non-recursive let variables. */
Set<String> movedLetVars = new HashSet<String>();
LetVarRescoper2 () {
scopes.push(new HashSet<String>());
}
/**
* We are entering a scope.
*/
@Override
public void enterSwitchAlt(Expression.Switch.SwitchAlt alt) {
scopes.push(new HashSet<String>());
}
/** At this point we are leaving a scope. We want to
* move the let variable declaration for any lets first
* encountered in this scope inwards.
* The transformation only considers switch alts as scopes
* since a switch alt will always translate into a scope
* in the Java code generated by the LECC machine.
*/
@Override
public void exitSwitchAlt(Expression.Switch.SwitchAlt alt) {
super.exitSwitchAlt(alt);
// Pop the scope and build up the set of let
// variables first encountered in this scope.
Set<String> scope = scopes.pop();
List<String> varsInScope = new ArrayList<String>();
for (final String varName : letVariables) {
if (scope.contains(varName)) {
varsInScope.add(varName);
}
}
// At this point we want to change the body of
// the switch alt to contain let bindings for the
// let variables first encountered in this scope.
// For example:
// let x = A; y = B;
// in
// case C of
// D -> ... x ... y ...;
// ;
// Would change to.
// let x = A; y = B;
// in
// case C of
// D -> let x = A; y = B; in ... x ... y ...;
// ;
Expression body = alt.getAltExpr();
for (int i = varsInScope.size()-1; i >= 0; i--) {
String varName = varsInScope.get(i);
Expression.LetNonRec lnr = letVariableDefs.get(varName);
body = new Expression.LetNonRec(lnr.getDefn(), body);
movedLetVars.add(lnr.getDefn().getVar());
}
alt.setAltExpr(body);
}
/**
* When we enter a LetNonRec we want to check the let
* variable usage info to determine if this is one of the
* let variables to be moved. If it is we should add it
* to the map 'letVarsToMove'.
*/
@Override
public void enterLetNonRec (Expression.LetNonRec lnr) {
letVariableDefs.put(lnr.getDefn().getVar(), lnr);
letVariables.add(lnr.getDefn().getVar());
}
/**
* Upon exiting a LetNonRec we apply the transformations in the usual
* post-order fashion the remove the information about this let variable
* from the Map and Set tracking the currently visible let variables.
*/
@Override
public void exitLetNonRec(Expression.LetNonRec let){
super.exitLetNonRec(let);
letVariableDefs.remove(let.getDefn().getVar());
letVariables.remove(let.getDefn().getVar());
}
/**
* If this var is a let var and this is the first time
* it has been encountered in this scope we want to
* add it to the current scope.
*/
@Override
public void enterVar(Expression.Var var) {
if (letVariableDefs.containsKey(var.getName().getUnqualifiedName())) {
for (final Set<String> s : scopes) {
if (s.contains(var.getName().getUnqualifiedName())) {
return;
}
}
Set<String> scope = scopes.peek();
scope.add(var.getName().getUnqualifiedName());
}
}
/**
* The transformation is quite simple.
* It only applies to LetNonRec expressions.
* Since we are doing the transformation post-order
* we can look at the moved variable set. If this
* variable has been moved we want to simply return the body.
* @param expressionToTransform
* @return the transformed Expression
*/
@Override
Expression transform (Expression expressionToTransform) {
Expression.LetNonRec lnr = expressionToTransform.asLetNonRec();
if (lnr == null) {
return expressionToTransform;
}
if (movedLetVars.contains(lnr.getDefn().getVar())) {
movedLetVars.remove(lnr.getDefn().getVar());
return lnr.getBody();
}
return lnr;
}
}
// Do the inward floating of let variables for the main function and all the lifted
// let variable definition functions.
boolean change = true;
while (change) {
LetVarRescoper2 lvr2 = new LetVarRescoper2();
e.walk(lvr2);
e = lvr2.transform(e);
change = lvr2.movedLetVars.size() > 0;
}
for (final LiftedLetVarInfo llvi : liftedLetVarInfo) {
change = true;
while (change) {
LetVarRescoper2 lvr2 = new LetVarRescoper2();
llvi.expression.walk(lvr2);
llvi.expression = lvr2.transform(llvi.expression);
change = lvr2.movedLetVars.size() > 0;
}
}
// Now we can in-line the definition of any single-use let variables.
/**
* This class will in-line the definitions of single-use let variables.
*/
final class LetVarInLiner extends PostOrderTransformVisitor {
private final ReferenceCounter referenceCounter = new ReferenceCounter();
private final ModuleName moduleName;
LetVarInLiner (ModuleName moduleName) {
this.moduleName = moduleName;
}
/**
* The transformation is quite simple.
* It only applies to LetNonRec expressions.
* Determine if the let var is referenced only
* once. If so we transform the let body and
* return it.
* @param expressionToTransform
* @return the transformed Expression
*/
@Override
Expression transform (Expression expressionToTransform) {
Expression.LetNonRec lnr = expressionToTransform.asLetNonRec();
if (lnr != null) {
QualifiedName varName = QualifiedName.make(moduleName, lnr.getDefn().getVar());
referenceCounter.reset(varName);
lnr.getBody().walk(referenceCounter);
if (referenceCounter.count == 1) {
// Since this variable is only referenced once in the body
// we can in-line its definition.
Expression letVarDef = lnr.getDefn().getExpr();
VarInliner varInliner = new VarInliner(varName, letVarDef);
Expression body = lnr.getBody();
body.walk(varInliner);
body = varInliner.transform(body);
return body;
}
}
return expressionToTransform;
}
/**
* Visitor which walks an Expression counting references to
* a named variable.
* @author rcypher
*/
final class ReferenceCounter extends Visitor {
private QualifiedName varName;
private int count;
void reset (QualifiedName varName) {
this.varName = varName;
count = 0;
}
@Override
void enterVar (Expression.Var var) {
if (var.getName().equals(varName)) {
count++;
}
}
}
/**
* Class to walk the expression tree substituting variable definitions for
* variable references wherever possible.
*/
final class VarInliner extends PreOrderTransformVisitor {
// Name of the variable being inlined.
private final QualifiedName varNameToInline;
// Definition of the variable being inlined.
private final Expression VarDefToInline;
VarInliner (QualifiedName varNameToInline, Expression varDefToInline) {
this.varNameToInline = varNameToInline;
this.VarDefToInline = varDefToInline;
}
// If the expression is the variable being inlined return the
// definition. Otherwise return the original expression.
@Override
Expression transform (Expression expr) {
if (expr.asVar() != null) {
Expression.Var var = expr.asVar();
if (varNameToInline.equals(var.getName())) {
expr = VarDefToInline;
}
}
return expr;
}
}
}
// Apply the let variable in-lining transformation to the main
// function and all the lifted let variable definition functions.
LetVarInLiner letVarInliner = new LetVarInLiner(module.getName());
e.walk(letVarInliner);
e = letVarInliner.transform(e);
for (final LiftedLetVarInfo llvi : liftedLetVarInfo) {
letVarInliner = new LetVarInLiner(module.getName());
llvi.expression.walk(letVarInliner);
llvi.expression = letVarInliner.transform(llvi.expression);
}
// Encapsulate the transformed main function expression and the
// lifted let variable definition functions and return.
return new LiftedLetVarResults(e, liftedLetVarInfo);
}
/**
* Converts top level and/or into and if-then-else under certain conditions.
* The conversion occurs if the right hand side of the and/or is tail recursive
* or contains a call to a closely connected component. This will expose tail recursive calls
* for optimization and avoid space usage issues.
*
* @param e
* @return the transformed expression.
*/
private Expression changeTopLevelIfThenElse (Expression e) {
if (e.asAppl() != null) {
// Check to see if this is a fully saturate application of
// Prelude.if
Expression[] chain = appChain (e);
if (chain == null) {
return e;
}
QualifiedName varName = chain[0].asVar().getName();
if (chain.length == 4 && varName.equals(ifName)) {
Expression condition = chain[1];
Expression thenPart = changeTopLevelIfThenElse(chain[2]);
Expression elsePart = changeTopLevelIfThenElse(chain[3]);
Expression.Switch.SwitchAlt alts[] = new Expression.Switch.SwitchAlt.Matching[2];
alts[0] = new Expression.Switch.SwitchAlt.Matching(Boolean.TRUE, Collections.<FieldName, String>emptyMap(), thenPart);
alts[1] = new Expression.Switch.SwitchAlt.Matching(Boolean.FALSE, Collections.<FieldName, String>emptyMap(), elsePart);
Expression.Switch newSwitch = new Expression.Switch(condition, alts, null);
return newSwitch;
}
} else if (e.asLet() != null) {
Expression.Let let = e.asLet();
let.setBody(changeTopLevelIfThenElse (let.getBody()));
} else if (e.asSwitch() != null) {
Expression.Switch sw = e.asSwitch();
Expression.Switch.SwitchAlt alts[] = sw.getAlts();
for (int i = 0; i < alts.length; ++i) {
alts[i].setAltExpr(changeTopLevelIfThenElse(alts[i].getAltExpr()));
}
}
return e;
}
}
/**
* Structure used to describe a lifted let variable
* definition function.
* @author rcypher
*/
public static class LiftedLetVarInfo {
/** Name of the lifted function. */
private final String functionName;
/** Qualified name of the lifted function. */
private final QualifiedName functionQualifiedName;
/** Arity of the lifted function. */
private final int arity;
/** Paramter names of the lifted function. */
private final String[] parameterNames;
/** Parameter types of the lifted function. */
private final TypeExpr[] parameterTypes;
/** Parameter strictness for the lifted function. */
private final boolean[] parameterStrictness;
/** Result type of the lifted function. */
private final TypeExpr resultType;
/** Body of the lifted function. */
private Expression expression;
LiftedLetVarInfo (
ModuleName moduleName,
String functionName,
int arity,
String[] parameterNames,
TypeExpr[] parameterTypes,
boolean[] parameterStrictness,
TypeExpr resultType,
Expression expression) {
this.functionName = functionName;
this.functionQualifiedName = QualifiedName.make(moduleName, functionName);
this.arity = arity;
this.parameterNames = parameterNames;
this.parameterTypes = parameterTypes;
this.expression = expression;
this.resultType = resultType;
this.parameterStrictness = parameterStrictness;
}
public String getFunctionName() {
return functionName;
}
public QualifiedName getFunctionQualifiedName () {
return functionQualifiedName;
}
public int getArity() {
return arity;
}
public String[] getParameterNames() {
return parameterNames;
}
public Expression getExpression() {
return expression;
}
public TypeExpr[] getParameterTypes() {
return parameterTypes;
}
public boolean[] getParameterStrictness () {
return parameterStrictness;
}
public TypeExpr getResultType() {
return resultType;
}
}
/**
* This class is used to move let variable bindings into the RHS of
* other let variable bindings.
* @author rcypher
*/
private static class LetVarRescoper {
/**
* A class used to hold information about the usage of a let variable.
* @author rcypher
*/
private static final class LetVarUseInfo {
/** The name of the let variable. */
private String varName;
/** The total number of times the let variable is referenced. */
private int useCount = 0;
/** The number of times the let variable is referenced in the definition of a let variable. */
private int useInLetNonRecDefinition = 0;
/** The number of references in the definitions of the named letNonRec variables. */
private Map /* String -> Integer */<String, Integer> letNonRecVarNameUseCounts = new HashMap<String, Integer>();
/**
* If the let variable named in 'varName' is defined in the definition of a
* LetNonRec variable value this field will hold the name of that LetNonRec variable.
* Otherwise it will be null.
*/
private String definedInLetNonRec;
LetVarUseInfo (String varName, String definedIn) {
if (varName == null) {
throw new NullPointerException ("Null varName value in FreeVariableInfo constructor.");
}
this.varName = varName;
this.definedInLetNonRec = definedIn;
}
void incrementUseCount (String containingLetVar) {
useCount++;
if (containingLetVar != null) {
useInLetNonRecDefinition++;
Integer vCount = letNonRecVarNameUseCounts.get(containingLetVar);
if (vCount == null) {
vCount = Integer.valueOf(1);
} else {
vCount = Integer.valueOf(vCount.intValue() + 1);
}
letNonRecVarNameUseCounts.put(containingLetVar, vCount);
}
}
String getVarName() {
return varName;
}
int getUseCount() {
return useCount;
}
int getUseCountInLetNonRecVarDefs() {
return useInLetNonRecDefinition;
}
Map<String, Integer> useCountsInLetNonRecVarDefs() {
return Collections.unmodifiableMap(letNonRecVarNameUseCounts);
}
}
/**
* A post order transform visitor derivative used specifically
* to move letNonRec variables. This performs the transformation:
* let
* x = A;
* y = B;
* in
* C;
* To:
* let
* y = let x = A; in B;
* in
* C;
* Where the only reference to x is in B.
*/
static class LetVarBindingToRHSofLetVarBinding extends PostOrderTransformVisitor {
/** Map of (String -> LetVarUseInfo). Usage info for all letvars in the expression. */
Map<String, LetVarUseInfo> letVarUseInfo;
/** String (variable name) -> Expression.LetNonRec. LetNonRec variables to be relocated. */
Map<String, LetNonRec> letVarsToMove = new HashMap<String, LetNonRec>();
/** Set of String. Names of moved non-recursive let variables. */
Set<String> movedLetVars = new HashSet<String>();
LetVarBindingToRHSofLetVarBinding (Map<String, LetVarUseInfo> letVarUseInfo) {
this.letVarUseInfo = letVarUseInfo;
}
/**
* When we enter a LetNonRec we want to check the let
* variable usage info to determine if this is one of the
* let variables to be moved. If it is we should add it
* to the map 'letVarsToMove'.
* @param lnr
*/
@Override
public void enterLetNonRec (Expression.LetNonRec lnr) {
// If this let var needs to be shifted add it to the
// movedLetVars.
LetVarUseInfo lvui = letVarUseInfo.get(lnr.getDefn().getVar());
if (lvui.getUseCount() == lvui.getUseCountInLetNonRecVarDefs() &&
lvui.useCountsInLetNonRecVarDefs().size() == 1) {
letVarsToMove.put(lnr.getDefn().getVar(), lnr);
}
super.enterLetNonRec(lnr);
}
/**
* Upon exiting a LetNonRec we apply the transformations in the usual
* post-order fashion. Then we do an additional change which is to
* move into the let variable value definition any let variables
* that are only used in that definition.
* @param let
*/
@Override
public void exitLetNonRec(Expression.LetNonRec let){
Expression.Let.LetDefn letDef = let.getDefn();
letDef.setExpr(transform (letDef.getExpr()));
let.setBody(transform(let.getBody()));
// If there are any vars in the movedLetVars that need to be moved
// to this vars definition.
for (final String varToMoveName : letVarsToMove.keySet()) {
LetVarUseInfo lvui = letVarUseInfo.get(varToMoveName);
if (lvui.useCountsInLetNonRecVarDefs().get(let.getDefn().getVar()) != null &&
!let.getDefn().getVar().equals(lvui.definedInLetNonRec)) {
Expression.LetNonRec mlnr = letVarsToMove.get(varToMoveName);
Expression.LetNonRec newLet = new Expression.LetNonRec(mlnr.getDefn(), let.getDefn().getExpr());
let.getDefn().setExpr(newLet);
// Be sure to add the name of the moved variable to the set of moved variables
movedLetVars.add(mlnr.getDefn().getVar());
}
}
}
/**
* The transformation is quite simple.
* It only applies to LetNonRec expressions.
* Since we are doing the transformation post-order
* we can look at the moved variable set. If this
* variable has been moved we want to simply return the body.
* @param expressionToTransform
* @return the transformed expression
*/
@Override
Expression transform (Expression expressionToTransform) {
Expression.LetNonRec lnr = expressionToTransform.asLetNonRec();
if (lnr == null) {
return expressionToTransform;
}
if (movedLetVars.contains(lnr.getDefn().getVar())) {
return lnr.getBody();
}
return lnr;
}
}
/**
* Finds the referenced variables in the expression and returns
* a set of qualified names.
* @param e
* @return a Map (String -> LetVarUseInfo).
*/
private Map<String, LetVarUseInfo> getLetVarUseInfo (Expression e) {
/**
* A class for determining usage of locally defined
* let variables.
*/
final class LetVarUseInfoFinder extends Visitor {
Set<String> definedVars = new HashSet<String>();
Map<String, LetVarUseInfo> referencedVars = new HashMap<String, LetVarUseInfo>();
ArrayStack<String> containingLetVar = ArrayStack.make();
LetVarUseInfoFinder () {
containingLetVar.push(null);
}
@Override
void enterVar(Expression.Var var) {
String varName = var.getName().getUnqualifiedName();
if (definedVars.contains(varName)) {
LetVarUseInfo lvui = referencedVars.get(varName);
lvui.incrementUseCount(containingLetVar.peek());
}
}
@Override
void enterLetNonRec(Expression.LetNonRec let){
definedVars.add(let.getDefn().getVar());
referencedVars.put(let.getDefn().getVar(), new LetVarUseInfo(let.getDefn().getVar(), containingLetVar.peek()));
}
@Override
void enterLetRec(Expression.LetRec let){
Expression.Let.LetDefn defns[] = let.getDefns();
for (int i = 0; i < defns.length; ++i) {
definedVars.add(defns[i].getVar());
referencedVars.put(defns[i].getVar(), new LetVarUseInfo(defns[i].getVar(), containingLetVar.peek()));
}
}
@Override
void enterLetNonRecDef(Expression.Let.LetDefn letDef) {
containingLetVar.push(letDef.getVar());
}
@Override
void exitLetNonRecDef(Expression.Let.LetDefn letDef) {
containingLetVar.pop();
}
}
LetVarUseInfoFinder vrf = new LetVarUseInfoFinder();
e.walk(vrf);
return vrf.referencedVars;
}
/**
* This function transforms an expression by moving letNonRec variables.
* For example:
* let
* x = ...;
* in
* let
* y = ... x ...;
* in
* body;
* Will be transformed to:
* let
* y = let x = ...; in ...x...;
* in
* body;
* This transformation occurs when all references to x are in the value definition
* of the let variable y and both x and y are non-recursive let variables.
* @param e
* @return the transformed expression.
*/
private Expression rescopeLetVariables (Expression e) {
Map<String, LetVarUseInfo> letVarUseInfo = getLetVarUseInfo(e);
LetVarBindingToRHSofLetVarBinding lvr = new LetVarBindingToRHSofLetVarBinding(letVarUseInfo);
e.walk(lvr);
e = lvr.transform(e);
return e;
}
}
/**
* Determine if the given expression contains a tail recursive call.
* Note: This simply looks for an instance of Expression.TailRecursiveCall which
* has been placed in the expression graph by previous transformations.
* @param e
* @return true if e contains tail recursive call
*/
static boolean isTailRecursive (Expression e) {
class TailRecursionFinder extends Visitor {
boolean tailRecursive = false;
@Override
public void enterTailRecursiveCall (Expression.TailRecursiveCall rc) {
tailRecursive = true;
}
}
TailRecursionFinder rf = new TailRecursionFinder();
e.walk(rf);
return rf.tailRecursive;
}
/**
* Determines if the given expression (e) is dependent on
* the named variable.
* @param e
* @param varName
* @return true if e references var
*/
static boolean isDependentOn (Expression e, QualifiedName varName) {
class IsDependent extends Visitor {
private final QualifiedName varName;
boolean dependent = false;
IsDependent (QualifiedName varName) {
this.varName = varName;
}
@Override
void enterVar (Expression.Var var) {
if (varName.equals(var.getName())) {
dependent = true;
}
}
}
IsDependent vc = new IsDependent (varName);
e.walk(vc);
return vc.dependent;
}
/**
* For each function in the module determine the strongly connected components
* and update the information in the MachineFunction.
* @param moduleName
* @param nameToExpressionMap
* @return a list of Set objects where each set represents a set of string connected components.
*/
static List<Set<String>> determineStronglyConnectedComponents (ModuleName moduleName, Map<String, Expression> nameToExpressionMap) {
class DependsOn extends Visitor {
private final Set<String> dependees = new HashSet<String>();
private final ModuleName moduleName;
private final String thisFunctionName;
DependsOn (ModuleName moduleName, String thisFunctionName) {
this.moduleName = moduleName;
this.thisFunctionName = thisFunctionName;
}
Set<String> getDependees() {
return dependees;
}
@Override
void enterVar (Expression.Var var) {
QualifiedName varName = var.getName();
if (varName.getModuleName().equals(moduleName)
&& !varName.getUnqualifiedName().equals(thisFunctionName)) {
dependees.add(varName.getUnqualifiedName());
}
}
}
VertexBuilderList<String> vbs = new VertexBuilderList<String>();
for (final Map.Entry<String, Expression> entry : nameToExpressionMap.entrySet()) {
String name = entry.getKey();
Expression e = entry.getValue();
if (e == null) {
continue;
}
DependsOn dd = new DependsOn (moduleName, name);
e.walk(dd);
vbs.add(new VertexBuilder<String>(name, dd.getDependees()));
}
Graph<String> g = new Graph<String>(vbs);
g = g.calculateStronglyConnectedComponents();
List<Set<String>> connectionSets = new ArrayList<Set<String>>();
for (int i = 0; i < g.getNStronglyConnectedComponents(); ++i) {
Graph<String>.Component component = g.getStronglyConnectedComponent(i);
// Use a LinkedHashSet so that the order of iteration will
// always be consistent.
Set<String> s = new LinkedHashSet<String>();
for (int j = 0; j < component.size(); ++j) {
Vertex<String> v = component.getVertex(j);
s.add(v.getName());
}
connectionSets.add(s);
}
return connectionSets;
}
static class Visitor {
void enterAppl(Expression.Appl appl){}
void exitAppl(Expression.Appl appl){}
void enterLetNonRec(Expression.LetNonRec let){}
void exitLetNonRec(Expression.LetNonRec let){}
void enterLetRec(Expression.LetRec let){}
void exitLetRec(Expression.LetRec let){}
void enterLetNonRecDef(Expression.Let.LetDefn letDef){}
void exitLetNonRecDef(Expression.Let.LetDefn letDef){}
void enterLetRecDef(Expression.Let.LetDefn letDef){}
void exitLetRecDef(Expression.Let.LetDefn letDef){}
void enterLiteral(Expression.Literal lit){}
void exitLiteral(Expression.Literal lit){}
void enterPackCons(Expression.PackCons packCons){}
void exitPackCons(Expression.PackCons packCons){}
void enterRecordCase(Expression.RecordCase recordCase){}
void exitRecordCase(Expression.RecordCase recordCase){}
void enterRecordUpdate(Expression.RecordUpdate recordUpdate){}
void exitRecordUpdate(Expression.RecordUpdate recordUpdate){}
void enterRecordExtension(Expression.RecordExtension recordExtension){}
void exitRecordExtension(Expression.RecordExtension recordExtension){}
void enterRecordSelection(Expression.RecordSelection recordSelection){}
void exitRecordSelection(Expression.RecordSelection recordSelection){}
void enterTailRecursiveCall (Expression.TailRecursiveCall rc) {}
void exitTailRecursiveCall (Expression.TailRecursiveCall rc) {}
void enterSwitch(Expression.Switch s){}
void exitSwitch(Expression.Switch s){}
void enterVar(Expression.Var var){}
void exitVar(Expression.Var var){}
void enterSwitchAlt (Expression.Switch.SwitchAlt alt){}
void exitSwitchAlt (Expression.Switch.SwitchAlt alt){}
void enterDataConsSelection(Expression.DataConsSelection dataConsSelection){}
void exitDataConsSelection(Expression.DataConsSelection dataConsSelection){}
void enterCast(Expression.Cast cast) {}
void exitCast(Expression.Cast cast) {}
}
private static abstract class PreOrderTransformVisitor extends Visitor {
abstract Expression transform (Expression e);
@Override
public void enterAppl(Expression.Appl appl){
appl.setE1 (transform (appl.getE1()));
appl.setE2 (transform (appl.getE2()));
}
@Override
public void enterLetNonRec(Expression.LetNonRec let){
Expression.Let.LetDefn letDef = let.getDefn();
letDef.setExpr(transform (letDef.getExpr()));
let.setBody(transform(let.getBody()));
}
@Override
public void enterLetRec(Expression.LetRec let){
Expression.Let.LetDefn[] defs = let.getDefns();
for (int i = 0; i < defs.length; ++i) {
defs[i].setExpr (transform (defs[i].getExpr()));
}
let.setBody(transform(let.getBody()));
}
@Override
public void enterRecordCase(Expression.RecordCase recordCase){
recordCase.setConditionExpr (transform (recordCase.getConditionExpr()));
recordCase.setResultExpr (transform (recordCase.getResultExpr()));
}
@Override
public void enterRecordUpdate(Expression.RecordUpdate recordUpdate){
Expression e = recordUpdate.getBaseRecordExpr();
recordUpdate.setBaseRecordExpr(transform (e));
for (final Map.Entry<FieldName, Expression> entry : recordUpdate.getUpdateFieldValuesMap().entrySet()) {
FieldName fieldName = entry.getKey();
Expression valueExpr = entry.getValue();
recordUpdate.setFieldValueUpdate(fieldName, transform (valueExpr));
}
}
@Override
public void enterRecordExtension(Expression.RecordExtension recordExtension){
Expression e = recordExtension.getBaseRecordExpr();
if (e != null) {
recordExtension.setBaseRecordExpr(transform (e));
}
for (final Map.Entry<FieldName, Expression> entry : recordExtension.getExtensionFieldsMap().entrySet()) {
FieldName fieldName = entry.getKey();
Expression valueExpr = entry.getValue();
recordExtension.setFieldExtension(fieldName, transform (valueExpr));
}
}
@Override
public void enterRecordSelection(Expression.RecordSelection recordSelection){
recordSelection.setRecordExpr (transform (recordSelection.getRecordExpr()));
}
@Override
public void enterSwitch(Expression.Switch s){
s.setSwitchExpr (transform (s.getSwitchExpr()));
}
@Override
public void enterSwitchAlt(Expression.Switch.SwitchAlt alt) {
alt.setAltExpr(transform(alt.getAltExpr()));
}
@Override
public void enterDataConsSelection(Expression.DataConsSelection dataConsSelection){
dataConsSelection.setDCValueExpr(transform(dataConsSelection.getDCValueExpr()));
}
@Override
public void enterTailRecursiveCall(Expression.TailRecursiveCall trc) {
Expression arguments[] = trc.getArguments();
for (int i = 0; i < arguments.length; ++i) {
arguments[i] = transform (arguments[i]);
}
trc.setArguments(arguments);
}
}
private static abstract class PostOrderTransformVisitor extends Visitor {
abstract Expression transform (Expression e);
@Override
public void exitAppl(Expression.Appl appl){
appl.setE1 (transform (appl.getE1()));
appl.setE2 (transform (appl.getE2()));
}
@Override
public void exitLetNonRec(Expression.LetNonRec let){
Expression.Let.LetDefn letDef = let.getDefn();
letDef.setExpr(transform (letDef.getExpr()));
let.setBody(transform(let.getBody()));
}
@Override
public void exitLetRec(Expression.LetRec let){
Expression.Let.LetDefn[] defs = let.getDefns();
for (int i = 0; i < defs.length; ++i) {
defs[i].setExpr (transform (defs[i].getExpr()));
}
let.setBody(transform(let.getBody()));
}
@Override
public void exitRecordCase(Expression.RecordCase recordCase){
recordCase.setConditionExpr (transform (recordCase.getConditionExpr()));
recordCase.setResultExpr (transform (recordCase.getResultExpr()));
}
@Override
public void exitRecordUpdate(Expression.RecordUpdate recordUpdate){
Expression e = recordUpdate.getBaseRecordExpr();
recordUpdate.setBaseRecordExpr(transform (e));
for (final Map.Entry<FieldName, Expression> entry : recordUpdate.getUpdateFieldValuesMap().entrySet()) {
FieldName fieldName = entry.getKey();
Expression valueExpr = entry.getValue();
recordUpdate.setFieldValueUpdate(fieldName, transform (valueExpr));
}
}
@Override
public void exitRecordExtension(Expression.RecordExtension recordExtension){
Expression e = recordExtension.getBaseRecordExpr();
if (e != null) {
recordExtension.setBaseRecordExpr(transform (e));
}
for (final Map.Entry<FieldName, Expression> entry : recordExtension.getExtensionFieldsMap().entrySet()) {
FieldName fieldName = entry.getKey();
Expression valueExpr = entry.getValue();
recordExtension.setFieldExtension(fieldName, transform (valueExpr));
}
}
@Override
public void exitRecordSelection(Expression.RecordSelection recordSelection){
recordSelection.setRecordExpr (transform (recordSelection.getRecordExpr()));
}
@Override
public void exitSwitch(Expression.Switch s){
s.setSwitchExpr (transform (s.getSwitchExpr()));
}
@Override
public void exitSwitchAlt(Expression.Switch.SwitchAlt alt) {
alt.setAltExpr(transform(alt.getAltExpr()));
}
@Override
public void exitDataConsSelection(Expression.DataConsSelection dataConsSelection){
dataConsSelection.setDCValueExpr(transform(dataConsSelection.getDCValueExpr()));
}
@Override
public void exitTailRecursiveCall(Expression.TailRecursiveCall trc) {
Expression arguments[] = trc.getArguments();
for (int i = 0; i < arguments.length; ++i) {
arguments[i] = transform (arguments[i]);
}
trc.setArguments(arguments);
}
}
private static class TailRecursionTransformer {
/** The MachineFunction object corresponding to the function containing the expression being transformed. */
MachineFunction function;
// Indicates whether a transformation has occurred.
boolean changed = false;
TailRecursionTransformer (MachineFunction coreFunction) {
this.function = coreFunction;
}
/**
* Determine if the given expression contains a top level
* fully saturated call to the given function.
* @param e
* @return true if a tail recursive call
*/
private boolean isTailRecursive (Expression e) {
return doTailRecursiveCheck (e);
}
private boolean doTailRecursiveCheck (Expression e) {
if (e.asAppl() != null) {
Expression[] chain = appChain (e);
if (chain == null) {
return false;
}
Expression.Var var = chain[0].asVar ();
if (var.getForeignFunctionInfo() != null) {
return false;
}
if (chain.length == function.getNFormalParameters() + 1 &&
var.getName().equals(function.getQualifiedName())) {
return true;
}
QualifiedName varName = var.getName();
// Check to see if this is a conditional.
if (chain.length == 4 && varName.equals(ifName)) {
return doTailRecursiveCheck (chain[2]) || doTailRecursiveCheck (chain[3]);
}
// Check to see if this is 'a' && tail recursive, 'a' || tail recursive, seq a tail recursive.
if (chain.length == 3 &&
(varName.equals(CAL_Prelude.Functions.and) ||
varName.equals(CAL_Prelude.Functions.or) ||
varName.equals(CAL_Prelude.Functions.seq))) {
return doTailRecursiveCheck (chain[2]);
}
} else if (e.asLetNonRec() != null) {
Expression.LetNonRec let = e.asLetNonRec();
return doTailRecursiveCheck (let.getBody());
} else if (e.asLetRec() != null) {
Expression.LetRec let = e.asLetRec();
return doTailRecursiveCheck (let.getBody());
} else if (e.asSwitch() != null) {
Expression.Switch sw = e.asSwitch();
Expression.Switch.SwitchAlt alts[] = sw.getAlts();
for (int i = 0; i < alts.length; ++i) {
if (doTailRecursiveCheck (alts[i].getAltExpr())) {
return true;
}
}
} else if (e.asRecordCase() != null) {
Expression.RecordCase rc = e.asRecordCase();
return doTailRecursiveCheck(rc.getResultExpr());
}
return false;
}
Expression transformTailRecursionCalls (Expression e) {
changed = false;
return doTailRecursionTransforms (e);
}
private Expression doTailRecursionTransforms (Expression e) {
if (e.asAppl() != null) {
Expression[] chain = appChain (e);
if (chain == null) {
return e;
}
Expression.Appl app = e.asAppl ();
QualifiedName varName = chain[0].asVar().getName();
// Check to see if this is an if-then-else.
if (chain.length == 4 && varName.equals(ifName)) {
app.setE2 (doTailRecursionTransforms (app.getE2 ()));
Expression.Appl prev = app.getE1().asAppl();
if (prev != null) {
prev.setE2 (doTailRecursionTransforms (prev.getE2 ()));
}
} else
if (chain.length == 3 && varName.equals(CAL_Prelude.Functions.seq)) {
app.setE2 (doTailRecursionTransforms (app.getE2 ()));
} else
if (varName.equals(function.getQualifiedName()) && isTailRecursive (e)) {
// This is a tail recursive call.
Expression[] arguments = new Expression [function.getNFormalParameters()];
System.arraycopy (chain, 1, arguments, 0, function.getNFormalParameters());
Expression.TailRecursiveCall rc = new Expression.TailRecursiveCall (chain[0].asVar(), arguments);
changed = true;
return rc;
}
} else if (e.asLetRec () != null) {
Expression.LetRec let = e.asLetRec();
let.setBody(doTailRecursionTransforms (let.getBody()));
} else if (e.asLetNonRec () != null) {
Expression.LetNonRec let = e.asLetNonRec();
let.setBody(doTailRecursionTransforms (let.getBody()));
} else if (e.asSwitch() != null) {
Expression.Switch sw = e.asSwitch();
Expression.Switch.SwitchAlt alts[] = sw.getAlts();
for (int i = 0; i < alts.length; ++i) {
Expression.Switch.SwitchAlt alt = alts[i];
alt.setAltExpr(doTailRecursionTransforms(alt.getAltExpr()));
}
} else if (e.asRecordCase() != null) {
Expression.RecordCase rc = e.asRecordCase();
rc.setResultExpr(doTailRecursionTransforms(rc.getResultExpr()));
}
return e;
}
}
/**
* Convert an application chain to an array.
* @param root
* @return null if this is not a fully saturated SC application, array of Expression otherwise.
*/
static private Expression[] appChain (Expression root) {
// Walk down the left branch.
Expression c = root;
int nArgs = 0;
while (c instanceof Expression.Appl) {
nArgs++;
c = ((Expression.Appl)c).getE1();
}
// At this point c should be an Expression.Var
if (!(c instanceof Expression.Var)) {
return null;
}
Expression[] chain = new Expression [nArgs + 1];
chain[0] = c;
c = root;
for (int i = nArgs; i >= 1; i--) {
chain[i] = ((Expression.Appl)c).getE2();
c = ((Expression.Appl)c).getE1();
}
return chain;
}
/**
* Build a list of all the Expression.Literal objects in the given expression.
* @param e
* @return a list of Expression.Literal objects.
*/
static List<Expression.Literal> literals (Expression e) {
class LiteralCollector extends Visitor {
private List<Expression.Literal> literals = new ArrayList<Expression.Literal>();
@Override
void enterLiteral(Expression.Literal lit){
literals.add (lit);
}
List<Expression.Literal> getLiterals () {
return literals;
}
}
LiteralCollector collector = new LiteralCollector();
e.walk(collector);
return collector.getLiterals();
}
/**
* This method takes an expression and walks across the
* expression setting the appropriate use count into any let variable
* definitions.
* @param e
*/
private static void letVariableUse(Expression e) {
// A visitor class for counting let variable usage. The compiler guarantees that
// let variables will have unique names.
class VarUseCounter extends Visitor {
// Map of String -> Integer. Let variable name -> reference count.
Map<String, LetDefn> varNamToDef = new HashMap<String, LetDefn>();
@Override
public void enterVar(Expression.Var var) {
// If this is a let variable in the counts map increment the reference count.
String varName = var.getName().getUnqualifiedName();
LetDefn letDef = varNamToDef.get(varName);
if (letDef != null) {
letDef.incrementUseCount();
}
}
@Override
public void enterLetNonRec(Expression.LetNonRec let) {
// Add the let variable to the counts map with a reference count of zero.
varNamToDef.put (let.getDefn().getVar(), let.getDefn());
let.getDefn().setUseCount(0);
}
@Override
public void enterLetRec(Expression.LetRec let) {
// Add the let variables to the counts map with a reference count of zero.
Expression.Let.LetDefn defs[] = let.getDefns();
for (int i = 0; i < defs.length; ++i) {
varNamToDef.put(defs[i].getVar(), defs[i]);
defs[i].setUseCount(0);
}
}
}
// Create a VarUseCounter and walk it across the expression.
VarUseCounter vc = new VarUseCounter();
e.walk(vc);
}
/**
* Determine if an application chain is an application of a dictionary function to the correct number of
* arguments to fully resolve the returned class method.
* Ex. Prelude.add x y z is oversaturated since add takes a single argument and resolves to addInt, addDouble, etc.
* If we know that the returned function is of arity two we see that it will be fully saturated and we can
* optimize the reduction/representation of the oversaturated application.
* @param e
* @param module
* @return number of arguments extra to the dictionary, or -1 if e is not an oversaturated dictionary application.
*/
public static int getNOversaturatedDictionaryArgs (Expression e, ModuleTypeInfo module) {
if (e.asAppl () == null) {
return -1;
}
// First we need to determine if the left hand side of the application is dictionary
Expression[] chain = appChain (e);
if (chain[0].asVar() == null) {
return -1;
}
Expression.Var var = chain[0].asVar();
if (var.getName().getUnqualifiedName().startsWith("$dictvar")) {
// Parse out the type class name.
// Variable name will be in the form $dictvarModule.Class#N where N is an integer
// number.
String name = var.getName().getUnqualifiedName();
name = name.substring(8);
QualifiedName qualifiedClassName = QualifiedName.makeFromCompoundName(name.substring(0, name.indexOf("#")));
ModuleName moduleName = qualifiedClassName.getModuleName();
String className = qualifiedClassName.getUnqualifiedName();
// Switch to the correct module for the type class.
if (!module.getModuleName().equals(moduleName)) {
module = module.getImportedModule(moduleName);
}
if (module == null) {
return -1;
}
// Get the type class.
TypeClass typeClass = module.getTypeClass(className);
if (typeClass == null) {
return -1;
}
// Get the class method in question.
ClassMethod cm = null;
int nExtraArgs = chain.length - 1;
if (typeClass.internal_isSingleMethodRootClass()) {
cm = typeClass.getNthClassMethod(0);
} else {
if (chain[1].asLiteral() == null || !(chain[1].asLiteral().getLiteral() instanceof Integer)) {
return -1;
}
nExtraArgs--;
int dictIndex = ((Integer)chain[1].asLiteral().getLiteral()).intValue();
for (int i = 0; i < typeClass.getNClassMethods(); ++i) {
if (typeClass.getNthClassMethod(i).internal_getDictionaryIndex() == dictIndex) {
cm = typeClass.getNthClassMethod(i);
break;
}
}
}
if (cm == null) {
return -1;
}
// return the expected number of extra arguments.
TypeExpr te = cm.getTypeExpr();
int nExpectedExtraArgs = te.getArity();
if (nExtraArgs == nExpectedExtraArgs) {
return nExpectedExtraArgs;
}
}
return -1;
}
/**
* Transform an expression by removing any function references which are
* tagged as being an alias of another function and replacing them with
* a reference to the other function.
* Also transorm function references which are tagged as being equivalent to
* a literal value.
* @param function the function to transform
* @param m the containing module
* @param logger
*/
static void antiAlias (MachineFunction function, Module m, CompilerMessageLogger logger) {
// This transform visitor simply substitues one Expression.Var for another if
// the first var is an alias of something else.
class AATransformVisitor extends PreOrderTransformVisitor {
private final Module module;
private final MachineFunction function;
private final CompilerMessageLogger logger;
AATransformVisitor (MachineFunction function, Module m, CompilerMessageLogger logger) {
this.module = m;
this.function = function;
this.logger = logger;
}
@Override
Expression transform (Expression e) {
if (e.asVar() != null) {
Expression.Var var = e.asVar();
MachineFunction mf = module.getFunction(var.getName());
// Determine if the var is an alias for another function.
if (mf != null) {
if (mf.getAliasOf() != null) {
QualifiedName aliasOf = mf.getAliasOf();
Module aliasModule = module.findModule(aliasOf.getModuleName());
if (aliasModule != null) {
ModuleTypeInfo aliasModuleTypeInfo = aliasModule.getModuleTypeInfo();
// Need to create a new Expression.Var to substitute.
// NOTE: We have to handle Prelude.if as a special case because there is no entity for it
// in the type info.
if (aliasModuleTypeInfo != null) {
FunctionalAgent functionalAgent = aliasModuleTypeInfo.getFunctionalAgent(aliasOf.getUnqualifiedName());
if (functionalAgent != null) {
//System.out.println ("substituting " + aliasOf + " for " + var.getName());
return new Expression.Var (functionalAgent);
} else {
return new Expression.Var (aliasOf);
}
} else {
logger.logMessage(new CompilerMessage(new MessageKind.Error.UnableToResolveFunctionAlias(function.getQualifiedName().getQualifiedName(), var.getName().getQualifiedName(), aliasOf.getQualifiedName())));
}
} else {
logger.logMessage(new CompilerMessage(new MessageKind.Error.UnableToResolveFunctionAlias(function.getQualifiedName().getQualifiedName(), var.getName().getQualifiedName(), aliasOf.getQualifiedName())));
}
} else
if (mf.getLiteralValue() != null) {
return new Expression.Literal(mf.getLiteralValue());
}
}
}
return e;
}
}
Expression e = function.getExpressionForm();
// Walk the expression doing any substitution for aliased functions.
AATransformVisitor tv = new AATransformVisitor(function, m, logger);
e = tv.transform(e);
e.walk(tv);
function.setExpression(e);
}
/**
* Finds the referenced variables in the expression which are
* not locally defined and returns a set of qualified names.
* @param e
* @param module
* @return a set of QualifiedName.
*/
public static Set<QualifiedName> findReferencedFreeVariables (Expression e, final Module module) {
if (e == null || module == null) {
throw new NullPointerException ("Invalid argument value in ExpressionAnalyzer.findReferencedVariables");
}
final class VariableReferenceFinder extends Visitor {
/** Set of QualifiedName */
Set<QualifiedName> definedVars = new HashSet<QualifiedName>();
/** Set of QualifiedName */
Set<QualifiedName> referencedVars = new LinkedHashSet<QualifiedName>();
@Override
void enterVar(Expression.Var var) {
// If this is not in the defined vars it is a free var.
QualifiedName varName = var.getName();
if (!definedVars.contains(varName) &&
module.getFunction(varName) == null) {
referencedVars.add(varName);
}
}
@Override
void enterLetRec(Expression.LetRec letRec) {
for (int i = 0; i < letRec.getDefns().length; ++i) {
definedVars.add(QualifiedName.make(module.getName(), letRec.getDefns()[i].getVar()));
}
}
@Override
void enterLetNonRec (Expression.LetNonRec letNonRec) {
definedVars.add(QualifiedName.make(module.getName(), letNonRec.getDefn().getVar()));
}
@Override
void enterSwitchAlt (Expression.Switch.SwitchAlt alt) {
if (alt.hasVars()) {
String[] names = alt.getVarNames();
for (int i = 0; i < names.length; ++i) {
definedVars.add(QualifiedName.make(module.getName(), names[i]));
}
}
}
@Override
void enterRecordCase(Expression.RecordCase recordCase){
final SortedMap<FieldName, String> fieldBindingVarMap = recordCase.getFieldBindingVarMap();
if (fieldBindingVarMap.size() > 0) {
for (final Map.Entry<FieldName, String> entry : fieldBindingVarMap.entrySet()) {
definedVars.add(QualifiedName.make(module.getName(), entry.getValue()));
}
}
}
}
// A visitor class for finding free variables. The compiler guarantees that
// all variables will have unique names.
VariableReferenceFinder vrf = new VariableReferenceFinder();
e.walk(vrf);
return vrf.referencedVars;
}
/**
* @return whether or not the given expression had an unsafe coerce call.
*/
public boolean getHadUnsafeCoerce(){
return hadUnsafeCoerce;
}
}