mkql_opt_literal.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. #include "mkql_opt_literal.h"
  2. #include "mkql_node_cast.h"
  3. #include "mkql_node_builder.h"
  4. #include "mkql_node_visitor.h"
  5. #include "mkql_program_builder.h"
  6. #include "mkql_node_printer.h"
  7. #include <library/cpp/containers/stack_vector/stack_vec.h>
  8. #include <util/generic/singleton.h>
  9. namespace NKikimr {
  10. namespace NMiniKQL {
  11. using namespace NDetail;
  12. namespace {
  13. TNode* LiteralAddMember(
  14. const TStructLiteral& oldStruct,
  15. const TStructType& newStructType,
  16. TRuntimeNode newMember,
  17. TRuntimeNode position,
  18. const TTypeEnvironment& env)
  19. {
  20. TStructLiteralBuilder resultBuilder(env);
  21. TDataLiteral* positionData = AS_VALUE(TDataLiteral, position);
  22. const ui32 positionValue = positionData->AsValue().Get<ui32>();
  23. MKQL_ENSURE(positionValue <= oldStruct.GetType()->GetMembersCount(), "Bad member index");
  24. for (ui32 i = 0; i < positionValue; ++i) {
  25. resultBuilder.Add(TString(oldStruct.GetType()->GetMemberName(i)), oldStruct.GetValue(i));
  26. }
  27. resultBuilder.Add(TString(newStructType.GetMemberName(positionValue)), newMember);
  28. for (ui32 i = positionValue; i < oldStruct.GetValuesCount(); ++i) {
  29. resultBuilder.Add(TString(oldStruct.GetType()->GetMemberName(i)), oldStruct.GetValue(i));
  30. }
  31. return resultBuilder.Build();
  32. }
  33. TNode* LiteralRemoveMember(
  34. const TStructLiteral& oldStruct,
  35. TRuntimeNode position,
  36. const TTypeEnvironment& env)
  37. {
  38. TStructLiteralBuilder resultBuilder(env);
  39. TDataLiteral* positionData = AS_VALUE(TDataLiteral, position);
  40. const ui32 positionValue = positionData->AsValue().Get<ui32>();
  41. MKQL_ENSURE(positionValue < oldStruct.GetType()->GetMembersCount(), "Bad member index");
  42. for (ui32 i = 0; i < positionValue; ++i) {
  43. resultBuilder.Add(TString(oldStruct.GetType()->GetMemberName(i)), oldStruct.GetValue(i));
  44. }
  45. for (ui32 i = positionValue + 1; i < oldStruct.GetValuesCount(); ++i) {
  46. resultBuilder.Add(TString(oldStruct.GetType()->GetMemberName(i)), oldStruct.GetValue(i));
  47. }
  48. return resultBuilder.Build();
  49. }
  50. TRuntimeNode OptimizeIf(TCallable& callable, const TTypeEnvironment& env) {
  51. Y_UNUSED(env);
  52. MKQL_ENSURE(callable.GetInputsCount() == 3, "Expected 3 arguments");
  53. auto predicateInput = callable.GetInput(0);
  54. auto thenInput = callable.GetInput(1);
  55. auto elseInput = callable.GetInput(2);
  56. if (predicateInput.HasValue()) {
  57. TDataLiteral* data = AS_VALUE(TDataLiteral, predicateInput);
  58. const bool predicateValue = data->AsValue().Get<bool>();
  59. return predicateValue ? thenInput : elseInput;
  60. }
  61. if (thenInput == elseInput) {
  62. return thenInput;
  63. }
  64. return TRuntimeNode(&callable, false);
  65. }
  66. TRuntimeNode OptimizeSize(TCallable& callable, const TTypeEnvironment& env) {
  67. MKQL_ENSURE(callable.GetInputsCount() == 1, "Expected 1 arguments");
  68. auto dataInput = callable.GetInput(0);
  69. if (dataInput.HasValue()) {
  70. if (dataInput.GetStaticType()->IsData()) {
  71. auto slot = *AS_TYPE(TDataType, dataInput.GetStaticType())->GetDataSlot();
  72. if (NYql::NUdf::GetDataTypeInfo(slot).Features & NYql::NUdf::EDataTypeFeatures::StringType) {
  73. TDataLiteral* value = AS_VALUE(TDataLiteral, dataInput);
  74. return TRuntimeNode(BuildDataLiteral(NUdf::TUnboxedValuePod((ui32)value->AsValue().AsStringRef().Size()), NUdf::EDataSlot::Uint32, env), true);
  75. }
  76. }
  77. }
  78. return TRuntimeNode(&callable, false);
  79. }
  80. TRuntimeNode OptimizeLength(TCallable& callable, const TTypeEnvironment& env) {
  81. MKQL_ENSURE(callable.GetInputsCount() == 1, "Expected 1 arguments");
  82. auto listOrDictInput = callable.GetInput(0);
  83. if (listOrDictInput.HasValue()) {
  84. if (listOrDictInput.GetStaticType()->IsList()) {
  85. TListLiteral* value = AS_VALUE(TListLiteral, listOrDictInput);
  86. return TRuntimeNode(BuildDataLiteral(NUdf::TUnboxedValuePod((ui64)value->GetItemsCount()), NUdf::EDataSlot::Uint64, env), true);
  87. }
  88. }
  89. return TRuntimeNode(&callable, false);
  90. }
  91. TRuntimeNode OptimizeAddMember(TCallable& callable, const TTypeEnvironment& env) {
  92. MKQL_ENSURE(callable.GetInputsCount() == 3, "Expected 3 arguments");
  93. auto callableReturnType = callable.GetType()->GetReturnType();
  94. MKQL_ENSURE(callableReturnType->IsStruct(), "Expected struct");
  95. const auto& newType = static_cast<TStructType&>(*callableReturnType);
  96. auto structInput = callable.GetInput(0);
  97. if (structInput.HasValue()) {
  98. TStructLiteral* value = AS_VALUE(TStructLiteral, structInput);
  99. return TRuntimeNode(LiteralAddMember(*value, newType, callable.GetInput(1), callable.GetInput(2), env), true);
  100. }
  101. return TRuntimeNode(&callable, false);
  102. }
  103. TRuntimeNode OptimizeRemoveMember(TCallable& callable, const TTypeEnvironment& env) {
  104. MKQL_ENSURE(callable.GetInputsCount() == 2, "Expected 2 arguments");
  105. auto callableReturnType = callable.GetType()->GetReturnType();
  106. MKQL_ENSURE(callableReturnType->IsStruct(), "Expected struct");
  107. auto structInput = callable.GetInput(0);
  108. if (structInput.HasValue()) {
  109. TStructLiteral* value = AS_VALUE(TStructLiteral, structInput);
  110. return TRuntimeNode(LiteralRemoveMember(*value, callable.GetInput(1), env), true);
  111. }
  112. return TRuntimeNode(&callable, false);
  113. }
  114. TRuntimeNode OptimizeMember(TCallable& callable, const TTypeEnvironment& env) {
  115. Y_UNUSED(env);
  116. MKQL_ENSURE(callable.GetInputsCount() == 2, "Expected 2 arguments");
  117. auto structInput = callable.GetInput(0);
  118. if (structInput.HasValue() && structInput.GetStaticType()->IsStruct()) {
  119. TStructLiteral* value = AS_VALUE(TStructLiteral, structInput);
  120. auto position = callable.GetInput(1);
  121. TDataLiteral* positionData = AS_VALUE(TDataLiteral, position);
  122. const ui32 positionValue = positionData->AsValue().Get<ui32>();
  123. MKQL_ENSURE(positionValue < value->GetValuesCount(), "Bad member index");
  124. return value->GetValue(positionValue);
  125. }
  126. return TRuntimeNode(&callable, false);
  127. }
  128. TRuntimeNode OptimizeFilter(TCallable& callable, const TTypeEnvironment& env) {
  129. if (callable.GetInputsCount() == 3U) {
  130. auto listInput = callable.GetInput(0);
  131. if (!listInput.GetStaticType()->IsList()) {
  132. return TRuntimeNode(&callable, false);
  133. }
  134. auto listType = static_cast<TListType*>(listInput.GetStaticType());
  135. auto predicateInput = callable.GetInput(2);
  136. if (predicateInput.HasValue()) {
  137. auto predicate = predicateInput.GetValue();
  138. MKQL_ENSURE(predicate->GetType()->IsData(), "Expected data");
  139. const auto& data = static_cast<const TDataLiteral&>(*predicate);
  140. const bool predicateValue = data.AsValue().Get<bool>();
  141. if (predicateValue) {
  142. return listInput;
  143. } else {
  144. return TRuntimeNode(TListLiteral::Create(nullptr, 0, listType, env), true);
  145. }
  146. }
  147. }
  148. return TRuntimeNode(&callable, false);
  149. }
  150. TRuntimeNode OptimizeMap(TCallable& callable, const TTypeEnvironment& env) {
  151. MKQL_ENSURE(callable.GetInputsCount() == 3, "Expected 3 arguments");
  152. auto returnType = callable.GetType()->GetReturnType();
  153. if (!returnType->IsList()) {
  154. return TRuntimeNode(&callable, false);
  155. }
  156. auto listType = static_cast<TListType*>(returnType);
  157. auto newItemInput = callable.GetInput(2);
  158. if (listType->GetItemType()->IsVoid() && newItemInput.HasValue()) {
  159. return TRuntimeNode(env.GetListOfVoidLazy(), true);
  160. }
  161. return TRuntimeNode(&callable, false);
  162. }
  163. TRuntimeNode OptimizeFlatMap(TCallable& callable, const TTypeEnvironment& env) {
  164. MKQL_ENSURE(callable.GetInputsCount() > 2U, "Expected 3 or more arguments");
  165. const auto returnType = callable.GetType()->GetReturnType();
  166. if (!returnType->IsList() || callable.GetInputsCount() > 3U) {
  167. return TRuntimeNode(&callable, false);
  168. }
  169. const auto listType = static_cast<TListType*>(returnType);
  170. const auto newItemInput = callable.GetInput(2);
  171. if (listType->GetItemType()->IsVoid() && newItemInput.HasValue()) {
  172. if (newItemInput.GetStaticType()->IsList()) {
  173. TListLiteral* list = AS_VALUE(TListLiteral, newItemInput);
  174. if (list->GetItemsCount() == 0) {
  175. return TRuntimeNode(env.GetListOfVoidLazy(), true);
  176. }
  177. } else {
  178. TOptionalLiteral* opt = AS_VALUE(TOptionalLiteral, newItemInput);
  179. if (!opt->HasItem()) {
  180. return TRuntimeNode(env.GetListOfVoidLazy(), true);
  181. }
  182. }
  183. }
  184. return TRuntimeNode(&callable, false);
  185. }
  186. TRuntimeNode OptimizeCoalesce(TCallable& callable, const TTypeEnvironment& env) {
  187. Y_UNUSED(env);
  188. MKQL_ENSURE(callable.GetInputsCount() == 2, "Expected 2 arguments");
  189. auto optionalInput = callable.GetInput(0);
  190. auto defaultInput = callable.GetInput(1);
  191. if (optionalInput.HasValue()) {
  192. auto optionalData = AS_VALUE(TOptionalLiteral, optionalInput);
  193. if (optionalData->HasItem()) {
  194. return optionalInput.GetStaticType()->IsSameType(*defaultInput.GetStaticType()) ? optionalInput : optionalData->GetItem();
  195. } else {
  196. return defaultInput;
  197. }
  198. }
  199. return TRuntimeNode(&callable, false);
  200. }
  201. TRuntimeNode OptimizeExists(TCallable& callable, const TTypeEnvironment& env) {
  202. MKQL_ENSURE(callable.GetInputsCount() == 1, "Expected 1 arguments");
  203. auto optionalInput = callable.GetInput(0);
  204. if (optionalInput.HasValue()) {
  205. const bool has = AS_VALUE(TOptionalLiteral, optionalInput)->HasItem();
  206. return TRuntimeNode(BuildDataLiteral(NUdf::TUnboxedValuePod(has), NUdf::EDataSlot::Bool, env), true);
  207. }
  208. return TRuntimeNode(&callable, false);
  209. }
  210. TRuntimeNode OptimizeNth(TCallable& callable, const TTypeEnvironment& env) {
  211. Y_UNUSED(env);
  212. MKQL_ENSURE(callable.GetInputsCount() == 2, "Expected 2 arguments");
  213. auto tupleInput = callable.GetInput(0);
  214. if (tupleInput.HasValue() && tupleInput.GetStaticType()->IsTuple()) {
  215. auto tuple = tupleInput.GetValue();
  216. auto indexData = AS_VALUE(TDataLiteral, callable.GetInput(1));
  217. const ui32 index = indexData->AsValue().Get<ui32>();
  218. const auto& value = static_cast<const TTupleLiteral&>(*tuple);
  219. MKQL_ENSURE(index < value.GetValuesCount(), "Index out of range");
  220. return value.GetValue(index);
  221. }
  222. return TRuntimeNode(&callable, false);
  223. }
  224. TRuntimeNode OptimizeExtend(TCallable& callable, const TTypeEnvironment& env) {
  225. auto returnType = callable.GetType()->GetReturnType();
  226. if (!returnType->IsList()) {
  227. return TRuntimeNode(&callable, false);
  228. }
  229. auto itemType = static_cast<TListType*>(returnType)->GetItemType();
  230. if (!itemType->IsVoid()) {
  231. return TRuntimeNode(&callable, false);
  232. }
  233. for (ui32 i = 0; i < callable.GetInputsCount(); ++i) {
  234. auto seq = callable.GetInput(i);
  235. auto seqType = seq.GetStaticType();
  236. MKQL_ENSURE(seqType->IsList(), "Expected list type in extend");
  237. MKQL_ENSURE(static_cast<TListType*>(seqType)->GetItemType()->IsVoid(), "Expected list of void");
  238. if (!seq.HasValue()) {
  239. return TRuntimeNode(&callable, false);
  240. }
  241. TListLiteral* listValue = AS_VALUE(TListLiteral, seq);
  242. if (listValue->GetItemsCount() != 0) {
  243. return TRuntimeNode(&callable, false);
  244. }
  245. }
  246. return TRuntimeNode(env.GetListOfVoidLazy(), true);
  247. }
  248. struct TOptimizationFuncMapFiller {
  249. THashMap<TString, TCallableVisitFunc> Map;
  250. TCallableVisitFuncProvider Provider;
  251. TOptimizationFuncMapFiller()
  252. {
  253. Map["If"] = &OptimizeIf;
  254. Map["Size"] = &OptimizeSize;
  255. Map["Length"] = &OptimizeLength;
  256. Map["AddMember"] = &OptimizeAddMember;
  257. Map["RemoveMember"] = &OptimizeRemoveMember;
  258. Map["Member"] = &OptimizeMember;
  259. Map["Filter"] = &OptimizeFilter;
  260. Map["Map"] = &OptimizeMap;
  261. Map["FlatMap"] = &OptimizeFlatMap;
  262. Map["Coalesce"] = &OptimizeCoalesce;
  263. Map["Exists"] = &OptimizeExists;
  264. Map["Nth"] = &OptimizeNth;
  265. Map["Extend"] = &OptimizeExtend;
  266. Provider = [&](TInternName name) {
  267. auto it = Map.find(name.Str());
  268. if (it != Map.end())
  269. return it->second;
  270. return TCallableVisitFunc();
  271. };
  272. }
  273. };
  274. } // namespace
  275. TCallableVisitFuncProvider GetLiteralPropagationOptimizationFuncProvider() {
  276. return Singleton<TOptimizationFuncMapFiller>()->Provider;
  277. }
  278. TRuntimeNode LiteralPropagationOptimization(TRuntimeNode root, const TTypeEnvironment& env, bool inPlace) {
  279. TExploringNodeVisitor explorer;
  280. explorer.Walk(root.GetNode(), env);
  281. bool wereChanges = false;
  282. return SinglePassVisitCallables(root, explorer, GetLiteralPropagationOptimizationFuncProvider(), env, inPlace, wereChanges);
  283. }
  284. } // namespace NMiniKQL
  285. } // namespace NKikimr