/******************************************************************************
* Copyright (c) 2009 - 2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*****************************************************************************/
/**
*
*/
package com.ibm.wala.memsat.translation;
import static com.ibm.wala.memsat.util.Strings.repeat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.cast.ir.ssa.AstAssertInstruction;
import com.ibm.wala.cast.ir.ssa.AstEchoInstruction;
import com.ibm.wala.cast.ir.ssa.AstGlobalRead;
import com.ibm.wala.cast.ir.ssa.AstGlobalWrite;
import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access;
import com.ibm.wala.cast.ir.ssa.AstLexicalRead;
import com.ibm.wala.cast.ir.ssa.AstLexicalWrite;
import com.ibm.wala.cast.ir.ssa.CAstBinaryOp;
import com.ibm.wala.cast.java.ipa.callgraph.AstJavaSSAPropagationCallGraphBuilder.EnclosingObjectReferenceKey;
import com.ibm.wala.cast.java.ssa.EnclosingObjectReference;
import com.ibm.wala.cast.js.ipa.summaries.JavaScriptSummarizedFunction;
import com.ibm.wala.cast.loader.AstMethod;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.memsat.frontEnd.FieldSSATable;
import com.ibm.wala.memsat.frontEnd.IRType;
import com.ibm.wala.memsat.frontEnd.WalaCGNodeInformation;
import com.ibm.wala.memsat.frontEnd.WalaInformation;
import com.ibm.wala.memsat.representation.ExpressionFactory;
import com.ibm.wala.memsat.representation.FieldExpression;
import com.ibm.wala.memsat.representation.HeapExpression;
import com.ibm.wala.memsat.representation.PhiExpression;
import com.ibm.wala.memsat.representation.RealExpression;
import com.ibm.wala.memsat.translation.Environment.Frame;
import com.ibm.wala.memsat.util.Nodes;
import com.ibm.wala.shrikeBT.BinaryOpInstruction;
import com.ibm.wala.shrikeBT.ShiftInstruction;
import com.ibm.wala.shrikeBT.UnaryOpInstruction;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSABinaryOpInstruction;
import com.ibm.wala.ssa.SSACFG;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
import com.ibm.wala.ssa.SSAConversionInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAGotoInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAMonitorInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSASwitchInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.ssa.SSAUnaryOpInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import kodkod.ast.Expression;
import kodkod.ast.Formula;
import kodkod.ast.IntExpression;
import kodkod.util.ints.IndexedEntry;
/**
* Provides access to a general translator that can be applied to any method.
* In the absence of a specialized translator for a given method, an instance
* of this class computes the method's translation by
* composing the translations of the individual instructions that comprise
* the method's loop-unrolled body. If a specialized translator is available,
* however, it will be used instead.
*
* @author Emina Torlak
*/
@SuppressWarnings("unused")
public final class Translator implements MethodTranslator {
private final SpecialTranslatorFactory specialTransls;
/**
* Constructs a new generic method translator.
*/
Translator(SpecialTranslatorFactory specialTranslFactory) {
this.specialTransls =specialTranslFactory;
}
/**
* Returns the translation of the top level method given by env.top.callInfo().
* @requires no env.top.call
* @return translation of the top level method given by env.top.callInfo().
*/
public static MethodTranslation translate(Environment env, MemoryInstructionHandler memoryHandler) {
assert env.top().call()==null;
final Translator transl = new Translator(SpecialTranslatorFactory.factory(env.factory().options(), env.factory().info()));
return transl.translate(Formula.TRUE, env, memoryHandler);
}
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.translation.MethodTranslator#translate(kodkod.ast.Formula, com.ibm.wala.memsat.translation.Environment, com.ibm.wala.memsat.translation.MemoryInstructionHandler)
*/
public MethodTranslation translate(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
final MethodReference method = env.top().callInfo().cgNode().getMethod().getReference();
final MethodTranslator special = specialTransls.translatorFor(method);
// System.err.println("method: " + method);
// System.err.println("special: " + special);
if (special==null) {
return new TranslationVisitor(entryGuard,env,memoryHandler).execute();
} else
return special.translate(entryGuard, env, memoryHandler);
}
/*------------------ GENERIC (INSTRUCTION-BY-INSTRUCTION) TRANSLATION VISITOR ------------------ */
/** Instruction-by-instruction translation visitor. */
private class TranslationVisitor extends AbstractInstructionVisitor<MethodTranslation> {
private int instIdx = -1;
private final Set<Formula> assertions, assumes;
private final Set<TranslationWarning> warnings;
private final Set<MethodReference> memoryInstructions;
private final Environment env;
private final MemoryInstructionHandler memoryHandler;
private final GuardHandler guardHandler;
private final PhiExpression<Expression> exceptionPhi;
private final PhiExpression<Object> returnPhi;
private final Map<SSAGetCaughtExceptionInstruction, PhiExpression<Expression>> catcherPhis;
private final ExpressionFactory factory;
private final WalaCGNodeInformation callInfo;
private final CGNode node;
private final FieldSSATable fieldSSA;
/** Constructs a fresh translation visitor for the top environment frame,
* with respect to the given entry guard and memory handler. */
TranslationVisitor(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
this.env = env;
this.assertions = new LinkedHashSet<Formula>();
this.assumes = new LinkedHashSet<Formula>();
this.warnings = new LinkedHashSet<TranslationWarning>();
this.memoryHandler = memoryHandler;
this.guardHandler = new GuardHandler(entryGuard, env, warnings);
this.factory = env.factory();
this.callInfo = env.top().callInfo();
this.node = callInfo.cgNode();
this.fieldSSA = callInfo.fieldSSA();
if (factory.options().memoryModel()!=null) {
this.memoryInstructions = factory.options().memoryModel().memoryInstructions();
} else {
this.memoryInstructions = Collections.emptySet();
}
this.exceptionPhi = factory.valuePhi(IRType.OBJECT);
final IRType returnType = IRType.convert(node.getMethod().getReturnType());
this.returnPhi = (returnType==null) ? null : factory.valuePhi(returnType);
this.catcherPhis = new LinkedHashMap<SSAGetCaughtExceptionInstruction, PhiExpression<Expression>>();
}
/**
* Translates the method in the topmost frame of this.env, pops the frame
* off and returns it, along with return, exception, etc., values wrapped
* in a Result object.
* @effects adds translations of callInfo.allInstructions() to this.env.top, and pops it off
* @return Result object storing the translation of the method in the topmost
* frame of this.env, as computed by this translation visitor
**/
@SuppressWarnings("unchecked")
protected final MethodTranslation execute() {
final int spaces = env.callStack().size();
final String indent = repeat(" ", spaces);
/*
System.out.println();
System.out.println(indent+"----------all instructions for "+callInfo.cgNode()+"------------");
System.out.println(prettyPrint(env.top().callInfo().cgNode().getIR(), spaces*2));
System.out.println(indent+"----------end all instructions for "+callInfo.cgNode()+"------------");
System.out.println(indent+"----------relevant instructions for "+callInfo.cgNode()+"------------");
for(Iterator<? extends IndexedEntry<SSAInstruction>> itr = callInfo.relevantInstructions(); itr.hasNext(); ) {
IndexedEntry<SSAInstruction> inst = itr.next();
System.out.println(indent+inst.index() + ":: "+inst.value());
}
System.out.println(indent+"----------end relevant instructions for "+callInfo.cgNode()+"------------");
*/
for(Iterator<? extends IndexedEntry<SSAInstruction>> itr = callInfo.relevantInstructions(); itr.hasNext(); ) {
IndexedEntry<SSAInstruction> inst = itr.next();
System.out.println(indent+"TRANSLATING "+inst.index() + ":: "+inst.value());
this.instIdx = inst.index();
inst.value().visit(this);
}
// System.out.println(indent+"------------------------------------\n");
// System.out.println("----------def use------------");
// DefUse du = env.top().callInfo().cgNode().getDU();
// for(int i = 1; i <= env.top().callInfo().cgNode().getIR().getSymbolTable().getMaxValueNumber(); i++) {
// System.out.println(i + " = " + env.localUse(i) + "; definer: " + du.getDef(i));
// }
// System.out.println("-----------------------------");
// System.out.println();
return new MethodTranslation() {
final Formula exitGuard = guardHandler.normalExitGuard();
// if returnValue phi is empty, then the return instruction(s) have been sliced out
// and therefore irrelevant, so we set the return value to null
final Object returnValue = returnPhi==null || returnPhi.size()==0 ? null : returnPhi.value();
final Expression exceptionValue = exceptionPhi.size()==0 ? null : exceptionPhi.value();
final Frame frame = env.pop();
public Set<Formula> assertions() { return assertions; }
public Set<Formula> assumptions() { return assumes; }
public Expression exceptionValue() { return exceptionValue; }
public Frame frame() { return frame; }
public Formula normalExitGuard() { return exitGuard; }
public <T> T returnValue() { return (T) returnValue; }
public Set<TranslationWarning> warnings() { return warnings; }
};
}
/*------------------ ASSERTION INSTRUCTION ------------------ */
/** @effects this.assertions'.add( this.entryGuard(blockFor(inst)) and ![[inst.getUse(0)]] ) */
public void visitAssert(AstAssertInstruction inst) {
final Formula assertion = env.boolUse(inst.getUse(0));
if (inst.isFromSpecification()) {
if (env.factory().options().assertsAreAssumptions())
assertions.add(Nodes.simplify(guardHandler.absoluteEntryGuard(inst).implies(assertion)));
else
assertions.add(Nodes.simplify(guardHandler.absoluteEntryGuard(inst).and(assertion.not())));
} else {
assumes.add(Nodes.simplify(guardHandler.absoluteEntryGuard(inst).implies(assertion)));
}
}
/*------------------ RETURN / THROW / CATCH INSTRUCTIONS ------------------ */
/** @effects updates this.top.returnVal with [[inst]] */
public final void visitReturn(SSAReturnInstruction inst) {
if (returnPhi != null) {
final Formula guard = guardHandler.relativeEntryGuard(inst);
returnPhi.add(guard, env.localUse(inst.getUse(0)));
}
}
/** @effects updates this.top.exceptionVal with [[inst]] */
public final void visitThrow(SSAThrowInstruction inst) {
visitThrower(inst, env.refUse(inst.getUse(0))); }
/** @effects updates exceptionPhi with [[exception]] if the block
* holding the given instruction is an exit block; otherwise, updates the
* defs of relevant SSAGetCaughtExceptionInstruction(s) */
private final void visitThrower(SSAInstruction inst, Expression exception) {
final SSACFG.BasicBlock block = guardHandler.blockFor(inst);
final Formula guard = guardHandler.relativeEntryGuard(block);
for(IBasicBlock<?> SB : node.getIR().getControlFlowGraph().getExceptionalSuccessors(block)) {
if (SB.isExitBlock()) {
exceptionPhi.add(guard, exception);
} else {
SSAGetCaughtExceptionInstruction catcher = (SSAGetCaughtExceptionInstruction)((SSACFG.ExceptionHandlerBasicBlock)SB).getCatchInstruction();
PhiExpression<Expression> catcherPhi = catcherPhis.get(catcher);
if (catcherPhi==null) {
catcherPhi = factory.valuePhi(IRType.OBJECT);
catcherPhis.put(catcher, catcherPhi);
}
catcherPhi.add(guard, exception);
}
}
}
/** @effects this.env.localDef(inst.getDef(), catcherPhis.get(inst).value()) */
public final void visitGetCaughtException(SSAGetCaughtExceptionInstruction inst) {
// we can remove the phi for the given catcher; it should no longer be needed
final PhiExpression<Expression> catcherPhi = catcherPhis.remove(inst);
assert catcherPhi != null;
env.localDef(inst.getDef(), catcherPhi.value());
}
/*------------------ INVOKE INSTRUCTIONS ------------------ */
/** @effects sets the heap values defined by the given instruction to
* their corresponding uses; in other, the instruction is not symbolically executed */
private final void notCalled(SSAAbstractInvokeInstruction inst) {
if (inst.hasDef()) {
int def = inst.getDef();
env.localDef(def, factory.constants().defaultValue(env.top().callInfo().typeOf(def)));
}
final int[] hUses = fieldSSA.getUses(inst);
for(int hDef : fieldSSA.getDefs(inst)) {
PointerKey field = fieldSSA.getField(hDef);
for(int hUse : hUses) {
if (field.equals(fieldSSA.getField(hUse))) {
env.heapDef(hDef, env.heapUse(hUse));
break;
}
}
assert env.heapUse(hDef)!=null;
}
// TODO: should the guard here be true or false?
guardHandler.addCallExitGuard(inst, Formula.TRUE);
}
/**
* Updates the given return, exception and heap phis
* with their type-guarded translations, if the given invocation can be handled.
* Otherwise does nothing.
* @effects updates this.assertions, this.env, and this.warnings, if needed
* @effects updates the given return, exception and heap phis
* with their type-guarded translations, if the given invocation can be handled.
* @return true if the invocation was handled, otherwise false.
**/
private boolean visitInvocation(SSAAbstractInvokeInstruction call, CGNode target,
PhiExpression<Object> callReturn, PhiExpression<Expression> callException,
PhiExpression<Formula> guardPhi, Map<PointerKey, PhiExpression<HeapExpression<Object>>> callHeap) {
final IMethod method = target.getMethod();
final WalaInformation info = factory.info();
if (!specialTransls.hasTranslatorFor(method.getReference()) &&
!(method instanceof AstMethod) &&
!(method instanceof JavaScriptSummarizedFunction)) {
warn(call, "cannot call unexpected method " + target);
return false;
}
final Formula typeGuard;
if (call.isDispatch()) {
final List<Formula> typeGuards = new ArrayList<Formula>();
for(InstanceKey key : info.pointsTo(callInfo.pointerKeyFor(call.getUse(0)))) {
if (info.analysisOptions().getMethodTargetSelector().
getCalleeTarget(target, call.getCallSite(), key.getConcreteType())
== target.getMethod()) {
Expression type = factory.constants().valueOf(key);
if (type != null) {
typeGuards.add(env.refUse(call.getUse(0)).in(type));
}
}
}
if (typeGuards.isEmpty()) {
warn(call, "cannot handle call to method " + target);
return false;
}
typeGuard = Formula.or(typeGuards);
} else {
typeGuard = Formula.TRUE;
}
final MethodTranslation result = translate(guardHandler.absoluteEntryGuard(call).and(typeGuard),
env.push(call, info.cgNodeInformation(target)), memoryHandler);
// get all assertions and warnings
assertions.addAll(result.assertions());
warnings.addAll(result.warnings());
// update normal exit guard for the inst
guardPhi.add(typeGuard, result.normalExitGuard());
// handle return value, if any
if (result.returnValue()!=null) {
callReturn.add(typeGuard, result.returnValue());
}
// handle exception value, if any
if (result.exceptionValue()!=null) {
callException.add(typeGuard, result.exceptionValue());
}
// handle heap updates, if any
final Frame frame = result.frame();
for(int hDef : fieldSSA.getDefs(call)) {
PointerKey field = fieldSSA.getField(hDef);
int v = frame.callInfo().fieldSSA().getExitValue(field);
HeapExpression<Object> fieldValue = null;
if (v != -1)
fieldValue = frame.heapUse(v);
else
fieldValue = env.heapUse(fieldSSA.getUse(call, fieldSSA.getUse(call, field))) ;
//HeapExpression<Object> fieldValue = frame.heapUse(frame.callInfo().fieldSSA().getExitValue(field));
PhiExpression<HeapExpression<Object>> fieldPhi = callHeap.get(field);
if (fieldPhi==null) {
fieldPhi = factory.heapPhi(typeGuard, fieldValue);
callHeap.put(field, fieldPhi);
} else {
fieldPhi.add(typeGuard, fieldValue);
}
}
return true;
}
/** @effects translates the code for the given call instruction and sets inst.getDef(),
* if any, to the returned value */
public final void visitAbstractInvoke(SSAAbstractInvokeInstruction inst) {
final WalaInformation info = factory.info();
final Set<CGNode> targets = info.callGraph().getPossibleTargets(env.top().callInfo().cgNode(), inst.getCallSite());
// a memory instruction (no-op)
if (memoryInstructions.contains(inst.getDeclaredTarget()) ||
(targets.size()==1 && memoryInstructions.contains(targets.iterator().next().getMethod().getReference()))) {
memoryHandler.handleSpecialInvoke(instIdx, inst, guardHandler.absoluteEntryGuard(inst), env);
return;
}
// unreached call site
if (targets.isEmpty()) {
warn(inst, "no targets");
notCalled(inst);
return;
}
// non-static call to unknown type
if (!inst.getCallSite().isStatic() &&
info.pointsTo(callInfo.pointerKeyFor(inst.getUse(0))).isEmpty()) {
warn(inst, "non-static call to unknown type");
notCalled(inst);
return;
}
assert inst.isDispatch() || targets.size()==1;
final int limit = factory.options().recursionLimit();
boolean called = false;
final PhiExpression<Expression> callException = factory.valuePhi(IRType.OBJECT);
final IRType retType = IRType.convert(inst.getDeclaredTarget().getReturnType());
final PhiExpression<Object> callReturn = retType==null ? null : factory.valuePhi(retType);
final PhiExpression<Formula> callGuard = factory.valuePhi(IRType.BOOLEAN);
final Map<PointerKey,PhiExpression<HeapExpression<Object>>> callHeap =
new LinkedHashMap<PointerKey, PhiExpression<HeapExpression<Object>>>();
for(CGNode target : targets) {
if (env.recursionCount(target) < limit) {
called |= visitInvocation(inst, target, callReturn, callException, callGuard, callHeap);
}
}
if (!called) {
notCalled(inst);
// System.out.println("NOT CALLED " + inst + " FROM " + callInfo.cgNode());
} else {
if (callGuard.size()>0) {
guardHandler.addCallExitGuard(inst, callGuard.value());
}
if (inst.hasDef() && callReturn.size()>0) {
// if callReturn is empty, then all of the return instructions for each
// of the targets have been sliced out, and therefore irrelevant.
env.localDef(inst.getDef(), callReturn.value());
}
if (callException.size() > 0) {
env.localDef(inst.getException(), callException.value());
visitThrower(inst, callException.value());
}
for(int hDef : fieldSSA.getDefs(inst)) {
env.heapDef(hDef, callHeap.get(fieldSSA.getField(hDef)).value());
}
}
}
/*------------------ JOIN INSTRUCTIONS ------------------ */
/** @effects sets the heap or local def for the given phi instruction in this.env */
public final void visitPhi(SSAPhiInstruction inst) {
final Formula[] useGuards = guardHandler.phiUseGuards(inst);
if (fieldSSA.isHeapPhi(inst)) {
final PhiExpression<HeapExpression<Object>> phi =
factory.heapPhi(useGuards[0], env.heapUse(inst.getUse(0)));
for(int i = 1; i < useGuards.length; i++) {
phi.add(useGuards[i], env.heapUse(inst.getUse(i)));
}
assert phi.size() > 0;
env.heapDef(inst.getDef(), phi.value());
} else {
final PhiExpression<Object> phi = factory.valuePhi(callInfo.typeOf(inst.getDef()));
for(int i = 0; i < useGuards.length; i++) {
phi.add(useGuards[i], env.localUse(inst.getUse(i)));
}
assert phi.size() > 0;
env.localDef(inst.getDef(), phi.value());
}
}
/** @effects env.localDef(inst.getDef(), [[ inst.getUse(0) ]] ) */
public final void visitPi(SSAPiInstruction inst) {
env.localDef(inst.getDef(), env.localUse(inst.getUse(0)));
}
/*------------------ MONITOR INSTRUCTION ------------------ */
/** @effects calls memoryHandler.handleMonitor(inst, env) */
public final void visitMonitor(SSAMonitorInstruction inst) {
memoryHandler.handleMonitor(instIdx, inst, guardHandler.absoluteEntryGuard(inst), env);
}
/*------------------ HEAP & LEXICAL ACCESS INSTRUCTIONS ------------------ */
/** @effects calls this.memoryHandler.handleGet(inst, env) */
public final void visitGet(SSAGetInstruction inst) {
memoryHandler.handleGet(instIdx, inst, guardHandler.absoluteEntryGuard(inst), env);
}
/** @effects calls this.memoryHandler.handlePut(inst, env) */
public final void visitPut(SSAPutInstruction inst) {
memoryHandler.handlePut(instIdx, inst, guardHandler.absoluteEntryGuard(inst), env);
}
/** @effects calls this.memoryHandler.handleArrayLoad(inst, env) */
public final void visitArrayLoad(SSAArrayLoadInstruction inst) {
memoryHandler.handleArrayLoad(instIdx, inst, guardHandler.absoluteEntryGuard(inst), env);
}
/** @effects calls this.memoryHandler.handleArrayLoad(inst, env) */
public final void visitArrayStore(SSAArrayStoreInstruction inst) {
memoryHandler.handleArrayStore(instIdx, inst, guardHandler.absoluteEntryGuard(inst), env);
}
/** @effects this.env.localDef(inst.getDef(), [[ inst ]] )*/
public final void visitArrayLength(SSAArrayLengthInstruction inst) {
//System.err.println(this.node.getIR().toString());
final PhiExpression<IntExpression> phi = factory.valuePhi(IRType.INTEGER);
final Expression arrayObj = env.refUse(inst.getUse(0));
for(int use : fieldSSA.getUses(inst)) {
FieldExpression<IntExpression> fieldPiece = env.fieldUse(use);
phi.add(arrayObj.in(fieldPiece.instances()), fieldPiece.read(arrayObj));
}
assert phi.size() > 0;
env.localDef(inst.getDef(), phi.value());
}
/** @effects visitGet(inst) */
public final void visitAstGlobalRead(AstGlobalRead inst) { visitGet(inst); }
/** @effects visitPut(inst) */
public final void visitAstGlobalWrite(AstGlobalWrite inst) { visitPut(inst); }
/** @effects all d: inst.getAccesses | this.env.localDef(d.valueNumber, this.env.lexUse(d) )*/
public final void visitAstLexicalRead(AstLexicalRead inst) {
int i = 0;
int[] uses = env.top().callInfo().fieldSSA().getUses(inst);
for(Access access : inst.getAccesses()) {
env.localDef(access.valueNumber, env.fieldUse(uses[i++]).read(null));
}
}
/*------------------ OBJECT / CLASS / TYPE INSTRUCTIONS ------------------ */
/** @effects updates local environment, and heap environment if needed */
public final void visitNew(SSANewInstruction inst) {
final Set<InstanceKey> defKeys = factory.info().pointsTo(callInfo.pointerKeyFor(inst.getDef()));
assert defKeys.size()==1;
final InstanceKey defKey = defKeys.iterator().next();
final Expression obj = env.instantiate(defKey);
env.localDef(inst.getDef(), obj);
final TypeReference type = inst.getConcreteType();
if (type.isArrayType()) { // update length field
final FieldExpression<IntExpression> arrLen = env.fieldUse(fieldSSA.getUse(inst, 0));
final IntExpression len = env.intUse(inst.getUse(0));
env.heapDef(fieldSSA.getDef(inst, 0), arrLen.write(obj, len));
} else if (!node.getMethod().isStatic()) { // update any enclosing reference fields
final IClass declaringClass = node.getMethod().getDeclaringClass();
final IClassHierarchy cha = factory.info().callGraph().getClassHierarchy();
for(int hUse : fieldSSA.getUses(inst)) {
FieldExpression<Expression> fieldValue = env.fieldUse(hUse);
EnclosingObjectReferenceKey field = (EnclosingObjectReferenceKey)fieldSSA.getField(hUse);
int hDef = fieldSSA.getDef(inst, fieldSSA.getDef(inst, field));
IClass enclosingClass = factory.info().pointsTo(field).iterator().next().getConcreteType();
if (cha.isSubclassOf(declaringClass, enclosingClass)) {
env.heapDef(hDef, fieldValue.write(obj, env.refUse(1)));
} else {
env.heapDef(hDef, fieldValue.write(obj, fieldValue.read(env.refUse(1))));
}
}
}
}
/**
* @effects this.env.localDef(inst.getDef(), [[inst.getUse(0)]] in [[inst.getCheckedType()]])
*/
public final void visitInstanceof(SSAInstanceofInstruction inst) {
env.localDef(inst.getDef(), env.refUse(inst.getUse(0)).in(factory.constants().valueOf(inst.getCheckedType())));
}
/** @effects this.env.localDef(inst.getDef(), [[ inst.getUse(0) ]] )*/
public final void visitCheckCast(SSACheckCastInstruction inst) {
env.localDef(inst.getDef(), env.refUse(inst.getUse(0)));
}
/**
* @effects this.env.localDef(inst.getDef(), [[inst.getUse(0)]] . [[inst.getEnclosingType()]] )
**/
public final void visitEnclosingObjectReference(EnclosingObjectReference inst) {
final PhiExpression<Expression> phi = factory.valuePhi(IRType.OBJECT);
final Expression obj = env.refUse(inst.getUse(0));
for(int use : fieldSSA.getUses(inst)) {
FieldExpression<Expression> fieldPiece = env.fieldUse(use);
phi.add(obj.in(fieldPiece.instances()), fieldPiece.read(obj));
}
assert phi.size() > 0;
env.localDef(inst.getDef(), phi.value());
}
/*------------------ COMPARISON / ARITHMETIC / CONVERSION INSTRUCTIONS ------------------ */
/**
* @effects this.env.localDef(inst.getDef(), [[inst.op]] [[inst.getUse(0)]] )
**/
public final void visitUnaryOp(SSAUnaryOpInstruction inst) {
if (inst.getOpcode() == UnaryOpInstruction.Operator.NEG) {
final int use = inst.getUse(0);
switch(env.top().callInfo().typeOf(use)) {
case BOOLEAN : env.localDef(inst.getDef(), env.boolUse(use).not()); break;
case INTEGER : env.localDef(inst.getDef(), env.intUse(use).negate()); break;
case REAL : env.localDef(inst.getDef(), env.realUse(use).negate()); break;
default : throw new AssertionError("unreachable");
}
} else {
warnOp(inst);
}
}
/**
* @requires inst.getOperator() instanceof ShiftInstruction.Operator
* @effects this.env.localDef(inst.getDef(), [[inst.getUse(0)]] [[inst.op]] [[inst.getUse(1)]] )
**/
private void visitBinaryShiftOp(SSABinaryOpInstruction inst) {
final int def = inst.getDef(), use0 = inst.getUse(0), use1 = inst.getUse(1);
final IRType type = callInfo.typeOf(def);
final ShiftInstruction.Operator op = (ShiftInstruction.Operator)inst.getOperator();
switch(type) {
case INTEGER :
final IntExpression i0 = env.intUse(use0), i1 = env.intUse(use1);
switch(op) {
case SHL : env.localDef(def, i0.shl(i1)); break;
case SHR : env.localDef(def, i0.sha(i1)); break;
case USHR : env.localDef(def, i0.shr(i1)); break;
default : throw new AssertionError("unreachable"); }
break;
default : throw new AssertionError("unreachable");
}
}
/**
* @requires inst.getOperator() instanceof BinaryOpInstruction.Operator
* @effects this.env.localDef(inst.getDef(), [[inst.getUse(0)]] [[inst.op]] [[inst.getUse(1)]] )
**/
private void visitBinaryArithmeticOp(SSABinaryOpInstruction inst) {
final int def = inst.getDef(), use0 = inst.getUse(0), use1 = inst.getUse(1);
final IRType type = callInfo.typeOf(def);
final BinaryOpInstruction.Operator op = (BinaryOpInstruction.Operator)inst.getOperator();
// System.out.println(inst + ", " + use0 + "=" + env.localUse(use0) + ", " + use1 + "=" + env.localUse(use1));
// System.out.println("type of "+ use0 + "="+callInfo.typeOf(use0) + ", type of " + use1 +"="+ callInfo.typeOf(use1) + ", type of " +def+"=" + callInfo.typeOf(def));
switch(type) {
case INTEGER :
final IntExpression i0 = env.intUse(use0), i1 = env.intUse(use1);
switch(op) {
case ADD : env.localDef(def, i0.plus(i1)); break;
case DIV : env.localDef(def, i0.divide(i1)); break;
case MUL : env.localDef(def, i0.multiply(i1)); break;
case AND : env.localDef(def, i0.and(i1)); break;
case OR : env.localDef(def, i0.or(i1)); break;
case REM : env.localDef(def, i0.modulo(i1)); break;
case SUB : env.localDef(def, i0.minus(i1)); break;
case XOR : env.localDef(def, i0.xor(i1)); break;
default : throw new AssertionError("unreachable"); }
break;
case REAL :
final RealExpression r0 = env.realUse(use0), r1 = env.realUse(use1);
switch(op) {
case ADD : env.localDef(def, r0.plus(r1)); break;
case DIV : env.localDef(def, r0.divide(r1)); break;
case MUL : env.localDef(def, r0.multiply(r1)); break;
case REM : env.localDef(def, r0.modulo(r1)); break;
case SUB : env.localDef(def, r0.minus(r1)); break;
default : throw new AssertionError("unreachable"); }
break;
default : throw new AssertionError("unreachable");
}
}
/**
* @requires inst.getOperator() instanceof AstConstants.BinaryOp
* @effects this.env.localDef(inst.getDef(), [[inst.getUse(0)]] [[inst.op]] [[inst.getUse(1)]] )
**/
private void visitBinaryComparisonOp(SSABinaryOpInstruction inst) {
final int def = inst.getDef(), use0 = inst.getUse(0), use1 = inst.getUse(1);
final IRType type = callInfo.typeOf(use0);
final CAstBinaryOp op = (CAstBinaryOp)inst.getOperator();
switch(type) {
case OBJECT :
final Expression obj0 = env.refUse(use0), obj1 = env.refUse(use1);
switch(op) {
case EQ : env.localDef(def, obj0.eq(obj1)); break;
case NE : env.localDef(def, obj0.eq(obj1).not()); break;
case CONCAT : warnOp(inst); env.localDef(def, factory.constants().nil()); break;
default : throw new AssertionError("unreachable"); }
break;
case BOOLEAN :
final Formula b0 = env.boolUse(use0), b1 = env.boolUse(use1);
switch(op) {
case EQ : env.localDef(def, b0.iff(b1)); break;
case NE : env.localDef(def, b0.iff(b1).not()); break;
default : throw new AssertionError("unreachable"); }
break;
case INTEGER :
final IntExpression i0 = env.intUse(use0), i1 = env.intUse(use1);
switch(op) {
case EQ : env.localDef(def, i0.eq(i1)); break;
case NE : env.localDef(def, i0.eq(i1).not()); break;
case LT : env.localDef(def, i0.lt(i1)); break;
case LE : env.localDef(def, i0.lte(i1)); break;
case GT : env.localDef(def, i0.gt(i1)); break;
case GE : env.localDef(def, i0.gte(i1)); break;
default : throw new AssertionError("unreachable"); }
break;
case REAL :
final RealExpression r0 = env.realUse(use0), r1 = env.realUse(use1);
switch(op) {
case EQ : env.localDef(def, r0.eq(r1)); break;
case NE : env.localDef(def, r0.eq(r1).not()); break;
case LT : env.localDef(def, r0.lt(r1)); break;
case LE : env.localDef(def, r0.lte(r1)); break;
case GT : env.localDef(def, r0.gt(r1)); break;
case GE : env.localDef(def, r0.gte(r1)); break;
default : throw new AssertionError("unreachable"); }
break;
default : throw new AssertionError("unreachable");
}
}
/**
* @effects this.env.localDef(inst.getDef(), [[inst.getUse(0)]] [[inst.op]] [[inst.getUse(1)]] )
**/
public final void visitBinaryOp(SSABinaryOpInstruction inst) {
final BinaryOpInstruction.IOperator op = inst.getOperator();
if (op instanceof CAstBinaryOp)
visitBinaryComparisonOp(inst);
else if (op instanceof BinaryOpInstruction.Operator)
visitBinaryArithmeticOp(inst);
else if (op instanceof ShiftInstruction.Operator)
visitBinaryShiftOp(inst);
else
throw new AssertionError("unreachable");
}
/** @effects this.env.localDef(inst.getDef(), ([[inst.getToType()]]) [[inst.getUse(0)]]) */
public void visitConversion(SSAConversionInstruction inst) {
final IRType from = IRType.convert(inst.getFromType());
final IRType to = IRType.convert(inst.getToType());
final int def = inst.getDef(), use = inst.getUse(0);
switch(from) {
case INTEGER :
final IntExpression i = env.intUse(use);
switch(to) {
case INTEGER : env.localDef(def, i); break;
case REAL : env.localDef(def, RealExpression.fromIntExpr(i)); break;
default : throw new AssertionError("unreachable"); }
break;
case REAL :
final RealExpression r = env.realUse(use);
switch(to) {
case INTEGER : env.localDef(def, r.toIntExpr()); break;
case REAL : env.localDef(def, r); break;
default : throw new AssertionError("unreachable"); }
break;
default : throw new AssertionError("unreachable");
}
}
/*------------------ EMPTY METHODS ------------------ */
/** Does nothing; echo only generates output */
public final void visitEcho(AstEchoInstruction instruction) {}
/** Does nothing; control dependence handled by guard computation. */
public final void visitGoto(SSAGotoInstruction instruction) {}
/** Does nothing; control dependence handled by guard computation. */
public final void visitConditionalBranch(SSAConditionalBranchInstruction instruction) {}
/** Does nothing; control dependence handled by guard computation. */
public final void visitSwitch(SSASwitchInstruction instruction) {}
/** Does nothing, lexical writes handled by call/return mechanism. */
public final void visitAstLexicalWrite(AstLexicalWrite instruction) {}
/*------------------ HELPER METHODS ------------------ */
/** @effects adds the given warning for the specified instruction to this.warnings*/
final void warn(SSAInstruction inst, String msg) {
warnings.add(new TranslationWarning(node.getIR(), inst, msg));
}
/** @effects adds the warning "operator not modelled" to this.warnings. */
final void warnOp(SSAInstruction inst) {
warn(inst, "operator not modelled");
}
}
}