diff options
author | Josh Blum | 2013-08-01 01:12:24 -0700 |
---|---|---|
committer | Josh Blum | 2013-08-01 01:12:24 -0700 |
commit | a23bf59c46dae7aa25c4763c6122d0822c88abc7 (patch) | |
tree | af37ea0c6b2872071938390c857e4efcb4975d86 | |
parent | 1897808616c91d277e24335a337bec92592fb87a (diff) | |
download | sandhi-a23bf59c46dae7aa25c4763c6122d0822c88abc7.tar.gz sandhi-a23bf59c46dae7aa25c4763c6122d0822c88abc7.tar.bz2 sandhi-a23bf59c46dae7aa25c4763c6122d0822c88abc7.zip |
gras: jit factory api + unit tests
-rw-r--r-- | cmake/Modules/FindClang.cmake | 50 | ||||
-rw-r--r-- | cmake/Modules/FindLLVM.cmake | 73 | ||||
-rw-r--r-- | include/gras/detail/factory.hpp | 44 | ||||
-rw-r--r-- | include/gras/factory.hpp | 32 | ||||
-rw-r--r-- | lib/CMakeLists.txt | 30 | ||||
-rw-r--r-- | lib/factory.cpp | 4 | ||||
-rw-r--r-- | lib/jit_factory.cpp | 158 | ||||
-rw-r--r-- | python/gras/GRAS_Factory.i | 28 | ||||
-rw-r--r-- | python/gras/__init__.py | 2 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/jit_factory_test.py | 150 | ||||
-rw-r--r-- | tmpl/factory.tmpl.hpp | 32 | ||||
-rw-r--r-- | tmpl/factory_detail.tmpl.hpp | 4 |
13 files changed, 550 insertions, 58 deletions
diff --git a/cmake/Modules/FindClang.cmake b/cmake/Modules/FindClang.cmake new file mode 100644 index 0000000..cbce0af --- /dev/null +++ b/cmake/Modules/FindClang.cmake @@ -0,0 +1,50 @@ +# Find Clang +# +# It defines the following variables +# CLANG_FOUND - True if Clang found. +# CLANG_INCLUDE_DIRS - where to find Clang include files +# CLANG_LIBS - list of clang libs + +if (NOT LLVM_INCLUDE_DIRS OR NOT LLVM_LIBRARY_DIRS) + message(FATAL_ERROR "No LLVM and Clang support requires LLVM") +else (NOT LLVM_INCLUDE_DIRS OR NOT LLVM_LIBRARY_DIRS) + +MACRO(FIND_AND_ADD_CLANG_LIB _libname_) +find_library(CLANG_${_libname_}_LIB ${_libname_} ${LLVM_LIBRARY_DIRS} ${CLANG_LIBRARY_DIRS}) +if (CLANG_${_libname_}_LIB) + set(CLANG_LIBS ${CLANG_LIBS} ${CLANG_${_libname_}_LIB}) +endif (CLANG_${_libname_}_LIB) +ENDMACRO(FIND_AND_ADD_CLANG_LIB) + +# Clang shared library provides just the limited C interface, so it +# can not be used. We look for the static libraries. +FIND_AND_ADD_CLANG_LIB(clangFrontend) +FIND_AND_ADD_CLANG_LIB(clangDriver) +FIND_AND_ADD_CLANG_LIB(clangCodeGen) +FIND_AND_ADD_CLANG_LIB(clangEdit) +FIND_AND_ADD_CLANG_LIB(clangSema) +FIND_AND_ADD_CLANG_LIB(clangChecker) +FIND_AND_ADD_CLANG_LIB(clangAnalysis) +FIND_AND_ADD_CLANG_LIB(clangRewrite) +FIND_AND_ADD_CLANG_LIB(clangAST) +FIND_AND_ADD_CLANG_LIB(clangParse) +FIND_AND_ADD_CLANG_LIB(clangLex) +FIND_AND_ADD_CLANG_LIB(clangBasic) +FIND_AND_ADD_CLANG_LIB(clang) + +find_path(CLANG_INCLUDE_DIRS clang/Basic/Version.h HINTS ${LLVM_INCLUDE_DIRS}) + +if (CLANG_LIBS AND CLANG_INCLUDE_DIRS) + MESSAGE(STATUS "Clang libs: " ${CLANG_LIBS}) + set(CLANG_FOUND TRUE) +endif (CLANG_LIBS AND CLANG_INCLUDE_DIRS) + +if (CLANG_FOUND) + message(STATUS "Found Clang: ${CLANG_INCLUDE_DIRS}") +else (CLANG_FOUND) + if (CLANG_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find Clang") + endif (CLANG_FIND_REQUIRED) +endif (CLANG_FOUND) + +endif (NOT LLVM_INCLUDE_DIRS OR NOT LLVM_LIBRARY_DIRS) diff --git a/cmake/Modules/FindLLVM.cmake b/cmake/Modules/FindLLVM.cmake new file mode 100644 index 0000000..a4ac5fb --- /dev/null +++ b/cmake/Modules/FindLLVM.cmake @@ -0,0 +1,73 @@ +# Find LLVM +# +# It defines the following variables +# LLVM_FOUND - True if llvm found. +# LLVM_INCLUDE_DIRS - where to find llvm include files +# LLVM_LIBRARY_DIRS - where to find llvm libs +# LLVM_CFLAGS - llvm compiler flags +# LLVM_LDFLAGS - llvm linker flags +# LLVM_MODULE_LIBS - list of llvm libs for working with modules. + +find_program(LLVM_CONFIG_EXECUTABLE llvm-config DOC "llvm-config executable") + +if (LLVM_CONFIG_EXECUTABLE) + message(STATUS "LLVM llvm-config found at: ${LLVM_CONFIG_EXECUTABLE}") +else (LLVM_CONFIG_EXECUTABLE) + message(FATAL_ERROR "Could NOT find LLVM executable") +endif (LLVM_CONFIG_EXECUTABLE) + +execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --version + OUTPUT_VARIABLE LLVM_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +string(REGEX REPLACE "^([0-9]+)\\.([0-9]+).*" "\\1" LLVM_VERSION_MAJOR + "${LLVM_VERSION}") + +string(REGEX REPLACE "^([0-9]+)\\.([0-9]+).*" "\\2" LLVM_VERSION_MINOR + "${LLVM_VERSION}") + +execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --includedir + OUTPUT_VARIABLE LLVM_INCLUDE_DIRS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --libdir + OUTPUT_VARIABLE LLVM_LIBRARY_DIRS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --cppflags + OUTPUT_VARIABLE LLVM_CFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +if (LLVM_CFLAGS MATCHES "\\-DNDEBUG") + set(LLVM_WITH_NDEBUG TRUE) +else (LLVM_CFLAGS MATCHES "\\-DNDEBUG") + set(LLVM_WITH_NDEBUG FALSE) +endif (LLVM_CFLAGS MATCHES "\\-DNDEBUG") + + +find_library(LLVM_MODULE_LIBS LLVM-${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR} ${LLVM_LIBRARY_DIRS}) +if (NOT LLVM_MODULE_LIBS) + execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --libs + OUTPUT_VARIABLE LLVM_MODULE_LIBS + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endif (NOT LLVM_MODULE_LIBS) + +execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --ldflags + OUTPUT_VARIABLE LLVM_LDFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +if (LLVM_CONFIG_EXECUTABLE) + set(LLVM_FOUND TRUE) +endif (LLVM_CONFIG_EXECUTABLE) diff --git a/include/gras/detail/factory.hpp b/include/gras/detail/factory.hpp index 551ba9e..36023de 100644 --- a/include/gras/detail/factory.hpp +++ b/include/gras/detail/factory.hpp @@ -40,7 +40,7 @@ template <typename ReturnType> void register_factory(const std::string &path, ReturnType(*fcn)()) { void *r = new FactoryRegistryEntryImpl0<ReturnType>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -64,7 +64,7 @@ template <typename ReturnType, typename A0> void register_factory(const std::string &path, ReturnType(*fcn)(const A0 &)) { void *r = new FactoryRegistryEntryImpl1<ReturnType, A0>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -88,7 +88,7 @@ template <typename ReturnType, typename A0, typename A1> void register_factory(const std::string &path, ReturnType(*fcn)(const A0 &, const A1 &)) { void *r = new FactoryRegistryEntryImpl2<ReturnType, A0, A1>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -112,7 +112,7 @@ template <typename ReturnType, typename A0, typename A1, typename A2> void register_factory(const std::string &path, ReturnType(*fcn)(const A0 &, const A1 &, const A2 &)) { void *r = new FactoryRegistryEntryImpl3<ReturnType, A0, A1, A2>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -136,7 +136,7 @@ template <typename ReturnType, typename A0, typename A1, typename A2, typename A void register_factory(const std::string &path, ReturnType(*fcn)(const A0 &, const A1 &, const A2 &, const A3 &)) { void *r = new FactoryRegistryEntryImpl4<ReturnType, A0, A1, A2, A3>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -160,7 +160,7 @@ template <typename ReturnType, typename A0, typename A1, typename A2, typename A void register_factory(const std::string &path, ReturnType(*fcn)(const A0 &, const A1 &, const A2 &, const A3 &, const A4 &)) { void *r = new FactoryRegistryEntryImpl5<ReturnType, A0, A1, A2, A3, A4>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -184,7 +184,7 @@ template <typename ReturnType, typename A0, typename A1, typename A2, typename A void register_factory(const std::string &path, ReturnType(*fcn)(const A0 &, const A1 &, const A2 &, const A3 &, const A4 &, const A5 &)) { void *r = new FactoryRegistryEntryImpl6<ReturnType, A0, A1, A2, A3, A4, A5>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -208,7 +208,7 @@ template <typename ReturnType, typename A0, typename A1, typename A2, typename A void register_factory(const std::string &path, ReturnType(*fcn)(const A0 &, const A1 &, const A2 &, const A3 &, const A4 &, const A5 &, const A6 &)) { void *r = new FactoryRegistryEntryImpl7<ReturnType, A0, A1, A2, A3, A4, A5, A6>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -232,7 +232,7 @@ template <typename ReturnType, typename A0, typename A1, typename A2, typename A void register_factory(const std::string &path, ReturnType(*fcn)(const A0 &, const A1 &, const A2 &, const A3 &, const A4 &, const A5 &, const A6 &, const A7 &)) { void *r = new FactoryRegistryEntryImpl8<ReturnType, A0, A1, A2, A3, A4, A5, A6, A7>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -256,7 +256,7 @@ template <typename ReturnType, typename A0, typename A1, typename A2, typename A void register_factory(const std::string &path, ReturnType(*fcn)(const A0 &, const A1 &, const A2 &, const A3 &, const A4 &, const A5 &, const A6 &, const A7 &, const A8 &)) { void *r = new FactoryRegistryEntryImpl9<ReturnType, A0, A1, A2, A3, A4, A5, A6, A7, A8>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -280,7 +280,7 @@ template <typename ReturnType, typename A0, typename A1, typename A2, typename A void register_factory(const std::string &path, ReturnType(*fcn)(const A0 &, const A1 &, const A2 &, const A3 &, const A4 &, const A5 &, const A6 &, const A7 &, const A8 &, const A9 &)) { void *r = new FactoryRegistryEntryImpl10<ReturnType, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } /*********************************************************************** @@ -290,7 +290,7 @@ inline Element *make(const std::string &path) { PMCList args(0); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } template <typename A0> @@ -298,7 +298,7 @@ Element *make(const std::string &path, const A0 &a0) { PMCList args(1); args[0] = PMC_M(a0); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } template <typename A0, typename A1> @@ -307,7 +307,7 @@ Element *make(const std::string &path, const A0 &a0, const A1 &a1) PMCList args(2); args[0] = PMC_M(a0); args[1] = PMC_M(a1); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } template <typename A0, typename A1, typename A2> @@ -317,7 +317,7 @@ Element *make(const std::string &path, const A0 &a0, const A1 &a1, const A2 &a2) args[0] = PMC_M(a0); args[1] = PMC_M(a1); args[2] = PMC_M(a2); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } template <typename A0, typename A1, typename A2, typename A3> @@ -328,7 +328,7 @@ Element *make(const std::string &path, const A0 &a0, const A1 &a1, const A2 &a2, args[1] = PMC_M(a1); args[2] = PMC_M(a2); args[3] = PMC_M(a3); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } template <typename A0, typename A1, typename A2, typename A3, typename A4> @@ -340,7 +340,7 @@ Element *make(const std::string &path, const A0 &a0, const A1 &a1, const A2 &a2, args[2] = PMC_M(a2); args[3] = PMC_M(a3); args[4] = PMC_M(a4); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5> @@ -353,7 +353,7 @@ Element *make(const std::string &path, const A0 &a0, const A1 &a1, const A2 &a2, args[3] = PMC_M(a3); args[4] = PMC_M(a4); args[5] = PMC_M(a5); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> @@ -367,7 +367,7 @@ Element *make(const std::string &path, const A0 &a0, const A1 &a1, const A2 &a2, args[4] = PMC_M(a4); args[5] = PMC_M(a5); args[6] = PMC_M(a6); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7> @@ -382,7 +382,7 @@ Element *make(const std::string &path, const A0 &a0, const A1 &a1, const A2 &a2, args[5] = PMC_M(a5); args[6] = PMC_M(a6); args[7] = PMC_M(a7); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8> @@ -398,7 +398,7 @@ Element *make(const std::string &path, const A0 &a0, const A1 &a1, const A2 &a2, args[6] = PMC_M(a6); args[7] = PMC_M(a7); args[8] = PMC_M(a8); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9> @@ -415,7 +415,7 @@ Element *make(const std::string &path, const A0 &a0, const A1 &a1, const A2 &a2, args[7] = PMC_M(a7); args[8] = PMC_M(a8); args[9] = PMC_M(a9); - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } } diff --git a/include/gras/factory.hpp b/include/gras/factory.hpp index fd03bb3..819877c 100644 --- a/include/gras/factory.hpp +++ b/include/gras/factory.hpp @@ -6,24 +6,28 @@ #include <gras/gras.hpp> #include <gras/element.hpp> #include <PMC/PMC.hpp> +#include <vector> #include <string> namespace gras { /*! - * Element factory: - * - Register factory functions into the global factory. - * - Call make() to create element from global factory. + * The just in time factory: + * Compile a C++ source and load it into the element factory. + * + * Flags are an optional list of compiler flags. + * See the man page for clang for possible options. + * Example: flags.push_back("-O3") + * + * Include directories control the header file search path. + * Users may leave this empty unless headers + * are installed into non-standard directories. + * + * \param source C++ source code in a string + * \param flags optional compiler flags */ -struct GRAS_API Factory -{ - /******************************************************************* - * Private registration hooks - ******************************************************************/ - static void _register_factory(const std::string &, void *); - static Element *_handle_make(const std::string &, const PMCC &); -}; +GRAS_API void jit_factory(const std::string &source, const std::vector<std::string> &flags); /*********************************************************************** * Register API - don't look here, template magic, not helpful @@ -101,6 +105,12 @@ static Element *make(const std::string &path, const A0 &, const A1 &, const A2 & template <typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9> static Element *make(const std::string &path, const A0 &, const A1 &, const A2 &, const A3 &, const A4 &, const A5 &, const A6 &, const A7 &, const A8 &, const A9 &); + +//! Register factory functions into the global factory. +GRAS_API void _register_factory(const std::string &, void *); + +//! Call make() to create element from global factory. +GRAS_API Element *_handle_make(const std::string &, const PMCC &); } /*! diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index ab94525..2d61b91 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -57,6 +57,7 @@ list(APPEND GRAS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/element.cpp ${CMAKE_CURRENT_SOURCE_DIR}/element_uid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/factory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/jit_factory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sbuffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/circular_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/buffer_queue_circ.cpp @@ -104,6 +105,35 @@ else() endif() ######################################################################## +# Setup factory compiler +######################################################################## +find_program(LLVM_CONFIG_EXECUTABLE llvm-config DOC "llvm-config executable") +if(LLVM_CONFIG_EXECUTABLE) + find_package(LLVM) +endif() +if(LLVM_FOUND) + find_program(CLANG_EXECUTABLE clang DOC "clang executable") + set(CLANG_FOUND ${CLANG_EXECUTABLE}) +else() + message(WARNING "LLVM library not found - optional for factory compiler") +endif() +if(NOT CLANG_FOUND) + message(WARNING "Clang executable not found - optional for factory compiler") +endif() + +if(CLANG_FOUND) + add_definitions(${LLVM_CFLAGS}) + link_directories(${LLVM_LIBRARY_DIRS}) + include_directories(${LLVM_INCLUDE_DIRS}) + list(APPEND GRAS_LIBRARIES ${LLVM_MODULE_LIBS}) + list(APPEND GRAS_LIBRARIES ${LLVM_LDFLAGS}) + SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_CURRENT_SOURCE_DIR}/jit_factory.cpp + PROPERTIES COMPILE_DEFINITIONS "CLANG_EXECUTABLE=${CLANG_EXECUTABLE}" + ) +endif() + +######################################################################## # Build library ######################################################################## add_library(gras SHARED ${GRAS_SOURCES}) diff --git a/lib/factory.cpp b/lib/factory.cpp index 7115197..d168dba 100644 --- a/lib/factory.cpp +++ b/lib/factory.cpp @@ -36,7 +36,7 @@ static FactoryRegistryType &get_factory_registry(void) static boost::mutex mutex; -void Factory::_register_factory(const std::string &path, void *entry) +void gras::_register_factory(const std::string &path, void *entry) { boost::mutex::scoped_lock l(mutex); if (get_factory_registry().count(path) != 0) @@ -46,7 +46,7 @@ void Factory::_register_factory(const std::string &path, void *entry) get_factory_registry()[path].reset(reinterpret_cast<FactoryRegistryEntry *>(entry)); } -Element *Factory::_handle_make(const std::string &path, const PMCC &args) +Element *gras::_handle_make(const std::string &path, const PMCC &args) { boost::mutex::scoped_lock l(mutex); if (get_factory_registry().count(path) == 0) diff --git a/lib/jit_factory.cpp b/lib/jit_factory.cpp new file mode 100644 index 0000000..cdcfc03 --- /dev/null +++ b/lib/jit_factory.cpp @@ -0,0 +1,158 @@ +// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information. + +#include <gras/factory.hpp> +#include <stdexcept> + +#ifdef CLANG_EXECUTABLE + +#include <llvm/LLVMContext.h> +#include <llvm/Support/TargetSelect.h> +#include <llvm/Bitcode/ReaderWriter.h> +#include <llvm/ExecutionEngine/ExecutionEngine.h> +#include <llvm/Support/MemoryBuffer.h> +#include <llvm/ExecutionEngine/JIT.h> +#include <llvm/Support/system_error.h> +#include <llvm/Module.h> + +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/thread/mutex.hpp> +#include <stdexcept> +#include <iostream> +#include <cstdio> +#include <fstream> +#include <map> + +/*********************************************************************** + * Helper function to call a clang compliation + **********************************************************************/ +static std::string call_clang(const std::string &source, const std::vector<std::string> &flags) +{ + //make up bitcode file path + char bitcode_file[L_tmpnam]; + std::tmpnam(bitcode_file); + + //write source to tmp file + char source_file[L_tmpnam]; + std::tmpnam(source_file); + std::ofstream source_fstream(source_file); + source_fstream << source; + source_fstream.close(); + + //begin command setup + std::vector<std::string> cmd; + cmd.push_back(BOOST_STRINGIZE(CLANG_EXECUTABLE)); + cmd.push_back("-emit-llvm"); + + //inject source + cmd.push_back("-c"); + cmd.push_back("-x"); + cmd.push_back("c++"); + cmd.push_back(source_file); + + //inject output + cmd.push_back("-o"); + cmd.push_back(bitcode_file); + + //inject args... + BOOST_FOREACH(const std::string &flag, flags) + { + cmd.push_back(flag.c_str()); + } + + //format command string + std::string command; + BOOST_FOREACH(const std::string &c, cmd) + { + command += c + " "; + } + std::cout << " " << command << std::endl; + const int ret = system(command.c_str()); + if (ret != 0) + { + throw std::runtime_error("GRAS compiler: error system exec clang"); + } + + //readback bitcode for result + std::ifstream bitcode_fstream(bitcode_file); + return std::string((std::istreambuf_iterator<char>(bitcode_fstream)), std::istreambuf_iterator<char>()); +} + +/*********************************************************************** + * Storage for execution engines + **********************************************************************/ +struct ExecutionEngineMonitor +{ + ExecutionEngineMonitor(void) + { + boost::mutex::scoped_lock l(mutex); + } + + ~ExecutionEngineMonitor(void) + { + boost::mutex::scoped_lock l(mutex); + for (size_t i = 0; i < ees.size(); i++) + { + ees[i]->runStaticConstructorsDestructors(true); + ees[i].reset(); + } + ees.clear(); + } + + void add(boost::shared_ptr<llvm::ExecutionEngine> ee) + { + boost::mutex::scoped_lock l(mutex); + ee->runStaticConstructorsDestructors(false); + ees.push_back(ee); + } + + llvm::LLVMContext &get_context(void) + { + return context; + } + + boost::mutex mutex; + llvm::LLVMContext context; + std::vector<boost::shared_ptr<llvm::ExecutionEngine> > ees; +}; + +static ExecutionEngineMonitor &get_eemon(void) +{ + static ExecutionEngineMonitor eemon; + return eemon; +} + +/*********************************************************************** + * factory compile implementation + **********************************************************************/ +void gras::jit_factory(const std::string &source, const std::vector<std::string> &flags) +{ + std::cout << "GRAS compiler: compile source into bitcode..." << std::endl; + const std::string bitcode = call_clang(source, flags); + + llvm::InitializeNativeTarget(); + llvm::llvm_start_multithreaded(); + std::string error; + + //create a memory buffer from the bitcode + boost::shared_ptr<llvm::MemoryBuffer> buffer(llvm::MemoryBuffer::getMemBuffer(bitcode)); + + //parse the bitcode into a module + llvm::Module *module = llvm::ParseBitcodeFile(buffer.get(), get_eemon().get_context(), &error); + if (not error.empty()) throw std::runtime_error("GRAS compiler: ParseBitcodeFile " + error); + + //create execution engine + boost::shared_ptr<llvm::ExecutionEngine> ee(llvm::ExecutionEngine::create(module, false, &error)); + if (not error.empty()) throw std::runtime_error("GRAS compiler: ExecutionEngine " + error); + std::cout << "GRAS compiler: execute static constructors..." << std::endl; + get_eemon().add(ee); +} + +#else //CLANG_EXECUTABLE + +void Factory::compile(const std::string &, const std::vector<std::string> &) +{ + throw std::runtime_error("GRAS compiler not built with Clang support!"); +} + +#endif //CLANG_EXECUTABLE diff --git a/python/gras/GRAS_Factory.i b/python/gras/GRAS_Factory.i index 2740b39..a5a6334 100644 --- a/python/gras/GRAS_Factory.i +++ b/python/gras/GRAS_Factory.i @@ -7,18 +7,16 @@ #include <gras/factory.hpp> %} -namespace gras -{ - %ignore Factory::register_factory; - %ignore Factory::make; -} - -%newobject gras::Factory::_handle_make; +%newobject gras::_handle_make; //////////////////////////////////////////////////////////////////////// // Export swig element comprehension //////////////////////////////////////////////////////////////////////// %include <std_string.i> +%include <std_vector.i> + +%template (StringVector) std::vector<std::string>; + %import <PMC/PMC.i> %import <gras/element.i> %include <gras/gras.hpp> @@ -42,6 +40,18 @@ def make(path, *args, **kwargs): from PMC import PMC_M pmcargs = PMC_M(list(args)) - return Factory._handle_make(path, pmcargs) - + return _handle_make(path, pmcargs) + +def try_load_dll(name): + import ctypes + import ctypes.util + dll_path = ctypes.util.find_library(name) + if not dll_path: dll_path = 'lib'+name+'.so' + ctypes.CDLL(dll_path, ctypes.RTLD_GLOBAL) + +def py_jit_factory(*args): + #load the dlls with ctypes for the JIT factory + try_load_dll("gras") + try_load_dll("pmc") + jit_factory(*args) %} diff --git a/python/gras/__init__.py b/python/gras/__init__.py index 884182c..ed69408 100644 --- a/python/gras/__init__.py +++ b/python/gras/__init__.py @@ -10,7 +10,7 @@ from GRAS_SBuffer import SBufferConfig, SBuffer from GRAS_Tags import Tag, StreamTag, PacketMsg from GRAS_TimeTag import TimeTag from GRAS_Element import Element -from GRAS_Factory import make, register_factory +from GRAS_Factory import make, register_factory, py_jit_factory as jit_factory import GRAS_Block import GRAS_HierBlock import GRAS_TopBlock diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 61d0146..63f6826 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -55,6 +55,7 @@ GR_ADD_TEST(sbuffer_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/sbuffe GR_ADD_TEST(query_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/query_test.py) GR_ADD_TEST(block_calls_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/block_calls_test.py) GR_ADD_TEST(time_tags_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/time_tags_test.py) +GR_ADD_TEST(jit_factory_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/jit_factory_test.py) ######################################################################## # Build an example loadable module diff --git a/tests/jit_factory_test.py b/tests/jit_factory_test.py new file mode 100644 index 0000000..326313c --- /dev/null +++ b/tests/jit_factory_test.py @@ -0,0 +1,150 @@ +# Copyright (C) by Josh Blum. See LICENSE.txt for licensing information. + +import unittest +import gras +import numpy +import time +from gras import TestUtils + +#figure out the local include directories +import os +root_dir = os.path.join(os.path.dirname(__file__), '..') +gras_inc = os.path.join(root_dir, 'include') +pmc_inc = os.path.join(root_dir, 'PMC', 'include') + +ADD_F32_SOURCE = """ +#include <gras/block.hpp> +#include <gras/factory.hpp> +#include <iostream> + +struct MyAddFloat32 : gras::Block +{ + MyAddFloat32(void): + gras::Block("MyAddFloat32") + { + this->input_config(0).item_size = sizeof(float); + this->output_config(0).item_size = sizeof(float); + } + + void work(const InputItems &ins, const OutputItems &outs) + { + const size_t n_nums = std::min(ins.min(), outs.min()); + float *out = outs[0].cast<float *>(); + const float *in0 = ins[0].cast<const float *>(); + const float *in1 = ins[1].cast<const float *>(); + + for (size_t i = 0; i < n_nums; i++) + { + out[i] = in0[i] + in1[i]; + } + + this->consume(n_nums); + this->produce(n_nums); + } +}; + +GRAS_REGISTER_FACTORY0("/tests/my_add_f32", MyAddFloat32) +""" + +ADD_CONST_F32_SOURCE = """ +#include <gras/block.hpp> +#include <gras/factory.hpp> +#include <iostream> + +struct MyAddConstFloat32 : gras::Block +{ + MyAddConstFloat32(void): + gras::Block("MyAddConstFloat32") + { + this->input_config(0).item_size = sizeof(float); + this->output_config(0).item_size = sizeof(float); + this->set_value(0); //initial state + this->register_call("set_value", &MyAddConstFloat32::set_value); + this->register_call("get_value", &MyAddConstFloat32::get_value); + } + + void set_value(const float &value) + { + _value = value; + } + + float get_value(void) + { + return _value; + } + + void work(const InputItems &ins, const OutputItems &outs) + { + const size_t n_nums = std::min(ins.min(), outs.min()); + float *out = outs[0].cast<float *>(); + const float *in = ins[0].cast<const float *>(); + + for (size_t i = 0; i < n_nums; i++) + { + out[i] = in[i] + _value; + } + + this->consume(n_nums); + this->produce(n_nums); + } + + float _value; +}; + +GRAS_REGISTER_FACTORY0("/tests/my_add_const_f32", MyAddConstFloat32) +""" + +class JITFactoryTest(unittest.TestCase): + + def setUp(self): + self.tb = gras.TopBlock() + + def tearDown(self): + self.tb = None + + def test_add_float32(self): + + gras.jit_factory(ADD_F32_SOURCE, ["-O3", "-I"+gras_inc, "-I"+pmc_inc]) + op = gras.make("/tests/my_add_f32") + + vec0 = numpy.array(numpy.random.randint(-150, +150, 10000), numpy.float32) + vec1 = numpy.array(numpy.random.randint(-150, +150, 10000), numpy.float32) + + src0 = TestUtils.VectorSource(numpy.float32, vec0) + src1 = TestUtils.VectorSource(numpy.float32, vec1) + dst = TestUtils.VectorSink(numpy.float32) + + self.tb.connect(src0, (op, 0)) + self.tb.connect(src1, (op, 1)) + self.tb.connect(op, dst) + self.tb.run() + + expected_result = list(vec0 + vec1) + actual_result = list(dst.data()) + + self.assertEqual(expected_result, actual_result) + + def test_add_const_float32(self): + + gras.jit_factory(ADD_CONST_F32_SOURCE, ["-O3", "-I"+gras_inc, "-I"+pmc_inc]) + op = gras.make("/tests/my_add_const_f32") + + offset = 42. + op.set_value(offset) #set offset for test + self.assertAlmostEqual(op.get_value(), offset) + + vec = numpy.array(numpy.random.randint(-150, +150, 10000), numpy.float32) + + src = TestUtils.VectorSource(numpy.float32, vec) + dst = TestUtils.VectorSink(numpy.float32) + + self.tb.connect(src, op, dst) + self.tb.run() + + expected_result = list(vec + offset) + actual_result = list(dst.data()) + + self.assertEqual(expected_result, actual_result) + +if __name__ == '__main__': + unittest.main() diff --git a/tmpl/factory.tmpl.hpp b/tmpl/factory.tmpl.hpp index 162fe6e..dc61064 100644 --- a/tmpl/factory.tmpl.hpp +++ b/tmpl/factory.tmpl.hpp @@ -6,24 +6,28 @@ #include <gras/gras.hpp> #include <gras/element.hpp> #include <PMC/PMC.hpp> +#include <vector> #include <string> namespace gras { /*! - * Element factory: - * - Register factory functions into the global factory. - * - Call make() to create element from global factory. + * The just in time factory: + * Compile a C++ source and load it into the element factory. + * + * Flags are an optional list of compiler flags. + * See the man page for clang for possible options. + * Example: flags.push_back("-O3") + * + * Include directories control the header file search path. + * Users may leave this empty unless headers + * are installed into non-standard directories. + * + * \param source C++ source code in a string + * \param flags optional compiler flags */ -struct GRAS_API Factory -{ - /******************************************************************* - * Private registration hooks - ******************************************************************/ - static void _register_factory(const std::string &, void *); - static Element *_handle_make(const std::string &, const PMCC &); -}; +GRAS_API void jit_factory(const std::string &source, const std::vector<std::string> &flags); /*********************************************************************** * Register API - don't look here, template magic, not helpful @@ -45,6 +49,12 @@ template <$expand('typename A%d', $NARGS)> static Element *make(const std::string &path, $expand('const A%d &', $NARGS)); #end for + +//! Register factory functions into the global factory. +GRAS_API void _register_factory(const std::string &, void *); + +//! Call make() to create element from global factory. +GRAS_API Element *_handle_make(const std::string &, const PMCC &); } /*! diff --git a/tmpl/factory_detail.tmpl.hpp b/tmpl/factory_detail.tmpl.hpp index 660a690..bab73cf 100644 --- a/tmpl/factory_detail.tmpl.hpp +++ b/tmpl/factory_detail.tmpl.hpp @@ -41,7 +41,7 @@ template <typename ReturnType, $expand('typename A%d', $NARGS)> void register_factory(const std::string &path, ReturnType(*fcn)($expand('const A%d &', $NARGS))) { void *r = new FactoryRegistryEntryImpl$(NARGS)<ReturnType, $expand('A%d', $NARGS)>(fcn); - Factory::_register_factory(path, r); + _register_factory(path, r); } #end for @@ -56,7 +56,7 @@ Element *make(const std::string &path, $expand('const A%d &a%d', $NARGS)) #for $i in range($NARGS): args[$i] = PMC_M(a$i); #end for - return Factory::_handle_make(path, PMC_M(args)); + return _handle_make(path, PMC_M(args)); } #end for |