下面列出了org.apache.log4j.Logger#trace ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* Dump data structures if tracing enabled
*/
public void printTraceInfo(Logger logger, GlobalConstants consts) {
if (logger.isTraceEnabled()) {
// Print congruence sets nicely
SetMultimap<ArgOrCV, ArgOrCV> sets = canonical.sets();
for (Entry<ArgOrCV, Collection<ArgOrCV>> e: sets.asMap().entrySet()) {
logger.trace(congType + " cong. class " + e.getKey() +
" => " + e.getValue());
}
// print componentIndex and inaccessible directly
int height = 0;
CongruentSets curr = this;
do {
if (!curr.componentIndex.isEmpty()) {
logger.trace("Components#" + height + ": " + curr.componentIndex);
}
if (!varsFromParent) {
logger.trace("Vars not inherited from parent");
}
height++;
curr = curr.parent;
} while (curr != null);
}
}
private void placeRefcounts(Logger logger, GlobalVars globals, Function fn,
Block block, RCTracker increments, Set<Var> parentAssignedAliasVars) {
// First canonicalize so we can merge refcounts
increments.canonicalize();
if (logger.isTraceEnabled()) {
logger.trace("");
logger.trace("Adding increments for block " + block.getType() + " of " +
fn.id());
logger.trace("==============================");
logger.trace(increments);
}
reorderContinuations(logger, block);
// Move any increment instructions up to this block
// if they can be combined with increments here
pullUpRefIncrements(block, increments);
placer.placeAll(logger, globals, fn, block, increments,
parentAssignedAliasVars);
}
/**
* Try reordering statements.
* @param logger
* @param block
*/
private void reorderInBlock(Logger logger, Function fn, Block block) {
logger.trace("tryReorder");
// Compute StatementInfo objects
ArrayList<StatementInfo> stmtInfos =
new ArrayList<StatementInfo>(block.getStatements().size());
for (Statement stmt: block.getStatements()) {
stmtInfos.add(StatementInfo.make(logger, fn, stmt));
}
// Keep track of which instructions should go after (x -> goes before)
// Instructions are identified by index of instruction before modifications
ListMultimap<Integer, Integer> before = ArrayListMultimap.create();
// Do two passes for forward and backward edges. Treat as hard and soft
// dependencies respectively to avoid creating cycles
for (int i = 0; i < stmtInfos.size(); i++) {
addDependencies(logger, fn, stmtInfos, i, true, before);
}
for (int i = 0; i < stmtInfos.size(); i++) {
addDependencies(logger, fn, stmtInfos, i, false, before);
}
for (int i = 0; i < stmtInfos.size(); i++) {
StatementInfo info1 = stmtInfos.get(i);
if (logger.isTraceEnabled())
logger.trace("Inst " + info1 + "(" + i + ") before: " + before.get(i));
}
block.replaceStatements(rebuildInstructions(logger, block, before));
}
private static void addOutputDep(Logger logger,
Instruction inst, ListMultimap<Var, Var> dependencyGraph,
ComponentGraph components, Var out, Var in) {
if (logger.isTraceEnabled())
logger.trace("Add dep " + out + " => " + in + " for inst " + inst);
dependencyGraph.put(out, in);
}
/**
*
* TODO: it is possible to extend this to further ops
* TODO: only aliases so far are structs
* @param inst
* @param aliases
* @return
*/
private static Instruction tryPropagateAliases(Logger logger,
Instruction inst, AliasTracker aliases, Set<Var> waitedForAliases) {
if (logger.isTraceEnabled()) {
logger.trace("Try propagate aliases for " + inst);
}
if (inst.op.isRetrieve(false)) {
Var src = inst.getInput(0).getVar();
AliasKey srcKey = checkedGetCanonical(logger, aliases, waitedForAliases, src);
Arg decr = Arg.ZERO;
if (inst.getInputs().size() > 1) {
decr = inst.getInput(1);
}
if (srcKey != null && srcKey.isPlainStructAlias()) {
return TurbineOp.structRetrieveSub(inst.getOutput(0), srcKey.var,
Arrays.asList(srcKey.path), decr);
} else if (srcKey != null && srcKey.isFilenameAlias() &&
decr.isInt() && decr.getInt() == 0) {
return TurbineOp.getFilenameVal(inst.getOutput(0), srcKey.var);
}
} else if (inst.op.isAssign(false)) {
Var dst = inst.getOutput(0);
AliasKey dstKey = checkedGetCanonical(logger, aliases, waitedForAliases, dst);
logger.trace("ALIAS FOR " + dst + ": " + dstKey);
if (dstKey != null && dstKey.isPlainStructAlias()) {
return TurbineOp.structStoreSub(dstKey.var, Arrays.asList(dstKey.path),
inst.getInput(0));
} else if (dstKey != null && dstKey.isFilenameAlias()) {
return TurbineOp.setFilenameVal(dstKey.var, inst.getInput(0));
}
}
return null;
}
private static void updateCongruent(Logger logger, GlobalConstants consts,
Function function, Instruction inst, int stmtIndex,
Congruences state) throws OptUnsafeError {
List<ValLoc> resVals = inst.getResults();
List<Alias> aliases = inst.getAliases();
if (logger.isTraceEnabled()) {
logger.trace("resVals: " + resVals);
logger.trace("aliases: " + aliases);
}
state.update(consts, function.id().uniqueName(), resVals, aliases,
stmtIndex);
}
private void buildInfoRec(Logger logger, Map<FnID, Function> funcMap,
Function f, Block block, ArrayInfo info,
HierarchicalSet<Var> candidates) {
addBlockCandidates(f, block, info, candidates);
for (Statement stmt: block.getStatements()) {
switch (stmt.type()) {
case INSTRUCTION:
updateInfo(logger, funcMap, block, info, stmt.instruction(),
candidates);
break;
default:
// Do nothing: handle conditionals below
break;
}
}
for (Continuation c: block.allComplexStatements()) {
for (Block inner: c.getBlocks()) {
buildInfoRec(logger, funcMap, f, inner, info, candidates.makeChild());
}
}
// Compute bottom-up properties
updateInfoBottomUp(logger, block, info, candidates);
if (logger.isTraceEnabled()) {
logger.trace("Collected info on block: " +
System.identityHashCode(block) + " " + block.getType());
for (Var candidate: candidates) {
logger.trace(candidate + " => " + info.getEntry(block, candidate));
}
}
}
/**
* Find maximum hoist with compatible execution context
* @param logger
* @param state
* @param maxHoist
* @return
*/
private int maxHoistContext(Logger logger, HoistTracking state,
ExecTarget target, int maxHoist) {
int maxCorrectContext = 0;
/*
* Avoid hoisting work through dispatches.
*/
boolean respectDispatch = (target.targetContext() != null &&
target.targetContext() != ExecContext.control() &&
target.targetContext() != ExecContext.wildcard());
HoistTracking curr = state;
for (int hoist = 1; hoist <= maxHoist; hoist++) {
// Don't go to parent if this was in a dispatch
if (curr.dispatched && respectDispatch) {
break;
}
curr = state.parent;
if (target.canRunIn(curr.execCx)) {
maxCorrectContext = hoist;
}
}
if (logger.isTraceEnabled()) {
logger.trace("Hoist limited to " + maxCorrectContext + " by execution " +
"target " + target);
}
return maxCorrectContext;
}
private void addDependencies(Logger logger, Function fn,
ArrayList<StatementInfo> stmtInfos, int i, boolean forwardEdges,
ListMultimap<Integer, Integer> before) {
StatementInfo info1 = stmtInfos.get(i);
if (logger.isTraceEnabled())
logger.trace("addDependencies " + info1);
// Find last instruction that writes inputs of inst1
// Build a DAG of dependences between instructions
for (int j = i + 1; j < stmtInfos.size(); j++) {
StatementInfo info2 = stmtInfos.get(j);
// TODO: should check for "expensive" statements to avoid
// deferring execution by putting instruction after it
if (forwardEdges && writesInputs(logger, info2, info1, false)) {
// These edges wont create cycle - backward edge
before.put(j, i);
}
if (!forwardEdges && writesInputs(logger, info1, info2, true)) {
// Check that there isn't a path from inst1 to inst2
if (pathExists(before, j, i)) {
if (logger.isTraceEnabled()) {
logger.trace("Drop edge " + j + " => " + i + " to avoid cycle");
}
} else {
before.put(i, j);
}
}
}
}
private void optRecurseOnBlock(Logger logger, Function f, Block block,
ArrayInfo info, InitState init,
HierarchicalSet<Var> cands, HierarchicalSet<Var> invalid) {
addBlockCandidates(f, block, info, cands);
for (Var cand: cands) {
if (!invalid.contains(cand)) {
AliasTracker blockAliases = info.getAliases(block);
AliasKey candKey = blockAliases.getCanonical(cand);
BlockVarInfo vi = info.getEntry(block, cand);
if (logger.isTraceEnabled()) {
logger.trace("Candidate: " + cand + " in block " +
System.identityHashCode(block) + " " + block.getType());
logger.trace(vi);
}
if (vi.otherModRec) {
logger.trace("Can't optimize due to other inserts!");
invalid.add(cand);
} else if ((vi.insertImmOnce && vi.insertImmHere) ||
(vi.noInserts() && vi.declaredHere)) {
// Criteria 1: declared here && no inserts here or in children
// TODO
// Criteria 2: declared in ancestor && not modified on any
// non-mutually-exclusive path
// Optimize here: cases where only inserted in this block,
// or no inserts at all
logger.trace("Can optimize!");
replaceInserts(logger, block, blockAliases, init, cand, candKey);
invalid.add(cand); // Don't try to opt in descendants
} else if (vi.insertImmOnce) {
logger.trace("Try to optimize in descendant block!");
// Do nothing: handle in child block
} else {
logger.trace("Optimization not valid!");
// Invalid: can't do optimization anywhere
invalid.add(cand);
}
}
}
for (Statement stmt: block.getStatements()) {
switch (stmt.type()) {
case INSTRUCTION:
// Update which variables are initialized
InitVariables.updateInitVars(logger, stmt, init, false);
break;
case CONDITIONAL:
// Recurse and optimize, plus also update init vars
optRecurseOnCont(logger, f, stmt.conditional(), info, init,
cands, invalid);
break;
}
}
for (Continuation cont: block.getContinuations()) {
optRecurseOnCont(logger, f, cont, info, init, cands, invalid);
}
}
@Override
protected final void logMessage(final Logger logger, final String msg, final Throwable t) {
logger.trace(msg, t);
}
/**
*
* @param logger
* @param ancestorBlock the block the instructions are moved from
* @param ancestors
* @param currBlock the block they are moved too (a descendant of the prior
* block)
* @param currContext
* @param currBlockIt all changes to instructions in curr block
* are made through this iterator, and it is rewound to the previous position
* before the function exits
* @param waitMap map of variable names to instructions/continuations they block
* on
* @param writtenV
* @return true if change made, list of moved continuations
*/
private Pair<Boolean, Set<Continuation>> relocateDependentInstructions(
Logger logger,
Block ancestorBlock, ExecContext ancestorContext,
StackLite<Continuation> ancestors,
Block currBlock, ExecContext currContext, ListIterator<Statement> currBlockIt,
SetMultimap<Var, InstOrCont> waitMap, Var writtenV) {
boolean changed = false;
// Remove from outer block
Set<InstOrCont> waits = waitMap.get(writtenV);
Set<Instruction> movedI = new HashSet<Instruction>();
Set<Continuation> movedC = new HashSet<Continuation>();
// Rely on later forward Dataflow pass to remove
// unneeded wait vars
/*
* NOTE: instructions/ continuations retain the same relative
* order they were in in the original block, this should help
* optimization pass
*/
for (InstOrCont ic: waits) {
if (logger.isTraceEnabled())
logger.trace("Pushing down: " + ic.toString());
boolean relocated;
switch (ic.type()) {
case CONTINUATION: {
if (logger.isTraceEnabled())
logger.trace("Relocating " + ic.continuation().getType());
relocated = relocateContinuation(ancestors, currBlock,
currContext, movedC, ic.continuation());
break;
}
case INSTRUCTION:
if (logger.isTraceEnabled())
logger.trace("Relocating " + ic.instruction());
relocated = relocateInstruction(ancestors, currContext,
currBlock, currBlockIt, movedI, ic.instruction());
break;
default:
throw new STCRuntimeError("how on earth did we get here...");
}
changed = changed || relocated;
}
// Remove instructions from old block
ancestorBlock.removeContinuations(movedC);
ancestorBlock.removeStatements(movedI);
// Rewind iterator so that next instruction returned
// will be the first one added
ICUtil.rewindIterator(currBlockIt, movedI.size());
// Rebuild wait map to reflect changes
updateWaiterMap(waitMap, movedC, movedI);
return Pair.create(changed, movedC);
}
/**
*
* @param logger
* @param f
* @param globalVars
* @return true if changes made
*/
private static boolean eliminateIter(Logger logger, Function f,
GlobalVars globalVars) {
/* All vars defined in function blocks that could possibly be eliminated */
HashSet<Var> removeCandidates = new HashSet<Var>();
/* Set of vars that are definitely required */
HashSet<Var> needed = new HashSet<Var>();
needed.addAll(globalVars.variables());
/* List of vars that were written. Need to ensure that all variables
* that are keys in writeEffect are tracked. */
List<Component> modifiedComponents = new ArrayList<Component>();
/*
* Graph of dependencies from vars to other vars. If edge exists v1 -> v2
* this means that if v1 is required, then v2 is required
*/
ListMultimap<Var, Var> dependencyGraph = ArrayListMultimap.create();
/* Track components so that we know if a write from A may flow to B*/
ComponentGraph components = new ComponentGraph();
walkFunction(logger, f, removeCandidates, needed, dependencyGraph,
modifiedComponents, components);
if (logger.isTraceEnabled()) {
logger.trace("Dead code elimination in function " + f.id() + "\n" +
"removal candidates: " + removeCandidates + "\n" +
"definitely needed: "+ needed + "\n" +
"dependencies: \n" + printDepGraph(dependencyGraph, 4) +
"modifiedComponents: " + modifiedComponents + "\n" +
"components: \n" + components);
}
/*
* Add in component info.
* Take into account that we might modify value of containing
* structure, e.g. array
*/
for (Component written: modifiedComponents) {
Set<Var> potentialAliases = components.findPotentialAliases(written);
if (logger.isTraceEnabled()) {
logger.trace("Modified var " + written + " potential aliases: " +
potentialAliases);
}
for (Var maybeAffected: potentialAliases) {
if (logger.isTraceEnabled()) {
logger.trace("Add transitive dep " + maybeAffected +
" => " + written);
}
// Need to keep var that we wrote the affected var through
dependencyGraph.put(maybeAffected, written.var);
}
}
if (logger.isTraceEnabled())
logger.trace("dependencies after component updates: \n" +
printDepGraph(dependencyGraph, 4));
/*
* Expand set of needed based on dependency graph
*/
StackLite<Var> workStack = new StackLite<Var>();
workStack.addAll(needed);
while (!workStack.isEmpty()) {
Var neededVar = workStack.pop();
// This loop converges as dependencyGraph is taken apart
List<Var> deps = dependencyGraph.removeAll(neededVar);
if (deps != null) {
needed.addAll(deps);
workStack.addAll(deps);
}
}
removeCandidates.removeAll(needed);
if (logger.isDebugEnabled()) {
logger.debug("Final variables to be eliminated: " + removeCandidates);
}
if (removeCandidates.isEmpty()) {
return false;
} else {
f.mainBlock().removeVars(removeCandidates);
return true;
}
}
private void piggybackOnContinuations(Logger logger, Function fn,
Block block, RCTracker tracker, RCDir dir, RefCountType rcType,
RefCountCandidates candidates, UseFinder subblockWalker, boolean reverse) {
// Try to piggyback on continuations, starting at bottom up
ListIterator<Continuation> cit = reverse ? block.continuationEndIterator()
: block.continuationIterator();
while ((reverse && cit.hasPrevious()) || (!reverse && cit.hasNext())) {
Continuation cont;
if (reverse) {
cont = cit.previous();
} else {
cont = cit.next();
}
if (RCUtil.isAsyncForeachLoop(cont)) {
AbstractForeachLoop loop = (AbstractForeachLoop) cont;
VarCount piggybacked;
do {
/* Process one at a time so that candidates is correctly updated
* for each call based on previous changes */
piggybacked = loop.tryPiggyBack(candidates, rcType, dir);
if (piggybacked != null) {
if (logger.isTraceEnabled()) {
logger.trace("Piggybacked on foreach: " + piggybacked + " " +
rcType + " " + piggybacked.count);
}
candidates.add(piggybacked.var, -piggybacked.count);
tracker.cancel(tracker.getRefCountVar(piggybacked.var), rcType,
-piggybacked.count);
}
} while (piggybacked != null);
}
// Walk continuation to find usages
subblockWalker.reset();
TreeWalk.walkSyncChildren(logger, fn, cont, subblockWalker);
removeCandidates(subblockWalker.getUsedVars(), tracker, candidates);
}
}
/**
* @param rootBlock
* @return true if the block makes progress of the specified type
*/
public static boolean blockProgress(Block rootBlock, Category type) {
Logger logger = Logging.getSTCLogger();
StackLite<Block> stack = new StackLite<Block>();
stack.push(rootBlock);
while (!stack.isEmpty()) {
Block block = stack.pop();
for (Statement stmt: block.getStatements()) {
if (stmt.type() == StatementType.INSTRUCTION) {
Instruction i = stmt.instruction();
if (type == Category.CHEAP) {
if (!i.isCheap()) {
if (logger.isTraceEnabled()) {
logger.trace("non-cheap instruction found: " + i);
}
return false;
}
} else if (type == Category.CHEAP_WORKER) {
if (!isCheapWorkerInst(i)) {
if (logger.isTraceEnabled()) {
logger.trace("non-cheap-worker instruction found: " + i);
}
return false;
}
} else {
assert(type == Category.NON_PROGRESS);
if (i.isProgressEnabling() || !i.isCheap()) {
if (logger.isTraceEnabled()) {
logger.trace("progress instruction found: " + i);
}
return false;
}
}
}
}
for (Continuation c: block.allComplexStatements()) {
if (!c.isAsync()) {
for (Block inner: c.getBlocks()) {
stack.push(inner);
}
}
}
}
return true;
}
private boolean trySquash(Logger logger, WaitStatement wait,
ExecContext waitContext, Block waitBlock, WaitStatement innerWait) {
if (logger.isTraceEnabled()) {
logger.trace("Attempting squash of wait(" + wait.getWaitVars() + ") " +
wait.target() + " " + wait.getMode() +
" with wait(" + innerWait.getWaitVars() + ") " +
innerWait.target() + " " + innerWait.getMode());
}
ExecContext innerContext = innerWait.childContext(waitContext);
// Check that locations are compatible
if (!compatibleLocPar(wait.targetLocation(), innerWait.targetLocation(),
wait.parallelism(), innerWait.parallelism())) {
logger.trace("Locations incompatible");
return false;
}
// Check that recursiveness matches
if (wait.isRecursive() != innerWait.isRecursive()) {
logger.trace("Recursiveness incompatible");
return false;
}
// Check that contexts are compatible
ExecContext possibleMergedContext;
if (innerContext.equals(waitContext)) {
possibleMergedContext = waitContext;
} else {
if (waitContext.isAnyWorkContext()) {
// Don't try to move work from worker context to another context
logger.trace("Contexts incompatible (outer is " + waitContext +
" and inner is " + innerContext);
return false;
} else if (waitContext.isWildcardContext()) {
logger.trace("Outer is wildcard: maybe change to worker");
possibleMergedContext = innerContext;
} else {
assert(waitContext.isControlContext());
assert(innerContext.isAnyWorkContext() ||
innerContext.isWildcardContext());
// Inner wait is on worker, try to see if we can
// move context of outer wait to worker
logger.trace("Outer is control: maybe change to worker");
possibleMergedContext = innerContext;
}
}
// Check that wait variables not defined in this block
for (WaitVar waitVar: innerWait.getWaitVars()) {
if (waitBlock.variables().contains(waitVar.var)) {
logger.trace("Squash failed: wait var declared inside");
return false;
}
}
if (!ProgressOpcodes.isNonProgress(waitBlock, possibleMergedContext)) {
// Progress might be deferred by squashing
logger.trace("Squash failed: progress would be deferred");
return false;
}
// Pull inner up
if (logger.isTraceEnabled())
logger.trace("Squash wait(" + innerWait.getWaitVars() + ")" +
" up into wait(" + wait.getWaitVars() + ")");
wait.addWaitVars(innerWait.getWaitVars());
if (innerWait.getMode() == WaitMode.TASK_DISPATCH ||
wait.getMode() == WaitMode.TASK_DISPATCH) {
// In either case, need to make sure tasks get dispatched
wait.setMode(WaitMode.TASK_DISPATCH);
}
if (!possibleMergedContext.equals(waitContext)) {
wait.setTarget(ExecTarget.dispatched(possibleMergedContext));
}
// Fixup any local waits in block
fixupNonDispatched(innerWait, possibleMergedContext);
fixupNonDispatched(wait, possibleMergedContext);
if (logger.isTraceEnabled()) {
logger.trace("Squash succeeded: wait(" + wait.getWaitVars() + ") "
+ wait.target() + " " + wait.getMode());
}
innerWait.inlineInto(waitBlock);
return true;
}
@Override
public Pair<Boolean, List<Continuation>> tryUnroll(Logger logger,
FnID function, Block outerBlock) {
logger.trace("DesiredUnroll for " + loopName + ": " + desiredUnroll);
boolean expandLoops = isExpandLoopsEnabled();
boolean fullUnroll = isFullUnrollEnabled();
if (!Types.isIntVal(start)) {
/*
* TODO: only unroll integer ranges now - don't want to deal with
* floating point rounding issues
*/
return NO_UNROLL;
}
if (!this.unrolled && this.desiredUnroll > 1) {
// Unroll explicitly marked loops
if (this.loopCounterVar != null) {
logger.warn("Can't unroll range loop with counter variable yet," +
" ignoring unroll annotation");
return NO_UNROLL;
}
return Pair.create(true, doUnroll(logger, function, outerBlock,
desiredUnroll));
} else if (expandLoops || fullUnroll) {
long instCount = loopBody.getInstructionCount();
long iterCount = constIterCount();
if (instCount == 0) {
return NO_UNROLL;
}
if (expandLoops && iterCount >= 0) {
// See if the loop has a small number of iterations, could just expand;
if (iterCount <= getUnrollMaxIters(true)) {
long extraInstructions = instCount * (iterCount - 1);
if (extraInstructions <= getUnrollMaxExtraInsts(true)) {
return Pair.create(true, doUnroll(logger, function, outerBlock,
(int)iterCount));
}
}
}
if (!fullUnroll) {
logger.trace("Full unrolled not enabled");
return NO_UNROLL;
}
if (this.unrolled) {
// Don't do extra unrolling unless we're just expanding a small loop
return NO_UNROLL;
}
// Finally, maybe unroll a few iterations
long threshold = getUnrollMaxExtraInsts(false);
long unrollFactor = Math.min(getUnrollMaxIters(false),
(threshold / instCount) + 1);
if (unrollFactor > 1) {
return Pair.create(true, doUnroll(logger, function, outerBlock,
(int)unrollFactor));
}
}
return NO_UNROLL;
}
public static void trace(Logger logger, String messageFormat,Object...args){
if(logger.isTraceEnabled())logger.trace(String.format(messageFormat,args));
}
/**
* Try to push down waits from current block into child blocks
* @param logger
* @param fn
* @param block
* @param currContext
* @return
*/
private boolean pushDownWaits(Logger logger, Program prog, Function fn,
Block block, ExecContext currContext) {
SetMultimap<Var, InstOrCont> waitMap = buildWaiterMap(prog, block);
if (logger.isTraceEnabled()) {
logger.trace("waitMap keys: " + waitMap.keySet());
}
if (waitMap.isEmpty()) {
// If waitMap is empty, can't push anything down, so just shortcircuit
return false;
}
boolean changed = false;
HashSet<Continuation> allPushedDown = new HashSet<Continuation>();
ArrayList<Continuation> contCopy =
new ArrayList<Continuation>(block.getContinuations());
for (Continuation c: contCopy) {
if (allPushedDown.contains(c)) {
// Was moved
continue;
}
ExecContext newContext = canPushDownInto(c, currContext);
if (newContext != null) {
for (Block innerBlock: c.getBlocks()) {
StackLite<Continuation> ancestors =
new StackLite<Continuation>();
ancestors.push(c);
PushDownResult pdRes =
pushDownWaitsRec(logger, fn, block, currContext, ancestors,
innerBlock, newContext, waitMap);
changed = changed || pdRes.anyChanges;
/* The list of continuations might be modified as continuations are
* pushed down - track which ones are relocated */
allPushedDown.addAll(pdRes.relocated);
}
}
}
return changed;
}
private void structBuildRec(Logger logger, Block block) {
// Track all assigned struct paths
ListMultimap<Var, List<String>> assignedPaths = ArrayListMultimap.create();
// Find all struct assign statements in block
for (Statement stmt: block.getStatements()) {
if (stmt.type() == StatementType.INSTRUCTION) {
Instruction inst = stmt.instruction();
if (inst.op == Opcode.STRUCT_STORE_SUB) {
Var struct = inst.getOutput(0);
List<Arg> inputs = inst.getInputs();
List<Arg> fields = inputs.subList(1, inputs.size());
assignedPaths.put(struct, Arg.extractStrings(fields));
}
}
}
// Check if all fields were assigned
for (Var candidate: assignedPaths.keySet()) {
StructType candidateType = (StructType)candidate.type().getImplType();
Set<List<String>> expectedPaths = allAssignablePaths(candidateType);
List<List<String>> assigned = assignedPaths.get(candidate);
logger.trace("Check candidate " + candidate.name() + "\n" +
"expected: " + expectedPaths + "\n" +
"assigned: " + assigned);
for (List<String> path: assigned) {
Type fieldType;
try {
fieldType = candidateType.fieldTypeByPath(path);
} catch (TypeMismatchException e) {
throw new STCRuntimeError(e.getMessage());
}
Set<List<String>> assignedSubPaths;
if (Types.isStruct(fieldType)) {
// Handle case where we assign a substruct
StructType structFieldType = (StructType)fieldType.getImplType();
assignedSubPaths = allAssignablePaths(structFieldType, path);
} else {
assignedSubPaths = Collections.singleton(path);
}
for (List<String> assignedPath: assignedSubPaths) {
boolean found = expectedPaths.remove(assignedPath);
if (!found) {
logger.warn("Invalid or double-assigned struct field: " +
candidate.name() + "." + assignedPath);
}
}
}
if (expectedPaths.isEmpty()) {
doStructBuildTransform(logger, block, candidate, assigned.size());
} else if (logger.isTraceEnabled()) {
logger.trace("Fields not assigned: " + expectedPaths);
}
}
for (Continuation cont: block.allComplexStatements()) {
for (Block cb: cont.getBlocks()) {
structBuildRec(logger, cb);
}
}
}