├── doc ├── CMakeLists.txt ├── slides │ └── cppcon'18.pdf └── readme │ ├── readme_is_up-to-date.test │ ├── update_doc.sh │ ├── CMakeLists.txt │ ├── README.md.in │ └── camfilter.cpp ├── tests ├── benchmark.test ├── doc │ └── lit.cfg.in ├── meta │ ├── bad_signature_a.cpp │ ├── bad_signature_c.cpp │ ├── bad_signature_b.cpp │ ├── type_list+func_traits.cpp │ └── new_func_traits.cpp ├── simple │ ├── int_a.cpp │ ├── static_var_a.cpp │ ├── long_a.cpp │ ├── nortti_a.cpp │ ├── ptr_a.cpp │ ├── static_var_b.cpp │ ├── float_a.cpp │ ├── double_a.cpp │ ├── opt_level.cpp │ ├── compose_bad.cpp │ ├── small_struct.cpp │ ├── thread.cpp │ ├── cache.cpp │ ├── exception_a.cpp │ ├── multi_file+regexp.cpp │ ├── serialize.cpp │ ├── unroll.cpp │ ├── int_ptr_a.cpp │ ├── serialize_static.cpp │ ├── devirtualization_nohint.cpp │ ├── devirtualization.cpp │ ├── fun_ptr_a.cpp │ ├── fun_ptr_b.cpp │ ├── fun_ptr_d.cpp │ ├── serialize_multifile.cpp │ ├── fun_ptr_f.cpp │ ├── fun_ptr_c.cpp │ ├── fun_ptr_e.cpp │ ├── compose_ref.cpp │ ├── custom_key_cache.cpp │ ├── struct_return.cpp │ ├── compose_ptr.cpp │ └── struct_arg.cpp ├── install │ ├── CMakeLists.txt │ └── test.cpp └── lit.cfg.in ├── include ├── CMakeLists.txt └── easy │ ├── runtime │ ├── LLVMHolder.h │ ├── Utils.h │ ├── LLVMHolderImpl.h │ ├── Function.h │ ├── RuntimePasses.h │ ├── BitcodeTracker.h │ └── Context.h │ ├── attributes.h │ ├── exceptions.h │ ├── options.h │ ├── jit.h │ ├── code_cache.h │ ├── function_wrapper.h │ ├── param.h │ └── meta.h ├── misc ├── docker │ ├── build_docker.sh │ └── GenDockerfile.py └── doc │ ├── python.py │ ├── include.py │ └── generate.py ├── .gitignore ├── cmake ├── CMakeLists.txt ├── EasyJitConfig.cmake.in ├── FindBenchmark.cmake └── Python.cmake ├── pass ├── StaticPasses.h ├── Utils.h ├── RegisterPasses.cpp ├── CMakeLists.txt ├── Utils.cpp ├── MayAliasTracer.h ├── MayAliasTracer.cpp ├── Layout.cpp └── Easy.cpp ├── runtime ├── pass │ ├── ContextAnalysis.cpp │ ├── InlineParametersHelper.h │ ├── DevirtualizeConstant.cpp │ ├── InlineParametersHelper.cpp │ └── InlineParameters.cpp ├── CMakeLists.txt ├── InitNativeTarget.cpp ├── Context.cpp ├── Utils.cpp ├── BitcodeTracker.cpp └── Function.cpp ├── CMakeTests.txt ├── benchmark ├── CMakeLists.txt └── benchmark.cpp ├── .travis-old.yml ├── LICENSE ├── CMakeLists.txt └── README.md /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(readme) 2 | -------------------------------------------------------------------------------- /tests/benchmark.test: -------------------------------------------------------------------------------- 1 | REQUIRES: benchmark 2 | RUN: %bin/easyjit-benchmark 3 | -------------------------------------------------------------------------------- /doc/slides/cppcon'18.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmmartinez/easy-just-in-time/HEAD/doc/slides/cppcon'18.pdf -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install(DIRECTORY easy 2 | DESTINATION include FILES_MATCHING PATTERN "*.h") 3 | -------------------------------------------------------------------------------- /doc/readme/readme_is_up-to-date.test: -------------------------------------------------------------------------------- 1 | // RUN: cd %S 2 | // RUN: bash update_doc.sh > %t.new 3 | // RUN: cp %S/../../README.md %t.old 4 | // RUN: diff %t.new %t.old 5 | -------------------------------------------------------------------------------- /misc/docker/build_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 GenDockerfile.py ../../.travis.yml > Dockerfile.easy && 3 | docker build -t easy/test -f Dockerfile.easy . 4 | -------------------------------------------------------------------------------- /misc/doc/python.py: -------------------------------------------------------------------------------- 1 | import generate 2 | 3 | def on_python(python_code): 4 | exec (python_code) in {'__builtins__':{}}, {} 5 | return 6 | 7 | generate.match_and_expand("python", on_python) 8 | -------------------------------------------------------------------------------- /include/easy/runtime/LLVMHolder.h: -------------------------------------------------------------------------------- 1 | #ifndef LLVMHOLDER 2 | #define LLVMHOLDER 3 | namespace easy { 4 | class LLVMHolder { 5 | public: 6 | virtual ~LLVMHolder() = default; 7 | }; 8 | } 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.ll 2 | *.ninja_deps 3 | *.ninja_log 4 | */CMakeCache.txt 5 | */CMakeFiles/ 6 | */bin/ 7 | */build.ninja 8 | */cmake_install.cmake 9 | */rules.ninja 10 | */.ninja_log 11 | */__pycache__ 12 | *.py[cod] 13 | -------------------------------------------------------------------------------- /cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/EasyJitConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/EasyJitConfig.cmake @ONLY) 2 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/EasyJitConfig.cmake 3 | DESTINATION lib/cmake) 4 | -------------------------------------------------------------------------------- /pass/StaticPasses.h: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_PASSES 2 | #define STATIC_PASSES 3 | 4 | #include 5 | 6 | namespace easy { 7 | llvm::Pass* createRegisterBitcodePass(); 8 | llvm::Pass* createRegisterLayoutPass(); 9 | } 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /pass/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | 6 | namespace llvm { 7 | class Module; 8 | class Function; 9 | } 10 | 11 | llvm::Function* GetCtor(llvm::Module &M, llvm::Twine Name, unsigned Priority = 65535); 12 | 13 | #endif // UTILS_H 14 | -------------------------------------------------------------------------------- /doc/readme/update_doc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | pushd $DIR >> /dev/null 5 | 6 | easy_jit_dir=../../ 7 | export PYTHONIOENCODING="utf-8" 8 | 9 | cat README.md.in | \ 10 | python3 ${easy_jit_dir}/misc/doc/python.py | \ 11 | python3 ${easy_jit_dir}/misc/doc/include.py 12 | -------------------------------------------------------------------------------- /runtime/pass/ContextAnalysis.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace llvm; 4 | using namespace easy; 5 | 6 | char easy::ContextAnalysis::ID = 0; 7 | 8 | llvm::Pass* easy::createContextAnalysisPass(easy::Context const &C) { 9 | return new ContextAnalysis(C); 10 | } 11 | 12 | static RegisterPass X("", "", true, true); 13 | -------------------------------------------------------------------------------- /tests/doc/lit.cfg.in: -------------------------------------------------------------------------------- 1 | import lit.formats 2 | import lit.util 3 | 4 | from subprocess import call 5 | 6 | lit_config.load_config(config, os.path.join("@CMAKE_CURRENT_BINARY_DIR@", "./tests/lit.cfg")) 7 | config.test_source_root = "@CMAKE_CURRENT_SOURCE_DIR@/doc" 8 | config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@/tests/doc" 9 | 10 | # for the documentation example 11 | if "@EASY_JIT_EXAMPLE@" in ["1", "ON"]: 12 | config.available_features.add('example') 13 | -------------------------------------------------------------------------------- /CMakeTests.txt: -------------------------------------------------------------------------------- 1 | find_python_module(lit REQUIRED) 2 | 3 | set(ALL_TESTS tests/) 4 | 5 | add_custom_target(check 6 | COMMAND ${PYTHON_EXEC} -m lit.main ${ALL_TESTS} -v --shuffle 7 | DEPENDS EasyJitPass EasyJitRuntime) 8 | 9 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tests/lit.cfg.in" "${CMAKE_CURRENT_BINARY_DIR}/tests/lit.cfg") 10 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tests/doc/lit.cfg.in" "${CMAKE_CURRENT_BINARY_DIR}/tests/doc/lit.cfg") 11 | -------------------------------------------------------------------------------- /tests/meta/bad_signature_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %not %clangxx %cxxflags -O2 %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2> %t.log 2 | // RUN: %FileCheck %s < %t.log 3 | 4 | #include 5 | #include 6 | 7 | // CHECK: An easy::jit option is expected 8 | 9 | using namespace std::placeholders; 10 | 11 | int foo(int) { 12 | return 0; 13 | } 14 | 15 | int main(int, char** argv) { 16 | 17 | auto foo_ = easy::jit(foo, 1, 2); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /tests/meta/bad_signature_c.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %not %clangxx %cxxflags -O2 %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2> %t.log 2 | // RUN: %FileCheck %s < %t.log 3 | 4 | #include 5 | #include 6 | 7 | // CHECK: Invalid bind, placeholder cannot be bound to a formal argument 8 | 9 | using namespace std::placeholders; 10 | 11 | int foo(float) { 12 | return 0; 13 | } 14 | 15 | int main(int, char** argv) { 16 | 17 | auto foo_ = easy::jit(foo, _2); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /tests/meta/bad_signature_b.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %not %clangxx %cxxflags -O2 %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2> %t.log 2 | // RUN: %FileCheck %s < %t.log 3 | 4 | #include 5 | #include 6 | 7 | using namespace std::placeholders; 8 | 9 | int foo(int, int, int) { 10 | return 0; 11 | } 12 | 13 | int main(int, char** argv) { 14 | 15 | auto foo_ = easy::jit(foo, 1, 2); 16 | foo_(); // CHECK: easy::jit: not providing enough argument to actual call 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /include/easy/runtime/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS 2 | #define UTILS 3 | 4 | #include 5 | #include 6 | 7 | namespace llvm { 8 | class LLVMContext; 9 | class Module; 10 | class Function; 11 | } 12 | 13 | namespace easy { 14 | 15 | std::string GetEntryFunctionName(llvm::Module const &M); 16 | void MarkAsEntry(llvm::Function &F); 17 | void UnmarkEntry(llvm::Module &M); 18 | 19 | std::unique_ptr 20 | CloneModuleWithContext(llvm::Module const &LM, llvm::LLVMContext &C); 21 | 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /misc/doc/include.py: -------------------------------------------------------------------------------- 1 | import generate 2 | import re 3 | 4 | 5 | def on_include(args): 6 | args = args.split() 7 | filename = args[0]; 8 | label = args[1]; 9 | 10 | all_code = open(filename.strip()).read() 11 | 12 | inline = re.compile(".*// INLINE FROM HERE #"+ label +"#(?P.*)// TO HERE #" + label + "#.*", flags=re.DOTALL) 13 | code = inline.match(all_code) 14 | code = code.group(1).rstrip().lstrip() 15 | 16 | print(code, end='') 17 | return 18 | 19 | generate.match_and_expand("include", on_include) 20 | -------------------------------------------------------------------------------- /cmake/EasyJitConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Try to find the easy::jit library, headers and compiler plugin. 2 | # EasyJit_INCLUDE_DIRS - the easy::jit include directory 3 | # EasyJit_LIBRARY_DIRS - library directory needed to use easy::jit 4 | # EasyJit_LIBRARY - library needed to use easy::jit 5 | # EasyJit_PLUGIN - compiler plugin 6 | 7 | set(EasyJit_INCLUDE_DIRS "@CMAKE_INSTALL_PREFIX@/include") 8 | set(EasyJit_LIBRARY_DIRS "@CMAKE_INSTALL_PREFIX@/lib") 9 | set(EasyJit_LIBRARY "EasyJitRuntime") 10 | set(EasyJit_PLUGIN "@CMAKE_INSTALL_PREFIX@/lib/EasyJitPass@CMAKE_SHARED_LIBRARY_SUFFIX@") 11 | -------------------------------------------------------------------------------- /pass/RegisterPasses.cpp: -------------------------------------------------------------------------------- 1 | #include "StaticPasses.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace llvm; 8 | using namespace easy; 9 | 10 | static void callback(const PassManagerBuilder &, 11 | legacy::PassManagerBase &PM) { 12 | PM.add(easy::createRegisterBitcodePass()); 13 | } 14 | 15 | RegisterStandardPasses Register(PassManagerBuilder::EP_OptimizerLast, callback); 16 | RegisterStandardPasses RegisterO0(PassManagerBuilder::EP_EnabledOnOptLevel0, callback); 17 | -------------------------------------------------------------------------------- /include/easy/attributes.h: -------------------------------------------------------------------------------- 1 | #ifndef NOINLINE 2 | #define NOINLINE 3 | 4 | #define CI_SECTION "compiler-interface" 5 | #define JIT_SECTION "easy-jit" 6 | #define LAYOUT_SECTION "layout" 7 | 8 | // mark functions in the easy::jit interface as no inline. 9 | // it's easier for the pass to find the original functions to be jitted. 10 | #define EASY_JIT_COMPILER_INTERFACE \ 11 | __attribute__((noinline)) __attribute__((section(CI_SECTION))) 12 | 13 | #define EASY_JIT_EXPOSE \ 14 | __attribute__((section(JIT_SECTION))) 15 | 16 | #define EASY_JIT_LAYOUT\ 17 | __attribute__((section(LAYOUT_SECTION))) 18 | 19 | #endif // NOINLINE 20 | -------------------------------------------------------------------------------- /runtime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(EasyJitRuntime SHARED 2 | BitcodeTracker.cpp 3 | Context.cpp 4 | Function.cpp 5 | InitNativeTarget.cpp 6 | Utils.cpp 7 | pass/ContextAnalysis.cpp 8 | pass/DevirtualizeConstant.cpp 9 | pass/InlineParameters.cpp 10 | pass/InlineParametersHelper.cpp 11 | ) 12 | 13 | llvm_config(EasyJitRuntime core codegen interpreter support mcjit native executionengine passes objcarcopts) 14 | 15 | 16 | set(EASY_JIT_RUNTIME ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libEasyJitRuntime${CMAKE_SHARED_LIBRARY_SUFFIX} PARENT_SCOPE) 17 | 18 | install(TARGETS EasyJitRuntime 19 | LIBRARY DESTINATION lib) 20 | -------------------------------------------------------------------------------- /pass/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_llvm_loadable_module(EasyJitPass 2 | RegisterPasses.cpp 3 | Easy.cpp 4 | Layout.cpp 5 | Utils.cpp 6 | MayAliasTracer.cpp 7 | PLUGIN_TOOL clang 8 | ) 9 | 10 | set(EASY_JIT_PASS ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/EasyJitPass${CMAKE_SHARED_LIBRARY_SUFFIX} PARENT_SCOPE) 11 | 12 | install(TARGETS EasyJitPass 13 | LIBRARY DESTINATION lib) 14 | 15 | # Get proper shared-library behavior (where symbols are not necessarily 16 | # resolved when the shared library is linked) on OS X. 17 | if(APPLE) 18 | set_target_properties(EasyJitPass PROPERTIES 19 | LINK_FLAGS "-undefined dynamic_lookup" 20 | ) 21 | endif(APPLE) 22 | -------------------------------------------------------------------------------- /tests/simple/int_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | int add (int a, int b) { 13 | return a+b; 14 | } 15 | 16 | int main() { 17 | easy::FunctionWrapper inc = easy::jit(add, _1, 1); 18 | 19 | // CHECK: inc(4) is 5 20 | // CHECK: inc(5) is 6 21 | // CHECK: inc(6) is 7 22 | // CHECK: inc(7) is 8 23 | for(int v = 4; v != 8; ++v) 24 | printf("inc(%d) is %d\n", v, inc(v)); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/simple/static_var_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | void add (int a, int b) { 13 | printf("inc(%d) is %d\n", a, a+b); 14 | } 15 | 16 | int main() { 17 | easy::FunctionWrapper inc = easy::jit(add, _1, 1); 18 | 19 | // CHECK: inc(4) is 5 20 | // CHECK: inc(5) is 6 21 | // CHECK: inc(6) is 7 22 | // CHECK: inc(7) is 8 23 | for(int v = 4; v != 8; ++v) 24 | inc(v); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/simple/long_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | int add (long a, long b) { 13 | return a+b; 14 | } 15 | 16 | int main() { 17 | easy::FunctionWrapper inc = easy::jit(add, _1, -1); 18 | 19 | // CHECK: inc(4) is 3 20 | // CHECK: inc(5) is 4 21 | // CHECK: inc(6) is 5 22 | // CHECK: inc(7) is 6 23 | for(int v = 4; v != 8; ++v) 24 | printf("inc(%d) is %d\n", v, inc(v)); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/simple/nortti_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t -fno-rtti 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | int add (int a, int b) { 13 | return a+b; 14 | } 15 | 16 | int main() { 17 | easy::FunctionWrapper inc = easy::jit(add, _1, 1); 18 | 19 | // CHECK: inc(4) is 5 20 | // CHECK: inc(5) is 6 21 | // CHECK: inc(6) is 7 22 | // CHECK: inc(7) is 8 23 | for(int v = 4; v != 8; ++v) 24 | printf("inc(%d) is %d\n", v, inc(v)); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/simple/ptr_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | int add (int a, int *b) { 13 | return a+*b; 14 | } 15 | 16 | int main() { 17 | int b = 1; 18 | easy::FunctionWrapper inc = easy::jit(add, _1, &b); 19 | 20 | // CHECK: inc(4) is 5 21 | // CHECK: inc(5) is 6 22 | // CHECK: inc(6) is 7 23 | // CHECK: inc(7) is 8 24 | for(int v = 4; v != 8; ++v) 25 | printf("inc(%d) is %d\n", v, inc(v)); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /tests/simple/static_var_b.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | void add (int b) { 13 | static int a = 4; 14 | printf("inc(%d) is %d\n", a, a+b); 15 | a++; 16 | } 17 | 18 | int main() { 19 | easy::FunctionWrapper inc = easy::jit(add, 1); 20 | 21 | // CHECK: inc(4) is 5 22 | // CHECK: inc(5) is 6 23 | // CHECK: inc(6) is 7 24 | // CHECK: inc(7) is 8 25 | for(int v = 4; v != 8; ++v) 26 | inc(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /pass/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | using namespace llvm; 10 | 11 | Function* GetCtor(Module &M, Twine Name, unsigned Priority) { 12 | LLVMContext &C = M.getContext(); 13 | Type* Void = Type::getVoidTy(C); 14 | FunctionType* VoidFun = FunctionType::get(Void, false); 15 | Function* Ctor = Function::Create(VoidFun, Function::PrivateLinkage, Name, &M); 16 | BasicBlock* Entry = BasicBlock::Create(C, "entry", Ctor); 17 | ReturnInst::Create(C, Entry); 18 | 19 | llvm::appendToGlobalCtors(M, Ctor, Priority); 20 | 21 | return Ctor; 22 | } 23 | -------------------------------------------------------------------------------- /tests/install/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.3) 2 | 3 | project(test) 4 | 5 | find_package(EasyJit REQUIRED CONFIG) 6 | 7 | message("Easy::Jit include dir: " ${EasyJit_INCLUDE_DIRS}) 8 | message("Easy::Jit lib dir: " ${EasyJit_LIBRARY_DIRS}) 9 | message("Easy::Jit runtime: " ${EasyJit_LIBRARY}) 10 | message("Easy::Jit plugin: " ${EasyJit_PLUGIN}) 11 | 12 | include_directories(${EasyJit_INCLUDE_DIRS}) 13 | link_directories(${EasyJit_LIBRARY_DIRS}) 14 | 15 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++14 -Xclang -disable-O0-optnone -Xclang -load -Xclang ${EasyJit_PLUGIN}") 16 | 17 | add_executable(InstallTest 18 | test.cpp 19 | ) 20 | 21 | target_link_libraries(InstallTest ${EasyJit_LIBRARY}) 22 | -------------------------------------------------------------------------------- /tests/simple/float_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | float add (float a, float b) { 13 | return a+b; 14 | } 15 | 16 | int main() { 17 | easy::FunctionWrapper inc = easy::jit(add, _1, 1); 18 | 19 | // CHECK: inc(4.00) is 5.00 20 | // CHECK: inc(5.00) is 6.00 21 | // CHECK: inc(6.00) is 7.00 22 | // CHECK: inc(7.00) is 8.00 23 | for(int v = 4; v != 8; ++v) 24 | printf("inc(%.2f) is %.2f\n", (float)v, inc(v)); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | option(EASY_JIT_BENCHMARK "Enable benchmarking" OFF) 2 | 3 | if(EASY_JIT_BENCHMARK) 4 | set(CMAKE_CXX_COMPILER ${LLVM_TOOLS_BINARY_DIR}/clang++) 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -disable-O0-optnone -Xclang -load -Xclang ${EASY_JIT_PASS}") 6 | 7 | add_executable(easyjit-benchmark benchmark.cpp) 8 | add_dependencies(easyjit-benchmark easy-jit-core) 9 | 10 | find_package(Benchmark REQUIRED) 11 | include_directories(${Benchmark_INCLUDE_DIRS}) 12 | link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) 13 | 14 | target_link_libraries (easyjit-benchmark ${Benchmark_LIBRARIES}) 15 | target_link_libraries (easyjit-benchmark EasyJitRuntime pthread) 16 | endif() 17 | -------------------------------------------------------------------------------- /include/easy/exceptions.h: -------------------------------------------------------------------------------- 1 | #ifndef EXCEPTIONS 2 | #define EXCEPTIONS 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace easy { 9 | struct exception 10 | : public std::runtime_error { 11 | exception(std::string const &Message, std::string const &Reason) 12 | : std::runtime_error(Message + Reason) {} 13 | virtual ~exception() = default; 14 | }; 15 | } 16 | 17 | #define DefineEasyException(Exception, Message) \ 18 | struct Exception : public easy::exception { \ 19 | Exception() : easy::exception(Message, "") {} \ 20 | Exception(std::string const &Reason) : easy::exception(Message, Reason) {} \ 21 | virtual ~Exception() = default; \ 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /tests/simple/double_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | double add (double a, double b) { 13 | return a+b; 14 | } 15 | 16 | int main() { 17 | easy::FunctionWrapper inc = easy::jit(add, _1, 1); 18 | 19 | // CHECK: inc(4.00) is 5.00 20 | // CHECK: inc(5.00) is 6.00 21 | // CHECK: inc(6.00) is 7.00 22 | // CHECK: inc(7.00) is 8.00 23 | for(int v = 4; v != 8; ++v) 24 | printf("inc(%.2f) is %.2f\n", (double)v, inc(v)); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/simple/opt_level.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | int add (int a, int b) { 13 | return a+b; 14 | } 15 | 16 | int main() { 17 | 18 | // TODO: I don't know how to test this correctly. 19 | easy::FunctionWrapper inc = easy::jit(add, _1, 1, easy::options::opt_level(0,1)); 20 | 21 | // CHECK: inc(4) is 5 22 | // CHECK: inc(5) is 6 23 | // CHECK: inc(6) is 7 24 | // CHECK: inc(7) is 8 25 | for(int v = 4; v != 8; ++v) 26 | printf("inc(%d) is %d\n", v, inc(v)); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /include/easy/runtime/LLVMHolderImpl.h: -------------------------------------------------------------------------------- 1 | #ifndef LLVMHOLDER_IMPL 2 | #define LLVMHOLDER_IMPL 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | namespace easy { 10 | class LLVMHolderImpl : public easy::LLVMHolder { 11 | public: 12 | 13 | std::unique_ptr Context_; 14 | std::unique_ptr Engine_; 15 | llvm::Module* M_; // the execution engine has the ownership 16 | 17 | LLVMHolderImpl(std::unique_ptr EE, std::unique_ptr C, llvm::Module* M) 18 | : Context_(std::move(C)), Engine_(std::move(EE)), M_(M) { 19 | } 20 | 21 | virtual ~LLVMHolderImpl() = default; 22 | }; 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /doc/readme/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | option(EASY_JIT_EXAMPLE "Build Examples" OFF) 2 | 3 | if(EASY_JIT_EXAMPLE) 4 | find_package(OpenCV REQUIRED) 5 | 6 | set(CMAKE_CXX_COMPILER ${LLVM_TOOLS_BINARY_DIR}/clang++) 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -disable-O0-optnone -Xclang -load -Xclang ${EASY_JIT_PASS}") 8 | 9 | add_executable(easyjit-example camfilter.cpp) 10 | add_dependencies(easyjit-example easy-jit-core) 11 | 12 | include_directories(${OpenCV_INCLUDE_DIRS}) 13 | target_link_libraries(easyjit-example ${OpenCV_LIBS}) 14 | 15 | link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) 16 | 17 | target_link_libraries (easyjit-example ${Benchmark_LIBRARIES}) 18 | target_link_libraries (easyjit-example EasyJitRuntime pthread) 19 | endif() 20 | -------------------------------------------------------------------------------- /runtime/InitNativeTarget.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace llvm; 8 | 9 | namespace { 10 | class InitNativeTarget { 11 | public: 12 | InitNativeTarget() { 13 | #if defined(_X86) 14 | LLVMInitializeX86Target(); 15 | LLVMInitializeX86TargetInfo(); 16 | LLVMInitializeX86TargetMC(); 17 | LLVMInitializeX86AsmPrinter(); 18 | #elif defined(_AARCH64) 19 | LLVMInitializeAArch64Target(); 20 | LLVMInitializeAArch64TargetInfo(); 21 | LLVMInitializeAArch64TargetMC(); 22 | LLVMInitializeAArch64AsmPrinter(); 23 | #endif 24 | 25 | sys::DynamicLibrary::LoadLibraryPermanently(nullptr); 26 | } 27 | } Init; 28 | } 29 | -------------------------------------------------------------------------------- /tests/simple/compose_bad.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %not %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std::placeholders; 10 | 11 | float mul(float a, float b) { 12 | return a*b; 13 | } 14 | 15 | int accumulate(std::vector const &vec, int acum, int(*fun)(int)) { 16 | int a = acum; 17 | for(int e : vec) 18 | a += fun(e); 19 | return a; 20 | } 21 | 22 | int main(int argc, char** argv) { 23 | 24 | easy::FunctionWrapper mul_by_two = easy::jit(mul, _1, 2.0); 25 | easy::FunctionWrapper const&)> mul_vector_by_two = easy::jit(accumulate, _1, 0, mul_by_two); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /tests/simple/small_struct.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | struct Point { 13 | int x; 14 | int y; 15 | }; 16 | 17 | int add (Point a, Point b) { 18 | return a.x+b.x+a.y+b.y; 19 | } 20 | 21 | int main() { 22 | easy::FunctionWrapper inc = easy::jit(add, _1, Point{1,1}); 23 | 24 | // CHECK: inc(4,4) is 10 25 | // CHECK: inc(5,5) is 12 26 | // CHECK: inc(6,6) is 14 27 | // CHECK: inc(7,7) is 16 28 | for(int v = 4; v != 8; ++v) 29 | printf("inc(%d,%d) is %d\n", v, v, inc(Point{v,v})); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /tests/simple/thread.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -lpthread -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std::placeholders; 12 | 13 | int add (int a, int b) { 14 | return a+b; 15 | } 16 | 17 | int main() { 18 | auto inc_future = std::async(std::launch::async, 19 | [](){ return easy::jit(add, _1, 1);}); 20 | 21 | // CHECK: inc(4) is 5 22 | // CHECK: inc(5) is 6 23 | // CHECK: inc(6) is 7 24 | // CHECK: inc(7) is 8 25 | auto inc = inc_future.get(); 26 | for(int v = 4; v != 8; ++v) 27 | printf("inc(%d) is %d\n", v, inc(v)); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /tests/simple/cache.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | int add (int a, int b) { 13 | return a+b; 14 | } 15 | 16 | int main() { 17 | easy::Cache<> C; 18 | 19 | // CHECK: inc(4) is 5 20 | // CHECK: inc(5) is 6 21 | // CHECK: inc(6) is 7 22 | // CHECK: inc(7) is 8 23 | 24 | for(int i = 0; i != 16; ++i) { 25 | auto const &inc = C.jit(add, _1, 1); 26 | 27 | if(!C.has(add, _1, 1)) { 28 | printf("code not in cache!\n"); 29 | return -1; 30 | } 31 | 32 | for(int v = 4; v != 8; ++v) 33 | printf("inc(%d) is %d\n", v, inc(v)); 34 | } 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /pass/MayAliasTracer.h: -------------------------------------------------------------------------------- 1 | #ifndef MAY_ALIAS_TRACER 2 | #define MAY_ALIAS_TRACER 3 | 4 | #include 5 | 6 | namespace llvm { 7 | class Value; 8 | class GlobalObject; 9 | } 10 | 11 | namespace easy { 12 | class MayAliasTracer { 13 | llvm::SmallPtrSet GOs_; 14 | 15 | using VSet = llvm::SmallPtrSetImpl; 16 | void mayAliasWithStoredValues(llvm::Value* V, VSet &Loaded, VSet &Stored); 17 | void mayAliasWithLoadedValues(llvm::Value* V, VSet &Loaded, VSet &Stored); 18 | 19 | 20 | public: 21 | 22 | MayAliasTracer(llvm::Value* V) { 23 | llvm::SmallPtrSet VLoaded; 24 | llvm::SmallPtrSet VStored; 25 | mayAliasWithLoadedValues(V, VLoaded, VStored); 26 | } 27 | auto count(llvm::GlobalObject& GO) const { return GOs_.count(&GO);} 28 | }; 29 | 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /cmake/FindBenchmark.cmake: -------------------------------------------------------------------------------- 1 | # Try to find the Google Benchmark library and headers. 2 | # Benchmark_FOUND - system has benchmark lib 3 | # Benchmark_INCLUDE_DIRS - the benchmark include directory 4 | # Benchmark_LIBRARIES - libraries needed to use benchmark 5 | 6 | find_path(Benchmark_INCLUDE_DIR 7 | NAMES benchmark/benchmark.h 8 | PATHS ${BENCHMARK_DIR}/include 9 | DOC "The directory where benchmark includes reside" 10 | ) 11 | 12 | find_library(Benchmark_LIBRARY 13 | NAMES benchmark 14 | PATHS ${BENCHMARK_DIR}/lib 15 | DOC "The benchmark library" 16 | ) 17 | 18 | set(Benchmark_INCLUDE_DIRS ${Benchmark_INCLUDE_DIR}) 19 | set(Benchmark_LIBRARIES ${Benchmark_LIBRARY}) 20 | 21 | include(FindPackageHandleStandardArgs) 22 | find_package_handle_standard_args(Benchmark 23 | FOUND_VAR Benchmark_FOUND 24 | REQUIRED_VARS Benchmark_INCLUDE_DIR Benchmark_LIBRARY 25 | ) 26 | 27 | mark_as_advanced(Benchmark_FOUND) 28 | -------------------------------------------------------------------------------- /tests/install/test.cpp: -------------------------------------------------------------------------------- 1 | // REQUIRES: install 2 | // 3 | // clean before, if not there may be unconsistencies 4 | // RUN: rm -fr build.ninja CMakeCache.txt CMakeFiles cmake_install.cmake InstallTest rules.ninja 5 | // 6 | // RUN: cmake -DCMAKE_CXX_COMPILER=%clang++ -DEasyJit_DIR=%install_dir/lib/cmake %S -G Ninja 7 | // RUN: cmake --build . 8 | // RUN: ./InstallTest > %t.out 9 | // RUN: %FileCheck %s < %t.out 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | using namespace std::placeholders; 18 | 19 | void test(int a) { 20 | printf("this is a test %d!\n", a); 21 | } 22 | 23 | int main() { 24 | easy::Cache<> C; 25 | auto test_jit0 = easy::jit(test, 0); 26 | auto const &test_jit1 = C.jit(test, 1); 27 | 28 | // CHECK: this is a test 0! 29 | // CHECK: this is a test 1! 30 | test_jit0(); 31 | test_jit1(); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /tests/simple/exception_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | int add (int a, int b) { 13 | if(a == 8) 14 | throw std::runtime_error{"an expected error occured"}; 15 | return a+b; 16 | } 17 | 18 | int main() { 19 | easy::FunctionWrapper inc = easy::jit(add, _1, 1); 20 | 21 | // CHECK: inc(4) is 5 22 | // CHECK: inc(5) is 6 23 | // CHECK: inc(6) is 7 24 | // CHECK: inc(7) is 8 25 | // CHECK: inc(8) is exception: an expected error occured 26 | // CHECK: inc(9) is 10 27 | for(int v = 4; v != 10; ++v) { 28 | try { 29 | printf("inc(%d) is %d\n", v, inc(v)); 30 | } catch(std::runtime_error &e) { 31 | printf("inc(%d) is exception: %s\n", v, e.what()); 32 | } 33 | } 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /tests/simple/multi_file+regexp.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %s -Xclang -load -Xclang %lib_pass -DMAIN -c -o %t.main.o 2 | // RUN: %clangxx %cxxflags %include_flags %s -Xclang -load -Xclang %lib_pass -DLIB -c -o %t.lib.o -mllvm -easy-export="add" 3 | // RUN: %clangxx %ld_flags %t.main.o %t.lib.o -o %t 4 | // RUN: %t > %t.out 5 | // RUN: %FileCheck %s < %t.out 6 | 7 | #ifdef LIB 8 | 9 | extern "C" int add (int a, int b) { 10 | return a+b; 11 | } 12 | 13 | #endif 14 | 15 | #ifdef MAIN 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | using namespace std::placeholders; 23 | 24 | extern "C" int add (int a, int b); 25 | 26 | int main() { 27 | easy::FunctionWrapper inc = easy::jit(add, _1, 1); 28 | 29 | // CHECK: inc(4) is 5 30 | // CHECK: inc(5) is 6 31 | // CHECK: inc(6) is 7 32 | // CHECK: inc(7) is 8 33 | for(int v = 4; v != 8; ++v) 34 | printf("inc(%d) is %d\n", v, inc(v)); 35 | 36 | return 0; 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /tests/simple/serialize.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std::placeholders; 13 | 14 | int add (int a, int b) { 15 | return a+b; 16 | } 17 | 18 | int main() { 19 | auto inc_store = easy::jit(add, _1, 1); 20 | 21 | std::stringstream out; 22 | inc_store.serialize(out); 23 | out.flush(); 24 | 25 | std::string buffer = out.str(); 26 | 27 | assert(buffer.size()); 28 | printf("buffer.size() = %lu\n", buffer.size()); 29 | 30 | std::stringstream in(buffer); 31 | auto inc_load = easy::FunctionWrapper::deserialize(in); 32 | 33 | // CHECK: inc(4) is 5 34 | // CHECK: inc(5) is 6 35 | // CHECK: inc(6) is 7 36 | // CHECK: inc(7) is 8 37 | for(int v = 4; v != 8; ++v) 38 | printf("inc(%d) is %d\n", v, inc_load(v)); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /tests/simple/unroll.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags -O2 %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t "%t.ll" > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | // only one function 13 | // with only one block (~no branch) 14 | // CHECK-IR: define 15 | // CHECK-IR-NOT: define 16 | // CHECK-IR-NOT: br 17 | // CHECK-IR: ret 18 | 19 | using namespace std::placeholders; 20 | 21 | int dot(int *a, std::vector b, int n) { 22 | int x = 0; 23 | for(size_t i = 0; i != n; ++i) { 24 | x += a[i]*b[i]; 25 | } 26 | return x; 27 | } 28 | 29 | int main(int, char** argv) { 30 | 31 | std::vector a = {1,2,3,4}, 32 | b = {4,3,2,1}; 33 | 34 | auto dot_a = easy::jit(dot, a.data(), _1, 4, easy::options::dump_ir(argv[1])); 35 | int x = dot_a(b); 36 | 37 | // CHECK: dot is 20 38 | printf("dot is %d\n", x); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /tests/simple/int_ptr_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t "%t.ll" > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | // verify that the variable 'b' is not loaded, and the addition is performed using its constant value 13 | // CHECK-IR-NOT: inttoptr 14 | // CHECK-IR-NOT: load i32 15 | // CHECK-IR: add{{.*}}4321 16 | 17 | using namespace std::placeholders; 18 | 19 | static int add (int a, int const *b) { 20 | return a+*b; 21 | } 22 | 23 | int const b = 4321; 24 | 25 | int main(int argc, char** argv) { 26 | easy::FunctionWrapper inc = easy::jit(add, _1, &b, easy::options::dump_ir(argv[1])); 27 | 28 | // CHECK: inc(4) is 4325 29 | // CHECK: inc(5) is 4326 30 | // CHECK: inc(6) is 4327 31 | // CHECK: inc(7) is 4328 32 | for(int v = 4; v != 8; ++v) 33 | printf("inc(%d) is %d\n", v, inc(v)); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /tests/simple/serialize_static.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std::placeholders; 13 | 14 | static int var = 0; 15 | 16 | int add (int a, int b) { 17 | return a+b+var; 18 | } 19 | 20 | int main() { 21 | auto inc_store = easy::jit(add, _1, 1); 22 | 23 | std::stringstream out; 24 | inc_store.serialize(out); 25 | out.flush(); 26 | 27 | std::string buffer = out.str(); 28 | 29 | assert(buffer.size()); 30 | printf("buffer.size() = %lu\n", buffer.size()); 31 | 32 | std::stringstream in(buffer); 33 | auto inc_load = easy::FunctionWrapper::deserialize(in); 34 | 35 | // CHECK: inc(4) is 6 36 | // CHECK: inc(5) is 8 37 | // CHECK: inc(6) is 10 38 | // CHECK: inc(7) is 12 39 | for(int v = 4; v != 8; ++v) { 40 | var++; 41 | printf("inc(%d) is %d\n", v, inc_load(v)); 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /misc/doc/generate.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | 4 | def get_split(tag): 5 | start = re.compile("") 7 | def split(contents): 8 | start_match = start.search(contents, pos=0) 9 | if not start_match: 10 | return contents, "", "", False 11 | end_match = end.search(contents, pos=start_match.end()) 12 | if not end_match: 13 | return contents, "", "", False 14 | pre = contents[0 : start_match.start()] 15 | args = contents[start_match.end() : end_match.start()] 16 | post = contents[end_match.end() : ] 17 | return pre, args, post, True 18 | 19 | return split 20 | 21 | def match_and_expand(tag, expand_with): 22 | do_match_and_expand(sys.stdin.read(), get_split(tag), expand_with) 23 | return 24 | 25 | def do_match_and_expand(contents, split, expand_with): 26 | pre, code, post, match = split(contents) 27 | if match : 28 | do_match_and_expand(pre, split, expand_with) 29 | expand_with(code) 30 | do_match_and_expand(post, split, expand_with) 31 | else: 32 | print(contents, end='') 33 | -------------------------------------------------------------------------------- /tests/simple/devirtualization_nohint.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags -O2 %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t "%t.ll" > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | // only one function 13 | // reading from a global variable 14 | // CHECK-IR-NOT: = tail call 15 | 16 | 17 | using namespace std::placeholders; 18 | 19 | struct Foo { 20 | virtual int doit() { return 1; } 21 | virtual ~Foo() = default; 22 | }; 23 | 24 | struct Bar : Foo { 25 | int doit() override { return 2; } 26 | }; 27 | 28 | int doit(Foo* f) { 29 | return f->doit(); 30 | } 31 | 32 | int main(int argc, char** argv) { 33 | Foo* f = nullptr; 34 | if(argc == 1) 35 | f = new Foo(); 36 | else 37 | f = new Bar(); 38 | 39 | easy::FunctionWrapper easy_doit = easy::jit(doit, f, easy::options::dump_ir(argv[1])); 40 | 41 | // CHECK: doit() is 2 42 | printf("doit() is %d\n", easy_doit()); 43 | 44 | delete f; 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /.travis-old.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | dist: trusty 4 | 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | - llvm-toolchain-trusty-6.0 10 | 11 | packages: 12 | - ninja-build 13 | - g++-6 14 | - llvm-6.0-dev 15 | - llvm-6.0-tools 16 | - clang-6.0 17 | - libopencv-dev 18 | 19 | before_install: 20 | - pip install --user --upgrade pip 21 | - pip install --user lit 22 | 23 | script: 24 | - mkdir _build && cd _build 25 | - git clone --depth=1 https://github.com/google/benchmark.git && git clone --depth=1 https://github.com/google/googletest.git benchmark/googletest && mkdir benchmark/_build && cd benchmark/_build && cmake .. -GNinja -DCMAKE_INSTALL_PREFIX=`pwd`/../_install && ninja && ninja install && cd ../.. 26 | - cmake -DLLVM_DIR=/usr/lib/llvm-6.0/cmake -DCMAKE_CXX_COMPILER=clang++-6.0 -DCMAKE_C_COMPILER=clang-6.0 -DEASY_JIT_EXAMPLE=ON -DEASY_JIT_BENCHMARK=ON -DBENCHMARK_DIR=`pwd`/benchmark/_install -DCMAKE_INSTALL_PREFIX=`pwd`/../_install .. -G Ninja 27 | - ninja 28 | - ninja install 29 | - ninja check 30 | 31 | -------------------------------------------------------------------------------- /tests/simple/devirtualization.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags -O2 %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t "%t.ll" > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | // only one function 13 | // reading from a global variable 14 | // CHECK-IR-NOT: = tail call 15 | 16 | 17 | using namespace std::placeholders; 18 | 19 | struct Foo { 20 | virtual int EASY_JIT_EXPOSE doit() { return 1; } 21 | virtual ~Foo() = default; 22 | }; 23 | 24 | struct Bar : Foo { 25 | int EASY_JIT_EXPOSE doit() override { return 2; } 26 | }; 27 | 28 | int doit(Foo* f) { 29 | return f->doit(); 30 | } 31 | 32 | int main(int argc, char** argv) { 33 | Foo* f = nullptr; 34 | if(argc == 1) 35 | f = new Foo(); 36 | else 37 | f = new Bar(); 38 | 39 | easy::FunctionWrapper easy_doit = easy::jit(doit, f, easy::options::dump_ir(argv[1])); 40 | 41 | // CHECK: doit() is 2 42 | printf("doit() is %d\n", easy_doit()); 43 | 44 | delete f; 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /tests/simple/fun_ptr_a.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t "%t.ll" > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | // only one function 13 | // reading from a global variable 14 | // CHECK-IR: @[[GLOBAL:.+]] = external 15 | // CHECK-IR: define 16 | // CHECK-IR-NOT: define 17 | // CHECK-IR-NOT: br 18 | // CHECK-IR: load{{.*}}[[GLOBAL]] 19 | // CHECK-IR: add 20 | // CHECK-IR: store{{.*}}[[GLOBAL]] 21 | // CHECK-IR: ret 22 | 23 | 24 | using namespace std::placeholders; 25 | 26 | static int bubu() { 27 | static int v = 0; 28 | return v++; 29 | } 30 | 31 | static int add (int a, int (*f)()) { 32 | return a+f(); 33 | } 34 | 35 | int main(int argc, char** argv) { 36 | easy::FunctionWrapper inc = easy::jit(add, _1, bubu, easy::options::dump_ir(argv[1])); 37 | 38 | // CHECK: inc(4) is 4 39 | // CHECK: inc(5) is 6 40 | // CHECK: inc(6) is 8 41 | // CHECK: inc(7) is 10 42 | for(int v = 4; v != 8; ++v) 43 | printf("inc(%d) is %d\n", v, inc(v)); 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /tests/simple/fun_ptr_b.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t "%t.ll" > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | // only one function, without any call 13 | // CHECK-IR-NOT: call{{.*@.*}} 14 | 15 | 16 | using namespace std::placeholders; 17 | 18 | static void foo(void* dat) { 19 | (*(int*)dat) += 1; 20 | } 21 | 22 | static void map (void* data, unsigned nmemb, unsigned size, void (*f)(void*)) { 23 | for(unsigned i = 0; i < nmemb; ++i) 24 | f((char*)data + i * size); 25 | } 26 | 27 | int main(int argc, char** argv) { 28 | easy::FunctionWrapper map_w = easy::jit(map, _1, _2, _3, foo, easy::options::dump_ir(argv[1])); 29 | 30 | int data[] = {1,2,3,4}; 31 | map_w(data, sizeof(data)/sizeof(data[0]), sizeof(data[0])); 32 | 33 | // CHECK: data[0] is 2 34 | // CHECK: data[1] is 3 35 | // CHECK: data[2] is 4 36 | // CHECK: data[3] is 5 37 | for(int v = 0; v != 4; ++v) 38 | printf("data[%d] is %d\n", v, data[v]); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /runtime/Context.cpp: -------------------------------------------------------------------------------- 1 | #include "easy/runtime/Context.h" 2 | 3 | using namespace easy; 4 | 5 | Context& Context::setParameterIndex(unsigned param_idx) { 6 | return setArg(param_idx); 7 | } 8 | 9 | Context& Context::setParameterInt(int64_t val) { 10 | return setArg(val); 11 | } 12 | 13 | Context& Context::setParameterFloat(double val) { 14 | return setArg(val); 15 | } 16 | 17 | Context& Context::setParameterPointer(const void* val) { 18 | return setArg(val); 19 | } 20 | 21 | Context& Context::setParameterStruct(serialized_arg arg) { 22 | return setArg(std::move(arg)); 23 | } 24 | 25 | Context& Context::setParameterModule(easy::Function const &F) { 26 | return setArg(F); 27 | } 28 | 29 | bool Context::operator==(const Context& Other) const { 30 | if(getOptLevel() != Other.getOptLevel()) 31 | return false; 32 | if(size() != Other.size()) 33 | return false; 34 | 35 | for(auto this_it = begin(), other_it = Other.begin(); 36 | this_it != end(); ++this_it, ++other_it) { 37 | ArgumentBase &ThisArg = **this_it; 38 | ArgumentBase &OtherArg = **other_it; 39 | if(!(ThisArg == OtherArg)) 40 | return false; 41 | } 42 | 43 | return true; 44 | } 45 | -------------------------------------------------------------------------------- /include/easy/options.h: -------------------------------------------------------------------------------- 1 | #ifndef OPTIONS 2 | #define OPTIONS 3 | 4 | #include 5 | 6 | #define EASY_NEW_OPTION_STRUCT(Name) \ 7 | struct Name; \ 8 | template<> struct is_option { \ 9 | static constexpr bool value = true; }; \ 10 | struct Name 11 | 12 | #define EASY_HANDLE_OPTION_STRUCT(Name, Ctx) \ 13 | void handle(easy::Context &Ctx) const 14 | 15 | 16 | namespace easy { 17 | namespace options{ 18 | 19 | template 20 | struct is_option { 21 | static constexpr bool value = false; 22 | }; 23 | 24 | EASY_NEW_OPTION_STRUCT(opt_level) 25 | : public std::pair { 26 | 27 | opt_level(unsigned OptLevel, unsigned OptSize) 28 | : std::pair(OptLevel,OptSize) {} 29 | 30 | EASY_HANDLE_OPTION_STRUCT(opt_level, C) { 31 | C.setOptLevel(first, second); 32 | } 33 | }; 34 | 35 | // option used for writing the ir to a file, useful for debugging 36 | EASY_NEW_OPTION_STRUCT(dump_ir) { 37 | dump_ir(std::string const &file) 38 | : file_(file) {} 39 | 40 | EASY_HANDLE_OPTION_STRUCT(dump_ir, C) { 41 | C.setDebugFile(file_); 42 | } 43 | 44 | private: 45 | std::string file_; 46 | }; 47 | } 48 | } 49 | 50 | #endif // OPTIONS 51 | -------------------------------------------------------------------------------- /tests/simple/fun_ptr_d.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t "%t.ll" > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | // only one function 13 | // reading from a global variable 14 | // CHECK-IR: @[[GLOBAL:.+]] = external 15 | // CHECK-IR: define 16 | // CHECK-IR-NOT: define 17 | // CHECK-IR-NOT: br 18 | // CHECK-IR: load{{.*}}[[GLOBAL]] 19 | // CHECK-IR: add 20 | // CHECK-IR: store{{.*}}[[GLOBAL]] 21 | // CHECK-IR: ret 22 | 23 | 24 | using namespace std::placeholders; 25 | 26 | static int bubu() { 27 | static int v = 0; 28 | return v++; 29 | } 30 | static int bibi() { 31 | return 0; 32 | } 33 | 34 | static int add (int a, int (*f)()) { 35 | return a+f(); 36 | } 37 | 38 | int main(int argc, char** argv) { 39 | easy::FunctionWrapper inc = easy::jit(add, _1, argc?bubu:bibi, easy::options::dump_ir(argv[1])); 40 | 41 | // CHECK: inc(4) is 4 42 | // CHECK: inc(5) is 6 43 | // CHECK: inc(6) is 8 44 | // CHECK: inc(7) is 10 45 | for(int v = 4; v != 8; ++v) 46 | printf("inc(%d) is %d\n", v, inc(v)); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/simple/serialize_multifile.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %s -DMAIN -c -o %t.main.o 2 | // RUN: %clangxx %cxxflags %include_flags %s -Xclang -load -Xclang %lib_pass -DLIB -c -o %t.lib.o -mllvm -easy-export="add" 3 | // RUN: %clangxx %ld_flags %t.main.o %t.lib.o -o %t 4 | // RUN: %t > %t.out 5 | // RUN: %FileCheck %s < %t.out 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace std::placeholders; 15 | 16 | #ifdef LIB 17 | 18 | static int var = 0; 19 | 20 | static int add (int a, int b) { 21 | return a+b+(var++); 22 | } 23 | 24 | std::string get_add(int b) { 25 | auto inc_store = easy::jit(add, _1, 1); 26 | 27 | std::ostringstream out; 28 | inc_store.serialize(out); 29 | out.flush(); 30 | 31 | return out.str(); 32 | } 33 | 34 | #endif 35 | 36 | #ifdef MAIN 37 | 38 | std::string get_add(int b); 39 | 40 | int main() { 41 | 42 | std::string bitcode = get_add(1); 43 | std::istringstream in(bitcode); 44 | auto inc_load = easy::FunctionWrapper::deserialize(in); 45 | 46 | // CHECK: inc(4) is 5 47 | // CHECK: inc(5) is 7 48 | // CHECK: inc(6) is 9 49 | // CHECK: inc(7) is 11 50 | for(int v = 4; v != 8; ++v) { 51 | printf("inc(%d) is %d\n", v, inc_load(v)); 52 | } 53 | 54 | return 0; 55 | } 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/easy/runtime/Function.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTION 2 | #define FUNCTION 3 | 4 | #include "LLVMHolder.h" 5 | #include 6 | 7 | namespace easy { 8 | class Function; 9 | } 10 | 11 | namespace llvm { 12 | class Module; 13 | } 14 | 15 | namespace std { 16 | template<> struct hash 17 | { 18 | typedef easy::Function argument_type; 19 | typedef std::size_t result_type; 20 | result_type operator()(argument_type const& F) const noexcept; 21 | }; 22 | } 23 | 24 | namespace easy { 25 | 26 | class Context; 27 | struct GlobalMapping; 28 | 29 | class Function { 30 | 31 | // do not reorder the fields and do not add virtual methods! 32 | void* Address; 33 | std::unique_ptr Holder; 34 | 35 | public: 36 | 37 | Function(void* Addr, std::unique_ptr H); 38 | 39 | void* getRawPointer() const { 40 | return Address; 41 | } 42 | 43 | void serialize(std::ostream&) const; 44 | static std::unique_ptr deserialize(std::istream&); 45 | 46 | bool operator==(easy::Function const&) const; 47 | 48 | llvm::Module const& getLLVMModule() const; 49 | 50 | static std::unique_ptr Compile(void *Addr, easy::Context const &C); 51 | 52 | friend 53 | std::hash::result_type std::hash::operator()(argument_type const& F) const noexcept; 54 | }; 55 | 56 | } 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /tests/simple/fun_ptr_f.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t "%t.ll" > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | // only one function, without any call 13 | // CHECK-IR-NOT: call{{ }} 14 | 15 | 16 | using namespace std::placeholders; 17 | 18 | static void foo(void* dat) { 19 | (*(int*)dat) += 1; 20 | } 21 | static void bar(void* dat) { 22 | (*(int*)dat) += 2; 23 | } 24 | 25 | static void map (void* data, unsigned nmemb, unsigned size, void (*f)(void*)) { 26 | for(unsigned i = 0; i < nmemb; ++i) 27 | f((char*)data + i * size); 28 | } 29 | 30 | int main(int argc, char** argv) { 31 | 32 | void (*(come_and_get_some[2]))(void*dat) = {foo, bar}; 33 | 34 | easy::FunctionWrapper map_w = easy::jit(map, _1, _2, _3, come_and_get_some[argc?1:0], easy::options::dump_ir(argv[1])); 35 | 36 | int data[] = {1,2,3,4}; 37 | map_w(data, sizeof(data)/sizeof(data[0]), sizeof(data[0])); 38 | 39 | // CHECK: data[0] is 3 40 | // CHECK: data[1] is 4 41 | // CHECK: data[2] is 5 42 | // CHECK: data[3] is 6 43 | for(int v = 0; v != 4; ++v) 44 | printf("data[%d] is %d\n", v, data[v]); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /tests/simple/fun_ptr_c.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t "%t.ll" > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | // only one function, without any call 13 | // CHECK-IR-NOT: call{{ }} 14 | 15 | 16 | using namespace std::placeholders; 17 | 18 | static void foo(void* dat) { 19 | (*(int*)dat) += 1; 20 | } 21 | static void bar(void* dat) { 22 | (*(int*)dat) += 2; 23 | } 24 | 25 | static void map (void* data, unsigned nmemb, unsigned size, void (*f)(void*)) { 26 | for(unsigned i = 0; i < nmemb; ++i) 27 | f((char*)data + i * size); 28 | } 29 | 30 | int main(int argc, char** argv) { 31 | 32 | static void (*(come_and_get_some[]))(void*dat) = {foo, bar}; 33 | 34 | easy::FunctionWrapper map_w = easy::jit(map, _1, _2, _3, come_and_get_some[argc?1:0], easy::options::dump_ir(argv[1])); 35 | 36 | int data[] = {1,2,3,4}; 37 | map_w(data, sizeof(data)/sizeof(data[0]), sizeof(data[0])); 38 | 39 | // CHECK: data[0] is 3 40 | // CHECK: data[1] is 4 41 | // CHECK: data[2] is 5 42 | // CHECK: data[3] is 6 43 | for(int v = 0; v != 4; ++v) 44 | printf("data[%d] is %d\n", v, data[v]); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /tests/simple/fun_ptr_e.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t "%t.ll" > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | // only one function, without any call 13 | // CHECK-IR-NOT: call{{ }} 14 | 15 | 16 | using namespace std::placeholders; 17 | 18 | static void foo(void* dat) { 19 | (*(int*)dat) += 1; 20 | } 21 | static void bar(void* dat) { 22 | (*(int*)dat) += 2; 23 | } 24 | 25 | static void map (void* data, unsigned nmemb, unsigned size, void (*f)(void*)) { 26 | for(unsigned i = 0; i < nmemb; ++i) 27 | f((char*)data + i * size); 28 | } 29 | 30 | int main(int argc, char** argv) { 31 | 32 | void (*(come_and_get_some[2]))(void*dat); 33 | come_and_get_some[0] = foo; 34 | come_and_get_some[1] = bar; 35 | 36 | easy::FunctionWrapper map_w = easy::jit(map, _1, _2, _3, come_and_get_some[argc?1:0], easy::options::dump_ir(argv[1])); 37 | 38 | int data[] = {1,2,3,4}; 39 | map_w(data, sizeof(data)/sizeof(data[0]), sizeof(data[0])); 40 | 41 | // CHECK: data[0] is 3 42 | // CHECK: data[1] is 4 43 | // CHECK: data[2] is 5 44 | // CHECK: data[3] is 6 45 | for(int v = 0; v != 4; ++v) 46 | printf("data[%d] is %d\n", v, data[v]); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/simple/compose_ref.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t 8 1 2 3 4 5 6 7 8 %t.ll > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | // 6 | // CHECK: 72 7 | // 8 | // only one function in the final IR 9 | // CHECK-IR: define 10 | // CHECK-IR-NOT: define 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace std::placeholders; 19 | 20 | int mul(int a, int b) { 21 | return a*b; 22 | } 23 | 24 | int accumulate(std::vector const &vec, int acum, int(&fun)(int)) { 25 | int a = acum; 26 | for(int e : vec) 27 | a += fun(e); 28 | return a; 29 | } 30 | 31 | int main(int argc, char** argv) { 32 | 33 | int n = atoi(argv[1]); 34 | 35 | // read input 36 | std::vector vec; 37 | for(int i = 0; i != n; ++i) 38 | vec.emplace_back(atoi(argv[i+2])); 39 | 40 | // generate code 41 | easy::FunctionWrapper mul_by_two = easy::jit(mul, _1, 2); 42 | easy::FunctionWrapper const&)> mul_vector_by_two = easy::jit(accumulate, _1, 0, mul_by_two, 43 | easy::options::dump_ir(argv[argc-1])); 44 | 45 | // kernel! 46 | int result = mul_vector_by_two(vec); 47 | 48 | // output 49 | printf("%d\n", result); 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /tests/simple/custom_key_cache.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std::placeholders; 12 | 13 | int add (int a, int b) { 14 | return a+b; 15 | } 16 | 17 | void test_int() { 18 | easy::Cache C; 19 | 20 | for(int i = 0; i != 6; ++i) { 21 | auto const &inc = C.jit(i, add, _1, i); 22 | 23 | if(!C.has(i)) { 24 | printf("code not in cache!\n"); 25 | } 26 | 27 | // CHECK-NOT: code not in cache 28 | // CHECK: inc.int(0) is 0 29 | // CHECK: inc.int(0) is 1 30 | // CHECK: inc.int(0) is 2 31 | // CHECK: inc.int(0) is 3 32 | // CHECK: inc.int(0) is 4 33 | // CHECK: inc.int(0) is 5 34 | 35 | printf("inc.int(%d) is %d\n", 0, inc(0)); 36 | } 37 | } 38 | 39 | void test_string() { 40 | easy::Cache C; 41 | 42 | for(int i = 0; i != 6; ++i) { 43 | auto const &inc = C.jit(std::to_string(i), add, _1, i); 44 | 45 | if(!C.has(std::to_string(i))) { 46 | printf("code not in cache!\n"); 47 | } 48 | 49 | // CHECK-NOT: code not in cache 50 | // CHECK: inc.str(0) is 0 51 | // CHECK: inc.str(0) is 1 52 | // CHECK: inc.str(0) is 2 53 | // CHECK: inc.str(0) is 3 54 | // CHECK: inc.str(0) is 4 55 | // CHECK: inc.str(0) is 5 56 | 57 | printf("inc.str(%d) is %d\n", 0, inc(0)); 58 | } 59 | } 60 | 61 | int main() { 62 | test_int(); 63 | test_string(); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /cmake/Python.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | # allow specifying which Python installation to use 4 | if (NOT PYTHON_EXEC) 5 | set(PYTHON_EXEC $ENV{PYTHON_EXEC}) 6 | endif (NOT PYTHON_EXEC) 7 | 8 | if (NOT PYTHON_EXEC) 9 | find_program(PYTHON_EXEC "python${Python_FIND_VERSION}" 10 | DOC "Location of python executable to use") 11 | endif(NOT PYTHON_EXEC) 12 | 13 | execute_process(COMMAND "${PYTHON_EXEC}" "-c" 14 | "import sys; print('%d.%d' % (sys.version_info[0],sys.version_info[1]))" 15 | OUTPUT_VARIABLE PYTHON_VERSION 16 | OUTPUT_STRIP_TRAILING_WHITESPACE) 17 | string(REPLACE "." "" PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION}) 18 | 19 | 20 | function(find_python_module module) 21 | string(TOUPPER ${module} module_upper) 22 | if(NOT PY_${module_upper}) 23 | if(ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED") 24 | set(${module}_FIND_REQUIRED TRUE) 25 | endif() 26 | # A module's location is usually a directory, but for binary modules it's a .so file. 27 | execute_process(COMMAND "${PYTHON_EXEC}" "-c" "import re, ${module}; print re.compile('/__init__.py.*').sub('',${module}.__file__)" 28 | RESULT_VARIABLE _${module}_status 29 | OUTPUT_VARIABLE _${module}_location 30 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) 31 | if(NOT _${module}_status) 32 | set(PY_${module_upper} ${_${module}_location} CACHE STRING "Location of Python module ${module}") 33 | endif(NOT _${module}_status) 34 | endif(NOT PY_${module_upper}) 35 | find_package_handle_standard_args(PY_${module} DEFAULT_MSG PY_${module_upper}) 36 | endfunction(find_python_module) 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Juan Manuel Martinez Caamaño and Serge Guelton and Quarkslab. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /runtime/pass/InlineParametersHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef INLINEPARAMETERSHELPER_H 2 | #define INLINEPARAMETERSHELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | namespace easy { 13 | 14 | struct HighLevelLayout { 15 | struct HighLevelArg { 16 | size_t Position_; 17 | size_t FirstParamIdx_; 18 | llvm::SmallVector Types_; 19 | bool StructByPointer_ = false; 20 | 21 | HighLevelArg(size_t Pos, size_t FirstParamIdx) : 22 | Position_(Pos), FirstParamIdx_(FirstParamIdx) { } 23 | explicit HighLevelArg() = default; 24 | }; 25 | 26 | llvm::Type* StructReturn_; 27 | llvm::SmallVector Args_; 28 | llvm::Type* Return_; 29 | 30 | HighLevelLayout(easy::Context const& C, llvm::Function &F); 31 | }; 32 | 33 | llvm::SmallVector GetForwardArgs(easy::HighLevelLayout::HighLevelArg &ArgInF, easy::HighLevelLayout &FHLL, 34 | llvm::Function &Wrapper, easy::HighLevelLayout &WrapperHLL); 35 | llvm::Constant* GetScalarArgument(easy::ArgumentBase const& Arg, llvm::Type* T); 36 | 37 | llvm::Constant* LinkPointerIfPossible(llvm::Module &M, easy::PtrArgument const &Ptr, llvm::Type* PtrTy); 38 | 39 | llvm::AllocaInst* GetStructAlloc(llvm::IRBuilder<> &B, llvm::DataLayout const &DL, easy::StructArgument const &Struct, llvm::Type* StructPtrTy); 40 | 41 | std::pair GetConstantFromRaw(llvm::DataLayout const& DL, llvm::Type* T, const uint8_t* Raw); 42 | 43 | } 44 | 45 | #endif // INLINEPARAMETERSHELPER_H 46 | -------------------------------------------------------------------------------- /tests/simple/struct_return.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::placeholders; 11 | 12 | template 13 | struct Point { 14 | T x; 15 | T y; 16 | }; 17 | 18 | template 19 | Point build (T a, T b) { 20 | return Point{(T)(a-7), (T)(2*b)}; 21 | } 22 | 23 | template 24 | void test() { 25 | T val = 1; 26 | easy::FunctionWrapper(T)> build_val = easy::jit(build, _1, val); 27 | 28 | for(int v = 4; v != 8; ++v) { 29 | Point xy = build_val((T)v); 30 | printf("point = %d %d \n", (int)xy.x, (int)xy.y); 31 | } 32 | } 33 | 34 | int main() { 35 | 36 | // CHECK: point = -3 2 37 | // CHECK: point = -2 2 38 | // CHECK: point = -1 2 39 | // CHECK: point = 0 2 40 | test(); 41 | 42 | // CHECK: point = -3 2 43 | // CHECK: point = -2 2 44 | // CHECK: point = -1 2 45 | // CHECK: point = 0 2 46 | test(); 47 | 48 | // CHECK: point = -3 2 49 | // CHECK: point = -2 2 50 | // CHECK: point = -1 2 51 | // CHECK: point = 0 2 52 | test(); 53 | 54 | // CHECK: point = -3 2 55 | // CHECK: point = -2 2 56 | // CHECK: point = -1 2 57 | // CHECK: point = 0 2 58 | test(); 59 | 60 | // CHECK: point = -3 2 61 | // CHECK: point = -2 2 62 | // CHECK: point = -1 2 63 | // CHECK: point = 0 2 64 | test(); 65 | 66 | // CHECK: point = -3 2 67 | // CHECK: point = -2 2 68 | // CHECK: point = -1 2 69 | // CHECK: point = 0 2 70 | test(); 71 | 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /tests/simple/compose_ptr.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t 8 1 2 3 4 5 6 7 8 %t.ll > %t.out 3 | // RUN: %FileCheck %s < %t.out 4 | // RUN: %FileCheck --check-prefix=CHECK-IR %s < %t.ll 5 | // 6 | // CHECK: 72 7 | // 8 | // only one function in the final IR 9 | // CHECK-IR: define 10 | // CHECK-IR-NOT: define 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace std::placeholders; 19 | 20 | int mul(int a, int b) { 21 | return a*b; 22 | } 23 | 24 | int accumulate(std::vector const &vec, int acum, int(*fun)(int)) { 25 | int a = acum; 26 | for(int e : vec) 27 | a += fun(e); 28 | return a; 29 | } 30 | 31 | int main(int argc, char** argv) { 32 | 33 | int n = atoi(argv[1]); 34 | 35 | // read input 36 | std::vector vec; 37 | for(int i = 0; i != n; ++i) 38 | vec.emplace_back(atoi(argv[i+2])); 39 | 40 | // generate code 41 | easy::FunctionWrapper mul_by_two = easy::jit(mul, _1, 2); 42 | 43 | static_assert(easy::is_function_wrapper::value, "Value not detected as function wrapper!"); 44 | static_assert(easy::is_function_wrapper::value, "Reference not detected as function wrapper!"); 45 | static_assert(easy::is_function_wrapper::value, "RReference not detected as function wrapper!"); 46 | 47 | easy::FunctionWrapper const&)> mul_vector_by_two = easy::jit(accumulate, _1, 0, mul_by_two, 48 | easy::options::dump_ir(argv[argc-1])); 49 | 50 | // kernel! 51 | int result = mul_vector_by_two(vec); 52 | 53 | // output 54 | printf("%d\n", result); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /include/easy/runtime/RuntimePasses.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNTIME_PASSES 2 | #define RUNTIME_PASSES 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace easy { 9 | struct ContextAnalysis : 10 | public llvm::ImmutablePass { 11 | 12 | static char ID; 13 | 14 | ContextAnalysis() 15 | : llvm::ImmutablePass(ID), C_(nullptr) {} 16 | ContextAnalysis(Context const &C) 17 | : llvm::ImmutablePass(ID), C_(&C) {} 18 | 19 | easy::Context const& getContext() const { 20 | return *C_; 21 | } 22 | 23 | private: 24 | 25 | easy::Context const *C_; 26 | }; 27 | 28 | struct InlineParameters: 29 | public llvm::ModulePass { 30 | 31 | static char ID; 32 | 33 | InlineParameters() 34 | : llvm::ModulePass(ID) {} 35 | InlineParameters(llvm::StringRef TargetName) 36 | : llvm::ModulePass(ID), TargetName_(TargetName) {} 37 | 38 | void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { 39 | AU.addRequired(); 40 | } 41 | 42 | bool runOnModule(llvm::Module &M) override; 43 | 44 | private: 45 | llvm::StringRef TargetName_; 46 | }; 47 | 48 | struct DevirtualizeConstant : 49 | public llvm::FunctionPass { 50 | 51 | static char ID; 52 | 53 | DevirtualizeConstant() 54 | : llvm::FunctionPass(ID) {} 55 | DevirtualizeConstant(llvm::StringRef TargetName) 56 | : llvm::FunctionPass(ID), TargetName_(TargetName) {} 57 | 58 | void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { 59 | AU.addRequired(); 60 | } 61 | 62 | bool runOnFunction(llvm::Function &F) override; 63 | 64 | private: 65 | llvm::StringRef TargetName_; 66 | }; 67 | 68 | llvm::Pass* createContextAnalysisPass(easy::Context const &C); 69 | llvm::Pass* createInlineParametersPass(llvm::StringRef Name); 70 | llvm::Pass* createDevirtualizeConstantPass(llvm::StringRef Name); 71 | } 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /include/easy/runtime/BitcodeTracker.h: -------------------------------------------------------------------------------- 1 | #ifndef BITCODETRACKER 2 | #define BITCODETRACKER 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace easy { 14 | 15 | struct GlobalMapping { 16 | const char* Name; 17 | void* Address; 18 | }; 19 | 20 | struct FunctionInfo { 21 | const char* Name; 22 | GlobalMapping* Globals; 23 | const char* Bitcode; 24 | size_t BitcodeLen; 25 | 26 | FunctionInfo(const char* N, GlobalMapping* G, const char* B, size_t BL) 27 | : Name(N), Globals(G), Bitcode(B), BitcodeLen(BL) 28 | { } 29 | }; 30 | 31 | struct LayoutInfo { 32 | size_t NumFields; 33 | }; 34 | 35 | class BitcodeTracker { 36 | 37 | // map function to all the info required for jit compilation 38 | std::unordered_map Functions; 39 | std::unordered_map NameToAddress; 40 | 41 | // map the addresses of the layout_id with the number of parameters 42 | std::unordered_map Layouts; 43 | 44 | public: 45 | 46 | void registerFunction(void* FPtr, const char* Name, GlobalMapping* Globals, const char* Bitcode, size_t BitcodeLen) { 47 | Functions.emplace(FPtr, FunctionInfo{Name, Globals, Bitcode, BitcodeLen}); 48 | NameToAddress.emplace(Name, FPtr); 49 | } 50 | 51 | void registerLayout(layout_id Id, size_t N) { 52 | Layouts.emplace(Id, LayoutInfo{N}); 53 | } 54 | 55 | void* getAddress(std::string const &Name); 56 | std::tuple getNameAndGlobalMapping(void* FPtr); 57 | bool hasGlobalMapping(void* FPtr) const; 58 | LayoutInfo const & getLayoutInfo(easy::layout_id id) const; 59 | 60 | using ModuleContextPair = std::pair, std::unique_ptr>; 61 | ModuleContextPair getModule(void* FPtr); 62 | std::unique_ptr getModuleWithContext(void* FPtr, llvm::LLVMContext &C); 63 | 64 | // get the singleton object 65 | static BitcodeTracker& GetTracker(); 66 | }; 67 | 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /misc/docker/GenDockerfile.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import sys 3 | 4 | Head = "# Dockerfile derived from easy::jit's .travis.yml" 5 | From = "ubuntu:latest" 6 | Manteiner = "Juan Manuel Martinez Caamaño jmartinezcaamao@gmail.com" 7 | base_packages = ['build-essential', 'python', 'python-pip', 'git', 'wget', 'unzip', 'cmake'] 8 | 9 | travis = yaml.load(open(sys.argv[1])) 10 | travis_sources = travis['addons']['apt']['sources'] 11 | travis_packages = travis['addons']['apt']['packages'] 12 | before_install = travis['before_install'] 13 | script = travis['script'] 14 | 15 | # I could not get a better way to do this 16 | AddSourceCmd = { 17 | "llvm-toolchain-trusty-6.0" : "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main | tee -a /etc/apt/sources.list > /dev/null", 18 | "ubuntu-toolchain-r-test" : "apt-add-repository -y \"ppa:ubuntu-toolchain-r/test\"" 19 | } 20 | 21 | Sources = ["RUN {cmd} \n".format(cmd=AddSourceCmd[source]) for source in travis_sources] 22 | 23 | Apt = """# add sources 24 | RUN apt-get update 25 | RUN apt-get install -y software-properties-common 26 | {AddSources} 27 | # install apt packages, base first, then travis 28 | RUN apt-get update 29 | RUN apt-get upgrade -y 30 | RUN apt-get install -y {base_packages} && \\ 31 | apt-get install -y {travis_packages} 32 | """.format(AddSources = "".join(Sources), base_packages = " ".join(base_packages), travis_packages=" ".join(travis_packages)) 33 | 34 | Checkout = "RUN git clone --depth=50 --branch=${branch} https://github.com/jmmartinez/easy-just-in-time.git easy-just-in-time && cd easy-just-in-time\n" 35 | BeforeInstall = "".join(["RUN cd /easy-just-in-time && {0} \n".format(cmd) for cmd in before_install]) 36 | Run = "RUN cd easy-just-in-time && \\\n" + "".join([" {cmd} && \\ \n".format(cmd=cmd) for cmd in script]) + " echo ok!" 37 | 38 | Template = """{Head} 39 | 40 | FROM {From} 41 | 42 | LABEL manteiner {Manteiner} 43 | 44 | ARG branch=master 45 | 46 | {Apt} 47 | # checkout 48 | {Checkout} 49 | # install other deps 50 | {BeforeInstall} 51 | # compile and test! 52 | {Run}""" 53 | 54 | print(Template.format(Head=Head, From=From, Manteiner=Manteiner, Apt=Apt, BeforeInstall=BeforeInstall, Checkout=Checkout, Run=Run)) 55 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.3) 2 | 3 | if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") 4 | message(FATAL_ERROR "Do not set the build directory equal to the source directory!") 5 | endif() 6 | 7 | message(STATUS "CPU Architecture: ${CMAKE_HOST_SYSTEM_PROCESSOR}") 8 | if (${CMAKE_HOST_SYSTEM_PROCESSOR} MATCHES "x86_64") 9 | add_definitions(-D_X86) 10 | elseif (${CMAKE_HOST_SYSTEM_PROCESSOR} MATCHES "aarch64") 11 | add_definitions(-D_AARCH64) 12 | endif() 13 | 14 | find_package(LLVM 6.0 REQUIRED CONFIG) 15 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") 16 | message(STATUS "Using LLVMConfig.cmake in ${LLVM_DIR}") 17 | message(STATUS "LLVM Root: ${LLVM_TOOLS_BINARY_DIR}") 18 | message(STATUS "LLVM Include dirs: ${LLVM_INCLUDE_DIRS}") 19 | 20 | list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") 21 | include(AddLLVM) 22 | include(Python) 23 | 24 | add_definitions(${LLVM_DEFINITIONS}) 25 | include_directories(${LLVM_INCLUDE_DIRS}) 26 | link_directories(${LLVM_LIBRARY_DIRS}) 27 | include_directories(include) 28 | 29 | set(CMAKE_CXX_STANDARD 14) 30 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 31 | 32 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) 33 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) 34 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) 35 | 36 | set(EASY_JIT_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) 37 | 38 | # Suppress superfluous randlib warnings about "*.a" having no symbols on MacOSX. 39 | if (APPLE) 40 | set(CMAKE_C_ARCHIVE_CREATE " Scr ") 41 | set(CMAKE_CXX_ARCHIVE_CREATE " Scr ") 42 | set(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") 43 | set(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") 44 | endif() 45 | 46 | add_subdirectory(cmake) 47 | add_subdirectory(include) 48 | add_subdirectory(pass) 49 | add_subdirectory(runtime) 50 | 51 | add_custom_target(easy-jit-core DEPENDS EasyJitPass EasyJitRuntime) 52 | 53 | add_subdirectory(doc) 54 | add_subdirectory(benchmark) 55 | 56 | include(CMakeTests.txt) 57 | -------------------------------------------------------------------------------- /tests/meta/type_list+func_traits.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %s -o /dev/null 2 | 3 | #include 4 | #include 5 | 6 | using namespace easy; 7 | using namespace easy::meta; 8 | 9 | int foo(int, bool, float); 10 | 11 | int main() { 12 | static_assert(std::is_same< 13 | type_list::head, 14 | int>::value, 15 | "not same type"); 16 | 17 | static_assert(std::is_same< 18 | type_list::at<0>, 19 | int>::value, 20 | "not same type"); 21 | static_assert(std::is_same< 22 | type_list::at<1>, 23 | bool>::value, 24 | "not same type"); 25 | static_assert(std::is_same< 26 | type_list::at<2>, 27 | float>::value, 28 | "not same type"); 29 | static_assert(type_list::size == 3, 30 | "not correct size"); 31 | static_assert(!type_list::empty, 32 | "detected as empty"); 33 | static_assert(std::is_same< 34 | type_list::tail::head, 35 | bool>::value, 36 | "not same type"); 37 | 38 | using foo_type = decltype(foo); 39 | using foo_traits = function_traits; 40 | 41 | static_assert(std::is_same::value, 42 | "not same type"); 43 | static_assert(std::is_same< 44 | foo_traits::parameter_list, 45 | type_list>::value, 46 | "not same type"); 47 | 48 | static_assert(std::is_same< 49 | typename meta::init_list<3,void>::type, 50 | type_list>::value, 51 | "not same type"); 52 | static_assert(std::is_same< 53 | typename meta::init_list<0,void>::type, 54 | type_list<>>::value, 55 | "not same type"); 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /runtime/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | using namespace llvm; 13 | 14 | static const char EasyJitMD[] = "easy::jit"; 15 | static const char EntryTag[] = "entry"; 16 | 17 | std::string easy::GetEntryFunctionName(Module const &M) { 18 | NamedMDNode* MD = M.getNamedMetadata(EasyJitMD); 19 | 20 | for(MDNode *Operand : MD->operands()) { 21 | if(Operand->getNumOperands() != 2) 22 | continue; 23 | MDString* Entry = dyn_cast(Operand->getOperand(0)); 24 | MDString* Name = dyn_cast(Operand->getOperand(1)); 25 | 26 | if(!Entry || !Name || Entry->getString() != EntryTag) 27 | continue; 28 | 29 | return Name->getString(); 30 | } 31 | 32 | llvm_unreachable("No entry function in easy::jit module!"); 33 | return ""; 34 | } 35 | 36 | void easy::MarkAsEntry(llvm::Function &F) { 37 | Module &M = *F.getParent(); 38 | LLVMContext &Ctx = F.getContext(); 39 | NamedMDNode* MD = M.getOrInsertNamedMetadata(EasyJitMD); 40 | MDNode* Node = MDNode::get(Ctx, { MDString::get(Ctx, EntryTag), 41 | MDString::get(Ctx, F.getName())}); 42 | MD->addOperand(Node); 43 | } 44 | 45 | void easy::UnmarkEntry(llvm::Module &M) { 46 | NamedMDNode* MD = M.getOrInsertNamedMetadata(EasyJitMD); 47 | M.eraseNamedMetadata(MD); 48 | } 49 | 50 | std::unique_ptr 51 | easy::CloneModuleWithContext(llvm::Module const &LM, llvm::LLVMContext &C) { 52 | // I have not found a better way to do this withouth having to fully reimplement 53 | // CloneModule 54 | 55 | std::string buf; 56 | 57 | // write module 58 | { 59 | llvm::raw_string_ostream stream(buf); 60 | llvm::WriteBitcodeToFile(&LM, stream); 61 | stream.flush(); 62 | } 63 | 64 | // read the module 65 | auto MemBuf = llvm::MemoryBuffer::getMemBuffer(llvm::StringRef(buf)); 66 | auto ModuleOrError = llvm::parseBitcodeFile(*MemBuf, C); 67 | if(ModuleOrError.takeError()) 68 | return nullptr; 69 | 70 | auto LMCopy = std::move(ModuleOrError.get()); 71 | return LMCopy; 72 | } 73 | -------------------------------------------------------------------------------- /include/easy/jit.h: -------------------------------------------------------------------------------- 1 | #ifndef EASY 2 | #define EASY 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace easy { 15 | 16 | namespace { 17 | template 18 | FunctionWrapper 19 | WrapFunction(std::unique_ptr F, meta::type_list) { 20 | return FunctionWrapper(std::move(F)); 21 | } 22 | 23 | template 24 | auto jit_with_context(easy::Context const &C, T &&Fun) { 25 | 26 | auto* FunPtr = meta::get_as_pointer(Fun); 27 | using FunOriginalTy = std::remove_pointer_t>; 28 | 29 | using new_type_traits = meta::new_function_traits>; 30 | using new_return_type = typename new_type_traits::return_type; 31 | using new_parameter_types = typename new_type_traits::parameter_list; 32 | 33 | auto CompiledFunction = 34 | Function::Compile(reinterpret_cast(FunPtr), C); 35 | 36 | auto Wrapper = 37 | WrapFunction(std::move(CompiledFunction), 38 | typename new_parameter_types::template push_front ()); 39 | return Wrapper; 40 | } 41 | 42 | template 43 | easy::Context get_context_for(Args&& ... args) { 44 | using FunOriginalTy = std::remove_pointer_t>; 45 | static_assert(std::is_function::value, 46 | "easy::jit: supports only on functions and function pointers"); 47 | 48 | using parameter_list = typename meta::function_traits::parameter_list; 49 | 50 | static_assert(parameter_list::size <= sizeof...(Args), 51 | "easy::jit: not providing enough argument to actual call"); 52 | 53 | easy::Context C; 54 | easy::set_parameters(parameter_list(), C, 55 | std::forward(args)...); 56 | return C; 57 | } 58 | } 59 | 60 | template 61 | auto EASY_JIT_COMPILER_INTERFACE jit(T &&Fun, Args&& ... args) { 62 | auto C = get_context_for(std::forward(args)...); 63 | return jit_with_context(C, std::forward(Fun)); 64 | } 65 | 66 | } 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /tests/simple/struct_arg.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %ld_flags %s -Xclang -load -Xclang %lib_pass -o %t 2 | // RUN: %t > %t.out 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | using namespace std::placeholders; 10 | 11 | template 12 | struct Point { 13 | T x; 14 | T y; 15 | 16 | Point(T _x, T _y) { 17 | x = _x; 18 | y = _y; 19 | } 20 | }; 21 | 22 | template 23 | long whopie (Point a, T b) { 24 | return a.x * b + a.y * b; 25 | } 26 | 27 | template 28 | void test_swap() { 29 | T val = 1; 30 | auto whop = easy::jit(whopie, _2, _1); 31 | 32 | for(int v = 4; v != 8; ++v) { 33 | long z = whop(val, Point(v*2, v*3)); 34 | printf(" swap(%d) %ld\n", v, z); 35 | 36 | if(z != whopie(Point(v*2, v*3), val)) 37 | exit(-1); 38 | } 39 | } 40 | 41 | template 42 | void test_specialzie_a() { 43 | T val = 1; 44 | auto whop = easy::jit(whopie, _1, val); 45 | 46 | for(int v = 4; v != 8; ++v) { 47 | long z = whop(Point(v*2, v*3)); 48 | printf(" spec_a(%d) %ld\n", v, z); 49 | 50 | if(z != whopie(Point(v*2, v*3), val)) 51 | exit(-1); 52 | } 53 | } 54 | 55 | template 56 | void test_specialzie_b() { 57 | T val = 1; 58 | auto whop = easy::jit(whopie, Point(val*2, val*3), _1); 59 | 60 | for(int v = 4; v != 8; ++v) { 61 | long z = whop(v); 62 | printf(" spec_b(%d) %ld\n", v, z); 63 | 64 | if(z != whopie(Point(val*2, val*3), v)) 65 | exit(-1); 66 | } 67 | } 68 | 69 | template 70 | void test_specialzie_ab() { 71 | T val = 1; 72 | auto whop = easy::jit(whopie, Point(val*2, val*3), val); 73 | 74 | for(int v = 4; v != 8; ++v) { 75 | long z = whop(); 76 | printf(" spec_ab() %ld\n", z); 77 | 78 | if(z != whopie(Point(val*2, val*3), val)) 79 | exit(-1); 80 | } 81 | } 82 | 83 | template 84 | void test() { 85 | printf("== %s ==\n", typeid(T).name()); 86 | test_swap(); 87 | test_specialzie_a(); 88 | test_specialzie_b(); 89 | test_specialzie_ab(); 90 | } 91 | 92 | int main() { 93 | 94 | test(); 95 | test(); 96 | test(); 97 | test(); 98 | test(); 99 | test(); 100 | 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /tests/lit.cfg.in: -------------------------------------------------------------------------------- 1 | import lit.formats 2 | import lit.util 3 | import os 4 | 5 | config.name = 'easy_jit' 6 | config.suffixes = ['.c', '.cpp', '.ll', '.test'] 7 | 8 | config.test_format = lit.formats.ShTest(True) 9 | 10 | config.test_source_root = "@CMAKE_CURRENT_SOURCE_DIR@/tests" 11 | config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@/tests" 12 | 13 | config.environment['PATH'] = os.pathsep.join(["@LLVM_TOOLS_BINARY_DIR@"] + [ config.environment['PATH'] ]) 14 | 15 | runtime_lib = os.path.basename("@EASY_JIT_RUNTIME@").split('.')[0].replace("lib", "", 1) 16 | runtime_lib_dir = os.path.dirname("@EASY_JIT_RUNTIME@") 17 | llvm_lib_dir = os.path.join(os.path.dirname("@LLVM_TOOLS_BINARY_DIR@"), "lib") 18 | 19 | includes = ["@EASY_JIT_ROOT@"] 20 | include_flags = " ".join(["-I'" + os.path.abspath(dir) + "'" for dir in "@LLVM_INCLUDE_DIRS@".split()] + ["-I'" + os.path.join(dir, "include") + "'" for dir in includes] ) 21 | 22 | ld_paths = [runtime_lib_dir, llvm_lib_dir] 23 | ld_flags = "" 24 | for ld_path in ld_paths: 25 | ld_flags = ld_flags + " -L'" + os.path.abspath(ld_path) + "' -rpath '" + os.path.abspath(ld_path) + "' " 26 | 27 | ld_flags = ld_flags + " -l" + runtime_lib 28 | 29 | # substitutions 30 | config.substitutions.append(('%bin', "@CMAKE_ARCHIVE_OUTPUT_DIRECTORY@")) 31 | config.substitutions.append(('%install_dir', "@CMAKE_INSTALL_PREFIX@")) 32 | config.substitutions.append(('%llvm_tools_dir', "@LLVM_TOOLS_BINARY_DIR@")) 33 | 34 | common_flags = "-g -Xclang -disable-O0-optnone " 35 | 36 | config.substitutions.append(('%clangxx', os.path.join("@LLVM_TOOLS_BINARY_DIR@", "clang++"))) 37 | config.substitutions.append(('%clang', os.path.join("@LLVM_TOOLS_BINARY_DIR@", "clang"))) 38 | config.substitutions.append(('%opt', os.path.join("@LLVM_TOOLS_BINARY_DIR@", "opt"))) 39 | config.substitutions.append(('%cxxflags', common_flags + "--std=c++14")) 40 | config.substitutions.append(('%cflags', common_flags)) 41 | config.substitutions.append(('%include_flags', include_flags)) 42 | config.substitutions.append(('%lib_pass', "@EASY_JIT_PASS@")) 43 | config.substitutions.append(('%lib_runtime', "@EASY_JIT_RUNTIME@")) 44 | config.substitutions.append(('%ld_flags', ld_flags)) 45 | 46 | config.substitutions.append(('%not', "!")) 47 | 48 | config.substitutions.append(('%FileCheck', os.path.join("@LLVM_TOOLS_BINARY_DIR@", "FileCheck"))) 49 | 50 | if "@EASY_JIT_BENCHMARK@" in ["1", "ON"] : 51 | config.available_features.add('benchmark') 52 | 53 | if "@CMAKE_INSTALL_PREFIX@" and os.path.exists(os.path.join("@CMAKE_INSTALL_PREFIX@", "include", "easy")): 54 | config.available_features.add('install') 55 | -------------------------------------------------------------------------------- /pass/MayAliasTracer.cpp: -------------------------------------------------------------------------------- 1 | #include "MayAliasTracer.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace llvm; 9 | 10 | void easy::MayAliasTracer::mayAliasWithStoredValues(Value* V, VSet &Loaded, VSet &Stored) { 11 | if(!Stored.insert(V).second) 12 | return; 13 | if(auto* GO = dyn_cast(V)) 14 | GOs_.insert(GO); 15 | 16 | if(auto * II = dyn_cast(V)) { 17 | if(II->getIntrinsicID() == Intrinsic::memcpy) { 18 | mayAliasWithLoadedValues(II->getArgOperand(1), Loaded, Stored); 19 | } 20 | } 21 | 22 | if(auto* SI = dyn_cast(V)) { 23 | mayAliasWithLoadedValues(SI->getValueOperand(), Loaded, Stored); 24 | } 25 | 26 | if(isa(V)||isa(V)||isa(V)) { 27 | for(User* U : V->users()) { 28 | mayAliasWithStoredValues(U, Loaded, Stored); 29 | } 30 | } 31 | } 32 | 33 | void easy::MayAliasTracer::mayAliasWithLoadedValues(Value * V, VSet &Loaded, VSet &Stored) { 34 | if(!Loaded.insert(V).second) 35 | return; 36 | if(auto* GO = dyn_cast(V)) 37 | GOs_.insert(GO); 38 | 39 | auto mayAliasWithLoadedOperand = [this, &Loaded, &Stored](Value* V) { mayAliasWithLoadedValues(V, Loaded, Stored);}; 40 | 41 | //TODO: generalize that 42 | if(auto* PHI = dyn_cast(V)) { 43 | std::for_each(PHI->op_begin(), PHI->op_end(), mayAliasWithLoadedOperand); 44 | } 45 | if(auto* Select = dyn_cast(V)) { 46 | mayAliasWithLoadedValues(Select->getTrueValue(), Loaded, Stored); 47 | mayAliasWithLoadedValues(Select->getFalseValue(), Loaded, Stored); 48 | } 49 | if(auto* Alloca = dyn_cast(V)) { 50 | mayAliasWithStoredValues(Alloca, Loaded, Stored); 51 | } 52 | if(auto *GEP = dyn_cast(V)) { 53 | mayAliasWithLoadedValues(GEP->getPointerOperand(), Loaded, Stored); 54 | } 55 | if(auto *BC = dyn_cast(V)) { 56 | mayAliasWithLoadedValues(BC->getOperand(0), Loaded, Stored); 57 | } 58 | if(auto const* CE = dyn_cast(V)) { 59 | switch(CE->getOpcode()) { 60 | case Instruction::GetElementPtr: 61 | case Instruction::BitCast: 62 | return mayAliasWithLoadedValues(CE->getOperand(0), Loaded, Stored); 63 | default: 64 | ; 65 | } 66 | } 67 | if(auto* OtherGV = dyn_cast(V)) { 68 | if(OtherGV->hasInitializer()) 69 | mayAliasWithLoadedValues(OtherGV->getInitializer(), Loaded, Stored); 70 | mayAliasWithStoredValues(OtherGV, Loaded, Stored); 71 | } 72 | if(auto* CA = dyn_cast(V)) { 73 | std::for_each(CA->op_begin(), CA->op_end(), mayAliasWithLoadedOperand); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /runtime/BitcodeTracker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace easy; 9 | using namespace llvm; 10 | 11 | namespace easy { 12 | DefineEasyException(BitcodeNotRegistered, "Cannot find bitcode."); 13 | DefineEasyException(BitcodeParseError, "Cannot parse bitcode for: "); 14 | } 15 | 16 | BitcodeTracker& BitcodeTracker::GetTracker() { 17 | static BitcodeTracker TheTracker; 18 | return TheTracker; 19 | } 20 | 21 | bool BitcodeTracker::hasGlobalMapping(void* FPtr) const { 22 | auto InfoPtr = Functions.find(FPtr); 23 | return InfoPtr != Functions.end(); 24 | } 25 | 26 | LayoutInfo const & BitcodeTracker::getLayoutInfo(easy::layout_id id) const { 27 | auto InfoPair = Layouts.find(id); 28 | assert(InfoPair != Layouts.end()); 29 | return InfoPair->second; 30 | } 31 | 32 | void* BitcodeTracker::getAddress(std::string const &Name) { 33 | auto Addr = NameToAddress.find(Name); 34 | if(Addr == NameToAddress.end()) 35 | return nullptr; 36 | return Addr->second; 37 | } 38 | 39 | std::tuple BitcodeTracker::getNameAndGlobalMapping(void* FPtr) { 40 | auto InfoPtr = Functions.find(FPtr); 41 | if(InfoPtr == Functions.end()) { 42 | throw easy::BitcodeNotRegistered(); 43 | } 44 | 45 | return std::make_tuple(InfoPtr->second.Name, InfoPtr->second.Globals); 46 | } 47 | 48 | std::unique_ptr BitcodeTracker::getModuleWithContext(void* FPtr, llvm::LLVMContext &C) { 49 | auto InfoPtr = Functions.find(FPtr); 50 | if(InfoPtr == Functions.end()) { 51 | throw easy::BitcodeNotRegistered(); 52 | } 53 | 54 | auto &Info = InfoPtr->second; 55 | 56 | llvm::StringRef BytecodeStr(Info.Bitcode, Info.BitcodeLen); 57 | std::unique_ptr Buf(llvm::MemoryBuffer::getMemBuffer(BytecodeStr)); 58 | auto ModuleOrErr = 59 | llvm::parseBitcodeFile(Buf->getMemBufferRef(), C); 60 | 61 | if (ModuleOrErr.takeError()) { 62 | throw easy::BitcodeParseError(Info.Name); 63 | } 64 | 65 | return std::move(ModuleOrErr.get()); 66 | } 67 | 68 | BitcodeTracker::ModuleContextPair BitcodeTracker::getModule(void* FPtr) { 69 | 70 | std::unique_ptr Context(new llvm::LLVMContext()); 71 | auto Module = getModuleWithContext(FPtr, *Context); 72 | return ModuleContextPair(std::move(Module), std::move(Context)); 73 | } 74 | 75 | // function to interface with the generated code 76 | extern "C" { 77 | void easy_register(void* FPtr, const char* Name, GlobalMapping* Globals, const char* Bitcode, size_t BitcodeLen) { 78 | BitcodeTracker::GetTracker().registerFunction(FPtr, Name, Globals, Bitcode, BitcodeLen); 79 | } 80 | void easy_register_layout(layout_id Id, size_t N) { 81 | BitcodeTracker::GetTracker().registerLayout(Id, N); 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /include/easy/code_cache.h: -------------------------------------------------------------------------------- 1 | #ifndef CACHE 2 | #define CACHE 3 | 4 | #include 5 | #include 6 | 7 | namespace easy { 8 | 9 | namespace { 10 | using AutoKey = std::pair; 11 | 12 | template 13 | class CacheBase { 14 | 15 | public: 16 | 17 | using Key = KeyTy; 18 | 19 | protected: 20 | 21 | std::unordered_map Cache_; 22 | using iterator = typename std::unordered_map::iterator; 23 | 24 | template 25 | auto const & compile_if_not_in_cache(std::pair &CacheEntry, T &&Fun, Args&& ... args) { 26 | using wrapper_ty = decltype(easy::jit(std::forward(Fun), std::forward(args)...)); 27 | 28 | FunctionWrapperBase &FWB = CacheEntry.first->second; 29 | if(CacheEntry.second) { 30 | auto FW = easy::jit(std::forward(Fun), std::forward(args)...); 31 | FWB = std::move(FW); 32 | } 33 | return reinterpret_cast(FWB); 34 | } 35 | }; 36 | } 37 | 38 | template 39 | class Cache : public CacheBase { 40 | public: 41 | 42 | template 43 | auto const& EASY_JIT_COMPILER_INTERFACE jit(Key const &K, T &&Fun, Args&& ... args) { 44 | auto CacheEntry = CacheBase::Cache_.emplace(K, FunctionWrapperBase()); 45 | return CacheBase::compile_if_not_in_cache(CacheEntry, std::forward(Fun), std::forward(args)...); 46 | } 47 | 48 | template 49 | auto const& EASY_JIT_COMPILER_INTERFACE jit(Key &&K, T &&Fun, Args&& ... args) { 50 | auto CacheEntry = CacheBase::Cache_.emplace(K, FunctionWrapperBase()); 51 | return CacheBase::compile_if_not_in_cache(CacheEntry, std::forward(Fun), std::forward(args)...); 52 | } 53 | 54 | bool has(Key const &K) const { 55 | auto const CacheEntry = CacheBase::Cache_.find(K); 56 | return CacheEntry != CacheBase::Cache_.end(); 57 | } 58 | }; 59 | 60 | 61 | template<> 62 | class Cache : public CacheBase { 63 | public: 64 | 65 | template 66 | auto const& EASY_JIT_COMPILER_INTERFACE jit(T &&Fun, Args&& ... args) { 67 | void* FunPtr = reinterpret_cast(meta::get_as_pointer(Fun)); 68 | auto CacheEntry = 69 | CacheBase::Cache_.emplace( 70 | Key(FunPtr, get_context_for(std::forward(args)...)), 71 | FunctionWrapperBase()); 72 | return CacheBase::compile_if_not_in_cache(CacheEntry, std::forward(Fun), std::forward(args)...); 73 | } 74 | 75 | template 76 | bool has(T &&Fun, Args&& ... args) const { 77 | void* FunPtr = reinterpret_cast(meta::get_as_pointer(Fun)); 78 | auto const CacheEntry = 79 | CacheBase::Cache_.find(Key(FunPtr, 80 | get_context_for(std::forward(args)...))); 81 | return CacheEntry != Cache_.end(); 82 | } 83 | }; 84 | 85 | } 86 | 87 | #endif // CACHE 88 | -------------------------------------------------------------------------------- /tests/meta/new_func_traits.cpp: -------------------------------------------------------------------------------- 1 | // RUN: %clangxx %cxxflags %include_flags %s -o /dev/null 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace easy; 8 | using namespace easy::meta; 9 | using namespace std::placeholders; 10 | 11 | int foo(int, bool, float, int); 12 | int baz(int, bool); 13 | 14 | int main() { 15 | 16 | using foo_type = decltype(foo); 17 | using baz_type = decltype(baz); 18 | using new_foo_traits_a = new_function_traits>; 19 | using new_foo_traits_b = new_function_traits>; 20 | using new_foo_traits_c = new_function_traits>; 21 | using new_foo_traits_d = new_function_traits>; 22 | using new_foo_traits_e = new_function_traits>; 23 | using new_foo_traits_f = new_function_traits>; 24 | using new_baz_traits_g = new_function_traits>; 25 | using new_baz_traits_h = new_function_traits>; 26 | 27 | // full specialization 28 | static_assert(new_foo_traits_a::parameter_list::empty, "fail A not empty"); 29 | static_assert(new_foo_traits_b::parameter_list::empty, "fail B not empty"); 30 | static_assert(new_foo_traits_c::parameter_list::empty, "fail C not empty"); 31 | 32 | // two int parameters 33 | static_assert(new_foo_traits_d::parameter_list::size == 2, "fail D size"); 34 | static_assert(std::is_same< 35 | typename new_foo_traits_d::parameter_list, 36 | meta::type_list 37 | >::value, "fail D types"); 38 | 39 | // one int parameter 40 | static_assert(new_foo_traits_e::parameter_list::size == 1, "fail E size"); 41 | static_assert(new_foo_traits_f::parameter_list::size == 2, "fail F size"); 42 | 43 | static_assert(std::is_same< 44 | typename new_foo_traits_e::parameter_list, 45 | meta::type_list 46 | >::value, "fail E types"); 47 | static_assert(std::is_same< 48 | typename new_foo_traits_f::parameter_list, 49 | meta::type_list 50 | >::value, "fail F types"); 51 | 52 | static_assert(new_baz_traits_g::parameter_list::size == 2, "fail G size"); 53 | static_assert(new_baz_traits_h::parameter_list::size == 2, "fail H size"); 54 | 55 | static_assert(std::is_same< 56 | typename new_baz_traits_g::parameter_list, 57 | meta::type_list 58 | >::value, "fail G types"); 59 | 60 | static_assert(std::is_same< 61 | typename new_baz_traits_h::parameter_list, 62 | meta::type_list 63 | >::value, "fail H types"); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /include/easy/function_wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTION_WRAPPER 2 | #define FUNCTION_WRAPPER 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace easy { 10 | 11 | class FunctionWrapperBase { 12 | 13 | protected: 14 | std::unique_ptr Fun_; 15 | 16 | public: 17 | // null object 18 | FunctionWrapperBase() = default; 19 | 20 | // default constructor 21 | FunctionWrapperBase(std::unique_ptr F) 22 | : Fun_(std::move(F)) {} 23 | 24 | // steal the implementation 25 | FunctionWrapperBase(FunctionWrapperBase &&FW) 26 | : Fun_(std::move(FW.Fun_)) {} 27 | FunctionWrapperBase& operator=(FunctionWrapperBase &&FW) { 28 | Fun_ = std::move(FW.Fun_); 29 | return *this; 30 | } 31 | 32 | Function const& getFunction() const { 33 | return *Fun_; 34 | } 35 | 36 | void* getRawPointer() const { 37 | return getFunction().getRawPointer(); 38 | } 39 | 40 | void serialize(std::ostream& os) const { 41 | getFunction().serialize(os); 42 | } 43 | 44 | static FunctionWrapperBase deserialize(std::istream& is) { 45 | std::unique_ptr Fun = Function::deserialize(is); 46 | return FunctionWrapperBase{std::move(Fun)}; 47 | } 48 | }; 49 | 50 | template 51 | class FunctionWrapper; 52 | 53 | template 54 | class FunctionWrapper : 55 | public FunctionWrapperBase { 56 | public: 57 | FunctionWrapper(std::unique_ptr F) 58 | : FunctionWrapperBase(std::move(F)) {} 59 | 60 | template 61 | Ret operator()(Args&& ... args) const { 62 | return getFunctionPointer()(std::forward(args)...); 63 | } 64 | 65 | auto getFunctionPointer() const { 66 | return ((Ret(__cdecl *)(Params...))getRawPointer()); 67 | } 68 | 69 | static FunctionWrapper deserialize(std::istream& is) { 70 | std::unique_ptr Fun = Function::deserialize(is); 71 | return FunctionWrapper{std::move(Fun)}; 72 | } 73 | }; 74 | 75 | // specialization for void return 76 | template 77 | class FunctionWrapper : 78 | public FunctionWrapperBase { 79 | public: 80 | FunctionWrapper(std::unique_ptr F) 81 | : FunctionWrapperBase(std::move(F)) {} 82 | 83 | template 84 | void operator()(Args&& ... args) const { 85 | return getFunctionPointer()(std::forward(args)...); 86 | } 87 | 88 | auto getFunctionPointer() const { 89 | return ((void(__cdecl *)(Params...))getRawPointer()); 90 | } 91 | 92 | static FunctionWrapper deserialize(std::istream& is) { 93 | std::unique_ptr Fun = Function::deserialize(is); 94 | return FunctionWrapper{std::move(Fun)}; 95 | } 96 | }; 97 | 98 | template 99 | struct is_function_wrapper { 100 | 101 | template 102 | struct is_function_wrapper_helper { 103 | static constexpr bool value = false; 104 | }; 105 | 106 | template 107 | struct is_function_wrapper_helper> { 108 | static constexpr bool value = true; 109 | using return_type = Ret; 110 | using params = meta::type_list; 111 | }; 112 | 113 | using helper = is_function_wrapper_helper>; 114 | 115 | static constexpr bool value = helper::value; 116 | }; 117 | 118 | template 119 | struct is_function_wrapper> { 120 | static constexpr bool value = true; 121 | using return_type = Ret; 122 | using params = meta::type_list; 123 | }; 124 | 125 | 126 | } 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /benchmark/benchmark.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void __attribute__((noinline)) kernel(int n, int m, int * image, int const * mask, int* out) { 8 | for(int i = 0; i < n - m; ++i) 9 | for(int j = 0; j < n - m; ++j) 10 | for(int k = 0; k < m; ++k) 11 | for(int l = 0; l < m; ++l) 12 | out[i * (n-m+1) + j] += image[(i+k) * n + j+l] * mask[k *m + l]; 13 | } 14 | 15 | /* To sort array elemets */ 16 | 17 | int int_cmp(int a, int b) 18 | { 19 | if (a > b) 20 | return 1; 21 | else 22 | { 23 | if (a == b) 24 | return 0; 25 | else 26 | return -1; 27 | } 28 | } 29 | 30 | // https://github.com/ctasims/The-C-Programming-Language--Kernighan-and-Ritchie/blob/master/ch04-functions-and-program-structure/qsort.c 31 | void __attribute__((noinline)) Qsort(int v[], int left, int right, int (*cmp)(int, int)) 32 | { 33 | int i, last; 34 | void swap(int v[], int i, int j); 35 | 36 | if (left >= right) // do nothing if array contains < 2 elems 37 | return; 38 | // move partition elem to v[0] 39 | swap(v, left, (left + right)/2); 40 | last = left; 41 | 42 | for (i = left+1; i <= right; i++) // partition 43 | if (cmp(v[i], v[left])) 44 | swap(v, ++last, i); 45 | 46 | swap(v, left, last); // restore partition elem 47 | Qsort(v, left, last-1, cmp); 48 | Qsort(v, last+1, right, cmp); 49 | } 50 | 51 | /* swap: interchange v[i] and v[j] */ 52 | void swap(int v[], int i, int j) 53 | { 54 | int temp; 55 | temp = v[i]; 56 | v[i] = v[j]; 57 | v[j] = temp; 58 | } 59 | 60 | static const int mask[3][3] = {{1,2,3},{0,0,0},{3,2,1}}; 61 | 62 | static void BM_convolve(benchmark::State& state) { 63 | using namespace std::placeholders; 64 | 65 | bool jit = state.range(1); 66 | int n = state.range(0); 67 | 68 | std::vector image(n*n,0); 69 | std::vector out((n-3)*(n-3),0); 70 | 71 | auto my_kernel = easy::jit(kernel, n, 3, _1, &mask[0][0], _2); 72 | 73 | benchmark::ClobberMemory(); 74 | 75 | for (auto _ : state) { 76 | if(jit) { 77 | my_kernel(image.data(), out.data()); 78 | } else { 79 | kernel(n, 3, image.data(), &mask[0][0], out.data()); 80 | } 81 | benchmark::ClobberMemory(); 82 | } 83 | } 84 | BENCHMARK(BM_convolve)->Ranges({{16,1024}, {0,1}}); 85 | 86 | static void BM_convolve_compile_jit(benchmark::State& state) { 87 | using namespace std::placeholders; 88 | for (auto _ : state) { 89 | auto my_kernel = easy::jit(kernel, 11, 3, _1, &mask[0][0], _2); 90 | benchmark::ClobberMemory(); 91 | } 92 | } 93 | BENCHMARK(BM_convolve_compile_jit); 94 | 95 | static void BM_convolve_cache_hit_jit(benchmark::State& state) { 96 | using namespace std::placeholders; 97 | static easy::Cache<> cache; 98 | cache.jit(kernel, 11, 3, _1, &mask[0][0], _2); 99 | benchmark::ClobberMemory(); 100 | 101 | for (auto _ : state) { 102 | auto const &my_kernel = cache.jit(kernel, 11, 3, _1, &mask[0][0], _2); 103 | benchmark::ClobberMemory(); 104 | } 105 | } 106 | BENCHMARK(BM_convolve_cache_hit_jit); 107 | 108 | static void BM_qsort(benchmark::State& state) { 109 | using namespace std::placeholders; 110 | 111 | bool jit = state.range(1); 112 | int n = state.range(0); 113 | 114 | std::vector vec(n); 115 | std::iota(vec.begin(), vec.end(), 0); 116 | std::random_shuffle(vec.begin(), vec.end()); 117 | 118 | auto my_qsort = easy::jit(Qsort, _1, _2, _3, int_cmp); 119 | benchmark::ClobberMemory(); 120 | 121 | for (auto _ : state) { 122 | if(jit) { 123 | my_qsort(vec.data(), 0, vec.size()-1); 124 | } else { 125 | Qsort(vec.data(), 0, vec.size()-1, int_cmp); 126 | } 127 | benchmark::ClobberMemory(); 128 | } 129 | } 130 | BENCHMARK(BM_qsort)->Ranges({{16,1024}, {0,1}}); 131 | 132 | static void BM_qsort_compile_jit(benchmark::State& state) { 133 | using namespace std::placeholders; 134 | for (auto _ : state) { 135 | auto my_qsort = easy::jit(Qsort, _1, _2, _3, int_cmp); 136 | benchmark::ClobberMemory(); 137 | } 138 | } 139 | BENCHMARK(BM_qsort_compile_jit); 140 | 141 | static void BM_qsort_cache_hit_jit(benchmark::State& state) { 142 | using namespace std::placeholders; 143 | static easy::Cache<> cache; 144 | cache.jit(Qsort, _1, _2, _3, int_cmp); 145 | benchmark::ClobberMemory(); 146 | for (auto _ : state) { 147 | auto const &my_qsort = cache.jit(Qsort, _1, _2, _3, int_cmp); 148 | benchmark::ClobberMemory(); 149 | } 150 | } 151 | BENCHMARK(BM_qsort_cache_hit_jit); 152 | 153 | BENCHMARK_MAIN(); 154 | -------------------------------------------------------------------------------- /include/easy/param.h: -------------------------------------------------------------------------------- 1 | #ifndef PARAM 2 | #define PARAM 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace easy { 11 | 12 | namespace layout { 13 | 14 | // the address of this function is used as id :) 15 | template 16 | char* serialize_arg(Arg); 17 | 18 | template 19 | layout_id __attribute__((noinline)) EASY_JIT_LAYOUT get_layout() { 20 | // horrible hack to get the ptr of get_struct_layout_internal as void* 21 | union { 22 | void* as_void_ptr; 23 | decltype(serialize_arg)* as_fun_ptr; 24 | } dummy; 25 | dummy.as_fun_ptr = serialize_arg; 26 | return dummy.as_void_ptr; 27 | } 28 | 29 | template 30 | void set_layout(easy::Context &C) { 31 | C.setArgumentLayout(get_layout()); 32 | } 33 | } 34 | 35 | namespace { 36 | 37 | // special types 38 | template 39 | struct set_parameter_helper { 40 | 41 | template 42 | struct function_wrapper_specialization_is_possible { 43 | 44 | template 45 | static std::true_type can_assign_fun_pointer(std::remove_pointer_t); 46 | 47 | template 48 | static std::false_type can_assign_fun_pointer (...); 49 | 50 | using type = decltype(can_assign_fun_pointer( 51 | *std::declval().getFunctionPointer())); 52 | 53 | static constexpr bool value { type::value }; 54 | }; 55 | 56 | template 57 | using _if = std::enable_if_t; 58 | 59 | template 60 | static void set_param(Context &C, 61 | _if<(bool)std::is_placeholder>::value, Arg>) { 62 | C.setParameterIndex(std::is_placeholder>::value-1); 63 | } 64 | 65 | template 66 | static void set_param(Context &C, 67 | _if::value, Arg> &&arg) { 68 | static_assert(function_wrapper_specialization_is_possible::value, 69 | "easy::jit composition is not possible. Incompatible types."); 70 | C.setParameterModule(arg.getFunction()); 71 | } 72 | }; 73 | 74 | template<> 75 | struct set_parameter_helper { 76 | 77 | template 78 | using _if = std::enable_if_t; 79 | 80 | template 81 | static void set_param(Context &C, 82 | _if::value, Arg> &&arg) { 83 | Param arg_as_param = arg; 84 | C.setParameterInt(arg_as_param); 85 | } 86 | 87 | template 88 | static void set_param(Context &C, 89 | _if::value, Arg> &&arg) { 90 | Param arg_as_param = arg; 91 | C.setParameterFloat(arg_as_param); 92 | } 93 | 94 | template 95 | static void set_param(Context &C, 96 | _if::value, Arg> &&arg) { 97 | Param arg_as_param = arg; 98 | C.setParameterTypedPointer(arg_as_param); 99 | } 100 | 101 | template 102 | static void set_param(Context &C, 103 | _if::value, Arg> &&arg) { 104 | C.setParameterTypedPointer(std::addressof(arg)); 105 | } 106 | 107 | template 108 | static void set_param(Context &C, 109 | _if::value, Arg> &&arg) { 110 | C.setParameterStruct(layout::serialize_arg(arg)); 111 | } 112 | }; 113 | 114 | template 115 | struct set_parameter { 116 | 117 | static constexpr bool is_ph = std::is_placeholder>::value; 118 | static constexpr bool is_fw = easy::is_function_wrapper::value; 119 | static constexpr bool is_special = is_ph || is_fw; 120 | 121 | using help = set_parameter_helper; 122 | }; 123 | 124 | } 125 | 126 | template 127 | void set_options(Context &, NoOptions&& ...) { 128 | static_assert(meta::type_list::empty, "Remaining options to be processed!"); 129 | } 130 | 131 | template 132 | void set_options(Context &C, Option0&& Opt, Options&& ... Opts) { 133 | using OptTy = std::decay_t; 134 | OptTy& OptRef = std::ref(Opt); 135 | static_assert(options::is_option::value, "An easy::jit option is expected"); 136 | 137 | OptRef.handle(C); 138 | set_options(C, std::forward(Opts)...); 139 | } 140 | 141 | template 142 | std::enable_if_t 143 | set_parameters(ParameterList, 144 | Context& C, Options&& ... opts) { 145 | set_options(C, std::forward(opts)...); 146 | } 147 | 148 | template 149 | std::enable_if_t 150 | set_parameters(ParameterList, 151 | Context &C, Arg0 &&arg0, Args&& ... args) { 152 | using Param0 = typename ParameterList::head; 153 | using ParametersTail = typename ParameterList::tail; 154 | 155 | layout::set_layout(C); 156 | set_parameter::help::template set_param(C, std::forward(arg0)); 157 | set_parameters(ParametersTail(), C, std::forward(args)...); 158 | } 159 | 160 | } 161 | 162 | #endif // PARAM 163 | -------------------------------------------------------------------------------- /doc/readme/README.md.in: -------------------------------------------------------------------------------- 1 | Easy::jit: A just-in-time compiler for C++ 2 | ========================================== 3 | 4 | About 5 | ----- 6 | 7 | Easy::jit is a _compiler-assisted_ library that enables simple Just-In-Time 8 | code generation for C++ codes. 9 | 10 | ### Talks 11 | 12 | * [Easy::jit at EuroLLVM'18](https://www.youtube.com/watch?v=sFxqI6Z_bhE) 13 | * [Easy::jit at FOSDEM'18](https://www.youtube.com/watch?v=5_rydTiB32I) 14 | 15 | Building 16 | -------- 17 | 18 | First, install clang and LLVM. 19 | 20 | ```bash 21 | apt install llvm-6.0-dev llvm-6.0-tools clang-6.0 22 | ``` 23 | 24 | Then, configure and compile the project. 25 | 26 | ```bash 27 | cmake -DLLVM_DIR=/usr/lib/llvm-6.0/cmake 28 | cmake --build . 29 | ``` 30 | 31 | To build the examples, install the [opencv](https://opencv.org/) library, 32 | and add the flags ```-DEASY_JIT_EXAMPLE=1``` to the cmake command. 33 | 34 | To enable benchmarking, install the [google benchmark](https://github.com/google/benchmark) framework, 35 | and add the flags ```-DEASY_JIT_BENCHMARK=1 -DBENCHMARK_DIR=``` to the cmake command. 36 | 37 | Everything is ready to go! 38 | 39 | ### Docker 40 | 41 | If you want to give only a quick test to the project, everything is provided to use it with docker. 42 | To do this, generate a Dockerfile from the current directory using the scripts in ```/misc/docker```, 43 | then generate your docker instance. 44 | 45 | ```bash 46 | python3 /misc/docker/GenDockerfile.py /.travis.yml > Dockerfile 47 | docker build -t easy/test -f Dockerfile 48 | docker run -ti easy/test /bin/bash 49 | ``` 50 | 51 | Basic usage 52 | ----------- 53 | 54 | ### Compiling my project with Easy::Jit 55 | 56 | Since the Easy::Jit library relies on assistance from the compiler, its 57 | mandatory to load a compiler plugin in order to use it. 58 | The flag ```-Xclang -load -Xclang /bin/EasyJitPass.so``` 59 | loads the plugin. 60 | 61 | The included headers require C++14 support, and remember to add the include directories! 62 | Use ```--std=c++14 -I/cpplib/include```. 63 | 64 | Finaly, the binary must be linked against the Easy::Jit runtime library, using 65 | ```-L/bin -lEasyJitRuntime```. 66 | 67 | Putting all together we get the command bellow. 68 | 69 | ```bash 70 | clang++-6.0 --std=c++14 \ 71 | -Xclang -load -Xclang /path/to/easy/jit/build/bin/bin/EasyJitPass.so \ 72 | -I/cpplib/include \ 73 | -L/bin -lEasyJitRuntime 74 | ``` 75 | 76 | ### Using Easy::Jit inside my project 77 | 78 | Consider the code below from a software that applies image filters on a video stream. 79 | In the following sections we are going to adapt it to use the Easy::jit library. 80 | The function to optimize is ```kernel```, which applies a mask on the entire image. 81 | 82 | The mask, its dimensions and area do not change often, so specializing the function for 83 | these parameters seems reasonable. 84 | Moreover, the image dimensions and number of channels typically remain constant during 85 | the entire execution; however, it is impossible to know their values as they depend on the stream. 86 | 87 | ```cpp 88 | 89 | ``` 90 | 91 | The main header for the library is ```easy/jit.h```, where the only core function 92 | of the library is exported. This function is called -- guess how? -- ```easy::jit```. 93 | We add the corresponding include directive them in the top of the file. 94 | 95 | ```cpp 96 | 97 | ``` 98 | 99 | With the call to ```easy::jit```, we specialize the function and obtain a new 100 | one taking only two parameters (the input and the output frame). 101 | 102 | ```cpp 103 | 104 | ``` 105 | 106 | #### Deducing which functions to expose at runtime 107 | 108 | Easy::jit embeds the [LLVM bitcode](https://llvm.org/docs/LangRef.html) 109 | representation of the functions to specialize at runtime in the binary code. 110 | To perform this, the library requires access to the implementation of these 111 | functions. 112 | Easy::jit does an effort to deduce which functions are specialized at runtime, 113 | still in many cases this is not possible. 114 | 115 | In this case, it's possible to use the ```EASY_JIT_EXPOSE``` macro, as shown in 116 | the following code, 117 | 118 | ```cpp 119 | void EASY_JIT_EXPOSE kernel() { /* ... */ } 120 | ``` 121 | 122 | or using a regular expression during compilation. 123 | The command bellow exports all functions whose name starts with "^kernel". 124 | 125 | ```bash 126 | clang++ ... -mllvm -easy-export="^kernel.*" ... 127 | ``` 128 | 129 | #### Caching 130 | 131 | In parallel to the ```easy/jit.h``` header, there is ```easy/code_cache.h``` which 132 | provides a code cache to avoid recompilation of functions that already have been 133 | generated. 134 | 135 | Bellow we show the code from previous section, but adapted to use a code cache. 136 | 137 | ```cpp 138 | 139 | ``` 140 | 141 | ```cpp 142 | 143 | ``` 144 | 145 | License 146 | ------- 147 | 148 | See file `LICENSE` at the top-level directory of this project. 149 | 150 | Thanks 151 | ------ 152 | 153 | Special thanks to Quarkslab for their support on working in personal projects. 154 | 155 | Warriors 156 | -------- 157 | 158 | Serge Guelton (serge_sans_paille) 159 | 160 | Juan Manuel Martinez Caamaño (jmmartinez) 161 | 162 | Kavon Farvardin (kavon) author of [atJIT](https://github.com/kavon/atJIT) 163 | -------------------------------------------------------------------------------- /runtime/pass/DevirtualizeConstant.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace llvm; 16 | 17 | char easy::DevirtualizeConstant::ID = 0; 18 | 19 | llvm::Pass* easy::createDevirtualizeConstantPass(llvm::StringRef Name) { 20 | return new DevirtualizeConstant(Name); 21 | } 22 | 23 | static ConstantInt* getVTableHostAddress(Value& V) { 24 | auto* VTable = dyn_cast(&V); 25 | if(!VTable) 26 | return nullptr; 27 | MDNode *Tag = VTable->getMetadata(LLVMContext::MD_tbaa); 28 | if(!Tag || !Tag->isTBAAVtableAccess()) 29 | return nullptr; 30 | 31 | // that's a vtable 32 | auto* Location = dyn_cast(VTable->getPointerOperand()->stripPointerCasts()); 33 | if(!Location) 34 | return nullptr; 35 | 36 | if(auto* CE = dyn_cast(Location)) { 37 | if(CE->getOpcode() == Instruction::IntToPtr) { 38 | Location = CE->getOperand(0); 39 | } 40 | } 41 | auto* CLocation = dyn_cast(Location); 42 | if(!CLocation) 43 | return nullptr; 44 | return CLocation; 45 | } 46 | 47 | static Function* findFunctionAndLinkModules(Module& M, void* HostValue) { 48 | auto &BT = easy::BitcodeTracker::GetTracker(); 49 | const char* FName = std::get<0>(BT.getNameAndGlobalMapping(HostValue)); 50 | 51 | if(!FName) 52 | return nullptr; 53 | 54 | std::unique_ptr LM = BT.getModuleWithContext(HostValue, M.getContext()); 55 | 56 | if(!Linker::linkModules(M, std::move(LM), Linker::OverrideFromSrc, 57 | [](Module &, const StringSet<> &){})) 58 | { 59 | GlobalValue *GV = M.getNamedValue(FName); 60 | if(Function* F = dyn_cast(GV)) { 61 | F->setLinkage(Function::PrivateLinkage); 62 | return F; 63 | } 64 | else { 65 | assert(false && "wtf"); 66 | } 67 | } 68 | return nullptr; 69 | } 70 | 71 | template 72 | bool Devirtualize(IIter it, IIter end) { 73 | bool Changed = false; 74 | for(; it != end; ++it) { 75 | Instruction &I = *it; 76 | auto* VTable = getVTableHostAddress(I); 77 | if(!VTable) 78 | continue; 79 | 80 | void** RuntimeLoadedValue = *(void***)(uintptr_t)(VTable->getZExtValue()); 81 | 82 | void* CalledPtrHostValue = *RuntimeLoadedValue; 83 | llvm::Function* F = findFunctionAndLinkModules(*I.getParent()->getParent()->getParent(), CalledPtrHostValue); 84 | if(!F) 85 | continue; 86 | 87 | Changed = true; 88 | 89 | // that's generally the load from the table 90 | for(User* U : VTable->users()) { 91 | ConstantExpr* Int2Ptr = dyn_cast(U->stripPointerCasts()); 92 | if(!Int2Ptr) 93 | continue; 94 | 95 | for(User* UU : Int2Ptr->users()) { 96 | auto* CalledPtr = dyn_cast(UU); 97 | if(!CalledPtr) 98 | continue; 99 | 100 | Type* ExpectedTy = CalledPtr->getType()->getContainedType(0); 101 | Constant* Called = ConstantExpr::getPointerCast(F, ExpectedTy); 102 | 103 | SmallVector Users{CalledPtr->user_begin(), CalledPtr->user_end()}; 104 | for(User* UUU : Users) 105 | if(auto* LI = dyn_cast(UUU)) 106 | LI->replaceAllUsesWith(Called); 107 | } 108 | } 109 | } 110 | return Changed; 111 | } 112 | 113 | bool CastCallWithPointerCasts(FunctionType* CalledTy, FunctionType* UncastedTy) { 114 | if(CalledTy->getReturnType() != UncastedTy->getReturnType()) 115 | return false; 116 | if(CalledTy->getNumParams() != UncastedTy->getNumParams()) 117 | return false; 118 | if(CalledTy->isVarArg() != UncastedTy->isVarArg()) 119 | return false; 120 | 121 | size_t N = CalledTy->getNumParams(); 122 | for(size_t i = 0; i != N; ++i) { 123 | Type* CArgTy = CalledTy->getParamType(i); 124 | Type* UArgTy = UncastedTy->getParamType(i); 125 | if(CArgTy != UArgTy) { 126 | if(!CArgTy->isPointerTy() || !UArgTy->isPointerTy()) 127 | return false; 128 | } 129 | } 130 | return true; 131 | } 132 | 133 | template 134 | void RecastCalls(IIter it, IIter end) { 135 | for(;it != end;) { 136 | CallSite CS{&*it++}; 137 | if(!CS) 138 | continue; 139 | 140 | Value* Called = CS.getCalledValue(); 141 | Value* Uncasted = Called->stripPointerCasts(); 142 | if(Called == Uncasted) 143 | continue; 144 | 145 | FunctionType* CalledTy = cast(Called->getType()->getContainedType(0)); 146 | FunctionType* UncastedTy = cast(Uncasted->getType()->getContainedType(0)); 147 | 148 | if(!CastCallWithPointerCasts(CalledTy, UncastedTy)) 149 | continue; 150 | 151 | CS.setCalledFunction(Uncasted); 152 | CS.mutateFunctionType(UncastedTy); 153 | 154 | // cast every pointer argument to the expected type 155 | IRBuilder<> B(CS.getInstruction()); 156 | 157 | size_t N = CS.getNumArgOperands(); 158 | for(unsigned i = 0; i != N; ++i) { 159 | Value* Arg = CS.getArgOperand(i); 160 | Type* ArgTy = Arg->getType(); 161 | Type* UncTy = UncastedTy->getParamType(i); 162 | if(ArgTy->isPointerTy() && ArgTy != UncTy) 163 | CS.setArgument(i, B.CreatePointerCast(Arg, UncTy, Arg->getName() + ".recast_calls")); 164 | } 165 | } 166 | } 167 | 168 | bool easy::DevirtualizeConstant::runOnFunction(llvm::Function &F) { 169 | 170 | if(F.getName() != TargetName_) 171 | return false; 172 | 173 | if(Devirtualize(inst_begin(F), inst_end(F))) { 174 | RecastCalls(inst_begin(F), inst_end(F)); 175 | return true; 176 | } 177 | return false; 178 | } 179 | 180 | static RegisterPass X("","",false, false); 181 | -------------------------------------------------------------------------------- /runtime/Function.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #ifdef NDEBUG 21 | #include 22 | #endif 23 | 24 | 25 | using namespace easy; 26 | 27 | namespace easy { 28 | DefineEasyException(ExecutionEngineCreateError, "Failed to create execution engine for:"); 29 | DefineEasyException(CouldNotOpenFile, "Failed to file to dump intermediate representation."); 30 | } 31 | 32 | Function::Function(void* Addr, std::unique_ptr H) 33 | : Address(Addr), Holder(std::move(H)) { 34 | } 35 | 36 | static std::unique_ptr GetHostTargetMachine() { 37 | std::unique_ptr TM(llvm::EngineBuilder().selectTarget()); 38 | return TM; 39 | } 40 | 41 | static void Optimize(llvm::Module& M, const char* Name, const easy::Context& C, unsigned OptLevel, unsigned OptSize) { 42 | 43 | llvm::Triple Triple{llvm::sys::getProcessTriple()}; 44 | 45 | llvm::PassManagerBuilder Builder; 46 | Builder.OptLevel = OptLevel; 47 | Builder.SizeLevel = OptSize; 48 | Builder.LibraryInfo = new llvm::TargetLibraryInfoImpl(Triple); 49 | Builder.Inliner = llvm::createFunctionInliningPass(OptLevel, OptSize, false); 50 | 51 | std::unique_ptr TM = GetHostTargetMachine(); 52 | assert(TM); 53 | TM->adjustPassManager(Builder); 54 | 55 | llvm::legacy::PassManager MPM; 56 | MPM.add(llvm::createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); 57 | MPM.add(easy::createContextAnalysisPass(C)); 58 | MPM.add(easy::createInlineParametersPass(Name)); 59 | Builder.populateModulePassManager(MPM); 60 | MPM.add(easy::createDevirtualizeConstantPass(Name)); 61 | 62 | #ifdef NDEBUG 63 | MPM.add(llvm::createVerifierPass()); 64 | #endif 65 | 66 | Builder.populateModulePassManager(MPM); 67 | 68 | MPM.run(M); 69 | } 70 | 71 | static std::unique_ptr GetEngine(std::unique_ptr M, const char *Name) { 72 | llvm::EngineBuilder ebuilder(std::move(M)); 73 | std::string eeError; 74 | 75 | std::unique_ptr EE(ebuilder.setErrorStr(&eeError) 76 | .setMCPU(llvm::sys::getHostCPUName()) 77 | .setEngineKind(llvm::EngineKind::JIT) 78 | .setOptLevel(llvm::CodeGenOpt::Level::Aggressive) 79 | .create()); 80 | 81 | if(!EE) { 82 | throw easy::ExecutionEngineCreateError(Name); 83 | } 84 | 85 | return EE; 86 | } 87 | 88 | static void MapGlobals(llvm::ExecutionEngine& EE, GlobalMapping* Globals) { 89 | for(GlobalMapping *GM = Globals; GM->Name; ++GM) { 90 | EE.addGlobalMapping(GM->Name, (uint64_t)GM->Address); 91 | } 92 | } 93 | 94 | static void WriteOptimizedToFile(llvm::Module const &M, std::string const& File) { 95 | if(File.empty()) 96 | return; 97 | std::error_code Error; 98 | llvm::raw_fd_ostream Out(File, Error, llvm::sys::fs::F_None); 99 | 100 | if(Error) 101 | throw CouldNotOpenFile(Error.message()); 102 | 103 | Out << M; 104 | } 105 | 106 | std::unique_ptr 107 | CompileAndWrap(const char*Name, GlobalMapping* Globals, 108 | std::unique_ptr Ctx, 109 | std::unique_ptr M) { 110 | 111 | llvm::Module* MPtr = M.get(); 112 | std::unique_ptr EE = GetEngine(std::move(M), Name); 113 | 114 | if(Globals) { 115 | MapGlobals(*EE, Globals); 116 | } 117 | 118 | void *Address = (void*)EE->getFunctionAddress(Name); 119 | 120 | std::unique_ptr Holder(new easy::LLVMHolderImpl{std::move(EE), std::move(Ctx), MPtr}); 121 | return std::unique_ptr(new Function(Address, std::move(Holder))); 122 | } 123 | 124 | llvm::Module const& Function::getLLVMModule() const { 125 | return *static_cast(*this->Holder).M_; 126 | } 127 | 128 | std::unique_ptr Function::Compile(void *Addr, easy::Context const& C) { 129 | 130 | auto &BT = BitcodeTracker::GetTracker(); 131 | 132 | const char* Name; 133 | GlobalMapping* Globals; 134 | std::tie(Name, Globals) = BT.getNameAndGlobalMapping(Addr); 135 | 136 | std::unique_ptr M; 137 | std::unique_ptr Ctx; 138 | std::tie(M, Ctx) = BT.getModule(Addr); 139 | 140 | unsigned OptLevel; 141 | unsigned OptSize; 142 | std::tie(OptLevel, OptSize) = C.getOptLevel(); 143 | 144 | Optimize(*M, Name, C, OptLevel, OptSize); 145 | 146 | WriteOptimizedToFile(*M, C.getDebugFile()); 147 | 148 | return CompileAndWrap(Name, Globals, std::move(Ctx), std::move(M)); 149 | } 150 | 151 | void easy::Function::serialize(std::ostream& os) const { 152 | std::string buf; 153 | llvm::raw_string_ostream stream(buf); 154 | 155 | LLVMHolderImpl const *H = reinterpret_cast(Holder.get()); 156 | llvm::WriteBitcodeToFile(H->M_, stream); 157 | stream.flush(); 158 | 159 | os << buf; 160 | } 161 | 162 | std::unique_ptr easy::Function::deserialize(std::istream& is) { 163 | 164 | auto &BT = BitcodeTracker::GetTracker(); 165 | 166 | std::string buf(std::istreambuf_iterator(is), {}); // read the entire istream 167 | auto MemBuf = llvm::MemoryBuffer::getMemBuffer(llvm::StringRef(buf)); 168 | 169 | std::unique_ptr Ctx(new llvm::LLVMContext()); 170 | auto ModuleOrError = llvm::parseBitcodeFile(*MemBuf, *Ctx); 171 | if(ModuleOrError.takeError()) { 172 | return nullptr; 173 | } 174 | 175 | auto M = std::move(ModuleOrError.get()); 176 | 177 | std::string FunName = easy::GetEntryFunctionName(*M); 178 | 179 | GlobalMapping* Globals = nullptr; 180 | if(void* OrigFunPtr = BT.getAddress(FunName)) { 181 | std::tie(std::ignore, Globals) = BT.getNameAndGlobalMapping(OrigFunPtr); 182 | } 183 | 184 | return CompileAndWrap(FunName.c_str(), Globals, std::move(Ctx), std::move(M)); 185 | } 186 | 187 | bool Function::operator==(easy::Function const& other) const { 188 | LLVMHolderImpl& This = static_cast(*this->Holder); 189 | LLVMHolderImpl& Other = static_cast(*other.Holder); 190 | return This.M_ == Other.M_; 191 | } 192 | 193 | std::hash::result_type 194 | std::hash::operator()(argument_type const& F) const noexcept { 195 | LLVMHolderImpl& This = static_cast(*F.Holder); 196 | return std::hash{}(This.M_); 197 | } 198 | -------------------------------------------------------------------------------- /runtime/pass/InlineParametersHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "InlineParametersHelper.h" 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | using namespace llvm; 9 | using namespace easy; 10 | 11 | HighLevelLayout::HighLevelLayout(easy::Context const& C, llvm::Function &F) { 12 | StructReturn_ = nullptr; 13 | 14 | FunctionType* FTy = F.getFunctionType(); 15 | if(F.arg_begin()->hasStructRetAttr()) 16 | StructReturn_ = FTy->getParamType(0); 17 | 18 | Return_ = FTy->getReturnType(); 19 | 20 | auto &BT = easy::BitcodeTracker::GetTracker(); 21 | 22 | size_t ParamIdx = 0; 23 | size_t ArgIdx = 0; 24 | if(StructReturn_) 25 | ParamIdx++; 26 | 27 | for(easy::layout_id lid : C.getLayout()) { 28 | size_t N = BT.getLayoutInfo(lid).NumFields; 29 | 30 | Args_.emplace_back(ArgIdx, ParamIdx); 31 | HighLevelArg& Arg = Args_.back(); 32 | 33 | size_t ArgEnd = (ParamIdx+N); 34 | 35 | bool SingleArg = (ParamIdx + 1) == ArgEnd; 36 | if(SingleArg) { 37 | Type* ParamTy = FTy->getParamType(ParamIdx); 38 | Arg.Types_.push_back(ParamTy); 39 | Arg.StructByPointer_ = ParamTy->isPointerTy(); 40 | ++ParamIdx; 41 | ++ArgIdx; 42 | } else { 43 | for(; ParamIdx != ArgEnd; ++ParamIdx) 44 | Arg.Types_.push_back(FTy->getParamType(ParamIdx)); 45 | ++ArgIdx; 46 | } 47 | } 48 | } 49 | 50 | llvm::SmallVector 51 | easy::GetForwardArgs(easy::HighLevelLayout::HighLevelArg &ArgInF, easy::HighLevelLayout &FHLL, 52 | llvm::Function &Wrapper, easy::HighLevelLayout &WrapperHLL) { 53 | 54 | llvm::SmallVector Args; 55 | 56 | auto GetArg = [&Wrapper, &FHLL](size_t i) -> Argument* 57 | { return &*(Wrapper.arg_begin()+i+(FHLL.StructReturn_ ? 1 : 0)); }; 58 | 59 | // find layout in the new wrapper 60 | size_t ArgPosition = ArgInF.Position_; 61 | auto &ArgInWrapper = *std::find_if(WrapperHLL.Args_.begin(), WrapperHLL.Args_.end(), 62 | [ArgPosition](HighLevelLayout::HighLevelArg &ArgInWrapper) 63 | { return ArgInWrapper.Position_ == ArgPosition; }); 64 | 65 | for(size_t j = 0; j != ArgInF.Types_.size(); ++j) { 66 | Args.push_back(GetArg(ArgInWrapper.FirstParamIdx_ + j)); 67 | } 68 | return Args; 69 | } 70 | 71 | Constant* easy::GetScalarArgument(ArgumentBase const& Arg, Type* T) { 72 | switch(Arg.kind()) { 73 | case easy::ArgumentBase::AK_Int: { 74 | auto const *Int = Arg.as(); 75 | return ConstantInt::get(T, Int->get(), true); 76 | } 77 | case easy::ArgumentBase::AK_Float: { 78 | auto const *Float = Arg.as(); 79 | return ConstantFP::get(T, Float->get()); 80 | } 81 | case easy::ArgumentBase::AK_Ptr: { 82 | auto const *Ptr = Arg.as(); 83 | uintptr_t Addr = (uintptr_t)Ptr->get(); 84 | return ConstantExpr::getIntToPtr( 85 | ConstantInt::get(Type::getInt64Ty(T->getContext()), Addr, false), 86 | T); 87 | } 88 | default: 89 | return nullptr; 90 | } 91 | } 92 | 93 | llvm::Constant* easy::LinkPointerIfPossible(llvm::Module &M, easy::PtrArgument const &Ptr, Type* PtrTy) { 94 | auto &BT = easy::BitcodeTracker::GetTracker(); 95 | void* PtrValue = const_cast(Ptr.get()); 96 | if(BT.hasGlobalMapping(PtrValue)) { 97 | const char* LName = std::get<0>(BT.getNameAndGlobalMapping(PtrValue)); 98 | std::unique_ptr LM = BT.getModuleWithContext(PtrValue, M.getContext()); 99 | 100 | if(!Linker::linkModules(M, std::move(LM), Linker::OverrideFromSrc, 101 | [](Module &, const StringSet<> &){})) 102 | { 103 | GlobalValue *GV = M.getNamedValue(LName); 104 | if(GlobalVariable* G = dyn_cast(GV)) { 105 | GV->setLinkage(llvm::Function::PrivateLinkage); 106 | if(GV->getType() != PtrTy) { 107 | return ConstantExpr::getPointerCast(GV, PtrTy); 108 | } 109 | return GV; 110 | } 111 | else if(llvm::Function* F = dyn_cast(GV)) { 112 | F->setLinkage(llvm::Function::PrivateLinkage); 113 | return F; 114 | } 115 | assert(false && "wtf"); 116 | } 117 | } 118 | return nullptr; 119 | } 120 | 121 | std::pair easy::GetConstantFromRaw(llvm::DataLayout const& DL, 122 | llvm::Type* T, const uint8_t* Raw) { 123 | // pack in a I8 constant vector and cast 124 | Type* I8 = Type::getInt8Ty(T->getContext()); 125 | size_t Size = DL.getTypeStoreSize(T); // TODO: not sure about this 126 | 127 | SmallVector Elements(Size, nullptr); 128 | for(size_t i = 0; i != Size; ++i) { 129 | Elements[i] = ConstantInt::get(I8, Raw[i]); 130 | } 131 | 132 | Constant* DataAsI8 = ConstantVector::get(Elements); 133 | Constant* DataAsT; 134 | if(T->isPointerTy()) { 135 | Type* TInt = DL.getIntPtrType(T->getContext()); 136 | Constant* DataAsTSizedInt = ConstantExpr::getBitCast(DataAsI8, TInt); 137 | DataAsT = ConstantExpr::getIntToPtr(DataAsTSizedInt, T); 138 | } else { 139 | DataAsT = ConstantExpr::getBitCast(DataAsI8, T); 140 | } 141 | return {DataAsT, Size}; 142 | } 143 | 144 | static 145 | size_t StoreStructField(llvm::IRBuilder<> &B, 146 | llvm::DataLayout const &DL, 147 | Type* Ty, 148 | uint8_t const* Raw, 149 | AllocaInst* Alloc, SmallVectorImpl &GEP) { 150 | 151 | StructType* STy = dyn_cast(Ty); 152 | size_t RawOffset = 0; 153 | if(STy) { 154 | errs() << "struct " << *STy << "\n"; 155 | size_t Fields = STy->getNumContainedTypes(); 156 | for(size_t Field = 0; Field != Fields; ++Field) { 157 | GEP.push_back(B.getInt32(Field)); 158 | size_t Size = StoreStructField(B, DL, STy->getElementType(Field), Raw+RawOffset, Alloc, GEP); 159 | RawOffset += Size; 160 | GEP.pop_back(); 161 | } 162 | } else { 163 | Constant* FieldValue; 164 | std::tie(FieldValue, RawOffset) = easy::GetConstantFromRaw(DL, Ty, (uint8_t const*)Raw); 165 | 166 | Value* FieldPtr = B.CreateGEP(nullptr, Alloc, GEP, "field.gep"); 167 | B.CreateStore(FieldValue, FieldPtr); 168 | } 169 | return RawOffset; 170 | } 171 | 172 | llvm::AllocaInst* easy::GetStructAlloc(llvm::IRBuilder<> &B, 173 | llvm::DataLayout const &DL, 174 | easy::StructArgument const &Struct, 175 | llvm::Type* StructPtrTy) { 176 | Type* StructTy = StructPtrTy->getContainedType(0); 177 | AllocaInst* Alloc = B.CreateAlloca(StructTy); 178 | 179 | SmallVector GEP = {B.getInt32(0)}; 180 | 181 | // TODO: Data points to the data structure or holds the data structure itself ? 182 | // Check that size matches the .data() 183 | 184 | size_t Size = StoreStructField(B, DL, StructTy, (uint8_t const*)Struct.get().data(), Alloc, GEP); 185 | 186 | return Alloc; 187 | } 188 | -------------------------------------------------------------------------------- /include/easy/runtime/Context.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTEXT 2 | #define CONTEXT 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | namespace easy { 14 | 15 | struct serialized_arg { 16 | std::vector buf; 17 | 18 | serialized_arg(char* serialized) { 19 | uint32_t size = *reinterpret_cast(serialized); 20 | const char* data = serialized + sizeof(uint32_t); 21 | buf.insert(buf.end(), data, data+size); 22 | 23 | free(serialized); 24 | } 25 | }; 26 | 27 | typedef void* layout_id; 28 | 29 | struct ArgumentBase { 30 | 31 | enum ArgumentKind { 32 | AK_Forward, 33 | AK_Int, 34 | AK_Float, 35 | AK_Ptr, 36 | AK_Struct, 37 | AK_Module, 38 | }; 39 | 40 | ArgumentBase() = default; 41 | virtual ~ArgumentBase() = default; 42 | 43 | bool operator==(ArgumentBase const &Other) const { 44 | return this->kind() == Other.kind() && 45 | this->compareWithSameType(Other); 46 | } 47 | 48 | template 49 | std::enable_if_t::value, ArgTy const*> 50 | as() const { 51 | if(kind() == ArgTy::Kind) return static_cast(this); 52 | else return nullptr; 53 | } 54 | 55 | friend std::hash; 56 | 57 | virtual ArgumentKind kind() const noexcept = 0; 58 | 59 | protected: 60 | virtual bool compareWithSameType(ArgumentBase const&) const = 0; 61 | virtual size_t hash() const noexcept = 0; 62 | }; 63 | 64 | #define DeclareArgument(Name, Type) \ 65 | class Name##Argument \ 66 | : public ArgumentBase { \ 67 | \ 68 | using HashType = std::remove_const_t>; \ 69 | \ 70 | Type Data_; \ 71 | public: \ 72 | Name##Argument(Type D) : ArgumentBase(), Data_(D) {} \ 73 | virtual ~Name ## Argument() override = default ;\ 74 | Type get() const { return Data_; } \ 75 | static constexpr ArgumentKind Kind = AK_##Name;\ 76 | ArgumentKind kind() const noexcept override { return Kind; } \ 77 | \ 78 | protected: \ 79 | bool compareWithSameType(ArgumentBase const& Other) const override { \ 80 | auto const &OtherCast = static_cast(Other); \ 81 | return Data_ == OtherCast.Data_; \ 82 | } \ 83 | \ 84 | size_t hash() const noexcept override { return std::hash{}(Data_); } \ 85 | } 86 | 87 | DeclareArgument(Forward, unsigned); 88 | DeclareArgument(Int, int64_t); 89 | DeclareArgument(Float, double); 90 | DeclareArgument(Ptr, void const*); 91 | DeclareArgument(Module, easy::Function const&); 92 | 93 | class StructArgument 94 | : public ArgumentBase { 95 | serialized_arg Data_; 96 | 97 | public: 98 | StructArgument(serialized_arg &&arg) 99 | : ArgumentBase(), Data_(arg) {} 100 | virtual ~StructArgument() override = default; 101 | std::vector const & get() const { return Data_.buf; } 102 | static constexpr ArgumentKind Kind = AK_Struct; 103 | ArgumentKind kind() const noexcept override { return Kind; } 104 | 105 | protected: 106 | bool compareWithSameType(ArgumentBase const& Other) const override { 107 | auto const &OtherCast = static_cast(Other); 108 | return get() == OtherCast.get(); 109 | } 110 | 111 | size_t hash() const noexcept override { 112 | std::hash hash{}; 113 | size_t R = 0; 114 | for (char c : get()) 115 | R ^= hash(c); 116 | return R; 117 | } 118 | }; 119 | 120 | // class that holds information about the just-in-time context 121 | class Context { 122 | 123 | std::vector> ArgumentMapping_; 124 | unsigned OptLevel_ = 2, OptSize_ = 0; 125 | std::string DebugFile_; 126 | 127 | // describes how the arguments of the function are passed 128 | // struct arguments can be packed in a single int, or passed field by field, 129 | // keep track of how many arguments a parameter takes 130 | std::vector ArgumentLayout_; 131 | 132 | template 133 | inline Context& setArg(Args && ... args) { 134 | ArgumentMapping_.emplace_back(new ArgTy(std::forward(args)...)); 135 | return *this; 136 | } 137 | 138 | public: 139 | 140 | Context() = default; 141 | 142 | bool operator==(const Context&) const; 143 | 144 | // set the mapping between 145 | Context& setParameterIndex(unsigned); 146 | Context& setParameterInt(int64_t); 147 | Context& setParameterFloat(double); 148 | Context& setParameterPointer(void const*); 149 | Context& setParameterStruct(serialized_arg); 150 | Context& setParameterModule(easy::Function const&); 151 | 152 | Context& setArgumentLayout(layout_id id) { 153 | ArgumentLayout_.push_back(id); // each layout id is associated with a number of fields in the bitcode tracker 154 | return *this; 155 | } 156 | 157 | decltype(ArgumentLayout_) const & getLayout() const { 158 | return ArgumentLayout_; 159 | } 160 | 161 | template 162 | Context& setParameterTypedPointer(T* ptr) { 163 | return setParameterPointer(reinterpret_cast(ptr)); 164 | } 165 | 166 | Context& setOptLevel(unsigned OptLevel, unsigned OptSize) { 167 | OptLevel_ = OptLevel; 168 | OptSize_ = OptSize; 169 | return *this; 170 | } 171 | 172 | Context& setDebugFile(std::string const &File) { 173 | DebugFile_ = File; 174 | return *this; 175 | } 176 | 177 | std::pair getOptLevel() const { 178 | return std::make_pair(OptLevel_, OptSize_); 179 | } 180 | 181 | std::string const& getDebugFile() const { 182 | return DebugFile_; 183 | } 184 | 185 | auto begin() const { return ArgumentMapping_.begin(); } 186 | auto end() const { return ArgumentMapping_.end(); } 187 | size_t size() const { return ArgumentMapping_.size(); } 188 | 189 | ArgumentBase const& getArgumentMapping(size_t i) const { 190 | return *ArgumentMapping_[i]; 191 | } 192 | 193 | friend bool operator<(easy::Context const &C1, easy::Context const &C2); 194 | }; 195 | 196 | } 197 | 198 | namespace std 199 | { 200 | template struct hash> 201 | { 202 | typedef std::pair argument_type; 203 | typedef std::size_t result_type; 204 | result_type operator()(argument_type const& s) const noexcept { 205 | return std::hash{}(s.first) ^ std::hash{}(s.second); 206 | } 207 | }; 208 | 209 | template<> struct hash 210 | { 211 | typedef easy::ArgumentBase argument_type; 212 | typedef std::size_t result_type; 213 | result_type operator()(argument_type const& s) const noexcept { 214 | return s.hash(); 215 | } 216 | }; 217 | 218 | template<> struct hash 219 | { 220 | typedef easy::Context argument_type; 221 | typedef std::size_t result_type; 222 | result_type operator()(argument_type const& C) const noexcept { 223 | size_t H = 0; 224 | std::hash ArgHash; 225 | std::hash> OptHash; 226 | for(auto const &Arg : C) 227 | H ^= ArgHash(*Arg); 228 | H ^= OptHash(C.getOptLevel()); 229 | return H; 230 | } 231 | }; 232 | } 233 | 234 | 235 | #endif 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Easy::jit: A just-in-time compiler for C++ 2 | ========================================== 3 | 4 | About 5 | ----- 6 | 7 | Easy::jit is a _compiler-assisted_ library that enables simple Just-In-Time 8 | code generation for C++ codes. 9 | 10 | ### Talks 11 | 12 | * [Easy::jit at EuroLLVM'18](https://www.youtube.com/watch?v=sFxqI6Z_bhE) 13 | * [Easy::jit at FOSDEM'18](https://www.youtube.com/watch?v=5_rydTiB32I) 14 | 15 | Building 16 | -------- 17 | 18 | First, install clang and LLVM. 19 | 20 | ```bash 21 | apt install llvm-6.0-dev llvm-6.0-tools clang-6.0 22 | ``` 23 | 24 | Then, configure and compile the project. 25 | 26 | ```bash 27 | cmake -DLLVM_DIR=/usr/lib/llvm-6.0/cmake 28 | cmake --build . 29 | ``` 30 | 31 | To build the examples, install the [opencv](https://opencv.org/) library, 32 | and add the flags ```-DEASY_JIT_EXAMPLE=1``` to the cmake command. 33 | 34 | To enable benchmarking, install the [google benchmark](https://github.com/google/benchmark) framework, 35 | and add the flags ```-DEASY_JIT_BENCHMARK=1 -DBENCHMARK_DIR=``` to the cmake command. 36 | 37 | Everything is ready to go! 38 | 39 | ### Docker 40 | 41 | If you want to give only a quick test to the project, everything is provided to use it with docker. 42 | To do this, generate a Dockerfile from the current directory using the scripts in ```/misc/docker```, 43 | then generate your docker instance. 44 | 45 | ```bash 46 | python3 /misc/docker/GenDockerfile.py /.travis.yml > Dockerfile 47 | docker build -t easy/test -f Dockerfile 48 | docker run -ti easy/test /bin/bash 49 | ``` 50 | 51 | Basic usage 52 | ----------- 53 | 54 | ### Compiling my project with Easy::Jit 55 | 56 | Since the Easy::Jit library relies on assistance from the compiler, its 57 | mandatory to load a compiler plugin in order to use it. 58 | The flag ```-Xclang -load -Xclang /bin/EasyJitPass.so``` 59 | loads the plugin. 60 | 61 | The included headers require C++14 support, and remember to add the include directories! 62 | Use ```--std=c++14 -I/cpplib/include```. 63 | 64 | Finaly, the binary must be linked against the Easy::Jit runtime library, using 65 | ```-L/bin -lEasyJitRuntime```. 66 | 67 | Putting all together we get the command bellow. 68 | 69 | ```bash 70 | clang++-6.0 --std=c++14 \ 71 | -Xclang -load -Xclang /path/to/easy/jit/build/bin/bin/EasyJitPass.so \ 72 | -I/cpplib/include \ 73 | -L/bin -lEasyJitRuntime 74 | ``` 75 | 76 | ### Using Easy::Jit inside my project 77 | 78 | Consider the code below from a software that applies image filters on a video stream. 79 | In the following sections we are going to adapt it to use the Easy::jit library. 80 | The function to optimize is ```kernel```, which applies a mask on the entire image. 81 | 82 | The mask, its dimensions and area do not change often, so specializing the function for 83 | these parameters seems reasonable. 84 | Moreover, the image dimensions and number of channels typically remain constant during 85 | the entire execution; however, it is impossible to know their values as they depend on the stream. 86 | 87 | ```cpp 88 | static void kernel(const char* mask, unsigned mask_size, unsigned mask_area, 89 | const unsigned char* in, unsigned char* out, 90 | unsigned rows, unsigned cols, unsigned channels) { 91 | unsigned mask_middle = (mask_size/2+1); 92 | unsigned middle = (cols+1)*mask_middle; 93 | 94 | for(unsigned i = 0; i != rows-mask_size; ++i) { 95 | for(unsigned j = 0; j != cols-mask_size; ++j) { 96 | for(unsigned ch = 0; ch != channels; ++ch) { 97 | 98 | long out_val = 0; 99 | for(unsigned ii = 0; ii != mask_size; ++ii) { 100 | for(unsigned jj = 0; jj != mask_size; ++jj) { 101 | out_val += mask[ii*mask_size+jj] * in[((i+ii)*cols+j+jj)*channels+ch]; 102 | } 103 | } 104 | out[(i*cols+j+middle)*channels+ch] = out_val / mask_area; 105 | } 106 | } 107 | } 108 | } 109 | 110 | static void apply_filter(const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) { 111 | kernel(mask, mask_size, mask_area, image.ptr(0,0), out->ptr(0,0), image.rows, image.cols, image.channels()); 112 | } 113 | ``` 114 | 115 | The main header for the library is ```easy/jit.h```, where the only core function 116 | of the library is exported. This function is called -- guess how? -- ```easy::jit```. 117 | We add the corresponding include directive them in the top of the file. 118 | 119 | ```cpp 120 | #include 121 | ``` 122 | 123 | With the call to ```easy::jit```, we specialize the function and obtain a new 124 | one taking only two parameters (the input and the output frame). 125 | 126 | ```cpp 127 | static void apply_filter(const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) { 128 | using namespace std::placeholders; 129 | 130 | auto kernel_opt = easy::jit(kernel, mask, mask_size, mask_area, _1, _2, image.rows, image.cols, image.channels()); 131 | kernel_opt(image.ptr(0,0), out->ptr(0,0)); 132 | } 133 | ``` 134 | 135 | #### Deducing which functions to expose at runtime 136 | 137 | Easy::jit embeds the [LLVM bitcode](https://llvm.org/docs/LangRef.html) 138 | representation of the functions to specialize at runtime in the binary code. 139 | To perform this, the library requires access to the implementation of these 140 | functions. 141 | Easy::jit does an effort to deduce which functions are specialized at runtime, 142 | still in many cases this is not possible. 143 | 144 | In this case, it's possible to use the ```EASY_JIT_EXPOSE``` macro, as shown in 145 | the following code, 146 | 147 | ```cpp 148 | void EASY_JIT_EXPOSE kernel() { /* ... */ } 149 | ``` 150 | 151 | or using a regular expression during compilation. 152 | The command bellow exports all functions whose name starts with "^kernel". 153 | 154 | ```bash 155 | clang++ ... -mllvm -easy-export="^kernel.*" ... 156 | ``` 157 | 158 | #### Caching 159 | 160 | In parallel to the ```easy/jit.h``` header, there is ```easy/code_cache.h``` which 161 | provides a code cache to avoid recompilation of functions that already have been 162 | generated. 163 | 164 | Bellow we show the code from previous section, but adapted to use a code cache. 165 | 166 | ```cpp 167 | #include 168 | ``` 169 | 170 | ```cpp 171 | static void apply_filter(const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) { 172 | using namespace std::placeholders; 173 | 174 | static easy::Cache<> cache; 175 | auto const &kernel_opt = cache.jit(kernel, mask, mask_size, mask_area, _1, _2, image.rows, image.cols, image.channels()); 176 | kernel_opt(image.ptr(0,0), out->ptr(0,0)); 177 | } 178 | ``` 179 | 180 | License 181 | ------- 182 | 183 | See file `LICENSE` at the top-level directory of this project. 184 | 185 | Thanks 186 | ------ 187 | 188 | Special thanks to Quarkslab for their support on working in personal projects. 189 | 190 | Warriors 191 | -------- 192 | 193 | Serge Guelton (serge_sans_paille) 194 | 195 | Juan Manuel Martinez Caamaño (jmmartinez) 196 | 197 | Kavon Farvardin (kavon) author of [atJIT](https://github.com/kavon/atJIT) 198 | -------------------------------------------------------------------------------- /pass/Layout.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define DEBUG_TYPE "easy-register-layout" 11 | #include 12 | 13 | #include 14 | 15 | #include "Utils.h" 16 | #include 17 | 18 | using namespace llvm; 19 | 20 | namespace easy { 21 | struct RegisterLayout : public ModulePass { 22 | static char ID; 23 | 24 | RegisterLayout() 25 | : ModulePass(ID) {}; 26 | 27 | bool runOnModule(Module &M) override { 28 | 29 | SmallVector LayoutFunctions; 30 | collectLayouts(M, LayoutFunctions); 31 | 32 | if(LayoutFunctions.empty()) 33 | return false; 34 | 35 | Function* Register = declareRegisterLayout(M); 36 | registerLayouts(LayoutFunctions, Register); 37 | 38 | return true; 39 | } 40 | 41 | static void collectLayouts(Module &M, SmallVectorImpl &LayoutFunctions) { 42 | for(Function &F : M) 43 | if(F.getSection() == LAYOUT_SECTION) 44 | LayoutFunctions.push_back(&F); 45 | } 46 | 47 | static Function* GetSerializeStruct(Function* F) { 48 | // fragile! 49 | for(Instruction &I : llvm::instructions(F)) { 50 | if(isa(I)) 51 | continue; 52 | for(Value* Op : I.operands()) { 53 | if(ConstantExpr* OpCE = dyn_cast(Op)) 54 | if(OpCE->getOpcode() == Instruction::BitCast) 55 | Op = OpCE->getOperand(0); 56 | if(Function* F = dyn_cast(Op)) 57 | return F; 58 | } 59 | } 60 | assert(false && "unreachable"); 61 | return nullptr; 62 | } 63 | 64 | static Function* DeclareMalloc(Module &M) { 65 | if(Function* Malloc = M.getFunction("malloc")) 66 | return Malloc; 67 | Type* IntPtrTy = M.getDataLayout().getIntPtrType(M.getContext()); 68 | Type* I8PtrTy = Type::getInt8PtrTy(M.getContext()); 69 | FunctionType* FTy = FunctionType::get(I8PtrTy, {IntPtrTy}, false); 70 | return Function::Create(FTy, Function::ExternalLinkage, "malloc", &M); 71 | } 72 | 73 | template 74 | static size_t GetStructSize(TypeIterator begin, TypeIterator end, DataLayout const &DL) { 75 | return std::accumulate(begin, end, 0ul, 76 | [&DL](size_t A, Type* T) { return A + DL.getTypeStoreSize(T); } ); 77 | } 78 | 79 | static void SerializeStruct(IRBuilder<> &B, size_t &Offset, DataLayout const & DL, 80 | Value* Buf, Value* ByVal, Type* CurLevelTy, SmallVectorImpl &GEPOffset) { 81 | StructType* Struct = dyn_cast(CurLevelTy); 82 | if(!Struct) { 83 | Value* ArgPtr = B.CreateGEP(ByVal, GEPOffset); 84 | Value* Argument = B.CreateLoad(ArgPtr); 85 | 86 | Value* Ptr = B.CreateConstGEP1_32(Buf, Offset); 87 | B.CreateStore(Argument, Ptr); 88 | 89 | Offset += DL.getTypeStoreSize(Argument->getType()); 90 | return; 91 | } 92 | 93 | Type* I32Ty = Type::getInt32Ty(B.getContext()); 94 | GEPOffset.push_back(nullptr); 95 | for(size_t Arg = 0; Arg != Struct->getNumElements(); Arg++) { 96 | GEPOffset.back() = ConstantInt::get(I32Ty, Arg); 97 | SerializeStruct(B, Offset, DL, Buf, ByVal, Struct->getElementType(Arg), GEPOffset); 98 | } 99 | GEPOffset.pop_back(); 100 | } 101 | 102 | static void SerializeArguments(IRBuilder<> &B, size_t &Offset, DataLayout const & DL, Value* Buf, Function* F) { 103 | for(size_t Arg = 0; Arg != F->arg_size(); Arg++) { 104 | Value *Argument = F->arg_begin()+Arg; 105 | Type* ArgTy = Argument->getType(); 106 | 107 | Value* Ptr = B.CreateConstGEP1_32(Buf, Offset); 108 | Ptr = B.CreatePointerCast(Ptr, PointerType::getUnqual(ArgTy), Argument->getName() + ".ptr"); 109 | B.CreateStore(Argument, Ptr); 110 | 111 | Offset += DL.getTypeStoreSize(Argument->getType()); 112 | } 113 | } 114 | 115 | static void DefineSerializeStruct(Function *F) { 116 | if(!F->isDeclaration()) 117 | return; 118 | 119 | LLVMContext &C = F->getContext(); 120 | 121 | Module &M = *F->getParent(); 122 | Function *Malloc = DeclareMalloc(M); 123 | 124 | DataLayout const &DL = M.getDataLayout(); 125 | 126 | FunctionType *FTy = F->getFunctionType(); 127 | StructType *STy = F->arg_begin()->hasByValAttr() ? cast(FTy->getParamType(0)->getContainedType(0)) : nullptr; 128 | bool PassedAsAPointer = STy; 129 | 130 | Type* I32 = Type::getInt32Ty(C); 131 | Type* I32Ptr = PointerType::getUnqual(I32); 132 | size_t I32Size = DL.getTypeStoreSize(I32); 133 | 134 | BasicBlock *BB = BasicBlock::Create(C, "entry", F); 135 | IRBuilder<> B(BB); 136 | 137 | size_t ArgSize; 138 | if(PassedAsAPointer) ArgSize = GetStructSize(STy->element_begin(), STy->element_end(), DL); 139 | else ArgSize = GetStructSize(FTy->param_begin(), FTy->param_end(), DL); 140 | 141 | Value* Buf = B.CreateCall(Malloc, {ConstantInt::get(Malloc->getFunctionType()->getParamType(0), I32Size + ArgSize)}, "buf"); 142 | B.CreateStore(ConstantInt::get(I32, ArgSize), B.CreatePointerCast(Buf, I32Ptr, "size.ptr")); 143 | 144 | SmallVector Offset = { ConstantInt::getNullValue(I32) }; 145 | if(PassedAsAPointer) SerializeStruct(B, I32Size, DL, Buf, F->arg_begin(), STy, Offset); 146 | else SerializeArguments(B, I32Size, DL, Buf, F); 147 | 148 | B.CreateRet(Buf); 149 | } 150 | 151 | static void registerLayouts(SmallVectorImpl &LayoutFunctions, Function* Register) { 152 | 153 | FunctionType* RegisterTy = Register->getFunctionType(); 154 | Type* IdTy = RegisterTy->getParamType(0); 155 | Type* NTy = RegisterTy->getParamType(1); 156 | 157 | // register the layout info in a constructor 158 | Function* Ctor = GetCtor(*Register->getParent(), "register_layout"); 159 | IRBuilder<> B(Ctor->getEntryBlock().getTerminator()); 160 | 161 | for(Function *F : LayoutFunctions) { 162 | Function* SerializeStructFun = GetSerializeStruct(F); 163 | 164 | DefineSerializeStruct(SerializeStructFun); 165 | 166 | size_t N = SerializeStructFun->getFunctionType()->getNumParams(); 167 | Value* Id = B.CreatePointerCast(SerializeStructFun, IdTy); 168 | B.CreateCall(Register, {Id, ConstantInt::get(NTy, N, false)}); 169 | } 170 | } 171 | 172 | static Function* declareRegisterLayout(Module &M) { 173 | StringRef Name = "easy_register_layout"; 174 | if(Function* F = M.getFunction(Name)) 175 | return F; 176 | 177 | LLVMContext &C = M.getContext(); 178 | DataLayout const &DL = M.getDataLayout(); 179 | 180 | Type* Void = Type::getVoidTy(C); 181 | Type* I8Ptr = Type::getInt8PtrTy(C); 182 | Type* SizeT = DL.getLargestLegalIntType(C); 183 | 184 | FunctionType* FTy = 185 | FunctionType::get(Void, {I8Ptr, SizeT}, false); 186 | return Function::Create(FTy, Function::ExternalLinkage, Name, &M); 187 | } 188 | }; 189 | 190 | char RegisterLayout::ID = 0; 191 | 192 | llvm::Pass* createRegisterLayoutPass() { 193 | return new RegisterLayout(); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /doc/readme/camfilter.cpp: -------------------------------------------------------------------------------- 1 | // REQUIRES: example 2 | // test that it compiles, links and loads correctly. 3 | // RUN: %bin/easyjit-example 1 1 2 3 4 s 1 2 3 4 s 1 2 3 4 q 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // INLINE FROM HERE #INCLUDE_EASY# 13 | #include 14 | // TO HERE #INCLUDE_EASY# 15 | 16 | // INLINE FROM HERE #INCLUDE_EASY_CACHE# 17 | #include 18 | // TO HERE #INCLUDE_EASY_CACHE# 19 | 20 | struct timeval; 21 | 22 | static double get_fps() { 23 | static struct timeval before; 24 | struct timeval now; 25 | 26 | gettimeofday(&now, nullptr); 27 | 28 | long secs = now.tv_sec - before.tv_sec; 29 | long usec = now.tv_usec - before.tv_usec; 30 | double diff = secs + ((double)usec)/1000000.0; 31 | before = now; 32 | 33 | return 1.0/diff; 34 | } 35 | 36 | namespace original { 37 | // INLINE FROM HERE #ORIGINAL# 38 | static void kernel(const char* mask, unsigned mask_size, unsigned mask_area, 39 | const unsigned char* in, unsigned char* out, 40 | unsigned rows, unsigned cols, unsigned channels) { 41 | unsigned mask_middle = (mask_size/2+1); 42 | unsigned middle = (cols+1)*mask_middle; 43 | 44 | for(unsigned i = 0; i != rows-mask_size; ++i) { 45 | for(unsigned j = 0; j != cols-mask_size; ++j) { 46 | for(unsigned ch = 0; ch != channels; ++ch) { 47 | 48 | long out_val = 0; 49 | for(unsigned ii = 0; ii != mask_size; ++ii) { 50 | for(unsigned jj = 0; jj != mask_size; ++jj) { 51 | out_val += mask[ii*mask_size+jj] * in[((i+ii)*cols+j+jj)*channels+ch]; 52 | } 53 | } 54 | out[(i*cols+j+middle)*channels+ch] = out_val / mask_area; 55 | } 56 | } 57 | } 58 | } 59 | 60 | static void apply_filter(const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) { 61 | kernel(mask, mask_size, mask_area, image.ptr(0,0), out->ptr(0,0), image.rows, image.cols, image.channels()); 62 | } 63 | // TO HERE #ORIGINAL# 64 | } 65 | 66 | namespace jit { 67 | using original::kernel; 68 | // INLINE FROM HERE #EASY# 69 | static void apply_filter(const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) { 70 | using namespace std::placeholders; 71 | 72 | auto kernel_opt = easy::jit(kernel, mask, mask_size, mask_area, _1, _2, image.rows, image.cols, image.channels()); 73 | kernel_opt(image.ptr(0,0), out->ptr(0,0)); 74 | } 75 | // TO HERE #EASY# 76 | } 77 | 78 | namespace cache { 79 | using original::kernel; 80 | // INLINE FROM HERE #EASY_CACHE# 81 | static void apply_filter(const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) { 82 | using namespace std::placeholders; 83 | 84 | static easy::Cache<> cache; 85 | auto const &kernel_opt = cache.jit(kernel, mask, mask_size, mask_area, _1, _2, image.rows, image.cols, image.channels()); 86 | kernel_opt(image.ptr(0,0), out->ptr(0,0)); 87 | } 88 | // TO HERE #EASY_CACHE# 89 | } 90 | 91 | static const char mask_no_filter[1] = {1}; 92 | static const char mask_gauss_3[9] = {1,1,1,1,1,1,1,1,1}; 93 | static const char mask_gauss_5[25] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; 94 | static const char mask_gauss_7[49] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; 95 | 96 | static const char* masks[] = { 97 | mask_no_filter, 98 | mask_gauss_3, 99 | mask_gauss_5, 100 | mask_gauss_7 101 | }; 102 | 103 | static unsigned mask_size[] = { 104 | 1, 105 | 3, 106 | 5, 107 | 7 108 | }; 109 | 110 | static unsigned mask_area[] = { 111 | 1, 112 | 9, 113 | 25, 114 | 49 115 | }; 116 | 117 | static void (*apply_filter)(const char*, unsigned, unsigned, cv::Mat &, cv::Mat *&) = original::apply_filter; 118 | 119 | static char get_keystroke(bool test, char** argv, int argc) { 120 | if(!test) 121 | return cv::waitKey(1); 122 | 123 | // read the first character of each argument and use it as the key stroke 124 | static int key_count = 2; 125 | if(key_count >= argc) 126 | return 'q'; 127 | 128 | char key = argv[key_count][0]; 129 | 130 | key_count++; 131 | return key; 132 | } 133 | 134 | static bool get_next_frame(bool test, cv::Mat &frame) { 135 | if(test) { 136 | // return a black frame 137 | frame = cv::Mat(cv::Size(640, 480), CV_8UC3, cv::Scalar(0)); 138 | return true; 139 | } else { 140 | // capture from the video camara 141 | static cv::VideoCapture video(0); 142 | 143 | if(!video.isOpened()) { 144 | std::cerr << "cannot open camara.\n"; 145 | return false; 146 | } 147 | 148 | video.read(frame); 149 | return true; 150 | } 151 | } 152 | 153 | int main(int argc, char** argv) { 154 | bool test = 0; 155 | if(argc > 2) 156 | test = atoi(argv[1]); 157 | 158 | std::cerr << "\npress 1, 2, 3, 4 to change the filter.\n" 159 | "s to switch the implementation, from original to easy-jit(no cache), and to easy-jit(cache).\n" 160 | "q to exit.\n\n"; 161 | 162 | int mask_no = 0; 163 | 164 | cv::Mat Frame; 165 | static cv::Mat Output; 166 | cv::Mat *Out = nullptr; 167 | 168 | unsigned time = 0; 169 | std::stringstream fps_message; 170 | 171 | double fps_history[4]; 172 | 173 | while (true) { 174 | if(!get_next_frame(test, Frame)) 175 | return -1; 176 | 177 | // allocate the output frame 178 | if(!Out) { 179 | Output = Frame.clone(); 180 | Out = &Output; 181 | } 182 | 183 | get_fps(); 184 | 185 | // apply the filter 186 | apply_filter(masks[mask_no], mask_size[mask_no], mask_area[mask_no], Frame, Out); 187 | 188 | double fps = get_fps(); 189 | fps_history[time%4] = fps; 190 | 191 | if(time % 4 == 0) { 192 | double fps_avg = std::accumulate(std::begin(fps_history), std::end(fps_history), 0.0)/4.0; 193 | fps_message.str(""); 194 | fps_message << "fps: " << fps_avg; 195 | } 196 | 197 | // show the fps, updated every 4 iterations 198 | cv::putText(*Out, fps_message.str().c_str(), cvPoint(30,30), 199 | cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, 200 | cvScalar(200,200,250), 1, CV_AA); 201 | 202 | if(!test) { 203 | cv::imshow("camera", *Out); 204 | } 205 | ++time; 206 | 207 | // user input 208 | char key = get_keystroke(test, argv, argc); 209 | if (key < 0) 210 | continue; 211 | 212 | switch(key) { 213 | case 'q': 214 | return 0; 215 | case 's': 216 | { 217 | if(apply_filter == original::apply_filter) { 218 | std::cerr << "using easy::jit (no-cache) implementation\n"; 219 | apply_filter = jit::apply_filter; 220 | } else if (apply_filter == jit::apply_filter) { 221 | std::cerr << "using easy::jit (cache) implementation\n"; 222 | apply_filter = cache::apply_filter; 223 | } else { 224 | std::cerr << "using original implementation\n"; 225 | apply_filter = original::apply_filter; 226 | } 227 | break; 228 | } 229 | case '1': 230 | case '2': 231 | case '3': 232 | case '4': 233 | { 234 | mask_no = key-'1'; 235 | std::cerr << "change mask to " << mask_no+1 << "!\n"; 236 | break; 237 | } 238 | default: 239 | std::cerr << "unknown stroke " << key << "\n"; 240 | } 241 | } 242 | 243 | return 0; 244 | } 245 | -------------------------------------------------------------------------------- /include/easy/meta.h: -------------------------------------------------------------------------------- 1 | #ifndef META 2 | #define META 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace easy { 10 | namespace meta { 11 | 12 | template 13 | struct type_list { 14 | 15 | template 16 | using push_front = struct type_list; 17 | template 18 | using push_back = struct type_list; 19 | template 20 | using remove = type_list<>; 21 | template 22 | static constexpr bool has = false; 23 | 24 | // undefined 25 | template 26 | using at = void; 27 | template 28 | using set = meta::type_list<>; 29 | 30 | static constexpr size_t size = 0; 31 | static constexpr bool empty = true; 32 | }; 33 | 34 | template 35 | struct type_list { 36 | using head = Head; 37 | using tail = struct type_list; 38 | 39 | template 40 | using push_front = struct type_list; 41 | template 42 | using push_back = struct type_list; 43 | template 44 | using at = std::conditional_t>; 45 | 46 | template 47 | using set = std::conditional_t, 49 | typename tail::template set::template push_front>; 50 | 51 | template 52 | using remove = std::conditional_t::value, 53 | typename tail::template remove, 54 | typename tail::template remove::template push_front>; 55 | template 56 | static constexpr bool has = std::is_same::value || tail:: template has; 57 | 58 | static constexpr size_t size = 1+tail::size; 59 | static constexpr bool empty = false; 60 | }; 61 | 62 | template 63 | struct init_list { 64 | 65 | template 66 | struct helper { 67 | using type = typename helper::type::template push_front; 68 | }; 69 | 70 | template 71 | struct helper<0,TT> { 72 | using type = meta::type_list<>; 73 | }; 74 | 75 | using type = typename helper::type; 76 | }; 77 | 78 | template 79 | T* get_as_pointer(T &&A) { return &A; } 80 | 81 | template 82 | T* get_as_pointer(T* A) { return A; } 83 | 84 | 85 | namespace { 86 | 87 | template 88 | using is_ph = std::is_placeholder>; 89 | 90 | template 91 | struct discard_options { 92 | 93 | template 94 | struct helper { 95 | using type = meta::type_list<>; 96 | }; 97 | 98 | template 99 | struct helper { 100 | using head = typename AL::head; 101 | using tail = typename AL::tail; 102 | static bool constexpr is_opt = 103 | options::is_option>::value; 104 | using recursive = typename helper::type; 105 | using type = std::conditional_t>; 108 | }; 109 | 110 | using type = typename helper::type; 111 | }; 112 | 113 | template 114 | struct max_placeholder { 115 | 116 | template 117 | struct helper { 118 | static constexpr size_t max = Max; 119 | }; 120 | 121 | template 122 | struct helper { 123 | using head = typename AL::head; 124 | using tail = typename AL::tail; 125 | static constexpr size_t max = helper(is_ph::value, Max)>::max; 126 | }; 127 | 128 | static constexpr size_t max = helper::max; 129 | }; 130 | 131 | template 132 | struct map_placeholder_to_type { 133 | 134 | template 135 | struct helper { 136 | static_assert(Seen::size == N, "Seen::size != N"); 137 | static_assert(Result::size == N, "Result::size != N"); 138 | static_assert(!Result::template has, "Void cannot appear in the resulting type"); 139 | using type = Result; 140 | }; 141 | 142 | template 143 | struct helper{ 144 | using al_head = typename AL::head; 145 | using al_tail = typename AL::tail; 146 | 147 | static bool constexpr parse_placeholder = is_ph::value && !Seen::template has; 148 | static size_t constexpr result_idx = parse_placeholder?is_ph::value-1:0; 149 | static size_t constexpr arg_idx = PL::size - AL::size; 150 | using pl_at_idx = typename PL::template at; 151 | 152 | // for 153 | // foo(int, bool, float) and specialization foo(int(4), _2, _1) 154 | // [int,bool,float] [int,_2,_1] [void,void] [] 2 155 | // [int,bool,float] [_2,_1] [void,void] [] 2 156 | // [int,bool,float] [_1] [void,bool] [_2] 2 157 | // [int,bool,float] [] [float,bool] [_2,_1] 2 158 | // yields new foo'(float _1, bool _2) = foo(int(4), _2, _1); 159 | 160 | static_assert(PL::size >= AL::size, "easy::jit: More parameters than arguments specified"); 161 | static_assert(result_idx < Result::size, "easy::jit: Cannot have a placeholder outside the maximum"); 162 | using new_result = std::conditional_t, Result>; 163 | using new_seen = std::conditional_t, Seen>; 164 | 165 | using type = typename helper::type; 166 | }; 167 | template 168 | struct helper, Result, Seen, N, false>{ 169 | static_assert(N==-1 /*just to make this context dependent*/, "easy::jit: Invalid bind, placeholder cannot be bound to a formal argument"); 170 | }; 171 | 172 | using default_param = void; 173 | static constexpr size_t N = max_placeholder::max; 174 | using type = typename helper::type, meta::type_list<>, N, N == 0>::type; 175 | }; 176 | 177 | template 178 | struct get_new_param_list { 179 | using type = typename map_placeholder_to_type::type; 180 | }; 181 | 182 | template 183 | Ret get_return_type(Ret(*)(Args...)); 184 | 185 | template 186 | type_list get_parameter_list(Ret(*)(Args...)); 187 | 188 | } 189 | 190 | template 191 | struct function_traits { 192 | static_assert(std::is_function::value, "function expected."); 193 | using ptr_ty = std::decay_t; 194 | using return_type = decltype(get_return_type(std::declval())); 195 | using parameter_list = decltype(get_parameter_list(std::declval())); 196 | }; 197 | 198 | template 199 | struct new_function_traits { 200 | using OriginalTraits = function_traits; 201 | using return_type = typename OriginalTraits::return_type; 202 | using clean_arg_list = typename discard_options::type; 203 | using parameter_list = typename get_new_param_list::type; 204 | }; 205 | 206 | } 207 | } 208 | 209 | 210 | 211 | #endif 212 | -------------------------------------------------------------------------------- /runtime/pass/InlineParameters.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "InlineParametersHelper.h" 16 | 17 | using namespace llvm; 18 | using easy::HighLevelLayout; 19 | 20 | char easy::InlineParameters::ID = 0; 21 | 22 | llvm::Pass* easy::createInlineParametersPass(llvm::StringRef Name) { 23 | return new InlineParameters(Name); 24 | } 25 | 26 | HighLevelLayout GetNewLayout(easy::Context const &C, HighLevelLayout &HLL) { 27 | 28 | assert(C.size() == HLL.Args_.size()); 29 | 30 | size_t NNewArgs = 0; 31 | for(auto const &Arg : C) 32 | if(auto const *Map = Arg->as()) 33 | NNewArgs = std::max(NNewArgs, Map->get()+1); 34 | 35 | HighLevelLayout NewHLL(HLL); 36 | NewHLL.Args_.clear(); 37 | NewHLL.Args_.resize(NNewArgs, HighLevelLayout::HighLevelArg()); 38 | 39 | SmallSet VisitedArgs; 40 | 41 | // only forwarded params are kept 42 | for(size_t arg = 0; arg != HLL.Args_.size(); ++arg) { 43 | if(auto const *Map = C.getArgumentMapping(arg).as()) { 44 | if(!VisitedArgs.insert(Map->get()).second) 45 | continue; 46 | NewHLL.Args_[Map->get()] = HLL.Args_[arg]; 47 | } 48 | } 49 | 50 | // set the param_idx once all the parameter sizes are known 51 | for(size_t new_arg = 0, ParamIdx = 0; new_arg != NewHLL.Args_.size(); ++new_arg) { 52 | NewHLL.Args_[new_arg].FirstParamIdx_ = ParamIdx; 53 | ParamIdx += NewHLL.Args_[new_arg].Types_.size(); 54 | } 55 | return NewHLL; 56 | } 57 | 58 | FunctionType* GetWrapperTy(HighLevelLayout &HLL) { 59 | SmallVector Args; 60 | if(HLL.StructReturn_) 61 | Args.push_back(HLL.StructReturn_); 62 | for(auto &HLArg : HLL.Args_) 63 | Args.insert(Args.end(), HLArg.Types_.begin(), HLArg.Types_.end()); 64 | return FunctionType::get(HLL.Return_, Args, false); 65 | } 66 | 67 | void GetInlineArgs(easy::Context const &C, 68 | Function& F, HighLevelLayout &FHLL, 69 | Function &Wrapper, HighLevelLayout &WrapperHLL, 70 | SmallVectorImpl &Args, IRBuilder<> &B) { 71 | 72 | LLVMContext &Ctx = F.getContext(); 73 | DataLayout const &DL = F.getParent()->getDataLayout(); 74 | 75 | if(FHLL.StructReturn_) 76 | Args.push_back(&*Wrapper.arg_begin()); 77 | 78 | for(size_t i = 0, n = C.size(); i != n; ++i) { 79 | auto const &Arg = C.getArgumentMapping(i); 80 | auto &ArgInF = FHLL.Args_[i]; 81 | 82 | switch(Arg.kind()) { 83 | 84 | case easy::ArgumentBase::AK_Forward: { 85 | auto Forward = GetForwardArgs(ArgInF, FHLL, Wrapper, WrapperHLL); 86 | Args.insert(Args.end(), Forward.begin(), Forward.end()); 87 | } break; 88 | case easy::ArgumentBase::AK_Int: 89 | case easy::ArgumentBase::AK_Float: { 90 | Args.push_back(easy::GetScalarArgument(Arg, ArgInF.Types_[0])); 91 | } break; 92 | 93 | case easy::ArgumentBase::AK_Ptr: { 94 | auto const *Ptr = Arg.as(); 95 | Type* PtrTy = FHLL.Args_[i].Types_[0]; 96 | 97 | Constant* PtrVal = easy::GetScalarArgument(Arg, PtrTy); 98 | if(Constant* LinkedPtr = easy::LinkPointerIfPossible(*Wrapper.getParent(), *Ptr, PtrTy)) 99 | PtrVal = LinkedPtr; 100 | 101 | Args.push_back(PtrVal); 102 | } break; 103 | 104 | case easy::ArgumentBase::AK_Struct: { 105 | auto const *Struct = Arg.as(); 106 | auto &ArgInF = FHLL.Args_[i]; 107 | 108 | if(ArgInF.StructByPointer_) { 109 | // struct is passed trough a pointer 110 | AllocaInst* ParamAlloc = easy::GetStructAlloc(B, DL, *Struct, ArgInF.Types_[0]); 111 | Args.push_back(ParamAlloc); 112 | } else { 113 | // struct is passed by value (may be many values) 114 | size_t N = ArgInF.Types_.size(); 115 | for(size_t ParamIdx = 0, RawOffset = 0; ParamIdx != N; ++ParamIdx) { 116 | Type* FieldTy = ArgInF.Types_[ParamIdx]; 117 | const char* RawField = &Struct->get()[RawOffset]; 118 | 119 | Constant* FieldValue; 120 | size_t RawSize; 121 | std::tie(FieldValue, RawSize) = easy::GetConstantFromRaw(DL, FieldTy, (uint8_t const*)RawField); 122 | 123 | Args.push_back(FieldValue); 124 | RawOffset += RawSize; 125 | } 126 | } 127 | } break; 128 | 129 | case easy::ArgumentBase::AK_Module: { 130 | 131 | auto &ArgInF = FHLL.Args_[i]; 132 | assert(ArgInF.Types_.size() == 1); 133 | 134 | easy::Function const &Function = Arg.as()->get(); 135 | llvm::Module const& FunctionModule = Function.getLLVMModule(); 136 | auto FunctionName = easy::GetEntryFunctionName(FunctionModule); 137 | 138 | std::unique_ptr LM = 139 | easy::CloneModuleWithContext(FunctionModule, Wrapper.getContext()); 140 | 141 | assert(LM); 142 | 143 | easy::UnmarkEntry(*LM); 144 | 145 | llvm::Module* M = Wrapper.getParent(); 146 | if(Linker::linkModules(*M, std::move(LM), Linker::OverrideFromSrc, 147 | [](Module &, const StringSet<> &){})) { 148 | llvm::report_fatal_error("Failed to link with another module!", true); 149 | } 150 | 151 | llvm::Function* FunctionInM = M->getFunction(FunctionName); 152 | FunctionInM->setLinkage(Function::PrivateLinkage); 153 | 154 | Args.push_back(FunctionInM); 155 | 156 | } break; 157 | } 158 | } 159 | } 160 | 161 | void RemapAttributes(Function const &F, HighLevelLayout const& HLL, Function &Wrapper, HighLevelLayout const& NewHLL) { 162 | auto FAttributes = F.getAttributes(); 163 | 164 | auto FunAttrs = FAttributes.getFnAttributes(); 165 | for(Attribute Attr : FunAttrs) 166 | Wrapper.addFnAttr(Attr); 167 | 168 | for(size_t new_arg = 0; new_arg != NewHLL.Args_.size(); ++new_arg) { 169 | auto const &NewArg = NewHLL.Args_[new_arg]; 170 | auto const &OrgArg = HLL.Args_[NewArg.Position_]; 171 | 172 | for(size_t field = 0; field != NewArg.Types_.size(); ++field) { 173 | Wrapper.addParamAttrs(field + NewArg.FirstParamIdx_, 174 | FAttributes.getParamAttributes(field + OrgArg.FirstParamIdx_)); 175 | } 176 | } 177 | } 178 | 179 | Function* CreateWrapperFun(Module &M, Function &F, HighLevelLayout &HLL, easy::Context const &C) { 180 | LLVMContext &CC = M.getContext(); 181 | 182 | HighLevelLayout NewHLL(GetNewLayout(C, HLL)); 183 | FunctionType *WrapperTy = GetWrapperTy(NewHLL); 184 | 185 | Function* Wrapper = Function::Create(WrapperTy, Function::ExternalLinkage, "", &M); 186 | 187 | BasicBlock* BB = BasicBlock::Create(CC, "", Wrapper); 188 | IRBuilder<> B(BB); 189 | 190 | SmallVector Args; 191 | GetInlineArgs(C, F, HLL, *Wrapper, NewHLL, Args, B); 192 | 193 | Value* Call = B.CreateCall(&F, Args); 194 | 195 | if(HLL.StructReturn_) { 196 | Wrapper->arg_begin()->addAttr(Attribute::StructRet); 197 | } 198 | 199 | if(Call->getType()->isVoidTy()) { 200 | B.CreateRetVoid(); 201 | } else { 202 | B.CreateRet(Call); 203 | } 204 | 205 | RemapAttributes(F, HLL, *Wrapper, NewHLL); 206 | 207 | return Wrapper; 208 | } 209 | 210 | bool easy::InlineParameters::runOnModule(llvm::Module &M) { 211 | 212 | easy::Context const &C = getAnalysis().getContext(); 213 | llvm::Function* F = M.getFunction(TargetName_); 214 | assert(F); 215 | 216 | HighLevelLayout HLL(C, *F); 217 | llvm::Function* WrapperFun = CreateWrapperFun(M, *F, HLL, C); 218 | 219 | // privatize F, steal its name, copy its attributes, and its cc 220 | F->setLinkage(llvm::Function::PrivateLinkage); 221 | WrapperFun->takeName(F); 222 | WrapperFun->setCallingConv(CallingConv::C); 223 | 224 | // add metadata to identify the entry function 225 | easy::MarkAsEntry(*WrapperFun); 226 | 227 | 228 | return true; 229 | } 230 | 231 | static RegisterPass X("","",false, false); 232 | -------------------------------------------------------------------------------- /pass/Easy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "llvm/InitializePasses.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | #define DEBUG_TYPE "easy-register-bitcode" 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #include 30 | 31 | #include "MayAliasTracer.h" 32 | #include "StaticPasses.h" 33 | #include "Utils.h" 34 | 35 | using namespace llvm; 36 | 37 | static cl::opt RegexString("easy-export", 38 | cl::desc("A regular expression to describe functions to expose at runtime."), 39 | cl::init("")); 40 | 41 | namespace easy { 42 | struct RegisterBitcode : public ModulePass { 43 | static char ID; 44 | 45 | RegisterBitcode() 46 | : ModulePass(ID) {}; 47 | 48 | bool runOnModule(Module &M) override { 49 | 50 | // execute the rest of the easy::jit passes 51 | legacy::PassManager Passes; 52 | Passes.add(easy::createRegisterLayoutPass()); 53 | bool Changed = Passes.run(M); 54 | 55 | SmallVector ObjectsToJIT; 56 | 57 | collectObjectsToJIT(M, ObjectsToJIT); 58 | 59 | if(ObjectsToJIT.empty()) 60 | return Changed; 61 | 62 | SmallVector LocalVariables; 63 | collectLocalGlobals(M, LocalVariables); 64 | nameGlobals(LocalVariables, "unnamed_local_global"); 65 | 66 | auto Bitcode = embedBitcode(M, ObjectsToJIT); 67 | GlobalVariable* GlobalMapping = getGlobalMapping(M, LocalVariables); 68 | 69 | Function* RegisterBitcodeFun = declareRegisterBitcode(M, GlobalMapping); 70 | registerBitcode(M, ObjectsToJIT, Bitcode, GlobalMapping, RegisterBitcodeFun); 71 | 72 | return Changed; 73 | } 74 | 75 | private: 76 | 77 | static bool canExtractBitcode(GlobalObject &GO, std::string &Reason) { 78 | if(GO.isDeclaration()) { 79 | Reason = "Can't extract a declaration."; 80 | return false; 81 | } 82 | return true; 83 | } 84 | 85 | static auto compilerInterface(Module &M) { 86 | SmallVector, 4> Funs; 87 | std::copy_if(M.begin(), M.end(), std::back_inserter(Funs), 88 | [](Function &F) {return F.getSection() == CI_SECTION;}); 89 | return Funs; 90 | } 91 | 92 | static bool mayBeOverload(Function *FOverload, Function *F) { 93 | auto *FOverloadTy = FOverload->getFunctionType(); 94 | auto *FTy = F->getFunctionType(); 95 | if (FTy->getReturnType() != FOverloadTy->getReturnType()) 96 | return false; 97 | if (FTy->getNumParams() != FOverloadTy->getNumParams()) 98 | return false; 99 | if (FOverloadTy->isVarArg()) 100 | return false; 101 | 102 | auto FParamIter = FTy->param_begin(); 103 | auto FOverloadParamIter = FOverloadTy->param_begin(); 104 | 105 | // TODO: check that first parameter type are compatible? 106 | if (!isa(*FOverloadParamIter)) 107 | return false; 108 | 109 | for (++FParamIter, ++FOverloadParamIter; FParamIter != FTy->param_end(); 110 | ++FParamIter, ++FOverloadParamIter) { 111 | if (*FParamIter != *FOverloadParamIter) 112 | return false; 113 | } 114 | 115 | // an overload must be registered in a virtual table 116 | for(User* U : F->users()) { 117 | auto* CE = dyn_cast(U); 118 | if(!CE || !CE->isCast()) 119 | continue; 120 | 121 | for(User* CEU : CE->users()) { 122 | if(auto* Init = dyn_cast(CEU)) { 123 | return true; // probably a vtable 124 | } 125 | } 126 | } 127 | 128 | return false; 129 | } 130 | 131 | void collectObjectsToJIT(Module &M, SmallVectorImpl &ObjectsToJIT) { 132 | 133 | // get **all** functions passed as parameter to easy jit calls 134 | // not only the target function, but also its parameters 135 | deduceObjectsToJIT(M); 136 | regexFunctionsToJIT(M); 137 | deduceVirtualMethodsToJIT(M); 138 | 139 | // get functions in section jit section 140 | for(GlobalObject &GO : M.global_objects()) { 141 | if(GO.getSection() != JIT_SECTION) 142 | continue; 143 | 144 | GO.setSection(""); // drop the identifier 145 | 146 | std::string Reason; 147 | if(!canExtractBitcode(GO, Reason)) { 148 | DEBUG(dbgs() << "Could not extract global '" << GO.getName() << "'. " << Reason << "\n"); 149 | continue; 150 | } 151 | DEBUG(dbgs() << "Global '" << GO.getName() << "' marked for extraction.\n"); 152 | 153 | ObjectsToJIT.push_back(&GO); 154 | } 155 | 156 | // also collect virtual functions in two steps 157 | 158 | } 159 | 160 | static bool isConstant(GlobalObject const& GO) { 161 | if(isa(GO)) 162 | return true; 163 | return cast(GO).isConstant(); 164 | } 165 | 166 | void deduceVirtualMethodsToJIT(Module &M) { 167 | // First collect all loaded types we could monitor stores but stores 168 | // could be done at call site, outside of ObjectsToJIT's scopes 169 | 170 | SmallPtrSet VirtualMethodTys; 171 | for(Function& F: M) { 172 | if(F.getSection() != JIT_SECTION) 173 | continue; 174 | for(auto& I : instructions(F)) { 175 | auto* LI = dyn_cast(&I); 176 | if(!LI) 177 | continue; 178 | 179 | MDNode *Tag = I.getMetadata(LLVMContext::MD_tbaa); 180 | if(!Tag || !Tag->isTBAAVtableAccess()) 181 | continue; 182 | 183 | VirtualMethodTys.insert(cast(cast(LI->getType())->getElementType())->getElementType()); 184 | } 185 | } 186 | 187 | // Second look at functions that have a type compatible with the virtual one 188 | for(GlobalObject &GO : M.global_objects()) { 189 | GlobalVariable* GV = dyn_cast(&GO); 190 | if(!GV || ! GV->hasInitializer()) 191 | continue; 192 | 193 | ConstantStruct* CS = dyn_cast(GV->getInitializer()); 194 | if(!CS || CS->getNumOperands() != 1) 195 | continue; 196 | 197 | ConstantAggregate* CA = dyn_cast(CS->getOperand(0)); 198 | if(!CA) 199 | continue; 200 | 201 | for(Use& U : CA->operands()) { 202 | Constant* C = dyn_cast(U.get()); 203 | if(!C) 204 | continue; 205 | if(auto* CE = dyn_cast(C)) { 206 | if(CE->isCast()) { 207 | C = CE->getOperand(0); 208 | } 209 | } 210 | auto* F = dyn_cast(C); 211 | if(!F) 212 | continue; 213 | 214 | auto* FTy = F->getFunctionType(); 215 | if(VirtualMethodTys.count(FTy)) { 216 | F->setSection(JIT_SECTION); 217 | // also look for overloads 218 | for(auto& FOverload: M) { 219 | if(&FOverload == F) 220 | continue; 221 | if(mayBeOverload(&FOverload, F)) 222 | FOverload.setSection(JIT_SECTION); 223 | } 224 | } 225 | } 226 | } 227 | } 228 | 229 | void deduceObjectsToJIT(Module &M) { 230 | for(Function &EasyJitFun : compilerInterface(M)) { 231 | for(User* U : EasyJitFun.users()) { 232 | if(CallSite CS{U}) { 233 | for(Value* O : CS.args()) { 234 | O = O->stripPointerCastsNoFollowAliases(); 235 | MayAliasTracer Tracer(O); 236 | for(GlobalObject& GO: M.global_objects()) { 237 | if(isConstant(GO) and Tracer.count(GO)) { 238 | GO.setSection(JIT_SECTION); 239 | } 240 | } 241 | } 242 | } 243 | } 244 | } 245 | } 246 | 247 | static void regexFunctionsToJIT(Module &M) { 248 | if(RegexString.empty()) 249 | return; 250 | llvm::Regex Match(RegexString); 251 | for(GlobalObject &GO : M.global_objects()) 252 | if(Match.match(GO.getName())) 253 | GO.setSection(JIT_SECTION); 254 | } 255 | 256 | static void collectLocalGlobals(Module &M, SmallVectorImpl &Globals) { 257 | for(GlobalVariable &GV : M.globals()) 258 | if(GV.hasLocalLinkage()) 259 | Globals.push_back(&GV); 260 | } 261 | 262 | static void nameGlobals(SmallVectorImpl &Globals, Twine Name) { 263 | for(GlobalValue *GV : Globals) 264 | if(!GV->hasName()) 265 | GV->setName(Name); 266 | } 267 | 268 | static GlobalVariable* 269 | getGlobalMapping(Module &M, SmallVectorImpl &Globals) { 270 | LLVMContext &C = M.getContext(); 271 | SmallVector Entries; 272 | 273 | Type* PtrTy = Type::getInt8PtrTy(C); 274 | StructType *EntryTy = StructType::get(C, {PtrTy, PtrTy}, true); 275 | 276 | for(GlobalValue* GV : Globals) { 277 | GlobalVariable* Name = getStringGlobal(M, GV->getName()); 278 | Constant* NameCast = ConstantExpr::getPointerCast(Name, PtrTy); 279 | Constant* GVCast = GV; 280 | if(GV->getType() != PtrTy) 281 | GVCast = ConstantExpr::getPointerCast(GV, PtrTy); 282 | Constant* Entry = ConstantStruct::get(EntryTy, {NameCast, GVCast}); 283 | Entries.push_back(Entry); 284 | } 285 | Entries.push_back(Constant::getNullValue(EntryTy)); 286 | 287 | Constant* Init = ConstantArray::get(ArrayType::get(EntryTy, Entries.size()), Entries); 288 | return new GlobalVariable(M, Init->getType(), true, 289 | GlobalVariable::PrivateLinkage, 290 | Init, "global_mapping"); 291 | } 292 | 293 | static SmallVector 294 | embedBitcode(Module &M, SmallVectorImpl &Objs) { 295 | SmallVector Bitcode(Objs.size()); 296 | for(size_t i = 0, n = Objs.size(); i != n; ++i) 297 | Bitcode[i] = embedBitcode(M, *Objs[i]); 298 | return Bitcode; 299 | } 300 | 301 | static GlobalVariable* embedBitcode(Module &M, GlobalObject& GO) { 302 | std::unique_ptr Embed = CloneModule(&M); 303 | 304 | GlobalValue *FEmbed = Embed->getNamedValue(GO.getName()); 305 | assert(FEmbed && "global value with that name exists"); 306 | cleanModule(*FEmbed, *Embed); 307 | 308 | Twine ModuleName = GO.getName() + "_bitcode"; 309 | Embed->setModuleIdentifier(ModuleName.str()); 310 | 311 | return writeModuleToGlobal(M, *Embed, FEmbed->getName() + "_bitcode"); 312 | } 313 | 314 | static std::string moduleToString(Module &M) { 315 | std::string s; 316 | raw_string_ostream so(s); 317 | WriteBitcodeToFile(&M, so); 318 | so.flush(); 319 | return s; 320 | } 321 | 322 | static GlobalVariable* writeModuleToGlobal(Module &M, Module &Embed, Twine Name) { 323 | std::string Bitcode = moduleToString(Embed); 324 | Constant* BitcodeInit = ConstantDataArray::getString(M.getContext(), Bitcode, true); 325 | return new GlobalVariable(M, BitcodeInit->getType(), true, 326 | GlobalVariable::PrivateLinkage, 327 | BitcodeInit, Name); 328 | } 329 | 330 | static void cleanModule(GlobalValue &Entry, Module &M) { 331 | 332 | llvm::StripDebugInfo(M); 333 | 334 | bool ForFunction = isa(Entry); 335 | 336 | auto Referenced = getReferencedFromEntry(Entry); 337 | Referenced.push_back(&Entry); 338 | 339 | if(ForFunction) { 340 | Entry.setLinkage(GlobalValue::ExternalLinkage); 341 | } 342 | 343 | //clean the cloned module 344 | legacy::PassManager Passes; 345 | Passes.add(createGVExtractionPass(Referenced)); 346 | Passes.add(createGlobalDCEPass()); 347 | Passes.add(createStripDeadDebugInfoPass()); 348 | Passes.add(createStripDeadPrototypesPass()); 349 | Passes.run(M); 350 | 351 | if(ForFunction) { 352 | fixLinkages(Entry, M); 353 | } 354 | } 355 | 356 | static std::vector getReferencedFromEntry(GlobalValue &Entry) { 357 | std::vector Funs; 358 | 359 | SmallPtrSet Visited; 360 | SmallVector ToVisit; 361 | ToVisit.push_back(&Entry); 362 | 363 | while(!ToVisit.empty()) { 364 | User* U = ToVisit.pop_back_val(); 365 | if(!Visited.insert(U).second) 366 | continue; 367 | if(Function* UF = dyn_cast(U)) { 368 | Funs.push_back(UF); 369 | 370 | for(Instruction &I : instructions(UF)) 371 | for(Value* Op : I.operands()) 372 | if(User* OpU = dyn_cast(Op)) 373 | ToVisit.push_back(OpU); 374 | } 375 | else if(GlobalVariable* GV = dyn_cast(U)) { 376 | if(GV->hasInitializer()) { 377 | ToVisit.push_back(GV->getInitializer()); 378 | } 379 | } 380 | 381 | for(Value* Op : U->operands()) 382 | if(User* OpU = dyn_cast(Op)) 383 | ToVisit.push_back(OpU); 384 | } 385 | 386 | return Funs; 387 | } 388 | 389 | static void fixLinkages(GlobalValue &Entry, Module &M) { 390 | for(GlobalValue &GV : M.global_values()) { 391 | if(GV.getName().startswith("llvm.")) 392 | continue; 393 | 394 | if(GlobalObject* GO = dyn_cast(&GV)) { 395 | GO->setComdat(nullptr); 396 | } 397 | 398 | if(auto* GVar = dyn_cast(&GV)) { 399 | // gv becomes a declaration 400 | GVar->setInitializer(nullptr); 401 | GVar->setVisibility(GlobalValue::DefaultVisibility); 402 | GVar->setLinkage(GlobalValue::ExternalLinkage); 403 | } else if(auto* F = dyn_cast(&GV)) { 404 | // f becomes private 405 | F->removeFnAttr(Attribute::NoInline); 406 | if(F == &Entry) 407 | continue; 408 | 409 | if(!F->isDeclaration() && 410 | (F->getVisibility() != GlobalValue::DefaultVisibility || 411 | F->getLinkage() != GlobalValue::PrivateLinkage)) { 412 | F->setVisibility(GlobalValue::DefaultVisibility); 413 | F->setLinkage(GlobalValue::PrivateLinkage); 414 | } 415 | } else llvm::report_fatal_error("Easy::Jit [not yet implemented]: handle aliases, ifuncs."); 416 | } 417 | } 418 | 419 | Function* declareRegisterBitcode(Module &M, GlobalVariable *GlobalMapping) { 420 | StringRef Name = "easy_register"; 421 | if(Function* F = M.getFunction(Name)) 422 | return F; 423 | 424 | LLVMContext &C = M.getContext(); 425 | DataLayout const &DL = M.getDataLayout(); 426 | 427 | Type* Void = Type::getVoidTy(C); 428 | Type* I8Ptr = Type::getInt8PtrTy(C); 429 | Type* GMTy = GlobalMapping->getType(); 430 | Type* SizeT = DL.getLargestLegalIntType(C); 431 | 432 | assert(SizeT); 433 | 434 | FunctionType* FTy = 435 | FunctionType::get(Void, {I8Ptr, I8Ptr, GMTy, I8Ptr, SizeT}, false); 436 | return Function::Create(FTy, Function::ExternalLinkage, Name, &M); 437 | } 438 | 439 | static void 440 | registerBitcode(Module &M, SmallVectorImpl &Objs, 441 | SmallVectorImpl &Bitcodes, 442 | Value* GlobalMapping, 443 | Function* RegisterBitcodeFun) { 444 | // Create static initializer with low priority to register everything 445 | Type* FPtr = RegisterBitcodeFun->getFunctionType()->getParamType(0); 446 | Type* StrPtr = RegisterBitcodeFun->getFunctionType()->getParamType(1); 447 | Type* BitcodePtr = RegisterBitcodeFun->getFunctionType()->getParamType(3); 448 | Type* SizeTy = RegisterBitcodeFun->getFunctionType()->getParamType(4); 449 | 450 | Function *Ctor = GetCtor(M, "register_bitcode"); 451 | IRBuilder<> B(Ctor->getEntryBlock().getTerminator()); 452 | 453 | for(size_t i = 0, n = Objs.size(); i != n; ++i) { 454 | GlobalVariable* Name = getStringGlobal(M, Objs[i]->getName()); 455 | ArrayType* ArrTy = cast(Bitcodes[i]->getInitializer()->getType()); 456 | size_t Size = ArrTy->getNumElements()-1; /*-1 for the 0 terminator*/ 457 | 458 | Value* Fun = B.CreatePointerCast(Objs[i], FPtr); 459 | Value* NameCast = B.CreatePointerCast(Name, StrPtr); 460 | Value* Bitcode = B.CreatePointerCast(Bitcodes[i], BitcodePtr); 461 | Value* BitcodeSize = ConstantInt::get(SizeTy, Size, false); 462 | 463 | // fun, name, gm, bitcode, bitcode size 464 | B.CreateCall(RegisterBitcodeFun, 465 | {Fun, NameCast, GlobalMapping, Bitcode, BitcodeSize}, ""); 466 | } 467 | } 468 | 469 | static GlobalVariable* getStringGlobal(Module& M, StringRef Name) { 470 | Constant* Init = ConstantDataArray::getString(M.getContext(), Name, true); 471 | return new GlobalVariable(M, Init->getType(), true, 472 | GlobalVariable::PrivateLinkage, 473 | Init, Name + "_name"); 474 | } 475 | }; 476 | 477 | char RegisterBitcode::ID = 0; 478 | static RegisterPass Register("easy-register-bitcode", 479 | "Parse the compilation unit and insert runtime library calls to register " 480 | "the bitcode associated to functions marked as \"jit\".", 481 | false, false); 482 | 483 | llvm::Pass* createRegisterBitcodePass() { 484 | return new RegisterBitcode(); 485 | } 486 | } 487 | --------------------------------------------------------------------------------