#include "mkql_builtins_impl.h" // Y_IGNORE #include "mkql_builtins_datetime.h" #include namespace NKikimr { namespace NMiniKQL { namespace { template struct TMul : 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_floating_point() ? BinaryOperator::CreateFMul(left, right, "mul", block): BinaryOperator::CreateMul(left, right, "mul", block); } #endif }; template struct TNumMulInterval { static_assert(TOutput::Features & NYql::NUdf::TimeIntervalType, "Output must be interval type"); static_assert(std::is_integral_v, "Left must be integral"); static_assert(std::is_integral_v, "Right 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 = static_cast(left.template Get()); const auto rv = static_cast(right.template Get()); const auto ret = lv * rv; if (rv == 0 || lv == 0) { return NUdf::TUnboxedValuePod(ret); } i64 i64Max = std::numeric_limits::max(); if constexpr (std::is_same_v) { if (left.Get() >= static_cast(i64Max)) { return NUdf::TUnboxedValuePod(); } } if constexpr (std::is_same_v) { if (right.Get() >= static_cast(i64Max)) { return NUdf::TUnboxedValuePod(); } } auto div = i64Max / rv; auto divAbs = (div >= 0) ? div : -div; if ((lv >= 0) ? (lv > divAbs) : (lv < -divAbs)) { return NUdf::TUnboxedValuePod(); } 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 bbMain = BasicBlock::Create(context, "bbMain", ctx.Func); const auto bbDone = BasicBlock::Create(context, "bbDone", ctx.Func); const auto resultType = Type::getInt128Ty(context); const auto result = PHINode::Create(resultType, 2, "result", bbDone); const auto lv = GetterFor(left, context, block); const auto lhs = StaticCast(lv, context, block); const auto rv = GetterFor(right, context, block); const auto rhs = StaticCast(rv, context, block); const auto mul = BinaryOperator::CreateMul(lhs, rhs, "mul", block); const auto zero = ConstantInt::get(Type::getInt64Ty(context), 0); const auto lhsZero = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, lhs, zero, "lhsZero", block); const auto rhsZero = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, rhs, zero, "rhsZero", block); const auto res = SetterFor(mul, context, block); BranchInst::Create(bbDone, bbMain, BinaryOperator::CreateOr(lhsZero, rhsZero, "mulZero", block), block); result->addIncoming(res, block); block = bbMain; const auto i64Max = ConstantInt::get(Type::getInt64Ty(context), std::numeric_limits::max()); const auto div = BinaryOperator::CreateSDiv(i64Max, rhs, "div", block); const auto divAbs = SelectInst::Create( CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGE, div, zero, "divPos", block), div, BinaryOperator::CreateNeg(div, "divNeg", block), "divAbs", block); const auto divAbsNeg = BinaryOperator::CreateNeg(divAbs, "divAbsNeg", block); const auto mulOverflow = SelectInst::Create( CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGE, lhs, zero, "lhsPos", block), CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGT, lhs, divAbs, "lhsDiv", block), CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLT, lhs, divAbsNeg, "lhsDivAbsNeg", block), "mulOverflow", block); const auto i64Overflow = BinaryOperator::CreateOr( GenIsInt64Overflow(lv, context, block), GenIsInt64Overflow(rv, context, block), "i64Overflow", block); const auto bad = BinaryOperator::CreateOr( BinaryOperator::CreateOr(i64Overflow, mulOverflow, "overflow", block), GenIsBadInterval(mul, context, block), "bad", block); const auto null = ConstantInt::get(resultType, 0); const auto sel = SelectInst::Create(bad, null, res, "sel", block); result->addIncoming(sel, block); BranchInst::Create(bbDone, block); block = bbDone; return result; } #endif }; } template void RegisterIntervalMul(IBuiltinFunctionRegistry& registry) { RegisterFunctionBinPolyOpt, TInterval, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); RegisterFunctionBinPolyOpt, TInterval, TNumMulInterval, TBinaryArgsOptWithNullableResult>(registry, "Mul"); } void RegisterMul(IBuiltinFunctionRegistry& registry) { RegisterBinaryNumericFunctionOpt(registry, "Mul"); RegisterIntervalMul>(registry); RegisterIntervalMul>(registry); } template void RegisterIntervalMul(TKernelFamilyBase& owner) { AddBinaryKernelPoly, TInterval, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TNumMulInterval>(owner); AddBinaryKernelPoly, TInterval, TNumMulInterval>(owner); } void RegisterMul(TKernelFamilyMap& kernelFamilyMap) { auto family = std::make_unique(); AddBinaryIntegralKernels(*family); AddBinaryRealKernels(*family); RegisterIntervalMul>(*family); RegisterIntervalMul>(*family); kernelFamilyMap["Mul"] = std::move(family); } } // namespace NMiniKQL } // namespace NKikimr