Package wyil.transforms

Source Code of wyil.transforms.BackPropagation$Env

// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// 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 the <organization> 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 DAVID J. PEARCE 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.

package wyil.transforms;

import static wycc.lang.SyntaxError.*;

import java.math.BigDecimal;
import java.util.*;

import wyautl.util.BigRational;
import wybs.lang.*;
import wycc.lang.NameID;
import wycc.lang.SyntacticElement;
import wycc.lang.Transform;
import wycc.util.Pair;
import wyfs.util.Trie;
import wyil.lang.*;
import wyil.lang.Code.Block.Entry;
import wyil.util.dfa.BackwardFlowAnalysis;

/**
* <p>
* Inserts implict coercions into those methods and functions of a WYIL module.
* This is done using a backwards dataflow analysis (hence, the name). The idea
* is that, at any point where the type of a variable changes, a coercion should
* be inserted. As a simple example, consider the following Whiley function:
* </p>
*
* <pre>
* function f(int x) => real:
*    return x
* </pre>
* <p>
* Here, the type of variable <code>x</code> changes from <code>int</code> to
* <code>real</code> in the <code>return</code> statement. Before back
* propagation is applied, the WYIL code for this function looks like this:
* </p>
*
* <pre>
* function f(int x) => real:
*     load x : int
*     return : real
* </pre>
* <p>
* We can see that, at one point, the variable <code>x</code> has type
* <code>int</code> and it is then immediately assumed to have type
* <code>real</code>. The semantics of WYIL code dictates that we must
* explicitly coerce variables in such situations. Thus, after back propagation
* is applied, we have the following:
* </p>
*
* <pre>
* function f(int x) => real:
*     load x : int
*     convert int to real
*     return : real
* </pre>
* <p>
* Here, the <code>convert</code> bytecode is responsible for changing the
* representation of <code>x</code> from <code>int</code> to <code>real</code>.
* On some architectures, this will correspond to a physical change in the
* underlying representation. On others, it may simply correspond to a no-op.
* </p>
*
* @author David J. Pearce
*
*/
public final class BackPropagation extends BackwardFlowAnalysis<BackPropagation.Env> implements Transform<WyilFile> {
  private static final HashMap<Integer,Code.Block> afterInserts = new HashMap<Integer,Code.Block>();
  private static final HashMap<Integer,Code.Block.Entry> rewrites = new HashMap<Integer,Code.Block.Entry>();

  /**
   * Determines whether constant propagation is enabled or not.
   */
  private boolean enabled = getEnable();


  public BackPropagation(Builder builder) {
    super();
  }

  // ===================================================
  // Configuration Options
  // ===================================================

  public static String describeEnable() {
    return "Enable/disable constant propagation";
  }

  public static boolean getEnable() {
    return true; // default value
  }

  public void setEnable(boolean flag) {
    this.enabled = flag;
  }

  // ===================================================
  // Code
  // ===================================================

  @Override
  public void apply(WyilFile module) {
    if(enabled) {
      super.apply(module);
    }
  }

  @Override
  protected WyilFile.TypeDeclaration propagate(WyilFile.TypeDeclaration type) {
    // TODO: back propagate through type constraints
    return type;
  }

  @Override
  protected Env lastStore() {
    Env environment = new Env();

    for (int i = 0; i < block.numSlots(); i++) {
      environment.add(Type.T_VOID);
    }

    return environment;
  }

  @Override
  protected WyilFile.Case propagate(WyilFile.Case mcase) {

    // TODO: back propagate through pre- and post-conditions

    methodCase = mcase;

    ArrayList<Code.Block> requires = new ArrayList<Code.Block>(
        mcase.precondition());
    for (int i = 0; i != requires.size(); ++i) {
      Code.Block tmp = propagate(requires.get(i));
      requires.set(i, tmp);
    }
    ArrayList<Code.Block> ensures = new ArrayList<Code.Block>(
        mcase.postcondition());
    for (int i = 0; i != ensures.size(); ++i) {
      Code.Block tmp = propagate(ensures.get(i));
      ensures.set(i, tmp);
    }
    Code.Block body = mcase.body();
    if (body != null) {
      body = propagate(body);
    }
    return new WyilFile.Case(body, requires, ensures, mcase.attributes());
  }

  protected Code.Block propagate(Code.Block block) {

    // Setup global items
    stores = new HashMap<String,Env>();
    afterInserts.clear();
    rewrites.clear();
    this.block = block;

    // Now, propagate through the block
    propagate(0,block.size(), lastStore(), Collections.EMPTY_LIST);

    // At this point, we apply the inserts
    Code.Block nblock = new Code.Block(block.numInputs());

    for(int i=0;i!=block.size();++i) {
      Code.Block.Entry rewrite = rewrites.get(i);
      if(rewrite != null) {
        nblock.add(rewrite);
      } else {
        nblock.add(block.get(i));
      }
      Code.Block afters = afterInserts.get(i);
      if(afters != null) {
        nblock.addAll(afters);
      }
    }

    return nblock;
  }

  @Override
  protected Env propagate(int index, Entry entry, Env environment) {
    Code code = entry.code;

    // reset the rewrites for this code, in case it changes
    afterInserts.remove(index);
    environment = (Env) environment.clone();

    if(code instanceof Codes.BinaryOperator) {
      infer(index,(Codes.BinaryOperator)code,entry,environment);
    } else if(code instanceof Codes.Convert) {
      infer(index,(Codes.Convert)code,entry,environment);
    } else if(code instanceof Codes.Const) {
      infer(index,(Codes.Const)code,entry,environment);
    } else if(code instanceof Codes.Debug) {
      infer(index,(Codes.Debug)code,entry,environment);
    } else if(code instanceof Codes.AssertOrAssume) {
      infer(index,(Codes.AssertOrAssume)code,entry,environment);
    } else if(code instanceof Codes.Fail) {
      infer(index,(Codes.Fail)code,entry,environment);
    } else if(code instanceof Codes.FieldLoad) {
      infer(index,(Codes.FieldLoad)code,entry,environment);
    } else if(code instanceof Codes.IndirectInvoke) {
      infer(index,(Codes.IndirectInvoke)code,entry,environment);
    } else if(code instanceof Codes.Invoke) {
      infer(index,(Codes.Invoke)code,entry,environment);
    } else if(code instanceof Codes.Invert) {
      infer(index,(Codes.Invert)code,entry,environment);
    } else if(code instanceof Codes.Lambda) {
      infer(index,(Codes.Lambda)code,entry,environment);
    } else if(code instanceof Codes.Label) {
      // skip
    } else if(code instanceof Codes.ListOperator) {
      infer(index,(Codes.ListOperator)code,entry,environment);
    } else if(code instanceof Codes.LengthOf) {
      infer(index,(Codes.LengthOf)code,entry,environment);
    } else if(code instanceof Codes.SubList) {
      infer(index,(Codes.SubList)code,entry,environment);
    } else if(code instanceof Codes.IndexOf) {
      infer(index,(Codes.IndexOf)code,entry,environment);
    } else if(code instanceof Codes.Assign) {
      infer(index,(Codes.Assign)code,entry,environment);
    } else if(code instanceof Codes.Update) {
      infer(index,(Codes.Update)code,entry,environment);
    } else if(code instanceof Codes.NewMap) {
      infer(index,(Codes.NewMap)code,entry,environment);
    } else if(code instanceof Codes.NewList) {
      infer(index,(Codes.NewList)code,entry,environment);
    } else if(code instanceof Codes.NewRecord) {
      infer(index,(Codes.NewRecord)code,entry,environment);
    } else if(code instanceof Codes.NewSet) {
      infer(index,(Codes.NewSet)code,entry,environment);
    } else if(code instanceof Codes.NewTuple) {
      infer(index,(Codes.NewTuple)code,entry,environment);
    } else if(code instanceof Codes.UnaryOperator) {
      infer(index,(Codes.UnaryOperator)code,entry,environment);
    } else if(code instanceof Codes.Dereference) {
      infer(index,(Codes.Dereference)code,entry,environment);
    } else if(code instanceof Codes.Return) {
      infer(index,(Codes.Return)code,entry,environment);
    } else if(code instanceof Codes.Nop) {
      // skip
    } else if(code instanceof Codes.SetOperator) {
      infer(index,(Codes.SetOperator)code,entry,environment);
    } else if(code instanceof Codes.StringOperator) {
      infer(index,(Codes.StringOperator)code,entry,environment);
    } else if(code instanceof Codes.SubString) {
      infer(index,(Codes.SubString)code,entry,environment);
    } else if(code instanceof Codes.NewObject) {
      infer(index,(Codes.NewObject)code,entry,environment);
    } else if(code instanceof Codes.Throw) {
      infer(index,(Codes.Throw)code,entry,environment);
    } else if(code instanceof Codes.TupleLoad) {
      infer(index,(Codes.TupleLoad)code,entry,environment);
    } else {
      internalFailure("unknown: " + code.getClass().getName(),filename,entry);
      return null;
    }

    // Now, update requirement for assignment register (if any)

    if(code instanceof Code.AbstractAssignable) {
      Code.AbstractAssignable aa = (Code.AbstractAssignable) code;
      if(aa.target() != Codes.NULL_REG) {
        environment.set(aa.target(),Type.T_VOID);
      }
    }

    return environment;
  }

  protected Env infer(int index,
      Codes.AssertOrAssume code, Code.Block.Entry stmt, Env environment) {
    // effectively a no-op for now.
    return environment;
  }

  private void infer(int index, Codes.BinaryOperator code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req,code.type(),code.target(),index,entry);
    if(code.kind == Codes.BinaryOperatorKind.LEFTSHIFT || code.kind == Codes.BinaryOperatorKind.RIGHTSHIFT) {
      environment.set(code.operand(0),code.type());
      environment.set(code.operand(1),Type.T_INT);
    } else if(code.kind == Codes.BinaryOperatorKind.RANGE){
      environment.set(code.operand(0),Type.T_INT);
      environment.set(code.operand(1),Type.T_INT);
    } else {
      environment.set(code.operand(0),code.type());
      environment.set(code.operand(1),code.type());
    }
  }

  private void infer(int index, Codes.Convert code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    // TODO: add insertion?
    environment.set(code.operand(0),code.type());
  }

  private void infer(int index, Codes.Const code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());

    if (req.equals(code.constant.type()) || req == Type.T_VOID) {
      // do nout!
    } else if(req == Type.T_ANY) {
      // This is really a strange hack which will eventually be eliminated
      // when all registers have fixed types. The issue is that we cannot
      // perform any conversion on the constant itself, since every
      // constant has a fixed type. Therefore, to ensure that the correct
      // (e.g. JVM) register type is given we must perform an explicit
      // coercion.
      coerceAfter(req,code.constant.type(),code.target(),index,entry);
    } else {
      Constant nconstant;
      if (req == Type.T_STRING) {
        // String coercion!
        nconstant = Constant.V_STRING(code.constant.toString());
      } else {
        nconstant = convert(req, code.constant, entry);
      }

      if(!nconstant.equals(code.constant)) {
        // Something has changed
        rewrites.put(
            index,
            new Code.Block.Entry(Codes.Const(code.target(), nconstant), entry
                .attributes()));
      }

      if(!nconstant.type().equals(req)) {
        // After the conversion, we may still need to use an explicit
        // coercion bytecode if the types still don't match. This can
        // happen, for example, if the required type is "any", then
        // we'll always need an explicit coercion since there is no
        // constant which yields the type "any".
        coerceAfter(req, nconstant.type(), code.target(), index,
            entry);
      }
    }
  }

  private void infer(int index, Codes.Debug code, Code.Block.Entry entry,
      Env environment) {
    environment.set(code.operand,Type.T_STRING);
  }

  private void infer(int index, Codes.Fail code, Code.Block.Entry entry,
      Env environment) {
  }

  private void infer(int index, Codes.FieldLoad code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    Type field = code.type().fields().get(code.field);
    coerceAfter(req,field,code.target(),index,entry);
    environment.set(code.operand(0), (Type) code.type());
  }

  private void infer(int index, Codes.IndirectInvoke code, Code.Block.Entry entry,
      Env environment) {

    if(code.type().ret() != Type.T_VOID && code.target() >= 0) {
      Type req = environment.get(code.target());
      coerceAfter(req,code.type().ret(),code.target(),index,entry);
    }

    environment.set(code.reference(),code.type());

    int[] parameters = code.parameters();
    for(int i=0;i!=parameters.length;++i) {
      int operand = parameters[i];
      Type type = code.type().params().get(i);
      environment.set(operand,type);
    }
  }

  private void infer(int index, Codes.Invoke code, Code.Block.Entry entry,
      Env environment) {

    if(code.type().ret() != Type.T_VOID && code.target() >= 0) {
      Type req = environment.get(code.target());
      coerceAfter(req,code.type().ret(),code.target(),index,entry);
    }

    for(int i=0;i!=code.operands().length;++i) {
      int operand = code.operands()[i];
      Type type = code.type().params().get(i);
      environment.set(operand,type);
    }
  }

  private void infer(int index, Codes.Invert code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    // FIXME: add support for dictionaries
    coerceAfter(req,code.type(),code.target(),index,entry);
    environment.set(code.operand(0), code.type());
  }

  private void infer(int index, Codes.ListOperator code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    Type codeType = (Type) code.type();
    coerceAfter(req,codeType,code.target(),index,entry);
    switch(code.kind) {
    case APPEND:
      environment.set(code.operand(0),codeType);
      environment.set(code.operand(1),codeType);
      break;
    case LEFT_APPEND:
      environment.set(code.operand(0),codeType);
      environment.set(code.operand(1),code.type().element());
      break;
    case RIGHT_APPEND:
      environment.set(code.operand(0),code.type().element());
      environment.set(code.operand(1),codeType);
      break;
    }
  }

  private void infer(int index, Codes.Lambda code, Code.Block.Entry entry,
      Env environment) {
    List<Type> params = code.type().params();
    int[] operands = code.operands();
    for (int i = 0; i != operands.length; ++i) {
      int operand = operands[i];
      if (operand != Codes.NULL_REG) {
        environment.set(operand, params.get(i));
      }
    }
  }

  private void infer(int index, Codes.LengthOf code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req,Type.T_INT,code.target(),index,entry);
    environment.set(code.operand(0),(Type) code.type());
  }

  private void infer(int index, Codes.SubList code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    Type codeType = (Type) code.type();
    coerceAfter(req,codeType,code.target(),index,entry);
    environment.set(code.operands()[0],codeType);
    environment.set(code.operands()[1],Type.T_INT);
    environment.set(code.operands()[2],Type.T_INT);
  }

  private void infer(int index, Codes.IndexOf code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req,code.type().value(),code.target(),index,entry);
    environment.set(code.operand(0),(Type) code.type());
    environment.set(code.operand(1),code.type().key());
  }

  private void infer(int index, Codes.Assign code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req,code.type(),code.target(),index,entry);
    environment.set(code.operand(0),code.type());
  }

  private void infer(int index, Codes.Update code, Code.Block.Entry stmt,
      Env environment) {

    Type src = environment.get(code.target());

    if(src == Type.T_VOID) {
      src = code.afterType;
    }

    // The first job is to make sure we've got the right types for indices
    // and key values loaded onto the stack.

    int i = 0;
    for (Codes.LVal lv : code) {
      if (lv instanceof Codes.StringLVal || lv instanceof Codes.ListLVal) {
        environment.set(code.operands()[i++], Type.T_INT);
      } else if (lv instanceof Codes.MapLVal) {
        Codes.MapLVal dlv = (Codes.MapLVal) lv;
        environment.set(code.operands()[i++], dlv.rawType().key());
      } else {
        // RecordLVal and ProcessLVal have no stack requirement
      }
    }

    // The second job is to try and determine whether there is any general
    // requirement on the value being assigned.

    environment.set(code.result(),code.rhs());
    environment.set(code.target(), code.type());
  }

  private void infer(int index, Codes.NewMap code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());

    // TODO: could do better here by rewriting bytecode. For example, if we
    // require a set then changing bytecode to newset makes sense!

    coerceAfter(req,code.type(),code.target(),index,entry);

    Type key = code.type().key();
    Type value = code.type().value();
    for(int i=0;i!=code.operands().length;i=i+2) {
      int keyOperand = code.operands()[i];
      int valueOperand = code.operands()[i+1];
      environment.set(keyOperand,key);
      environment.set(valueOperand,value);
    }
  }

  private void infer(int index, Codes.NewRecord code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req, code.type(),code.target(),index, entry);
    ArrayList<String> keys = new ArrayList<String>(code.type().keys());
    Collections.sort(keys);
    Map<String, Type> fields = code.type().fields();
    for (int i = 0; i != keys.size(); ++i) {
      String key = keys.get(i);
      int operand = code.operands()[i];
      environment.set(operand, fields.get(key));
    }
  }

  private void infer(int index, Codes.NewList code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());

    coerceAfter(req, code.type(), code.target(), index, entry);
    Type value = code.type().element();
    for (int operand : code.operands()) {
      environment.set(operand, value);
    }
  }

  private void infer(int index, Codes.NewSet code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req,code.type(),code.target(),index,entry);
    Type value = code.type().element();
    for (int operand : code.operands()) {
      environment.set(operand, value);
    }
  }

  private void infer(int index, Codes.NewTuple code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req,code.type(),code.target(),index,entry);
    for(int i=0;i!=code.operands().length;++i) {
      int operand = code.operands()[i];
      Type type = code.type().element(i);
      environment.set(operand,type);
    }
  }

  private void infer(int index, Codes.Return code, Code.Block.Entry entry,
      Env environment) {
    if(code.type != Type.T_VOID) {
      environment.set(code.operand,code.type);
    }
  }

  private void infer(int index, Codes.SetOperator code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    Type codeType = (Type) code.type();
    coerceAfter(req,codeType,code.target(),index,entry);
    switch(code.kind) {
    case UNION:
    case INTERSECTION:
    case DIFFERENCE:
      environment.set(code.operand(0),codeType);
      environment.set(code.operand(1),codeType);
      break;
    case LEFT_UNION:
    case LEFT_INTERSECTION:
    case LEFT_DIFFERENCE:
      environment.set(code.operand(0),codeType);
      environment.set(code.operand(1),code.type().element());
      break;
    case RIGHT_UNION:
    case RIGHT_INTERSECTION:
      environment.set(code.operand(0),code.type().element());
      environment.set(code.operand(1),codeType);
      break;
    }
  }

  private void infer(int index, Codes.StringOperator code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req,Type.T_STRING,code.target(),index,entry);
    switch(code.kind) {
    case APPEND:
      environment.set(code.operand(0),Type.T_STRING);
      environment.set(code.operand(1),Type.T_STRING);
      break;
    case LEFT_APPEND:
      environment.set(code.operand(0),Type.T_STRING);
      environment.set(code.operand(1),Type.T_CHAR);
      break;
    case RIGHT_APPEND:
      environment.set(code.operand(0),Type.T_CHAR);
      environment.set(code.operand(1),Type.T_STRING);
      break;
    }
  }

  private void infer(int index, Codes.SubString code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req,Type.T_STRING,code.target(),index,entry);
    environment.set(code.operands()[0],Type.T_STRING);
    environment.set(code.operands()[1],Type.T_INT);
    environment.set(code.operands()[2],Type.T_INT);
  }

  private void infer(int index, Codes.UnaryOperator code, Code.Block.Entry entry,
      Env environment) {
    switch(code.kind) {
      case NEG: {
        Type req = environment.get(code.target());
        coerceAfter(req,code.type(),code.target(),index,entry);
        environment.set(code.operand(0),code.type());
        break;
      }
      case NUMERATOR:
      case DENOMINATOR: {
        Type req = environment.get(code.target());
        coerceAfter(req,Type.T_INT,code.target(),index,entry);
        environment.set(code.operand(0),Type.T_REAL);
      }
    }

  }

  private void infer(int index, Codes.NewObject code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    if(req instanceof Type.Reference) {
      Type.Reference tp = (Type.Reference) req;
      coerceAfter(tp.element(), code.type().element(), code.operand(0), index,
          entry);
      environment.set(code.operand(0),tp.element());
    } else {
      // default
      environment.set(code.operand(0),code.type().element());
    }
  }

  private void infer(int index, Codes.Throw code, Code.Block.Entry entry,
      Env environment) {
    environment.set(code.operand,code.type);
  }

  private void infer(int index, Codes.TupleLoad code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req,code.type().elements().get(code.index),code.target(),index,entry);
    environment.set(code.operand(0),(Type) code.type());
  }

  private void infer(int index, Codes.Dereference code, Code.Block.Entry entry,
      Env environment) {
    Type req = environment.get(code.target());
    coerceAfter(req,code.type().element(),code.target(),index,entry);
    environment.set(code.operand(0),code.type());
  }

  @Override
  protected Env propagate(int index,
      Codes.If code, Entry stmt, Env trueEnv, Env falseEnv) {

    Env environment = join(trueEnv,falseEnv);

    if(code.op == Codes.Comparator.IN) {
      Type.EffectiveCollection src = (Type.EffectiveCollection) code.type;
      environment.set(code.leftOperand,src.element());
      environment.set(code.rightOperand,code.type);
    } else {
      environment.set(code.leftOperand,code.type);
      environment.set(code.rightOperand,code.type);
    }

    return environment;
  }

  @Override
  protected Env propagate(int index,
      Codes.IfIs code, Entry stmt, Env trueEnv, Env falseEnv) {

    Env environment = join(trueEnv,falseEnv);

    environment.set(code.operand,code.type);

    return environment;
  }

  @Override
  protected Env propagate(int index, Codes.Switch code, Entry stmt,
      List<Env> environments, Env defEnv) {

    Env environment = defEnv;

    for (int i = 0; i != code.branches.size(); ++i) {
      environment = join(environment, environments.get(i));
    }

    environment.set(code.operand, code.type);
    return environment;
  }

  @Override
  protected Env propagate(Type handler, Env normalEnv, Env exceptionEnv) {
    // FIXME: if there are any constraints coming from the exceptional
    // environment, then we need to translate them into coercions at the
    // beginning of the catch handler.
    return normalEnv;
  }

  @Override
  protected Env propagate(int start, int end, Codes.Loop loop,
      Entry stmt, Env environment, List<Pair<Type,String>> handlers) {

    environment = new Env(environment);

    Env oldEnv = null;
    Env newEnv = null;

    do {
      // iterate until a fixed point reached
      oldEnv = newEnv != null ? newEnv : environment;
      newEnv = propagate(start+1,end, oldEnv, handlers);
      newEnv = join(environment,newEnv);
    } while (!newEnv.equals(oldEnv));

    if(loop instanceof Codes.ForAll) {
      Codes.ForAll fall = (Codes.ForAll) loop;
      environment.set(fall.sourceOperand, (Type) fall.type);
      // FIXME: a conversion here might be necessary?
      environment.set(fall.indexOperand,Type.T_VOID);
    }

    return environment;
  }

  /**
   * Explicitly coerce a constant to a given type.
   *
   * @param to
   * @param from
   * @param elem
   * @return
   */
  public Constant convert(Type to, Constant from, SyntacticElement elem) {
    if(to.equals(from.type()) || to == Type.T_ANY) {
      return from;
    } else if(to == Type.T_REAL && from instanceof Constant.Integer) {
      Constant.Integer i = (Constant.Integer) from;
      return Constant.V_DECIMAL(new BigDecimal(i.value));
    } else if(to instanceof Type.Set && from instanceof Constant.Set) {
      Type.Set ts = (Type.Set) to;
      Constant.Set cs = (Constant.Set) from;
      Type ts_element = ts.element();
      HashSet<Constant> values = new HashSet<Constant>();
      for(Constant c : cs.values) {
        values.add(convert(ts_element,c,elem));
      }
      return Constant.V_SET(values);
    } else if(to instanceof Type.Map && from instanceof Constant.Map) {
      Type.Map tm = (Type.Map) to;
      Constant.Map cm = (Constant.Map) from;
      Type tm_key = tm.key();
      Type tm_value = tm.value();
      HashMap<Constant,Constant> values = new HashMap<Constant,Constant>();
      for(Map.Entry<Constant,Constant> c : cm.values.entrySet()) {
        values.put(convert(tm_key, c.getKey(), elem),
            convert(tm_value, c.getValue(), elem));
      }
      return Constant.V_MAP(values);
    } else if(to instanceof Type.List && from instanceof Constant.List) {
      Type.List tl = (Type.List) to;
      Constant.List cl = (Constant.List) from;
      Type tl_element = tl.element();
      ArrayList<Constant> values = new ArrayList<Constant>();
      for(Constant c : cl.values) {
        values.add(convert(tl_element,c,elem));
      }
      return Constant.V_LIST(values);
    } else if(to instanceof Type.Record && from instanceof Constant.Record) {
      Type.Record tr = (Type.Record) to;
      Constant.Record cr = (Constant.Record) from;
      HashMap<String, Type> tm_fields = tr.fields();
      HashMap<String, Constant> values = new HashMap<String, Constant>();
      for (Map.Entry<String, Constant> c : cr.values.entrySet()) {
        String field = c.getKey();
        values.put(field,
            convert(tm_fields.get(field), c.getValue(), elem));
      }
      return Constant.V_RECORD(values);
    } else {
      // Observe that this is always safe, although we probably can do
      // better in some cases. The reason that it's safe is simply that an
      // explicit coercion bytecode will always be added if the type of
      // the generated constant does not exactly match the required type.
      return from;
    }
  }

  /**
   * Insert an implicit coercion after a given bytecode.
   *
   * @param to
   *            --- type being coerced into.
   * @param from
   *            --- type being coerced from.
   * @param target
   *            --- target register being coerced.
   * @param index
   *            --- index of bytecode in function/method body.
   * @param elem
   *            --- syntactic element for bytecode.
   */
  private void coerceAfter(Type to, Type from, int target, int index, SyntacticElement elem) {

    if (to.equals(from) || to == Type.T_VOID) {
      afterInserts.remove(index);
    } else if(to == Type.T_STRING) {
      // this indicates a string conversion is required
      Pair<Type.Function, NameID> p = choseToString(from);
      to = p.first().params().get(0);

      Code.Block block = new Code.Block(0);
      if (!from.equals(to)) {
        block.add(Codes.Convert(from, target, target, to),
            elem.attributes());
      }
      block.add(
          Codes.Invoke(p.first(), target, new int[] { target },
              p.second()), elem.attributes());
      afterInserts.put(index, block);
    } else {
      Code.Block block = new Code.Block(0);
      block.add(Codes.Convert(from, target, target, to), elem.attributes());
      afterInserts.put(index,block);
    }

    // this method *should* be structured as follows:

//    if (to.equals(from)) {
//      ///
//    } else if(Type.isExplicitCoerciveSubtype(to,from)) {
//      ...
//    } else if(to == Type.T_STRING) {
//      ...
//    } else {
//      ...
//    }
  }

  /**
   * Choose the best toString method based on the given type.
   *
   * @param key
   * @return
   */
  private static Pair<Type.Function,NameID> choseToString(Type type) {
    Type.Function ft;
    NameID name;

    if (type == Type.T_BYTE) {
      ft = (Type.Function) Type.Function(Type.T_STRING, Type.T_VOID,
          Type.T_BYTE);
      name = new NameID(Trie.fromString("whiley/lang/Byte"),
          "toString");
    } else if (type == Type.T_CHAR) {
      ft = (Type.Function) Type.Function(Type.T_STRING, Type.T_VOID,
          Type.T_CHAR);
      name = new NameID(Trie.fromString("whiley/lang/Char"),
          "toString");
    } else {
      ft = (Type.Function) Type.Function(Type.T_STRING, Type.T_VOID,
          Type.T_ANY);
      name = new NameID(Trie.fromString("whiley/lang/Any"),
          "toString");
    }

    return new Pair<Type.Function,NameID>(ft,name);
  }

  private Env join(Env env1, Env env2) {
    if (env2 == null) {
      return env1;
    } else if (env1 == null) {
      return env2;
    }

    Env env = new Env();
    for (int i = 0; i != Math.min(env1.size(), env2.size()); ++i) {
      env.add(Type.Union(env1.get(i), env2.get(i)));
    }

    /**
     * <p>
     * The following may seem strange, but it's necessary to support
     * constraint failures which can happen in the middle of expressions. In
     * such case, a conditional is inserted by the "constraint inline"
     * phase, where it fails on one side but succeeds on the other. Stack
     * requirements may be present from the succeeding branch and we need to
     * propagate those backwards still.
     * </p>
     * <p>
     * There are possibly other ways this could be handled. For example, we
     * might use null to signal that a particular branch is heading
     * immediately into a fail statement.
     * </p>
     */
    if(env1.size() > env2.size()) {
      for(int i=env.size();i!=env1.size();++i) {
        env.add(env1.get(i));
      }
    } else if(env2.size() > env1.size()) {
      for(int i=env.size();i!=env2.size();++i) {
        env.add(env2.get(i));
      }
    }

    return env;
  }

  protected static class Env extends ArrayList<Type> {
    public Env() {
    }
    public Env(Collection<Type> v) {
      super(v);
    }
    public Env clone() {
      return new Env(this);
    }
  }
}
TOP

Related Classes of wyil.transforms.BackPropagation$Env

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.