package com.oracle.truffle.llvm.parser;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInterface;
import com.oracle.truffle.api.nodes.RepeatingNode;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.llvm.parser.LLVMLivenessAnalysis.LLVMLivenessAnalysisResult;
import com.oracle.truffle.llvm.parser.LLVMPhiManager.Phi;
import com.oracle.truffle.llvm.parser.metadata.debuginfo.DebugInfoFunctionProcessor;
import com.oracle.truffle.llvm.parser.metadata.debuginfo.SourceVariable;
import com.oracle.truffle.llvm.parser.model.SymbolImpl;
import com.oracle.truffle.llvm.parser.model.attributes.Attribute;
import com.oracle.truffle.llvm.parser.model.attributes.Attribute.Kind;
import com.oracle.truffle.llvm.parser.model.attributes.Attribute.KnownAttribute;
import com.oracle.truffle.llvm.parser.model.blocks.InstructionBlock;
import com.oracle.truffle.llvm.parser.model.functions.FunctionDefinition;
import com.oracle.truffle.llvm.parser.model.functions.FunctionParameter;
import com.oracle.truffle.llvm.parser.model.functions.LazyFunctionParser;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.DbgDeclareInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.DbgValueInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.Instruction;
import com.oracle.truffle.llvm.parser.nodes.LLVMBitcodeInstructionVisitor;
import com.oracle.truffle.llvm.parser.nodes.LLVMRuntimeDebugInformation;
import com.oracle.truffle.llvm.parser.nodes.LLVMSymbolReadResolver;
import com.oracle.truffle.llvm.parser.util.LLVMControlFlowGraph;
import com.oracle.truffle.llvm.parser.util.LLVMControlFlowGraph.CFGBlock;
import com.oracle.truffle.llvm.parser.util.LLVMControlFlowGraph.CFGLoop;
import com.oracle.truffle.llvm.runtime.CommonNodeFactory;
import com.oracle.truffle.llvm.runtime.GetStackSpaceFactory;
import com.oracle.truffle.llvm.runtime.LLVMContext;
import com.oracle.truffle.llvm.runtime.LLVMFunctionCode.LazyToTruffleConverter;
import com.oracle.truffle.llvm.runtime.LLVMLanguage;
import com.oracle.truffle.llvm.runtime.NodeFactory;
import com.oracle.truffle.llvm.runtime.datalayout.DataLayout;
import com.oracle.truffle.llvm.runtime.debug.scope.LLVMSourceLocation;
import com.oracle.truffle.llvm.runtime.debug.type.LLVMSourceFunctionType;
import com.oracle.truffle.llvm.runtime.except.LLVMUserException;
import com.oracle.truffle.llvm.runtime.memory.LLVMStack.UniquesRegion;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMControlFlowNode;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMExpressionNode;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMStatementNode;
import com.oracle.truffle.llvm.runtime.nodes.base.LLVMBasicBlockNode;
import com.oracle.truffle.llvm.runtime.nodes.memory.LLVMUnpackVarargsNodeGen;
import com.oracle.truffle.llvm.runtime.options.SulongEngineOption;
import com.oracle.truffle.llvm.runtime.types.AggregateType;
import com.oracle.truffle.llvm.runtime.types.ArrayType;
import com.oracle.truffle.llvm.runtime.types.PointerType;
import com.oracle.truffle.llvm.runtime.types.PrimitiveType;
import com.oracle.truffle.llvm.runtime.types.StructureType;
import com.oracle.truffle.llvm.runtime.types.Type;
import com.oracle.truffle.llvm.runtime.types.symbols.SSAValue;
import org.graalvm.options.OptionValues;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
public class LazyToTruffleConverterImpl implements LazyToTruffleConverter {
private static final String LOOP_SUCCESSOR_FRAME_ID = "<loop successor>";
private final LLVMParserRuntime runtime;
private final FunctionDefinition method;
private final Source source;
private final LazyFunctionParser parser;
private final DebugInfoFunctionProcessor diProcessor;
private final DataLayout dataLayout;
private RootCallTarget resolved;
LazyToTruffleConverterImpl(LLVMParserRuntime runtime, FunctionDefinition method, Source source, LazyFunctionParser parser,
DebugInfoFunctionProcessor diProcessor, DataLayout dataLayout) {
this.runtime = runtime;
this.method = method;
this.source = source;
this.parser = parser;
this.diProcessor = diProcessor;
this.resolved = null;
this.dataLayout = dataLayout;
}
@Override
public RootCallTarget convert() {
CompilerAsserts.neverPartOfCompilation();
synchronized (this) {
if (resolved == null) {
resolved = generateCallTarget();
}
return resolved;
}
}
private RootCallTarget generateCallTarget() {
LLVMContext context = LLVMLanguage.getContext();
NodeFactory nodeFactory = runtime.getNodeFactory();
OptionValues options = context.getEnv().getOptions();
String printASTOption = options.get(SulongEngineOption.PRINT_AST);
boolean printAST = false;
if (!printASTOption.isEmpty()) {
String[] regexes = printASTOption.split(",");
for (String regex : regexes) {
if (method.getName().matches(regex)) {
printAST = true;
System.out.println("\n========== " + method.getName() + "\n");
break;
}
}
}
parser.parse(diProcessor, source, runtime, LLVMLanguage.getContext());
final Map<InstructionBlock, List<Phi>> phis = LLVMPhiManager.getPhis(method);
LLVMLivenessAnalysisResult liveness = LLVMLivenessAnalysis.computeLiveness(phis, method, LLVMLanguage.getContext().lifetimeAnalysisStream());
FrameDescriptor frame = new FrameDescriptor();
UniquesRegion uniquesRegion = new UniquesRegion();
GetStackSpaceFactory getStackSpaceFactory = GetStackSpaceFactory.createGetUniqueStackSpaceFactory(uniquesRegion, frame);
LLVMSymbolReadResolver symbols = new LLVMSymbolReadResolver(runtime, frame, getStackSpaceFactory, dataLayout, options.get(SulongEngineOption.LL_DEBUG));
frame.addFrameSlot(LLVMUserException.FRAME_SLOT_ID, null, FrameSlotKind.Object);
for (FunctionParameter parameter : method.getParameters()) {
symbols.findOrAddFrameSlot(frame, parameter);
}
HashSet<Integer> neededForDebug = getDebugValues();
boolean initDebugValues = true;
LLVMRuntimeDebugInformation info = new LLVMRuntimeDebugInformation(method.getBlocks().size());
LLVMBasicBlockNode[] blockNodes = new LLVMBasicBlockNode[method.getBlocks().size()];
for (InstructionBlock block : method.getBlocks()) {
List<Phi> blockPhis = phis.get(block);
ArrayList<LLVMLivenessAnalysis.NullerInformation> blockNullerInfos = liveness.getNullableWithinBlock()[block.getBlockIndex()];
LLVMBitcodeInstructionVisitor visitor = new LLVMBitcodeInstructionVisitor(frame, uniquesRegion, blockPhis, method.getParameters().size(), symbols, context,
blockNullerInfos, neededForDebug, dataLayout, nodeFactory);
if (initDebugValues) {
for (SourceVariable variable : method.getSourceFunction().getVariables()) {
if (variable.hasFragments()) {
visitor.initializeAggregateLocalVariable(variable);
}
}
initDebugValues = false;
}
for (int i = 0; i < block.getInstructionCount(); i++) {
visitor.setInstructionIndex(i);
block.getInstruction(i).accept(visitor);
}
LLVMStatementNode[] nodes = visitor.finish();
info.setBlockDebugInfo(block.getBlockIndex(), visitor.getDebugInfo());
blockNodes[block.getBlockIndex()] = LLVMBasicBlockNode.createBasicBlockNode(options, nodes, visitor.getControlFlowNode(), block.getBlockIndex(), block.getName());
}
for (int j = 0; j < blockNodes.length; j++) {
FrameSlot[] nullableBeforeBlock = getNullableFrameSlots(frame, liveness.getNullableBeforeBlock()[j]);
FrameSlot[] nullableAfterBlock = getNullableFrameSlots(frame, liveness.getNullableAfterBlock()[j]);
blockNodes[j].setNullableFrameSlots(nullableBeforeBlock, nullableAfterBlock);
}
info.setBlocks(blockNodes);
FrameSlot loopSuccessorSlot = null;
if (options.get(SulongEngineOption.ENABLE_OSR)) {
LLVMControlFlowGraph cfg = new LLVMControlFlowGraph(method.getBlocks().toArray(FunctionDefinition.EMPTY));
cfg.build();
if (cfg.isReducible() && cfg.getCFGLoops().size() > 0) {
loopSuccessorSlot = frame.addFrameSlot(LOOP_SUCCESSOR_FRAME_ID, FrameSlotKind.Int);
resolveLoops(blockNodes, cfg, frame, loopSuccessorSlot, info, options);
}
}
LLVMSourceLocation location = method.getLexicalScope();
LLVMStatementNode[] copyArgumentsToFrameArray = copyArgumentsToFrame(frame, symbols).toArray(LLVMStatementNode.NO_STATEMENTS);
RootNode rootNode = nodeFactory.createFunction(frame.findFrameSlot(LLVMUserException.FRAME_SLOT_ID), blockNodes, uniquesRegion, copyArgumentsToFrameArray, frame, loopSuccessorSlot, info,
method.getName(), method.getSourceName(), method.getParameters().size(), source, location);
method.onAfterParse();
if (printAST) {
printCompactTree(rootNode);
System.out.println();
}
return Truffle.getRuntime().createCallTarget(rootNode);
}
private HashSet<Integer> getDebugValues() {
HashSet<Integer> neededForDebug = new HashSet<>();
for (InstructionBlock block : method.getBlocks()) {
for (Instruction instruction : block.getInstructions()) {
SymbolImpl value;
if (instruction instanceof DbgValueInstruction) {
value = ((DbgValueInstruction) instruction).getValue();
} else if (instruction instanceof DbgDeclareInstruction) {
value = ((DbgDeclareInstruction) instruction).getValue();
} else {
continue;
}
if (value instanceof SSAValue) {
neededForDebug.add(((SSAValue) value).getFrameIdentifier());
}
}
}
return neededForDebug;
}
private static void printCompactTree(Node node) {
printCompactTree(new PrintWriter(System.out), null, node, null, 1);
}
private static void printCompactTree(PrintWriter p, NodeInterface parent, NodeInterface node, String fieldName, int level) {
if (node == null) {
return;
}
for (int i = 0; i < level; i++) {
p.print(" ");
}
if (parent == null) {
p.println(node);
} else {
p.print(fieldName);
p.print(" = ");
p.println(node);
}
for (Class<?> c = node.getClass(); c != Object.class; c = c.getSuperclass()) {
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (NodeInterface.class.isAssignableFrom(field.getType())) {
try {
field.setAccessible(true);
NodeInterface value = (NodeInterface) field.get(node);
if (value != null) {
printCompactTree(p, node, value, field.getName(), level + 1);
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
} else if (NodeInterface[].class.isAssignableFrom(field.getType())) {
try {
field.setAccessible(true);
NodeInterface[] value = (NodeInterface[]) field.get(node);
if (value != null) {
for (int i = 0; i < value.length; i++) {
printCompactTree(p, node, value[i], field.getName() + "[" + i + "]", level + 1);
}
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
p.flush();
}
private void resolveLoops(LLVMBasicBlockNode[] nodes, LLVMControlFlowGraph cfg, FrameDescriptor frame, FrameSlot loopSuccessorSlot, LLVMRuntimeDebugInformation info, OptionValues options) {
LLVMBasicBlockNode[] originalBodyNodes = nodes.clone();
info.setBlocks(originalBodyNodes);
for (CFGLoop loop : cfg.getCFGLoops()) {
int headerId = loop.getHeader().id;
int[] indexMapping = new int[nodes.length];
Arrays.fill(indexMapping, -1);
List<LLVMStatementNode> bodyNodes = new ArrayList<>();
LLVMBasicBlockNode header = nodes[headerId];
bodyNodes.add(header);
indexMapping[headerId] = 0;
int i = 1;
for (CFGBlock block : loop.getBody()) {
bodyNodes.add(nodes[block.id]);
indexMapping[block.id] = i++;
}
int[] loopSuccessors = loop.getSuccessorIDs();
RepeatingNode loopBody = runtime.getNodeFactory().createLoopDispatchNode(frame.findFrameSlot(LLVMUserException.FRAME_SLOT_ID), Collections.unmodifiableList(bodyNodes),
originalBodyNodes, headerId, indexMapping, loopSuccessors, loopSuccessorSlot);
LLVMControlFlowNode loopNode = runtime.getNodeFactory().createLoop(loopBody, loopSuccessors);
nodes[headerId] = LLVMBasicBlockNode.createBasicBlockNode(options, new LLVMStatementNode[0], loopNode, headerId, "loopAt" + headerId);
nodes[headerId].setNullableFrameSlots(header.nullableBefore, header.nullableAfter);
for (CFGLoop innerLoop : loop.getInnerLoops()) {
nodes[innerLoop.getHeader().id] = null;
}
}
}
@Override
public LLVMSourceFunctionType getSourceType() {
convert();
return method.getSourceFunction().getSourceType();
}
private static FrameSlot[] getNullableFrameSlots(FrameDescriptor frame, BitSet nullable) {
int bitIndex = -1;
ArrayList<FrameSlot> nullableSlots = new ArrayList<>();
while ((bitIndex = nullable.nextSetBit(bitIndex + 1)) >= 0) {
int frameIdentifier = bitIndex;
nullableSlots.add(LLVMBitcodeInstructionVisitor.findFrameSlot(frame, frameIdentifier));
}
if (nullableSlots.size() > 0) {
return nullableSlots.toArray(new FrameSlot[nullableSlots.size()]);
} else {
return null;
}
}
private static boolean functionParameterHasByValueAttribute(FunctionParameter parameter) {
if (parameter.getParameterAttribute() != null) {
for (Attribute a : parameter.getParameterAttribute().getAttributes()) {
if (a instanceof KnownAttribute && ((KnownAttribute) a).getAttr() == Kind.BYVAL) {
return true;
}
}
}
return false;
}
private LLVMExpressionNode getTargetAddress(LLVMExpressionNode baseAddress, Type sourceType, ArrayDeque<Long> indices) {
NodeFactory nf = runtime.getNodeFactory();
int indicesSize = indices.size();
Long[] indicesArr = new Long[indicesSize];
LLVMExpressionNode[] indexNodes = new LLVMExpressionNode[indicesSize];
int i = indicesSize - 1;
for (Long idx : indices) {
indicesArr[i] = idx;
indexNodes[i] = CommonNodeFactory.createLiteral(idx.longValue(), PrimitiveType.I64);
i--;
}
assert i == -1;
PrimitiveType[] indexTypes = new PrimitiveType[indicesSize];
Arrays.fill(indexTypes, PrimitiveType.I64);
LLVMExpressionNode nestedGEPs = CommonNodeFactory.createNestedElementPointerNode(
nf,
dataLayout,
indexNodes,
indicesArr,
indexTypes,
baseAddress,
sourceType);
return nestedGEPs;
}
private void copyStructArgumentsToFrame(List<LLVMStatementNode> initializers, NodeFactory nodeFactory, FrameSlot slot, int argIndex, PointerType topLevelPointerType, Type currentType,
ArrayDeque<Long> indices) {
if (currentType instanceof StructureType || currentType instanceof ArrayType) {
AggregateType t = (AggregateType) currentType;
for (long i = 0; i < t.getNumberOfElements(); i++) {
indices.push(i);
copyStructArgumentsToFrame(initializers, nodeFactory, slot, argIndex, topLevelPointerType, t.getElementType(i), indices);
indices.pop();
}
} else {
LLVMExpressionNode targetAddress = getTargetAddress(CommonNodeFactory.createFrameRead(topLevelPointerType, slot), topLevelPointerType.getPointeeType(), indices);
LLVMExpressionNode argMaybeUnpack = LLVMUnpackVarargsNodeGen.create(nodeFactory.createFunctionArgNode(argIndex, topLevelPointerType));
LLVMExpressionNode sourceAddress = getTargetAddress(argMaybeUnpack, topLevelPointerType.getPointeeType(), indices);
LLVMExpressionNode sourceLoadNode = nodeFactory.createLoad(currentType, sourceAddress);
LLVMStatementNode storeNode = nodeFactory.createStore(targetAddress, sourceLoadNode, currentType);
initializers.add(storeNode);
}
}
private List<LLVMStatementNode> copyArgumentsToFrame(FrameDescriptor frame, LLVMSymbolReadResolver symbols) {
NodeFactory nodeFactory = runtime.getNodeFactory();
List<FunctionParameter> parameters = method.getParameters();
List<LLVMStatementNode> formalParamInits = new ArrayList<>();
int argIndex = 1;
if (method.getType().getReturnType() instanceof StructureType) {
argIndex++;
}
for (FunctionParameter parameter : parameters) {
FrameSlot slot = symbols.findOrAddFrameSlot(frame, parameter);
if (parameter.getType() instanceof PointerType && functionParameterHasByValueAttribute(parameter)) {
PointerType pointerType = (PointerType) parameter.getType();
Type pointeeType = pointerType.getPointeeType();
GetStackSpaceFactory allocaFactory = GetStackSpaceFactory.createAllocaFactory();
LLVMExpressionNode allocation = allocaFactory.createGetStackSpace(nodeFactory, pointeeType);
formalParamInits.add(nodeFactory.createFrameWrite(pointerType, allocation, slot));
ArrayDeque<Long> indices = new ArrayDeque<>();
copyStructArgumentsToFrame(formalParamInits, nodeFactory, slot, argIndex++, pointerType, pointeeType, indices);
} else {
LLVMExpressionNode parameterNode = nodeFactory.createFunctionArgNode(argIndex++, parameter.getType());
formalParamInits.add(nodeFactory.createFrameWrite(parameter.getType(), parameterNode, slot));
}
}
return formalParamInits;
}
}