2424#include " gandiva/engine.h"
2525
2626#include < iostream>
27+ #include < memory>
28+ #include < mutex>
2729#include < sstream>
2830#include < string>
2931#include < unordered_set>
4143#include < llvm/Analysis/Passes.h>
4244#include < llvm/Analysis/TargetTransformInfo.h>
4345#include < llvm/Bitcode/BitcodeReader.h>
46+ #include < llvm/ExecutionEngine/ExecutionEngine.h>
4447#include < llvm/ExecutionEngine/MCJIT.h>
4548#include < llvm/IR/DataLayout.h>
49+ #include < llvm/IR/IRBuilder.h>
50+ #include < llvm/IR/LLVMContext.h>
4651#include < llvm/IR/LegacyPassManager.h>
4752#include < llvm/IR/Verifier.h>
4853#include < llvm/Linker/Linker.h>
6166#pragma warning(pop)
6267#endif
6368
69+ #include " gandiva/configuration.h"
6470#include " gandiva/decimal_ir.h"
6571#include " gandiva/exported_funcs_registry.h"
6672
73+ #include " arrow/util/make_unique.h"
74+
6775namespace gandiva {
6876
6977extern const unsigned char kPrecompiledBitcode [];
7078extern const size_t kPrecompiledBitcodeSize ;
7179
72- std::once_flag init_once_flag;
73-
74- bool Engine::init_once_done_ = false ;
75- std::set<std::string> Engine::loaded_libs_ = {};
76- std::mutex Engine::mtx_;
80+ std::once_flag llvm_init_once_flag;
81+ static bool llvm_init = false ;
7782
78- // One-time initializations.
7983void Engine::InitOnce () {
80- DCHECK_EQ (init_once_done_ , false );
84+ DCHECK_EQ (llvm_init , false );
8185
8286 llvm::InitializeNativeTarget ();
8387 llvm::InitializeNativeTargetAsmPrinter ();
8488 llvm::InitializeNativeTargetAsmParser ();
8589 llvm::InitializeNativeTargetDisassembler ();
86-
8790 llvm::sys::DynamicLibrary::LoadLibraryPermanently (nullptr );
8891
89- init_once_done_ = true ;
92+ llvm_init = true ;
9093}
9194
92- // / factory method to construct the engine.
93- Status Engine::Make (std::shared_ptr<Configuration> config,
94- std::unique_ptr<Engine>* engine) {
95- static auto host_cpu_name = llvm::sys::getHostCPUName ();
96- std::unique_ptr<Engine> engine_obj (new Engine ());
97-
98- std::call_once (init_once_flag, [&engine_obj] { engine_obj->InitOnce (); });
99- engine_obj->context_ .reset (new llvm::LLVMContext ());
100- engine_obj->ir_builder_ .reset (new llvm::IRBuilder<>(*(engine_obj->context ())));
101- engine_obj->types_ .reset (new LLVMTypes (*(engine_obj->context ())));
102-
103- // Create the execution engine
104- std::unique_ptr<llvm::Module> cg_module (
105- new llvm::Module (" codegen" , *(engine_obj->context ())));
106- engine_obj->module_ = cg_module.get ();
107-
108- llvm::EngineBuilder engineBuilder (std::move (cg_module));
109- engineBuilder.setMCPU (host_cpu_name);
110- engineBuilder.setEngineKind (llvm::EngineKind::JIT );
111- engineBuilder.setOptLevel (llvm::CodeGenOpt::Aggressive);
112- engineBuilder.setErrorStr (&(engine_obj->llvm_error_ ));
113- engine_obj->execution_engine_ .reset (engineBuilder.create ());
114- if (engine_obj->execution_engine_ == NULL ) {
115- engine_obj->module_ = NULL ;
116- return Status::CodeGenError (engine_obj->llvm_error_ );
117- }
118-
95+ Engine::Engine (const std::shared_ptr<Configuration>& conf,
96+ std::unique_ptr<llvm::LLVMContext> ctx,
97+ std::unique_ptr<llvm::ExecutionEngine> engine, llvm::Module* module )
98+ : context_(std::move(ctx)),
99+ execution_engine_ (std::move(engine)),
100+ ir_builder_(arrow::internal::make_unique<llvm::IRBuilder<>>(*context_)),
101+ module_(module ),
102+ types_(*context_),
103+ optimize_(conf->optimize ()) {}
104+
105+ Status Engine::Init () {
119106 // Add mappings for functions that can be accessed from LLVM/IR module.
120- engine_obj-> AddGlobalMappings ();
107+ AddGlobalMappings ();
121108
122- auto status = engine_obj-> LoadPreCompiledIR ();
123- ARROW_RETURN_NOT_OK (status );
109+ ARROW_RETURN_NOT_OK ( LoadPreCompiledIR () );
110+ ARROW_RETURN_NOT_OK (DecimalIR::AddFunctions ( this ) );
124111
125- // Add decimal functions
126- status = DecimalIR::AddFunctions (engine_obj.get ());
127- ARROW_RETURN_NOT_OK (status);
112+ return Status::OK ();
113+ }
128114
129- *engine = std::move (engine_obj);
115+ // / factory method to construct the engine.
116+ Status Engine::Make (const std::shared_ptr<Configuration>& conf,
117+ std::unique_ptr<Engine>* out) {
118+ std::call_once (llvm_init_once_flag, InitOnce);
119+
120+ auto ctx = arrow::internal::make_unique<llvm::LLVMContext>();
121+ auto module = arrow::internal::make_unique<llvm::Module>(" codegen" , *ctx);
122+
123+ // Capture before moving, ExceutionEngine does not allow retrieving the
124+ // original Module.
125+ auto module_ptr = module .get ();
126+
127+ auto opt_level =
128+ conf->optimize () ? llvm::CodeGenOpt::Aggressive : llvm::CodeGenOpt::None;
129+ // Note that the lifetime of the error string is not captured by the
130+ // ExecutionEngine but only for the lifetime of the builder. Found by
131+ // inspecting LLVM sources.
132+ std::string builder_error;
133+ std::unique_ptr<llvm::ExecutionEngine> exec_engine{
134+ llvm::EngineBuilder (std::move (module ))
135+ .setMCPU (llvm::sys::getHostCPUName ())
136+ .setEngineKind (llvm::EngineKind::JIT )
137+ .setOptLevel (opt_level)
138+ .setErrorStr (&builder_error)
139+ .create ()};
140+
141+ if (exec_engine == nullptr ) {
142+ return Status::CodeGenError (" Could not instantiate llvm::ExecutionEngine: " ,
143+ builder_error);
144+ }
145+
146+ std::unique_ptr<Engine> engine{
147+ new Engine (conf, std::move (ctx), std::move (exec_engine), module_ptr)};
148+ ARROW_RETURN_NOT_OK (engine->Init ());
149+ *out = std::move (engine);
130150 return Status::OK ();
131151}
132152
@@ -191,15 +211,10 @@ Status Engine::RemoveUnusedFunctions() {
191211}
192212
193213// Optimise and compile the module.
194- Status Engine::FinalizeModule (bool optimise_ir, bool dump_ir, std::string* final_ir) {
195- auto status = RemoveUnusedFunctions ();
196- ARROW_RETURN_NOT_OK (status);
197-
198- if (dump_ir) {
199- DumpIR (" Before optimise" );
200- }
214+ Status Engine::FinalizeModule () {
215+ ARROW_RETURN_NOT_OK (RemoveUnusedFunctions ());
201216
202- if (optimise_ir ) {
217+ if (optimize_ ) {
203218 // misc passes to allow for inlining, vectorization, ..
204219 std::unique_ptr<llvm::legacy::PassManager> pass_manager (
205220 new llvm::legacy::PassManager ());
@@ -222,15 +237,8 @@ Status Engine::FinalizeModule(bool optimise_ir, bool dump_ir, std::string* final
222237 pass_builder.OptLevel = 3 ;
223238 pass_builder.populateModulePassManager (*pass_manager);
224239 pass_manager->run (*module_);
225-
226- if (dump_ir) {
227- DumpIR (" After optimise" );
228- }
229- }
230- if (final_ir != nullptr ) {
231- llvm::raw_string_ostream stream (*final_ir);
232- module_->print (stream, nullptr );
233240 }
241+
234242 ARROW_RETURN_IF (llvm::verifyModule (*module_, &llvm::errs ()),
235243 Status::CodeGenError (" Module verification failed after optimizer" ));
236244
@@ -249,20 +257,20 @@ void* Engine::CompiledFunction(llvm::Function* irFunction) {
249257void Engine::AddGlobalMappingForFunc (const std::string& name, llvm::Type* ret_type,
250258 const std::vector<llvm::Type*>& args,
251259 void * function_ptr) {
252- auto prototype = llvm::FunctionType::get (ret_type, args, false /* isVarArg*/ );
253- auto fn = llvm::Function::Create (prototype, llvm::GlobalValue::ExternalLinkage, name,
254- module ());
260+ constexpr bool is_var_arg = false ;
261+ auto prototype = llvm::FunctionType::get (ret_type, args, is_var_arg);
262+ constexpr auto linkage = llvm::GlobalValue::ExternalLinkage;
263+ auto fn = llvm::Function::Create (prototype, linkage, name, module ());
255264 execution_engine_->addGlobalMapping (fn, function_ptr);
256265}
257266
258267void Engine::AddGlobalMappings () { ExportedFuncsRegistry::AddMappings (this ); }
259268
260- void Engine::DumpIR (std::string prefix) {
261- std::string str;
262-
263- llvm::raw_string_ostream stream (str);
269+ std::string Engine::DumpIR () {
270+ std::string ir;
271+ llvm::raw_string_ostream stream (ir);
264272 module_->print (stream, nullptr );
265- std::cout << " ==== " << prefix << " === " << str << " \n " ;
273+ return ir ;
266274}
267275
268276} // namespace gandiva
0 commit comments