/** * Bottom-up approach, keeping track of which variables will be read by successor elements. */ private void verifyBlock(CfgBlock block, LiveVariables blockLiveVariables, Set<Symbol> readSymbols) { Set<Symbol> willBeRead = new HashSet<>(blockLiveVariables.getOut()); for (Tree element : Lists.reverse(block.elements())) { Map<Symbol, VariableUsage> usagesInElement = blockLiveVariables.getVariableUsages(element); for (Map.Entry<Symbol, VariableUsage> symbolWithUsage : usagesInElement.entrySet()) { Symbol symbol = symbolWithUsage.getKey(); if (!readSymbols.contains(symbol)) { // will be reported by S1481 continue; } VariableUsage usage = symbolWithUsage.getValue(); if (usage.isWrite() && !usage.isRead()) { if (!willBeRead.contains(symbol) && !shouldSkip(element, symbol)) { context().newIssue(this, element, String.format(MESSAGE_TEMPLATE, symbol.name())); } willBeRead.remove(symbol); } else if (usage.isRead()) { willBeRead.add(symbol); } } } }
/** * See "worklist algorithm" in http://www.cs.cornell.edu/courses/cs4120/2013fa/lectures/lec26-fa13.pdf * An alternative terminology for "kill/gen" is "def/use" */ private void compute(ControlFlowGraph cfg, SymbolTable symbols) { cfg.blocks().forEach(block -> liveVariablesPerBlock.put(block, LiveVariables.build(block, symbols))); Deque<CfgBlock> workList = new ArrayDeque<>(cfg.blocks()); while (!workList.isEmpty()) { CfgBlock currentBlock = workList.pop(); LiveVariables liveVariables = liveVariablesPerBlock.get(currentBlock); boolean liveInHasChanged = liveVariables.propagate(liveVariablesPerBlock); if (liveInHasChanged) { currentBlock.predecessors().forEach(workList::push); } } }
private void init(CfgBlock block, SymbolTable symbols) { // 'writtenOnly' has variables that are WRITE-ONLY inside at least one element // (as opposed to 'kill' which can have a variable that inside an element is both READ and WRITTEN) Set<Symbol> writtenOnly = new HashSet<>(); for (Tree element : block.elements()) { Map<Symbol, VariableUsage> variableUsages = ReadWriteVisitor.getVariableUsages(element, symbols); variableUsagesPerElement.put(element, variableUsages); computeGenAndKill(writtenOnly, variableUsages); } }
/** * Bottom-up approach, keeping track of which variables will be read by successor elements. */ private void verifyBlock(CfgBlock block, LiveVariables blockLiveVariables, Set<Symbol> readSymbols) { Set<Symbol> willBeRead = new HashSet<>(blockLiveVariables.getOut()); for (Tree element : Lists.reverse(block.elements())) { Map<Symbol, VariableUsage> usagesInElement = blockLiveVariables.getVariableUsages(element); for (Map.Entry<Symbol, VariableUsage> symbolWithUsage : usagesInElement.entrySet()) { Symbol symbol = symbolWithUsage.getKey(); if (!readSymbols.contains(symbol)) { // will be reported by S1481 continue; } VariableUsage usage = symbolWithUsage.getValue(); if (usage.isWrite() && !usage.isRead()) { if (!willBeRead.contains(symbol) && !shouldSkip(element, symbol)) { context().newIssue(this, element, String.format(MESSAGE_TEMPLATE, symbol.name())); } willBeRead.remove(symbol); } else if (usage.isRead()) { willBeRead.add(symbol); } } } }
private void assertLva(ControlFlowGraph actualCfg, LiveVariablesAnalysis actualLva) { debugDotNotation = CfgPrinter.toDot(actualCfg); assertThat(actualCfg.blocks()) .withFailMessage(buildDebugMessage("size", "CFG")) .hasSize(expectedCfg.size()); for (CfgBlock actualBlock : actualCfg.blocks()) { if (actualBlock.equals(actualCfg.end())) { continue; } String blockTestId = expectedCfg.testId(actualBlock); LiveVariablesAnalysis.LiveVariables actualLiveVariables = actualLva.getLiveVariables(actualBlock); assertVariablesAreEqual("Gen Variables", actualLiveVariables.getGen(), expectedCfg.expectedGenVariables(actualBlock), blockTestId); assertVariablesAreEqual("Killed Variables", actualLiveVariables.getKill(), expectedCfg.expectedKilledVariables(actualBlock), blockTestId); assertVariablesAreEqual("Live In Variables", actualLiveVariables.getIn(), expectedCfg.expectedLiveInVariables(actualBlock), blockTestId); assertVariablesAreEqual("Live Out Variables", actualLiveVariables.getOut(), expectedCfg.expectedLiveOutVariables(actualBlock), blockTestId); } }
/** * See "worklist algorithm" in http://www.cs.cornell.edu/courses/cs4120/2013fa/lectures/lec26-fa13.pdf * An alternative terminology for "kill/gen" is "def/use" */ private void compute(ControlFlowGraph cfg, SymbolTable symbols) { cfg.blocks().forEach(block -> liveVariablesPerBlock.put(block, LiveVariables.build(block, symbols))); Deque<CfgBlock> workList = new ArrayDeque<>(cfg.blocks()); while (!workList.isEmpty()) { CfgBlock currentBlock = workList.pop(); LiveVariables liveVariables = liveVariablesPerBlock.get(currentBlock); boolean liveInHasChanged = liveVariables.propagate(liveVariablesPerBlock); if (liveInHasChanged) { currentBlock.predecessors().forEach(workList::push); } } }
/** * Builds a new LiveVariables instance for the given block and initializes the 'kill' and 'gen' symbol sets. */ static LiveVariables build(CfgBlock block, SymbolTable symbols) { LiveVariables instance = new LiveVariables(block); instance.init(block, symbols); return instance; }
/** * Builds a new LiveVariables instance for the given block and initializes the 'kill' and 'gen' symbol sets. */ static LiveVariables build(CfgBlock block, SymbolTable symbols) { LiveVariables instance = new LiveVariables(block); instance.init(block, symbols); return instance; }
private void init(CfgBlock block, SymbolTable symbols) { // 'writtenOnly' has variables that are WRITE-ONLY inside at least one element // (as opposed to 'kill' which can have a variable that inside an element is both READ and WRITTEN) Set<Symbol> writtenOnly = new HashSet<>(); for (Tree element : block.elements()) { Map<Symbol, VariableUsage> variableUsages = ReadWriteVisitor.getVariableUsages(element, symbols); variableUsagesPerElement.put(element, variableUsages); computeGenAndKill(writtenOnly, variableUsages); } }