package com.oracle.truffle.llvm.parser.util;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.oracle.truffle.llvm.parser.model.blocks.InstructionBlock;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.TerminatingInstruction;
public final class LLVMControlFlowGraph {
private CFGBlock[] blocks;
private List<CFGLoop> cfgLoops;
private int nextLoop = 0;
private boolean reducible = true;
public final class CFGBlock {
private final InstructionBlock instructionBlock;
public int id;
public List<CFGBlock> sucs = new ArrayList<>();
public List<CFGBlock> preds = new ArrayList<>();
public boolean visited = false;
public boolean active = false;
public boolean = false;
public BitSet loops;
public int loopId;
public CFGBlock(InstructionBlock block) {
this.instructionBlock = block;
this.id = block.getBlockIndex();
loops = new BitSet(64);
}
@Override
public String toString() {
return instructionBlock.toString();
}
}
public final class CFGLoop {
private CFGBlock ;
private List<CFGBlock> body;
private List<CFGLoop> innerLoops;
private Set<CFGBlock> successors;
private final int id;
public CFGLoop(int id) {
this.id = id;
body = new ArrayList<>();
innerLoops = new ArrayList<>();
}
public List<CFGBlock> getBody() {
return body;
}
public List<CFGLoop> getInnerLoops() {
return innerLoops;
}
public Set<CFGBlock> getSuccessors() {
if (successors == null) {
calculateSuccessors();
}
return this.successors;
}
private void calculateSuccessors() {
successors = new HashSet<>();
for (CFGBlock s : loopHeader.sucs) {
if (!isInLoop(s)) {
successors.add(s);
}
}
for (CFGBlock b : body) {
if (b.isLoopHeader) {
for (CFGLoop l : innerLoops) {
if (l.getHeader().equals(b)) {
for (CFGBlock ib : l.getSuccessors()) {
if (!isInLoop(ib)) {
successors.add(ib);
}
}
}
}
} else {
for (CFGBlock s : b.sucs) {
if (!isInLoop(s)) {
successors.add(s);
}
}
}
}
}
public int[] getSuccessorIDs() {
if (successors == null) {
calculateSuccessors();
}
int[] sucIDs = new int[successors.size()];
int i = 0;
for (CFGBlock b : successors) {
sucIDs[i++] = b.id;
}
return sucIDs;
}
public boolean isInLoop(CFGBlock block) {
return block == loopHeader || body.contains(block);
}
public CFGBlock () {
return loopHeader;
}
@Override
public String toString() {
return "Loop: " + this.id + " - Header: " + this.loopHeader.toString();
}
}
public LLVMControlFlowGraph(InstructionBlock[] blocks) {
this.blocks = new CFGBlock[blocks.length];
for (int i = 0; i < blocks.length; i++) {
this.blocks[i] = new CFGBlock(blocks[i]);
}
cfgLoops = new ArrayList<>();
}
private void resolveEdges() {
for (CFGBlock block : blocks) {
TerminatingInstruction term = block.instructionBlock.getTerminatingInstruction();
for (int i = 0; i < term.getSuccessorCount(); i++) {
int sucId = term.getSuccessor(i).getBlockIndex();
block.sucs.add(this.blocks[sucId]);
this.blocks[sucId].preds.add(block);
}
}
}
public List<CFGLoop> getCFGLoops() {
return cfgLoops;
}
public boolean isReducible() {
return reducible;
}
public void build() {
reducible = true;
resolveEdges();
if (!(openLoops(blocks[0]).isEmpty())) {
reducible = false;
return;
}
try {
sortLoops();
} catch (ControlFlowBailoutException e) {
reducible = false;
return;
}
for (CFGLoop l : getCFGLoops()) {
l.calculateSuccessors();
}
}
private boolean sortLoops() throws ControlFlowBailoutException {
List<CFGLoop> sorted = new ArrayList<>();
List<CFGLoop> active = new ArrayList<>();
for (CFGLoop l : getCFGLoops()) {
sortLoop(sorted, active, l);
}
cfgLoops = sorted;
return true;
}
private void sortLoop(List<CFGLoop> sorted, List<CFGLoop> active, CFGLoop loop) throws ControlFlowBailoutException {
if (sorted.contains(loop)) {
return;
}
active.add(loop);
for (CFGBlock b : loop.body) {
if (b.isLoopHeader) {
CFGLoop inner = cfgLoops.get(b.loopId);
if (active.contains(inner)) {
throw new ControlFlowBailoutException("Irreducible nestedness!");
}
sortLoop(sorted, active, inner);
loop.innerLoops.add(inner);
}
}
loop.body.sort(new Comparator<CFGBlock>() {
@Override
public int compare(CFGBlock o1, CFGBlock o2) {
return o2.id - o1.id;
}
});
sorted.add(loop);
active.remove(loop);
}
private BitSet openLoops(CFGBlock block) {
if (block.visited) {
if (block.active) {
makeLoopHeader(block);
return block.loops;
} else if (block.isLoopHeader) {
BitSet outerLoops = new BitSet();
outerLoops.or(block.loops);
outerLoops.clear(block.loopId);
return outerLoops;
} else {
return block.loops;
}
}
block.visited = true;
block.active = true;
BitSet loops = new BitSet();
for (CFGBlock successor : block.sucs) {
loops.or(openLoops(successor));
if (successor.active) {
loops.set(successor.loopId);
}
}
block.loops = loops;
if (block.isLoopHeader) {
loops.clear(block.loopId);
}
block.active = false;
for (int i = 0; i < nextLoop; i++) {
if (loops.get(i)) {
this.cfgLoops.get(i).body.add(block);
}
}
return loops;
}
private void (CFGBlock block) {
if (!block.isLoopHeader) {
block.isLoopHeader = true;
assert block.loops.isEmpty();
block.loops.set(nextLoop);
cfgLoops.add(new CFGLoop(nextLoop));
cfgLoops.get(nextLoop).loopHeader = block;
block.loopId = nextLoop++;
}
assert block.loops.cardinality() == 1;
}
private class ControlFlowBailoutException extends Exception {
private static final long serialVersionUID = 1L;
ControlFlowBailoutException(String string) {
super(string);
}
}
}