diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/arrow/cpp/src/gandiva/engine.cc | |
parent | Initial commit. (diff) | |
download | ceph-upstream/18.2.2.tar.xz ceph-upstream/18.2.2.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/arrow/cpp/src/gandiva/engine.cc')
-rw-r--r-- | src/arrow/cpp/src/gandiva/engine.cc | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/src/arrow/cpp/src/gandiva/engine.cc b/src/arrow/cpp/src/gandiva/engine.cc new file mode 100644 index 000000000..f0b768f5f --- /dev/null +++ b/src/arrow/cpp/src/gandiva/engine.cc @@ -0,0 +1,338 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// TODO(wesm): LLVM 7 produces pesky C4244 that disable pragmas around the LLVM +// includes seem to not fix as with LLVM 6 +#if defined(_MSC_VER) +#pragma warning(disable : 4244) +#endif + +#include "gandiva/engine.h" + +#include <iostream> +#include <memory> +#include <mutex> +#include <sstream> +#include <string> +#include <unordered_set> +#include <utility> + +#include "arrow/util/logging.h" + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4141) +#pragma warning(disable : 4146) +#pragma warning(disable : 4244) +#pragma warning(disable : 4267) +#pragma warning(disable : 4624) +#endif + +#include <llvm/Analysis/Passes.h> +#include <llvm/Analysis/TargetTransformInfo.h> +#include <llvm/Bitcode/BitcodeReader.h> +#include <llvm/ExecutionEngine/ExecutionEngine.h> +#include <llvm/ExecutionEngine/MCJIT.h> +#include <llvm/IR/DataLayout.h> +#include <llvm/IR/IRBuilder.h> +#include <llvm/IR/LLVMContext.h> +#include <llvm/IR/LegacyPassManager.h> +#include <llvm/IR/Verifier.h> +#include <llvm/Linker/Linker.h> +#include <llvm/MC/SubtargetFeature.h> +#include <llvm/Support/DynamicLibrary.h> +#include <llvm/Support/Host.h> +#include <llvm/Support/TargetRegistry.h> +#include <llvm/Support/TargetSelect.h> +#include <llvm/Support/raw_ostream.h> +#include <llvm/Transforms/IPO.h> +#include <llvm/Transforms/IPO/PassManagerBuilder.h> +#include <llvm/Transforms/InstCombine/InstCombine.h> +#include <llvm/Transforms/Scalar.h> +#include <llvm/Transforms/Scalar/GVN.h> +#include <llvm/Transforms/Utils.h> +#include <llvm/Transforms/Vectorize.h> + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#include "arrow/util/make_unique.h" +#include "gandiva/configuration.h" +#include "gandiva/decimal_ir.h" +#include "gandiva/exported_funcs_registry.h" + +namespace gandiva { + +extern const unsigned char kPrecompiledBitcode[]; +extern const size_t kPrecompiledBitcodeSize; + +std::once_flag llvm_init_once_flag; +static bool llvm_init = false; +static llvm::StringRef cpu_name; +static llvm::SmallVector<std::string, 10> cpu_attrs; + +void Engine::InitOnce() { + DCHECK_EQ(llvm_init, false); + + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); + llvm::InitializeNativeTargetDisassembler(); + llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); + + cpu_name = llvm::sys::getHostCPUName(); + llvm::StringMap<bool> host_features; + std::string cpu_attrs_str; + if (llvm::sys::getHostCPUFeatures(host_features)) { + for (auto& f : host_features) { + std::string attr = f.second ? std::string("+") + f.first().str() + : std::string("-") + f.first().str(); + cpu_attrs.push_back(attr); + cpu_attrs_str += " " + attr; + } + } + ARROW_LOG(INFO) << "Detected CPU Name : " << cpu_name.str(); + ARROW_LOG(INFO) << "Detected CPU Features:" << cpu_attrs_str; + llvm_init = true; +} + +Engine::Engine(const std::shared_ptr<Configuration>& conf, + std::unique_ptr<llvm::LLVMContext> ctx, + std::unique_ptr<llvm::ExecutionEngine> engine, llvm::Module* module) + : context_(std::move(ctx)), + execution_engine_(std::move(engine)), + ir_builder_(arrow::internal::make_unique<llvm::IRBuilder<>>(*context_)), + module_(module), + types_(*context_), + optimize_(conf->optimize()) {} + +Status Engine::Init() { + // Add mappings for functions that can be accessed from LLVM/IR module. + AddGlobalMappings(); + + ARROW_RETURN_NOT_OK(LoadPreCompiledIR()); + ARROW_RETURN_NOT_OK(DecimalIR::AddFunctions(this)); + + return Status::OK(); +} + +/// factory method to construct the engine. +Status Engine::Make(const std::shared_ptr<Configuration>& conf, + std::unique_ptr<Engine>* out) { + std::call_once(llvm_init_once_flag, InitOnce); + + auto ctx = arrow::internal::make_unique<llvm::LLVMContext>(); + auto module = arrow::internal::make_unique<llvm::Module>("codegen", *ctx); + + // Capture before moving, ExecutionEngine does not allow retrieving the + // original Module. + auto module_ptr = module.get(); + + auto opt_level = + conf->optimize() ? llvm::CodeGenOpt::Aggressive : llvm::CodeGenOpt::None; + + // Note that the lifetime of the error string is not captured by the + // ExecutionEngine but only for the lifetime of the builder. Found by + // inspecting LLVM sources. + std::string builder_error; + + llvm::EngineBuilder engine_builder(std::move(module)); + + engine_builder.setEngineKind(llvm::EngineKind::JIT) + .setOptLevel(opt_level) + .setErrorStr(&builder_error); + + if (conf->target_host_cpu()) { + engine_builder.setMCPU(cpu_name); + engine_builder.setMAttrs(cpu_attrs); + } + std::unique_ptr<llvm::ExecutionEngine> exec_engine{engine_builder.create()}; + + if (exec_engine == nullptr) { + return Status::CodeGenError("Could not instantiate llvm::ExecutionEngine: ", + builder_error); + } + + std::unique_ptr<Engine> engine{ + new Engine(conf, std::move(ctx), std::move(exec_engine), module_ptr)}; + ARROW_RETURN_NOT_OK(engine->Init()); + *out = std::move(engine); + return Status::OK(); +} + +// This method was modified from its original version for a part of MLIR +// Original source from +// https://github.com/llvm/llvm-project/blob/9f2ce5b915a505a5488a5cf91bb0a8efa9ddfff7/mlir/lib/ExecutionEngine/ExecutionEngine.cpp +// The original copyright notice follows. + +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +static void SetDataLayout(llvm::Module* module) { + auto target_triple = llvm::sys::getDefaultTargetTriple(); + std::string error_message; + auto target = llvm::TargetRegistry::lookupTarget(target_triple, error_message); + if (!target) { + return; + } + + std::string cpu(llvm::sys::getHostCPUName()); + llvm::SubtargetFeatures features; + llvm::StringMap<bool> host_features; + + if (llvm::sys::getHostCPUFeatures(host_features)) { + for (auto& f : host_features) { + features.AddFeature(f.first(), f.second); + } + } + + std::unique_ptr<llvm::TargetMachine> machine( + target->createTargetMachine(target_triple, cpu, features.getString(), {}, {})); + + module->setDataLayout(machine->createDataLayout()); +} +// end of the mofified method from MLIR + +// Handling for pre-compiled IR libraries. +Status Engine::LoadPreCompiledIR() { + auto bitcode = llvm::StringRef(reinterpret_cast<const char*>(kPrecompiledBitcode), + kPrecompiledBitcodeSize); + + /// Read from file into memory buffer. + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer_or_error = + llvm::MemoryBuffer::getMemBuffer(bitcode, "precompiled", false); + + ARROW_RETURN_IF(!buffer_or_error, + Status::CodeGenError("Could not load module from IR: ", + buffer_or_error.getError().message())); + + std::unique_ptr<llvm::MemoryBuffer> buffer = move(buffer_or_error.get()); + + /// Parse the IR module. + llvm::Expected<std::unique_ptr<llvm::Module>> module_or_error = + llvm::getOwningLazyBitcodeModule(move(buffer), *context()); + if (!module_or_error) { + // NOTE: llvm::handleAllErrors() fails linking with RTTI-disabled LLVM builds + // (ARROW-5148) + std::string str; + llvm::raw_string_ostream stream(str); + stream << module_or_error.takeError(); + return Status::CodeGenError(stream.str()); + } + std::unique_ptr<llvm::Module> ir_module = move(module_or_error.get()); + + // set dataLayout + SetDataLayout(ir_module.get()); + + ARROW_RETURN_IF(llvm::verifyModule(*ir_module, &llvm::errs()), + Status::CodeGenError("verify of IR Module failed")); + ARROW_RETURN_IF(llvm::Linker::linkModules(*module_, move(ir_module)), + Status::CodeGenError("failed to link IR Modules")); + + return Status::OK(); +} + +// Get rid of all functions that don't need to be compiled. +// This helps in reducing the overall compilation time. This pass is trivial, +// and is always done since the number of functions in gandiva is very high. +// (Adapted from Apache Impala) +// +// Done by marking all the unused functions as internal, and then, running +// a pass for dead code elimination. +Status Engine::RemoveUnusedFunctions() { + // Setup an optimiser pipeline + std::unique_ptr<llvm::legacy::PassManager> pass_manager( + new llvm::legacy::PassManager()); + + std::unordered_set<std::string> used_functions; + used_functions.insert(functions_to_compile_.begin(), functions_to_compile_.end()); + + pass_manager->add( + llvm::createInternalizePass([&used_functions](const llvm::GlobalValue& func) { + return (used_functions.find(func.getName().str()) != used_functions.end()); + })); + pass_manager->add(llvm::createGlobalDCEPass()); + pass_manager->run(*module_); + return Status::OK(); +} + +// Optimise and compile the module. +Status Engine::FinalizeModule() { + ARROW_RETURN_NOT_OK(RemoveUnusedFunctions()); + + if (optimize_) { + // misc passes to allow for inlining, vectorization, .. + std::unique_ptr<llvm::legacy::PassManager> pass_manager( + new llvm::legacy::PassManager()); + + llvm::TargetIRAnalysis target_analysis = + execution_engine_->getTargetMachine()->getTargetIRAnalysis(); + pass_manager->add(llvm::createTargetTransformInfoWrapperPass(target_analysis)); + pass_manager->add(llvm::createFunctionInliningPass()); + pass_manager->add(llvm::createInstructionCombiningPass()); + pass_manager->add(llvm::createPromoteMemoryToRegisterPass()); + pass_manager->add(llvm::createGVNPass()); + pass_manager->add(llvm::createNewGVNPass()); + pass_manager->add(llvm::createCFGSimplificationPass()); + pass_manager->add(llvm::createLoopVectorizePass()); + pass_manager->add(llvm::createSLPVectorizerPass()); + pass_manager->add(llvm::createGlobalOptimizerPass()); + + // run the optimiser + llvm::PassManagerBuilder pass_builder; + pass_builder.OptLevel = 3; + pass_builder.populateModulePassManager(*pass_manager); + pass_manager->run(*module_); + } + + ARROW_RETURN_IF(llvm::verifyModule(*module_, &llvm::errs()), + Status::CodeGenError("Module verification failed after optimizer")); + + // do the compilation + execution_engine_->finalizeObject(); + module_finalized_ = true; + + return Status::OK(); +} + +void* Engine::CompiledFunction(llvm::Function* irFunction) { + DCHECK(module_finalized_); + return execution_engine_->getPointerToFunction(irFunction); +} + +void Engine::AddGlobalMappingForFunc(const std::string& name, llvm::Type* ret_type, + const std::vector<llvm::Type*>& args, + void* function_ptr) { + constexpr bool is_var_arg = false; + auto prototype = llvm::FunctionType::get(ret_type, args, is_var_arg); + constexpr auto linkage = llvm::GlobalValue::ExternalLinkage; + auto fn = llvm::Function::Create(prototype, linkage, name, module()); + execution_engine_->addGlobalMapping(fn, function_ptr); +} + +void Engine::AddGlobalMappings() { ExportedFuncsRegistry::AddMappings(this); } + +std::string Engine::DumpIR() { + std::string ir; + llvm::raw_string_ostream stream(ir); + module_->print(stream, nullptr); + return ir; +} + +} // namespace gandiva |