#include "mkql_builtins_impl.h" // Y_IGNORE #include "mkql_builtins_datetime.h" #include namespace NKikimr { namespace NMiniKQL { namespace { template struct TDiv : public TSimpleArithmeticBinary> { static_assert(std::is_floating_point::value, "expected floating point"); 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 BinaryOperator::CreateFDiv(left, right, "div", block); } #endif }; template struct TIntegralDiv { static_assert(std::is_integral::value, "integral type expected"); static constexpr auto NullMode = TKernel::ENullMode::AlwaysNull; static NUdf::TUnboxedValuePod Execute(const NUdf::TUnboxedValuePod& left, const NUdf::TUnboxedValuePod& right) { const auto lv = static_cast(left.template Get()); const auto rv = static_cast(right.template Get()); if (rv == 0 || (std::is_signed::value && sizeof(TOutput) <= sizeof(TLeft) && rv == TOutput(-1) && lv == Min())) { return NUdf::TUnboxedValuePod(); } return NUdf::TUnboxedValuePod(lv / rv); } #ifndef MKQL_DISABLE_CODEGEN static Value* Generate(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block) { auto& context = ctx.Codegen.GetContext(); const auto lv = StaticCast(GetterFor(left, context, block), context, block); const auto rv = StaticCast(GetterFor(right, context, block), context, block); const auto type = Type::getInt128Ty(context); const auto zero = ConstantInt::get(type, 0); const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, rv, ConstantInt::get(rv->getType(), 0), "check", block); const auto done = BasicBlock::Create(context, "done", ctx.Func); const auto good = BasicBlock::Create(context, "good", ctx.Func); const auto result = PHINode::Create(type, 2, "result", done); result->addIncoming(zero, block); if constexpr (std::is_signed() && sizeof(TOutput) <= sizeof(TLeft)) { const auto min = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, lv, ConstantInt::get(lv->getType(), Min()), "min", block); const auto one = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, rv, ConstantInt::get(rv->getType(), -1), "one", block); const auto two = BinaryOperator::CreateAnd(min, one, "two", block); const auto all = BinaryOperator::CreateOr(check, two, "all", block); BranchInst::Create(done, good, all, block); } else { BranchInst::Create(done, good, check, block); } block = good; const auto div = std::is_signed() ? BinaryOperator::CreateSDiv(lv, rv, "div", block) : BinaryOperator::CreateUDiv(lv, rv, "div", block); const auto full = SetterFor(div, context, block); result->addIncoming(full, block); BranchInst::Create(done, block); block = done; return result; } #endif }; template struct TNumDivInterval { static_assert(TLeft::Features & NYql::NUdf::TimeIntervalType, "Left must be interval type"); static_assert(TRight::Features & NYql::NUdf::IntegralType, "Right must be integral type"); static_assert(TOutput::Features & NYql::NUdf::TimeIntervalType, "Output must be interval type"); static_assert(std::is_same_v, "Output layout type must be i64"); static constexpr auto NullMode = TKernel::ENullMode::AlwaysNull; static NUdf::TUnboxedValuePod Execute(const NUdf::TUnboxedValuePod& left, const NUdf::TUnboxedValuePod& right) { if constexpr (std::is_same_v) { if (right.Get() > static_cast(std::numeric_limits::max())) { return NUdf::TUnboxedValuePod(i64(0)); } } const auto lv = static_cast(left.template Get()); const auto rv = static_cast(right.template Get()); if (rv == 0) { return NUdf::TUnboxedValuePod(); } return NUdf::TUnboxedValuePod(lv / rv); } #ifndef MKQL_DISABLE_CODEGEN static Value* Generate(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block) { auto& context = ctx.Codegen.GetContext(); const auto bbMain = BasicBlock::Create(context, "bbMain", ctx.Func); const auto bbDone = BasicBlock::Create(context, "bbDone", ctx.Func); const auto resultType = Type::getInt128Ty(context); const auto null = ConstantInt::get(resultType, 0); const auto result = PHINode::Create(resultType, 3, "result", bbDone); const auto rv = GetterFor(right, context, block); const auto rvZero = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, rv, ConstantInt::get(rv->getType(), 0), "rvZero", block); BranchInst::Create(bbDone, bbMain, rvZero, block); result->addIncoming(null, block); block = bbMain; const auto rvOverflow = GenIsInt64Overflow(rv, context, block); const auto zero = SetterFor( ConstantInt::get(Type::getInt64Ty(context), 0), context, block); const auto lval = StaticCast( GetterFor(left, context, block), context, block); const auto rval = StaticCast( rv, context, block); const auto div = BinaryOperator::CreateSDiv(lval, rval, "div", block); const auto divResult = SetterFor(div, context, block); const auto res = SelectInst::Create(rvOverflow, zero, divResult, "res", block); result->addIncoming(res, block); BranchInst::Create(bbDone, block); block = bbDone; return result; } #endif }; } template void RegisterIntegralDiv(IBuiltinFunctionRegistry& registry) { RegisterFunctionBinPolyOpt, TInterval, TNumDivInterval, TBinaryArgsOptWithNullableResult>(registry, "Div"); RegisterFunctionBinPolyOpt, TInterval, TNumDivInterval, TBinaryArgsOptWithNullableResult>(registry, "Div"); RegisterFunctionBinPolyOpt, TInterval, TNumDivInterval, TBinaryArgsOptWithNullableResult>(registry, "Div"); RegisterFunctionBinPolyOpt, TInterval, TNumDivInterval, TBinaryArgsOptWithNullableResult>(registry, "Div"); RegisterFunctionBinPolyOpt, TInterval, TNumDivInterval, TBinaryArgsOptWithNullableResult>(registry, "Div"); RegisterFunctionBinPolyOpt, TInterval, TNumDivInterval, TBinaryArgsOptWithNullableResult>(registry, "Div"); RegisterFunctionBinPolyOpt, TInterval, TNumDivInterval, TBinaryArgsOptWithNullableResult>(registry, "Div"); RegisterFunctionBinPolyOpt, TInterval, TNumDivInterval, TBinaryArgsOptWithNullableResult>(registry, "Div"); } void RegisterDiv(IBuiltinFunctionRegistry& registry) { RegisterBinaryRealFunctionOpt(registry, "Div"); RegisterBinaryIntegralFunctionOpt(registry, "Div"); RegisterIntegralDiv>(registry); RegisterIntegralDiv>(registry); } template void RegisterIntervalDiv(TKernelFamilyBase& owner) { AddBinaryKernelPoly, TInterval, TNumDivInterval>(owner); AddBinaryKernelPoly, TInterval, TNumDivInterval>(owner); AddBinaryKernelPoly, TInterval, TNumDivInterval>(owner); AddBinaryKernelPoly, TInterval, TNumDivInterval>(owner); AddBinaryKernelPoly, TInterval, TNumDivInterval>(owner); AddBinaryKernelPoly, TInterval, TNumDivInterval>(owner); AddBinaryKernelPoly, TInterval, TNumDivInterval>(owner); AddBinaryKernelPoly, TInterval, TNumDivInterval>(owner); } void RegisterDiv(TKernelFamilyMap& kernelFamilyMap) { auto family = std::make_unique(); AddBinaryIntegralKernels(*family); AddBinaryRealKernels(*family); RegisterIntervalDiv>(*family); RegisterIntervalDiv>(*family); kernelFamilyMap["Div"] = std::move(family); } } // namespace NMiniKQL } // namespace NKikimr