AmpleSetWorker.java
package edu.udel.cis.vsl.civl.kripke;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import edu.udel.cis.vsl.civl.err.CIVLUnimplementedFeatureException;
import edu.udel.cis.vsl.civl.err.UnsatisfiablePathConditionException;
import edu.udel.cis.vsl.civl.library.IF.LibraryEnabler;
import edu.udel.cis.vsl.civl.model.IF.SystemFunction;
import edu.udel.cis.vsl.civl.model.IF.expression.Expression;
import edu.udel.cis.vsl.civl.model.IF.location.Location;
import edu.udel.cis.vsl.civl.model.IF.statement.AssertStatement;
import edu.udel.cis.vsl.civl.model.IF.statement.AssignStatement;
import edu.udel.cis.vsl.civl.model.IF.statement.AssumeStatement;
import edu.udel.cis.vsl.civl.model.IF.statement.CallOrSpawnStatement;
import edu.udel.cis.vsl.civl.model.IF.statement.MPIRecvStatement;
import edu.udel.cis.vsl.civl.model.IF.statement.MPISendStatement;
import edu.udel.cis.vsl.civl.model.IF.statement.MPIStatement;
import edu.udel.cis.vsl.civl.model.IF.statement.MallocStatement;
import edu.udel.cis.vsl.civl.model.IF.statement.ReturnStatement;
import edu.udel.cis.vsl.civl.model.IF.statement.Statement;
import edu.udel.cis.vsl.civl.model.IF.statement.WaitStatement;
import edu.udel.cis.vsl.civl.model.IF.variable.Variable;
import edu.udel.cis.vsl.civl.model.common.statement.StatementList;
import edu.udel.cis.vsl.civl.semantics.IF.Evaluator;
import edu.udel.cis.vsl.civl.state.IF.DynamicScope;
import edu.udel.cis.vsl.civl.state.IF.ProcessState;
import edu.udel.cis.vsl.civl.state.IF.StackEntry;
import edu.udel.cis.vsl.civl.state.IF.State;
import edu.udel.cis.vsl.civl.util.Pair;
import edu.udel.cis.vsl.sarl.IF.expr.SymbolicExpression;
/**
* This class is responsible for computing the ample processes set at a given
* state. It is a helper of Enabler. TODO: think about ways to realize the
* independence between POR algorithm and system function implementations.
*
* @author Manchun Zheng (zmanchun)
*
*/
public class AmpleSetWorker {
/* ********************************* Types ***************************** */
/**
* The status of the computation of memory units: used in
* {@link#impactMemoryUnits}, when the result is INCOMPLETE it means that
* the approximation of the impact memory units not done and thus it could
* be anything (thus the ample set will be all processes); in contrast,
* NORMAL means that the computation is done and can be used to calculate
* the ample set.
*
* @author Manchun Zheng (zmanchun)
*
*/
private enum MemoryUnitsStatus {
NORMAL, INCOMPLETE
};
/* **************************** Static Fields ************************** */
/**
* The name of $comm_enqueu.
*/
private static final String COMM_ENQUE = "$comm_enqueue";
/**
* The name of $comm_dequeu.
*/
private static final String COMM_DEQUE = "$comm_dequeue";
/* *************************** Instance Fields ************************* */
/**
* The map of active processes (i.e., non-null processes with non-empty
* stack that have at least one enabled statement) and the corresponding
* possible $comm_enqueue/$comm_dequeu function calls.
*/
Map<Integer, Set<CallOrSpawnStatement>> activeProcesses = new LinkedHashMap<>();
/**
* Mapping of process ID's and the corresponding set of ample process ID's.
* Used for finding the smallest ample set.
*/
Map<Integer, Set<Integer>> ampleProcessesMap = new HashMap<>();
/**
* The unique enabler used in the system. Used here for evaluating the guard
* of statements.
*/
private Enabler enabler;
/**
* The unique evaluator of the system. Used to evaluate expressions and
* variables for calculating impact or reachable memory units and so on.
*/
private Evaluator evaluator;
/**
* Turn on/off the printing of debugging information for the ample set
* algorithm.
*/
private boolean debugging = false;
/**
* The output stream used for debugging.
*/
private PrintStream debugOut = System.out;
/**
* map of process ID's and their impact memory units (NULL impact memory
* units means that the computation is incomplete and all active processes
* should be included in the ample set)
*/
Map<Integer, Set<SymbolicExpression>> impactMemUnitsMap = new HashMap<>();
/**
* map of process ID's and their reachable memory units with information
* that whether the memory unit is to be written.
*/
Map<Integer, Map<SymbolicExpression, Boolean>> reachableMemUnitsMap = new HashMap<>();
/**
* map of communicators and the sets of process ID's with different ranks of
* the communicator
*/
Map<SymbolicExpression, Map<Integer, Set<Integer>>> processesInCommMap = new HashMap<>();
/**
* map of process ID's and its rank id in each communicator.
*/
Map<Integer, Map<SymbolicExpression, Integer>> processRankMap = new HashMap<>();
Map<Integer, Set<CallOrSpawnStatement>> enabledSystemCallMap = new HashMap<>();
/**
* The current state at which the ample set is to be computed.
*/
private State state;
/* ***************************** Constructors ************************** */
/**
* Creates a new instance of ample set worker for a given state.
*
* @param state
* The state that this ample set is going to work for.
* @param enabler
* The enabler used in the system.
* @param evaluator
* The evaluator used in the system.
* @param debug
* The option to turn on/off the printing of debugging
* information.
* @param debugOut
* The print stream for debugging information.
*/
AmpleSetWorker(State state, Enabler enabler, Evaluator evaluator,
boolean debug, PrintStream debugOut) {
this.state = state;
this.enabler = enabler;
this.evaluator = evaluator;
this.debugging = debug;
this.debugOut = debugOut;
}
/* *********************** Package-private Methods ********************* */
/**
* Obtain the set of ample processes for the current state.
*
* @return
*/
Set<ProcessState> ampleProcesses() {
Set<Integer> ampleProcessIDs;
Set<ProcessState> ampleProcesses = new LinkedHashSet<>();
// process and if it contains any comm_enque or comm_deque call
computeActiveProcesses();
// return immediately if at most one process is activated.
if (activeProcesses.size() <= 1)
ampleProcessIDs = new LinkedHashSet<Integer>(
activeProcesses.keySet());
else {
ampleProcessIDs = ampleProcessesWork();
}
for (int pid : ampleProcessIDs) {
ampleProcesses.add(state.getProcessState(pid));
}
return ampleProcesses;
}
/* *************************** Private Methods ************************* */
/**
* Compute the ample set when there are more than one active processes.
*
* @return The set of process ID's to be contained in the ample set.
*/
private Set<Integer> ampleProcessesWork() {
Set<Integer> result = new LinkedHashSet<>();
int minimalAmpleSetSize = activeProcesses.size() + 1;
int minAmpleSetId = -1;// keep track of the smallest ample set
preprocessing();
for (int pid : activeProcesses.keySet()) {
Set<Integer> ampleProcessIDs = ampleSetOfProcess(pid);
ampleProcessesMap.put(pid, ampleProcessIDs);
if (ampleProcessIDs.size() < minimalAmpleSetSize) {
minAmpleSetId = pid;
minimalAmpleSetSize = ampleProcessIDs.size();
}
if (minimalAmpleSetSize == 1)
break;
}
for (int pid : ampleProcessesMap.get(minAmpleSetId)) {
result.add(pid);
}
return result;
}
/**
* Compute the ample set starting with a specific process.
*
* @param startPid
* The id of the process to start with.
* @return The set of process ID's to be contained in the ample set.
*/
private Set<Integer> ampleSetOfProcess(int startPid) {
Set<Integer> ampleProcessIDs = new LinkedHashSet<>();
Stack<Integer> workingProcessIDs = new Stack<>();
workingProcessIDs.add(startPid);
ampleProcessIDs.add(startPid);
while (!workingProcessIDs.isEmpty()) {
int pid = workingProcessIDs.pop();
ProcessState thisProc = state.getProcessState(pid);
Set<SymbolicExpression> impactMemUnits = impactMemUnitsMap.get(pid);
Map<SymbolicExpression, Boolean> reachableMemUnitsMapOfThis = reachableMemUnitsMap
.get(pid);
Set<CallOrSpawnStatement> systemCalls = this.enabledSystemCallMap
.get(pid);
if (impactMemUnits == null) {
// The current process is entering an atomic/atom block
// whose impact memory units can't be computed
// completely
ampleProcessIDs = activeProcesses.keySet();
return ampleProcessIDs;
}
if (systemCalls != null && !systemCalls.isEmpty()) {
for (CallOrSpawnStatement call : systemCalls) {
SystemFunction systemFunction = (SystemFunction) call
.function();
LibraryEnabler lib = enabler.libraryEnabler(
call.getSource(), systemFunction.getLibrary());
Set<Integer> ampleSubSet = lib.ampleSet(state, pid, call,
reachableMemUnitsMap);
if (ampleSubSet != null && !ampleSubSet.isEmpty()) {
for (int amplePid : ampleSubSet) {
if (amplePid != pid
&& activeProcesses.keySet().contains(
amplePid)
&& !ampleProcessIDs.contains(amplePid)
&& !workingProcessIDs.contains(amplePid)) {
workingProcessIDs.add(amplePid);
ampleProcessIDs.add(amplePid);
if (ampleProcessIDs.size() == activeProcesses
.keySet().size())
return ampleProcessIDs;
}
}
}
}
}
for (Statement s : thisProc.getLocation().outgoing()) {
// this process has a wait statement
if (s instanceof WaitStatement) {
int joinID = evaluator.joinedIDofWait(state, thisProc,
(WaitStatement) s);
if (!ampleProcessIDs.contains(joinID)
&& workingProcessIDs.contains(joinID)) {
workingProcessIDs.add(joinID);
ampleProcessIDs.add(joinID);
if (ampleProcessIDs.size() == activeProcesses.keySet()
.size())
break;
}
}
}
for (int otherPid : activeProcesses.keySet()) {
Map<SymbolicExpression, Boolean> reachableMemUnitsMapOfOther;
// add new ample id earlier
if (otherPid == pid || ampleProcessIDs.contains(otherPid)
|| workingProcessIDs.contains(otherPid))
continue;
reachableMemUnitsMapOfOther = reachableMemUnitsMap
.get(otherPid);
for (SymbolicExpression unit : impactMemUnits) {
if (reachableMemUnitsMapOfOther.containsKey(unit)) {
if ((reachableMemUnitsMapOfThis.get(unit) || reachableMemUnitsMapOfOther
.get(unit))) {
workingProcessIDs.add(otherPid);
ampleProcessIDs.add(otherPid);
break;
}
}
}
if (ampleProcessIDs.size() == activeProcesses.keySet().size())
return ampleProcessIDs;
}
}
return ampleProcessIDs;
}
/**
* Compute active processes at the current state, i.e., non-null processes
* with non-empty stack that have at least one enabled statements.
*
*/
private void computeActiveProcesses() {
for (ProcessState p : state.getProcessStates()) {
boolean active = false;
Set<CallOrSpawnStatement> commCalls;
int pid;
if (p == null || p.hasEmptyStack())
continue;
commCalls = new HashSet<>();
pid = p.getPid();
for (Statement s : p.getLocation().outgoing()) {
if (!enabler.getGuard(s, pid, state).value.isFalse()) {
active = true;
if (s instanceof CallOrSpawnStatement) {
CallOrSpawnStatement callOrSpawnStatement = (CallOrSpawnStatement) s;
if (callOrSpawnStatement.isSystemCall()) {
SystemFunction systemFunction = (SystemFunction) callOrSpawnStatement
.function();
String library = systemFunction.getLibrary();
String name = systemFunction.name().name();
if (library.equals("civlc")) {
if (name.equals(COMM_DEQUE)
|| name.equals(COMM_ENQUE)) {
commCalls.add((CallOrSpawnStatement) s);
}
}
}
}
}
}
if (active)
activeProcesses.put(pid, commCalls);
}
}
/**
* The impact memory units of a certain process at the current state. The
* computation could be incomplete when there is atomic/atom block that
* contains function calls.
*
* @param p
* The process whose impact memory units are to be computed.
* @return The impact memory units of the process and the status to denote
* if the computation is complete.
*/
private Pair<MemoryUnitsStatus, Set<SymbolicExpression>> impactMemoryUnits(
ProcessState p) {
Set<SymbolicExpression> memUnits = new HashSet<>();
int pid = p.getPid();
Location pLocation = p.getLocation();
Set<CallOrSpawnStatement> systemCalls = new HashSet<>();
this.enabledSystemCallMap.put(pid, systemCalls);
if (pLocation.enterAtom() || pLocation.enterAtomic()
|| p.atomicCount() > 0) {
return impactMemoryUnitsOfAtomicBlock(pLocation, pid);
} else {
for (Statement s : pLocation.outgoing()) {
try {
memUnits.addAll(impactMemoryUnitsOfStatement(s, pid));
} catch (UnsatisfiablePathConditionException e) {
continue;
}
}
}
return new Pair<>(MemoryUnitsStatus.NORMAL, memUnits);
}
/**
* Compute the set of impact memory units of an atomic or atom block. All
* system function bodies are assumed to be independent (only the arguments
* are taken for computation). If there is any normal function calls, then
* the computation is terminated immediately and an empty set is returned
* with the INCOMPLETE status. This implementation is chosen because
* checking the impact memory units of function calls could be expensive and
* complicated and would be not worthy.
*
* @param location
* The start location of the atomic/atom block.
* @param pid
* The ID of the current process.
* @return The set of impact memory units of the atomic/atom block and a
* status variable to denote if the computation can be done
* completely.
*/
private Pair<MemoryUnitsStatus, Set<SymbolicExpression>> impactMemoryUnitsOfAtomicBlock(
Location location, int pid) {
int atomicCount = state.getProcessState(pid).atomicCount();
Stack<Integer> atomFlags = new Stack<Integer>();
if (atomicCount > 0) {
for (int i = 0; i < atomicCount; i++) {
atomFlags.push(1);
}
}
if (atomicCount > 0 || location.enterAtom() || location.enterAtomic()) {
Set<Integer> checkedLocations = new HashSet<Integer>();
Stack<Location> workings = new Stack<Location>();
Set<SymbolicExpression> memUnits = new HashSet<>();
workings.add(location);
// DFS searching for reachable statements inside the $atomic/$atom
// block
while (!workings.isEmpty()) {
Location currentLocation = workings.pop();
checkedLocations.add(currentLocation.id());
if (location.enterAtom() && currentLocation.enterAtom())
atomFlags.push(1);
if (location.enterAtomic() && currentLocation.enterAtomic())
atomFlags.push(1);
if (location.enterAtom() && currentLocation.leaveAtom())
atomFlags.pop();
if (location.enterAtomic() && currentLocation.leaveAtomic())
atomFlags.pop();
if (atomFlags.isEmpty()) {
if (location.enterAtom()) {
if (!currentLocation.enterAtom())
atomFlags.push(1);
}
if (location.enterAtomic()) {
if (!currentLocation.enterAtomic())
atomFlags.push(1);
}
continue;
}
if (currentLocation.getNumOutgoing() > 0) {
int number = currentLocation.getNumOutgoing();
for (int i = 0; i < number; i++) {
Statement s = currentLocation.getOutgoing(i);
if (s instanceof CallOrSpawnStatement) {
CallOrSpawnStatement callOrSpawnStatement = (CallOrSpawnStatement) s;
if (callOrSpawnStatement.isCall()) {
// assume that system functions are always ample
// TODO check comm_deque and comm_enque for
// hybrid mpi processes.
if (!(callOrSpawnStatement.function() instanceof SystemFunction))
return new Pair<MemoryUnitsStatus, Set<SymbolicExpression>>(
MemoryUnitsStatus.INCOMPLETE,
memUnits);
}
}
try {
memUnits.addAll(impactMemoryUnitsOfStatement(s, pid));
} catch (UnsatisfiablePathConditionException e) {
}
if (s.target() != null) {
if (!checkedLocations.contains(s.target().id())) {
workings.push(s.target());
}
}
}
}
}
return new Pair<MemoryUnitsStatus, Set<SymbolicExpression>>(
MemoryUnitsStatus.NORMAL, memUnits);
}
return null;
}
/**
* Compute the impact memory units of a given statement of a certain process
* at the current state.
*
* @param statement
* The statement whose impact memory units are to be computed.
* @param pid
* The id of the process that owns the statement.
* @return the impact memory units of the statement
* @throws UnsatisfiablePathConditionException
*/
private Set<SymbolicExpression> impactMemoryUnitsOfStatement(
Statement statement, int pid)
throws UnsatisfiablePathConditionException {
Set<SymbolicExpression> memUnits = new HashSet<>();
Set<SymbolicExpression> memUnitsPartial = memoryUnit(statement.guard(),
pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
switch (statement.statementKind()) {
case ASSERT:
AssertStatement assertStatement = (AssertStatement) statement;
Expression assertExpression = assertStatement.getExpression();
Expression[] printfArgs = assertStatement.printfArguments();
memUnitsPartial = memoryUnit(assertExpression, pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
if (printfArgs != null) {
for (Expression argument : printfArgs) {
memUnitsPartial = memoryUnit(argument, pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
}
}
break;
case ASSIGN:
case CHOOSE:
AssignStatement assignStatement = (AssignStatement) statement;
memUnitsPartial = memoryUnit(assignStatement.getLhs(), pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
memUnitsPartial = memoryUnit(assignStatement.rhs(), pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
break;
case ASSUME:
AssumeStatement assumeStatement = (AssumeStatement) statement;
Expression assumeExpression = assumeStatement.getExpression();
memUnitsPartial = memoryUnit(assumeExpression, pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
break;
case CALL_OR_SPAWN:
CallOrSpawnStatement call = (CallOrSpawnStatement) statement;
if (call.isSystemCall()) {
this.enabledSystemCallMap.get(pid).add(call);
}
for (Expression argument : call.arguments()) {
memUnitsPartial = memoryUnit(argument, pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
}
break;
case MALLOC:
MallocStatement mallocStatement = (MallocStatement) statement;
memUnitsPartial = memoryUnit(mallocStatement.getLHS(), pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
memUnitsPartial = memoryUnit(mallocStatement.getScopeExpression(),
pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
memUnitsPartial = memoryUnit(mallocStatement.getSizeExpression(),
pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
break;
case MPI:
memUnitsPartial = impactMemoryUnitsOfMPIStatement(
(MPIStatement) statement, pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
break;
case NOOP:
break;
case RETURN:
ReturnStatement returnStatement = (ReturnStatement) statement;
if (returnStatement.expression() != null) {
memUnitsPartial = memoryUnit(returnStatement.expression(), pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
}
break;
case STATEMENT_LIST:
StatementList statementList = (StatementList) statement;
for (Statement subStatement : statementList.statements()) {
memUnits.addAll(impactMemoryUnitsOfStatement(subStatement, pid));
}
break;
case WAIT:
memUnitsPartial = memoryUnit(((WaitStatement) statement).process(),
pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
break;
default:
throw new CIVLUnimplementedFeatureException(
"Impact memory units for statement: ", statement);
}
return memUnits;
}
/**
* Compute the impact memory units of an MPI statement.
*
* @param statement
* The MPI statement whose impact memory units are to be
* computed.
* @param pid
* The ID of the process that executes the MPI statement.
* @return the impact memory units of the given MPI statement
* @throws UnsatisfiablePathConditionException
*/
private Set<SymbolicExpression> impactMemoryUnitsOfMPIStatement(
MPIStatement statement, int pid)
throws UnsatisfiablePathConditionException {
Set<SymbolicExpression> memUnits = new HashSet<>();
Set<SymbolicExpression> memUnitsPartial;
switch (statement.mpiStatementKind()) {
case IBARRIER:
break;
case IRECV:
break;
case ISEND:
break;
case RECV:
MPIRecvStatement mpiRecvStatement = (MPIRecvStatement) statement;
for (Expression argument : mpiRecvStatement.getArgumentsList()) {
memUnitsPartial = memoryUnit(argument, pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
}
break;
case SEND:
// TODO: why the program never goes there ?
MPISendStatement mpiSendStatement = (MPISendStatement) statement;
for (Expression argument : mpiSendStatement.getArgumentsList()) {
memUnitsPartial = memoryUnit(argument, pid);
if (memUnitsPartial != null) {
memUnits.addAll(memUnitsPartial);
}
}
break;
case WAIT:
break;
default:
throw new CIVLUnimplementedFeatureException(
"Impact memory units for statement: ", statement);
}
return memUnits;
}
/**
* Compute the set of memory units accessed by a given expression of a
* certain process at the current state.
*
* @param expression
* The expression whose impact memory units are to be computed.
* @param pid
* The id of the process that the expression belongs to.
* @return
* @throws UnsatisfiablePathConditionException
*/
private Set<SymbolicExpression> memoryUnit(Expression expression, int pid)
throws UnsatisfiablePathConditionException {
Set<SymbolicExpression> memoryUnits = new HashSet<>();
evaluator.memoryUnitsOfExpression(state, pid, expression, memoryUnits);
if (debugging) {
printMemoryUnitsOfExpression(expression, memoryUnits);
}
// printMemoryUnitsOfExpression(expression, memoryUnits);
return memoryUnits;
}
/**
* Pre-processing for ample set computation, including:
* <ul>
* <li>Compute the impact memory units for each process; and</li>
* <li>Compute the reachable memory units for each process.</li>
* </ul>
*/
private void preprocessing() {
for (int pid : activeProcesses.keySet()) {
ProcessState p = state.getProcessState(pid);
Pair<MemoryUnitsStatus, Set<SymbolicExpression>> impactMemUnitsPair = impactMemoryUnits(p);
if (impactMemUnitsPair.left == MemoryUnitsStatus.INCOMPLETE) {
impactMemUnitsMap.put(pid, null);
} else {
impactMemUnitsMap.put(pid, impactMemUnitsPair.right);
}
reachableMemUnitsMap.put(pid, reachableMemoryUnits(p));
}
}
/**
* Print the impact memory units of a expression. Used for debugging.
*
* @param expression
* The expression to be printed.
* @param memUnits
* The memory units of the expression.
*/
private void printMemoryUnitsOfExpression(Expression expression,
Set<SymbolicExpression> memUnits) {
debugOut.println(expression.toString() + "\t mem units:");
for (SymbolicExpression memUnit : memUnits) {
debugOut.print(memUnit + "\t");
}
debugOut.println();
}
/**
* Given a process, compute the set of reachable memory units and if the
* memory unit could be modified at the current location or any future
* location.
*
* @param p
* The process whose reachable memory units are to be computed.
* @return A map of reachable memory units and if they could be modified by
* the process.
*/
private Map<SymbolicExpression, Boolean> reachableMemoryUnits(ProcessState p) {
Set<Integer> checkedDyScopes = new HashSet<>();
Map<SymbolicExpression, Boolean> memUnitPermissionMap = new HashMap<>();
Set<Variable> writableVariables = p.getLocation().writableVariables();
// only look at the top stack is sufficient
StackEntry callStack = p.peekStack();
int dyScopeID = callStack.scope();
while (dyScopeID >= 0) {
if (checkedDyScopes.contains(dyScopeID))
break;
else {
DynamicScope dyScope = state.getScope(dyScopeID);
int size = dyScope.numberOfValues();
for (int vid = 0; vid < size; vid++) {
Variable variable = dyScope.lexicalScope().variable(vid);
Set<SymbolicExpression> varMemUnits = evaluator
.memoryUnitsOfVariable(dyScope.getValue(vid),
dyScopeID, vid, state);
boolean permission = writableVariables.contains(variable) ? true
: false;
for (SymbolicExpression unit : varMemUnits) {
if (!memUnitPermissionMap.containsKey(unit)) {
memUnitPermissionMap.put(unit, permission);
}
}
}
checkedDyScopes.add(dyScopeID);
dyScopeID = state.getParentId(dyScopeID);
}
}
return memUnitPermissionMap;
}
}