package wyil.util;
import java.util.List;
import wybs.lang.Build;
import wyil.lang.Bytecode;
import wyil.lang.SyntaxTree;
import wyil.lang.SyntaxTree.Location;
import wyil.lang.WyilFile;
/**
* <p>
* Responsible for determining when a value of a dynamically sized data type can
* be "moved" or must be "copied". Moving is preferable preferable (when
* permitted) because the original reference can be used without copying the
* underlying data. The following provides a useful example:
* </p>
*
* <pre>
* function g(int[] xs, int i) -> (int[] ys):
* xs[j] = f(xs)
* return xs
* </pre>
*
* <p>
* Here, in the invocation <code>f(xs)</code> the array <code>xs</code> cannot
* be moved since it is live afterwards. Instead, we must clone <code>xs</code>
* at this point. However, the subsequent use of <code>xs</code> in the
* <code>return</code> statement does not require a clone and can be moved
* (since <code>xs</code> is no longer live).
* </p>
*
* <p>
* The following illustrates another situation where temporary moves or
* "borrows" are permitted:
* </p>
*
* <pre>
* function get(int[] xs, int i, int j) -> (int r):
* int sum = xs[i]
* sum = sum + xs[j]
* return sum
* </pre>
*
* Clearly, there is not need to clone <code>xs</code> when it is used in the
* initialiser for <code>sum</code>. This is because the use is temporary and
* does not modify <code>xs</code>. We say that <code>xs</code> is not
* <em>consumed</em>.
*
* @author David J. Pearce
*
*/
public class MoveAnalysis implements Build.Stage<WyilFile> {
public MoveAnalysis(Build.Task builder) {
}
@Override
public void apply(WyilFile module) {
for(WyilFile.Type type : module.types()) {
check(type);
}
for(WyilFile.FunctionOrMethod method : module.functionOrMethods()) {
check(method);
}
}
private void check(WyilFile.Type t) {
for(Location<Bytecode.Expr> e : t.getInvariant()) {
check(false,e);
}
}
private void check(WyilFile.FunctionOrMethod fm) {
SyntaxTree tree = fm.getTree();
// Examine all entries in this block looking for a conversion bytecode
List<SyntaxTree.Location<?>> expressions = tree.getLocations();
for (int i = 0; i != expressions.size(); ++i) {
SyntaxTree.Location<?> l = expressions.get(i);
if (l.getBytecode() instanceof Bytecode.Stmt) {
check((Location<Bytecode.Stmt>) l);
}
}
}
private void check(Location<Bytecode.Stmt> stmt) {
switch(stmt.getOpcode()) {
case Bytecode.OPCODE_assert:
case Bytecode.OPCODE_assume: {
check(false,(Location<Bytecode.Expr>) stmt.getOperand(0));
break;
}
case Bytecode.OPCODE_vardeclinit: {
check(true, (Location<Bytecode.Expr>) stmt.getOperand(0));
break;
}
case Bytecode.OPCODE_return: {
for(int i=0;i!=stmt.numberOfOperands();++i) {
check(false,(Location<Bytecode.Expr>) stmt.getOperand(i));
}
break;
}
case Bytecode.OPCODE_if:
case Bytecode.OPCODE_ifelse:
case Bytecode.OPCODE_switch:
case Bytecode.OPCODE_while:
case Bytecode.OPCODE_dowhile: {
check(false,(Location<Bytecode.Expr>) stmt.getOperand(0));
break;
}
case Bytecode.OPCODE_debug: {
check(false,(Location<Bytecode.Expr>) stmt.getOperand(0));
break;
}
case Bytecode.OPCODE_invoke:
case Bytecode.OPCODE_indirectinvoke: {
check(false,(Location) stmt);
break;
}
case Bytecode.OPCODE_assign: {
Location<?>[] lhs = stmt.getOperandGroup(SyntaxTree.LEFTHANDSIDE);
Location<?>[] rhs = stmt.getOperandGroup(SyntaxTree.RIGHTHANDSIDE);
for(int i=0;i!=lhs.length;++i) {
check(false,(Location<Bytecode.Expr>) lhs[i]);
}
for(int i=0;i!=rhs.length;++i) {
check(true,(Location<Bytecode.Expr>) rhs[i]);
}
break;
}
case Bytecode.OPCODE_aliasdecl:
case Bytecode.OPCODE_vardecl:
case Bytecode.OPCODE_fail:
case Bytecode.OPCODE_block:
case Bytecode.OPCODE_namedblock:
case Bytecode.OPCODE_break:
case Bytecode.OPCODE_continue:
case Bytecode.OPCODE_skip:
// do nothing here
break;
default:
throw new IllegalArgumentException(
"Unknown bytecode encountered: " + stmt.getBytecode().getClass().getName());
}
}
private void check(boolean consumed, Location<Bytecode.Expr> expr) {
switch(expr.getOpcode()) {
case Bytecode.OPCODE_lambda:
case Bytecode.OPCODE_is:
check(false,(Location<Bytecode.Expr>) expr.getOperand(0));
break;
case Bytecode.OPCODE_all:
case Bytecode.OPCODE_some: {
for(int i=0;i!=expr.numberOfOperands();++i) {
check(false,(Location<Bytecode.Expr>) expr.getOperand(i));
}
for (int i = 0; i != expr.numberOfOperandGroups(); ++i) {
Location<?>[] range = expr.getOperandGroup(i);
check(false,(Location<Bytecode.Expr>) range[SyntaxTree.START]);
check(false,(Location<Bytecode.Expr>) range[SyntaxTree.END]);
}
break;
}
case Bytecode.OPCODE_convert: {
check(consumed,(Location<Bytecode.Expr>) expr.getOperand(0));
break;
}
case Bytecode.OPCODE_fieldload:
case Bytecode.OPCODE_neg:
case Bytecode.OPCODE_arraylength:
case Bytecode.OPCODE_dereference:
case Bytecode.OPCODE_add:
case Bytecode.OPCODE_sub:
case Bytecode.OPCODE_mul:
case Bytecode.OPCODE_div:
case Bytecode.OPCODE_rem:
case Bytecode.OPCODE_eq:
case Bytecode.OPCODE_ne:
case Bytecode.OPCODE_lt:
case Bytecode.OPCODE_le:
case Bytecode.OPCODE_gt:
case Bytecode.OPCODE_ge:
case Bytecode.OPCODE_logicalnot:
case Bytecode.OPCODE_logicaland:
case Bytecode.OPCODE_logicalor:
case Bytecode.OPCODE_bitwiseinvert:
case Bytecode.OPCODE_bitwiseor:
case Bytecode.OPCODE_bitwisexor:
case Bytecode.OPCODE_bitwiseand:
case Bytecode.OPCODE_shl:
case Bytecode.OPCODE_shr:
case Bytecode.OPCODE_arrayindex:
case Bytecode.OPCODE_arraygen: {
for(int i=0;i!=expr.numberOfOperands();++i) {
check(false,(Location<Bytecode.Expr>) expr.getOperand(i));
}
break;
}
case Bytecode.OPCODE_array:
case Bytecode.OPCODE_record: {
for(int i=0;i!=expr.numberOfOperands();++i) {
check(consumed,(Location<Bytecode.Expr>) expr.getOperand(i));
}
break;
}
case Bytecode.OPCODE_invoke:
case Bytecode.OPCODE_indirectinvoke:
case Bytecode.OPCODE_newobject: {
for (int i = 0; i != expr.numberOfOperands(); ++i) {
check(true, (Location<Bytecode.Expr>) expr.getOperand(i));
}
break;
}
case Bytecode.OPCODE_const:
break;
case Bytecode.OPCODE_varmove:
case Bytecode.OPCODE_varcopy:
if (!consumed) {
// In this case, we have identified a variable access which is
// not consumed and therefore can be implemented as a move.
Bytecode.VariableAccess bytecode = new Bytecode.VariableAccess(false, expr.getOperand(0).getIndex());
SyntaxTree.Location<?> move = new SyntaxTree.Location<>(expr.getEnclosingTree(), expr.getType(), bytecode,
expr.attributes());
expr.getEnclosingTree().getLocations().set(expr.getIndex(), move);
}
break;
default:
throw new IllegalArgumentException("Unknown bytecode encountered: " + expr.getBytecode().getClass().getName());
}
}
}