mkql_builtins_mod.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. #include "mkql_builtins_impl.h" // Y_IGNORE
  2. #include <cmath>
  3. namespace NKikimr {
  4. namespace NMiniKQL {
  5. namespace {
  6. template<typename TLeft, typename TRight, typename TOutput>
  7. struct TMod : public TSimpleArithmeticBinary<TLeft, TRight, TOutput, TMod<TLeft, TRight, TOutput>> {
  8. static_assert(std::is_floating_point<TOutput>::value, "expected floating point");
  9. static constexpr auto NullMode = TKernel::ENullMode::Default;
  10. static TOutput Do(TOutput left, TOutput right)
  11. {
  12. return std::fmod(left, right);
  13. }
  14. #ifndef MKQL_DISABLE_CODEGEN
  15. static Value* Gen(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block)
  16. {
  17. ctx.Codegen.AddGlobalMapping("fmod", reinterpret_cast<const void*>(static_cast<double(*)(double, double)>(&std::fmod)));
  18. ctx.Codegen.AddGlobalMapping("fmodf", reinterpret_cast<const void*>(static_cast<float(*)(float, float)>(&std::fmod)));
  19. return BinaryOperator::CreateFRem(left, right, "frem", block);
  20. }
  21. #endif
  22. };
  23. template <typename TLeft, typename TRight, typename TOutput>
  24. struct TIntegralMod {
  25. static_assert(std::is_integral<TOutput>::value, "integral type expected");
  26. static constexpr auto NullMode = TKernel::ENullMode::AlwaysNull;
  27. static NUdf::TUnboxedValuePod Execute(const NUdf::TUnboxedValuePod& left, const NUdf::TUnboxedValuePod& right)
  28. {
  29. const auto lv = static_cast<TOutput>(left.template Get<TLeft>());
  30. const auto rv = static_cast<TOutput>(right.template Get<TRight>());
  31. if (rv == 0 ||
  32. (std::is_signed<TOutput>::value && sizeof(TOutput) <= sizeof(TLeft) && rv == TOutput(-1) && lv == Min<TOutput>()))
  33. {
  34. return NUdf::TUnboxedValuePod();
  35. }
  36. return NUdf::TUnboxedValuePod(lv % rv);
  37. }
  38. #ifndef MKQL_DISABLE_CODEGEN
  39. static Value* Generate(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block)
  40. {
  41. auto& context = ctx.Codegen.GetContext();
  42. const auto lv = StaticCast<TLeft, TOutput>(GetterFor<TLeft>(left, context, block), context, block);
  43. const auto rv = StaticCast<TRight, TOutput>(GetterFor<TRight>(right, context, block), context, block);
  44. const auto type = Type::getInt128Ty(context);
  45. const auto zero = ConstantInt::get(type, 0);
  46. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, rv, ConstantInt::get(rv->getType(), 0), "check", block);
  47. const auto done = BasicBlock::Create(context, "done", ctx.Func);
  48. const auto good = BasicBlock::Create(context, "good", ctx.Func);
  49. const auto result = PHINode::Create(type, 2, "result", done);
  50. result->addIncoming(zero, block);
  51. if constexpr (std::is_signed<TOutput>() && sizeof(TOutput) <= sizeof(TLeft)) {
  52. const auto min = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, lv, ConstantInt::get(lv->getType(), Min<TOutput>()), "min", block);
  53. const auto one = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, rv, ConstantInt::get(rv->getType(), -1), "one", block);
  54. const auto two = BinaryOperator::CreateAnd(min, one, "two", block);
  55. const auto all = BinaryOperator::CreateOr(check, two, "all", block);
  56. BranchInst::Create(done, good, all, block);
  57. } else {
  58. BranchInst::Create(done, good, check, block);
  59. }
  60. block = good;
  61. const auto rem = std::is_signed<TOutput>() ? BinaryOperator::CreateSRem(lv, rv, "rem", block) : BinaryOperator::CreateURem(lv, rv, "rem", block);
  62. const auto full = SetterFor<TOutput>(rem, context, block);
  63. result->addIncoming(full, block);
  64. BranchInst::Create(done, block);
  65. block = done;
  66. return result;
  67. }
  68. #endif
  69. };
  70. }
  71. void RegisterMod(IBuiltinFunctionRegistry& registry) {
  72. RegisterBinaryRealFunctionOpt<TMod, TBinaryArgsOpt>(registry, "Mod");
  73. RegisterBinaryIntegralFunctionOpt<TIntegralMod, TBinaryArgsOptWithNullableResult>(registry, "Mod");
  74. }
  75. void RegisterMod(TKernelFamilyMap& kernelFamilyMap) {
  76. kernelFamilyMap["Mod"] = std::make_unique<TBinaryNumericKernelFamily<TIntegralMod, TMod>>();
  77. }
  78. } // namespace NMiniKQL
  79. } // namespace NKikimr