2018-04-25 11:55:54 +00:00
|
|
|
#include <Columns/ColumnConst.h>
|
2018-04-24 19:42:06 +00:00
|
|
|
#include <Columns/ColumnVector.h>
|
|
|
|
#include <Common/typeid_cast.h>
|
|
|
|
#include <DataTypes/DataTypeNullable.h>
|
|
|
|
#include <DataTypes/DataTypesNumber.h>
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
#include <Interpreters/ExpressionJIT.h>
|
|
|
|
|
|
|
|
#include <llvm/IR/BasicBlock.h>
|
|
|
|
#include <llvm/IR/DataLayout.h>
|
|
|
|
#include <llvm/IR/DerivedTypes.h>
|
|
|
|
#include <llvm/IR/Function.h>
|
|
|
|
#include <llvm/IR/IRBuilder.h>
|
|
|
|
#include <llvm/IR/LLVMContext.h>
|
|
|
|
#include <llvm/IR/Mangler.h>
|
|
|
|
#include <llvm/IR/Module.h>
|
|
|
|
#include <llvm/IR/Type.h>
|
|
|
|
#include <llvm/IR/Verifier.h>
|
|
|
|
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
|
|
|
#include <llvm/ExecutionEngine/JITSymbol.h>
|
|
|
|
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
|
|
|
|
#include <llvm/ExecutionEngine/Orc/CompileUtils.h>
|
|
|
|
#include <llvm/ExecutionEngine/Orc/IRCompileLayer.h>
|
|
|
|
#include <llvm/ExecutionEngine/Orc/NullResolver.h>
|
|
|
|
#include <llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h>
|
|
|
|
#include <llvm/Support/TargetSelect.h>
|
|
|
|
#include <llvm/Support/raw_ostream.h>
|
|
|
|
#include <llvm/Target/TargetMachine.h>
|
2018-04-24 14:12:45 +00:00
|
|
|
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
Implement JIT compilation, without a loop for now.
It actually seems to work, so long as you only have one row that is. E.g.
> select something(cast(number + 6 as Float64), cast(number + 2 as Float64)) from system.numbers limit 1';
8
with this IR:
define void @"something(CAST(plus(number, 6), 'Float64'), CAST(plus(number, 2), 'Float64'))"(void**, i8*, double*) {
entry:
%3 = load void*, void** %0
%4 = bitcast void* %3 to double*
%5 = load double, double* %4
%6 = getelementptr void*, void** %0, i32 1
%7 = load void*, void** %6
%8 = bitcast void* %7 to double*
%9 = load double, double* %8
%10 = fadd double %5, %9
store double %10, double* %2
ret void
}
2018-04-23 23:52:54 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
}
|
|
|
|
|
2018-04-24 19:42:06 +00:00
|
|
|
template <typename T>
|
|
|
|
static bool typeIsA(const DataTypePtr & type)
|
|
|
|
{
|
2018-04-25 15:16:48 +00:00
|
|
|
return typeid_cast<const T *>(removeNullable(type).get());;
|
2018-04-24 19:42:06 +00:00
|
|
|
}
|
|
|
|
|
2018-04-24 10:25:18 +00:00
|
|
|
struct LLVMContext::Data
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
{
|
2018-04-23 22:50:29 +00:00
|
|
|
llvm::LLVMContext context;
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
std::shared_ptr<llvm::Module> module;
|
|
|
|
std::unique_ptr<llvm::TargetMachine> machine;
|
|
|
|
llvm::orc::RTDyldObjectLinkingLayer objectLayer;
|
|
|
|
llvm::orc::IRCompileLayer<decltype(objectLayer), llvm::orc::SimpleCompiler> compileLayer;
|
|
|
|
llvm::DataLayout layout;
|
|
|
|
llvm::IRBuilder<> builder;
|
|
|
|
|
2018-04-24 10:25:18 +00:00
|
|
|
Data()
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
: module(std::make_shared<llvm::Module>("jit", context))
|
|
|
|
, machine(llvm::EngineBuilder().selectTarget())
|
|
|
|
, objectLayer([]() { return std::make_shared<llvm::SectionMemoryManager>(); })
|
|
|
|
, compileLayer(objectLayer, llvm::orc::SimpleCompiler(*machine))
|
|
|
|
, layout(machine->createDataLayout())
|
|
|
|
, builder(context)
|
|
|
|
{
|
|
|
|
module->setDataLayout(layout);
|
|
|
|
module->setTargetTriple(machine->getTargetTriple().getTriple());
|
|
|
|
}
|
|
|
|
|
2018-04-23 22:50:29 +00:00
|
|
|
llvm::Type * toNativeType(const DataTypePtr & type)
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
{
|
2018-04-25 11:05:10 +00:00
|
|
|
/// LLVM doesn't have unsigned types, it has unsigned instructions.
|
2018-04-24 19:42:06 +00:00
|
|
|
if (typeIsA<DataTypeInt8>(type) || typeIsA<DataTypeUInt8>(type))
|
2018-04-23 22:50:29 +00:00
|
|
|
return builder.getInt8Ty();
|
2018-04-24 19:42:06 +00:00
|
|
|
if (typeIsA<DataTypeInt16>(type) || typeIsA<DataTypeUInt16>(type))
|
2018-04-23 22:50:29 +00:00
|
|
|
return builder.getInt16Ty();
|
2018-04-24 19:42:06 +00:00
|
|
|
if (typeIsA<DataTypeInt32>(type) || typeIsA<DataTypeUInt32>(type))
|
2018-04-23 22:50:29 +00:00
|
|
|
return builder.getInt32Ty();
|
2018-04-24 19:42:06 +00:00
|
|
|
if (typeIsA<DataTypeInt64>(type) || typeIsA<DataTypeUInt64>(type))
|
2018-04-23 22:50:29 +00:00
|
|
|
return builder.getInt64Ty();
|
2018-04-24 19:42:06 +00:00
|
|
|
if (typeIsA<DataTypeFloat32>(type))
|
2018-04-23 22:50:29 +00:00
|
|
|
return builder.getFloatTy();
|
2018-04-24 19:42:06 +00:00
|
|
|
if (typeIsA<DataTypeFloat64>(type))
|
2018-04-23 22:50:29 +00:00
|
|
|
return builder.getDoubleTy();
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-04-23 22:50:29 +00:00
|
|
|
LLVMCompiledFunction * lookup(const std::string& name)
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
{
|
|
|
|
std::string mangledName;
|
|
|
|
llvm::raw_string_ostream mangledNameStream(mangledName);
|
|
|
|
llvm::Mangler::getNameWithPrefix(mangledNameStream, name, layout);
|
2018-04-25 11:05:10 +00:00
|
|
|
/// why is `findSymbol` not const? we may never know.
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
return reinterpret_cast<LLVMCompiledFunction *>(compileLayer.findSymbol(mangledNameStream.str(), false).getAddress().get());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-24 10:25:18 +00:00
|
|
|
LLVMContext::LLVMContext()
|
|
|
|
: shared(std::make_shared<LLVMContext::Data>())
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
{}
|
|
|
|
|
2018-04-24 10:25:18 +00:00
|
|
|
void LLVMContext::finalize()
|
|
|
|
{
|
2018-04-24 14:12:45 +00:00
|
|
|
if (!shared->module->size())
|
|
|
|
return;
|
2018-04-24 13:21:42 +00:00
|
|
|
shared->module->print(llvm::errs(), nullptr, false, true);
|
2018-04-24 14:12:45 +00:00
|
|
|
llvm::PassManagerBuilder builder;
|
|
|
|
llvm::legacy::FunctionPassManager fpm(shared->module.get());
|
|
|
|
builder.OptLevel = 2;
|
|
|
|
builder.populateFunctionPassManager(fpm);
|
|
|
|
for (auto & function : *shared->module)
|
|
|
|
fpm.run(function);
|
|
|
|
llvm::cantFail(shared->compileLayer.addModule(shared->module, std::make_shared<llvm::orc::NullResolver>()));
|
2018-04-24 13:21:42 +00:00
|
|
|
shared->module->print(llvm::errs(), nullptr, false, true);
|
2018-04-24 10:25:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool LLVMContext::isCompilable(const IFunctionBase& function) const
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
{
|
2018-04-24 10:25:18 +00:00
|
|
|
if (!function.isCompilable() || !shared->toNativeType(function.getReturnType()))
|
|
|
|
return false;
|
|
|
|
for (const auto & type : function.getArgumentTypes())
|
|
|
|
if (!shared->toNativeType(type))
|
|
|
|
return false;
|
|
|
|
return true;
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
}
|
|
|
|
|
2018-04-24 10:25:18 +00:00
|
|
|
LLVMPreparedFunction::LLVMPreparedFunction(LLVMContext context, std::shared_ptr<const IFunctionBase> parent)
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
: parent(parent), context(context), function(context->lookup(parent->getName()))
|
|
|
|
{}
|
|
|
|
|
2018-04-24 19:42:06 +00:00
|
|
|
void LLVMPreparedFunction::executeImpl(Block & block, const ColumnNumbers & arguments, size_t result)
|
|
|
|
{
|
2018-04-25 11:05:10 +00:00
|
|
|
/// assuming that the function has default behavior on NULL, the column will be wrapped by `PreparedFunctionImpl::execute`.
|
2018-04-25 15:16:48 +00:00
|
|
|
size_t block_size = block.rows();
|
|
|
|
auto col_res = removeNullable(parent->getReturnType())->createColumn()->cloneResized(block_size);
|
2018-04-24 19:42:06 +00:00
|
|
|
if (block_size)
|
2018-04-25 15:16:48 +00:00
|
|
|
{
|
|
|
|
std::vector<const void *> columns(arguments.size());
|
|
|
|
std::vector<char> is_const(arguments.size());
|
|
|
|
for (size_t i = 0; i < arguments.size(); i++)
|
|
|
|
{
|
|
|
|
auto * column = block.getByPosition(arguments[i]).column.get();
|
|
|
|
if (!column)
|
|
|
|
throw Exception("column " + block.getByPosition(arguments[i]).name + " is missing", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
if (!column->isFixedAndContiguous())
|
|
|
|
throw Exception("column type " + column->getName() + " is not a contiguous array; its data type "
|
|
|
|
"should've had no native equivalent in LLVMContext::Data::toNativeType", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
columns[i] = column->getRawData().data;
|
|
|
|
is_const[i] = column->isColumnConst();
|
|
|
|
}
|
|
|
|
function(columns.data(), is_const.data(), const_cast<char *>(col_res->getRawData().data), block_size);
|
|
|
|
}
|
2018-04-24 19:42:06 +00:00
|
|
|
block.getByPosition(result).column = std::move(col_res);
|
|
|
|
};
|
|
|
|
|
2018-04-24 10:25:18 +00:00
|
|
|
LLVMFunction::LLVMFunction(ExpressionActions::Actions actions_, LLVMContext context)
|
|
|
|
: actions(std::move(actions_)), context(context)
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
{
|
|
|
|
std::unordered_set<std::string> seen;
|
|
|
|
for (const auto & action : actions)
|
|
|
|
{
|
|
|
|
const auto & names = action.argument_names;
|
|
|
|
const auto & types = action.function->getArgumentTypes();
|
|
|
|
for (size_t i = 0; i < names.size(); i++)
|
|
|
|
{
|
|
|
|
if (seen.emplace(names[i]).second)
|
|
|
|
{
|
|
|
|
arg_names.push_back(names[i]);
|
|
|
|
arg_types.push_back(types[i]);
|
|
|
|
}
|
|
|
|
}
|
2018-04-25 13:44:24 +00:00
|
|
|
seen.insert(action.result_name);
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
}
|
|
|
|
|
Implement JIT compilation, without a loop for now.
It actually seems to work, so long as you only have one row that is. E.g.
> select something(cast(number + 6 as Float64), cast(number + 2 as Float64)) from system.numbers limit 1';
8
with this IR:
define void @"something(CAST(plus(number, 6), 'Float64'), CAST(plus(number, 2), 'Float64'))"(void**, i8*, double*) {
entry:
%3 = load void*, void** %0
%4 = bitcast void* %3 to double*
%5 = load double, double* %4
%6 = getelementptr void*, void** %0, i32 1
%7 = load void*, void** %6
%8 = bitcast void* %7 to double*
%9 = load double, double* %8
%10 = fadd double %5, %9
store double %10, double* %2
ret void
}
2018-04-23 23:52:54 +00:00
|
|
|
llvm::FunctionType * func_type = llvm::FunctionType::get(context->builder.getVoidTy(), {
|
|
|
|
llvm::PointerType::getUnqual(llvm::PointerType::getUnqual(context->builder.getVoidTy())),
|
|
|
|
llvm::PointerType::getUnqual(context->builder.getInt8Ty()),
|
2018-04-24 10:25:18 +00:00
|
|
|
llvm::PointerType::getUnqual(context->toNativeType(actions.back().function->getReturnType())),
|
2018-04-24 13:21:42 +00:00
|
|
|
context->builder.getIntNTy(sizeof(size_t) * 8),
|
Implement JIT compilation, without a loop for now.
It actually seems to work, so long as you only have one row that is. E.g.
> select something(cast(number + 6 as Float64), cast(number + 2 as Float64)) from system.numbers limit 1';
8
with this IR:
define void @"something(CAST(plus(number, 6), 'Float64'), CAST(plus(number, 2), 'Float64'))"(void**, i8*, double*) {
entry:
%3 = load void*, void** %0
%4 = bitcast void* %3 to double*
%5 = load double, double* %4
%6 = getelementptr void*, void** %0, i32 1
%7 = load void*, void** %6
%8 = bitcast void* %7 to double*
%9 = load double, double* %8
%10 = fadd double %5, %9
store double %10, double* %2
ret void
}
2018-04-23 23:52:54 +00:00
|
|
|
}, /*isVarArg=*/false);
|
2018-04-24 13:21:42 +00:00
|
|
|
auto * func = llvm::Function::Create(func_type, llvm::Function::ExternalLinkage, actions.back().result_name, context->module.get());
|
Implement JIT compilation, without a loop for now.
It actually seems to work, so long as you only have one row that is. E.g.
> select something(cast(number + 6 as Float64), cast(number + 2 as Float64)) from system.numbers limit 1';
8
with this IR:
define void @"something(CAST(plus(number, 6), 'Float64'), CAST(plus(number, 2), 'Float64'))"(void**, i8*, double*) {
entry:
%3 = load void*, void** %0
%4 = bitcast void* %3 to double*
%5 = load double, double* %4
%6 = getelementptr void*, void** %0, i32 1
%7 = load void*, void** %6
%8 = bitcast void* %7 to double*
%9 = load double, double* %8
%10 = fadd double %5, %9
store double %10, double* %2
ret void
}
2018-04-23 23:52:54 +00:00
|
|
|
auto args = func->args().begin();
|
2018-04-25 11:05:10 +00:00
|
|
|
llvm::Value * inputs = &*args++; /// void** - tuple of columns, each a contiguous data block
|
|
|
|
llvm::Value * consts = &*args++; /// char* - for each column, 0 if it is full, 1 if it points to a single constant value
|
|
|
|
llvm::Value * output = &*args++; /// void* - space for the result
|
|
|
|
llvm::Value * counter = &*args++; /// size_t - number of entries to read from non-const values and write to output
|
2018-04-24 13:21:42 +00:00
|
|
|
|
|
|
|
auto * entry = llvm::BasicBlock::Create(context->context, "entry", func);
|
|
|
|
context->builder.SetInsertPoint(entry);
|
|
|
|
|
|
|
|
std::vector<llvm::Value *> inputs_v(arg_types.size());
|
|
|
|
std::vector<llvm::Value *> deltas_v(arg_types.size());
|
2018-04-24 10:25:18 +00:00
|
|
|
for (size_t i = 0; i < arg_types.size(); i++)
|
Implement JIT compilation, without a loop for now.
It actually seems to work, so long as you only have one row that is. E.g.
> select something(cast(number + 6 as Float64), cast(number + 2 as Float64)) from system.numbers limit 1';
8
with this IR:
define void @"something(CAST(plus(number, 6), 'Float64'), CAST(plus(number, 2), 'Float64'))"(void**, i8*, double*) {
entry:
%3 = load void*, void** %0
%4 = bitcast void* %3 to double*
%5 = load double, double* %4
%6 = getelementptr void*, void** %0, i32 1
%7 = load void*, void** %6
%8 = bitcast void* %7 to double*
%9 = load double, double* %8
%10 = fadd double %5, %9
store double %10, double* %2
ret void
}
2018-04-23 23:52:54 +00:00
|
|
|
{
|
2018-04-24 13:21:42 +00:00
|
|
|
if (i != 0)
|
|
|
|
{
|
|
|
|
inputs = context->builder.CreateConstGEP1_32(inputs, 1);
|
|
|
|
consts = context->builder.CreateConstGEP1_32(consts, 1);
|
|
|
|
}
|
|
|
|
auto * type = llvm::PointerType::getUnqual(context->toNativeType(arg_types[i]));
|
|
|
|
auto * step = context->builder.CreateICmpEQ(context->builder.CreateLoad(consts), llvm::ConstantInt::get(context->builder.getInt8Ty(), 0));
|
|
|
|
inputs_v[i] = context->builder.CreatePointerCast(context->builder.CreateLoad(inputs), type);
|
|
|
|
deltas_v[i] = context->builder.CreateZExt(step, context->builder.getInt32Ty());
|
|
|
|
}
|
|
|
|
|
2018-04-25 11:05:10 +00:00
|
|
|
/// assume nonzero initial value in `counter`
|
2018-04-24 13:21:42 +00:00
|
|
|
auto * loop = llvm::BasicBlock::Create(context->context, "loop", func);
|
2018-04-25 11:05:10 +00:00
|
|
|
context->builder.CreateBr(loop);
|
2018-04-24 13:21:42 +00:00
|
|
|
context->builder.SetInsertPoint(loop);
|
|
|
|
|
2018-04-24 18:10:22 +00:00
|
|
|
std::unordered_map<std::string, std::function<llvm::Value * ()>> by_name;
|
2018-04-24 13:21:42 +00:00
|
|
|
std::vector<llvm::PHINode *> phi(inputs_v.size());
|
|
|
|
for (size_t i = 0; i < inputs_v.size(); i++)
|
|
|
|
{
|
|
|
|
phi[i] = context->builder.CreatePHI(inputs_v[i]->getType(), 2);
|
|
|
|
phi[i]->addIncoming(inputs_v[i], entry);
|
Implement JIT compilation, without a loop for now.
It actually seems to work, so long as you only have one row that is. E.g.
> select something(cast(number + 6 as Float64), cast(number + 2 as Float64)) from system.numbers limit 1';
8
with this IR:
define void @"something(CAST(plus(number, 6), 'Float64'), CAST(plus(number, 2), 'Float64'))"(void**, i8*, double*) {
entry:
%3 = load void*, void** %0
%4 = bitcast void* %3 to double*
%5 = load double, double* %4
%6 = getelementptr void*, void** %0, i32 1
%7 = load void*, void** %6
%8 = bitcast void* %7 to double*
%9 = load double, double* %8
%10 = fadd double %5, %9
store double %10, double* %2
ret void
}
2018-04-23 23:52:54 +00:00
|
|
|
}
|
2018-04-24 13:21:42 +00:00
|
|
|
auto * output_phi = context->builder.CreatePHI(output->getType(), 2);
|
|
|
|
auto * counter_phi = context->builder.CreatePHI(counter->getType(), 2);
|
|
|
|
output_phi->addIncoming(output, entry);
|
|
|
|
counter_phi->addIncoming(counter, entry);
|
Implement JIT compilation, without a loop for now.
It actually seems to work, so long as you only have one row that is. E.g.
> select something(cast(number + 6 as Float64), cast(number + 2 as Float64)) from system.numbers limit 1';
8
with this IR:
define void @"something(CAST(plus(number, 6), 'Float64'), CAST(plus(number, 2), 'Float64'))"(void**, i8*, double*) {
entry:
%3 = load void*, void** %0
%4 = bitcast void* %3 to double*
%5 = load double, double* %4
%6 = getelementptr void*, void** %0, i32 1
%7 = load void*, void** %6
%8 = bitcast void* %7 to double*
%9 = load double, double* %8
%10 = fadd double %5, %9
store double %10, double* %2
ret void
}
2018-04-23 23:52:54 +00:00
|
|
|
|
2018-04-24 13:21:42 +00:00
|
|
|
for (size_t i = 0; i < phi.size(); i++)
|
2018-04-24 18:10:22 +00:00
|
|
|
if (!by_name.emplace(arg_names[i], [&, i]() { return context->builder.CreateLoad(phi[i]); }).second)
|
2018-04-24 13:21:42 +00:00
|
|
|
throw Exception("duplicate input column name", ErrorCodes::LOGICAL_ERROR);
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
for (const auto & action : actions)
|
|
|
|
{
|
2018-04-24 13:21:42 +00:00
|
|
|
ValuePlaceholders action_input;
|
|
|
|
action_input.reserve(action.argument_names.size());
|
Implement JIT compilation, without a loop for now.
It actually seems to work, so long as you only have one row that is. E.g.
> select something(cast(number + 6 as Float64), cast(number + 2 as Float64)) from system.numbers limit 1';
8
with this IR:
define void @"something(CAST(plus(number, 6), 'Float64'), CAST(plus(number, 2), 'Float64'))"(void**, i8*, double*) {
entry:
%3 = load void*, void** %0
%4 = bitcast void* %3 to double*
%5 = load double, double* %4
%6 = getelementptr void*, void** %0, i32 1
%7 = load void*, void** %6
%8 = bitcast void* %7 to double*
%9 = load double, double* %8
%10 = fadd double %5, %9
store double %10, double* %2
ret void
}
2018-04-23 23:52:54 +00:00
|
|
|
for (const auto & name : action.argument_names)
|
2018-04-24 13:21:42 +00:00
|
|
|
action_input.push_back(by_name.at(name));
|
2018-04-24 18:10:22 +00:00
|
|
|
auto generator = [&action, &context, action_input{std::move(action_input)}]()
|
|
|
|
{
|
|
|
|
return action.function->compile(context->builder, action_input);
|
|
|
|
};
|
|
|
|
if (!by_name.emplace(action.result_name, std::move(generator)).second)
|
Implement JIT compilation, without a loop for now.
It actually seems to work, so long as you only have one row that is. E.g.
> select something(cast(number + 6 as Float64), cast(number + 2 as Float64)) from system.numbers limit 1';
8
with this IR:
define void @"something(CAST(plus(number, 6), 'Float64'), CAST(plus(number, 2), 'Float64'))"(void**, i8*, double*) {
entry:
%3 = load void*, void** %0
%4 = bitcast void* %3 to double*
%5 = load double, double* %4
%6 = getelementptr void*, void** %0, i32 1
%7 = load void*, void** %6
%8 = bitcast void* %7 to double*
%9 = load double, double* %8
%10 = fadd double %5, %9
store double %10, double* %2
ret void
}
2018-04-23 23:52:54 +00:00
|
|
|
throw Exception("duplicate action result name", ErrorCodes::LOGICAL_ERROR);
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
}
|
2018-04-24 18:10:22 +00:00
|
|
|
context->builder.CreateStore(by_name.at(actions.back().result_name)(), output_phi);
|
Implement JIT compilation, without a loop for now.
It actually seems to work, so long as you only have one row that is. E.g.
> select something(cast(number + 6 as Float64), cast(number + 2 as Float64)) from system.numbers limit 1';
8
with this IR:
define void @"something(CAST(plus(number, 6), 'Float64'), CAST(plus(number, 2), 'Float64'))"(void**, i8*, double*) {
entry:
%3 = load void*, void** %0
%4 = bitcast void* %3 to double*
%5 = load double, double* %4
%6 = getelementptr void*, void** %0, i32 1
%7 = load void*, void** %6
%8 = bitcast void* %7 to double*
%9 = load double, double* %8
%10 = fadd double %5, %9
store double %10, double* %2
ret void
}
2018-04-23 23:52:54 +00:00
|
|
|
|
2018-04-24 18:10:22 +00:00
|
|
|
auto * cur_block = context->builder.GetInsertBlock();
|
2018-04-24 13:21:42 +00:00
|
|
|
for (size_t i = 0; i < phi.size(); i++)
|
2018-04-24 18:10:22 +00:00
|
|
|
phi[i]->addIncoming(context->builder.CreateGEP(phi[i], deltas_v[i]), cur_block);
|
|
|
|
output_phi->addIncoming(context->builder.CreateConstGEP1_32(output_phi, 1), cur_block);
|
|
|
|
counter_phi->addIncoming(context->builder.CreateSub(counter_phi, llvm::ConstantInt::get(counter_phi->getType(), 1)), cur_block);
|
2018-04-24 13:21:42 +00:00
|
|
|
|
|
|
|
auto * end = llvm::BasicBlock::Create(context->context, "end", func);
|
|
|
|
context->builder.CreateCondBr(context->builder.CreateICmpNE(counter_phi, llvm::ConstantInt::get(counter_phi->getType(), 1)), loop, end);
|
|
|
|
context->builder.SetInsertPoint(end);
|
|
|
|
context->builder.CreateRetVoid();
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
}
|
|
|
|
|
2018-04-25 11:55:54 +00:00
|
|
|
static Field evaluateFunction(IFunctionBase & function, const IDataType & type, const Field & arg)
|
|
|
|
{
|
|
|
|
const auto & arg_types = function.getArgumentTypes();
|
|
|
|
if (arg_types.size() != 1 || !arg_types[0]->equals(type))
|
|
|
|
return {};
|
|
|
|
auto column = arg_types[0]->createColumn();
|
|
|
|
column->insert(arg);
|
|
|
|
Block block = {{ ColumnConst::create(std::move(column), 1), arg_types[0], "_arg" }, { nullptr, function.getReturnType(), "_result" }};
|
|
|
|
function.execute(block, {0}, 1);
|
|
|
|
auto result = block.getByPosition(1).column;
|
|
|
|
return result && result->size() == 1 ? (*result)[0] : Field();
|
|
|
|
}
|
|
|
|
|
|
|
|
IFunctionBase::Monotonicity LLVMFunction::getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const
|
|
|
|
{
|
|
|
|
const IDataType * type_ = &type;
|
|
|
|
Field left_ = left;
|
|
|
|
Field right_ = right;
|
|
|
|
Monotonicity result(true, true, true);
|
|
|
|
/// monotonicity is only defined for unary functions, to the chain must describe a sequence of nested calls
|
|
|
|
for (size_t i = 0; i < actions.size(); i++)
|
|
|
|
{
|
|
|
|
Monotonicity m = actions[i].function->getMonotonicityForRange(type, left_, right_);
|
|
|
|
if (!m.is_monotonic)
|
|
|
|
return m;
|
|
|
|
result.is_positive ^= !m.is_positive;
|
|
|
|
result.is_always_monotonic &= m.is_always_monotonic;
|
|
|
|
if (i + 1 < actions.size())
|
|
|
|
{
|
|
|
|
if (left_ != Field())
|
|
|
|
left_ = evaluateFunction(*actions[i].function, *type_, left_);
|
|
|
|
if (right_ != Field())
|
|
|
|
right_ = evaluateFunction(*actions[i].function, *type_, right_);
|
|
|
|
if (!m.is_positive)
|
|
|
|
std::swap(left_, right_);
|
|
|
|
type_ = actions[i].function->getReturnType().get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
Add a JIT interface for row-wise default-nullable functions.
Not actually implemented, though. It does print out some jit-compiled stuff,
but that's about it. For example, this query:
select number from system.numbers where something(cast(number as Float64)) == 4
results in this on server's stderr:
define double @"something(CAST(number, 'Float64'))"(void**, i8*, void*) {
"something(CAST(number, 'Float64'))":
ret double 1.234500e+04
}
(and an exception, because that's what the non-jitted method does.)
As one may notice, this function neither reads the input (first argument;
tuple of arrays) nor writes the output (third argument; array), instead
returning some general nonsense.
In addition, `#if USE_EMBEDDED_COMPILER` doesn't work for some reason,
including LLVM headers requires -Wno-unused-parameter, this probably only
works on LLVM 5.0 due to rampant API instability, and I'm definitely
no expert on CMake. In short, there's still a long way to go.
2018-04-23 22:29:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
struct LLVMTargetInitializer
|
|
|
|
{
|
|
|
|
LLVMTargetInitializer()
|
|
|
|
{
|
|
|
|
llvm::InitializeNativeTarget();
|
|
|
|
llvm::InitializeNativeTargetAsmPrinter();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static LLVMTargetInitializer llvmInitializer;
|