Commit 9cb959e0 authored by Jaikrishnan Menon's avatar Jaikrishnan Menon

Import initial codegen support from the IMC and integrate LLVM

parent ea9a120d
# Copyright 2017 Nervana Systems Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
include(ExternalProject)
find_package(LLVM CONFIG)
if(LLVM_FOUND)
if(${LLVM_PACKAGE_VERSION} VERSION_GREATER "4.0")
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
else()
set(LLVM_FOUND FALSE)
endif()
endif()
if(NOT LLVM_FOUND)
if((NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") AND
(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows"))
message(STATUS "Fetching LLVM from llvm.org")
set(LLVM_RELEASE_URL http://releases.llvm.org/5.0.0/clang+llvm-5.0.0-linux-x86_64-ubuntu16.04.tar.xz)
# The 'BUILD_BYPRODUCTS' argument was introduced in CMake 3.2.
if(${CMAKE_VERSION} VERSION_LESS 3.2)
ExternalProject_Add(
ext_llvm
URL ${LLVM_RELEASE_URL}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
UPDATE_COMMAND ""
)
else()
ExternalProject_Add(
ext_llvm
URL ${LLVM_RELEASE_URL}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
UPDATE_COMMAND ""
BUILD_BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/ext_llvm-prefix/src/ext_llvm/lib/libLLVMCore.a"
)
endif()
ExternalProject_Get_Property(ext_llvm source_dir)
set(LLVM_INCLUDE_DIR "${source_dir}/include")
set(LLVM_LIB_DIR "${source_dir}/lib")
set(LLVM_LINK_LIBS clangTooling clangFrontendTool clangFrontend clangDriver clangSerialization clangCodeGen clangParse clangSema clangStaticAnalyzerFrontend clangStaticAnalyzerCheckers clangStaticAnalyzerCore clangAnalysis clangARCMigrate clangRewriteFrontend clangEdit clangAST clangLex clangBasic LLVMLTO LLVMPasses LLVMObjCARCOpts LLVMSymbolize LLVMDebugInfoPDB LLVMDebugInfoDWARF LLVMMIRParser LLVMCoverage LLVMTableGen LLVMDlltoolDriver LLVMOrcJIT LLVMXCoreDisassembler LLVMXCoreCodeGen LLVMXCoreDesc LLVMXCoreInfo LLVMXCoreAsmPrinter LLVMSystemZDisassembler LLVMSystemZCodeGen LLVMSystemZAsmParser LLVMSystemZDesc LLVMSystemZInfo LLVMSystemZAsmPrinter LLVMSparcDisassembler LLVMSparcCodeGen LLVMSparcAsmParser LLVMSparcDesc LLVMSparcInfo LLVMSparcAsmPrinter LLVMPowerPCDisassembler LLVMPowerPCCodeGen LLVMPowerPCAsmParser LLVMPowerPCDesc LLVMPowerPCInfo LLVMPowerPCAsmPrinter LLVMNVPTXCodeGen LLVMNVPTXDesc LLVMNVPTXInfo LLVMNVPTXAsmPrinter LLVMMSP430CodeGen LLVMMSP430Desc LLVMMSP430Info LLVMMSP430AsmPrinter LLVMMipsDisassembler LLVMMipsCodeGen LLVMMipsAsmParser LLVMMipsDesc LLVMMipsInfo LLVMMipsAsmPrinter LLVMLanaiDisassembler LLVMLanaiCodeGen LLVMLanaiAsmParser LLVMLanaiDesc LLVMLanaiAsmPrinter LLVMLanaiInfo LLVMHexagonDisassembler LLVMHexagonCodeGen LLVMHexagonAsmParser LLVMHexagonDesc LLVMHexagonInfo LLVMBPFDisassembler LLVMBPFCodeGen LLVMBPFDesc LLVMBPFInfo LLVMBPFAsmPrinter LLVMARMDisassembler LLVMARMCodeGen LLVMARMAsmParser LLVMARMDesc LLVMARMInfo LLVMARMAsmPrinter LLVMAMDGPUDisassembler LLVMAMDGPUCodeGen LLVMAMDGPUAsmParser LLVMAMDGPUDesc LLVMAMDGPUInfo LLVMAMDGPUAsmPrinter LLVMAMDGPUUtils LLVMAArch64Disassembler LLVMAArch64CodeGen LLVMAArch64AsmParser LLVMAArch64Desc LLVMAArch64Info LLVMAArch64AsmPrinter LLVMAArch64Utils LLVMObjectYAML LLVMLibDriver LLVMOption LLVMX86Disassembler LLVMX86AsmParser LLVMX86CodeGen LLVMGlobalISel LLVMSelectionDAG LLVMAsmPrinter LLVMDebugInfoCodeView LLVMDebugInfoMSF LLVMX86Desc LLVMMCDisassembler LLVMX86Info LLVMX86AsmPrinter LLVMX86Utils LLVMMCJIT LLVMLineEditor LLVMInterpreter LLVMExecutionEngine LLVMRuntimeDyld LLVMCodeGen LLVMTarget LLVMCoroutines LLVMipo LLVMInstrumentation LLVMVectorize LLVMScalarOpts LLVMLinker LLVMIRReader LLVMAsmParser LLVMInstCombine LLVMTransformUtils LLVMBitWriter LLVMAnalysis LLVMProfileData LLVMObject LLVMMCParser LLVMMC LLVMBitReader LLVMCore LLVMBinaryFormat LLVMSupport LLVMDemangle tinfo z m)
set(LLVM_FOUND TRUE)
set(Clang_FOUND TRUE)
endif()
endif()
if(LLVM_FOUND AND NOT Clang_FOUND)
find_package(Clang CONFIG)
endif()
# TODO: Figure out if this terminates the build or do we allow interpretation-only builds
#if(NOT LLVM_FOUND OR NOT Clang_FOUND)
#endif()
# Populate header and library paths from package-exported info
# if we found system-level LLVM and Clang packages
if(NOT LLVM_INCLUDE_DIR)
set(LLVM_INCLUDE_DIR ${LLVM_INCLUDE_DIRS})
set(LLVM_LIB_DIR ${LLVM_LIBRARY_DIRS})
llvm_map_components_to_libnames(llvm_libs support core engine)
set(LLVM_LINK_LIBS ${llvm_libs})
endif()
# Export all necessary info
set(LLVM_INCLUDE_DIR ${LLVM_INCLUDE_DIR} PARENT_SCOPE)
set(LLVM_LIB_DIR ${LLVM_LIB_DIR} PARENT_SCOPE)
set(LLVM_LINK_LIBS ${LLVM_LINK_LIBS} PARENT_SCOPE)
...@@ -77,9 +77,23 @@ include_directories( ...@@ -77,9 +77,23 @@ include_directories(
"${EIGEN_INCLUDE_DIR}" "${EIGEN_INCLUDE_DIR}"
) )
if(LLVM_INCLUDE_DIR)
include_directories(SYSTEM ${LLVM_INCLUDE_DIR})
link_directories(${LLVM_LIB_DIR})
# Add sources for the LLVM-based code generator
set(SRC ${SRC}
codegen/compiler.cpp
)
set_source_files_properties(codegen/compiler.cpp PROPERTIES COMPILE_FLAGS "-fno-rtti")
endif()
add_library(ngraph SHARED ${SRC}) add_library(ngraph SHARED ${SRC})
target_include_directories(ngraph PUBLIC "${NGRAPH_INCLUDE_PATH}") target_include_directories(ngraph PUBLIC "${NGRAPH_INCLUDE_PATH}")
if(LLVM_LINK_LIBS)
target_link_libraries(ngraph LINK_PRIVATE ${LLVM_LINK_LIBS})
endif()
if (APPLE) if (APPLE)
set_property(TARGET ngraph PROPERTY PREFIX "lib") set_property(TARGET ngraph PROPERTY PREFIX "lib")
set_property(TARGET ngraph PROPERTY OUTPUT_NAME "ngraph.so") set_property(TARGET ngraph PROPERTY OUTPUT_NAME "ngraph.so")
...@@ -137,4 +151,4 @@ install(DIRECTORY ...@@ -137,4 +151,4 @@ install(DIRECTORY
DESTINATION "${CMAKE_INSTALL_LIB}" DESTINATION "${CMAKE_INSTALL_LIB}"
) )
add_dependencies(ngraph eigen) add_dependencies(ngraph eigen ext_llvm)
#include <iostream>
#include <clang/CodeGen/ObjectFilePCHContainerOperations.h>
#include <clang/Driver/DriverDiagnostic.h>
#include <clang/Driver/Options.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/CompilerInvocation.h>
#include <clang/Frontend/FrontendDiagnostic.h>
#include <clang/Frontend/TextDiagnosticBuffer.h>
#include <clang/Frontend/TextDiagnosticPrinter.h>
#include <clang/Frontend/Utils.h>
#include <clang/FrontendTool/Utils.h>
#include <clang/Lex/Preprocessor.h>
#include <clang/Lex/PreprocessorOptions.h>
#include <llvm/ADT/Statistic.h>
#include <llvm/LinkAllPasses.h>
#include <llvm/Option/Arg.h>
#include <llvm/Option/ArgList.h>
#include <llvm/Option/OptTable.h>
#include <llvm/Support/ErrorHandling.h>
#include <llvm/Support/ManagedStatic.h>
#include <llvm/Support/Signals.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Timer.h>
#include <llvm/Support/raw_ostream.h>
#include <clang/Basic/DiagnosticOptions.h>
#include <clang/Basic/TargetInfo.h>
#include <clang/CodeGen/CodeGenAction.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/TextDiagnosticPrinter.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include "compiler.hpp"
using namespace clang;
using namespace llvm;
using namespace llvm::opt;
using namespace std;
using namespace nervana::cpu;
std::string GetExecutablePath(const char* Argv0)
{
// This just needs to be some symbol in the binary; C++ doesn't
// allow taking the address of ::main however.
void* MainAddr = (void*)(intptr_t)GetExecutablePath;
return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
}
execution_state::execution_state()
: m_execution_engine{nullptr}
{
}
execution_state::~execution_state()
{
// /// Take the LLVM context used by this action.
// llvm::LLVMContext *takeLLVMContext();
// if (m_execution_engine)
// {
// m_execution_engine->runStaticConstructorsDestructors(true);
// }
}
std::unique_ptr<llvm::Module> execution_state::compile(const string& source, const string& name)
{
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllAsmParsers();
// Prepare compilation arguments
vector<const char*> args;
args.push_back(name.c_str());
// Prepare DiagnosticEngine
DiagnosticOptions DiagOpts;
TextDiagnosticPrinter* textDiagPrinter = new clang::TextDiagnosticPrinter(errs(), &DiagOpts);
IntrusiveRefCntPtr<clang::DiagnosticIDs> pDiagIDs;
DiagnosticsEngine* pDiagnosticsEngine =
new DiagnosticsEngine(pDiagIDs, &DiagOpts, textDiagPrinter);
// Create and initialize CompilerInstance
std::unique_ptr<CompilerInstance> Clang(new CompilerInstance());
Clang->createDiagnostics();
// Initialize CompilerInvocation
CompilerInvocation::CreateFromArgs(
Clang->getInvocation(), &args[0], &args[0] + args.size(), *pDiagnosticsEngine);
// Infer the builtin include path if unspecified.
if (Clang->getHeaderSearchOpts().UseBuiltinIncludes &&
Clang->getHeaderSearchOpts().ResourceDir.empty())
{
void* MainAddr = (void*)(intptr_t)GetExecutablePath;
auto path = CompilerInvocation::GetResourcesPath(args[0], MainAddr);
Clang->getHeaderSearchOpts().ResourceDir = path;
}
auto& HSO = Clang->getInvocation().getHeaderSearchOpts();
// Add base toolchain-supplied header paths
// Ideally one would use the Linux toolchain definition in clang/lib/Driver/ToolChains.h
// But that's a private header and isn't part of the public libclang API
// Instead of re-implementing all of that functionality in a custom toolchain
// just hardcode the paths relevant to frequently used build/test machines for now
HSO.AddPath("/usr/include/x86_64-linux-gnu", clang::frontend::System, false, false);
HSO.AddPath("/usr/include", clang::frontend::System, false, false);
// Add C++ standard library headers
// Debian-like + GCC 4.8 libstdc++
HSO.AddPath("/usr/include/x86_64-linux-gnu/c++/4.8", clang::frontend::System, false, false);
HSO.AddPath("/usr/include/c++/4.8", clang::frontend::System, false, false);
// Debian-like + GCC 5 libstdc++
HSO.AddPath("/usr/include/x86_64-linux-gnu/c++/5", clang::frontend::System, false, false);
HSO.AddPath("/usr/include/c++/5", clang::frontend::System, false, false);
// Map code filename to a memoryBuffer
StringRef source_ref(source);
unique_ptr<MemoryBuffer> buffer = MemoryBuffer::getMemBufferCopy(source_ref);
Clang->getInvocation().getPreprocessorOpts().addRemappedFile(name, buffer.get());
// Create and execute action
// CodeGenAction *compilerAction = new EmitLLVMOnlyAction();
// CodeGenAction* compilerAction = new EmitAssemblyAction();
CodeGenAction* compilerAction = new EmitCodeGenOnlyAction();
Clang->ExecuteAction(*compilerAction);
buffer.release();
return compilerAction->takeModule();
}
bool execution_state::add_module(std::unique_ptr<llvm::Module>& module)
{
bool rc = false;
if (module)
{
rc = true;
if (!m_execution_engine)
{
std::string jit_error_string;
// auto mm = unique_ptr<RTDyldMemoryManager>(new method_resolver(this));
m_execution_engine = llvm::EngineBuilder(move(module))
.setEngineKind(llvm::EngineKind::JIT)
.setOptLevel(llvm::CodeGenOpt::Aggressive)
.setErrorStr(&jit_error_string)
// .setUseMCJIT(true)
// .setMCJITMemoryManager(std::move(mm))
.create();
if (m_execution_engine)
{
}
else
{
cout << "nullptr engine\n";
cout << jit_error_string << endl;
rc = false;
}
}
}
else
{
cout << "nullptr module\n";
rc = false;
}
return rc;
}
void execution_state::finalize()
{
if (m_execution_engine)
{
m_execution_engine->finalizeObject();
m_execution_engine->runStaticConstructorsDestructors(false);
}
else
{
throw std::runtime_error("must add_module before finalize");
}
}
#pragma once
#include <llvm/ExecutionEngine/MCJIT.h> // forces JIT to link in
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/Option/Arg.h>
namespace nervana
{
namespace cpu
{
class module;
class execution_state;
}
}
class nervana::cpu::module
{
public:
private:
std::unique_ptr<llvm::Module> m_module;
};
class nervana::cpu::execution_state : public llvm::SectionMemoryManager
{
public:
execution_state();
~execution_state();
std::unique_ptr<llvm::Module> compile(const std::string& source, const std::string& name = "");
bool add_module(std::unique_ptr<llvm::Module>&);
void finalize();
template <typename ftype>
std::function<ftype> find_function(const std::string& func_name)
{
auto f = m_execution_engine->getPointerToNamedFunction(func_name);
return f_cast<ftype>(f);
}
private:
llvm::ExecutionEngine* m_execution_engine;
template <typename signature>
std::function<signature> f_cast(void* f)
{
return static_cast<signature*>((signature*)f);
}
// class method_resolver : public llvm::RTDyldMemoryManager
// {
// public:
// method_resolver(compiler* m);
// virtual uint64_t getSymbolAddress(const std::string &name) override;
// private:
// compiler* m_Compiler;
// };
};
...@@ -47,6 +47,11 @@ if(MKLDNN_INCLUDE_DIR) ...@@ -47,6 +47,11 @@ if(MKLDNN_INCLUDE_DIR)
set(SRC ${SRC} mkldnn.cpp) set(SRC ${SRC} mkldnn.cpp)
endif() endif()
if(LLVM_INCLUDE_DIR)
include_directories(SYSTEM ${LLVM_INCLUDE_DIR})
set(SRC ${SRC} codegen.cpp)
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCURDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}\\\"") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCURDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}\\\"")
......
// ----------------------------------------------------------------------------
// Copyright 2017 Nervana Systems Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <sstream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "ngraph/codegen/compiler.hpp"
using namespace std;
TEST(codegen, simple_return)
{
constexpr auto name = "test.cpp";
constexpr auto source = R"(extern "C" int test() { return 2+5; })";
nervana::cpu::execution_state estate;
auto module = estate.compile(source, name);
ASSERT_NE(nullptr, module);
estate.add_module(module);
estate.finalize();
auto func = estate.find_function<int()>("test");
ASSERT_NE(nullptr, func);
int result = func();
EXPECT_EQ(7, result);
}
TEST(codegen, pass_args)
{
constexpr auto name = "test.cpp";
constexpr auto source = R"(extern "C" int test(int a, int b) { return a+b; })";
nervana::cpu::execution_state estate;
auto module = estate.compile(source, name);
ASSERT_NE(nullptr, module);
estate.add_module(module);
estate.finalize();
auto func = estate.find_function<int(int, int)>("test");
ASSERT_NE(nullptr, func);
int result = func(20, 22);
EXPECT_EQ(42, result);
}
TEST(codegen, include)
{
constexpr auto name = "test.cpp";
constexpr auto source =
R"(
#include <cmath>
extern "C" int test(int a, int b)
{
return (int)pow((double)a,(double)b);
}
)";
nervana::cpu::execution_state estate;
auto module = estate.compile(source, name);
ASSERT_NE(nullptr, module);
estate.add_module(module);
estate.finalize();
auto func = estate.find_function<int(int, int)>("test");
ASSERT_NE(nullptr, func);
int result = func(20, 2);
EXPECT_EQ(400, result);
}
...@@ -14,3 +14,4 @@ ...@@ -14,3 +14,4 @@
include( ../cmake/external_gtest.cmake ) include( ../cmake/external_gtest.cmake )
include( ../cmake/external_eigen.cmake ) include( ../cmake/external_eigen.cmake )
include( ../cmake/external_mkldnn.cmake ) include( ../cmake/external_mkldnn.cmake )
include( ../cmake/external_llvm.cmake )
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment