From 6d2259f2cff73e251934e2189fe11bddd9a044df Mon Sep 17 00:00:00 2001 From: pyos Date: Thu, 10 May 2018 17:02:25 +0300 Subject: [PATCH] Implement jit for comparisons, except for (double, int). That one has some edge cases which I can't be bothered to code. --- dbms/src/DataTypes/Native.h | 44 ++++++++------ dbms/src/Functions/FunctionsComparison.h | 77 ++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 17 deletions(-) diff --git a/dbms/src/DataTypes/Native.h b/dbms/src/DataTypes/Native.h index 6a793d13ca4..e6167b03a73 100644 --- a/dbms/src/DataTypes/Native.h +++ b/dbms/src/DataTypes/Native.h @@ -30,6 +30,15 @@ static inline bool typeIsEither(const IDataType & type) return (typeid_cast(&type) || ...); } +static inline bool typeIsSigned(const IDataType & type) +{ + return typeIsEither< + DataTypeInt8, DataTypeInt16, DataTypeInt32, DataTypeInt64, + DataTypeFloat32, DataTypeFloat64, + DataTypeDate, DataTypeDateTime, DataTypeInterval + >(type); +} + static inline llvm::Type * toNativeType(llvm::IRBuilderBase & builder, const IDataType & type) { if (auto * nullable = typeid_cast(&type)) @@ -77,11 +86,26 @@ static inline llvm::Value * nativeBoolCast(llvm::IRBuilder<> & b, const DataType 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) +static inline llvm::Value * nativeCast(llvm::IRBuilder<> & b, const DataTypePtr & from, llvm::Value * value, llvm::Type * to) { auto * n_from = value->getType(); + if (n_from == to) + return value; + if (n_from->isIntegerTy() && to->isFloatingPointTy()) + return typeIsSigned(*from) ? b.CreateSIToFP(value, to) : b.CreateUIToFP(value, to); + if (n_from->isFloatingPointTy() && to->isIntegerTy()) + return typeIsSigned(*from) ? b.CreateFPToSI(value, to) : b.CreateFPToUI(value, to); + if (n_from->isIntegerTy() && to->isIntegerTy()) + return b.CreateIntCast(value, to, typeIsSigned(*from)); + if (n_from->isFloatingPointTy() && to->isFloatingPointTy()) + return b.CreateFPCast(value, to); + throw Exception("Cannot cast " + from->getName() + " to requested type", ErrorCodes::NOT_IMPLEMENTED); +} + +static inline llvm::Value * nativeCast(llvm::IRBuilder<> & b, const DataTypePtr & from, llvm::Value * value, const DataTypePtr & to) +{ auto * n_to = toNativeType(b, to); - if (n_from == n_to) + if (value->getType() == n_to) return value; if (from->isNullable() && to->isNullable()) { @@ -95,21 +119,7 @@ static inline llvm::Value * nativeCast(llvm::IRBuilder<> & b, const DataTypePtr 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); + return nativeCast(b, from, value, n_to); } } diff --git a/dbms/src/Functions/FunctionsComparison.h b/dbms/src/Functions/FunctionsComparison.h index 0cf19833821..229cbfe20e2 100644 --- a/dbms/src/Functions/FunctionsComparison.h +++ b/dbms/src/Functions/FunctionsComparison.h @@ -53,12 +53,26 @@ template struct EqualsOp using SymmetricOp = EqualsOp; static UInt8 apply(A a, B b) { return accurate::equalsOp(a, b); } + +#if USE_EMBEDDED_COMPILER + static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool /*is_signed*/) + { + return x->getType()->isIntegerTy() ? b.CreateICmpEQ(x, y) : b.CreateFCmpOEQ(x, y); /// qNaNs always compare false + } +#endif }; template struct NotEqualsOp { using SymmetricOp = NotEqualsOp; static UInt8 apply(A a, B b) { return accurate::notEqualsOp(a, b); } + +#if USE_EMBEDDED_COMPILER + static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool /*is_signed*/) + { + return x->getType()->isIntegerTy() ? b.CreateICmpNE(x, y) : b.CreateFCmpONE(x, y); + } +#endif }; template struct GreaterOp; @@ -67,12 +81,26 @@ template struct LessOp { using SymmetricOp = GreaterOp; static UInt8 apply(A a, B b) { return accurate::lessOp(a, b); } + +#if USE_EMBEDDED_COMPILER + static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool is_signed) + { + return x->getType()->isIntegerTy() ? (is_signed ? b.CreateICmpSLT(x, y) : b.CreateICmpULT(x, y)) : b.CreateFCmpOLT(x, y); + } +#endif }; template struct GreaterOp { using SymmetricOp = LessOp; static UInt8 apply(A a, B b) { return accurate::greaterOp(a, b); } + +#if USE_EMBEDDED_COMPILER + static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool is_signed) + { + return x->getType()->isIntegerTy() ? (is_signed ? b.CreateICmpSGT(x, y) : b.CreateICmpUGT(x, y)) : b.CreateFCmpOGT(x, y); + } +#endif }; template struct GreaterOrEqualsOp; @@ -81,12 +109,26 @@ template struct LessOrEqualsOp { using SymmetricOp = GreaterOrEqualsOp; static UInt8 apply(A a, B b) { return accurate::lessOrEqualsOp(a, b); } + +#if USE_EMBEDDED_COMPILER + static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool is_signed) + { + return x->getType()->isIntegerTy() ? (is_signed ? b.CreateICmpSLE(x, y) : b.CreateICmpULE(x, y)) : b.CreateFCmpOLE(x, y); + } +#endif }; template struct GreaterOrEqualsOp { using SymmetricOp = LessOrEqualsOp; static UInt8 apply(A a, B b) { return accurate::greaterOrEqualsOp(a, b); } + +#if USE_EMBEDDED_COMPILER + static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool is_signed) + { + return x->getType()->isIntegerTy() ? (is_signed ? b.CreateICmpSGE(x, y) : b.CreateICmpUGE(x, y)) : b.CreateFCmpOGE(x, y); + } +#endif }; @@ -1136,6 +1178,41 @@ public: col_with_type_and_name_left.type, col_with_type_and_name_right.type, left_is_num, input_rows_count); } + +#if USE_EMBEDDED_COMPILER + bool isCompilableImpl(const DataTypes & types) const override + { + auto isBigInteger = &typeIsEither; + auto isFloatingPoint = &typeIsEither; + if ((isBigInteger(*types[0]) && isFloatingPoint(*types[1])) || (isBigInteger(*types[1]) && isFloatingPoint(*types[0]))) + return false; /// TODO: implement (double, int_N where N > double's mantissa width) + return types[0]->isValueRepresentedByNumber() && types[1]->isValueRepresentedByNumber(); + } + + llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override + { + auto & b = static_cast &>(builder); + auto * x = values[0](); + auto * y = values[1](); + if (!types[0]->equals(*types[1])) + { + llvm::Type * common; + if (x->getType()->isIntegerTy() && y->getType()->isIntegerTy()) + common = b.getIntNTy(std::max( + /// if one integer has a sign bit, make sure the other does as well. llvm generates optimal code + /// (e.g. uses overflow flag on x86) for (word size + 1)-bit integer operations. + x->getType()->getIntegerBitWidth() + (!typeIsSigned(*types[0]) && typeIsSigned(*types[1])), + y->getType()->getIntegerBitWidth() + (!typeIsSigned(*types[1]) && typeIsSigned(*types[0])))); + else + /// (double, float) or (double, int_N where N <= double's mantissa width) -> double + common = b.getDoubleTy(); + x = nativeCast(b, types[0], x, common); + y = nativeCast(b, types[1], y, common); + } + auto * result = Op::compile(b, x, y, typeIsSigned(*types[0]) || typeIsSigned(*types[1])); + return b.CreateSelect(result, b.getInt8(1), b.getInt8(0)); + } +#endif };