summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Blum2013-08-01 01:12:24 -0700
committerJosh Blum2013-08-01 01:12:24 -0700
commita23bf59c46dae7aa25c4763c6122d0822c88abc7 (patch)
treeaf37ea0c6b2872071938390c857e4efcb4975d86
parent1897808616c91d277e24335a337bec92592fb87a (diff)
downloadsandhi-a23bf59c46dae7aa25c4763c6122d0822c88abc7.tar.gz
sandhi-a23bf59c46dae7aa25c4763c6122d0822c88abc7.tar.bz2
sandhi-a23bf59c46dae7aa25c4763c6122d0822c88abc7.zip
gras: jit factory api + unit tests
-rw-r--r--cmake/Modules/FindClang.cmake50
-rw-r--r--cmake/Modules/FindLLVM.cmake73
-rw-r--r--include/gras/detail/factory.hpp44
-rw-r--r--include/gras/factory.hpp32
-rw-r--r--lib/CMakeLists.txt30
-rw-r--r--lib/factory.cpp4
-rw-r--r--lib/jit_factory.cpp158
-rw-r--r--python/gras/GRAS_Factory.i28
-rw-r--r--python/gras/__init__.py2
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/jit_factory_test.py150
-rw-r--r--tmpl/factory.tmpl.hpp32
-rw-r--r--tmpl/factory_detail.tmpl.hpp4
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