mkql_tostring.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. #include "mkql_tostring.h"
  2. #include <yql/essentials/minikql/computation/mkql_computation_node_holders.h>
  3. #include <yql/essentials/minikql/computation/mkql_computation_node_codegen.h> // Y_IGNORE
  4. #include <yql/essentials/minikql/mkql_node_cast.h>
  5. #include <yql/essentials/minikql/mkql_node_builder.h>
  6. #include <yql/essentials/minikql/mkql_type_ops.h>
  7. #include <yql/essentials/minikql/mkql_string_util.h>
  8. #include <yql/essentials/public/decimal/yql_decimal.h>
  9. #include <yql/essentials/public/udf/udf_terminator.h>
  10. #ifndef MKQL_DISABLE_CODEGEN
  11. Y_PRAGMA_DIAGNOSTIC_PUSH
  12. Y_PRAGMA("GCC diagnostic ignored \"-Wreturn-type-c-linkage\"")
  13. extern "C" NYql::NUdf::TUnboxedValuePod DataToString(NYql::NUdf::TUnboxedValuePod data, NYql::NUdf::EDataSlot slot) {
  14. return NKikimr::NMiniKQL::ValueToString(slot, data);
  15. }
  16. extern "C" NYql::NUdf::TUnboxedValuePod DecimalToString(NYql::NDecimal::TInt128 decimal, ui8 precision, ui8 scale) {
  17. if (const auto str = NYql::NDecimal::ToString(decimal, precision, scale)) {
  18. return NKikimr::NMiniKQL::MakeString(NYql::NUdf::TStringRef(str, std::strlen(str)));
  19. }
  20. return NYql::NUdf::TUnboxedValuePod();
  21. }
  22. Y_PRAGMA_DIAGNOSTIC_POP
  23. #endif
  24. namespace NKikimr {
  25. namespace NMiniKQL {
  26. namespace {
  27. template <bool IsOptional>
  28. class TDecimalToStringWrapper : public TMutableCodegeneratorNode<TDecimalToStringWrapper<IsOptional>> {
  29. typedef TMutableCodegeneratorNode<TDecimalToStringWrapper<IsOptional>> TBaseComputation;
  30. public:
  31. TDecimalToStringWrapper(TComputationMutables& mutables, IComputationNode* data, ui8 precision, ui8 scale)
  32. : TBaseComputation(mutables, EValueRepresentation::String)
  33. , Data(data)
  34. , Precision(precision)
  35. , Scale(scale)
  36. {
  37. MKQL_ENSURE(precision > 0 && precision <= NYql::NDecimal::MaxPrecision, "Wrong precision.");
  38. MKQL_ENSURE(scale <= precision, "Wrong scale.");
  39. }
  40. NUdf::TUnboxedValuePod DoCalculate(TComputationContext& ctx) const {
  41. const auto& dataValue = Data->GetValue(ctx);
  42. if (IsOptional && !dataValue) {
  43. return NUdf::TUnboxedValuePod();
  44. }
  45. if (const auto str = NYql::NDecimal::ToString(dataValue.GetInt128(), Precision, Scale)) {
  46. return MakeString(NUdf::TStringRef(str, std::strlen(str)));
  47. }
  48. Throw();
  49. }
  50. #ifndef MKQL_DISABLE_CODEGEN
  51. Value* DoGenerateGetValue(const TCodegenContext& ctx, BasicBlock*& block) const {
  52. auto& context = ctx.Codegen.GetContext();
  53. const auto valType = Type::getInt128Ty(context);
  54. const auto psType = Type::getInt8Ty(context);
  55. const auto valTypePtr = PointerType::getUnqual(valType);
  56. const auto name = "DecimalToString";
  57. ctx.Codegen.AddGlobalMapping(name, reinterpret_cast<const void*>(&DecimalToString));
  58. const auto fnType = NYql::NCodegen::ETarget::Windows != ctx.Codegen.GetEffectiveTarget() ?
  59. FunctionType::get(valType, { valType, psType, psType }, false):
  60. FunctionType::get(Type::getVoidTy(context), { valTypePtr, valTypePtr, psType, psType }, false);
  61. const auto func = ctx.Codegen.GetModule().getOrInsertFunction(name, fnType);
  62. const auto fail = BasicBlock::Create(context, "fail", ctx.Func);
  63. const auto nice = BasicBlock::Create(context, "nice", ctx.Func);
  64. const auto zero = ConstantInt::get(valType, 0ULL);
  65. const auto precision = ConstantInt::get(psType, Precision);
  66. const auto scale = ConstantInt::get(psType, Scale);
  67. const auto value = GetNodeValue(Data, ctx, block);
  68. Value* result;
  69. if constexpr (IsOptional) {
  70. const auto call = BasicBlock::Create(context, "call", ctx.Func);
  71. const auto res = PHINode::Create(valType, 2, "result", nice);
  72. res->addIncoming(zero, block);
  73. result = res;
  74. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, value, zero, "check", block);
  75. BranchInst::Create(nice, call, check, block);
  76. block = call;
  77. Value* string;
  78. if (NYql::NCodegen::ETarget::Windows != ctx.Codegen.GetEffectiveTarget()) {
  79. string = CallInst::Create(func, { GetterForInt128(value, block), precision, scale }, "to_string", block);
  80. } else {
  81. const auto retPtr = new AllocaInst(valType, 0U, "ret_ptr", block);
  82. new StoreInst(GetterForInt128(value, block), retPtr, block);
  83. CallInst::Create(func, { retPtr, retPtr, precision, scale }, "", block);
  84. string = new LoadInst(valType, retPtr, "res", block);
  85. }
  86. res->addIncoming(string, block);
  87. const auto test = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, string, zero, "test", block);
  88. BranchInst::Create(fail, nice, test, block);
  89. } else {
  90. if (NYql::NCodegen::ETarget::Windows != ctx.Codegen.GetEffectiveTarget()) {
  91. result = CallInst::Create(func, { GetterForInt128(value, block), precision, scale }, "to_string", block);
  92. } else {
  93. const auto retPtr = new AllocaInst(valType, 0U, "ret_ptr", block);
  94. new StoreInst(GetterForInt128(value, block), retPtr, block);
  95. CallInst::Create(func, { retPtr, retPtr, precision, scale }, "", block);
  96. result = new LoadInst(valType, retPtr, "res", block);
  97. }
  98. const auto test = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, result, zero, "test", block);
  99. BranchInst::Create(fail, nice, test, block);
  100. }
  101. block = fail;
  102. const auto doFunc = ConstantInt::get(Type::getInt64Ty(context), GetMethodPtr(&TDecimalToStringWrapper::Throw));
  103. const auto doFuncType = FunctionType::get(Type::getVoidTy(context), {}, false);
  104. const auto doFuncPtr = CastInst::Create(Instruction::IntToPtr, doFunc, PointerType::getUnqual(doFuncType), "thrower", block);
  105. CallInst::Create(doFuncType, doFuncPtr, {}, "", block);
  106. new UnreachableInst(context, block);
  107. block = nice;
  108. return result;
  109. }
  110. #endif
  111. private:
  112. void RegisterDependencies() const final {
  113. this->DependsOn(Data);
  114. }
  115. [[noreturn]] static void Throw() {
  116. UdfTerminate("Ivalid Decimal value.");
  117. }
  118. IComputationNode* const Data;
  119. const ui8 Precision, Scale;
  120. };
  121. template <bool IsOptional>
  122. class TToStringWrapper : public TMutableCodegeneratorNode<TToStringWrapper<IsOptional>> {
  123. typedef TMutableCodegeneratorNode<TToStringWrapper<IsOptional>> TBaseComputation;
  124. public:
  125. TToStringWrapper(TComputationMutables& mutables, IComputationNode* data, NUdf::TDataTypeId schemeType)
  126. : TBaseComputation(mutables, EValueRepresentation::String)
  127. , Data(data)
  128. , SchemeType(NUdf::GetDataSlot(schemeType))
  129. {}
  130. NUdf::TUnboxedValue DoCalculate(TComputationContext& ctx) const {
  131. const auto& dataValue = Data->GetValue(ctx);
  132. if (IsOptional && !dataValue) {
  133. return NUdf::TUnboxedValuePod();
  134. }
  135. return ValueToString(SchemeType, dataValue);
  136. }
  137. #ifndef MKQL_DISABLE_CODEGEN
  138. Value* DoGenerateGetValue(const TCodegenContext& ctx, BasicBlock*& block) const {
  139. auto& context = ctx.Codegen.GetContext();
  140. const auto valType = Type::getInt128Ty(context);
  141. const auto slotType = Type::getInt32Ty(context);
  142. const auto valTypePtr = PointerType::getUnqual(valType);
  143. const auto name = "DataToString";
  144. ctx.Codegen.AddGlobalMapping(name, reinterpret_cast<const void*>(&DataToString));
  145. const auto fnType = NYql::NCodegen::ETarget::Windows != ctx.Codegen.GetEffectiveTarget() ?
  146. FunctionType::get(valType, { valType, slotType }, false):
  147. FunctionType::get(Type::getVoidTy(context), { valTypePtr, valTypePtr, slotType }, false);
  148. const auto func = ctx.Codegen.GetModule().getOrInsertFunction(name, fnType);
  149. const auto zero = ConstantInt::get(valType, 0ULL);
  150. const auto slot = ConstantInt::get(slotType, static_cast<ui32>(SchemeType));
  151. const auto value = GetNodeValue(Data, ctx, block);
  152. if constexpr (IsOptional) {
  153. const auto done = BasicBlock::Create(context, "done", ctx.Func);
  154. const auto call = BasicBlock::Create(context, "call", ctx.Func);
  155. const auto result = PHINode::Create(valType, 2, "result", done);
  156. result->addIncoming(zero, block);
  157. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, value, zero, "check", block);
  158. BranchInst::Create(done, call, check, block);
  159. block = call;
  160. Value* string;
  161. if (NYql::NCodegen::ETarget::Windows != ctx.Codegen.GetEffectiveTarget()) {
  162. string = CallInst::Create(func, { value, slot }, "to_string", block);
  163. } else {
  164. const auto retPtr = new AllocaInst(valType, 0U, "ret_ptr", block);
  165. new StoreInst(value, retPtr, block);
  166. CallInst::Create(func, { retPtr, retPtr, slot }, "", block);
  167. string = new LoadInst(valType, retPtr, "res", block);
  168. }
  169. if (Data->IsTemporaryValue())
  170. ValueCleanup(Data->GetRepresentation(), value, ctx, block);
  171. result->addIncoming(string, block);
  172. BranchInst::Create(done, block);
  173. block = done;
  174. return result;
  175. } else {
  176. Value* string;
  177. if (NYql::NCodegen::ETarget::Windows != ctx.Codegen.GetEffectiveTarget()) {
  178. string = CallInst::Create(func, { value, slot }, "to_string", block);
  179. } else {
  180. const auto retPtr = new AllocaInst(valType, 0U, "ret_ptr", block);
  181. new StoreInst(value, retPtr, block);
  182. CallInst::Create(func, { retPtr, retPtr, slot}, "", block);
  183. string = new LoadInst(valType, retPtr, "res", block);
  184. }
  185. if (Data->IsTemporaryValue())
  186. ValueCleanup(Data->GetRepresentation(), value, ctx, block);
  187. return string;
  188. }
  189. }
  190. #endif
  191. private:
  192. void RegisterDependencies() const final {
  193. this->DependsOn(Data);
  194. }
  195. IComputationNode* const Data;
  196. const NUdf::EDataSlot SchemeType;
  197. };
  198. class TAsIsWrapper : public TDecoratorCodegeneratorNode<TAsIsWrapper> {
  199. using TBaseComputation = TDecoratorCodegeneratorNode<TAsIsWrapper>;
  200. public:
  201. TAsIsWrapper(IComputationNode* optional)
  202. : TBaseComputation(optional)
  203. {}
  204. NUdf::TUnboxedValuePod DoCalculate(TComputationContext&, const NUdf::TUnboxedValuePod& value) const { return value; }
  205. #ifndef MKQL_DISABLE_CODEGEN
  206. Value* DoGenerateGetValue(const TCodegenContext&, Value* value, BasicBlock*&) const { return value; }
  207. #endif
  208. };
  209. }
  210. IComputationNode* WrapToString(TCallable& callable, const TComputationNodeFactoryContext& ctx) {
  211. MKQL_ENSURE(callable.GetInputsCount() == 1, "Expected 1 arg");
  212. bool isOptional;
  213. const auto dataType = UnpackOptionalData(callable.GetInput(0), isOptional);
  214. const auto schemeType = dataType->GetSchemeType();
  215. const auto data = LocateNode(ctx.NodeLocator, callable, 0);
  216. if (NUdf::EDataTypeFeatures::StringType & NUdf::GetDataTypeInfo(NUdf::GetDataSlot(schemeType)).Features) {
  217. return new TAsIsWrapper(data);
  218. } else if (NUdf::TDataType<NUdf::TDecimal>::Id == schemeType) {
  219. const auto& params = static_cast<TDataDecimalType*>(dataType)->GetParams();
  220. if (isOptional) {
  221. return new TDecimalToStringWrapper<true>(ctx.Mutables, data, params.first, params.second);
  222. } else {
  223. return new TDecimalToStringWrapper<false>(ctx.Mutables, data, params.first, params.second);
  224. }
  225. } else {
  226. if (isOptional) {
  227. return new TToStringWrapper<true>(ctx.Mutables, data, schemeType);
  228. } else {
  229. return new TToStringWrapper<false>(ctx.Mutables, data, schemeType);
  230. }
  231. }
  232. }
  233. }
  234. }