#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 TSub : 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::CreateSub(left, right, "sub", block) : BinaryOperator::CreateFSub(left, right, "sub", block); } #endif }; template struct TDecimalSub { static NUdf::TUnboxedValuePod Execute(const NUdf::TUnboxedValuePod& left, const NUdf::TUnboxedValuePod& right) { const auto l = left.GetInt128(); const auto r = right.GetInt128(); const auto s = l - r; using namespace NYql::NDecimal; if (IsNormal(l) && IsNormal(r) && IsNormal(s)) return NUdf::TUnboxedValuePod(s); if (IsNan(l) || IsNan(r) || !s) return NUdf::TUnboxedValuePod(Nan()); else return NUdf::TUnboxedValuePod(s > 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 sub = BinaryOperator::CreateSub(l, r, "sub", 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 sok = NDecimal::GenInBounds(sub, bounds.first, bounds.second, block); const auto bok = BinaryOperator::CreateAnd(lok, rok, "bok", block); const auto ok = BinaryOperator::CreateAnd(sok, 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(sub->getType(), 3, "result", done); result->addIncoming(sub, 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(sub->getType(), 0); const auto zero = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, sub, 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, sub, 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 TDateTimeSub : public TSimpleArithmeticBinary, true>{ static constexpr auto NullMode = TKernel::ENullMode::Default; static_assert(TOutput::Features & NYql::NUdf::TimeIntervalType, "Interval type expected"); static typename TOutput::TLayout Do(typename TLeft::TLayout left, typename TRight::TLayout right) { return ToScaledDate(left) - ToScaledDate(right); } #ifndef MKQL_DISABLE_CODEGEN static Value* Gen(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block) { auto& context = ctx.Codegen.GetContext(); const auto lhs = GenToScaledDate(left, context, block); const auto rhs = GenToScaledDate(right, context, block); const auto sub = BinaryOperator::CreateSub(lhs, rhs, "sub", block); return sub; } #endif }; template struct TIntervalSubInterval { static_assert(TLeft::Features & NYql::NUdf::TimeIntervalType, "Left must be interval type"); static_assert(TRight::Features & NYql::NUdf::TimeIntervalType, "Right must be interval type"); static_assert(TOutput::Features & NYql::NUdf::TimeIntervalType, "Output must be interval type"); static constexpr auto NullMode = TKernel::ENullMode::AlwaysNull; static NUdf::TUnboxedValuePod Execute(const NUdf::TUnboxedValuePod& left, const NUdf::TUnboxedValuePod& right) { const auto lv = left.template Get(); const auto rv = right.template Get(); const auto ret = lv - rv; return IsBadInterval(ret) ? NUdf::TUnboxedValuePod() : 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 sub = BinaryOperator::CreateSub(lhs, rhs, "sub", block); const auto full = SetterFor(sub, context, block); const auto bad = GenIsBadInterval(sub, context, block); const auto zero = ConstantInt::get(Type::getInt128Ty(context), 0); const auto sel = SelectInst::Create(bad, zero, full, "sel", block); return sel; } #endif }; template struct TBigIntervalSub { 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 sub = BinaryOperator::CreateSub(lhs, rhs, "sub", block); const auto wide = SetterFor(sub, context, block); const auto lneg = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLT, lhs, ConstantInt::get(lhs->getType(), 0), "lneg", block); const auto rpos = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGT, rhs, ConstantInt::get(rhs->getType(), 0), "rpos", block); const auto apos = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGT, sub, ConstantInt::get(sub->getType(), 0), "apos", block); const auto npp = BinaryOperator::CreateAnd(apos, BinaryOperator::CreateAnd(lneg, rpos, "np", block), "npp", block); const auto lpos = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGT, lhs, ConstantInt::get(lhs->getType(), 0), "lpos", block); const auto rneg = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLT, rhs, ConstantInt::get(rhs->getType(), 0), "rneg", block); const auto aneg = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLT, sub, ConstantInt::get(sub->getType(), 0), "aneg", block); const auto pnn = BinaryOperator::CreateAnd(aneg, BinaryOperator::CreateAnd(lpos, rneg, "pn", block), "pnn", block); const auto bad = BinaryOperator::CreateOr( BinaryOperator::CreateOr(npp, pnn, "overflow", block), GenIsBadInterval>(sub, 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 struct TAnyDateTimeSubIntervalT { static_assert(TRight::Features & NYql::NUdf::TimeIntervalType, "right must be interval type"); static_assert(std::is_same::value, "left and output must be same"); 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 (IsBadDateTime(ret)) { return NUdf::TUnboxedValuePod(); } auto data = NUdf::TUnboxedValuePod(FromScaledDate(ret)); if (Tz) { data.SetTimezoneId(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 sub = BinaryOperator::CreateSub(lhs, rhs, "sub", block); const auto wide = SetterFor(GenFromScaledDate(sub, context, block), context, block); const auto bad = GenIsBadDateTime(sub, context, block); const auto type = Type::getInt128Ty(context); const auto zero = ConstantInt::get(type, 0); if (Tz) { const uint64_t init[] = {0ULL, 0xFFFFULL}; const auto mask = ConstantInt::get(type, APInt(128, 2, init)); const auto tzid = BinaryOperator::CreateAnd(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 using TAnyDateTimeSubInterval = TAnyDateTimeSubIntervalT; template using TAnyDateTimeSubIntervalTz = TAnyDateTimeSubIntervalT; } template void RegisterDateSub(IBuiltinFunctionRegistry& registry) { 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 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>>; using TOutput = std::conditional_t, NUdf::TDataType>; RegisterFunctionBinPolyOpt(registry, "Sub"); RegisterFunctionBinPolyOpt(registry, "Sub"); RegisterFunctionBinPolyOpt(registry, "Sub"); RegisterFunctionBinPolyOpt(registry, "Sub"); RegisterFunctionBinPolyOpt(registry, "Sub"); RegisterFunctionBinPolyOpt(registry, "Sub"); RegisterFunctionBinPolyOpt(registry, "Sub"); RegisterFunctionBinPolyOpt(registry, "Sub"); RegisterFunctionBinPolyOpt(registry, "Sub"); } template void RegisterDateSub(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 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>>; using TOutput = std::conditional_t, NUdf::TDataType>; AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); } void RegisterSub(IBuiltinFunctionRegistry& registry) { RegisterBinaryNumericFunctionOpt(registry, "Sub"); NDecimal::RegisterBinaryFunctionForAllPrecisions(registry, "Sub_"); RegisterDateSub(registry); RegisterDateSub(registry); RegisterDateSub(registry); RegisterDateSub(registry); // NarrowDate minus BigDate RegisterDateSub(registry); RegisterDateSub(registry); RegisterDateSub(registry); RegisterDateSub(registry); // BigDate minus NarrowDate RegisterDateSub(registry); RegisterDateSub(registry); RegisterDateSub(registry); RegisterDateSub(registry); // BigDate minus BigDate RegisterDateSub(registry); RegisterDateSub(registry); RegisterDateSub(registry); RegisterDateSub(registry); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TIntervalSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TBigIntervalSub, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TBigIntervalSub, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TBigIntervalSub, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubInterval, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); RegisterFunctionBinPolyOpt, NUdf::TDataType, NUdf::TDataType, TAnyDateTimeSubIntervalTz, TBinaryArgsOptWithNullableResult>(registry, "Sub"); } template void RegisterDateSubInterval(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>; AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); AddBinaryKernelPoly(owner); } template void RegisterIntervalSubInterval(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 RegisterSub(TKernelFamilyMap& kernelFamilyMap) { auto family = std::make_unique(); AddBinaryIntegralKernels(*family); AddBinaryRealKernels(*family); AddBinaryDecimalKernels(*family); RegisterDateSub(*family); RegisterDateSub(*family); RegisterDateSub(*family); RegisterDateSub(*family); // NarrowDate minus BigDate RegisterDateSub(*family); RegisterDateSub(*family); RegisterDateSub(*family); RegisterDateSub(*family); // BigDate minus NarrowDate RegisterDateSub(*family); RegisterDateSub(*family); RegisterDateSub(*family); RegisterDateSub(*family); // BigDate minus BigDate RegisterDateSub(*family); RegisterDateSub(*family); RegisterDateSub(*family); RegisterDateSub(*family); RegisterDateSubInterval(*family); RegisterDateSubInterval(*family); RegisterDateSubInterval(*family); RegisterDateSubInterval(*family); RegisterDateSubInterval(*family); RegisterDateSubInterval(*family); RegisterDateSubInterval(*family); RegisterDateSubInterval(*family); RegisterIntervalSubInterval(*family); RegisterIntervalSubInterval(*family); RegisterIntervalSubInterval(*family); RegisterIntervalSubInterval(*family); kernelFamilyMap["Sub"] = std::move(family); } } // namespace NMiniKQL } // namespace NKikimr