2018-04-25 15:19:22 +00:00
|
|
|
#include <Interpreters/ExpressionJIT.h>
|
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
|
2018-04-25 11:55:54 +00:00
|
|
|
#include <Columns/ColumnConst.h>
|
2018-04-27 21:30:38 +00:00
|
|
|
#include <Columns/ColumnNullable.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>
|
2018-04-27 21:30:38 +00:00
|
|
|
#include <DataTypes/Native.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 <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/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/Target/TargetMachine.h>
|
2018-04-27 21:30:38 +00:00
|
|
|
#include <llvm/Support/TargetSelect.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
|
|
|
|
|
|
|
|
|
|
|
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 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-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;
|
|
|
|
llvm::PassManagerBuilder builder;
|
|
|
|
llvm::legacy::FunctionPassManager fpm(shared->module.get());
|
2018-04-28 11:12:21 +00:00
|
|
|
builder.OptLevel = 3;
|
|
|
|
builder.SLPVectorize = true;
|
|
|
|
builder.LoopVectorize = true;
|
|
|
|
builder.RerollLoops = true;
|
|
|
|
builder.VerifyInput = true;
|
|
|
|
builder.VerifyOutput = true;
|
2018-04-24 14:12:45 +00:00
|
|
|
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 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-27 22:03:52 +00:00
|
|
|
if (!toNativeType(shared->builder, function.getReturnType()))
|
2018-04-24 10:25:18 +00:00
|
|
|
return false;
|
|
|
|
for (const auto & type : function.getArgumentTypes())
|
2018-04-27 21:30:38 +00:00
|
|
|
if (!toNativeType(shared->builder, type))
|
2018-04-24 10:25:18 +00:00
|
|
|
return false;
|
2018-04-27 22:03:52 +00:00
|
|
|
return function.isCompilable();
|
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)
|
2018-04-27 21:30:38 +00:00
|
|
|
: parent(parent), context(context)
|
|
|
|
{
|
|
|
|
std::string mangledName;
|
|
|
|
llvm::raw_string_ostream mangledNameStream(mangledName);
|
|
|
|
llvm::Mangler::getNameWithPrefix(mangledNameStream, parent->getName(), context->layout);
|
|
|
|
function = reinterpret_cast<const void *>(context->compileLayer.findSymbol(mangledNameStream.str(), false).getAddress().get());
|
|
|
|
}
|
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-27 15:44:38 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
struct ColumnData
|
|
|
|
{
|
2018-04-27 21:30:38 +00:00
|
|
|
const char * data = nullptr;
|
|
|
|
const char * null = nullptr;
|
2018-04-27 15:44:38 +00:00
|
|
|
size_t stride;
|
|
|
|
};
|
2018-04-27 21:30:38 +00:00
|
|
|
|
|
|
|
struct ColumnDataPlaceholders
|
|
|
|
{
|
|
|
|
llvm::PHINode * data;
|
|
|
|
llvm::PHINode * null;
|
|
|
|
llvm::Value * data_init;
|
|
|
|
llvm::Value * null_init;
|
|
|
|
llvm::Value * stride;
|
|
|
|
llvm::Value * is_const;
|
|
|
|
};
|
2018-04-27 15:44:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ColumnData getColumnData(const IColumn * column)
|
|
|
|
{
|
2018-04-27 21:30:38 +00:00
|
|
|
ColumnData result;
|
|
|
|
const bool is_const = column->isColumnConst();
|
|
|
|
if (is_const)
|
|
|
|
column = &reinterpret_cast<const ColumnConst *>(column)->getDataColumn();
|
|
|
|
if (auto * nullable = typeid_cast<const ColumnNullable *>(column))
|
|
|
|
{
|
|
|
|
result.null = nullable->getNullMapColumn().getRawData().data;
|
|
|
|
column = &nullable->getNestedColumn();
|
|
|
|
}
|
|
|
|
result.data = column->getRawData().data;
|
|
|
|
result.stride = is_const ? 0 : column->sizeOfValueIfFixed();
|
|
|
|
return result;
|
2018-04-27 15:44:38 +00:00
|
|
|
}
|
|
|
|
|
2018-04-27 21:30:38 +00:00
|
|
|
void LLVMPreparedFunction::execute(Block & block, const ColumnNumbers & arguments, size_t result)
|
2018-04-24 19:42:06 +00:00
|
|
|
{
|
2018-04-25 15:16:48 +00:00
|
|
|
size_t block_size = block.rows();
|
2018-04-27 21:30:38 +00:00
|
|
|
auto col_res = 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
|
|
|
{
|
2018-04-27 15:44:38 +00:00
|
|
|
std::vector<ColumnData> columns(arguments.size() + 1);
|
2018-04-25 15:16:48 +00:00
|
|
|
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);
|
2018-04-27 15:44:38 +00:00
|
|
|
columns[i] = getColumnData(column);
|
2018-04-25 15:16:48 +00:00
|
|
|
}
|
2018-04-27 15:44:38 +00:00
|
|
|
columns[arguments.size()] = getColumnData(col_res.get());
|
|
|
|
reinterpret_cast<void (*) (size_t, ColumnData *)>(function)(block_size, columns.data());
|
2018-04-25 15:16:48 +00:00
|
|
|
}
|
2018-04-24 19:42:06 +00:00
|
|
|
block.getByPosition(result).column = std::move(col_res);
|
|
|
|
};
|
|
|
|
|
2018-04-26 11:09:10 +00:00
|
|
|
LLVMFunction::LLVMFunction(ExpressionActions::Actions actions_, LLVMContext context, const Block & sample_block)
|
2018-04-24 10:25:18 +00:00
|
|
|
: 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
|
|
|
{
|
2018-04-27 21:30:38 +00:00
|
|
|
auto & b = context->builder;
|
|
|
|
auto * size_type = b.getIntNTy(sizeof(size_t) * 8);
|
|
|
|
auto * data_type = llvm::StructType::get(b.getInt8PtrTy(), b.getInt8PtrTy(), size_type);
|
|
|
|
auto * func_type = llvm::FunctionType::get(b.getVoidTy(), { size_type, llvm::PointerType::get(data_type, 0) }, /*isVarArg=*/false);
|
|
|
|
auto * func = llvm::Function::Create(func_type, llvm::Function::ExternalLinkage, actions.back().result_name, context->module.get());
|
|
|
|
auto args = func->args().begin();
|
|
|
|
llvm::Value * counter = &*args++;
|
|
|
|
llvm::Value * columns = &*args++;
|
|
|
|
|
|
|
|
auto * entry = llvm::BasicBlock::Create(context->context, "entry", func);
|
|
|
|
b.SetInsertPoint(entry);
|
|
|
|
|
2018-04-26 11:09:10 +00:00
|
|
|
std::unordered_map<std::string, std::function<llvm::Value * ()>> by_name;
|
|
|
|
for (const auto & c : sample_block)
|
2018-04-28 14:41:13 +00:00
|
|
|
if (auto * value = getNativeValue(toNativeType(b, c.type), c.column.get(), 0))
|
2018-04-27 21:30:38 +00:00
|
|
|
by_name[c.name] = [=]() { return value; };
|
2018-04-26 11:09:10 +00:00
|
|
|
|
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++)
|
|
|
|
{
|
2018-04-27 21:30:38 +00:00
|
|
|
if (!seen.emplace(names[i]).second || by_name.find(names[i]) != by_name.end())
|
|
|
|
continue;
|
|
|
|
arg_names.push_back(names[i]);
|
|
|
|
arg_types.push_back(types[i]);
|
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 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
|
|
|
}
|
|
|
|
|
2018-04-27 21:30:38 +00:00
|
|
|
std::vector<ColumnDataPlaceholders> columns_v(arg_types.size() + 1);
|
|
|
|
for (size_t i = 0; i <= arg_types.size(); i++)
|
|
|
|
{
|
|
|
|
auto & column_type = (i == arg_types.size()) ? getReturnType() : arg_types[i];
|
|
|
|
auto * type = llvm::PointerType::get(toNativeType(b, removeNullable(column_type)), 0);
|
|
|
|
columns_v[i].data_init = b.CreatePointerCast(b.CreateLoad(b.CreateConstInBoundsGEP2_32(data_type, columns, i, 0)), type);
|
|
|
|
columns_v[i].stride = b.CreateLoad(b.CreateConstInBoundsGEP2_32(data_type, columns, i, 2));
|
|
|
|
if (column_type->isNullable())
|
|
|
|
{
|
|
|
|
columns_v[i].null_init = b.CreateLoad(b.CreateConstInBoundsGEP2_32(data_type, columns, i, 1));
|
|
|
|
columns_v[i].is_const = b.CreateICmpEQ(columns_v[i].stride, b.getIntN(sizeof(size_t) * 8, 0));
|
|
|
|
}
|
|
|
|
}
|
2018-04-24 13:21:42 +00:00
|
|
|
|
2018-04-27 21:30:38 +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-28 11:12:21 +00:00
|
|
|
by_name[arg_names[i]] = [&, &col = columns_v[i], i]() -> llvm::Value *
|
2018-04-27 21:30:38 +00:00
|
|
|
{
|
2018-04-28 11:12:21 +00:00
|
|
|
auto * value = b.CreateLoad(col.data);
|
2018-04-27 21:30:38 +00:00
|
|
|
if (!col.null)
|
2018-04-28 11:12:21 +00:00
|
|
|
return value;
|
2018-04-28 14:12:00 +00:00
|
|
|
auto * is_null = b.CreateICmpNE(b.CreateLoad(col.null), b.getInt8(0));
|
|
|
|
auto * nullable = getDefaultNativeValue(toNativeType(b, arg_types[i]));
|
2018-04-28 11:12:21 +00:00
|
|
|
return b.CreateInsertValue(b.CreateInsertValue(nullable, value, {0}), is_null, {1});
|
2018-04-27 21:30:38 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
for (const auto & action : actions)
|
2018-04-27 15:44:38 +00:00
|
|
|
{
|
2018-04-27 21:30:38 +00:00
|
|
|
ValuePlaceholders input;
|
|
|
|
for (const auto & name : action.argument_names)
|
|
|
|
input.push_back(by_name.at(name));
|
|
|
|
/// TODO: pass compile-time constant arguments to `compilePrologue`?
|
|
|
|
auto extra = action.function->compilePrologue(b);
|
|
|
|
for (auto * value : extra)
|
|
|
|
input.emplace_back([=]() { return value; });
|
2018-04-28 14:12:00 +00:00
|
|
|
by_name[action.result_name] = [&, input = std::move(input)]() {
|
|
|
|
auto * result = action.function->compile(b, input);
|
|
|
|
if (result->getType() != toNativeType(b, action.function->getReturnType()))
|
|
|
|
throw Exception("function " + action.function->getName() + " generated an llvm::Value of invalid type",
|
|
|
|
ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return result;
|
|
|
|
};
|
2018-04-24 13:21:42 +00:00
|
|
|
}
|
|
|
|
|
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-27 21:30:38 +00:00
|
|
|
b.CreateBr(loop);
|
|
|
|
b.SetInsertPoint(loop);
|
|
|
|
auto * counter_phi = b.CreatePHI(counter->getType(), 2);
|
2018-04-24 13:21:42 +00:00
|
|
|
counter_phi->addIncoming(counter, entry);
|
2018-04-27 15:44:38 +00:00
|
|
|
for (auto & col : columns_v)
|
|
|
|
{
|
2018-04-27 21:30:38 +00:00
|
|
|
col.data = b.CreatePHI(col.data_init->getType(), 2);
|
2018-04-27 15:44:38 +00:00
|
|
|
col.data->addIncoming(col.data_init, entry);
|
2018-04-27 21:30:38 +00:00
|
|
|
if (col.null_init)
|
|
|
|
{
|
|
|
|
col.null = b.CreatePHI(col.null_init->getType(), 2);
|
|
|
|
col.null->addIncoming(col.null_init, entry);
|
|
|
|
}
|
2018-04-27 15:44:38 +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
|
|
|
|
2018-04-28 14:12:00 +00:00
|
|
|
auto * result = by_name.at(getName())();
|
2018-04-27 21:30:38 +00:00
|
|
|
if (columns_v[arg_types.size()].null)
|
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-28 11:12:21 +00:00
|
|
|
b.CreateStore(b.CreateExtractValue(result, {0}), columns_v[arg_types.size()].data);
|
2018-04-28 14:12:00 +00:00
|
|
|
b.CreateStore(b.CreateSelect(b.CreateExtractValue(result, {1}), b.getInt8(1), b.getInt8(0)), columns_v[arg_types.size()].null);
|
2018-04-27 21:30:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
b.CreateStore(result, columns_v[arg_types.size()].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
|
|
|
}
|
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-27 21:30:38 +00:00
|
|
|
auto * cur_block = b.GetInsertBlock();
|
2018-04-27 15:44:38 +00:00
|
|
|
for (auto & col : columns_v)
|
|
|
|
{
|
2018-04-27 21:30:38 +00:00
|
|
|
auto * as_char = b.CreatePointerCast(col.data, b.getInt8PtrTy());
|
2018-04-28 11:12:21 +00:00
|
|
|
auto * as_type = b.CreatePointerCast(b.CreateInBoundsGEP(as_char, col.stride), col.data->getType());
|
2018-04-27 15:44:38 +00:00
|
|
|
col.data->addIncoming(as_type, cur_block);
|
2018-04-27 21:30:38 +00:00
|
|
|
if (col.null)
|
2018-04-28 11:12:21 +00:00
|
|
|
col.null->addIncoming(b.CreateSelect(col.is_const, col.null, b.CreateConstInBoundsGEP1_32(b.getInt8Ty(), col.null, 1)), cur_block);
|
2018-04-27 15:44:38 +00:00
|
|
|
}
|
2018-04-27 21:30:38 +00:00
|
|
|
counter_phi->addIncoming(b.CreateSub(counter_phi, llvm::ConstantInt::get(size_type, 1)), cur_block);
|
2018-04-24 13:21:42 +00:00
|
|
|
|
|
|
|
auto * end = llvm::BasicBlock::Create(context->context, "end", func);
|
2018-04-27 21:30:38 +00:00
|
|
|
b.CreateCondBr(b.CreateICmpNE(counter_phi, llvm::ConstantInt::get(size_type, 1)), loop, end);
|
|
|
|
b.SetInsertPoint(end);
|
|
|
|
b.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-28 14:41:13 +00:00
|
|
|
static void applyFunction(IFunctionBase & function, Field & value)
|
2018-04-25 11:55:54 +00:00
|
|
|
{
|
2018-04-28 14:41:13 +00:00
|
|
|
const auto & type = function.getArgumentTypes().at(0);
|
|
|
|
Block block = {{ type->createColumnConst(1, value), type, "x" }, { nullptr, function.getReturnType(), "y" }};
|
2018-04-25 11:55:54 +00:00
|
|
|
function.execute(block, {0}, 1);
|
2018-04-28 14:41:13 +00:00
|
|
|
block.safeGetByPosition(1).column->get(0, value);
|
2018-04-25 11:55:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2018-04-25 15:30:57 +00:00
|
|
|
/// monotonicity is only defined for unary functions, so the chain must describe a sequence of nested calls
|
2018-04-25 11:55:54 +00:00
|
|
|
for (size_t i = 0; i < actions.size(); i++)
|
|
|
|
{
|
2018-04-28 14:41:13 +00:00
|
|
|
Monotonicity m = actions[i].function->getMonotonicityForRange(*type_, left_, right_);
|
2018-04-25 11:55:54 +00:00
|
|
|
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())
|
2018-04-28 14:41:13 +00:00
|
|
|
applyFunction(*actions[i].function, left_);
|
2018-04-25 11:55:54 +00:00
|
|
|
if (right_ != Field())
|
2018-04-28 14:41:13 +00:00
|
|
|
applyFunction(*actions[i].function, right_);
|
2018-04-25 11:55:54 +00:00
|
|
|
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
|
|
|
|
{
|
2018-04-27 15:44:38 +00:00
|
|
|
struct LLVMTargetInitializer
|
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-27 15:44:38 +00:00
|
|
|
LLVMTargetInitializer()
|
|
|
|
{
|
|
|
|
llvm::InitializeNativeTarget();
|
|
|
|
llvm::InitializeNativeTargetAsmPrinter();
|
|
|
|
}
|
|
|
|
} llvmInitializer;
|
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 15:19:22 +00:00
|
|
|
#endif
|