package com.oracle.truffle.llvm.parser.nodes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.llvm.parser.metadata.debuginfo.SourceVariable;
import com.oracle.truffle.llvm.parser.metadata.debuginfo.ValueFragment;
import com.oracle.truffle.llvm.runtime.CommonNodeFactory;
import com.oracle.truffle.llvm.runtime.debug.scope.LLVMSourceSymbol;
import com.oracle.truffle.llvm.runtime.debug.value.LLVMDebugObjectBuilder;
import com.oracle.truffle.llvm.runtime.debug.value.LLVMDebugValue;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMExpressionNode;
import com.oracle.truffle.llvm.runtime.nodes.base.LLVMBasicBlockNode;
import com.oracle.truffle.llvm.runtime.nodes.intrinsics.llvm.debug.LLVMDebugAggregateObjectBuilder;
import com.oracle.truffle.llvm.runtime.nodes.intrinsics.llvm.debug.LLVMDebugSimpleObjectBuilder;
import com.oracle.truffle.llvm.runtime.types.symbols.LocalVariableDebugInfo;
public final class LLVMRuntimeDebugInformation implements LocalVariableDebugInfo {
abstract static class LocalVarDebugInfo {
public final int instructionIndex;
public final LLVMSourceSymbol variable;
LocalVarDebugInfo(int instructionIndex, LLVMSourceSymbol variable) {
this.instructionIndex = instructionIndex;
this.variable = variable;
}
public abstract LLVMDebugObjectBuilder process(LLVMDebugObjectBuilder previous, Frame frame);
public abstract boolean isInitialize();
}
static class SimpleLocalVariable extends LocalVarDebugInfo {
private final boolean mustDereference;
private final Object value;
private final int valueFrameIdentifier;
SimpleLocalVariable(int instructionIndex, boolean mustDereference, Object value, int valueFrameIdentifier, LLVMSourceSymbol variable) {
super(instructionIndex, variable);
this.mustDereference = mustDereference;
this.value = value;
this.valueFrameIdentifier = valueFrameIdentifier;
}
protected Object getValue(Frame frame) {
if (valueFrameIdentifier != -1) {
FrameSlot slot = frame.getFrameDescriptor().findFrameSlot(valueFrameIdentifier);
if (slot != null) {
return frame.getValue(slot);
}
} else if (value != null) {
if (value instanceof LLVMExpressionNode) {
try {
return ((LLVMExpressionNode) value).executeGeneric((VirtualFrame) frame);
} catch (IllegalStateException e) {
}
} else {
return value;
}
}
return null;
}
protected LLVMDebugValue.Builder createBuilder() {
return mustDereference ? CommonNodeFactory.createDebugDeclarationBuilder() : CommonNodeFactory.createDebugValueBuilder();
}
@Override
public LLVMDebugObjectBuilder process(LLVMDebugObjectBuilder previous, Frame frame) {
return new LLVMDebugSimpleObjectBuilder(createBuilder(), getValue(frame));
}
@Override
public boolean isInitialize() {
return true;
}
}
static final class InitAggreateLocalVariable extends LocalVarDebugInfo {
private final int[] offsets;
private final int[] lengths;
InitAggreateLocalVariable(int instructionIndex, SourceVariable variable) {
super(instructionIndex, variable.getSymbol());
assert variable.hasFragments();
List<ValueFragment> fragments = variable.getFragments();
offsets = new int[fragments.size()];
lengths = new int[fragments.size()];
for (int i = 0; i < fragments.size(); i++) {
ValueFragment fragment = fragments.get(i);
offsets[i] = fragment.getOffset();
lengths[i] = fragment.getLength();
}
}
@Override
public LLVMDebugObjectBuilder process(LLVMDebugObjectBuilder previous, Frame frame) {
return new LLVMDebugAggregateObjectBuilder(offsets, lengths);
}
@Override
public boolean isInitialize() {
return true;
}
}
static final class ClearLocalVariableParts extends LocalVarDebugInfo {
private final int[] parts;
ClearLocalVariableParts(int instructionIndex, LLVMSourceSymbol variable, int[] parts) {
super(instructionIndex, variable);
this.parts = parts;
}
@Override
public LLVMDebugObjectBuilder process(LLVMDebugObjectBuilder previous, Frame frame) {
((LLVMDebugAggregateObjectBuilder) previous).clear(parts);
return previous;
}
@Override
public boolean isInitialize() {
return false;
}
}
static final class SetLocalVariablePart extends SimpleLocalVariable {
private final int partIndex;
SetLocalVariablePart(int instructionIndex, boolean mustDereference, Object value, int valueFrameIdentifier, LLVMSourceSymbol variable, int partIndex) {
super(instructionIndex, mustDereference, value, valueFrameIdentifier, variable);
this.partIndex = partIndex;
}
@Override
public LLVMDebugObjectBuilder process(LLVMDebugObjectBuilder previous, Frame frame) {
((LLVMDebugAggregateObjectBuilder) previous).setPart(partIndex, createBuilder(), getValue(frame));
return previous;
}
@Override
public boolean isInitialize() {
return false;
}
}
private final LocalVarDebugInfo[][] infos;
private ArrayList<Integer>[] predecessors;
private LLVMBasicBlockNode[] blocks;
private ArrayList<HashMap<LLVMSourceSymbol, List<LocalVarDebugInfo>>> blockEntryDebugInfo;
public LLVMRuntimeDebugInformation(int blockCount) {
this.infos = new LocalVarDebugInfo[blockCount][];
}
public void setBlockDebugInfo(int blockIndex, LocalVarDebugInfo[] debugInfo) {
assert infos[blockIndex] == null;
infos[blockIndex] = debugInfo;
}
public void setBlocks(LLVMBasicBlockNode[] blocks) {
this.blocks = blocks;
}
@Override
public Map<LLVMSourceSymbol, Object> getLocalVariables(Frame frame, Node node) {
Node current = node;
while (current != null) {
if (current.getParent() instanceof LLVMBasicBlockNode) {
LLVMBasicBlockNode block = (LLVMBasicBlockNode) current.getParent();
for (int i = 0; i < block.getStatements().length; i++) {
if (block.getStatements()[i] == current) {
return getLocalVariablesForIndex(frame, block.getBlockId(), i);
}
}
assert current == block.getTerminatingInstruction();
return getLocalVariablesForIndex(frame, block.getBlockId(), Integer.MAX_VALUE);
}
current = current.getParent();
}
return getLocalVariablesForIndex(frame, 0, 0);
}
private HashMap<LLVMSourceSymbol, List<LocalVarDebugInfo>> applyBlockInfo(HashMap<LLVMSourceSymbol, List<LocalVarDebugInfo>> blockEntryState, int blockId, int end) {
HashMap<LLVMSourceSymbol, List<LocalVarDebugInfo>> result = new HashMap<>();
for (Map.Entry<LLVMSourceSymbol, List<LocalVarDebugInfo>> entry : blockEntryState.entrySet()) {
result.put(entry.getKey(), new ArrayList<>(entry.getValue()));
}
for (LocalVarDebugInfo info : infos[blockId]) {
if (info.instructionIndex > end) {
break;
}
List<LocalVarDebugInfo> list;
if (info.isInitialize()) {
result.put(info.variable, list = new ArrayList<>());
} else {
list = result.get(info.variable);
if (list == null) {
result.put(info.variable, list = new ArrayList<>());
}
}
list.add(info);
}
return result;
}
private Map<LLVMSourceSymbol, Object> getLocalVariablesForIndex(Frame frame, int blockId, int index) {
initializePredecessors();
initializeDebugInfo();
HashMap<LLVMSourceSymbol, List<LocalVarDebugInfo>> info = applyBlockInfo(blockEntryDebugInfo.get(blockId), blockId, index);
HashMap<LLVMSourceSymbol, Object> values = new HashMap<>();
for (Map.Entry<LLVMSourceSymbol, List<LocalVarDebugInfo>> entry : info.entrySet()) {
LLVMDebugObjectBuilder builder = null;
for (LocalVarDebugInfo di : entry.getValue()) {
builder = di.process(builder, frame);
}
values.put(entry.getKey(), builder.getValue(entry.getKey()));
}
return values;
}
private void initializePredecessors() {
if (predecessors == null) {
@SuppressWarnings({"unchecked", "rawtypes"})
ArrayList<Integer>[] result = new ArrayList[infos.length];
for (int i = 0; i < infos.length; i++) {
result[i] = new ArrayList<>(2);
}
for (LLVMBasicBlockNode b : blocks) {
for (int successor : b.getTerminatingInstruction().getSuccessors()) {
if (successor >= 0) {
result[successor].add(b.getBlockId());
}
}
}
predecessors = result;
}
}
private void initializeDebugInfo() {
if (blockEntryDebugInfo == null) {
ArrayList<HashMap<LLVMSourceSymbol, List<LocalVarDebugInfo>>> result = new ArrayList<>();
for (int i = 0; i < infos.length; i++) {
result.add(new HashMap<>());
}
boolean changed;
do {
changed = false;
for (int i = 0; i < infos.length; i++) {
changed = changed | merge(i, result, predecessors[i], true);
}
} while (changed);
do {
changed = false;
for (int i = 0; i < infos.length; i++) {
changed = changed | merge(i, result, predecessors[i], false);
}
} while (changed);
blockEntryDebugInfo = result;
}
}
private boolean merge(int blockId, List<HashMap<LLVMSourceSymbol, List<LocalVarDebugInfo>>> entryDebugInfo, ArrayList<Integer> preds, boolean propagate) {
if (preds.isEmpty()) {
return false;
}
HashMap<LLVMSourceSymbol, List<LocalVarDebugInfo>> result = applyBlockInfo(entryDebugInfo.get(preds.get(0)), preds.get(0), Integer.MAX_VALUE);
for (int i = 1; i < preds.size(); i++) {
HashMap<LLVMSourceSymbol, List<LocalVarDebugInfo>> variables = applyBlockInfo(entryDebugInfo.get(preds.get(i)), preds.get(i), Integer.MAX_VALUE);
Iterator<Entry<LLVMSourceSymbol, List<LocalVarDebugInfo>>> iterator = variables.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<LLVMSourceSymbol, List<LocalVarDebugInfo>> existing = iterator.next();
if (propagate) {
if (!result.containsKey(existing.getKey())) {
result.put(existing.getKey(), existing.getValue());
}
} else {
if (!Objects.equals(variables.get(existing.getKey()), existing.getValue())) {
iterator.remove();
}
}
}
}
HashMap<LLVMSourceSymbol, List<LocalVarDebugInfo>> old = entryDebugInfo.get(blockId);
entryDebugInfo.set(blockId, result);
return !old.equals(result);
}
}