Esempio n. 1
0
bool CompilerEngine::compileCode(const QString &pCode)
{
    // Reset our compiler engine

    reset();

    // Determine our target triple
    // Note: normally, we would call llvm::sys::getProcessTriple(), but this
    //       returns the information about the system on which LLVM was built.
    //       In most cases it is fine, but on OS X it may be a problem. Indeed,
    //       with OS X 10.9, Apple decided to extend the C standard by adding
    //       some functions (e.g. __exp10()). So, if the given code needs one of
    //       those functions, then OpenCOR will crash if run on an 'old' version
    //       of OS X. So, to avoid this issue, we set the target triple
    //       ourselves, based on the system on which OpenCOR is being used...

    std::string targetTriple;

#if defined(Q_OS_WIN)
    targetTriple = (sizeof(void *) == 4)?"i686-pc-windows-msvc-elf":"x86_64-pc-windows-msvc-elf";
    // Note: MCJIT currently works only through the ELF object format, hence we
    //       are appending "-elf"...
#elif defined(Q_OS_LINUX)
    targetTriple = (sizeof(void *) == 4)?"i686-pc-linux-gnu":"x86_64-pc-linux-gnu";
#elif defined(Q_OS_MAC)
    targetTriple = "x86_64-apple-darwin"+std::to_string(QSysInfo::MacintoshVersion+2);
#else
    #error Unsupported platform
#endif

    // Get a driver to compile our code

#ifdef QT_DEBUG
    llvm::raw_ostream &outputStream = llvm::outs();
#else
    llvm::raw_ostream &outputStream = llvm::nulls();
#endif
    llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnosticOptions = new clang::DiagnosticOptions();

    clang::DiagnosticsEngine diagnosticsEngine(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()),
                                               &*diagnosticOptions,
                                               new clang::TextDiagnosticPrinter(outputStream, &*diagnosticOptions));

    clang::driver::Driver driver("clang", targetTriple, diagnosticsEngine);

    driver.setCheckInputsExist(false);

    // Get a compilation object to which we pass some arguments

    llvm::StringRef dummyFileName("dummyFile.c");

    llvm::SmallVector<const char *, 16> compilationArguments;

    compilationArguments.push_back("clang");
    compilationArguments.push_back("-fsyntax-only");
    compilationArguments.push_back("-O3");
    compilationArguments.push_back("-ffast-math");
    compilationArguments.push_back("-Werror");
    compilationArguments.push_back(dummyFileName.data());

    std::unique_ptr<clang::driver::Compilation> compilation(driver.BuildCompilation(compilationArguments));

    if (!compilation) {
        mError = tr("the compilation object could not be created");

        return false;
    }

    // The compilation object should have only one command, so if it doesn't
    // then something went wrong

    const clang::driver::JobList &jobList = compilation->getJobs();

    if (    (jobList.size() != 1)
        || !llvm::isa<clang::driver::Command>(*jobList.begin())) {
        mError = tr("the compilation object must contain only one command");

        return false;
    }

    // Retrieve the command job

    const clang::driver::Command &command = llvm::cast<clang::driver::Command>(*jobList.begin());
    QString commandName = command.getCreator().getName();

    if (commandName.compare("clang")) {
        mError = tr("a <strong>clang</strong> command was expected, but a <strong>%1</strong> command was found instead").arg(commandName);

        return false;
    }

    // Create a compiler invocation using our command's arguments

    const clang::driver::ArgStringList &commandArguments = command.getArguments();
    std::unique_ptr<clang::CompilerInvocation> compilerInvocation(new clang::CompilerInvocation());

    clang::CompilerInvocation::CreateFromArgs(*compilerInvocation,
                                              commandArguments.data(),
                                              commandArguments.data()+commandArguments.size(),
                                              diagnosticsEngine);

    // Map our dummy file to a memory buffer

    QByteArray codeByteArray = pCode.toUtf8();

    compilerInvocation->getPreprocessorOpts().addRemappedFile(dummyFileName, llvm::MemoryBuffer::getMemBuffer(codeByteArray.constData()).release());

    // Create a compiler instance to handle the actual work

    clang::CompilerInstance compilerInstance;

    compilerInstance.setInvocation(compilerInvocation.release());

    // Create the compiler instance's diagnostics engine

    compilerInstance.createDiagnostics();

    if (!compilerInstance.hasDiagnostics()) {
        mError = tr("the diagnostics engine could not be created");

        return false;
    }

    // Create and execute the frontend to generate an LLVM bitcode module
    // Note: the LLVM team has been meaning to modify
    //       CompilerInstance::ExecuteAction() so that we could specify the
    //       output stream we want to use (rather than always use llvm::errs()),
    //       but they have yet to actually do it, so we modified it ourselves...

    std::unique_ptr<clang::CodeGenAction> codeGenerationAction(new clang::EmitLLVMOnlyAction(&llvm::getGlobalContext()));

    if (!compilerInstance.ExecuteAction(*codeGenerationAction, outputStream)) {
        mError = tr("the code could not be compiled");

        reset(false);

        return false;
    }

    // Retrieve the LLVM bitcode module

    std::unique_ptr<llvm::Module> module = codeGenerationAction->takeModule();

    // Initialise the native target (and its ASM printer), so not only can we
    // then create an execution engine, but more importantly its data layout
    // will match that of our target platform

    llvm::InitializeNativeTarget();
    llvm::InitializeNativeTargetAsmPrinter();

    // Create and keep track of an execution engine

    mExecutionEngine = std::unique_ptr<llvm::ExecutionEngine>(llvm::EngineBuilder(std::move(module)).setEngineKind(llvm::EngineKind::JIT).create());

    if (!mExecutionEngine) {
        mError = tr("the execution engine could not be created");

        delete module.release();

        return false;
    }

    return true;
}
Esempio n. 2
0
bool CompilerEngine::compileCode(const QString &pCode)
{
    // Prepend all the external functions that may, or not, be needed by the
    // given code
    // Note: indeed, we cannot include header files since we don't (and don't
    //       want in order to avoid complications) deploy them with OpenCOR. So,
    //       instead, we must declare as external functions all the functions
    //       that we would normally use through header files...

    QString code =  "extern double fabs(double);\n"
                    "\n"
                    "extern double log(double);\n"
                    "extern double exp(double);\n"
                    "\n"
                    "extern double floor(double);\n"
                    "extern double ceil(double);\n"
                    "\n"
                    "extern double factorial(double);\n"
                    "\n"
                    "extern double sin(double);\n"
                    "extern double sinh(double);\n"
                    "extern double asin(double);\n"
                    "extern double asinh(double);\n"
                    "\n"
                    "extern double cos(double);\n"
                    "extern double cosh(double);\n"
                    "extern double acos(double);\n"
                    "extern double acosh(double);\n"
                    "\n"
                    "extern double tan(double);\n"
                    "extern double tanh(double);\n"
                    "extern double atan(double);\n"
                    "extern double atanh(double);\n"
                    "\n"
                    "extern double sec(double);\n"
                    "extern double sech(double);\n"
                    "extern double asec(double);\n"
                    "extern double asech(double);\n"
                    "\n"
                    "extern double csc(double);\n"
                    "extern double csch(double);\n"
                    "extern double acsc(double);\n"
                    "extern double acsch(double);\n"
                    "\n"
                    "extern double cot(double);\n"
                    "extern double coth(double);\n"
                    "extern double acot(double);\n"
                    "extern double acoth(double);\n"
                    "\n"
                    "extern double arbitrary_log(double, double);\n"
                    "\n"
                    "extern double pow(double, double);\n"
                    "\n"
                    "extern double multi_min(int, ...);\n"
                    "extern double multi_max(int, ...);\n"
                    "\n"
                    "extern double gcd_multi(int, ...);\n"
                    "extern double lcm_multi(int, ...);\n"
                    "\n"
                   +pCode;

    // Reset our compiler engine

    reset();

    // Determine our target triple
    // Note: normally, we would call llvm::sys::getProcessTriple(), but this
    //       returns the information about the system on which LLVM was built.
    //       In most cases it is fine, but on OS X it may be a problem. Indeed,
    //       with OS X 10.9, Apple decided to extend the C standard by adding
    //       some functions (e.g. __exp10()). So, if the given code needs one of
    //       those functions, then OpenCOR will crash if run on an 'old' version
    //       of OS X. So, to avoid this issue, we set the target triple
    //       ourselves, based on the system on which OpenCOR is to be used...

    std::string targetTriple;

#if defined(Q_OS_WIN)
    targetTriple = "x86_64-pc-windows-msvc-elf";
    // Note: MCJIT currently works only through the ELF object format, hence we
    //       are appending "-elf"...
#elif defined(Q_OS_LINUX)
    targetTriple = "x86_64-pc-linux-gnu";
#elif defined(Q_OS_MAC)
    targetTriple = "x86_64-apple-darwin"+std::to_string(QSysInfo::MacintoshVersion+2);
#else
    #error Unsupported platform
#endif

    // Get a driver to compile our code

    llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnosticOptions = new clang::DiagnosticOptions();
    clang::DiagnosticsEngine diagnosticsEngine(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()),
                                               &*diagnosticOptions);
    clang::driver::Driver driver("clang", targetTriple, diagnosticsEngine);

    driver.setCheckInputsExist(false);

    // Get a compilation object to which we pass some arguments

    llvm::StringRef dummyFileName("dummyFile.c");
    llvm::SmallVector<const char *, 16> compilationArguments;

    compilationArguments.push_back("clang");
    compilationArguments.push_back("-fsyntax-only");
    compilationArguments.push_back("-O3");
    compilationArguments.push_back("-ffast-math");
    compilationArguments.push_back("-Werror");
    compilationArguments.push_back(dummyFileName.data());

    std::unique_ptr<clang::driver::Compilation> compilation(driver.BuildCompilation(compilationArguments));

    if (!compilation) {
        mError = tr("the compilation object could not be created");

        return false;
    }

    // The compilation object should have only one command, so if it doesn't
    // then something went wrong

    const clang::driver::JobList &jobList = compilation->getJobs();

    if (    (jobList.size() != 1)
        || !llvm::isa<clang::driver::Command>(*jobList.begin())) {
        mError = tr("the compilation object must contain only one command");

        return false;
    }

    // Retrieve the command job

    const clang::driver::Command &command = llvm::cast<clang::driver::Command>(*jobList.begin());
    QString commandName = command.getCreator().getName();

    if (commandName.compare("clang")) {
        mError = tr("a <strong>clang</strong> command was expected, but a <strong>%1</strong> command was found instead").arg(commandName);

        return false;
    }

    // Create a compiler invocation using our command's arguments

    const clang::driver::ArgStringList &commandArguments = command.getArguments();
    std::unique_ptr<clang::CompilerInvocation> compilerInvocation(new clang::CompilerInvocation());

    clang::CompilerInvocation::CreateFromArgs(*compilerInvocation,
                                              commandArguments.data(),
                                              commandArguments.data()+commandArguments.size(),
                                              diagnosticsEngine);

    // Map our dummy file to a memory buffer

    QByteArray codeByteArray = code.toUtf8();

    compilerInvocation->getPreprocessorOpts().addRemappedFile(dummyFileName, llvm::MemoryBuffer::getMemBuffer(codeByteArray.constData()).release());

    // Create a compiler instance to handle the actual work

    clang::CompilerInstance compilerInstance;

    compilerInstance.setInvocation(compilerInvocation.release());

    // Create the compiler instance's diagnostics engine

    compilerInstance.createDiagnostics();

    if (!compilerInstance.hasDiagnostics()) {
        mError = tr("the diagnostics engine could not be created");

        return false;
    }

    // Create and execute the frontend to generate an LLVM bitcode module

    std::unique_ptr<clang::CodeGenAction> codeGenerationAction(new clang::EmitLLVMOnlyAction(&llvm::getGlobalContext()));

    if (!compilerInstance.ExecuteAction(*codeGenerationAction)) {
        mError = tr("the code could not be compiled");

        reset(false);

        return false;
    }

    // Retrieve the LLVM bitcode module

    std::unique_ptr<llvm::Module> module = codeGenerationAction->takeModule();

    // Initialise the native target (and its ASM printer), so not only can we
    // then create an execution engine, but more importantly its data layout
    // will match that of our target platform

    llvm::InitializeNativeTarget();
    llvm::InitializeNativeTargetAsmPrinter();

    // Create and keep track of an execution engine

    mExecutionEngine = std::unique_ptr<llvm::ExecutionEngine>(llvm::EngineBuilder(std::move(module)).setEngineKind(llvm::EngineKind::JIT).create());

    if (!mExecutionEngine) {
        mError = tr("the execution engine could not be created");

        delete module.release();

        return false;
    }

    // Map all the external functions that may, or not, be needed by the given
    // code

#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
    #define FUNCTION_NAME(x) (x)
#elif defined(Q_OS_MAC)
    #define FUNCTION_NAME(x) (std::string(std::string("_")+(x)).c_str())
#else
    #error Unsupported platform
#endif

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("fabs"), (uint64_t) compiler_fabs);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("log"), (uint64_t) compiler_log);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("exp"), (uint64_t) compiler_exp);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("floor"), (uint64_t) compiler_floor);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("ceil"), (uint64_t) compiler_ceil);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("factorial"), (uint64_t) compiler_factorial);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("sin"), (uint64_t) compiler_sin);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("sinh"), (uint64_t) compiler_sinh);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("asin"), (uint64_t) compiler_asin);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("asinh"), (uint64_t) compiler_asinh);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("cos"), (uint64_t) compiler_cos);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("cosh"), (uint64_t) compiler_cosh);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("acos"), (uint64_t) compiler_acos);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("acosh"), (uint64_t) compiler_acosh);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("tan"), (uint64_t) compiler_tan);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("tanh"), (uint64_t) compiler_tanh);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("atan"), (uint64_t) compiler_atan);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("atanh"), (uint64_t) compiler_atanh);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("sec"), (uint64_t) compiler_sec);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("sech"), (uint64_t) compiler_sech);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("asec"), (uint64_t) compiler_asec);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("asech"), (uint64_t) compiler_asech);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("csc"), (uint64_t) compiler_csc);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("csch"), (uint64_t) compiler_csch);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("acsc"), (uint64_t) compiler_acsc);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("acsch"), (uint64_t) compiler_acsch);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("cot"), (uint64_t) compiler_cot);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("coth"), (uint64_t) compiler_coth);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("acot"), (uint64_t) compiler_acot);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("acoth"), (uint64_t) compiler_acoth);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("arbitrary_log"), (uint64_t) compiler_arbitrary_log);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("pow"), (uint64_t) compiler_pow);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("multi_min"), (uint64_t) compiler_multi_min);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("multi_max"), (uint64_t) compiler_multi_max);

    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("gcd_multi"), (uint64_t) compiler_gcd_multi);
    mExecutionEngine->addGlobalMapping(FUNCTION_NAME("lcm_multi"), (uint64_t) compiler_lcm_multi);

    return true;
}