All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.unive.lisa.interprocedural.context.recursion.RecursionSolver Maven / Gradle / Ivy

The newest version!
package it.unive.lisa.interprocedural.context.recursion;

import it.unive.lisa.analysis.AbstractState;
import it.unive.lisa.analysis.AnalysisState;
import it.unive.lisa.analysis.OptimizedAnalyzedCFG;
import it.unive.lisa.analysis.SemanticException;
import it.unive.lisa.analysis.StatementStore;
import it.unive.lisa.analysis.lattices.ExpressionSet;
import it.unive.lisa.analysis.lattices.GenericMapLattice;
import it.unive.lisa.conf.FixpointConfiguration;
import it.unive.lisa.interprocedural.InterproceduralAnalysisException;
import it.unive.lisa.interprocedural.OpenCallPolicy;
import it.unive.lisa.interprocedural.callgraph.CallGraph;
import it.unive.lisa.interprocedural.context.ContextBasedAnalysis;
import it.unive.lisa.interprocedural.context.ContextSensitivityToken;
import it.unive.lisa.program.Application;
import it.unive.lisa.program.cfg.CFG;
import it.unive.lisa.program.cfg.fixpoints.CFGFixpoint.CompoundState;
import it.unive.lisa.program.cfg.statement.Expression;
import it.unive.lisa.program.cfg.statement.Statement;
import it.unive.lisa.program.cfg.statement.call.CFGCall;
import it.unive.lisa.program.cfg.statement.call.Call;
import it.unive.lisa.symbolic.value.Identifier;
import it.unive.lisa.symbolic.value.PushInv;
import it.unive.lisa.util.StringUtilities;
import it.unive.lisa.util.collections.workset.WorkingSet;
import it.unive.lisa.util.datastructures.graph.algorithms.FixpointException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * A recursion solver that applies the iterates of the recursion starting from
 * bottom. This solver operates by restarting the recursion from
 * {@link Recursion#getInvocation()} a number of times, until the results of all
 * the members stabilize.
 * 
 * @author Luca Negrini
 * 
 * @param  the type of {@link AbstractState} contained into the analysis
 *                state
 */
public class RecursionSolver> extends ContextBasedAnalysis {

	private static final Logger LOG = LogManager.getLogger(RecursionSolver.class);

	private final Recursion recursion;

	private final boolean returnsVoid;

	private final Map, ContextSensitivityToken>> finalEntryStates;

	private final BaseCasesFinder baseCases;

	private GenericMapLattice> previousApprox;

	private GenericMapLattice> recursiveApprox;

	private AnalysisState base;

	/**
	 * Builds the solver.
	 * 
	 * @param backing   the analysis that backs this solver, and that can be
	 *                      used to query call results
	 * @param recursion the recursion to solve
	 */
	public RecursionSolver(
			ContextBasedAnalysis backing,
			Recursion recursion) {
		super(backing);
		this.recursion = recursion;
		finalEntryStates = new HashMap<>();
		// the return value of each back call must be the same as the one
		// starting the recursion, as they invoke the same cfg
		returnsVoid = returnsVoid(recursion.getInvocation(), null);
		baseCases = new BaseCasesFinder<>(backing, recursion, returnsVoid);
	}

	@Override
	public void init(
			Application app,
			CallGraph callgraph,
			OpenCallPolicy policy)
			throws InterproceduralAnalysisException {
		// we mark this as unsupported to make sure it never gets used as a root
		// analysis
		throw new UnsupportedOperationException();
	}

	@Override
	public void fixpoint(
			AnalysisState entryState,
			Class> fixpointWorkingSet,
			FixpointConfiguration conf)
			throws FixpointException {
		// we mark this as unsupported to make sure it never gets used as a root
		// analysis
		throw new UnsupportedOperationException();
	}

	@Override
	public AnalysisState getAbstractResultOf(
			CFGCall call,
			AnalysisState entryState,
			ExpressionSet[] parameters,
			StatementStore expressions)
			throws SemanticException {
		boolean inRecursion = recursion.getMembers().contains(call.getCFG());
		if (inRecursion && call.getTargetedCFGs().contains(recursion.getRecursionHead())) {
			// this is a back call
			finalEntryStates.put(call, Pair.of(entryState, token));

			AnalysisState approx = null;
			if (recursiveApprox.getMap() != null)
				approx = recursiveApprox.getMap().get(call);
			if (approx == null)
				// no state: we must start with the base cases
				approx = transferToCallsite(recursion.getInvocation(), call, base);
			// we bring in the entry state to carry over the correct scope
			AnalysisState res = approx.lub(entryState);
			Identifier meta = call.getMetaVariable();
			if (!res.getState().knowsIdentifier(meta)) {
				// if we have no information for the return value, we want to
				// force it to bottom as it means that this is either the first
				// execution (that must start from bottom) or that the recursion
				// diverges
				PushInv inv = new PushInv(meta.getStaticType(), call.getLocation());
				res = res.assign(meta, inv, call);
			}
			return res;
		}
		return super.getAbstractResultOf(call, entryState, parameters, expressions);
	}

	@Override
	protected boolean canShortcut(
			CFG cfg) {
		// we want to compute the recursive chain with no shortcuts
		return !recursion.getMembers().contains(cfg);
	}

	@Override
	protected boolean shouldCheckForRecursions() {
		return false;
	}

	/**
	 * Solves the recursion by applying its iterates starting from bottom.
	 * 
	 * @throws SemanticException if an exception happens during the computation
	 */
	public void solve() throws SemanticException {
		int recursionCount = 0;
		Call start = recursion.getInvocation();
		Collection ends = finalEntryStates.keySet();
		CompoundState entryState = recursion.getEntryState();

		LOG.info("Solving recursion at " + start.getLocation() + " for context " + recursion.getInvocationToken());

		recursiveApprox = new GenericMapLattice<>(entryState.postState.bottom());
		recursiveApprox = recursiveApprox.bottom();
		base = baseCases.find();

		Expression[] actuals = start.getParameters();
		ExpressionSet[] params = new ExpressionSet[actuals.length];
		for (int i = 0; i < params.length; i++)
			params[i] = entryState.intermediateStates.getState(actuals[i]).getComputedExpressions();

		do {
			LOG.debug(StringUtilities.ordinal(recursionCount + 1)
					+ " evaluation of recursive chain at "
					+ start.getLocation());

			previousApprox = recursiveApprox;

			// we reset the analysis at the point where the starting call can be
			// evaluated
			token = recursion.getInvocationToken();
			AnalysisState post = start.forwardSemanticsAux(
					this,
					entryState.postState,
					params,
					entryState.intermediateStates);

			for (CFGCall end : ends)
				recursiveApprox = recursiveApprox.putState(end, transferToCallsite(start, end, post));

			if (conf.recursionWideningThreshold < 0)
				recursiveApprox = previousApprox.lub(recursiveApprox);
			else if (recursionCount == conf.recursionWideningThreshold)
				recursiveApprox = previousApprox.widening(recursiveApprox);
			else {
				recursionCount++;
				recursiveApprox = previousApprox.lub(recursiveApprox);
			}
		} while (!recursiveApprox.lessOrEqual(previousApprox));

		if (conf.optimize)
			// as the fixpoint results do not contain an explicit entry for the
			// recursive call, we need to store the approximation for the
			// recursive call manually or the unwinding won't manage to solve it
			for (CFGCall call : ends) {
				Pair, ContextSensitivityToken> pair = finalEntryStates.get(call);
				AnalysisState callEntry = pair.getLeft();
				ContextSensitivityToken callingToken = pair.getRight();

				// we get the cfg containing the call
				OptimizedAnalyzedCFG caller = (OptimizedAnalyzedCFG) results.get(call.getCFG())
						.get(callingToken);

				// we get the actual call that is part of the cfg
				Call source = call;
				while (source.getSource() != null)
					source = source.getSource();

				// it might happen that the call is a hotspot and we don't need
				// any additional work
				if (!caller.hasPostStateOf(source)) {
					// we add the value to the entry state, bringing in also the
					// base case
					AnalysisState local = transferToCallsite(start, call, base);
					AnalysisState returned = callEntry.lub(recursiveApprox.getState(call).lub(local));
					Identifier meta = call.getMetaVariable();
					if (!returned.getState().knowsIdentifier(meta)) {
						// if we have no information for the return value, we
						// want to
						// force it to bottom as it means that this is either
						// the first
						// execution (that must start from bottom) or that the
						// recursion
						// diverges
						PushInv inv = new PushInv(meta.getStaticType(), call.getLocation());
						returned = returned.assign(meta, inv, call);
					}

					// finally, we store it in the result
					caller.storePostStateOf(source, returned);
				}
			}
	}

	private AnalysisState transferToCallsite(
			Call original,
			CFGCall destination,
			AnalysisState state)
			throws SemanticException {
		AnalysisState res = state.bottom();
		Identifier meta = destination.getMetaVariable();
		if (returnsVoid)
			res = state;
		else
			for (Identifier variable : original.getMetaVariables())
				// we transfer the return value
				res = res.lub(state.assign(meta, variable, original));

		if (!res.getState().knowsIdentifier(meta)) {
			// if we have no information for the return value, we want to
			// force it to bottom as it means that this is either the first
			// execution (that must start from bottom) or that the recursion
			// diverges
			PushInv inv = new PushInv(meta.getStaticType(), destination.getLocation());
			res = res.assign(meta, inv, destination);
		}

		// we only keep variables that can be affected by the recursive
		// chain: the whole recursion return value, and all variables
		// that are not sensible to scoping. All other variables are
		// subjected to push and pop operations and cannot be
		// considered a contribution of the recursive call.
		res = res.forgetIdentifiersIf(i -> i.canBeScoped() && !i.equals(meta));
		return res;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy