package org.graalvm.compiler.lir.gen;
import static jdk.vm.ci.code.ValueUtil.isIllegal;
import static jdk.vm.ci.code.ValueUtil.isLegal;
import static jdk.vm.ci.meta.Value.ILLEGAL;
import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
import java.util.ArrayList;
import java.util.List;
import jdk.internal.vm.compiler.collections.EconomicMap;
import jdk.internal.vm.compiler.collections.Equivalence;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.lir.LIRInsertionBuffer;
import org.graalvm.compiler.lir.LIRInstruction;
import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
import jdk.vm.ci.meta.AllocatableValue;
import jdk.vm.ci.meta.Value;
public class PhiResolver {
static class PhiResolverNode {
final Value operand;
final ArrayList<PhiResolverNode> destinations;
boolean assigned;
boolean visited;
boolean startNode;
PhiResolverNode(Value operand) {
this.operand = operand;
destinations = new ArrayList<>(4);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(operand.toString());
if (!destinations.isEmpty()) {
buf.append(" ->");
for (PhiResolverNode node : destinations) {
buf.append(' ').append(node.operand);
}
}
return buf.toString();
}
}
private final LIRGeneratorTool gen;
private final MoveFactory moveFactory;
private final LIRInsertionBuffer buffer;
private final int insertBefore;
private PhiResolverNode loop;
private Value temp;
private final ArrayList<PhiResolverNode> variableOperands = new ArrayList<>(3);
private final ArrayList<PhiResolverNode> otherOperands = new ArrayList<>(3);
private final EconomicMap<Value, PhiResolverNode> operandToNodeMap = EconomicMap.create(Equivalence.DEFAULT);
public static PhiResolver create(LIRGeneratorTool gen) {
AbstractBlockBase<?> block = gen.getCurrentBlock();
assert block != null;
ArrayList<LIRInstruction> instructions = gen.getResult().getLIR().getLIRforBlock(block);
return new PhiResolver(gen, new LIRInsertionBuffer(), instructions, instructions.size());
}
public static PhiResolver create(LIRGeneratorTool gen, LIRInsertionBuffer buffer, List<LIRInstruction> instructions, int insertBefore) {
return new PhiResolver(gen, buffer, instructions, insertBefore);
}
protected PhiResolver(LIRGeneratorTool gen, LIRInsertionBuffer buffer, List<LIRInstruction> instructions, int insertBefore) {
this.gen = gen;
moveFactory = gen.getSpillMoveFactory();
temp = ILLEGAL;
this.buffer = buffer;
this.buffer.init(instructions);
this.insertBefore = insertBefore;
}
public void dispose() {
for (int i = variableOperands.size() - 1; i >= 0; i--) {
PhiResolverNode node = variableOperands.get(i);
if (!node.visited) {
loop = null;
move(node, null);
node.startNode = true;
assert isIllegal(temp) : "moveTempTo() call missing";
}
}
for (int i = otherOperands.size() - 1; i >= 0; i--) {
PhiResolverNode node = otherOperands.get(i);
for (int j = node.destinations.size() - 1; j >= 0; j--) {
emitMove(node.destinations.get(j).operand, node.operand);
}
}
buffer.finish();
}
public void move(Value dest, Value src) {
assert isVariable(dest) : "destination must be virtual";
assert isLegal(src) : "source for phi move is illegal";
assert isLegal(dest) : "destination for phi move is illegal";
PhiResolverNode srcNode = sourceNode(src);
PhiResolverNode destNode = destinationNode(dest);
srcNode.destinations.add(destNode);
}
private PhiResolverNode createNode(Value operand, boolean source) {
PhiResolverNode node;
if (isVariable(operand)) {
node = operandToNodeMap.get(operand);
assert node == null || node.operand.equals(operand);
if (node == null) {
node = new PhiResolverNode(operand);
operandToNodeMap.put(operand, node);
}
if (source) {
if (!variableOperands.contains(node)) {
variableOperands.add(node);
}
}
} else {
assert source;
node = new PhiResolverNode(operand);
otherOperands.add(node);
}
return node;
}
private PhiResolverNode destinationNode(Value opr) {
return createNode(opr, false);
}
private void emitMove(Value dest, Value src) {
assert isLegal(src);
assert isLegal(dest);
LIRInstruction move = moveFactory.createMove((AllocatableValue) dest, src);
buffer.append(insertBefore, move);
}
private void move(PhiResolverNode dest, PhiResolverNode src) {
if (!dest.visited) {
dest.visited = true;
for (int i = dest.destinations.size() - 1; i >= 0; i--) {
move(dest.destinations.get(i), dest);
}