#include "mkql_builtins_decimal.h" // Y_IGNORE #include "mkql_builtins_compare.h" #include namespace NKikimr { namespace NMiniKQL { namespace { template ::value>* = nullptr> inline T Max(T l, T r) { return std::max(l, r); } template ::value>* = nullptr> inline T Max(T l, T r) { return std::fmax(l, r); } template struct TMax : public TSimpleArithmeticBinary> { static TOutput Do(TLeft left, TRight right) { return Max(left, right); } #ifndef MKQL_DISABLE_CODEGEN static Value* Gen(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block) { if constexpr (std::is_floating_point()) { auto& context = ctx.Codegen.GetContext(); auto& module = ctx.Codegen.GetModule(); const auto fnType = FunctionType::get(GetTypeFor(context), {left->getType(), right->getType()}, false); const auto& name = GetFuncNameForType("llvm.maxnum"); const auto func = module.getOrInsertFunction(name, fnType).getCallee(); const auto res = CallInst::Create(fnType, func, {left, right}, "maxnum", block); return res; } else { const auto check = CmpInst::Create(Instruction::ICmp, std::is_signed() ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_ULT, left, right, "less", block); const auto res = SelectInst::Create(check, right, left, "max", block); return res; } } #endif }; template struct TFloatAggrMax : public TSimpleArithmeticBinary> { static TType Do(TType left, TType right) { return left > right || std::isnan(left) ? left : right; } #ifndef MKQL_DISABLE_CODEGEN static Value* Gen(Value* left, Value* right, const TCodegenContext&, BasicBlock*& block) { const auto ugt = CmpInst::Create(Instruction::FCmp, FCmpInst::FCMP_UGT, left, right, "greater", block); const auto ord = CmpInst::Create(Instruction::FCmp, FCmpInst::FCMP_ORD, ConstantFP::get(right->getType(), 0.0), right, "ordered", block); const auto both = BinaryOperator::CreateAnd(ugt, ord, "and", block); return SelectInst::Create(both, left, right, "max", block); } #endif }; template using TAggrMax = std::conditional_t::value, TFloatAggrMax, TMax>; template struct TTzMax : public TSelectArithmeticBinaryCopyTimezone> { static bool Do(TType left, TType right) { return left >= right; } #ifndef MKQL_DISABLE_CODEGEN static Value* Gen(Value* left, Value* right, const TCodegenContext&, BasicBlock*& block) { return CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_UGE, left, right, "greater_or_equal", block); } #endif }; template struct TAggrTzMax : public TSelectArithmeticBinaryWithTimezone> { static bool Do(TType left, TType right) { return left >= right; } static bool DoTz(ui16 left, ui16 right) { return left >= right; } #ifndef MKQL_DISABLE_CODEGEN static Value* Gen(Value* left, Value* right, const TCodegenContext&, BasicBlock*& block) { return CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_UGE, left, right, "greater_value", block); } static Value* GenTz(Value* left, Value* right, const TCodegenContext&, BasicBlock*& block) { return CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_UGE, left, right, "greater_timezone", block); } #endif }; template struct TDecimalMax { static NUdf::TUnboxedValuePod Execute(const NUdf::TUnboxedValuePod& left, const NUdf::TUnboxedValuePod& right) { const auto rv = right.GetInt128(); if (!NYql::NDecimal::IsComparable(rv)) return left; const auto lv = left.GetInt128(); if (!NYql::NDecimal::IsComparable(lv)) return right; return NUdf::TUnboxedValuePod(lv < rv ? rv : lv); } #ifndef MKQL_DISABLE_CODEGEN static Value* Generate(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block) { auto& context = ctx.Codegen.GetContext(); const auto next = BasicBlock::Create(context, "next", ctx.Func); const auto good = BasicBlock::Create(context, "good", ctx.Func); const auto done = BasicBlock::Create(context, "done", ctx.Func); const auto result = PHINode::Create(Type::getInt128Ty(context), 3, "result", done); const auto r = GetterForInt128(right, block); const auto rok = NDecimal::GenIsComparable(r, context, block); result->addIncoming(left, block); BranchInst::Create(next, done, rok, block); block = next; const auto l = GetterForInt128(left, block); const auto lok = NDecimal::GenIsComparable(l, context, block); result->addIncoming(right, block); BranchInst::Create(good, done, lok, block); block = good; const auto less = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLT, l, r, "less", block); const auto res = SelectInst::Create(less, right, left, "max", block); result->addIncoming(res, block); BranchInst::Create(done, block); block = done; return result; } #endif }; template struct TDecimalAggrMax { static NUdf::TUnboxedValuePod Execute(const NUdf::TUnboxedValuePod& left, const NUdf::TUnboxedValuePod& right) { const auto lv = left.GetInt128(); const auto rv = right.GetInt128(); return lv > rv ? left : right; } #ifndef MKQL_DISABLE_CODEGEN static Value* Generate(Value* left, Value* right, const TCodegenContext&, BasicBlock*& block) { const auto l = GetterForInt128(left, block); const auto r = GetterForInt128(right, block); const auto greater = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SGT, l, r, "greater", block); const auto res = SelectInst::Create(greater, left, right, "max", block); return res; } #endif }; template struct TCustomMax { static NUdf::TUnboxedValuePod Execute(NUdf::TUnboxedValuePod left, NUdf::TUnboxedValuePod right) { const bool r = CompareCustoms(left, right) < 0; (r ? left : right).DeleteUnreferenced(); return r ? right : left; } #ifndef MKQL_DISABLE_CODEGEN static Value* Generate(Value* left, Value* right, const TCodegenContext& ctx, BasicBlock*& block) { auto& context = ctx.Codegen.GetContext(); const auto res = CallBinaryUnboxedValueFunction(&CompareCustoms, Type::getInt32Ty(context), left, right, ctx.Codegen, block); const auto comp = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLT, res, ConstantInt::get(res->getType(), 0), "less", block); const auto min = SelectInst::Create(comp, left, right, "min", block); ValueCleanup(EValueRepresentation::String, min, ctx, block); const auto max = SelectInst::Create(comp, right, left, "max", block); return max; } #endif }; } void RegisterMax(IBuiltinFunctionRegistry& registry) { RegisterBinaryNumericFunctionOpt(registry, "Max"); RegisterBooleanSameTypesFunction(registry, "Max"); RegisterDatetimeSameTypesFunction(registry, "Max"); RegisterTzDatetimeSameTypesFunction(registry, "Max"); RegisterCustomSameTypesFunction, TDecimalMax, TBinaryArgsOpt>(registry, "Max"); RegisterCustomSameTypesFunction, TCustomMax, TBinaryArgsOpt>(registry, "Max"); RegisterCustomSameTypesFunction, TCustomMax, TBinaryArgsOpt>(registry, "Max"); } void RegisterAggrMax(IBuiltinFunctionRegistry& registry) { RegisterNumericAggregateFunction(registry, "AggrMax"); RegisterBooleanAggregateFunction(registry, "AggrMax"); RegisterDatetimeAggregateFunction(registry, "AggrMax"); RegisterBigDateAggregateFunction(registry, "AggrMax"); RegisterTzDatetimeAggregateFunction(registry, "AggrMax"); RegisterCustomAggregateFunction, TDecimalAggrMax, TBinaryArgsSameOpt>(registry, "AggrMax"); RegisterCustomAggregateFunction, TCustomMax, TBinaryArgsSameOpt>(registry, "AggrMax"); RegisterCustomAggregateFunction, TCustomMax, TBinaryArgsSameOpt>(registry, "AggrMax"); } } // namespace NMiniKQL } // namespace NKikimr