#include "mkql_builtins_impl.h" // Y_IGNORE #include namespace NKikimr { namespace NMiniKQL { namespace { template struct TMod : 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 std::fmod(left, right); } #ifndef MKQL_DISABLE_CODEGEN static Value* Gen(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block) { ctx.Codegen.AddGlobalMapping("fmod", reinterpret_cast(static_cast(&std::fmod))); ctx.Codegen.AddGlobalMapping("fmodf", reinterpret_cast(static_cast(&std::fmod))); return BinaryOperator::CreateFRem(left, right, "frem", block); } #endif }; template struct TIntegralMod { 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 rem = std::is_signed() ? BinaryOperator::CreateSRem(lv, rv, "rem", block) : BinaryOperator::CreateURem(lv, rv, "rem", block); const auto full = SetterFor(rem, context, block); result->addIncoming(full, block); BranchInst::Create(done, block); block = done; return result; } #endif }; } void RegisterMod(IBuiltinFunctionRegistry& registry) { RegisterBinaryRealFunctionOpt(registry, "Mod"); RegisterBinaryIntegralFunctionOpt(registry, "Mod"); } void RegisterMod(TKernelFamilyMap& kernelFamilyMap) { kernelFamilyMap["Mod"] = std::make_unique>(); } } // namespace NMiniKQL } // namespace NKikimr