Implement jit for most arithmetic functions, remove the test function

This commit is contained in:
pyos 2018-04-30 01:43:02 +03:00
parent 7529aa55a4
commit 059bbcacca
7 changed files with 322 additions and 89 deletions

View File

@ -4,6 +4,7 @@
#if USE_EMBEDDED_COMPILER
#include <Common/typeid_cast.h>
#include <DataTypes/DataTypeDate.h>
#include <DataTypes/DataTypeDateTime.h>
#include <DataTypes/DataTypeFixedString.h>
@ -55,6 +56,21 @@ 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)
{
if (value->getType() == type)
return value;
if (value->getType()->isIntegerTy())
{
if (type->isIntegerTy())
return builder.CreateIntCast(value, type, is_signed);
return is_signed ? builder.CreateSIToFP(value, type) : builder.CreateUIToFP(value, type);
}
if (type->isFloatingPointTy())
return builder.CreateFPCast(value, type);
return is_signed ? builder.CreateFPToSI(value, type) : builder.CreateFPToUI(value, type);
}
}
#endif

View File

@ -104,10 +104,7 @@ if (ENABLE_TESTS)
endif ()
if (USE_EMBEDDED_COMPILER)
#llvm_map_components_to_libraries(REQUIRED_LLVM_LIBRARIES all)
#target_link_libraries(clickhouse_functions PRIVATE ${REQUIRED_LLVM_LIBRARIES})
target_include_directories (clickhouse_functions BEFORE PUBLIC ${LLVM_INCLUDE_DIRS})
# LLVM 5.0 has a bunch of unused parameters in its header files.
# TODO: global-disable this warning
set_source_files_properties(FunctionsLLVMTest.cpp PROPERTIES COMPILE_FLAGS "-Wno-unused-parameter -g")
# LLVM has a bunch of unused parameters in its header files.
target_compile_options (clickhouse_functions PRIVATE "-Wno-unused-parameter")
endif ()

View File

@ -5,12 +5,16 @@
#include <Columns/ColumnNullable.h>
#include <DataTypes/DataTypeNullable.h>
#include <IO/WriteHelpers.h>
#include "FunctionsArithmetic.h"
namespace DB
{
namespace ErrorCodes
{
extern const int ILLEGAL_COLUMN;
}
const ColumnConst * checkAndGetColumnConstStringOrFixedString(const IColumn * column)
{
if (!column->isColumnConst())

View File

@ -4,6 +4,7 @@
#include <DataTypes/DataTypeDate.h>
#include <DataTypes/DataTypeDateTime.h>
#include <DataTypes/DataTypeInterval.h>
#include <DataTypes/Native.h>
#include <Columns/ColumnVector.h>
#include <Columns/ColumnConst.h>
#include <Functions/IFunction.h>
@ -19,6 +20,10 @@
#include <common/intExp.h>
#include <boost/math/common_factor.hpp>
#if USE_EMBEDDED_COMPILER
#include <llvm/IR/IRBuilder.h>
#endif
namespace DB
{
@ -106,6 +111,15 @@ struct PlusImpl
/// Next everywhere, static_cast - so that there is no wrong result in expressions of the form Int64 c = UInt32(a) * Int32(-1).
return static_cast<Result>(a) + b;
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
{
return left->getType()->isIntegerTy() ? b.CreateAdd(left, right) : b.CreateFAdd(left, right);
}
#endif
};
@ -119,6 +133,15 @@ struct MultiplyImpl
{
return static_cast<Result>(a) * b;
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
{
return left->getType()->isIntegerTy() ? b.CreateMul(left, right) : b.CreateFMul(left, right);
}
#endif
};
template <typename A, typename B>
@ -131,6 +154,15 @@ struct MinusImpl
{
return static_cast<Result>(a) - b;
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
{
return left->getType()->isIntegerTy() ? b.CreateSub(left, right) : b.CreateFSub(left, right);
}
#endif
};
template <typename A, typename B>
@ -143,6 +175,17 @@ struct DivideFloatingImpl
{
return static_cast<Result>(a) / b;
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
{
if (left->getType()->isIntegerTy())
throw Exception("DivideFloatingImpl expected a floating-point type", ErrorCodes::LOGICAL_ERROR);
return b.CreateFDiv(left, right);
}
#endif
};
@ -189,6 +232,10 @@ struct DivideIntegralImpl
throwIfDivisionLeadsToFPE(a, b);
return a / b;
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// don't know how to throw from LLVM IR
#endif
};
template <typename A, typename B>
@ -201,6 +248,10 @@ struct DivideIntegralOrZeroImpl
{
return unlikely(divisionLeadsToFPE(a, b)) ? 0 : a / b;
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// TODO implement the checks
#endif
};
template <typename A, typename B>
@ -212,9 +263,12 @@ struct ModuloImpl
static inline Result apply(A a, B b)
{
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b));
return typename NumberTraits::ToInteger<A>::Type(a)
% typename NumberTraits::ToInteger<B>::Type(b);
return typename NumberTraits::ToInteger<A>::Type(a) % typename NumberTraits::ToInteger<B>::Type(b);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// don't know how to throw from LLVM IR
#endif
};
template <typename A, typename B>
@ -225,9 +279,19 @@ struct BitAndImpl
template <typename Result = ResultType>
static inline Result apply(A a, B b)
{
return static_cast<Result>(a)
& static_cast<Result>(b);
return static_cast<Result>(a) & static_cast<Result>(b);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
{
if (!left->getType()->isIntegerTy())
throw Exception("BitAndImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
return b.CreateAnd(left, right);
}
#endif
};
template <typename A, typename B>
@ -238,9 +302,19 @@ struct BitOrImpl
template <typename Result = ResultType>
static inline Result apply(A a, B b)
{
return static_cast<Result>(a)
| static_cast<Result>(b);
return static_cast<Result>(a) | static_cast<Result>(b);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
{
if (!left->getType()->isIntegerTy())
throw Exception("BitOrImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
return b.CreateOr(left, right);
}
#endif
};
template <typename A, typename B>
@ -251,9 +325,19 @@ struct BitXorImpl
template <typename Result = ResultType>
static inline Result apply(A a, B b)
{
return static_cast<Result>(a)
^ static_cast<Result>(b);
return static_cast<Result>(a) ^ static_cast<Result>(b);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
{
if (!left->getType()->isIntegerTy())
throw Exception("BitXorImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
return b.CreateXor(left, right);
}
#endif
};
template <typename A, typename B>
@ -264,9 +348,19 @@ struct BitShiftLeftImpl
template <typename Result = ResultType>
static inline Result apply(A a, B b)
{
return static_cast<Result>(a)
<< static_cast<Result>(b);
return static_cast<Result>(a) << static_cast<Result>(b);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
{
if (!left->getType()->isIntegerTy())
throw Exception("BitShiftLeftImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
return b.CreateShl(left, right);
}
#endif
};
template <typename A, typename B>
@ -277,9 +371,19 @@ struct BitShiftRightImpl
template <typename Result = ResultType>
static inline Result apply(A a, B b)
{
return static_cast<Result>(a)
>> static_cast<Result>(b);
return static_cast<Result>(a) >> static_cast<Result>(b);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool is_signed)
{
if (!left->getType()->isIntegerTy())
throw Exception("BitShiftRightImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
return is_signed ? b.CreateAShr(left, right) : b.CreateLShr(left, right);
}
#endif
};
template <typename A, typename B>
@ -293,6 +397,19 @@ struct BitRotateLeftImpl
return (static_cast<Result>(a) << static_cast<Result>(b))
| (static_cast<Result>(a) >> ((sizeof(Result) * 8) - static_cast<Result>(b)));
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
{
if (!left->getType()->isIntegerTy())
throw Exception("BitRotateLeftImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
auto * size = llvm::ConstantInt::get(left->getType(), left->getType()->getPrimitiveSizeInBits());
/// XXX how is this supposed to behave in signed mode?
return b.CreateOr(b.CreateShl(left, right), b.CreateLShr(left, b.CreateSub(size, right)));
}
#endif
};
template <typename A, typename B>
@ -306,24 +423,35 @@ struct BitRotateRightImpl
return (static_cast<Result>(a) >> static_cast<Result>(b))
| (static_cast<Result>(a) << ((sizeof(Result) * 8) - static_cast<Result>(b)));
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
{
if (!left->getType()->isIntegerTy())
throw Exception("BitRotateRightImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
auto * size = llvm::ConstantInt::get(left->getType(), left->getType()->getPrimitiveSizeInBits());
return b.CreateOr(b.CreateLShr(left, right), b.CreateShl(left, b.CreateSub(size, right)));
}
#endif
};
template <typename T>
std::enable_if_t<std::is_integral_v<T>, T> toInteger(T x) { return x; }
template <typename T>
std::enable_if_t<std::is_floating_point_v<T>, Int64> toInteger(T x) { return Int64(x); }
template <typename A, typename B>
struct BitTestImpl
{
using ResultType = UInt8;
template <typename Result = ResultType>
static inline Result apply(A a, B b) { return (toInteger(a) >> toInteger(b)) & 1; };
};
static inline Result apply(A a, B b)
{
return (typename NumberTraits::ToInteger<A>::Type(a) >> typename NumberTraits::ToInteger<B>::Type(b)) & 1;
};
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// TODO
#endif
};
template <typename A, typename B>
struct LeastBaseImpl
@ -336,6 +464,18 @@ struct LeastBaseImpl
/** gcc 4.9.2 successfully vectorizes a loop from this function. */
return static_cast<Result>(a) < static_cast<Result>(b) ? static_cast<Result>(a) : static_cast<Result>(b);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool is_signed)
{
if (!left->getType()->isIntegerTy())
/// XXX minnum is basically fmin(), it may or may not match whatever apply() does
return b.CreateMinNum(left, right);
return b.CreateSelect(is_signed ? b.CreateICmpSLT(left, right) : b.CreateICmpULT(left, right), left, right);
}
#endif
};
template <typename A, typename B>
@ -349,6 +489,10 @@ struct LeastSpecialImpl
static_assert(std::is_same_v<Result, ResultType>, "ResultType != Result");
return accurate::lessOp(a, b) ? static_cast<Result>(a) : static_cast<Result>(b);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// ???
#endif
};
template <typename A, typename B>
@ -365,6 +509,18 @@ struct GreatestBaseImpl
{
return static_cast<Result>(a) > static_cast<Result>(b) ? static_cast<Result>(a) : static_cast<Result>(b);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool is_signed)
{
if (!left->getType()->isIntegerTy())
/// XXX maxnum is basically fmax(), it may or may not match whatever apply() does
return b.CreateMaxNum(left, right);
return b.CreateSelect(is_signed ? b.CreateICmpSGT(left, right) : b.CreateICmpUGT(left, right), left, right);
}
#endif
};
template <typename A, typename B>
@ -378,6 +534,10 @@ struct GreatestSpecialImpl
static_assert(std::is_same_v<Result, ResultType>, "ResultType != Result");
return accurate::greaterOp(a, b) ? static_cast<Result>(a) : static_cast<Result>(b);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// ???
#endif
};
template <typename A, typename B>
@ -393,6 +553,15 @@ struct NegateImpl
{
return -static_cast<ResultType>(a);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * arg, bool)
{
return arg->getType()->isIntegerTy() ? b.CreateNeg(arg) : b.CreateFNeg(arg);
}
#endif
};
template <typename A>
@ -404,6 +573,17 @@ struct BitNotImpl
{
return ~static_cast<ResultType>(a);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = true;
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * arg, bool)
{
if (!arg->getType()->isIntegerTy())
throw Exception("BitNotImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
return b.CreateNot(arg);
}
#endif
};
template <typename A>
@ -420,6 +600,10 @@ struct AbsImpl
else if constexpr (std::is_floating_point_v<A>)
return static_cast<ResultType>(std::abs(a));
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// special type handling, some other time
#endif
};
template <typename A, typename B>
@ -436,6 +620,10 @@ struct GCDImpl
typename NumberTraits::ToInteger<Result>::Type(a),
typename NumberTraits::ToInteger<Result>::Type(b));
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// exceptions (and a non-trivial algorithm)
#endif
};
template <typename A, typename B>
@ -452,6 +640,10 @@ struct LCMImpl
typename NumberTraits::ToInteger<Result>::Type(a),
typename NumberTraits::ToInteger<Result>::Type(b));
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// exceptions (and a non-trivial algorithm)
#endif
};
template <typename A>
@ -463,6 +655,10 @@ struct IntExp2Impl
{
return intExp2(a);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// library function
#endif
};
template <typename A>
@ -474,6 +670,10 @@ struct IntExp10Impl
{
return intExp10(a);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false; /// library function
#endif
};
/// Used to indicate undefined operation
@ -745,6 +945,43 @@ public:
if (!valid)
throw Exception(getName() + "'s arguments do not match the expected data types", ErrorCodes::LOGICAL_ERROR);
}
#if USE_EMBEDDED_COMPILER
bool isCompilableImpl(const DataTypes & arguments) const override
{
return castBothTypes(arguments[0].get(), arguments[1].get(), [&](const auto & left, const auto & right)
{
using LeftDataType = std::decay_t<decltype(left)>;
using RightDataType = std::decay_t<decltype(right)>;
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
using OpSpec = Op<typename LeftDataType::FieldType, typename RightDataType::FieldType>;
return !std::is_same_v<ResultDataType, InvalidType> && OpSpec::compilable;
});
}
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
{
llvm::Value * result = nullptr;
castBothTypes(types[0].get(), types[1].get(), [&](const auto & left, const auto & right)
{
using LeftDataType = std::decay_t<decltype(left)>;
using RightDataType = std::decay_t<decltype(right)>;
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
using OpSpec = Op<typename LeftDataType::FieldType, typename RightDataType::FieldType>;
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>);
result = OpSpec::compile(b, lval, rval, std::is_signed_v<typename ResultDataType::FieldType>);
return true;
}
return false;
});
return result;
}
#endif
};
@ -821,6 +1058,35 @@ public:
throw Exception(getName() + "'s argument does not match the expected data type", ErrorCodes::LOGICAL_ERROR);
}
#if USE_EMBEDDED_COMPILER
bool isCompilableImpl(const DataTypes & arguments) const override
{
return castType(arguments[0].get(), [&](const auto & type)
{
return Op<typename std::decay_t<decltype(type)>::FieldType>::compilable;
});
}
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
{
llvm::Value * result = nullptr;
castType(types[0].get(), [&](const auto & type)
{
using T0 = typename std::decay_t<decltype(type)>::FieldType;
using T1 = typename Op<T0>::ResultType;
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>);
result = Op<T0>::compile(b, v, std::is_signed_v<T1>);
return true;
}
return false;
});
return result;
}
#endif
bool hasInformationAboutMonotonicity() const override
{
return FunctionUnaryArithmeticMonotonicity<Name>::has();

View File

@ -1,60 +0,0 @@
#include <DataTypes/DataTypesNumber.h>
#include <Functions/FunctionFactory.h>
#include <Functions/IFunction.h>
#if USE_EMBEDDED_COMPILER
#include <llvm/IR/IRBuilder.h>
#endif
namespace DB
{
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
}
class FunctionSomething : public IFunction
{
public:
static constexpr auto name = "something";
#if USE_EMBEDDED_COMPILER
bool isCompilableImpl(const DataTypes & types) const override
{
return types.size() == 2 && types[0]->equals(*types[1]);
}
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
{
if (types[0]->equals(DataTypeFloat32{}) || types[0]->equals(DataTypeFloat64{}))
return static_cast<llvm::IRBuilder<>&>(builder).CreateFAdd(values[0](), values[1]());
return static_cast<llvm::IRBuilder<>&>(builder).CreateAdd(values[0](), values[1]());
}
#endif
static FunctionPtr create(const Context &) { return std::make_shared<FunctionSomething>(); }
String getName() const override { return name; }
size_t getNumberOfArguments() const override { return 2; }
bool useDefaultImplementationForConstants() const override { return true; }
DataTypePtr getReturnTypeImpl(const DataTypes & types) const override { return types[0]; }
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) override
{
throw Exception("should've used the jitted version", ErrorCodes::NOT_IMPLEMENTED);
}
};
void registerFunctionsLLVMTest(FunctionFactory & factory)
{
factory.registerFunction<FunctionSomething>();
}
}

View File

@ -93,6 +93,10 @@ struct RoundToExp2Impl
{
return roundDownToPowerOfTwo<T>(x);
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false;
#endif
};
@ -120,6 +124,10 @@ struct RoundDurationImpl
: (x < 36000 ? 18000
: 36000))))))))))))));
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false;
#endif
};
template <typename A>
@ -137,6 +145,10 @@ struct RoundAgeImpl
: (x < 55 ? 45
: 55)))));
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false;
#endif
};

View File

@ -42,7 +42,6 @@ void registerFunctionsGeo(FunctionFactory &);
void registerFunctionsCharset(FunctionFactory &);
void registerFunctionsNull(FunctionFactory &);
void registerFunctionsFindCluster(FunctionFactory &);
void registerFunctionsLLVMTest(FunctionFactory &);
void registerFunctions()
@ -80,7 +79,6 @@ void registerFunctions()
registerFunctionsCharset(factory);
registerFunctionsNull(factory);
registerFunctionsFindCluster(factory);
registerFunctionsLLVMTest(factory);
}
}