package com.oracle.truffle.llvm;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.llvm.initialization.LoadModulesNode;
import com.oracle.truffle.llvm.initialization.LoadNativeNode;
import com.oracle.truffle.llvm.parser.LLVMParser;
import com.oracle.truffle.llvm.parser.LLVMParserResult;
import com.oracle.truffle.llvm.parser.LLVMParserRuntime;
import com.oracle.truffle.llvm.parser.binary.BinaryParser;
import com.oracle.truffle.llvm.parser.binary.BinaryParserResult;
import com.oracle.truffle.llvm.parser.model.ModelModule;
import com.oracle.truffle.llvm.parser.model.functions.FunctionSymbol;
import com.oracle.truffle.llvm.parser.model.symbols.globals.GlobalVariable;
import com.oracle.truffle.llvm.parser.model.target.TargetDataLayout;
import com.oracle.truffle.llvm.parser.nodes.LLVMSymbolReadResolver;
import com.oracle.truffle.llvm.parser.scanner.LLVMScanner;
import com.oracle.truffle.llvm.runtime.CommonNodeFactory;
import com.oracle.truffle.llvm.runtime.DefaultLibraryLocator;
import com.oracle.truffle.llvm.runtime.GetStackSpaceFactory;
import com.oracle.truffle.llvm.runtime.LLVMContext;
import com.oracle.truffle.llvm.runtime.LLVMContext.InternalLibraryLocator;
import com.oracle.truffle.llvm.runtime.LLVMFunction;
import com.oracle.truffle.llvm.runtime.LLVMFunctionCode;
import com.oracle.truffle.llvm.runtime.LLVMLanguage;
import com.oracle.truffle.llvm.runtime.LLVMScope;
import com.oracle.truffle.llvm.runtime.LibraryLocator;
import com.oracle.truffle.llvm.runtime.NativeContextExtension;
import com.oracle.truffle.llvm.runtime.NodeFactory;
import com.oracle.truffle.llvm.runtime.PlatformCapability;
import com.oracle.truffle.llvm.runtime.datalayout.DataLayout;
import com.oracle.truffle.llvm.runtime.debug.LLVMSourceContext;
import com.oracle.truffle.llvm.runtime.debug.scope.LLVMSourceFileReference;
import com.oracle.truffle.llvm.runtime.debug.value.LLVMDebugObjectBuilder;
import com.oracle.truffle.llvm.runtime.except.LLVMLinkerException;
import com.oracle.truffle.llvm.runtime.except.LLVMParserException;
import com.oracle.truffle.llvm.runtime.global.LLVMGlobal;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMExpressionNode;
import com.oracle.truffle.llvm.runtime.options.SulongEngineOption;
import org.graalvm.polyglot.io.ByteSequence;
import java.io.IOException;
import java.nio.ByteOrder;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
final class ParserDriver {
public static CallTarget parse(LLVMContext context, AtomicInteger bitcodeID, Source source) {
return new ParserDriver(context, bitcodeID).parseWithDependencies(source);
}
private final LLVMContext context;
private final LLVMLanguage language;
private final AtomicInteger nextFreeBitcodeID;
private final ArrayList<Object> dependencies = new ArrayList<>();
private ParserDriver(LLVMContext context, AtomicInteger moduleID) {
this.context = context;
this.language = context.getLanguage();
this.nextFreeBitcodeID = moduleID;
}
private CallTarget parseWithDependencies(Source source) {
ByteSequence bytes;
if (source.hasBytes()) {
bytes = source.getBytes();
} else if (source.hasCharacters()) {
throw new LLVMParserException("Unexpected character-based source with mime type: " + source.getMimeType());
} else {
throw new LLVMParserException("Should not reach here: Source is neither char-based nor byte-based!");
}
return parseWithDependencies(source, bytes);
}
private CallTarget parseWithDependencies(Source source, ByteSequence bytes) {
insertDefaultDependencies(source.getName());
LLVMParserResult result = parseLibraryWithSource(source, bytes);
if (result == null) {
TruffleFile file = createNativeTruffleFile(source.getName(), source.getPath());
if (file == null) {
return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(0));
}
return createNativeLibraryCallTarget(file);
}
if (context.isInternalLibraryFile(result.getRuntime().getFile())) {
String libraryName = getSimpleLibraryName(source.getName());
language.addInternalFileScope(libraryName, result.getRuntime().getFileScope());
if (libraryName.equals("libsulong")) {
context.addLibsulongDataLayout(result.getDataLayout());
}
resolveRenamedSymbols(result);
}
addExternalSymbolsToScopes(result);
return createLibraryCallTarget(source.getName(), result, source);
}
@TruffleBoundary
private TruffleFile createNativeTruffleFile(String libName, String libPath) {
NativeContextExtension nativeContextExtension = context.getContextExtensionOrNull(NativeContextExtension.class);
if (nativeContextExtension != null) {
TruffleFile file = DefaultLibraryLocator.INSTANCE.locate(context, libName, "<native library>");
if (file == null) {
LibraryLocator.traceDelegateNative(context, libPath);
file = context.getEnv().getInternalTruffleFile(libPath);
}
return file;
}
return null;
}
private void insertDefaultDependencies(String currentLib) {
String[] sulongLibraryNames = language.getCapability(PlatformCapability.class).getSulongDefaultLibraries();
for (String sulongLibraryName : sulongLibraryNames) {
if (!currentLib.equals(sulongLibraryName)) {
TruffleFile file = createTruffleFile(sulongLibraryName, null, InternalLibraryLocator.INSTANCE, "<internal library>");
if (file != null) {
CallTarget calls = language.getCachedLibrary(file.getPath());
if (calls != null) {
dependencies.add(calls);
} else {
Object sourceOrCallTarget = createDependencySource(sulongLibraryName, null, false, file);
if (sourceOrCallTarget != null && !dependencies.contains(sourceOrCallTarget)) {
dependencies.add(sourceOrCallTarget);
}
}
}
}
}
List<String> externals = SulongEngineOption.getPolyglotOptionExternalLibraries(context.getEnv());
for (String externalLibraryName : externals) {
if (!currentLib.equals(externalLibraryName)) {
TruffleFile file = createTruffleFile(externalLibraryName, null, DefaultLibraryLocator.INSTANCE, "<command-line library>");
if (file != null) {
CallTarget calls = language.getCachedLibrary(file.getPath());
if (calls != null) {
dependencies.add(calls);
} else {
Object sourceOrCallTarget = createDependencySource(externalLibraryName, externalLibraryName, true, file);
if (sourceOrCallTarget != null && !dependencies.contains(sourceOrCallTarget)) {
dependencies.add(sourceOrCallTarget);
}
}
}
}
}
}
static final String SULONG_RENAME_MARKER = "___sulong_import_";
static final int SULONG_RENAME_MARKER_LEN = SULONG_RENAME_MARKER.length();
protected void resolveRenamedSymbols(LLVMParserResult parserResult) {
ListIterator<FunctionSymbol> it = parserResult.getExternalFunctions().listIterator();
while (it.hasNext()) {
FunctionSymbol external = it.next();
String name = external.getName();
LLVMScope scope;
String originalName;
String lib;
if (name.startsWith(SULONG_RENAME_MARKER)) {
int idx = name.indexOf('_', SULONG_RENAME_MARKER_LEN);
if (idx > 0) {
lib = name.substring(SULONG_RENAME_MARKER_LEN, idx);
scope = language.getInternalFileScopes(getSimpleLibraryName(lib));
if (scope == null) {
try {
String libName = lib + "." + language.getCapability(PlatformCapability.class).getLibrarySuffix();
TruffleFile file = createTruffleFile(libName, null, InternalLibraryLocator.INSTANCE, "<default bitcode library>");
context.getEnv().parseInternal(Source.newBuilder("llvm", file).internal(context.isInternalLibraryFile(file)).build());
scope = language.getInternalFileScopes(getSimpleLibraryName(lib));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
if (scope == null) {
throw new LLVMLinkerException(String.format("The symbol %s could not be imported because library %s was not found during symbol renaming", external.getName(), lib));
}
originalName = name.substring(idx + 1);
createNewFunction(scope, originalName, parserResult, external, lib, name, it);
}
} else if (CXXDemangler.isRenamedNamespaceSymbol(name)) {
ArrayList<String> namespaces = CXXDemangler.decodeNamespace(name);
lib = CXXDemangler.getAndRemoveLibraryName(namespaces);
scope = language.getInternalFileScopes(getSimpleLibraryName(lib));
if (scope == null) {
try {
String libName = lib + "." + language.getCapability(PlatformCapability.class).getLibrarySuffix();
TruffleFile file = createTruffleFile(libName, null, InternalLibraryLocator.INSTANCE, "<default bitcode library>");
context.getEnv().parseInternal(Source.newBuilder("llvm", file).internal(context.isInternalLibraryFile(file)).build());
scope = language.getInternalFileScopes(getSimpleLibraryName(lib));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
if (scope == null) {
throw new LLVMLinkerException(String.format("The symbol %s could not be imported because library %s was not found during symbol renaming", external.getName(), lib));
}
originalName = CXXDemangler.encodeNamespace(namespaces);
createNewFunction(scope, originalName, parserResult, external, lib, name, it);
}
}
}
private static void createNewFunction(LLVMScope scope, String originalName, LLVMParserResult parserResult, FunctionSymbol external, String lib, String name, ListIterator<FunctionSymbol> it) {
LLVMFunction originalSymbol = scope.getFunction(originalName);
if (originalSymbol == null) {
throw new LLVMLinkerException(
String.format("The symbol %s could not be imported because the symbol %s was not found in library %s", external.getName(), originalName, lib));
}
LLVMFunction newFunction = LLVMFunction.create(name, originalSymbol.getFunction(), originalSymbol.getType(),
parserResult.getRuntime().getBitcodeID(), external.getIndex(), external.isExported(), parserResult.getRuntime().getFile().getPath());
LLVMScope fileScope = parserResult.getRuntime().getFileScope();
fileScope.register(newFunction);
it.remove();
parserResult.getDefinedFunctions().add(external);
}
private static String getSimpleLibraryName(String name) {
int index = name.indexOf(".");
if (index == -1) {
return name;
}
return name.substring(0, index);
}
private LLVMParserResult parseBinary(BinaryParserResult binaryParserResult, TruffleFile file) {
ModelModule module = new ModelModule();
Source source = binaryParserResult.getSource();
LLVMScanner.parseBitcode(binaryParserResult.getBitcode(), module, source);
TargetDataLayout layout = module.getTargetDataLayout();
DataLayout targetDataLayout = new DataLayout(layout.getDataLayout());
if (targetDataLayout.getByteOrder() != ByteOrder.LITTLE_ENDIAN) {
throw new LLVMParserException("Byte order " + targetDataLayout.getByteOrder() + " of file " + source.getPath() + " is not supported");
}
NodeFactory nodeFactory = context.getLanguage().getActiveConfiguration().createNodeFactory(language, targetDataLayout);
LLVMScope fileScope = new LLVMScope();
int bitcodeID = nextFreeBitcodeID.getAndIncrement();
LLVMParserRuntime runtime = new LLVMParserRuntime(fileScope, nodeFactory, bitcodeID, file, source.getName(), getSourceFilesWithChecksums(context.getEnv(), module));
LLVMParser parser = new LLVMParser(source, runtime);
LLVMParserResult result = parser.parse(module, targetDataLayout);
createDebugInfo(module, new LLVMSymbolReadResolver(runtime, new FrameDescriptor(), GetStackSpaceFactory.createAllocaFactory(), targetDataLayout, false));
return result;
}
private static List<LLVMSourceFileReference> getSourceFilesWithChecksums(TruffleLanguage.Env env, ModelModule module) {
if (SulongEngineOption.shouldVerifyCompileUnitChecksums(env)) {
List<LLVMSourceFileReference> sourceWithChecksum = module.getSourceFileReferences().stream().filter(f -> f.getChecksumKind() != LLVMSourceFileReference.ChecksumKind.CSK_None).collect(
Collectors.toList());
if (!sourceWithChecksum.isEmpty()) {
return sourceWithChecksum;
}
}
return null;
}
private void createDebugInfo(ModelModule model, LLVMSymbolReadResolver symbolResolver) {
final LLVMSourceContext sourceContext = context.getSourceContext();
model.getSourceGlobals().forEach((symbol, irValue) -> {
final LLVMExpressionNode node = symbolResolver.resolve(irValue);
final LLVMDebugObjectBuilder value = CommonNodeFactory.createDebugStaticValue(node, irValue instanceof GlobalVariable);
sourceContext.registerStatic(symbol, value);
});
model.getSourceStaticMembers().forEach(((type, symbol) -> {
final LLVMExpressionNode node = symbolResolver.resolve(symbol);
final LLVMDebugObjectBuilder value = CommonNodeFactory.createDebugStaticValue(node, symbol instanceof GlobalVariable);
type.setValue(value);
}));
}
private LLVMParserResult parseLibraryWithSource(Source source, ByteSequence bytes) {
BinaryParserResult binaryParserResult = BinaryParser.parse(bytes, source, context);
if (binaryParserResult != null) {
context.addLibraryPaths(binaryParserResult.getLibraryPaths());
TruffleFile file = createTruffleFile(source.getName(), source.getPath(), binaryParserResult.getLocator(), "<source library>");
processDependencies(source.getName(), file, binaryParserResult);
return parseBinary(binaryParserResult, file);
} else {
LibraryLocator.traceDelegateNative(context, source);
return null;
}
}
private TruffleFile createTruffleFile(String libName, String libPath, LibraryLocator locator, String reason) {
TruffleFile file = locator.locate(context, libName, reason);
if (file == null) {
if (libPath != null) {
file = context.getEnv().getInternalTruffleFile(libPath);
} else {
Path path = Paths.get(libName);
LibraryLocator.traceDelegateNative(context, path);
file = context.getEnv().getInternalTruffleFile(path.toUri());
}
}
return file;
}
private void processDependencies(String libraryName, TruffleFile libFile, BinaryParserResult binaryParserResult) {
for (String lib : context.preprocessDependencies(binaryParserResult.getLibraries(), libFile)) {
if (!libraryName.equals(lib)) {
TruffleFile file = createTruffleFile(lib, null, binaryParserResult.getLocator(), "<dependency library>");
CallTarget calls = language.getCachedLibrary(file.getPath());
if (calls != null && !dependencies.contains(calls)) {
dependencies.add(calls);
} else {
Object sourceOrCallTarget = createDependencySource(lib, lib, true, file);
if (sourceOrCallTarget != null && !dependencies.contains(sourceOrCallTarget)) {
dependencies.add(sourceOrCallTarget);
}
}
}
}
}
private Object createDependencySource(String libName, String libPath, boolean isNative, TruffleFile file) {
assert file != null;
if (!file.isRegularFile()) {
if (!isNative) {
throw new LLVMParserException("'" + file.getName() + "' is not a file or does not exist.");
} else {
TruffleFile nativeFile = createNativeTruffleFile(libName, libPath);
if (nativeFile == null) {
return null;
}
return createNativeLibraryCallTarget(nativeFile);
}
}
Source source;
if (language.containsLibrarySource(file.getPath())) {
source = language.getLibrarySource(file.getPath());
} else {
try {
source = Source.newBuilder("llvm", file).internal(context.isInternalLibraryFile(file)).build();
language.addLibrarySource(file.getPath(), source);
} catch (IOException | SecurityException | OutOfMemoryError ex) {
throw new LLVMParserException("Error reading file " + file.getName() + ".");
}
}
return source;
}
private static void addExternalSymbolsToScopes(LLVMParserResult parserResult) {
LLVMScope fileScope = parserResult.getRuntime().getFileScope();
for (FunctionSymbol function : parserResult.getExternalFunctions()) {
if (!fileScope.contains(function.getName())) {
fileScope.register(LLVMFunction.create(function.getName(), new LLVMFunctionCode.UnresolvedFunction(), function.getType(), parserResult.getRuntime().getBitcodeID(),
function.getIndex(), false, parserResult.getRuntime().getFile().getPath()));
}
}
for (GlobalVariable global : parserResult.getExternalGlobals()) {
if (!fileScope.contains(global.getName())) {
fileScope.register(
LLVMGlobal.create(global.getName(), global.getType(), global.getSourceSymbol(), global.isReadOnly(), global.getIndex(), parserResult.getRuntime().getBitcodeID(),
false));
}
}
}
private CallTarget createLibraryCallTarget(String name, LLVMParserResult parserResult, Source source) {
if (context.getEnv().getOptions().get(SulongEngineOption.PARSE_ONLY)) {
return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(0));
} else {
boolean lazyParsing = context.getEnv().getOptions().get(SulongEngineOption.LAZY_PARSING);
LoadModulesNode loadModules = LoadModulesNode.create(name, parserResult, lazyParsing, context.isInternalLibraryFile(parserResult.getRuntime().getFile()), dependencies, source, language);
return Truffle.getRuntime().createCallTarget(loadModules);
}
}
private CallTarget createNativeLibraryCallTarget(TruffleFile file) {
if (context.getEnv().getOptions().get(SulongEngineOption.PARSE_ONLY)) {
return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(0));
} else {
LoadNativeNode loadNative = LoadNativeNode.create(new FrameDescriptor(), language, file);
return Truffle.getRuntime().createCallTarget(loadNative);
}
}
}