Merge branch 'llvm-jit' of github.com:pyos/ClickHouse into pyos-llvm-jit

This commit is contained in:
Alexey Milovidov 2018-05-08 00:25:13 +03:00
commit 3453bf577a
7 changed files with 162 additions and 47 deletions

View File

@ -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);
}
}

View File

@ -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 ()

View File

@ -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;
}

View File

@ -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";

View File

@ -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
};

View File

@ -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);

View File

@ -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})