/*
* Copyright (c) 2016, Oracle and/or its affiliates.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.truffle.llvm.parser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.interop.ForeignAccess;
import com.oracle.truffle.api.interop.Message;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.llvm.parser.datalayout.DataLayoutConverter;
import com.oracle.truffle.llvm.parser.metadata.SourceSectionGenerator;
import com.oracle.truffle.llvm.parser.model.ModelModule;
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.globals.GlobalAlias;
import com.oracle.truffle.llvm.parser.model.globals.GlobalConstant;
import com.oracle.truffle.llvm.parser.model.globals.GlobalValueSymbol;
import com.oracle.truffle.llvm.parser.model.globals.GlobalVariable;
import com.oracle.truffle.llvm.parser.model.symbols.constants.aggregate.ArrayConstant;
import com.oracle.truffle.llvm.parser.model.symbols.constants.aggregate.StructureConstant;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.Instruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.ValueInstruction;
import com.oracle.truffle.llvm.parser.model.target.TargetDataLayout;
import com.oracle.truffle.llvm.parser.model.visitors.ValueInstructionVisitor;
import com.oracle.truffle.llvm.parser.nodes.LLVMSymbolResolver;
import com.oracle.truffle.llvm.parser.util.LLVMParserAsserts;
import com.oracle.truffle.llvm.parser.util.Pair;
import com.oracle.truffle.llvm.runtime.LLVMContext;
import com.oracle.truffle.llvm.runtime.LLVMException;
import com.oracle.truffle.llvm.runtime.LLVMFunctionDescriptor;
import com.oracle.truffle.llvm.runtime.LLVMLanguage;
import com.oracle.truffle.llvm.runtime.LLVMLogger;
import com.oracle.truffle.llvm.runtime.memory.LLVMNativeFunctions;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMExpressionNode;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMStackFrameNuller;
import com.oracle.truffle.llvm.runtime.options.LLVMOptions;
import com.oracle.truffle.llvm.runtime.types.AggregateType;
import com.oracle.truffle.llvm.runtime.types.ArrayType;
import com.oracle.truffle.llvm.runtime.types.FunctionType;
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.Symbol;
public final class LLVMParserRuntime {
public static LLVMParserResult parse(Source source, LLVMLanguage language, LLVMContext context, SulongNodeFactory nodeFactory) {
final BitcodeParserResult parserResult = BitcodeParserResult.getFromSource(source);
final ModelModule model = parserResult.getModel();
final StackAllocation stackAllocation = parserResult.getStackAllocation();
final TargetDataLayout layout = model.getTargetDataLayout();
assert layout != null;
final DataLayoutConverter.DataSpecConverterImpl targetDataLayout = DataLayoutConverter.getConverter(layout.getDataLayout());
final LLVMParserRuntime visitor = new LLVMParserRuntime(source, language, context, stackAllocation, parserResult.getLabels(), parserResult.getPhis(), targetDataLayout, nodeFactory);
final LLVMModelVisitor module = new LLVMModelVisitor(visitor);
if (!LLVMLogger.TARGET_NONE.equals(LLVMOptions.DEBUG.printMetadata())) {
model.getMetadata().print(LLVMLogger.print(LLVMOptions.DEBUG.printMetadata()));
}
model.accept(module);
LLVMFunctionDescriptor mainFunction = visitor.getFunction("@main");
LLVMExpressionNode[] globals = visitor.getGobalVariables().toArray(new LLVMExpressionNode[0]);
RootNode globalVarInits = nodeFactory.createStaticInitsRootNode(visitor, globals);
RootCallTarget globalVarInitsTarget = Truffle.getRuntime().createCallTarget(globalVarInits);
LLVMExpressionNode[] deallocs = visitor.getDeallocations();
RootNode globalVarDeallocs = nodeFactory.createStaticInitsRootNode(visitor, deallocs);
RootCallTarget globalVarDeallocsTarget = Truffle.getRuntime().createCallTarget(globalVarDeallocs);
final List<RootCallTarget> constructorFunctions = visitor.getConstructors();
final List<RootCallTarget> destructorFunctions = visitor.getDestructors();
final RootCallTarget mainFunctionCallTarget;
if (mainFunction != null) {
final RootCallTarget mainCallTarget = mainFunction.getLLVMIRFunction();
final RootNode globalFunction = nodeFactory.createGlobalRootNode(visitor, mainCallTarget, context.getMainArguments(), source, mainFunction.getType().getArgumentTypes());
final RootCallTarget globalFunctionRoot = Truffle.getRuntime().createCallTarget(globalFunction);
final RootNode globalRootNode = nodeFactory.createGlobalRootNodeWrapping(visitor, globalFunctionRoot, mainFunction.getType().getReturnType());
mainFunctionCallTarget = Truffle.getRuntime().createCallTarget(globalRootNode);
} else {
mainFunctionCallTarget = null;
}
return new LLVMParserResult(mainFunctionCallTarget, globalVarInitsTarget, globalVarDeallocsTarget, constructorFunctions, destructorFunctions);
}
private final LLVMContext context;
private final LLVMLanguage language;
private final LLVMLabelList labels;
private final LLVMPhiManager phis;
private final List<LLVMExpressionNode> deallocations = new ArrayList<>();
private final Map<GlobalAlias, Symbol> aliases = new HashMap<>();
private final Map<String, LLVMFunctionDescriptor> functions = new HashMap<>();
private final Map<GlobalValueSymbol, LLVMExpressionNode> globals = new HashMap<>();
private final DataLayoutConverter.DataSpecConverterImpl targetDataLayout;
private final SulongNodeFactory nodeFactory;
private final Source source;
private final StackAllocation stack;
private final LLVMSymbolResolver symbolResolver;
private final SourceSectionGenerator sourceSectionGenerator;
private LLVMParserRuntime(Source source, LLVMLanguage language, LLVMContext context, StackAllocation stack, LLVMLabelList labels, LLVMPhiManager phis,
DataLayoutConverter.DataSpecConverterImpl layout, SulongNodeFactory nodeFactory) {
this.source = source;
this.context = context;
this.stack = stack;
this.labels = labels;
this.phis = phis;
this.targetDataLayout = layout;
this.nodeFactory = nodeFactory;
this.language = language;
this.symbolResolver = new LLVMSymbolResolver(labels, this);
this.sourceSectionGenerator = new SourceSectionGenerator();
}
LLVMExpressionNode createFunction(FunctionDefinition method, FrameSlot exceptionValueSlot, LLVMLifetimeAnalysis lifetimes) {
String functionName = method.getName();
LLVMBitcodeFunctionVisitor visitor = new LLVMBitcodeFunctionVisitor(
this,
stack.getFrame(functionName),
labels.labels(functionName),
phis.getPhiMap(functionName),
nodeFactory,
method.getParameters().size(),
symbolResolver,
method);
initFunction(visitor);
method.accept(visitor);
final LLVMStackFrameNuller[][] slotNullerBeginNodes = getSlotNuller(method, lifetimes.getNullableBefore());
final LLVMStackFrameNuller[][] slotNullerAfterNodes = getSlotNuller(method, lifetimes.getNullableAfter());
return nodeFactory.createFunctionBlockNode(this, exceptionValueSlot, visitor.getBlocks(), slotNullerBeginNodes, slotNullerAfterNodes);
}
private LLVMStackFrameNuller[][] getSlotNuller(FunctionDefinition method, Map<InstructionBlock, FrameSlot[]> slots) {
final LLVMStackFrameNuller[][] indexToSlotNuller = new LLVMStackFrameNuller[method.getBlockCount()][];
for (int j = 0; j < method.getBlockCount(); j++) {
final InstructionBlock block = method.getBlock(j);
final FrameSlot[] deadSlots = slots.get(block);
if (deadSlots != null) {
LLVMParserAsserts.assertNoNullElement(deadSlots);
indexToSlotNuller[j] = getSlotNullerNode(deadSlots, method);
}
}
return indexToSlotNuller;
}
private LLVMStackFrameNuller[] getSlotNullerNode(FrameSlot[] deadSlots, FunctionDefinition method) {
if (deadSlots == null) {
return new LLVMStackFrameNuller[0];
}
final LLVMStackFrameNuller[] nullers = new LLVMStackFrameNuller[deadSlots.length];
int i = 0;
for (FrameSlot slot : deadSlots) {
nullers[i++] = getNullerNode(slot, method);
}
LLVMParserAsserts.assertNoNullElement(nullers);
return nullers;
}
private LLVMStackFrameNuller getNullerNode(FrameSlot slot, FunctionDefinition method) {
final String identifier = (String) slot.getIdentifier();
final Type type = findType(method, identifier);
return nodeFactory.createFrameNuller(this, identifier, type, slot);
}
List<LLVMExpressionNode> copyArgumentsToFrame(FrameDescriptor frame, FunctionDefinition method) {
final List<FunctionParameter> parameters = method.getParameters();
final List<LLVMExpressionNode> formalParamInits = new ArrayList<>();
int argIndex = 0;
if (method.getType().getReturnType() instanceof StructureType) {
argIndex++;
}
for (final FunctionParameter parameter : parameters) {
final LLVMExpressionNode parameterNode = nodeFactory.createFunctionArgNode(argIndex++, parameter.getType());
final FrameSlot slot = frame.findFrameSlot(parameter.getName());
formalParamInits.add(nodeFactory.createFrameWrite(this, parameter.getType(), parameterNode, slot, null));
}
return formalParamInits;
}
private LLVMExpressionNode createGlobal(GlobalValueSymbol global) {
if (global == null || global.getValue() == null) {
return null;
}
LLVMExpressionNode constant = symbolResolver.resolve(global.getValue());
if (constant != null) {
final Type type = ((PointerType) global.getType()).getPointeeType();
final int size = getByteSize(type);
final LLVMExpressionNode globalVarAddress = getGlobalVariable(global);
if (size != 0) {
final LLVMExpressionNode store;
if (type instanceof ArrayType || type instanceof StructureType) {
store = nodeFactory.createStore(this, globalVarAddress, constant, type, null);
} else {
final Type t = global.getValue().getType();
store = nodeFactory.createStore(this, globalVarAddress, constant, t, null);
}
return store;
}
}
return null;
}
public LLVMContext getContext() {
return context;
}
private LLVMExpressionNode[] getDeallocations() {
return deallocations.toArray(new LLVMExpressionNode[deallocations.size()]);
}
private LLVMFunctionDescriptor getFunction(String name) {
return functions.get(name);
}
void addFunction(LLVMFunctionDescriptor function) {
functions.put(function.getName(), function);
}
private LLVMExpressionNode getGlobalVariable(GlobalValueSymbol global) {
Symbol g = global;
while (g instanceof GlobalAlias) {
g = aliases.get(g);
}
if (g instanceof GlobalValueSymbol) {
final GlobalValueSymbol variable = (GlobalValueSymbol) g;
return globals.computeIfAbsent(variable, k -> allocateGlobal(variable));
} else {
return symbolResolver.resolve(g);
}
}
private static final String CONSTRUCTORS_VARNAME = "@llvm.global_ctors";
private static final Comparator<Pair<Integer, ?>> ASCENDING_PRIORITY = (p1, p2) -> p1.getFirst() >= p2.getFirst() ? 1 : -1;
private List<RootCallTarget> getConstructors() {
return getStructor(CONSTRUCTORS_VARNAME, ASCENDING_PRIORITY);
}
private static final String DESTRUCTORS_VARNAME = "@llvm.global_dtors";
private static final Comparator<Pair<Integer, ?>> DESCENDING_PRIORITY = (p1, p2) -> p1.getFirst() < p2.getFirst() ? 1 : -1;
private List<RootCallTarget> getDestructors() {
return getStructor(DESTRUCTORS_VARNAME, DESCENDING_PRIORITY);
}
private List<RootCallTarget> getStructor(String name, Comparator<Pair<Integer, ?>> priorityComparator) {
for (GlobalValueSymbol globalValueSymbol : globals.keySet()) {
if (globalValueSymbol.getName().equals(name)) {
final LLVMExpressionNode[] targets = resolveStructor(globalValueSymbol, priorityComparator);
final RootCallTarget constructorFunctionsRootCallTarget = Truffle.getRuntime().createCallTarget(nodeFactory.createStaticInitsRootNode(this, targets));
return Collections.singletonList(constructorFunctionsRootCallTarget);
}
}
return Collections.emptyList();
}
private static final int LEAST_CONSTRUCTOR_PRIORITY = 65535;
private LLVMExpressionNode[] resolveStructor(GlobalValueSymbol globalVar, Comparator<Pair<Integer, ?>> priorityComparator) {
final Object globalVariableDescriptor = globalVariableScope.get(globalVar.getName());
final ArrayConstant arrayConstant = (ArrayConstant) globalVar.getValue();
final int elemCount = arrayConstant.getElementCount();
final StructureType elementType = (StructureType) arrayConstant.getType().getElementType();
final int structSize = getByteSize(elementType);
final FunctionType functionType = (FunctionType) ((PointerType) elementType.getElementType(1)).getPointeeType();
final int indexedTypeLength = getByteAlignment(functionType);
final ArrayList<Pair<Integer, LLVMExpressionNode>> structors = new ArrayList<>(elemCount);
for (int i = 0; i < elemCount; i++) {
final LLVMExpressionNode globalVarAddress = nodeFactory.createLiteral(this, globalVariableDescriptor, new PointerType(globalVar.getType()));
final LLVMExpressionNode iNode = nodeFactory.createLiteral(this, i, PrimitiveType.I32);
final LLVMExpressionNode structPointer = nodeFactory.createTypedElementPointer(this, globalVarAddress, iNode, structSize, elementType);
final LLVMExpressionNode loadedStruct = nodeFactory.createLoad(this, elementType, structPointer);
final LLVMExpressionNode oneLiteralNode = nodeFactory.createLiteral(this, 1, PrimitiveType.I32);
final LLVMExpressionNode functionLoadTarget = nodeFactory.createTypedElementPointer(this, loadedStruct, oneLiteralNode, indexedTypeLength, functionType);
final LLVMExpressionNode loadedFunction = nodeFactory.createLoad(this, functionType, functionLoadTarget);
final LLVMExpressionNode[] argNodes = new LLVMExpressionNode[]{};
final LLVMExpressionNode functionCall = nodeFactory.createFunctionCall(this, loadedFunction, argNodes, functionType, null);
final StructureConstant structorDefinition = (StructureConstant) arrayConstant.getElement(i);
final Symbol prioritySymbol = structorDefinition.getElement(0);
final Integer priority = LLVMSymbolResolver.evaluateIntegerConstant(prioritySymbol);
structors.add(new Pair<>(priority != null ? priority : LEAST_CONSTRUCTOR_PRIORITY, functionCall));
}
return structors.stream().sorted(priorityComparator).map(Pair::getSecond).toArray(LLVMExpressionNode[]::new);
}
private final Map<String, Object> globalVariableScope = new HashMap<>();
private LLVMExpressionNode allocateGlobal(GlobalValueSymbol global) {
final Object globalValue;
if (global instanceof GlobalVariable) {
globalValue = nodeFactory.allocateGlobalVariable(this, (GlobalVariable) global);
} else if (global instanceof GlobalConstant) {
globalValue = nodeFactory.allocateGlobalConstant(this, (GlobalConstant) global);
} else {
throw new AssertionError("Cannot allocate global: " + global);
}
globalVariableScope.put(global.getName(), globalValue);
return nodeFactory.createLiteral(this, globalValue, new PointerType(global.getType()));
}
private List<LLVMExpressionNode> getGobalVariables() {
final List<LLVMExpressionNode> globalNodes = new ArrayList<>();
for (GlobalValueSymbol global : this.globals.keySet()) {
final LLVMExpressionNode store = createGlobal(global);
if (store != null) {
globalNodes.add(store);
}
}
return globalNodes;
}
private static Type findType(FunctionDefinition method, String identifier) {
final Type methodType = method.getType(identifier);
if (methodType != null) {
return methodType;
} else {
throw new IllegalStateException("Cannot find Instruction with name: " + identifier);
}
}
private LLVMBitcodeFunctionVisitor functionVisitor = null;
private final Map<String, Type> nameToTypeMapping = new HashMap<>();
private final ValueInstructionVisitor nameToTypeMappingVisitor = new ValueInstructionVisitor() {
@Override
public void visitValueInstruction(ValueInstruction valueInstruction) {
nameToTypeMapping.put(valueInstruction.getName(), valueInstruction.getType());
}
};
private void initFunction(LLVMBitcodeFunctionVisitor visitor) {
this.functionVisitor = visitor;
nameToTypeMapping.clear();
nameToTypeMapping.put(LLVMException.FRAME_SLOT_ID, new PointerType(null));
if (visitor != null) {
for (int i = 0; i < visitor.getFunction().getBlockCount(); i++) {
visitor.getFunction().getBlock(i).accept(nameToTypeMappingVisitor);
}
visitor.getFunction().getParameters().forEach(p -> nameToTypeMapping.put(p.getName(), p.getType()));
}
}
void exitFunction() {
this.functionVisitor = null;
nameToTypeMapping.clear();
}
public LLVMExpressionNode allocateFunctionLifetime(Type type, int size, int alignment) {
return nodeFactory.createAlloc(this, type, size, alignment, null, null);
}
public Object getGlobalAddress(GlobalValueSymbol var) {
return getGlobalVariable(var);
}
public int getByteAlignment(Type type) {
return type.getAlignment(targetDataLayout);
}
public int getByteSize(Type type) {
return type.getSize(targetDataLayout);
}
public int getBytePadding(int offset, Type type) {
return Type.getPadding(offset, type, targetDataLayout);
}
public int getIndexOffset(int index, AggregateType type) {
return type.getOffsetOf(index, targetDataLayout);
}
public FrameDescriptor getGlobalFrameDescriptor() {
return stack.getRootFrame();
}
public FrameDescriptor getMethodFrameDescriptor() {
return functionVisitor.getFrame();
}
public void addDestructor(LLVMExpressionNode destructorNode) {
deallocations.add(destructorNode);
}
public long getNativeHandle(String name) {
CompilerAsserts.neverPartOfCompilation();
try {
return ForeignAccess.sendAsPointer(Message.AS_POINTER.createNode(), context.getNativeLookup().getNativeDataObject(name));
} catch (UnsupportedMessageException e) {
throw new IllegalStateException(e);
}
}
public Map<String, Type> getVariableNameTypesMapping() {
return Collections.unmodifiableMap(nameToTypeMapping);
}
public LLVMNativeFunctions getNativeFunctions() {
return context.getNativeFunctions();
}
public SulongNodeFactory getNodeFactory() {
return nodeFactory;
}
Map<GlobalAlias, Symbol> getAliases() {
return aliases;
}
Map<GlobalValueSymbol, LLVMExpressionNode> getGlobals() {
return globals;
}
StackAllocation getStack() {
return stack;
}
Source getSource() {
return source;
}
LLVMPhiManager getPhis() {
return phis;
}
public LLVMLanguage getLanguage() {
return language;
}
SourceSection getSourceSection(FunctionDefinition function) {
return sourceSectionGenerator.getOrDefault(function, source);
}
SourceSection getSourceSection(Instruction instruction) {
return sourceSectionGenerator.getOrDefault(instruction);
}
}