/* * 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; import java.io.File; import java.io.IOException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.ServiceLoader; import java.util.function.Consumer; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.api.vm.PolyglotEngine.Builder; import com.oracle.truffle.llvm.parser.LLVMParserResult; import com.oracle.truffle.llvm.parser.LLVMParserRuntime; import com.oracle.truffle.llvm.parser.SulongNodeFactory; import com.oracle.truffle.llvm.runtime.LLVMContext; 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.options.LLVMOptions; @TruffleLanguage.Registration(name = "Sulong", version = "0.01", mimeType = {Sulong.LLVM_BITCODE_MIME_TYPE, Sulong.LLVM_BITCODE_BASE64_MIME_TYPE, Sulong.SULONG_LIBRARY_MIME_TYPE}) public final class Sulong extends LLVMLanguage { public interface LLVMLanguageProvider { LLVMContext createContext(com.oracle.truffle.api.TruffleLanguage.Env env); CallTarget parse(LLVMLanguage language, LLVMContext context, Source code, String... argumentNames) throws IOException; void disposeContext(LLVMContext context); } public static final LLVMLanguageProvider provider = getProvider(); public static final String MAIN_ARGS_KEY = "Sulong Main Args"; public static final String LLVM_SOURCE_FILE_KEY = "Sulong Source File"; public static final String PARSE_ONLY_KEY = "Parse only"; private com.oracle.truffle.api.TruffleLanguage.Env environment; @Override protected LLVMContext createContext(com.oracle.truffle.api.TruffleLanguage.Env env) { this.environment = env; return provider.createContext(env); } @Override public com.oracle.truffle.api.TruffleLanguage.Env getEnvironment() { return environment; } @Override protected void disposeContext(LLVMContext context) { context.printNativeCallStatistic(); provider.disposeContext(context); } @Override protected CallTarget parse(com.oracle.truffle.api.TruffleLanguage.ParsingRequest request) throws Exception { Source source = request.getSource(); return provider.parse(this, findLLVMContext(), source, request.getArgumentNames().toArray(new String[request.getArgumentNames().size()])); } @Override protected Object findExportedSymbol(LLVMContext context, String globalName, boolean onlyExplicit) { String atname = "@" + globalName; // for interop for (LLVMFunctionDescriptor descr : context.getFunctionDescriptors()) { if (descr != null && descr.getName().equals(globalName)) { return descr; } else if (descr != null && descr.getName().equals(atname)) { return descr; } } return null; } @Override protected Object getLanguageGlobal(LLVMContext context) { return context; } @Override protected boolean isObjectOfLanguage(Object object) { throw new AssertionError(); } @Override public LLVMContext findLLVMContext() { return getContextReference().get(); } private static SulongNodeFactory getNodeFactory() { ServiceLoader<SulongNodeFactory> loader = ServiceLoader.load(SulongNodeFactory.class); if (!loader.iterator().hasNext()) { throw new AssertionError("Could not find a " + SulongNodeFactory.class.getSimpleName() + " for the creation of the Truffle nodes"); } SulongNodeFactory factory = null; String expectedConfigName = LLVMOptions.ENGINE.nodeConfiguration(); for (SulongNodeFactory prov : loader) { String configName = prov.getConfigurationName(); if (configName != null && configName.equals(expectedConfigName)) { factory = prov; } } if (factory == null) { throw new AssertionError("Could not find a " + SulongNodeFactory.class.getSimpleName() + " with the name " + expectedConfigName); } return factory; } private static Sulong.LLVMLanguageProvider getProvider() { return new Sulong.LLVMLanguageProvider() { @Override public CallTarget parse(LLVMLanguage language, LLVMContext context, Source code, String... argumentNames) throws IOException { try { return parse(language, context, code); } catch (Throwable t) { throw new IOException("Error while trying to parse " + code.getPath(), t); } } private CallTarget parse(LLVMLanguage language, LLVMContext context, Source code) throws IOException { CallTarget mainFunction = null; if (code.getMimeType().equals(Sulong.LLVM_BITCODE_MIME_TYPE) || code.getMimeType().equals(Sulong.LLVM_BITCODE_BASE64_MIME_TYPE)) { LLVMParserResult parserResult = parseBitcodeFile(code, language, context); mainFunction = parserResult.getMainFunction(); handleParserResult(context, parserResult); } else if (code.getMimeType().equals(Sulong.SULONG_LIBRARY_MIME_TYPE)) { final SulongLibrary library = new SulongLibrary(new File(code.getPath())); List<Source> sourceFiles = new ArrayList<>(); library.readContents(dependentLibrary -> { context.addLibraryToNativeLookup(dependentLibrary); }, source -> { sourceFiles.add(source); }); for (Source source : sourceFiles) { String mimeType = source.getMimeType(); try { LLVMParserResult parserResult; if (mimeType.equals(Sulong.LLVM_BITCODE_MIME_TYPE) || mimeType.equals(Sulong.LLVM_BITCODE_BASE64_MIME_TYPE)) { parserResult = parseBitcodeFile(source, language, context); } else { throw new UnsupportedOperationException(mimeType); } handleParserResult(context, parserResult); if (parserResult.getMainFunction() != null) { mainFunction = parserResult.getMainFunction(); } } catch (Throwable t) { throw new IOException("Error while trying to parse " + source.getName(), t); } } if (mainFunction == null) { mainFunction = Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(null)); } } else { throw new IllegalArgumentException("undeclared mime type"); } parseDynamicBitcodeLibraries(language, context); if (context.isParseOnly()) { return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(mainFunction)); } else { return mainFunction; } } private void visitBitcodeLibraries(Consumer<Source> sharedLibraryConsumer) throws IOException { String[] dynamicLibraryPaths = LLVMOptions.ENGINE.dynamicBitcodeLibraries(); if (dynamicLibraryPaths != null && dynamicLibraryPaths.length != 0) { for (String s : dynamicLibraryPaths) { addLibrary(s, sharedLibraryConsumer); } } } private void addLibrary(String s, Consumer<Source> sharedLibraryConsumer) throws IOException { File lib = Paths.get(s).toFile(); Source source = Source.newBuilder(lib).build(); sharedLibraryConsumer.accept(source); } private void parseDynamicBitcodeLibraries(LLVMLanguage language, LLVMContext context) throws IOException { if (!context.haveLoadedDynamicBitcodeLibraries()) { context.setHaveLoadedDynamicBitcodeLibraries(); visitBitcodeLibraries(source -> { try { getProvider().parse(language, context, source); } catch (Throwable t) { throw new RuntimeException("Error while trying to parse dynamic library " + source.getName(), t); } }); } } private void handleParserResult(LLVMContext context, LLVMParserResult result) { context.registerGlobalVarInit(result.getGlobalVarInits()); context.registerGlobalVarDealloc(result.getGlobalVarDeallocs()); if (result.getConstructorFunctions() != null) { for (RootCallTarget constructorFunction : result.getConstructorFunctions()) { context.registerConstructorFunction(constructorFunction); } } if (result.getDestructorFunctions() != null) { for (RootCallTarget destructorFunction : result.getDestructorFunctions()) { context.registerDestructorFunction(destructorFunction); } } if (!context.isParseOnly()) { result.getGlobalVarInits().call(); for (RootCallTarget constructorFunction : result.getConstructorFunctions()) { constructorFunction.call(result.getConstructorFunctions()); } } } @Override public LLVMContext createContext(Env env) { LLVMContext context = new LLVMContext(env); if (env != null) { Object mainArgs = env.getConfig().get(Sulong.MAIN_ARGS_KEY); if (mainArgs != null) { context.setMainArguments((Object[]) mainArgs); } Object sourceFile = env.getConfig().get(Sulong.LLVM_SOURCE_FILE_KEY); if (sourceFile != null) { context.setMainSourceFile((Source) sourceFile); } Object parseOnly = env.getConfig().get(Sulong.PARSE_ONLY_KEY); if (parseOnly != null) { context.setParseOnly((boolean) parseOnly); } } return context; } @Override public void disposeContext(LLVMContext context) { for (RootCallTarget destructorFunction : context.getDestructorFunctions()) { destructorFunction.call(destructorFunction); } for (RootCallTarget destructor : context.getGlobalVarDeallocs()) { destructor.call(); } context.getThreadingStack().freeStacks(); } }; } public static void main(String[] args) throws Exception { if (args.length == 0) { throw new IllegalArgumentException("please provide a file to execute!"); } File file = new File(args[0]); Object[] otherArgs = new Object[args.length - 1]; System.arraycopy(args, 1, otherArgs, 0, otherArgs.length); int status = executeMain(file, otherArgs); System.exit(status); } private static LLVMParserResult parseBitcodeFile(Source source, LLVMLanguage language, LLVMContext context) { SulongNodeFactory nodeFactory = getNodeFactory(); context.setNativeIntrinsicsFactory(nodeFactory.getNativeIntrinsicsFactory(language, context)); return LLVMParserRuntime.parse(source, language, context, nodeFactory); } public static int executeMain(File file, Object... args) { LLVMLogger.info("current file: " + file.getAbsolutePath()); Source fileSource; try { fileSource = Source.newBuilder(file).build(); return evaluateFromSource(fileSource, args); } catch (IOException e) { throw new AssertionError(e); } } private static int evaluateFromSource(Source fileSource, Object... args) { Builder engineBuilder = PolyglotEngine.newBuilder(); engineBuilder.config(Sulong.LLVM_BITCODE_MIME_TYPE, Sulong.MAIN_ARGS_KEY, args); engineBuilder.config(Sulong.LLVM_BITCODE_MIME_TYPE, Sulong.LLVM_SOURCE_FILE_KEY, fileSource); PolyglotEngine vm = engineBuilder.build(); try { Integer result = vm.eval(fileSource).as(Integer.class); return result; } finally { vm.dispose(); } } }