mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-21 09:10:48 +00:00
Merge branch 'llvm-jit' of github.com:pyos/ClickHouse into pyos-llvm-jit
This commit is contained in:
commit
3453bf577a
@ -62,19 +62,54 @@ static inline llvm::Type * toNativeType(llvm::IRBuilderBase & builder, const Dat
|
||||
return toNativeType(builder, *type);
|
||||
}
|
||||
|
||||
static inline llvm::Value * castNativeNumber(llvm::IRBuilder<> & builder, llvm::Value * value, llvm::Type * type, bool is_signed)
|
||||
static inline llvm::Value * nativeBoolCast(llvm::IRBuilder<> & b, const DataTypePtr & from, llvm::Value * value)
|
||||
{
|
||||
if (value->getType() == type)
|
||||
return value;
|
||||
if (value->getType()->isIntegerTy())
|
||||
if (from->isNullable())
|
||||
{
|
||||
if (type->isIntegerTy())
|
||||
return builder.CreateIntCast(value, type, is_signed);
|
||||
return is_signed ? builder.CreateSIToFP(value, type) : builder.CreateUIToFP(value, type);
|
||||
auto * inner = nativeBoolCast(b, removeNullable(from), b.CreateExtractValue(value, {0}));
|
||||
return b.CreateAnd(b.CreateNot(b.CreateExtractValue(value, {1})), inner);
|
||||
}
|
||||
if (type->isFloatingPointTy())
|
||||
return builder.CreateFPCast(value, type);
|
||||
return is_signed ? builder.CreateFPToSI(value, type) : builder.CreateFPToUI(value, type);
|
||||
auto * zero = llvm::Constant::getNullValue(value->getType());
|
||||
if (value->getType()->isIntegerTy())
|
||||
return b.CreateICmpNE(value, zero);
|
||||
if (value->getType()->isFloatingPointTy())
|
||||
return b.CreateFCmpONE(value, zero); /// QNaN is false
|
||||
throw Exception("Cannot cast non-number " + from->getName() + " to bool", ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
static inline llvm::Value * nativeCast(llvm::IRBuilder<> & b, const DataTypePtr & from, llvm::Value * value, const DataTypePtr & to)
|
||||
{
|
||||
auto * n_from = value->getType();
|
||||
auto * n_to = toNativeType(b, to);
|
||||
if (n_from == n_to)
|
||||
return value;
|
||||
if (from->isNullable() && to->isNullable())
|
||||
{
|
||||
auto * inner = nativeCast(b, removeNullable(from), b.CreateExtractValue(value, {0}), to);
|
||||
return b.CreateInsertValue(inner, b.CreateExtractValue(value, {1}), {1});
|
||||
}
|
||||
if (from->isNullable())
|
||||
return nativeCast(b, removeNullable(from), b.CreateExtractValue(value, {0}), to);
|
||||
if (to->isNullable())
|
||||
{
|
||||
auto * inner = nativeCast(b, from, value, removeNullable(to));
|
||||
return b.CreateInsertValue(llvm::Constant::getNullValue(n_to), inner, {0});
|
||||
}
|
||||
|
||||
bool is_signed = typeIsEither<
|
||||
DataTypeInt8, DataTypeInt16, DataTypeInt32, DataTypeInt64,
|
||||
DataTypeFloat32, DataTypeFloat64,
|
||||
DataTypeDate, DataTypeDateTime, DataTypeInterval
|
||||
>(*from);
|
||||
if (n_from->isIntegerTy() && n_to->isFloatingPointTy())
|
||||
return is_signed ? b.CreateSIToFP(value, n_to) : b.CreateUIToFP(value, n_to);
|
||||
if (n_from->isFloatingPointTy() && n_to->isIntegerTy())
|
||||
return is_signed ? b.CreateFPToSI(value, n_to) : b.CreateFPToUI(value, n_to);
|
||||
if (n_from->isIntegerTy() && n_to->isIntegerTy())
|
||||
return b.CreateIntCast(value, n_to, is_signed);
|
||||
if (n_from->isFloatingPointTy() && n_to->isFloatingPointTy())
|
||||
return b.CreateFPCast(value, n_to);
|
||||
throw Exception("Cannot cast " + from->getName() + " to " + to->getName(), ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -112,6 +112,4 @@ endif ()
|
||||
|
||||
if (USE_EMBEDDED_COMPILER)
|
||||
target_include_directories (clickhouse_functions BEFORE PUBLIC ${LLVM_INCLUDE_DIRS})
|
||||
# LLVM has a bunch of unused parameters in its header files.
|
||||
target_compile_options (clickhouse_functions PRIVATE "-Wno-unused-parameter")
|
||||
endif ()
|
||||
|
@ -661,7 +661,14 @@ struct IntExp2Impl
|
||||
}
|
||||
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
static constexpr bool compilable = false; /// library function
|
||||
static constexpr bool compilable = true;
|
||||
|
||||
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * arg, bool)
|
||||
{
|
||||
if (!arg->getType()->isIntegerTy())
|
||||
throw Exception("IntExp2Impl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
||||
return b.CreateShl(llvm::ConstantInt::get(arg->getType(), 1), arg);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -975,9 +982,9 @@ public:
|
||||
if constexpr (!std::is_same_v<ResultDataType, InvalidType> && OpSpec::compilable)
|
||||
{
|
||||
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
||||
auto * type = toNativeType(b, ResultDataType{});
|
||||
auto * lval = castNativeNumber(b, values[0](), type, std::is_signed_v<typename LeftDataType::FieldType>);
|
||||
auto * rval = castNativeNumber(b, values[1](), type, std::is_signed_v<typename RightDataType::FieldType>);
|
||||
auto type = std::make_shared<ResultDataType>();
|
||||
auto * lval = nativeCast(b, types[0], values[0](), type);
|
||||
auto * rval = nativeCast(b, types[1], values[1](), type);
|
||||
result = OpSpec::compile(b, lval, rval, std::is_signed_v<typename ResultDataType::FieldType>);
|
||||
return true;
|
||||
}
|
||||
@ -1081,7 +1088,7 @@ public:
|
||||
if constexpr (Op<T1>::compilable)
|
||||
{
|
||||
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
||||
auto * v = castNativeNumber(b, values[0](), toNativeType(b, DataTypeNumber<T1>{}), std::is_signed_v<T0>);
|
||||
auto * v = nativeCast(b, types[0], values[0](), std::make_shared<DataTypeNumber<T1>>());
|
||||
result = Op<T0>::compile(b, v, std::is_signed_v<T1>);
|
||||
return true;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <DataTypes/DataTypeFixedString.h>
|
||||
#include <DataTypes/DataTypeTuple.h>
|
||||
#include <DataTypes/DataTypeNullable.h>
|
||||
#include <DataTypes/Native.h>
|
||||
#include <Columns/ColumnVector.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Columns/ColumnConst.h>
|
||||
@ -113,7 +114,64 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class FunctionIf : public IFunction
|
||||
template <bool null_is_false>
|
||||
class FunctionIfBase : public IFunction
|
||||
{
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
public:
|
||||
bool isCompilableImpl(const DataTypes & types) const override
|
||||
{
|
||||
for (const auto & type : types)
|
||||
if (!removeNullable(type)->isValueRepresentedByNumber())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
|
||||
{
|
||||
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
||||
auto type = getReturnTypeImpl(types);
|
||||
llvm::Value * null = nullptr;
|
||||
if (!null_is_false && type->isNullable())
|
||||
null = b.CreateInsertValue(llvm::Constant::getNullValue(toNativeType(b, type)), b.getTrue(), {1});
|
||||
auto * head = b.GetInsertBlock();
|
||||
auto * join = llvm::BasicBlock::Create(head->getContext(), "", head->getParent());
|
||||
std::vector<std::pair<llvm::BasicBlock *, llvm::Value *>> returns;
|
||||
for (size_t i = 0; i + 1 < types.size(); i += 2)
|
||||
{
|
||||
auto * then = llvm::BasicBlock::Create(head->getContext(), "", head->getParent());
|
||||
auto * next = llvm::BasicBlock::Create(head->getContext(), "", head->getParent());
|
||||
auto * cond = values[i]();
|
||||
if (!null_is_false && types[i]->isNullable())
|
||||
{
|
||||
returns.emplace_back(head, null);
|
||||
auto * nonnull = llvm::BasicBlock::Create(head->getContext(), "", head->getParent());
|
||||
b.CreateCondBr(b.CreateExtractValue(cond, {1}), join, nonnull);
|
||||
b.SetInsertPoint(nonnull);
|
||||
b.CreateCondBr(nativeBoolCast(b, removeNullable(types[i]), b.CreateExtractValue(cond, {0})), then, next);
|
||||
}
|
||||
else
|
||||
{
|
||||
b.CreateCondBr(nativeBoolCast(b, types[i], cond), then, next);
|
||||
}
|
||||
b.SetInsertPoint(then);
|
||||
returns.emplace_back(then, nativeCast(b, types[i + 1], values[i + 1](), type));
|
||||
b.CreateBr(join);
|
||||
b.SetInsertPoint(next);
|
||||
head = next;
|
||||
}
|
||||
returns.emplace_back(head, nativeCast(b, types.back(), values.back()(), type));
|
||||
b.CreateBr(join);
|
||||
b.SetInsertPoint(join);
|
||||
auto * phi = b.CreatePHI(toNativeType(b, type), returns.size());
|
||||
for (const auto & r : returns)
|
||||
phi->addIncoming(r.second, r.first);
|
||||
return phi;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class FunctionIf : public FunctionIfBase</*null_is_false=*/false>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = "if";
|
||||
@ -916,8 +974,8 @@ public:
|
||||
/// - arrays of such types.
|
||||
///
|
||||
/// Additionally the arguments, conditions or branches, support nullable types
|
||||
/// and the NULL value.
|
||||
class FunctionMultiIf final : public IFunction
|
||||
/// and the NULL value, with a NULL condition treated as false.
|
||||
class FunctionMultiIf final : public FunctionIfBase</*null_is_false=*/true>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = "multiIf";
|
||||
|
@ -192,20 +192,6 @@ struct AssociativeOperationImpl<Op, 1>
|
||||
};
|
||||
|
||||
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
static llvm::Value * isNativeTrueValue(llvm::IRBuilder<> & b, const DataTypePtr & type, llvm::Value * x)
|
||||
{
|
||||
if (type->isNullable())
|
||||
{
|
||||
auto * subexpr = isNativeTrueValue(b, removeNullable(type), b.CreateExtractValue(x, {0}));
|
||||
return b.CreateAnd(b.CreateNot(b.CreateExtractValue(x, {1})), subexpr);
|
||||
}
|
||||
auto * zero = llvm::Constant::getNullValue(x->getType());
|
||||
return x->getType()->isIntegerTy() ? b.CreateICmpNE(x, zero) : b.CreateFCmpONE(x, zero); /// QNaN -> false
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
template <typename Impl, typename Name>
|
||||
class FunctionAnyArityLogical : public IFunction
|
||||
{
|
||||
@ -407,9 +393,9 @@ public:
|
||||
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
||||
if constexpr (!Impl::isSaturable())
|
||||
{
|
||||
auto * result = isNativeTrueValue(b, types[0], values[0]());
|
||||
auto * result = nativeBoolCast(b, types[0], values[0]());
|
||||
for (size_t i = 1; i < types.size(); i++)
|
||||
result = Impl::apply(b, result, isNativeTrueValue(b, types[i], values[i]()));
|
||||
result = Impl::apply(b, result, nativeBoolCast(b, types[i], values[i]()));
|
||||
return b.CreateSelect(result, b.getInt8(1), b.getInt8(0));
|
||||
}
|
||||
constexpr bool breakOnTrue = Impl::isSaturatedValue(true);
|
||||
@ -421,7 +407,7 @@ public:
|
||||
{
|
||||
b.SetInsertPoint(next);
|
||||
auto * value = values[i]();
|
||||
auto * truth = isNativeTrueValue(b, types[i], value);
|
||||
auto * truth = nativeBoolCast(b, types[i], value);
|
||||
if (!types[i]->equals(DataTypeUInt8{}))
|
||||
value = b.CreateSelect(truth, b.getInt8(1), b.getInt8(0));
|
||||
phi->addIncoming(value, b.GetInsertBlock());
|
||||
@ -509,7 +495,7 @@ public:
|
||||
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
|
||||
{
|
||||
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
||||
return b.CreateSelect(Impl<UInt8>::apply(b, isNativeTrueValue(b, types[0], values[0]())), b.getInt8(1), b.getInt8(0));
|
||||
return b.CreateSelect(Impl<UInt8>::apply(b, nativeBoolCast(b, types[0], values[0]())), b.getInt8(1), b.getInt8(0));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
@ -18,6 +18,7 @@
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
||||
|
||||
#include <llvm/Analysis/TargetTransformInfo.h>
|
||||
#include <llvm/Config/llvm-config.h>
|
||||
#include <llvm/IR/BasicBlock.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
@ -36,6 +37,9 @@
|
||||
#include <llvm/ExecutionEngine/Orc/NullResolver.h>
|
||||
#include <llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h>
|
||||
#include <llvm/Target/TargetMachine.h>
|
||||
#include <llvm/MC/SubtargetFeature.h>
|
||||
#include <llvm/Support/Host.h>
|
||||
#include <llvm/Support/TargetRegistry.h>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
|
||||
@ -120,6 +124,30 @@ static void applyFunction(IFunctionBase & function, Field & value)
|
||||
block.safeGetByPosition(1).column->get(0, value);
|
||||
}
|
||||
|
||||
static llvm::TargetMachine * getNativeMachine()
|
||||
{
|
||||
std::string error;
|
||||
auto cpu = llvm::sys::getHostCPUName();
|
||||
auto triple = llvm::sys::getProcessTriple();
|
||||
auto target = llvm::TargetRegistry::lookupTarget(triple, error);
|
||||
if (!target)
|
||||
throw Exception("Could not initialize native target: " + error, ErrorCodes::CANNOT_COMPILE_CODE);
|
||||
llvm::SubtargetFeatures features;
|
||||
llvm::StringMap<bool> feature_map;
|
||||
if (llvm::sys::getHostCPUFeatures(feature_map))
|
||||
for (auto& f : feature_map)
|
||||
features.AddFeature(f.first(), f.second);
|
||||
llvm::TargetOptions options;
|
||||
return target->createTargetMachine(
|
||||
triple, cpu, features.getString(), options, llvm::None,
|
||||
#if LLVM_VERSION_MAJOR >= 6
|
||||
llvm::None, llvm::CodeGenOpt::Default, /*jit=*/true
|
||||
#else
|
||||
llvm::CodeModel::Default, llvm::CodeGenOpt::Default
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
struct LLVMContext
|
||||
{
|
||||
llvm::LLVMContext context;
|
||||
@ -142,7 +170,7 @@ struct LLVMContext
|
||||
#else
|
||||
: module(std::make_shared<llvm::Module>("jit", context))
|
||||
#endif
|
||||
, machine(llvm::EngineBuilder().selectTarget())
|
||||
, machine(getNativeMachine())
|
||||
#if LLVM_VERSION_MAJOR >= 7
|
||||
, objectLayer(execution_session, [](llvm::orc::VModuleKey)
|
||||
{
|
||||
@ -168,6 +196,7 @@ struct LLVMContext
|
||||
if (!module->size())
|
||||
return;
|
||||
llvm::PassManagerBuilder builder;
|
||||
llvm::legacy::PassManager mpm;
|
||||
llvm::legacy::FunctionPassManager fpm(module.get());
|
||||
builder.OptLevel = 3;
|
||||
builder.SLPVectorize = true;
|
||||
@ -175,9 +204,16 @@ struct LLVMContext
|
||||
builder.RerollLoops = true;
|
||||
builder.VerifyInput = true;
|
||||
builder.VerifyOutput = true;
|
||||
machine->adjustPassManager(builder);
|
||||
fpm.add(llvm::createTargetTransformInfoWrapperPass(machine->getTargetIRAnalysis()));
|
||||
mpm.add(llvm::createTargetTransformInfoWrapperPass(machine->getTargetIRAnalysis()));
|
||||
builder.populateFunctionPassManager(fpm);
|
||||
builder.populateModulePassManager(mpm);
|
||||
fpm.doInitialization();
|
||||
for (auto & function : *module)
|
||||
fpm.run(function);
|
||||
fpm.doFinalization();
|
||||
mpm.run(*module);
|
||||
|
||||
/// name, mangled name
|
||||
std::vector<std::pair<std::string, std::string>> function_names;
|
||||
@ -196,7 +232,7 @@ struct LLVMContext
|
||||
if (compileLayer.addModule(module_key, std::move(module)))
|
||||
throw Exception("Cannot add module to compile layer", ErrorCodes::CANNOT_COMPILE_CODE);
|
||||
#else
|
||||
if (compileLayer.addModule(module, std::make_shared<llvm::orc::NullResolver>()))
|
||||
if (!compileLayer.addModule(module, std::make_shared<llvm::orc::NullResolver>()))
|
||||
throw Exception("Cannot add module to compile layer", ErrorCodes::CANNOT_COMPILE_CODE);
|
||||
#endif
|
||||
|
||||
@ -320,8 +356,8 @@ static void compileFunction(std::shared_ptr<LLVMContext> & context, const IFunct
|
||||
auto * cur_block = b.GetInsertBlock();
|
||||
for (auto & col : columns)
|
||||
{
|
||||
/// currently, stride is either 0 or size of native type
|
||||
auto * is_const = b.CreateICmpEQ(col.stride, llvm::ConstantInt::get(size_type, 0));
|
||||
/// stride is either 0 or size of native type; output column is never constant; neither is at least one input
|
||||
auto * is_const = &col == &columns.back() || columns.size() <= 2 ? b.getFalse() : b.CreateICmpEQ(col.stride, llvm::ConstantInt::get(size_type, 0));
|
||||
col.data->addIncoming(b.CreateSelect(is_const, col.data, b.CreateConstInBoundsGEP1_32(nullptr, col.data, 1)), cur_block);
|
||||
if (col.null)
|
||||
col.null->addIncoming(b.CreateSelect(is_const, col.null, b.CreateConstInBoundsGEP1_32(nullptr, col.null, 1)), cur_block);
|
||||
|
@ -10,11 +10,6 @@ target_compile_options(clickhouse-compiler-lib PRIVATE -fno-rtti -fno-exceptions
|
||||
|
||||
llvm_map_components_to_libnames(REQUIRED_LLVM_LIBRARIES all)
|
||||
|
||||
# We link statically with zlib, and LLVM (sometimes) tries to bring its own dependency.
|
||||
list(REMOVE_ITEM REQUIRED_LLVM_LIBRARIES "-lz")
|
||||
# Wrong library in freebsd:
|
||||
list(REMOVE_ITEM REQUIRED_LLVM_LIBRARIES "-l/usr/lib/libexecinfo.so")
|
||||
|
||||
message(STATUS "Using LLVM ${LLVM_VERSION}: ${LLVM_INCLUDE_DIRS} : ${REQUIRED_LLVM_LIBRARIES}")
|
||||
|
||||
target_include_directories(clickhouse-compiler-lib PRIVATE ${LLVM_INCLUDE_DIRS})
|
||||
|
Loading…
Reference in New Issue
Block a user