#include "mkql_builtins_impl.h" // Y_IGNORE #include "mkql_builtins_datetime.h" #include "mkql_builtins_decimal.h" // Y_IGNORE #include namespace NKikimr { namespace NMiniKQL { namespace { template struct TAdd : public TSimpleArithmeticBinary> { static constexpr auto NullMode = TKernel::ENullMode::Default; static TOutput Do(TOutput left, TOutput right) { return left + right; } #ifndef MKQL_DISABLE_CODEGEN static Value* Gen(Value* left, Value* right, const TCodegenContext&, BasicBlock*& block) { return std::is_integral() ? BinaryOperator::CreateAdd(left, right, "add", block) : BinaryOperator::CreateFAdd(left, right, "add", block); } #endif }; template using TAggrAdd = TAdd; template struct TDecimalAdd { static NUdf::TUnboxedValuePod Execute(const NUdf::TUnboxedValuePod& left, const NUdf::TUnboxedValuePod& right) { const auto l = left.GetInt128(); const auto r = right.GetInt128(); const auto a = l + r; using namespace NYql::NDecimal; if (IsNormal(l) && IsNormal(r) && IsNormal(a)) return NUdf::TUnboxedValuePod(a); if (IsNan(l) || IsNan(r) || !a) return NUdf::TUnboxedValuePod(Nan()); else return NUdf::TUnboxedValuePod(a > 0 ? +Inf() : -Inf()); } #ifndef MKQL_DISABLE_CODEGEN static Value* Generate(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block) { auto& context = ctx.Codegen.GetContext(); const auto& bounds = NDecimal::GenBounds(context); const auto l = GetterForInt128(left, block); const auto r = GetterForInt128(right, block); const auto add = BinaryOperator::CreateAdd(l, r, "add", block); const auto lok = NDecimal::GenInBounds(l, bounds.first, bounds.second, block); const auto rok = NDecimal::GenInBounds(r, bounds.first, bounds.second, block); const auto aok = NDecimal::GenInBounds(add, bounds.first, bounds.second, block); const auto bok = BinaryOperator::CreateAnd(lok, rok, "bok", block); const auto ok = BinaryOperator::CreateAnd(aok, bok, "ok", block); const auto bads = BasicBlock::Create(context, "bads", ctx.Func); const auto infs = BasicBlock::Create(context, "infs", ctx.Func); const auto done = BasicBlock::Create(context, "done", ctx.Func); const auto result = PHINode::Create(add->getType(), 3, "result", done); result->addIncoming(add, block); BranchInst::Create(done, bads, ok, block); block = bads; const auto lnan = NDecimal::GenIsNonComparable(l, context, block); const auto rnan = NDecimal::GenIsNonComparable(r, context, block); const auto anan = BinaryOperator::CreateOr(lnan, rnan, "anan", block); const auto null = ConstantInt::get(add->getType(), 0); const auto zero = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, add, null, "zero", block); const auto nan = BinaryOperator::CreateOr(anan, zero, "nan", block); result->addIncoming(GetDecimalNan(context), block); BranchInst::Create(done, infs, nan, block); block = infs; const auto plus = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGT, add, null, "plus", block); const auto inf = SelectInst::Create(plus, GetDecimalPlusInf(context), GetDecimalMinusInf(context), "inf", block); result->addIncoming(inf, block); BranchInst::Create(done, block); block = done; return SetterForInt128(result, block); } #endif static_assert(Precision <= NYql::NDecimal::MaxPrecision, "Too large precision!"); }; template struct TDateTimeAddT { static_assert(std::is_integral::value, "left must be integral"); static_assert(std::is_integral::value, "right must be integral"); static_assert(std::is_integral::value, "output must be integral"); static constexpr auto NullMode = TKernel::ENullMode::AlwaysNull; static NUdf::TUnboxedValuePod Execute(const NUdf::TUnboxedValuePod& left, const NUdf::TUnboxedValuePod& right) { const auto lv = ToScaledDate(left.template Get()); const auto rv = ToScaledDate(right.template Get()); const auto ret = lv + rv; if (IsBadScaledDate(ret)) { return NUdf::TUnboxedValuePod(); } auto data = NUdf::TUnboxedValuePod(FromScaledDate(ret)); if constexpr (Tz) { data.SetTimezoneId(((std::is_same>() || std::is_same>()) ? right : left).GetTimezoneId()); } return data; } #ifndef MKQL_DISABLE_CODEGEN static Value* Generate(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block) { auto& context = ctx.Codegen.GetContext(); const auto lhs = GenToScaledDate(GetterFor(left, context, block), context, block); const auto rhs = GenToScaledDate(GetterFor(right, context, block), context, block); const auto add = BinaryOperator::CreateAdd(lhs, rhs, "add", block); const auto wide = SetterFor(GenFromScaledDate(add, context, block), context, block); const auto bad = GenIsBadScaledDate(add, context, block); const auto type = Type::getInt128Ty(context); const auto zero = ConstantInt::get(type, 0); if constexpr (Tz) { const uint64_t init[] = {0ULL, 0xFFFFULL}; const auto mask = ConstantInt::get(type, APInt(128, 2, init)); const auto tzid = BinaryOperator::CreateAnd((std::is_same>() || std::is_same>()) ? right : left, mask, "tzid", block); const auto full = BinaryOperator::CreateOr(wide, tzid, "full", block); const auto sel = SelectInst::Create(bad, zero, full, "sel", block); return sel; } else { const auto sel = SelectInst::Create(bad, zero, wide, "sel", block); return sel; } } #endif }; template struct TBigIntervalAdd { static_assert(std::is_same_v, "Left must be i64"); static_assert(std::is_same_v, "Right must be i64"); static_assert(std::is_same_v, "Output must be i64"); static constexpr auto NullMode = TKernel::ENullMode::AlwaysNull; static NUdf::TUnboxedValuePod Execute(const NUdf::TUnboxedValuePod& left, const NUdf::TUnboxedValuePod& right) { i64 lv = left.Get(); i64 rv = right.Get(); i64 ret = lv + rv; // detect overflow if (lv > 0 && rv > 0 && ret < 0) { return NUdf::TUnboxedValuePod(); } else if (lv < 0 && rv < 0 && ret > 0) { return NUdf::TUnboxedValuePod(); } else if (IsBadInterval>(ret)) { return NUdf::TUnboxedValuePod(); } return NUdf::TUnboxedValuePod(ret); } #ifndef MKQL_DISABLE_CODEGEN static Value* Generate(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block) { auto& context = ctx.Codegen.GetContext(); const auto lhs = GetterFor(left, context, block); const auto rhs = GetterFor(right, context, block); const auto add = BinaryOperator::CreateAdd(lhs, rhs, "add", block); const auto wide = SetterFor(add, context, block); const auto lneg = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLT, lhs, ConstantInt::get(lhs->getType(), 0), "lneg", block); const auto rneg = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLT, rhs, ConstantInt::get(rhs->getType(), 0), "rneg", block); const auto apos = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGT, add, ConstantInt::get(add->getType(), 0), "apos", block); const auto posAddNegArg = BinaryOperator::CreateAnd(apos, BinaryOperator::CreateAnd(lneg, rneg, "negArg", block), "posAddNegArg", block); const auto lpos = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGT, lhs, ConstantInt::get(lhs->getType(), 0), "lpos", block); const auto rpos = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGT, rhs, ConstantInt::get(rhs->getType(), 0), "rpos", block); const auto aneg = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLT, add, ConstantInt::get(add->getType(), 0), "aneg", block); const auto negAddPosArg = BinaryOperator::CreateAnd(aneg, BinaryOperator::CreateAnd(lpos, rpos, "posArg", block), "negAddPosArg", block); const auto bad = BinaryOperator::CreateOr( BinaryOperator::CreateOr(posAddNegArg, negAddPosArg, "overflow", block), GenIsBadInterval>(add, context, block), "bad", block); const auto zero = ConstantInt::get(Type::getInt128Ty(context), 0); const auto sel = SelectInst::Create(bad, zero, wide, "sel", block); return sel; } #endif }; template using TDateTimeAdd = TDateTimeAddT; template using TDateTimeAddTz = TDateTimeAddT; template class TAdder> void RegisterAddDateAndInterval(IBuiltinFunctionRegistry& registry) { using TDate1 = std::conditional_t, NUdf::TDataType>; using TDate2 = std::conditional_t, NUdf::TDataType>; using TDate3 = std::conditional_t, NUdf::TDataType>; using TDate1Big = std::conditional_t, NUdf::TDataType>; using TDate2Big = std::conditional_t, NUdf::TDataType>; using TDate3Big = std::conditional_t, NUdf::TDataType>; RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); RegisterFunctionBinPolyOpt(registry, "Add"); } template using TIntervalAggrAdd = TDateTimeAdd; } void RegisterAdd(IBuiltinFunctionRegistry& registry) { RegisterBinaryNumericFunctionOpt(registry, "Add"); NDecimal::RegisterBinaryFunctionForAllPrecisions(registry, "Add_"); RegisterAddDateAndInterval, TDateTimeAdd>(registry); RegisterAddDateAndInterval, TDateTimeAddTz>(registry); RegisterAddDateAndInterval, TDateTimeAdd>(registry); RegisterAddDateAndInterval, TDateTimeAddTz>(registry); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TDateTimeAdd, TBinaryArgsOptWithNullableResult>(registry, "Add"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TBigIntervalAdd, TBinaryArgsOptWithNullableResult>(registry, "Add"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TBigIntervalAdd, TBinaryArgsOptWithNullableResult>(registry, "Add"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TBigIntervalAdd, TBinaryArgsOptWithNullableResult>(registry, "Add"); } template void RegisterDateAddInterval(TKernelFamilyBase& owner) { using TDateLeft1 = std::conditional_t, NUdf::TDataType>, std::conditional_t, NUdf::TDataType>>; using TDateLeft2 = std::conditional_t, NUdf::TDataType>, std::conditional_t, NUdf::TDataType>>; using TDateLeft3 = std::conditional_t, NUdf::TDataType>, std::conditional_t, NUdf::TDataType>>; using TIntervalRight = std::conditional_t, NUdf::TDataType>; if constexpr (Tz) { AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); } else { AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); } } template void RegisterIntervalAddDate(TKernelFamilyBase& owner) { using TIntervalLeft = std::conditional_t, NUdf::TDataType>; using TDateRight1 = std::conditional_t, NUdf::TDataType>, std::conditional_t, NUdf::TDataType>>; using TDateRight2 = std::conditional_t, NUdf::TDataType>, std::conditional_t, NUdf::TDataType>>; using TDateRight3 = std::conditional_t, NUdf::TDataType>, std::conditional_t, NUdf::TDataType>>; if constexpr (Tz) { AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); } else { AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); } } template void RegisterIntervalAddInterval(TKernelFamilyBase& owner) { using TLeft = std::conditional_t, NUdf::TDataType>; using TRight = std::conditional_t, NUdf::TDataType>; using TOutput = std::conditional_t, NUdf::TDataType>; if constexpr (BigInterval1 || BigInterval2) { AddBinaryKernelPoly(owner); } else { AddBinaryKernelPoly(owner); } } void RegisterAdd(TKernelFamilyMap& kernelFamilyMap) { auto family = std::make_unique(); AddBinaryIntegralKernels(*family); AddBinaryRealKernels(*family); AddBinaryDecimalKernels(*family); RegisterDateAddInterval(*family); RegisterDateAddInterval(*family); RegisterDateAddInterval(*family); RegisterDateAddInterval(*family); RegisterDateAddInterval(*family); RegisterDateAddInterval(*family); RegisterDateAddInterval(*family); RegisterDateAddInterval(*family); RegisterIntervalAddDate(*family); RegisterIntervalAddDate(*family); RegisterIntervalAddDate(*family); RegisterIntervalAddDate(*family); RegisterIntervalAddDate(*family); RegisterIntervalAddDate(*family); RegisterIntervalAddDate(*family); RegisterIntervalAddDate(*family); RegisterIntervalAddInterval(*family); RegisterIntervalAddInterval(*family); RegisterIntervalAddInterval(*family); RegisterIntervalAddInterval(*family); kernelFamilyMap["Add"] = std::move(family); } void RegisterAggrAdd(IBuiltinFunctionRegistry& registry) { RegisterNumericAggregateFunction(registry, "AggrAdd"); RegisterAggregateFunctionPoly, TIntervalAggrAdd, TBinaryArgsSameOptArgsWithNullableResult>(registry, "AggrAdd"); NDecimal::RegisterAggregateFunctionForAllPrecisions(registry, "AggrAdd_"); } } // namespace NMiniKQL } // namespace NKikimr