Integration Into Existing Project¶
PochiVM’s intuitive syntax and powerful inlining features is at the cost of a somewhat complicated build process. Further complicated by the nature of LLVM’s sensitive version requirements, it is impossible that PochiVM could be provided as a plug-and-use library.
So unfortunately, integrating PochiVM into an existing project with an existing build system is more complicated, and requires understanding of PochiVM’s internal build process and requirements. In the rest of the section, we will go through the internals of PochiVM’s build system, so one can figure out how to best integrate PochiVM into an existing project.
Build Process Behind the Scene¶
Get LLVM IR of Runtime Library¶
We start with the .cpp files in the runtime folder.
The first step of the build process is to let clang++ emit the LLVM IR for those source files.
This is done using the clang-specific compiler flag -emit-llvm.
After this step we get a bunch of .bc files (though CMake still names them with the extension of .o),
which are the LLVM IR bitcode for the C++ sources.
Extract Information from IR¶
The runtime library builder (which source code resides in runtime_lib_builder folder) is
responsible for a list of things (these are skippable technical details):
Figure out which C++ entities (functions, member functions, member objects, etc) are registered in
pochivm_register_runtime.cppby JIT-executing the entry-point function__pochivm_register_runtime_library__in a sandbox.Extract LLVM metadata and IR information of the runtime library for all registered entities, generate the extracted IR into data files, which will be linked into the main binary, so PochiVM can access them at runtime.
Generate C++ header files, so users can express the use of the registered C++ entities in an intuitive syntax (that header file is what allows static type-checking of function paramaters, and allows one to write calls in generated code just as if writing in C++).
Generate
libruntime.aby invokingllcto lower the LLVM IR bitcode to.oobject files, and invokingarto archive the.oobject files into a library. It is important that we usellcto convert the.bcfiles to object file, notclang++to re-compile the source file, otherwise we could get inconsistent inlining decisions which result in missing symbols.
In short, the runtime library libruntime.a must be built using a special build process,
which as a side-product, also generates certain C++ header and source files needed to build the PochiVM library.
When integrating PochiVM into your own project’s build system, you have to keep this build step unchanged.
Build the Rest of the Project¶
Once the runtime library is built, the PochiVM library may be built.
Then, the rest of the project which depends on PochiVM may be built.
It is important that you keep the build dependency that
runtimelibrary depends onruntime_lib_builderutility.
PochiVMlibrary depends onruntimelibrary.All code using PochiVM depends on
PochiVMlibrary.
Final Linking Step¶
The following CMake link flags and libraries are needed for any binary file that uses PochiVM:
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic ")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LLVM_EXTRA_LINK_FLAGS} ")
target_link_libraries([your_target_name] PUBLIC
"-Wl,--whole-archive" runtime "-Wl,--no-whole-archive"
runtime_bc
pochivm
${LLVM_EXTRA_LINK_LIBRARIES}
)
The -Wl,--whole-archive linker flag is important: any function in runtime library,
even if not referenced directly by the rest of the codebase, may be called at runtime by generated code,
so we must tell the linker to keep all symbols.
Warning
PochiVM does not work with -flto (clang’s link-time-optimization feature),
since there is a known bug that clang LTO does not respect -Wl,--whole-archive linker flag.
Post-Build Validation Pass¶
PochiVM also provides a post-build validation utility to validate that nothing has gone wrong on the PochiVM side.
While this is optional, it is recommended that you run the pass just in case you hit a bug in PochiVM.
The utility for this validation is in folder post_build_verifier,
and the validator program takes a single argument, the built binary file to validate.
You can replicate the CMake logic by looking at how the post-build-pass is implemented in the project root’s CMakeLists.txt.
Note
If post_build_verifier returns an error and you have not modified the build process,
it is most likely a bug in PochiVM itself. Please report a bug.
Why PochiVM is Built in Docker¶
As you can see, the build steps involve a reflective step:
the C++ source file is compiled by clang++ to LLVM IR,
then the LLVM IR is parsed by the LLVM library and linked back into our application.
By design of LLVM, each version of LLVM is explicitly version-locked with the clang++ having the same version number.
This implies that LLVM of version A.B.C can only understand the LLVM IR output of clang++ of exactly the same version number A.B.C.
Furthermore, LLVM library has no backward or forward API compatibility.
This means that PochiVM (which is currently using LLVM 10.0.0) may or may not work with another LLVM version.
So in short, the PochiVM library and the runtime library must be built using precisely LLVM 10.0.0 and clang++ 10.0.0.
Of course it is impractical to ask one to install a specific version of clang++ and LLVM in order to build PochiVM.
This is why we used docker to provide a virtualized build environment in which we have precise control over the version of the toolchain.
Nonetheless, you don’t have to switch your whole project to use docker, or getting locked on a specific compiler version
(although it would be easier to integrate if you are fine with it).
Only the source code in runtime folder must be compiled by that specific version of clang++.
The rest of your project (including the ones using pochivm.h) is free to use whatever compiler that supports C++17 (since pochivm.h employed many C++17 features), and does not need access to any LLVM headers, and may or may not use docker in the build system.
Of course the final binary still needs to be linked against LLVM 10.0.0, but one can just store the LLVM libraries statically somewhere in the project by copying them out from docker.