mkql_map_join.cpp 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010
  1. #include "mkql_map_join.h"
  2. #include <yql/essentials/minikql/computation/mkql_computation_node_codegen.h> // Y_IGNORE
  3. #include <yql/essentials/minikql/computation/mkql_computation_node_holders.h>
  4. #include <yql/essentials/minikql/computation/mkql_computation_node_holders_codegen.h>
  5. #include <yql/essentials/minikql/mkql_node_cast.h>
  6. #include <yql/essentials/minikql/mkql_program_builder.h>
  7. #include <yql/essentials/minikql/invoke_builtins/mkql_builtins.h>
  8. #include <util/string/cast.h>
  9. namespace NKikimr {
  10. namespace NMiniKQL {
  11. namespace {
  12. template<bool IsTuple>
  13. class TWideMapJoinBase {
  14. protected:
  15. TWideMapJoinBase(TComputationMutables& mutables, std::vector<TFunctionDescriptor>&& leftKeyConverters,
  16. TDictType* dictType, std::vector<EValueRepresentation>&& outputRepresentations, std::vector<ui32>&& leftKeyColumns,
  17. std::vector<ui32>&& leftRenames, std::vector<ui32>&& rightRenames,
  18. IComputationWideFlowNode* flow, IComputationNode* dict, ui32 inputWidth)
  19. : LeftKeyConverters(std::move(leftKeyConverters))
  20. , DictType(dictType)
  21. , OutputRepresentations(std::move(outputRepresentations))
  22. , LeftKeyColumns(std::move(leftKeyColumns))
  23. , LeftRenames(std::move(leftRenames))
  24. , RightRenames(std::move(rightRenames))
  25. , UsedInputs(GetUsedInputs())
  26. , Flow(flow)
  27. , Dict(dict)
  28. , KeyTuple(mutables)
  29. , InputsIndex(mutables.CurValueIndex)
  30. , WideFieldsIndex(mutables.CurWideFieldsIndex)
  31. {
  32. mutables.DeferWideFieldsInit(inputWidth, UsedInputs);
  33. }
  34. #ifndef MKQL_DISABLE_CODEGEN
  35. Value* GenMakeKeysTuple(Value* keysPtr, const ICodegeneratorInlineWideNode::TGettersList& getters, const TCodegenContext& ctx, BasicBlock*& block) const {
  36. auto& context = ctx.Codegen.GetContext();
  37. const auto zero = ConstantInt::get(Type::getInt128Ty(context), 0);
  38. const auto keys = getters[LeftKeyColumns.front()](ctx, block);
  39. new StoreInst(keys, keysPtr, block);
  40. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, keys, zero, "check", block);
  41. return check;
  42. }
  43. Value* GenMakeKeysTuple(Value* keysPtr, const ICodegeneratorInlineWideNode::TGettersList& getters, Value* itemsPtr, Type* keysType, const TCodegenContext& ctx, BasicBlock*& block) const {
  44. auto& context = ctx.Codegen.GetContext();
  45. const auto idxType = Type::getInt32Ty(context);
  46. const auto zero = ConstantInt::get(Type::getInt128Ty(context), 0);
  47. const auto keys = KeyTuple.GenNewArray(LeftKeyColumns.size(), itemsPtr, ctx, block);
  48. const auto items = new LoadInst(PointerType::getUnqual(keysType), itemsPtr, "items", block);
  49. const auto done = BasicBlock::Create(context, "done", ctx.Func);
  50. const auto result = PHINode::Create(Type::getInt1Ty(context), (LeftKeyColumns.size() + 1U) << 1U , "result", done);
  51. const auto keyType = AS_TYPE(TTupleType, DictType->GetKeyType());
  52. for (ui32 i = 0; i < LeftKeyColumns.size(); ++i) {
  53. const auto index = ConstantInt::get(idxType, i);
  54. const auto ptr = GetElementPtrInst::CreateInBounds(keysType, items, {ConstantInt::get(idxType, 0), index}, (TString("ptr_") += ToString(i)).c_str(), block);
  55. const auto elem = getters[LeftKeyColumns[i]](ctx, block);
  56. const auto converter = reinterpret_cast<TGeneratorPtr>(LeftKeyConverters[i].Generator);
  57. const auto conv = converter ? converter(reinterpret_cast<Value *const *>(&elem), ctx, block) : elem;
  58. result->addIncoming(ConstantInt::getTrue(context), block);
  59. const auto next = BasicBlock::Create(context, (TString("next_") += ToString(i)).c_str(), ctx.Func);
  60. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, conv, zero, "check", block);
  61. BranchInst::Create(done, next, check, block);
  62. block = next;
  63. new StoreInst(conv, ptr, block);
  64. ValueAddRef(GetValueRepresentation(keyType->GetElementType(i)), conv, ctx, block);
  65. }
  66. result->addIncoming(ConstantInt::getFalse(context), block);
  67. BranchInst::Create(done, block);
  68. block = done;
  69. new StoreInst(keys, keysPtr, block);
  70. return result;
  71. }
  72. void GenFillLeftStruct(const std::vector<Value*>& pointers, ICodegeneratorInlineWideNode::TGettersList& output) const {
  73. for (auto i = 0U; i < pointers.size(); ++i) {
  74. output[LeftRenames[(i << 1U) + 1U]] = [p = pointers[i]](const TCodegenContext& ctx, BasicBlock*& block) {
  75. auto& context = ctx.Codegen.GetContext();
  76. return new LoadInst(Type::getInt128Ty(context), p, "value", block);
  77. };
  78. }
  79. }
  80. void GenFillLeftStruct(const ICodegeneratorInlineWideNode::TGettersList& input, ICodegeneratorInlineWideNode::TGettersList& output) const {
  81. for (auto i = 0U; i < LeftRenames.size(); ++i) {
  82. const auto& src = input[LeftRenames[i]];
  83. output[LeftRenames[++i]] = src;
  84. }
  85. }
  86. bool IsUnusedInput(const ui32 index) const {
  87. for (auto i = 0U; i < LeftRenames.size(); ++++i)
  88. if (LeftRenames[i] == index)
  89. return false;
  90. return true;
  91. }
  92. template<class TLeftSideSource>
  93. std::array<Value*, 2U> GenFillOutput(ui32 idx, const TCodegenContext& ctx, const TLeftSideSource& input, ICodegeneratorInlineWideNode::TGettersList& output) const {
  94. GenFillLeftStruct(input, output);
  95. auto& context = ctx.Codegen.GetContext();
  96. const auto valueType = Type::getInt128Ty(context);
  97. const auto zero = ConstantInt::get(valueType, 0);
  98. auto width = 0U;
  99. for (auto i = 0U; i < RightRenames.size(); ++++i) {
  100. width = std::max(width, RightRenames[i]);
  101. }
  102. const auto arrayType = ArrayType::get(valueType, ++width);
  103. const auto atTop = &ctx.Func->getEntryBlock().back();
  104. const auto stub = new AllocaInst(arrayType, 0U, "stub", atTop);
  105. Value* zeros = UndefValue::get(arrayType);
  106. for (auto i = 0U; i < width; ++i) {
  107. zeros = InsertValueInst::Create(zeros, zero, {i}, (TString("zero_") += ToString(i)).c_str(), atTop);
  108. }
  109. new StoreInst(zeros, stub, atTop);
  110. const auto item = new AllocaInst(valueType, 0U, "item", atTop);
  111. const auto placeholder = new AllocaInst(stub->getType(), 0U, "placeholder", atTop);
  112. const auto pointer = GetElementPtrInst::CreateInBounds(valueType, ctx.GetMutables(), {ConstantInt::get(Type::getInt32Ty(context), idx)}, "pointer", atTop);
  113. for (auto i = 0U; i < RightRenames.size(); ++i) {
  114. const auto from = RightRenames[i];
  115. const auto to = RightRenames[++i];
  116. const auto kind = OutputRepresentations[to];
  117. output[to] = [from, kind, item, pointer, placeholder, arrayType, valueType](const TCodegenContext& ctx, BasicBlock*& block) {
  118. auto& context = ctx.Codegen.GetContext();
  119. const auto index = ConstantInt::get(Type::getInt32Ty(context), from);
  120. const auto pointerType = PointerType::getUnqual(arrayType);
  121. const auto elements = new LoadInst(pointerType, placeholder, "elements", block);
  122. const auto null = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, elements, ConstantPointerNull::get(pointerType), "null", block);
  123. const auto fast = BasicBlock::Create(context, "fast", ctx.Func);
  124. const auto slow = BasicBlock::Create(context, "slow", ctx.Func);
  125. const auto done = BasicBlock::Create(context, "done", ctx.Func);
  126. const auto out = PHINode::Create(pointer->getType(), 2U, "out", done);
  127. BranchInst::Create(slow, fast, null, block);
  128. block = fast;
  129. const auto ptr = GetElementPtrInst::CreateInBounds(arrayType, elements, {ConstantInt::get(Type::getInt32Ty(context), 0), index}, "ptr", block);
  130. out->addIncoming(ptr, block);
  131. BranchInst::Create(done, block);
  132. block = slow;
  133. const auto value = new LoadInst(valueType, pointer, "value", block);
  134. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetElement>(item, value, ctx.Codegen, block, index);
  135. ValueRelease(kind, item, ctx, block);
  136. out->addIncoming(item, block);
  137. BranchInst::Create(done, block);
  138. block = done;
  139. const auto load = new LoadInst(valueType, out, "load", block);
  140. return load;
  141. };
  142. }
  143. return {{placeholder, stub}};
  144. }
  145. #endif
  146. NUdf::TUnboxedValue MakeKeysTuple(TComputationContext& ctx, NUdf::TUnboxedValue** fields) const {
  147. if constexpr (IsTuple) {
  148. NUdf::TUnboxedValue* items = nullptr;
  149. const auto keys = KeyTuple.NewArray(ctx, LeftKeyColumns.size(), items);
  150. if (!LeftKeyColumns.empty()) {
  151. Y_ABORT_UNLESS(items);
  152. for (auto i = 0U; i < LeftKeyColumns.size(); ++i) {
  153. const auto value = fields[LeftKeyColumns[i]];
  154. const auto converter = LeftKeyConverters[i].Function;
  155. if (!(*items++ = converter ? converter(value) : *value))
  156. return NUdf::TUnboxedValuePod();
  157. }
  158. }
  159. return keys;
  160. } else {
  161. const auto value = fields[LeftKeyColumns.front()];
  162. const auto converter = LeftKeyConverters.front().Function;
  163. return converter ? converter(value) : *value;
  164. }
  165. }
  166. void FillLeftStruct(NUdf::TUnboxedValue*const* output, NUdf::TUnboxedValue** fields) const {
  167. for (auto i = 0U; i < LeftRenames.size(); ++i) {
  168. const auto prevIndex = LeftRenames[i];
  169. const auto newIndex = LeftRenames[++i];
  170. if (const auto out = output[newIndex])
  171. *out = *fields[prevIndex];
  172. }
  173. }
  174. void FillRightStruct(const NUdf::TUnboxedValue& structObj, NUdf::TUnboxedValue*const* output) const {
  175. if (const auto ptr = structObj.GetElements()) {
  176. for (auto i = 0U; i < RightRenames.size(); ++i) {
  177. const auto prevIndex = RightRenames[i];
  178. const auto newIndex = RightRenames[++i];
  179. if (const auto out = output[newIndex])
  180. *out = ptr[prevIndex];
  181. }
  182. } else {
  183. for (auto i = 0U; i < RightRenames.size(); ++i) {
  184. const auto prevIndex = RightRenames[i];
  185. const auto newIndex = RightRenames[++i];
  186. if (const auto out = output[newIndex])
  187. *out = structObj.GetElement(prevIndex);
  188. }
  189. }
  190. }
  191. void NullRightStruct(NUdf::TUnboxedValue*const* output) const {
  192. for (auto i = 0U; i < RightRenames.size(); ++i) {
  193. const auto newIndex = RightRenames[++i];
  194. if (const auto out = output[newIndex])
  195. *out = NUdf::TUnboxedValuePod();
  196. }
  197. }
  198. std::set<ui32> GetUsedInputs() const {
  199. std::set<ui32> unique;
  200. for (auto i = 0U; i < LeftKeyColumns.size(); ++i)
  201. unique.emplace(LeftKeyColumns[i]);
  202. for (auto i = 0U; i < LeftRenames.size(); i += 2U)
  203. unique.emplace(LeftRenames[i]);
  204. return unique;
  205. }
  206. const std::vector<TFunctionDescriptor> LeftKeyConverters;
  207. TDictType* const DictType;
  208. const std::vector<EValueRepresentation> OutputRepresentations;
  209. const std::vector<ui32> LeftKeyColumns;
  210. const std::vector<ui32> LeftRenames;
  211. const std::vector<ui32> RightRenames;
  212. const std::set<ui32> UsedInputs;
  213. IComputationWideFlowNode* const Flow;
  214. IComputationNode* const Dict;
  215. const TContainerCacheOnContext KeyTuple;
  216. ui32 InputsIndex;
  217. ui32 WideFieldsIndex;
  218. };
  219. template<bool WithoutRight, bool RightRequired, bool IsTuple>
  220. class TWideMapJoinWrapper : public TWideMapJoinBase<IsTuple>, public TStatefulWideFlowCodegeneratorNode<TWideMapJoinWrapper<WithoutRight, RightRequired, IsTuple>> {
  221. using TBaseComputation = TStatefulWideFlowCodegeneratorNode<TWideMapJoinWrapper<WithoutRight, RightRequired, IsTuple>>;
  222. public:
  223. TWideMapJoinWrapper(TComputationMutables& mutables, std::vector<TFunctionDescriptor>&& leftKeyConverters,
  224. TDictType* dictType, std::vector<EValueRepresentation>&& outputRepresentations, std::vector<ui32>&& leftKeyColumns,
  225. std::vector<ui32>&& leftRenames, std::vector<ui32>&& rightRenames,
  226. IComputationWideFlowNode* flow, IComputationNode* dict, ui32 inputWidth)
  227. : TWideMapJoinBase<IsTuple>(mutables, std::move(leftKeyConverters), dictType, std::move(outputRepresentations)
  228. , std::move(leftKeyColumns), std::move(leftRenames), std::move(rightRenames), flow, dict, inputWidth)
  229. , TBaseComputation(mutables, flow, EValueRepresentation::Boxed)
  230. {}
  231. EFetchResult DoCalculate(NUdf::TUnboxedValue& lookup, TComputationContext& ctx, NUdf::TUnboxedValue*const* output) const {
  232. auto** fields = ctx.WideFields.data() + this->WideFieldsIndex;
  233. const auto dict = this->Dict->GetValue(ctx);
  234. do {
  235. if (const auto res = this->Flow->FetchValues(ctx, fields); EFetchResult::One != res)
  236. return res;
  237. const auto keys = this->MakeKeysTuple(ctx, fields);
  238. if constexpr (WithoutRight) {
  239. if ((keys && dict.Contains(keys)) == RightRequired)
  240. break;
  241. else
  242. continue;
  243. } else if (keys) {
  244. if (lookup = dict.Lookup(keys)) {
  245. this->FillLeftStruct(output, fields);
  246. this->FillRightStruct(lookup, output);
  247. return EFetchResult::One;
  248. }
  249. }
  250. } while (RightRequired || WithoutRight);
  251. this->FillLeftStruct(output, fields);
  252. this->NullRightStruct(output);
  253. return EFetchResult::One;
  254. }
  255. #ifndef MKQL_DISABLE_CODEGEN
  256. ICodegeneratorInlineWideNode::TGenerateResult DoGenGetValues(const TCodegenContext& ctx, Value* lookupPtr, BasicBlock*& block) const {
  257. MKQL_ENSURE(!this->Dict->IsTemporaryValue(), "Dict can't be temporary");
  258. auto& context = ctx.Codegen.GetContext();
  259. const auto valueType = Type::getInt128Ty(context);
  260. const auto resultType = Type::getInt32Ty(context);
  261. const auto zero = ConstantInt::get(valueType, 0);
  262. const auto keysType = IsTuple ? ArrayType::get(valueType, this->LeftKeyColumns.size()) : nullptr;
  263. const auto kitmsPtr = IsTuple ? new AllocaInst(PointerType::getUnqual(keysType), 0U, "kitms_ptr", &ctx.Func->getEntryBlock().back()) : nullptr;
  264. const auto keysPtr = new AllocaInst(valueType, 0U, "keys_ptr", &ctx.Func->getEntryBlock().back());
  265. new StoreInst(zero, keysPtr, block);
  266. const auto dict = GetNodeValue(this->Dict, ctx, block);
  267. const auto loop = BasicBlock::Create(context, "loop", ctx.Func);
  268. const auto next = BasicBlock::Create(context, "next", ctx.Func);
  269. const auto step = BasicBlock::Create(context, "step", ctx.Func);
  270. const auto stop = BasicBlock::Create(context, "stop", ctx.Func);
  271. const auto result = PHINode::Create(resultType, RightRequired ? 2U : 3U, "result", stop);
  272. BranchInst::Create(loop, block);
  273. block = loop;
  274. const auto current = GetNodeValues(this->Flow, ctx, block);
  275. const auto special = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLE, current.first, ConstantInt::get(resultType, 0), "special", block);
  276. result->addIncoming(current.first, block);
  277. BranchInst::Create(stop, next, special, block);
  278. block = next;
  279. const auto none = IsTuple ?
  280. this->GenMakeKeysTuple(keysPtr, current.second, kitmsPtr, keysType, ctx, block):
  281. this->GenMakeKeysTuple(keysPtr, current.second, ctx, block);
  282. ICodegeneratorInlineWideNode::TGettersList getters(this->OutputRepresentations.size());
  283. if constexpr (WithoutRight) {
  284. this->GenFillLeftStruct(current.second, getters);
  285. if constexpr (RightRequired) {
  286. BranchInst::Create(loop, step, none, block);
  287. } else {
  288. result->addIncoming(ConstantInt::get(resultType, i32(EFetchResult::One)), block);
  289. BranchInst::Create(stop, step, none, block);
  290. }
  291. block = step;
  292. const auto cont = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Contains>(Type::getInt1Ty(context), dict, ctx.Codegen, block, keysPtr);
  293. if constexpr (!IsTuple) {
  294. ValueCleanup(GetValueRepresentation(this->DictType->GetKeyType()), keysPtr, ctx, block);
  295. }
  296. result->addIncoming(ConstantInt::get(resultType, i32(EFetchResult::One)), block);
  297. if constexpr (RightRequired) {
  298. BranchInst::Create(stop, loop, cont, block);
  299. } else {
  300. BranchInst::Create(loop, stop, cont, block);
  301. }
  302. } else {
  303. const auto output = this->GenFillOutput(static_cast<const IComputationNode*>(this)->GetIndex(), ctx, current.second, getters);
  304. const auto left = RightRequired ? nullptr : BasicBlock::Create(context, "left", ctx.Func);
  305. if constexpr (RightRequired)
  306. BranchInst::Create(loop, step, none, block);
  307. else
  308. BranchInst::Create(left, step, none, block);
  309. block = step;
  310. ValueUnRef(EValueRepresentation::Boxed, lookupPtr, ctx, block);
  311. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Lookup>(lookupPtr, dict, ctx.Codegen, block, keysPtr);
  312. const auto lookup = new LoadInst(valueType, lookupPtr, "lookup", block);
  313. if constexpr (!IsTuple) {
  314. ValueCleanup(GetValueRepresentation(this->DictType->GetKeyType()), keysPtr, ctx, block);
  315. }
  316. const auto ok = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_NE, lookup, zero, "ok", block);
  317. const auto full = BasicBlock::Create(context, "full", ctx.Func);
  318. if constexpr (RightRequired)
  319. BranchInst::Create(full, loop, ok, block);
  320. else {
  321. BranchInst::Create(full, left, ok, block);
  322. block = left;
  323. new StoreInst(std::get<1U>(output), std::get<0U>(output), block);
  324. result->addIncoming(ConstantInt::get(resultType, i32(EFetchResult::One)), block);
  325. BranchInst::Create(stop, block);
  326. }
  327. {
  328. block = full;
  329. const auto elements = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetElements>(std::get<1U>(output)->getType(), lookup, ctx.Codegen, block);
  330. new StoreInst(elements, std::get<0U>(output), block);
  331. result->addIncoming(ConstantInt::get(resultType, i32(EFetchResult::One)), block);
  332. BranchInst::Create(stop, block);
  333. }
  334. }
  335. block = stop;
  336. return {result, std::move(getters)};
  337. }
  338. #endif
  339. private:
  340. void RegisterDependencies() const final {
  341. if (const auto flow = this->FlowDependsOn(this->Flow))
  342. this->DependsOn(flow, this->Dict);
  343. }
  344. };
  345. template<bool RightRequired, bool IsTuple>
  346. class TWideMultiMapJoinWrapper : public TWideMapJoinBase<IsTuple>, public TPairStateWideFlowCodegeneratorNode<TWideMultiMapJoinWrapper<RightRequired, IsTuple>> {
  347. using TBaseComputation = TPairStateWideFlowCodegeneratorNode<TWideMultiMapJoinWrapper<RightRequired, IsTuple>>;
  348. using TBase = TWideMapJoinBase<IsTuple>;
  349. public:
  350. TWideMultiMapJoinWrapper(TComputationMutables& mutables, std::vector<TFunctionDescriptor>&& leftKeyConverters,
  351. TDictType* dictType, std::vector<EValueRepresentation>&& outputRepresentations, std::vector<ui32>&& leftKeyColumns,
  352. std::vector<ui32>&& leftRenames, std::vector<ui32>&& rightRenames,
  353. IComputationWideFlowNode* flow, IComputationNode* dict, ui32 inputWidth)
  354. : TWideMapJoinBase<IsTuple>(mutables, std::move(leftKeyConverters), dictType, std::move(outputRepresentations)
  355. , std::move(leftKeyColumns), std::move(leftRenames), std::move(rightRenames), flow, dict, inputWidth)
  356. , TBaseComputation(mutables, flow, EValueRepresentation::Boxed, EValueRepresentation::Boxed)
  357. {
  358. if (!TBase::LeftRenames.empty()) {
  359. LeftRenamesStorageIndex = mutables.CurValueIndex;
  360. mutables.CurValueIndex += TBase::LeftRenames.size() >> 1U;
  361. }
  362. }
  363. EFetchResult DoCalculate(NUdf::TUnboxedValue& iter, NUdf::TUnboxedValue& item, TComputationContext& ctx, NUdf::TUnboxedValue*const* output) const {
  364. auto** fields = ctx.WideFields.data() + this->WideFieldsIndex;
  365. for (auto iterator = std::move(iter);;) {
  366. if (iterator.HasValue()) {
  367. if (iterator.Next(item)) {
  368. this->FillLeftStruct(output, fields);
  369. this->FillRightStruct(item, output);
  370. iter = std::move(iterator);
  371. return EFetchResult::One;
  372. }
  373. }
  374. for (const auto& dict = this->Dict->GetValue(ctx);;) {
  375. if (const auto res = this->Flow->FetchValues(ctx, fields); EFetchResult::One != res)
  376. return res;
  377. if (const auto keys = this->MakeKeysTuple(ctx, fields)) {
  378. if (const auto lookup = dict.Lookup(keys)) {
  379. iterator = lookup.GetListIterator();
  380. break;
  381. }
  382. }
  383. if constexpr (!RightRequired) {
  384. this->FillLeftStruct(output, fields);
  385. this->NullRightStruct(output);
  386. return EFetchResult::One;
  387. }
  388. }
  389. }
  390. }
  391. #ifndef MKQL_DISABLE_CODEGEN
  392. ICodegeneratorInlineWideNode::TGenerateResult DoGenGetValues(const TCodegenContext& ctx, Value* iteraratorPtr, Value* itemPtr, BasicBlock*& block) const {
  393. MKQL_ENSURE(!this->Dict->IsTemporaryValue(), "Dict can't be temporary");
  394. auto& context = ctx.Codegen.GetContext();
  395. const auto resultType = Type::getInt32Ty(context);
  396. const auto valueType = Type::getInt128Ty(context);
  397. const auto zero = ConstantInt::get(valueType, 0);
  398. const auto keysType = IsTuple ? ArrayType::get(valueType, this->LeftKeyColumns.size()) : nullptr;
  399. const auto kitmsPtr = IsTuple ? new AllocaInst(PointerType::getUnqual(keysType), 0U, "kitms_ptr", &ctx.Func->getEntryBlock().back()) : nullptr;
  400. const auto keysPtr = new AllocaInst(valueType, 0U, "keys_ptr", &ctx.Func->getEntryBlock().back());
  401. std::vector<Value*> leftStoragePointers;
  402. leftStoragePointers.reserve(TBase::LeftRenames.size() >> 1U);
  403. auto i = 0U;
  404. const auto values = ctx.GetMutables();
  405. std::generate_n(std::back_inserter(leftStoragePointers), TBase::LeftRenames.size() >> 1U,
  406. [&](){ return GetElementPtrInst::CreateInBounds(valueType, values, {ConstantInt::get(resultType, LeftRenamesStorageIndex + i++)}, (TString("left_out_") += ToString(i)).c_str(), &ctx.Func->getEntryBlock().back()); });
  407. const auto work = BasicBlock::Create(context, "work", ctx.Func);
  408. BranchInst::Create(work, block);
  409. block = work;
  410. const auto subiter = new LoadInst(valueType, iteraratorPtr, "subiter", block);
  411. const auto hasi = BasicBlock::Create(context, "hasi", ctx.Func);
  412. const auto loop = BasicBlock::Create(context, "loop", ctx.Func);
  413. const auto full = BasicBlock::Create(context, "full", ctx.Func);
  414. const auto skip = BasicBlock::Create(context, "loop", ctx.Func);
  415. const auto part = BasicBlock::Create(context, "part", ctx.Func);
  416. const auto next = BasicBlock::Create(context, "next", ctx.Func);
  417. const auto step = BasicBlock::Create(context, "step", ctx.Func);
  418. const auto fill = BasicBlock::Create(context, "fill", ctx.Func);
  419. const auto left = RightRequired ? nullptr : BasicBlock::Create(context, "left", ctx.Func);
  420. const auto exit = BasicBlock::Create(context, "exit", ctx.Func);
  421. const auto result = PHINode::Create(resultType, RightRequired ? 2U : 3U, "result", exit);
  422. ICodegeneratorInlineWideNode::TGettersList getters(this->OutputRepresentations.size());
  423. BranchInst::Create(hasi, part, HasValue(subiter, block, context), block);
  424. block = part;
  425. for (const auto ptr : leftStoragePointers) {
  426. new StoreInst(GetInvalid(context), ptr, block);
  427. }
  428. const auto dict = GetNodeValue(this->Dict, ctx, block);
  429. BranchInst::Create(loop, block);
  430. block = loop;
  431. const auto current = GetNodeValues(this->Flow, ctx, block);
  432. const auto output = this->GenFillOutput(static_cast<const IComputationNode*>(this)->GetIndex() + 1U, ctx, leftStoragePointers, getters);
  433. const auto special = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_SLE, current.first, ConstantInt::get(resultType, 0), "special", block);
  434. result->addIncoming(current.first, block);
  435. BranchInst::Create(exit, next, special, block);
  436. block = hasi;
  437. const auto status = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Next>(Type::getInt1Ty(context), subiter, ctx.Codegen, block, itemPtr);
  438. BranchInst::Create(full, skip, status, block);
  439. {
  440. block = full;
  441. const auto item = new LoadInst(valueType, itemPtr, "item", block);
  442. const auto elements = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetElements>(std::get<1U>(output)->getType(), item, ctx.Codegen, block);
  443. new StoreInst(elements, std::get<0U>(output), block);
  444. result->addIncoming(ConstantInt::get(resultType, i32(EFetchResult::One)), block);
  445. BranchInst::Create(exit, block);
  446. }
  447. {
  448. block = skip;
  449. UnRefBoxed(subiter, ctx, block);
  450. new StoreInst(zero, iteraratorPtr, block);
  451. for (auto i = 0U; i < leftStoragePointers.size(); ++i) {
  452. const auto ptr = leftStoragePointers[i];
  453. ValueUnRef(TBase::OutputRepresentations[TBase::LeftRenames[(i << 1U) + 1U]], ptr, ctx, block);
  454. new StoreInst(GetInvalid(context), ptr, block);
  455. }
  456. BranchInst::Create(part, block);
  457. }
  458. block = next;
  459. const auto none = IsTuple ?
  460. this->GenMakeKeysTuple(keysPtr, current.second, kitmsPtr, keysType, ctx, block):
  461. this->GenMakeKeysTuple(keysPtr, current.second, ctx, block);
  462. if constexpr (RightRequired)
  463. BranchInst::Create(loop, step, none, block);
  464. else
  465. BranchInst::Create(left, step, none, block);
  466. block = step;
  467. ValueUnRef(EValueRepresentation::Boxed, itemPtr, ctx, block);
  468. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Lookup>(itemPtr, dict, ctx.Codegen, block, keysPtr);
  469. if constexpr (!IsTuple) {
  470. if (this->IsUnusedInput(this->LeftKeyColumns.front())) {
  471. ValueCleanup(GetValueRepresentation(this->DictType->GetKeyType()), keysPtr, ctx, block);
  472. }
  473. }
  474. const auto lookup = new LoadInst(valueType, itemPtr, "lookup", block);
  475. const auto ok = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_NE, lookup, zero, "ok", block);
  476. if constexpr (RightRequired)
  477. BranchInst::Create(fill, loop, ok, block);
  478. else {
  479. BranchInst::Create(fill, left, ok, block);
  480. block = left;
  481. for (auto i = 0U; i < leftStoragePointers.size(); ++i) {
  482. const auto item = current.second[TBase::LeftRenames[i << 1U]](ctx, block);
  483. new StoreInst(item, leftStoragePointers[i], block);
  484. }
  485. new StoreInst(std::get<1U>(output), std::get<0U>(output), block);
  486. result->addIncoming(ConstantInt::get(resultType, i32(EFetchResult::One)), block);
  487. BranchInst::Create(exit, block);
  488. }
  489. {
  490. block = fill;
  491. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetListIterator>(iteraratorPtr, lookup, ctx.Codegen, block);
  492. for (auto i = 0U; i < leftStoragePointers.size(); ++i) {
  493. const auto item = current.second[TBase::LeftRenames[i << 1U]](ctx, block);
  494. ValueAddRef(TBase::OutputRepresentations[TBase::LeftRenames[(i << 1U) + 1U]], item, ctx, block);
  495. new StoreInst(item, leftStoragePointers[i], block);
  496. }
  497. BranchInst::Create(work, block);
  498. }
  499. block = exit;
  500. return {result, std::move(getters)};
  501. }
  502. #endif
  503. private:
  504. void RegisterDependencies() const final {
  505. if (const auto flow = this->FlowDependsOn(this->Flow))
  506. this->DependsOn(flow, this->Dict);
  507. }
  508. ui32 LeftRenamesStorageIndex = 0U;
  509. };
  510. template<bool IsTuple>
  511. class TMapJoinCoreWrapperBase {
  512. protected:
  513. TMapJoinCoreWrapperBase(TComputationMutables& mutables, std::vector<TFunctionDescriptor>&& leftKeyConverters,
  514. TDictType* dictType, std::vector<EValueRepresentation>&& outputRepresentations, std::vector<ui32>&& leftKeyColumns,
  515. std::vector<ui32>&& leftRenames, std::vector<ui32>&& rightRenames, IComputationNode* stream, IComputationNode* dict)
  516. : LeftKeyConverters(std::move(leftKeyConverters))
  517. , DictType(dictType)
  518. , OutputRepresentations(std::move(outputRepresentations))
  519. , LeftKeyColumns(std::move(leftKeyColumns))
  520. , LeftRenames(std::move(leftRenames))
  521. , RightRenames(std::move(rightRenames))
  522. , Stream(stream)
  523. , Dict(dict)
  524. , ResStruct(mutables)
  525. , KeyTuple(mutables)
  526. {}
  527. static void FillStruct(const NUdf::TUnboxedValue& structObj, NUdf::TUnboxedValue* items, const std::vector<ui32>& renames) {
  528. if (renames.empty()) {
  529. return;
  530. }
  531. Y_ABORT_UNLESS(items);
  532. if (const auto ptr = structObj.GetElements()) {
  533. for (auto i = 0U; i < renames.size();) {
  534. const auto prevIndex = renames[i++];
  535. const auto newIndex = renames[i++];
  536. items[newIndex] = ptr[prevIndex];
  537. }
  538. } else {
  539. for (auto i = 0U; i < renames.size();) {
  540. const auto prevIndex = renames[i++];
  541. const auto newIndex = renames[i++];
  542. items[newIndex] = structObj.GetElement(prevIndex);
  543. }
  544. }
  545. }
  546. void FillLeftStruct(const NUdf::TUnboxedValue& structObj, NUdf::TUnboxedValue* items) const {
  547. FillStruct(structObj, items, LeftRenames);
  548. }
  549. void FillRightStruct(const NUdf::TUnboxedValue& structObj, NUdf::TUnboxedValue* items) const {
  550. FillStruct(structObj, items, RightRenames);
  551. }
  552. NUdf::TUnboxedValue MakeKeysTuple(TComputationContext& ctx, const NUdf::TUnboxedValuePod& structObj) const {
  553. if (IsTuple) {
  554. NUdf::TUnboxedValue* items = nullptr;
  555. const auto keys = KeyTuple.NewArray(ctx, LeftKeyColumns.size(), items);
  556. if (!LeftKeyColumns.empty()) {
  557. Y_ABORT_UNLESS(items);
  558. const auto ptr = structObj.GetElements();
  559. for (auto i = 0U; i < LeftKeyColumns.size(); ++i) {
  560. auto value = ptr ? ptr[LeftKeyColumns[i]] : structObj.GetElement(LeftKeyColumns[i]);
  561. const auto converter = LeftKeyConverters[i].Function;
  562. if (!(*items++ = converter ? NUdf::TUnboxedValue(converter(&value)) : std::move(value)))
  563. return NUdf::TUnboxedValuePod();
  564. }
  565. }
  566. return keys;
  567. } else {
  568. const auto value = structObj.GetElement(LeftKeyColumns.front());
  569. const auto converter = LeftKeyConverters.front().Function;
  570. return converter ? NUdf::TUnboxedValue(converter(&value)) : value;
  571. }
  572. }
  573. #ifndef MKQL_DISABLE_CODEGEN
  574. void GenFillLeftStruct(Value* left, Value* items, Type* arrayType, const TCodegenContext& ctx, BasicBlock*& block) const {
  575. auto& context = ctx.Codegen.GetContext();
  576. const auto idxType = Type::getInt32Ty(context);
  577. const auto valType = Type::getInt128Ty(context);
  578. const auto ptrType = PointerType::getUnqual(valType);
  579. const auto elements = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetElements>(ptrType, left, ctx.Codegen, block);
  580. const auto null = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, elements, ConstantPointerNull::get(ptrType), "null", block);
  581. const auto fast = BasicBlock::Create(context, "fast", ctx.Func);
  582. const auto slow = BasicBlock::Create(context, "slow", ctx.Func);
  583. const auto done = BasicBlock::Create(context, "done", ctx.Func);
  584. BranchInst::Create(slow, fast, null, block);
  585. {
  586. block = fast;
  587. for (auto i = 0U; i < LeftRenames.size();) {
  588. const auto oldI = LeftRenames[i++];
  589. const auto newI = LeftRenames[i++];
  590. const auto oldIndex = ConstantInt::get(idxType, oldI);
  591. const auto newIndex = ConstantInt::get(idxType, newI);
  592. const auto oldPtr = GetElementPtrInst::CreateInBounds(valType, elements, {oldIndex}, "old", block);
  593. const auto newPtr = GetElementPtrInst::CreateInBounds(arrayType, items, {ConstantInt::get(idxType, 0), newIndex}, "new", block);
  594. const auto item = new LoadInst(valType, oldPtr, "item", block);
  595. new StoreInst(item, newPtr, block);
  596. ValueAddRef(OutputRepresentations[newI], newPtr, ctx, block);
  597. }
  598. BranchInst::Create(done, block);
  599. }
  600. {
  601. block = slow;
  602. for (auto i = 0U; i < LeftRenames.size();) {
  603. const auto oldI = LeftRenames[i++];
  604. const auto newI = LeftRenames[i++];
  605. const auto oldIndex = ConstantInt::get(idxType, oldI);
  606. const auto newIndex = ConstantInt::get(idxType, newI);
  607. const auto item = GetElementPtrInst::CreateInBounds(arrayType, items, {ConstantInt::get(idxType, 0), newIndex}, "item", block);
  608. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetElement>(item, left, ctx.Codegen, block, oldIndex);
  609. }
  610. BranchInst::Create(done, block);
  611. }
  612. block = done;
  613. }
  614. void GenFillRightStruct(Value* right, Value* items, Type* arrayType, const TCodegenContext& ctx, BasicBlock*& block) const {
  615. auto& context = ctx.Codegen.GetContext();
  616. const auto idxType = Type::getInt32Ty(context);
  617. const auto valType = Type::getInt128Ty(context);
  618. const auto ptrType = PointerType::getUnqual(valType);
  619. const auto elements = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetElements>(ptrType, right, ctx.Codegen, block);
  620. const auto null = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, elements, ConstantPointerNull::get(ptrType), "null", block);
  621. const auto fast = BasicBlock::Create(context, "fast", ctx.Func);
  622. const auto slow = BasicBlock::Create(context, "slow", ctx.Func);
  623. const auto done = BasicBlock::Create(context, "done", ctx.Func);
  624. BranchInst::Create(slow, fast, null, block);
  625. {
  626. block = fast;
  627. for (auto i = 0U; i < RightRenames.size();) {
  628. const auto oldI = RightRenames[i++];
  629. const auto newI = RightRenames[i++];
  630. const auto oldIndex = ConstantInt::get(idxType, oldI);
  631. const auto newIndex = ConstantInt::get(idxType, newI);
  632. const auto oldPtr = GetElementPtrInst::CreateInBounds(valType, elements, {oldIndex}, "old", block);
  633. const auto newPtr = GetElementPtrInst::CreateInBounds(arrayType, items, {ConstantInt::get(idxType, 0), newIndex}, "new", block);
  634. const auto elem = new LoadInst(valType, oldPtr, "elem", block);
  635. new StoreInst(elem, newPtr, block);
  636. ValueAddRef(OutputRepresentations[newI], newPtr, ctx, block);
  637. }
  638. BranchInst::Create(done, block);
  639. }
  640. {
  641. block = slow;
  642. for (auto i = 0U; i < RightRenames.size();) {
  643. const auto oldI = RightRenames[i++];
  644. const auto newI = RightRenames[i++];
  645. const auto oldIndex = ConstantInt::get(idxType, oldI);
  646. const auto newIndex = ConstantInt::get(idxType, newI);
  647. const auto item = GetElementPtrInst::CreateInBounds(arrayType, items, {ConstantInt::get(idxType, 0), newIndex}, "item", block);
  648. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetElement>(item, right, ctx.Codegen, block, oldIndex);
  649. }
  650. BranchInst::Create(done, block);
  651. }
  652. block = done;
  653. }
  654. Value* GenMakeKeysTuple(Value* keysPtr, Value* current, const TCodegenContext& ctx, BasicBlock*& block) const {
  655. auto& context = ctx.Codegen.GetContext();
  656. const auto idxType = Type::getInt32Ty(context);
  657. const auto zero = ConstantInt::get(Type::getInt128Ty(context), 0);
  658. const auto index = ConstantInt::get(idxType, LeftKeyColumns.front());
  659. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetElement>(keysPtr, current, ctx.Codegen, block, index);
  660. if (const auto converter = reinterpret_cast<TGeneratorPtr>(LeftKeyConverters.front().Generator)) {
  661. Value *const elem = new LoadInst(Type::getInt128Ty(context), keysPtr, "elem", block);
  662. const auto conv = converter(&elem, ctx, block);
  663. new StoreInst(conv, keysPtr, block);
  664. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, conv, zero, "check", block);
  665. return check;
  666. } else {
  667. const auto keys = new LoadInst(Type::getInt128Ty(context), keysPtr, "keys", block);
  668. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, keys, zero, "check", block);
  669. return check;
  670. }
  671. }
  672. Value* GenMakeKeysTuple(Value* keysPtr, Value* current, Value* itemsPtr, Type* keysType, const TCodegenContext& ctx, BasicBlock*& block) const {
  673. auto& context = ctx.Codegen.GetContext();
  674. const auto idxType = Type::getInt32Ty(context);
  675. const auto valueType = Type::getInt128Ty(context);
  676. const auto zero = ConstantInt::get(valueType, 0);
  677. const auto keys = KeyTuple.GenNewArray(LeftKeyColumns.size(), itemsPtr, ctx, block);
  678. const auto items = new LoadInst(PointerType::getUnqual(keysType), itemsPtr, "items", block);
  679. const auto ptrType = PointerType::getUnqual(valueType);
  680. const auto elements = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetElements>(ptrType, current, ctx.Codegen, block);
  681. const auto null = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, elements, ConstantPointerNull::get(ptrType), "null", block);
  682. const auto fast = BasicBlock::Create(context, "fast", ctx.Func);
  683. const auto slow = BasicBlock::Create(context, "slow", ctx.Func);
  684. const auto done = BasicBlock::Create(context, "done", ctx.Func);
  685. const auto result = PHINode::Create(Type::getInt1Ty(context), (LeftKeyColumns.size() + 1U) << 1U , "result", done);
  686. BranchInst::Create(slow, fast, null, block);
  687. {
  688. block = fast;
  689. const auto keyType = AS_TYPE(TTupleType, DictType->GetKeyType());
  690. for (ui32 i = 0; i < LeftKeyColumns.size(); ++i) {
  691. const auto oldIndex = ConstantInt::get(idxType, LeftKeyColumns[i]);
  692. const auto newIndex = ConstantInt::get(idxType, i);
  693. const auto oldPtr = GetElementPtrInst::CreateInBounds(valueType, elements, {oldIndex}, "old", block);
  694. const auto newPtr = GetElementPtrInst::CreateInBounds(keysType, items, {ConstantInt::get(idxType, 0), newIndex}, "new", block);
  695. const auto elem = new LoadInst(valueType, oldPtr, "elem", block);
  696. const auto converter = reinterpret_cast<TGeneratorPtr>(LeftKeyConverters[i].Generator);
  697. const auto conv = converter ? converter(reinterpret_cast<Value *const *>(&elem), ctx, block) : elem;
  698. result->addIncoming(ConstantInt::getTrue(context), block);
  699. const auto next = BasicBlock::Create(context, (TString("next_") += ToString(i)).c_str(), ctx.Func);
  700. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, conv, zero, "check", block);
  701. BranchInst::Create(done, next, check, block);
  702. block = next;
  703. new StoreInst(conv, newPtr, block);
  704. ValueAddRef(GetValueRepresentation(keyType->GetElementType(i)), newPtr, ctx, block);
  705. }
  706. result->addIncoming(ConstantInt::getFalse(context), block);
  707. BranchInst::Create(done, block);
  708. }
  709. {
  710. block = slow;
  711. for (ui32 i = 0; i < LeftKeyColumns.size(); ++i) {
  712. const auto item = GetElementPtrInst::CreateInBounds(keysType, items, {ConstantInt::get(idxType, 0), ConstantInt::get(idxType, i)}, "item", block);
  713. const auto index = ConstantInt::get(idxType, LeftKeyColumns[i]);
  714. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetElement>(item, current, ctx.Codegen, block, index);
  715. const auto next = BasicBlock::Create(context, (TString("next_") += ToString(i)).c_str(), ctx.Func);
  716. const auto elem = new LoadInst(valueType, item, "elem", block);
  717. if (const auto converter = reinterpret_cast<TGeneratorPtr>(LeftKeyConverters[i].Generator)) {
  718. const auto conv = converter(reinterpret_cast<Value *const *>(&elem), ctx, block);
  719. new StoreInst(conv, item, block);
  720. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, conv, zero, "check", block);
  721. result->addIncoming(ConstantInt::getTrue(context), block);
  722. BranchInst::Create(done, next, check, block);
  723. } else {
  724. const auto check = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, elem, zero, "check", block);
  725. result->addIncoming(ConstantInt::getTrue(context), block);
  726. BranchInst::Create(done, next, check, block);
  727. }
  728. block = next;
  729. }
  730. result->addIncoming(ConstantInt::getFalse(context), block);
  731. BranchInst::Create(done, block);
  732. }
  733. block = done;
  734. new StoreInst(keys, keysPtr, block);
  735. return result;
  736. }
  737. #endif
  738. const std::vector<TFunctionDescriptor> LeftKeyConverters;
  739. TDictType* const DictType;
  740. const std::vector<EValueRepresentation> OutputRepresentations;
  741. const std::vector<ui32> LeftKeyColumns;
  742. const std::vector<ui32> LeftRenames;
  743. const std::vector<ui32> RightRenames;
  744. IComputationNode* const Stream;
  745. IComputationNode* const Dict;
  746. const TContainerCacheOnContext ResStruct;
  747. const TContainerCacheOnContext KeyTuple;
  748. };
  749. enum class ERightKind {
  750. None = 0,
  751. Once,
  752. Many
  753. };
  754. template<ERightKind RightKind, bool RightRequired, bool IsTuple>
  755. class TMapJoinCoreFlowWrapper : public TMapJoinCoreWrapperBase<IsTuple>, public std::conditional_t<ERightKind::Many != RightKind,
  756. TStatelessFlowCodegeneratorNode<TMapJoinCoreFlowWrapper<RightKind, RightRequired, IsTuple>>,
  757. TPairStateFlowCodegeneratorNode<TMapJoinCoreFlowWrapper<RightKind, RightRequired, IsTuple>>> {
  758. typedef std::conditional_t<ERightKind::Many != RightKind,
  759. TStatelessFlowCodegeneratorNode<TMapJoinCoreFlowWrapper<RightKind, RightRequired, IsTuple>>,
  760. TPairStateFlowCodegeneratorNode<TMapJoinCoreFlowWrapper<RightKind, RightRequired, IsTuple>>> TBaseComputation;
  761. public:
  762. TMapJoinCoreFlowWrapper(TComputationMutables& mutables, EValueRepresentation kind, std::vector<TFunctionDescriptor>&& leftKeyConverters,
  763. TDictType* dictType, std::vector<EValueRepresentation>&& outputRepresentations, std::vector<ui32>&& leftKeyColumns,
  764. std::vector<ui32>&& leftRenames, std::vector<ui32>&& rightRenames,
  765. IComputationNode* flow, IComputationNode* dict)
  766. : TMapJoinCoreWrapperBase<IsTuple>(mutables, std::move(leftKeyConverters), dictType, std::move(outputRepresentations),
  767. std::move(leftKeyColumns), std::move(leftRenames), std::move(rightRenames), flow, dict), TBaseComputation(mutables, flow, kind)
  768. {}
  769. NUdf::TUnboxedValuePod DoCalculate(TComputationContext& ctx) const {
  770. for (const auto dict = this->Dict->GetValue(ctx);;) {
  771. auto item = this->Stream->GetValue(ctx);
  772. if (item.IsSpecial()) {
  773. return item.Release();
  774. }
  775. const auto keys = this->MakeKeysTuple(ctx, item);
  776. switch (RightKind) {
  777. case ERightKind::Once:
  778. if (keys) {
  779. if (const auto lookup = dict.Lookup(keys)) {
  780. NUdf::TUnboxedValue* items = nullptr;
  781. const auto result = this->ResStruct.NewArray(ctx, this->OutputRepresentations.size(), items);
  782. this->FillLeftStruct(item, items);
  783. this->FillRightStruct(lookup, items);
  784. return result;
  785. }
  786. }
  787. if constexpr (RightRequired)
  788. continue;
  789. else
  790. break;
  791. case ERightKind::None:
  792. if constexpr (RightRequired) {
  793. if (keys && dict.Contains(keys))
  794. break;
  795. else
  796. continue;
  797. } else {
  798. if (keys && dict.Contains(keys))
  799. continue;
  800. else
  801. break;
  802. }
  803. default:
  804. Y_ABORT("Unreachable");
  805. }
  806. NUdf::TUnboxedValue* items = nullptr;
  807. const auto result = this->ResStruct.NewArray(ctx, this->OutputRepresentations.size(), items);
  808. this->FillLeftStruct(item, items);
  809. return result;
  810. }
  811. }
  812. NUdf::TUnboxedValuePod DoCalculate(NUdf::TUnboxedValue& curr, NUdf::TUnboxedValue& iter, TComputationContext& ctx) const {
  813. for (auto iterator = std::move(iter);;) {
  814. if (iterator.HasValue() && curr.HasValue()) {
  815. if (NUdf::TUnboxedValue item; iterator.Next(item)) {
  816. NUdf::TUnboxedValue* items = nullptr;
  817. const auto result = this->ResStruct.NewArray(ctx, this->OutputRepresentations.size(), items);
  818. this->FillLeftStruct(curr, items);
  819. this->FillRightStruct(item, items);
  820. iter = std::move(iterator);
  821. return result;
  822. }
  823. }
  824. const auto& dict = this->Dict->GetValue(ctx);
  825. for (auto current = std::move(curr);;) {
  826. current = this->Stream->GetValue(ctx);
  827. if (current.IsSpecial()) {
  828. return current.Release();
  829. }
  830. if (const auto keys = this->MakeKeysTuple(ctx, current)) {
  831. if (const auto lookup = dict.Lookup(keys)) {
  832. iterator = lookup.GetListIterator();
  833. curr = std::move(current);
  834. break;
  835. }
  836. }
  837. if constexpr (!RightRequired) {
  838. NUdf::TUnboxedValue* items = nullptr;
  839. const auto result = this->ResStruct.NewArray(ctx, this->OutputRepresentations.size(), items);
  840. this->FillLeftStruct(current, items);
  841. return result;
  842. }
  843. }
  844. }
  845. }
  846. #ifndef MKQL_DISABLE_CODEGEN
  847. Value* DoGenerateGetValue(const TCodegenContext& ctx, BasicBlock*& block) const {
  848. auto& context = ctx.Codegen.GetContext();
  849. const auto valueType = Type::getInt128Ty(context);
  850. const auto zero = ConstantInt::get(valueType, 0);
  851. const auto arrayType = ArrayType::get(valueType, this->OutputRepresentations.size());
  852. const auto keysType = IsTuple ? ArrayType::get(valueType, this->LeftKeyColumns.size()) : nullptr;
  853. const auto itemsType = PointerType::getUnqual(arrayType);
  854. const auto itemsPtr = new AllocaInst(itemsType, 0U, "items_ptr", &ctx.Func->getEntryBlock().back());
  855. const auto kitmsPtr = IsTuple ? new AllocaInst(PointerType::getUnqual(keysType), 0U, "kitms_ptr", &ctx.Func->getEntryBlock().back()) : nullptr;
  856. const auto keysPtr = new AllocaInst(valueType, 0U, "keys_ptr", &ctx.Func->getEntryBlock().back());
  857. new StoreInst(zero, keysPtr, block);
  858. const auto itemPtr = new AllocaInst(valueType, 0U, "item_ptr", &ctx.Func->getEntryBlock().back());
  859. new StoreInst(zero, itemPtr, block);
  860. const auto dict = GetNodeValue(this->Dict, ctx, block);
  861. const auto loop = BasicBlock::Create(context, "loop", ctx.Func);
  862. const auto stop = BasicBlock::Create(context, "stop", ctx.Func);
  863. const auto result = PHINode::Create(valueType, 3U, "result", stop);
  864. BranchInst::Create(loop, block);
  865. block = loop;
  866. const auto current = GetNodeValue(this->Stream, ctx, block);
  867. result->addIncoming(current, block);
  868. const auto next = BasicBlock::Create(context, "next", ctx.Func);
  869. BranchInst::Create(stop, next, IsSpecial(current, block, context), block);
  870. block = next;
  871. const auto none = IsTuple ?
  872. this->GenMakeKeysTuple(keysPtr, current, kitmsPtr, keysType, ctx, block):
  873. this->GenMakeKeysTuple(keysPtr, current, ctx, block);
  874. const auto skip = BasicBlock::Create(context, "skip", ctx.Func);
  875. const auto step = BasicBlock::Create(context, "step", ctx.Func);
  876. const auto half = BasicBlock::Create(context, "half", ctx.Func);
  877. BranchInst::Create(RightRequired ? skip : half, step, none, block);
  878. block = step;
  879. switch (RightKind) {
  880. case ERightKind::None: {
  881. const auto cont = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Contains>(Type::getInt1Ty(context), dict, ctx.Codegen, block, keysPtr);
  882. if constexpr (!IsTuple) {
  883. ValueUnRef(GetValueRepresentation(this->DictType->GetKeyType()), keysPtr, ctx, block);
  884. }
  885. if constexpr (RightRequired) {
  886. BranchInst::Create(half, skip, cont, block);
  887. } else {
  888. BranchInst::Create(skip, half, cont, block);
  889. }
  890. break;
  891. }
  892. case ERightKind::Once: {
  893. new StoreInst(zero, itemPtr, block);
  894. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Lookup>(itemPtr, dict, ctx.Codegen, block, keysPtr);
  895. if constexpr (!IsTuple) {
  896. ValueUnRef(GetValueRepresentation(this->DictType->GetKeyType()), keysPtr, ctx, block);
  897. }
  898. const auto lookup = new LoadInst(valueType, itemPtr, "lookup", block);
  899. const auto ok = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_NE, lookup, zero, "ok", block);
  900. const auto full = BasicBlock::Create(context, "full", ctx.Func);
  901. BranchInst::Create(full, RightRequired ? skip : half, ok, block);
  902. {
  903. block = full;
  904. const auto out = this->ResStruct.GenNewArray(this->OutputRepresentations.size(), itemsPtr, ctx, block);
  905. const auto items = new LoadInst(itemsType, itemsPtr, "items", block);
  906. this->GenFillLeftStruct(current, items, arrayType, ctx, block);
  907. this->GenFillRightStruct(lookup, items, arrayType, ctx, block);
  908. UnRefBoxed(lookup, ctx, block);
  909. ValueCleanup(static_cast<const IComputationNode*>(this)->GetRepresentation(), current, ctx, block);
  910. result->addIncoming(out, block);
  911. BranchInst::Create(stop, block);
  912. }
  913. break;
  914. }
  915. case ERightKind::Many:
  916. Y_ABORT("Wrong case");
  917. }
  918. {
  919. block = half;
  920. const auto out = this->ResStruct.GenNewArray(this->OutputRepresentations.size(), itemsPtr, ctx, block);
  921. const auto items = new LoadInst(itemsType, itemsPtr, "items", block);
  922. this->GenFillLeftStruct(current, items, arrayType, ctx, block);
  923. ValueCleanup(static_cast<const IComputationNode*>(this)->GetRepresentation(), current, ctx, block);
  924. result->addIncoming(out, block);
  925. BranchInst::Create(stop, block);
  926. }
  927. {
  928. block = skip;
  929. ValueCleanup(static_cast<const IComputationNode*>(this)->GetRepresentation(), current, ctx, block);
  930. BranchInst::Create(loop, block);
  931. }
  932. block = stop;
  933. if (this->Dict->IsTemporaryValue())
  934. CleanupBoxed(dict, ctx, block);
  935. return result;
  936. }
  937. Value* DoGenerateGetValue(const TCodegenContext& ctx, Value* currentPtr, Value* iteraratorPtr, BasicBlock*& block) const {
  938. auto& context = ctx.Codegen.GetContext();
  939. const auto valueType = Type::getInt128Ty(context);
  940. const auto zero = ConstantInt::get(valueType, 0);
  941. const auto arrayType = ArrayType::get(valueType, this->OutputRepresentations.size());
  942. const auto keysType = IsTuple ? ArrayType::get(valueType, this->LeftKeyColumns.size()) : nullptr;
  943. const auto itemsType = PointerType::getUnqual(arrayType);
  944. const auto itemsPtr = new AllocaInst(itemsType, 0U, "items_ptr", &ctx.Func->getEntryBlock().back());
  945. const auto kitmsPtr = IsTuple ? new AllocaInst(PointerType::getUnqual(keysType), 0U, "kitms_ptr", &ctx.Func->getEntryBlock().back()) : nullptr;
  946. const auto keysPtr = new AllocaInst(valueType, 0U, "keys_ptr", &ctx.Func->getEntryBlock().back());
  947. const auto itemPtr = new AllocaInst(valueType, 0U, "item_ptr", &ctx.Func->getEntryBlock().back());
  948. new StoreInst(zero, itemPtr, block);
  949. const auto work = BasicBlock::Create(context, "work", ctx.Func);
  950. BranchInst::Create(work, block);
  951. block = work;
  952. const auto subiter = new LoadInst(valueType, iteraratorPtr, "subiter", block);
  953. const auto hasi = BasicBlock::Create(context, "hasi", ctx.Func);
  954. const auto loop = BasicBlock::Create(context, "loop", ctx.Func);
  955. const auto full = BasicBlock::Create(context, "full", ctx.Func);
  956. const auto skip = BasicBlock::Create(context, "loop", ctx.Func);
  957. const auto part = BasicBlock::Create(context, "part", ctx.Func);
  958. const auto next = BasicBlock::Create(context, "next", ctx.Func);
  959. const auto stop = BasicBlock::Create(context, "stop", ctx.Func);
  960. const auto exit = BasicBlock::Create(context, "exit", ctx.Func);
  961. const auto result = PHINode::Create(valueType, 3U, "result", exit);
  962. BranchInst::Create(hasi, part, HasValue(subiter, block, context), block);
  963. block = hasi;
  964. const auto curr = new LoadInst(valueType, currentPtr, "curr", block);
  965. const auto status = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Next>(Type::getInt1Ty(context), subiter, ctx.Codegen, block, itemPtr);
  966. BranchInst::Create(full, skip, status, block);
  967. {
  968. block = full;
  969. const auto out = this->ResStruct.GenNewArray(this->OutputRepresentations.size(), itemsPtr, ctx, block);
  970. const auto items = new LoadInst(itemsType, itemsPtr, "items", block);
  971. const auto item = new LoadInst(valueType, itemPtr, "item", block);
  972. this->GenFillLeftStruct(curr, items, arrayType, ctx, block);
  973. this->GenFillRightStruct(item, items, arrayType, ctx, block);
  974. UnRefBoxed(item, ctx, block);
  975. result->addIncoming(out, block);
  976. BranchInst::Create(exit, block);
  977. }
  978. {
  979. block = skip;
  980. UnRefBoxed(curr, ctx, block);
  981. UnRefBoxed(subiter, ctx, block);
  982. new StoreInst(zero, currentPtr, block);
  983. new StoreInst(zero, iteraratorPtr, block);
  984. BranchInst::Create(part, block);
  985. }
  986. block = part;
  987. const auto dict = GetNodeValue(this->Dict, ctx, block);
  988. BranchInst::Create(loop, block);
  989. block = loop;
  990. GetNodeValue(currentPtr, this->Stream, ctx, block);
  991. const auto current = new LoadInst(valueType, currentPtr, "current", block);
  992. BranchInst::Create(stop, next, IsSpecial(current, block, context), block);
  993. block = stop;
  994. if (this->Dict->IsTemporaryValue())
  995. CleanupBoxed(dict, ctx, block);
  996. result->addIncoming(current, block);
  997. BranchInst::Create(exit, block);
  998. block = next;
  999. const auto none = IsTuple ?
  1000. this->GenMakeKeysTuple(keysPtr, current, kitmsPtr, keysType, ctx, block):
  1001. this->GenMakeKeysTuple(keysPtr, current, ctx, block);
  1002. const auto step = BasicBlock::Create(context, "step", ctx.Func);
  1003. const auto hsnt = BasicBlock::Create(context, "half", ctx.Func);
  1004. BranchInst::Create(hsnt, step, none, block);
  1005. block = step;
  1006. new StoreInst(zero, itemPtr, block);
  1007. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Lookup>(itemPtr, dict, ctx.Codegen, block, keysPtr);
  1008. if constexpr (!IsTuple) {
  1009. ValueUnRef(GetValueRepresentation(this->DictType->GetKeyType()), keysPtr, ctx, block);
  1010. }
  1011. const auto lookup = new LoadInst(valueType, itemPtr, "lookup", block);
  1012. const auto ok = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_NE, lookup, zero, "ok", block);
  1013. const auto fill = BasicBlock::Create(context, "fill", ctx.Func);
  1014. BranchInst::Create(fill, hsnt, ok, block);
  1015. block = hsnt;
  1016. if constexpr (RightRequired) {
  1017. UnRefBoxed(current, ctx, block);
  1018. new StoreInst(zero, currentPtr, block);
  1019. BranchInst::Create(loop, block);
  1020. } else {
  1021. const auto out = this->ResStruct.GenNewArray(this->OutputRepresentations.size(), itemsPtr, ctx, block);
  1022. const auto items = new LoadInst(itemsType, itemsPtr, "items", block);
  1023. this->GenFillLeftStruct(current, items, arrayType, ctx, block);
  1024. UnRefBoxed(current, ctx, block);
  1025. new StoreInst(zero, currentPtr, block);
  1026. if (this->Dict->IsTemporaryValue())
  1027. CleanupBoxed(dict, ctx, block);
  1028. result->addIncoming(out, block);
  1029. BranchInst::Create(exit, block);
  1030. }
  1031. {
  1032. block = fill;
  1033. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetListIterator>(iteraratorPtr, lookup, ctx.Codegen, block);
  1034. CleanupBoxed(lookup, ctx, block);
  1035. BranchInst::Create(work, block);
  1036. }
  1037. block = exit;
  1038. return result;
  1039. }
  1040. #endif
  1041. private:
  1042. void RegisterDependencies() const final {
  1043. if (const auto flow = this->FlowDependsOn(this->Stream))
  1044. this->DependsOn(flow, this->Dict);
  1045. }
  1046. };
  1047. template<ERightKind RightKind, bool RightRequired, bool IsTuple>
  1048. class TMapJoinCoreWrapper : public TMapJoinCoreWrapperBase<IsTuple>, public TCustomValueCodegeneratorNode<TMapJoinCoreWrapper<RightKind, RightRequired, IsTuple>> {
  1049. private:
  1050. typedef TCustomValueCodegeneratorNode<TMapJoinCoreWrapper<RightKind, RightRequired, IsTuple>> TBaseComputation;
  1051. class TCodegenValue : public TComputationValue<TCodegenValue> {
  1052. public:
  1053. using TBase = TComputationValue<TCodegenValue>;
  1054. using TFetchPtr = NUdf::EFetchStatus (*)(TComputationContext*, NUdf::TUnboxedValuePod, NUdf::TUnboxedValuePod, NUdf::TUnboxedValuePod&);
  1055. TCodegenValue(TMemoryUsageInfo* memInfo, TFetchPtr fetch, TComputationContext* ctx, NUdf::TUnboxedValue&& stream, NUdf::TUnboxedValue&& dict)
  1056. : TBase(memInfo)
  1057. , FetchFunc(fetch)
  1058. , Ctx(ctx)
  1059. , Stream(std::move(stream))
  1060. , Dict(std::move(dict))
  1061. {}
  1062. private:
  1063. NUdf::EFetchStatus Fetch(NUdf::TUnboxedValue& result) final {
  1064. return FetchFunc(Ctx, static_cast<const NUdf::TUnboxedValuePod&>(Stream), static_cast<const NUdf::TUnboxedValuePod&>(Dict), result);
  1065. }
  1066. const TFetchPtr FetchFunc;
  1067. TComputationContext* const Ctx;
  1068. const NUdf::TUnboxedValue Stream;
  1069. const NUdf::TUnboxedValue Dict;
  1070. };
  1071. class TCodegenStatefulValue : public TComputationValue<TCodegenStatefulValue> {
  1072. public:
  1073. using TBase = TComputationValue<TCodegenStatefulValue>;
  1074. using TFetchPtr = NUdf::EFetchStatus (*)(TComputationContext*, NUdf::TUnboxedValuePod, NUdf::TUnboxedValuePod, NUdf::TUnboxedValuePod&, NUdf::TUnboxedValuePod&, NUdf::TUnboxedValuePod&);
  1075. TCodegenStatefulValue(TMemoryUsageInfo* memInfo, TFetchPtr fetch, TComputationContext* ctx, NUdf::TUnboxedValue&& stream, NUdf::TUnboxedValue&& dict)
  1076. : TBase(memInfo)
  1077. , FetchFunc(fetch)
  1078. , Ctx(ctx)
  1079. , Stream(std::move(stream))
  1080. , Dict(std::move(dict))
  1081. {}
  1082. private:
  1083. NUdf::EFetchStatus Fetch(NUdf::TUnboxedValue& result) final {
  1084. return FetchFunc(Ctx, static_cast<const NUdf::TUnboxedValuePod&>(Stream), static_cast<const NUdf::TUnboxedValuePod&>(Dict), Current, Iterator, result);
  1085. }
  1086. const TFetchPtr FetchFunc;
  1087. TComputationContext* const Ctx;
  1088. const NUdf::TUnboxedValue Stream;
  1089. const NUdf::TUnboxedValue Dict;
  1090. NUdf::TUnboxedValue Current;
  1091. NUdf::TUnboxedValue Iterator;
  1092. };
  1093. using TSelf = TMapJoinCoreWrapper<RightKind, RightRequired, IsTuple>;
  1094. using TBase = TCustomValueCodegeneratorNode<TSelf>;
  1095. class TValue : public TComputationValue<TValue> {
  1096. public:
  1097. using TBase = TComputationValue<TValue>;
  1098. TValue(TMemoryUsageInfo* memInfo, NUdf::TUnboxedValue&& stream,
  1099. NUdf::TUnboxedValue&& dict, TComputationContext& ctx, const TSelf* self)
  1100. : TBase(memInfo)
  1101. , Stream(std::move(stream))
  1102. , Dict(std::move(dict))
  1103. , Ctx(ctx)
  1104. , Self(self)
  1105. {}
  1106. private:
  1107. NUdf::EFetchStatus Fetch(NUdf::TUnboxedValue& result) override {
  1108. for (NUdf::TUnboxedValue current;;) {
  1109. if (const auto status = Stream.Fetch(current); status != NUdf::EFetchStatus::Ok) {
  1110. return status;
  1111. }
  1112. const auto keys = Self->MakeKeysTuple(Ctx, current);
  1113. switch (RightKind) {
  1114. case ERightKind::Once:
  1115. if (keys) {
  1116. if (const auto lookup = Dict.Lookup(keys)) {
  1117. NUdf::TUnboxedValue* items = nullptr;
  1118. result = Self->ResStruct.NewArray(Ctx, Self->OutputRepresentations.size(), items);
  1119. Self->FillLeftStruct(current, items);
  1120. Self->FillRightStruct(lookup, items);
  1121. return NUdf::EFetchStatus::Ok;
  1122. }
  1123. }
  1124. if constexpr (RightRequired)
  1125. continue;
  1126. else
  1127. break;
  1128. case ERightKind::None:
  1129. if constexpr (RightRequired) {
  1130. if (keys && Dict.Contains(keys))
  1131. break;
  1132. else
  1133. continue;
  1134. } else {
  1135. if (keys && Dict.Contains(keys))
  1136. continue;
  1137. else
  1138. break;
  1139. }
  1140. default:
  1141. Y_ABORT("Unreachable");
  1142. }
  1143. NUdf::TUnboxedValue* items = nullptr;
  1144. result = Self->ResStruct.NewArray(Ctx, Self->OutputRepresentations.size(), items);
  1145. Self->FillLeftStruct(current, items);
  1146. return NUdf::EFetchStatus::Ok;
  1147. }
  1148. }
  1149. private:
  1150. NUdf::TUnboxedValue Stream;
  1151. NUdf::TUnboxedValue Dict;
  1152. TComputationContext& Ctx;
  1153. const TSelf* const Self;
  1154. };
  1155. class TMultiRowValue : public TComputationValue<TMultiRowValue> {
  1156. public:
  1157. using TBase = TComputationValue<TMultiRowValue>;
  1158. TMultiRowValue(TMemoryUsageInfo* memInfo, NUdf::TUnboxedValue&& stream,
  1159. NUdf::TUnboxedValue&& dict, TComputationContext& ctx, const TSelf* self)
  1160. : TBase(memInfo)
  1161. , Stream(std::move(stream))
  1162. , Dict(std::move(dict))
  1163. , Ctx(ctx)
  1164. , Self(self)
  1165. {}
  1166. private:
  1167. NUdf::EFetchStatus Fetch(NUdf::TUnboxedValue& result) override {
  1168. for (auto iterator = std::move(Iterator);;) {
  1169. if (iterator && Current) {
  1170. if (NUdf::TUnboxedValue item; iterator.Next(item)) {
  1171. NUdf::TUnboxedValue* items = nullptr;
  1172. result = Self->ResStruct.NewArray(Ctx, Self->OutputRepresentations.size(), items);
  1173. Self->FillLeftStruct(Current, items);
  1174. Self->FillRightStruct(item, items);
  1175. Iterator = std::move(iterator);
  1176. return NUdf::EFetchStatus::Ok;
  1177. }
  1178. }
  1179. for (auto current = std::move(Current);;) {
  1180. if (const auto status = Stream.Fetch(current); NUdf::EFetchStatus::Ok != status) {
  1181. return status;
  1182. }
  1183. if (const auto keys = Self->MakeKeysTuple(Ctx, current)) {
  1184. if (const auto lookup = Dict.Lookup(keys)) {
  1185. iterator = lookup.GetListIterator();
  1186. Current = std::move(current);
  1187. break;
  1188. }
  1189. }
  1190. if (!RightRequired) {
  1191. NUdf::TUnboxedValue* items = nullptr;
  1192. result = Self->ResStruct.NewArray(Ctx, Self->OutputRepresentations.size(), items);
  1193. Self->FillLeftStruct(current, items);
  1194. return NUdf::EFetchStatus::Ok;
  1195. }
  1196. }
  1197. }
  1198. }
  1199. private:
  1200. NUdf::TUnboxedValue Stream;
  1201. NUdf::TUnboxedValue Dict;
  1202. TComputationContext& Ctx;
  1203. const TSelf* const Self;
  1204. NUdf::TUnboxedValue Current;
  1205. NUdf::TUnboxedValue Iterator;
  1206. };
  1207. using TMyCodegenValue = std::conditional_t<ERightKind::Many == RightKind, TCodegenStatefulValue, TCodegenValue>;
  1208. public:
  1209. TMapJoinCoreWrapper(TComputationMutables& mutables, std::vector<TFunctionDescriptor>&& leftKeyConverters,
  1210. TDictType* dictType, std::vector<EValueRepresentation>&& outputRepresentations, std::vector<ui32>&& leftKeyColumns,
  1211. std::vector<ui32>&& leftRenames, std::vector<ui32>&& rightRenames,
  1212. IComputationNode* stream, IComputationNode* dict)
  1213. : TMapJoinCoreWrapperBase<IsTuple>(mutables, std::move(leftKeyConverters), dictType, std::move(outputRepresentations),
  1214. std::move(leftKeyColumns), std::move(leftRenames), std::move(rightRenames), stream, dict), TBaseComputation(mutables)
  1215. {}
  1216. NUdf::TUnboxedValuePod DoCalculate(TComputationContext& ctx) const {
  1217. #ifndef MKQL_DISABLE_CODEGEN
  1218. if (ctx.ExecuteLLVM && MapJoin)
  1219. return ctx.HolderFactory.Create<TMyCodegenValue>(MapJoin, &ctx, this->Stream->GetValue(ctx), this->Dict->GetValue(ctx));
  1220. #endif
  1221. return ctx.HolderFactory.Create<std::conditional_t<ERightKind::Many == RightKind, TMultiRowValue, TValue>>(this->Stream->GetValue(ctx), this->Dict->GetValue(ctx), ctx, this);
  1222. }
  1223. private:
  1224. void RegisterDependencies() const final {
  1225. this->DependsOn(this->Stream);
  1226. this->DependsOn(this->Dict);
  1227. }
  1228. #ifndef MKQL_DISABLE_CODEGEN
  1229. void GenerateFunctions(NYql::NCodegen::ICodegen& codegen) final {
  1230. MapJoinFunc = RightKind == ERightKind::Many ? GenerateStatefulMapper(codegen) : GenerateMapper(codegen);
  1231. codegen.ExportSymbol(MapJoinFunc);
  1232. }
  1233. void FinalizeFunctions(NYql::NCodegen::ICodegen& codegen) final {
  1234. if (MapJoinFunc)
  1235. MapJoin = reinterpret_cast<TMapJoinPtr>(codegen.GetPointerToFunction(MapJoinFunc));
  1236. }
  1237. Function* GenerateMapper(NYql::NCodegen::ICodegen& codegen) const {
  1238. auto& module = codegen.GetModule();
  1239. auto& context = codegen.GetContext();
  1240. const auto& name = TBaseComputation::MakeName("Fetch");
  1241. if (const auto f = module.getFunction(name.c_str()))
  1242. return f;
  1243. const auto valueType = Type::getInt128Ty(context);
  1244. const auto arrayType = ArrayType::get(valueType, this->OutputRepresentations.size());
  1245. const auto keysType = IsTuple ? ArrayType::get(valueType, this->LeftKeyColumns.size()) : nullptr;
  1246. const auto containerType = static_cast<Type*>(valueType);
  1247. const auto contextType = GetCompContextType(context);
  1248. const auto statusType = Type::getInt32Ty(context);
  1249. const auto funcType = FunctionType::get(statusType, {PointerType::getUnqual(contextType), containerType, containerType, PointerType::getUnqual(valueType)}, false);
  1250. TCodegenContext ctx(codegen);
  1251. ctx.Func = cast<Function>(module.getOrInsertFunction(name.c_str(), funcType).getCallee());
  1252. DISubprogramAnnotator annotator(ctx, ctx.Func);
  1253. auto args = ctx.Func->arg_begin();
  1254. ctx.Ctx = &*args;
  1255. const auto streamArg = &*++args;
  1256. const auto dictArg = &*++args;
  1257. const auto valuePtr = &*++args;
  1258. const auto main = BasicBlock::Create(context, "main", ctx.Func);
  1259. auto block = main;
  1260. const auto stream = static_cast<Value*>(streamArg);
  1261. const auto dict = static_cast<Value*>(dictArg);
  1262. const auto zero = ConstantInt::get(valueType, 0);
  1263. const auto fsok = ConstantInt::get(statusType, static_cast<ui32>(NUdf::EFetchStatus::Ok));
  1264. const auto itemsType = PointerType::getUnqual(arrayType);
  1265. const auto itemsPtr = new AllocaInst(itemsType, 0U, "items_ptr", block);
  1266. const auto kitmsPtr = IsTuple ? new AllocaInst(PointerType::getUnqual(keysType), 0U, "kitms_ptr", block) : nullptr;
  1267. const auto keysPtr = new AllocaInst(valueType, 0U, "keys_ptr", block);
  1268. new StoreInst(zero, keysPtr, block);
  1269. const auto itemPtr = new AllocaInst(valueType, 0U, "item_ptr", block);
  1270. new StoreInst(zero, itemPtr, block);
  1271. const auto loop = BasicBlock::Create(context, "loop", ctx.Func);
  1272. const auto stop = BasicBlock::Create(context, "stop", ctx.Func);
  1273. BranchInst::Create(loop, block);
  1274. block = loop;
  1275. const auto status = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Fetch>(statusType, stream, codegen, block, itemPtr);
  1276. ReturnInst::Create(context, status, stop);
  1277. const auto stat = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_NE, status, fsok, "stat", block);
  1278. const auto next = BasicBlock::Create(context, "next", ctx.Func);
  1279. BranchInst::Create(stop, next, stat, block);
  1280. block = next;
  1281. const auto current = new LoadInst(valueType, itemPtr, "current", block);
  1282. const auto none = IsTuple ?
  1283. this->GenMakeKeysTuple(keysPtr, current, kitmsPtr, keysType, ctx, block):
  1284. this->GenMakeKeysTuple(keysPtr, current, ctx, block);
  1285. const auto skip = BasicBlock::Create(context, "skip", ctx.Func);
  1286. const auto step = BasicBlock::Create(context, "step", ctx.Func);
  1287. const auto half = BasicBlock::Create(context, "half", ctx.Func);
  1288. BranchInst::Create(RightRequired ? skip : half, step, none, block);
  1289. block = step;
  1290. switch (RightKind) {
  1291. case ERightKind::None: {
  1292. const auto cont = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Contains>(Type::getInt1Ty(context), dict, codegen, block, keysPtr);
  1293. if constexpr (!IsTuple) {
  1294. ValueUnRef(GetValueRepresentation(this->DictType->GetKeyType()), keysPtr, ctx, block);
  1295. }
  1296. if constexpr (RightRequired) {
  1297. BranchInst::Create(half, skip, cont, block);
  1298. } else {
  1299. BranchInst::Create(skip, half, cont, block);
  1300. }
  1301. break;
  1302. }
  1303. case ERightKind::Once: {
  1304. new StoreInst(zero, itemPtr, block);
  1305. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Lookup>(itemPtr, dict, codegen, block, keysPtr);
  1306. if constexpr (!IsTuple) {
  1307. ValueUnRef(GetValueRepresentation(this->DictType->GetKeyType()), keysPtr, ctx, block);
  1308. }
  1309. const auto lookup = new LoadInst(valueType, itemPtr, "lookup", block);
  1310. const auto ok = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_NE, lookup, zero, "ok", block);
  1311. const auto full = BasicBlock::Create(context, "full", ctx.Func);
  1312. BranchInst::Create(full, RightRequired ? skip : half, ok, block);
  1313. {
  1314. block = full;
  1315. const auto result = this->ResStruct.GenNewArray(this->OutputRepresentations.size(), itemsPtr, ctx, block);
  1316. const auto items = new LoadInst(itemsType, itemsPtr, "items", block);
  1317. this->GenFillLeftStruct(current, items, arrayType, ctx, block);
  1318. this->GenFillRightStruct(lookup, items, arrayType, ctx, block);
  1319. SafeUnRefUnboxedOne(valuePtr, ctx, block);
  1320. AddRefBoxed(result, ctx, block);
  1321. new StoreInst(result, valuePtr, block);
  1322. UnRefBoxed(current, ctx, block);
  1323. UnRefBoxed(lookup, ctx, block);
  1324. ReturnInst::Create(context, fsok, block);
  1325. }
  1326. break;
  1327. }
  1328. case ERightKind::Many:
  1329. Y_ABORT("Wrong case");
  1330. }
  1331. {
  1332. block = half;
  1333. const auto result = this->ResStruct.GenNewArray(this->OutputRepresentations.size(), itemsPtr, ctx, block);
  1334. const auto items = new LoadInst(itemsType, itemsPtr, "items", block);
  1335. this->GenFillLeftStruct(current, items, arrayType, ctx, block);
  1336. SafeUnRefUnboxedOne(valuePtr, ctx, block);
  1337. AddRefBoxed(result, ctx, block);
  1338. new StoreInst(result, valuePtr, block);
  1339. UnRefBoxed(current, ctx, block);
  1340. ReturnInst::Create(context, fsok, block);
  1341. }
  1342. {
  1343. block = skip;
  1344. UnRefBoxed(current, ctx, block);
  1345. new StoreInst(zero, itemPtr, block);
  1346. BranchInst::Create(loop, block);
  1347. }
  1348. return ctx.Func;
  1349. }
  1350. Function* GenerateStatefulMapper(NYql::NCodegen::ICodegen& codegen) const {
  1351. auto& module = codegen.GetModule();
  1352. auto& context = codegen.GetContext();
  1353. const auto& name = TBaseComputation::MakeName("Fetch");
  1354. if (const auto f = module.getFunction(name.c_str()))
  1355. return f;
  1356. const auto valueType = Type::getInt128Ty(context);
  1357. const auto arrayType = ArrayType::get(valueType, this->OutputRepresentations.size());
  1358. const auto keysType = IsTuple ? ArrayType::get(valueType, this->LeftKeyColumns.size()) : nullptr;
  1359. const auto containerType = static_cast<Type*>(valueType);
  1360. const auto contextType = GetCompContextType(context);
  1361. const auto statusType = Type::getInt32Ty(context);
  1362. const auto funcType = FunctionType::get(statusType, {PointerType::getUnqual(contextType), containerType, containerType, PointerType::getUnqual(valueType), PointerType::getUnqual(valueType), PointerType::getUnqual(valueType)}, false);
  1363. TCodegenContext ctx(codegen);
  1364. ctx.Func = cast<Function>(module.getOrInsertFunction(name.c_str(), funcType).getCallee());
  1365. DISubprogramAnnotator annotator(ctx, ctx.Func);
  1366. auto args = ctx.Func->arg_begin();
  1367. ctx.Ctx = &*args;
  1368. const auto streamArg = &*++args;
  1369. const auto dictArg = &*++args;
  1370. const auto currentArg = &*++args;
  1371. const auto iteratorArg = &*++args;
  1372. const auto valuePtr = &*++args;
  1373. const auto main = BasicBlock::Create(context, "main", ctx.Func);
  1374. auto block = main;
  1375. const auto stream = static_cast<Value*>(streamArg);
  1376. const auto dict = static_cast<Value*>(dictArg);
  1377. const auto zero = ConstantInt::get(valueType, 0);
  1378. const auto fsok = ConstantInt::get(statusType, static_cast<ui32>(NUdf::EFetchStatus::Ok));
  1379. const auto itemsType = PointerType::getUnqual(arrayType);
  1380. const auto itemsPtr = new AllocaInst(itemsType, 0U, "items_ptr", block);
  1381. const auto kitmsPtr = IsTuple ? new AllocaInst(PointerType::getUnqual(keysType), 0U, "kitms_ptr", block) : nullptr;
  1382. const auto keysPtr = new AllocaInst(valueType, 0U, "keys_ptr", block);
  1383. const auto itemPtr = new AllocaInst(valueType, 0U, "item_ptr", block);
  1384. new StoreInst(zero, itemPtr, block);
  1385. const auto work = BasicBlock::Create(context, "work", ctx.Func);
  1386. BranchInst::Create(work, block);
  1387. block = work;
  1388. const auto subiter = new LoadInst(valueType, iteratorArg, "subiter", block);
  1389. const auto hasi = BasicBlock::Create(context, "hasi", ctx.Func);
  1390. const auto loop = BasicBlock::Create(context, "loop", ctx.Func);
  1391. const auto full = BasicBlock::Create(context, "full", ctx.Func);
  1392. const auto skip = BasicBlock::Create(context, "loop", ctx.Func);
  1393. const auto ichk = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_NE, subiter, zero, "ichk", block);
  1394. BranchInst::Create(hasi, loop, ichk, block);
  1395. {
  1396. block = hasi;
  1397. const auto status = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Next>(Type::getInt1Ty(context), subiter, codegen, block, itemPtr);
  1398. BranchInst::Create(full, skip, status, block);
  1399. }
  1400. {
  1401. block = skip;
  1402. UnRefBoxed(subiter, ctx, block);
  1403. new StoreInst(zero, iteratorArg, block);
  1404. BranchInst::Create(loop, block);
  1405. }
  1406. {
  1407. block = full;
  1408. const auto result = this->ResStruct.GenNewArray(this->OutputRepresentations.size(), itemsPtr, ctx, block);
  1409. const auto items = new LoadInst(itemsType, itemsPtr, "items", block);
  1410. const auto curr = new LoadInst(valueType, currentArg, "curr", block);
  1411. const auto item = new LoadInst(valueType, itemPtr, "item", block);
  1412. this->GenFillLeftStruct(curr, items, arrayType, ctx, block);
  1413. this->GenFillRightStruct(item, items, arrayType, ctx, block);
  1414. SafeUnRefUnboxedOne(valuePtr, ctx, block);
  1415. AddRefBoxed(result, ctx, block);
  1416. new StoreInst(result, valuePtr, block);
  1417. UnRefBoxed(item, ctx, block);
  1418. ReturnInst::Create(context, fsok, block);
  1419. }
  1420. const auto next = BasicBlock::Create(context, "next", ctx.Func);
  1421. const auto stop = BasicBlock::Create(context, "stop", ctx.Func);
  1422. {
  1423. block = loop;
  1424. const auto status = CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Fetch>(statusType, stream, codegen, block, currentArg);
  1425. ReturnInst::Create(context, status, stop);
  1426. const auto stat = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_NE, status, fsok, "stat", block);
  1427. BranchInst::Create(stop, next, stat, block);
  1428. }
  1429. {
  1430. block = next;
  1431. const auto current = new LoadInst(valueType, currentArg, "current", block);
  1432. const auto none = IsTuple ?
  1433. this->GenMakeKeysTuple(keysPtr, current, kitmsPtr, keysType, ctx, block):
  1434. this->GenMakeKeysTuple(keysPtr, current, ctx, block);
  1435. const auto step = BasicBlock::Create(context, "step", ctx.Func);
  1436. const auto hsnt = RightRequired ? loop : BasicBlock::Create(context, "half", ctx.Func);
  1437. BranchInst::Create(hsnt, step, none, block);
  1438. block = step;
  1439. new StoreInst(zero, itemPtr, block);
  1440. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::Lookup>(itemPtr, dict, ctx.Codegen, block, keysPtr);
  1441. if constexpr (!IsTuple) {
  1442. ValueUnRef(GetValueRepresentation(this->DictType->GetKeyType()), keysPtr, ctx, block);
  1443. }
  1444. const auto lookup = new LoadInst(valueType, itemPtr, "lookup", block);
  1445. const auto ok = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_NE, lookup, zero, "ok", block);
  1446. const auto fill = BasicBlock::Create(context, "fill", ctx.Func);
  1447. BranchInst::Create(fill, hsnt, ok, block);
  1448. if constexpr (!RightRequired) {
  1449. block = hsnt;
  1450. const auto result = this->ResStruct.GenNewArray(this->OutputRepresentations.size(), itemsPtr, ctx, block);
  1451. const auto items = new LoadInst(itemsType, itemsPtr, "items", block);
  1452. this->GenFillLeftStruct(current, items, arrayType, ctx, block);
  1453. SafeUnRefUnboxedOne(valuePtr, ctx, block);
  1454. AddRefBoxed(result, ctx, block);
  1455. new StoreInst(result, valuePtr, block);
  1456. ReturnInst::Create(context, fsok, block);
  1457. }
  1458. {
  1459. block = fill;
  1460. CallBoxedValueVirtualMethod<NUdf::TBoxedValueAccessor::EMethod::GetListIterator>(iteratorArg, lookup, codegen, block);
  1461. CleanupBoxed(lookup, ctx, block);
  1462. BranchInst::Create(work, block);
  1463. }
  1464. }
  1465. return ctx.Func;
  1466. }
  1467. using TMapJoinPtr = typename TMyCodegenValue::TFetchPtr;
  1468. Function* MapJoinFunc = nullptr;
  1469. TMapJoinPtr MapJoin = nullptr;
  1470. #endif
  1471. };
  1472. }
  1473. IComputationNode* WrapMapJoinCore(TCallable& callable, const TComputationNodeFactoryContext& ctx) {
  1474. MKQL_ENSURE(callable.GetInputsCount() == 6, "Expected 6 args");
  1475. const auto type = callable.GetType()->GetReturnType();
  1476. const auto leftStreamNode = callable.GetInput(0);
  1477. const auto leftItemType = leftStreamNode.GetStaticType()->IsFlow() ?
  1478. AS_TYPE(TFlowType, leftStreamNode)->GetItemType():
  1479. AS_TYPE(TStreamType, leftStreamNode)->GetItemType();
  1480. const auto dictNode = callable.GetInput(1);
  1481. const auto dictType = AS_TYPE(TDictType, dictNode);
  1482. const auto dictKeyType = dictType->GetKeyType();
  1483. const auto joinKindNode = callable.GetInput(2);
  1484. const auto rawKind = AS_VALUE(TDataLiteral, joinKindNode)->AsValue().Get<ui32>();
  1485. const auto kind = GetJoinKind(rawKind);
  1486. const bool isMany = dictType->GetPayloadType()->IsList();
  1487. const bool boolWithoutRight = EJoinKind::LeftOnly == kind || EJoinKind::LeftSemi == kind;
  1488. const auto returnItemType = type->IsFlow() ?
  1489. AS_TYPE(TFlowType, callable.GetType()->GetReturnType())->GetItemType():
  1490. AS_TYPE(TStreamType, callable.GetType()->GetReturnType())->GetItemType();
  1491. const auto leftKeyColumnsNode = AS_VALUE(TTupleLiteral, callable.GetInput(3));
  1492. const bool isTupleKey = leftKeyColumnsNode->GetValuesCount() > 1;
  1493. const auto leftRenamesNode = AS_VALUE(TTupleLiteral, callable.GetInput(4));
  1494. const auto rightRenamesNode = AS_VALUE(TTupleLiteral, callable.GetInput(5));
  1495. std::vector<ui32> leftKeyColumns, leftRenames, rightRenames;
  1496. leftKeyColumns.reserve(leftKeyColumnsNode->GetValuesCount());
  1497. for (ui32 i = 0; i < leftKeyColumnsNode->GetValuesCount(); ++i) {
  1498. leftKeyColumns.emplace_back(AS_VALUE(TDataLiteral, leftKeyColumnsNode->GetValue(i))->AsValue().Get<ui32>());
  1499. }
  1500. leftRenames.reserve(leftRenamesNode->GetValuesCount());
  1501. for (ui32 i = 0; i < leftRenamesNode->GetValuesCount(); ++i) {
  1502. leftRenames.emplace_back(AS_VALUE(TDataLiteral, leftRenamesNode->GetValue(i))->AsValue().Get<ui32>());
  1503. }
  1504. rightRenames.reserve(rightRenamesNode->GetValuesCount());
  1505. for (ui32 i = 0; i < rightRenamesNode->GetValuesCount(); ++i) {
  1506. rightRenames.emplace_back(AS_VALUE(TDataLiteral, rightRenamesNode->GetValue(i))->AsValue().Get<ui32>());
  1507. }
  1508. std::vector<TFunctionDescriptor> leftKeyConverters;
  1509. leftKeyConverters.resize(leftKeyColumns.size());
  1510. for (ui32 i = 0; i < leftKeyColumns.size(); ++i) {
  1511. const auto leftColumnType = leftItemType->IsTuple() ?
  1512. AS_TYPE(TTupleType, leftItemType)->GetElementType(leftKeyColumns[i]):
  1513. (leftItemType->IsMulti() ?
  1514. AS_TYPE(TMultiType, leftItemType)->GetElementType(leftKeyColumns[i]):
  1515. AS_TYPE(TStructType, leftItemType)->GetMemberType(leftKeyColumns[i]));
  1516. const auto rightType = isTupleKey ? AS_TYPE(TTupleType, dictKeyType)->GetElementType(i) : dictKeyType;
  1517. bool isOptional;
  1518. if (UnpackOptional(leftColumnType, isOptional)->IsSameType(*rightType)) {
  1519. continue;
  1520. }
  1521. bool isLeftOptional;
  1522. const auto leftDataType = UnpackOptionalData(leftColumnType, isLeftOptional);
  1523. bool isRightOptional;
  1524. const auto rightDataType = UnpackOptionalData(rightType, isRightOptional);
  1525. if (leftDataType->GetSchemeType() != rightDataType->GetSchemeType()) {
  1526. // find a converter
  1527. const std::array<TArgType, 2U> argsTypes = {{{rightDataType->GetSchemeType(), isLeftOptional}, {leftDataType->GetSchemeType(), isLeftOptional}}};
  1528. leftKeyConverters[i] = ctx.FunctionRegistry.GetBuiltins()->GetBuiltin("Convert", argsTypes.data(), 2U);
  1529. }
  1530. }
  1531. std::vector<EValueRepresentation> outputRepresentations;
  1532. if (returnItemType->IsTuple()) {
  1533. const auto tupleType = AS_TYPE(TTupleType, returnItemType);
  1534. outputRepresentations.reserve(tupleType->GetElementsCount());
  1535. for (ui32 i = 0U; i < tupleType->GetElementsCount(); ++i)
  1536. outputRepresentations.emplace_back(GetValueRepresentation(tupleType->GetElementType(i)));
  1537. } else if (returnItemType->IsMulti()) {
  1538. const auto multiType = AS_TYPE(TMultiType, returnItemType);
  1539. outputRepresentations.reserve(multiType->GetElementsCount());
  1540. for (ui32 i = 0U; i < multiType->GetElementsCount(); ++i)
  1541. outputRepresentations.emplace_back(GetValueRepresentation(multiType->GetElementType(i)));
  1542. } else if (returnItemType->IsStruct()) {
  1543. const auto structType = AS_TYPE(TStructType, returnItemType);
  1544. outputRepresentations.reserve(structType->GetMembersCount());
  1545. for (ui32 i = 0U; i < structType->GetMembersCount(); ++i)
  1546. outputRepresentations.emplace_back(GetValueRepresentation(structType->GetMemberType(i)));
  1547. }
  1548. const auto flow = LocateNode(ctx.NodeLocator, callable, 0);
  1549. const auto dict = LocateNode(ctx.NodeLocator, callable, 1);
  1550. #define NEW_WRAPPER(KIND, RIGHT_REQ, IS_TUPLE) \
  1551. if (type->IsFlow()) { \
  1552. if (const auto wide = dynamic_cast<IComputationWideFlowNode*>(flow)) { \
  1553. const auto width = GetWideComponentsCount(AS_TYPE(TFlowType, callable.GetInput(0U).GetStaticType())); \
  1554. if (boolWithoutRight) \
  1555. return new TWideMapJoinWrapper<true, RIGHT_REQ, IS_TUPLE>(ctx.Mutables, \
  1556. std::move(leftKeyConverters), dictType, std::move(outputRepresentations), \
  1557. std::move(leftKeyColumns), std::move(leftRenames), std::move(rightRenames), wide, dict, width); \
  1558. else if (isMany) \
  1559. return new TWideMultiMapJoinWrapper<RIGHT_REQ, IS_TUPLE>(ctx.Mutables, \
  1560. std::move(leftKeyConverters), dictType, std::move(outputRepresentations), \
  1561. std::move(leftKeyColumns), std::move(leftRenames), std::move(rightRenames), wide, dict, width); \
  1562. else \
  1563. return new TWideMapJoinWrapper<false, RIGHT_REQ, IS_TUPLE>(ctx.Mutables, \
  1564. std::move(leftKeyConverters), dictType, std::move(outputRepresentations), \
  1565. std::move(leftKeyColumns), std::move(leftRenames), std::move(rightRenames), wide, dict, width); \
  1566. } else \
  1567. return new TMapJoinCoreFlowWrapper<KIND, RIGHT_REQ, IS_TUPLE>(ctx.Mutables, GetValueRepresentation(type), \
  1568. std::move(leftKeyConverters), dictType, std::move(outputRepresentations), \
  1569. std::move(leftKeyColumns), std::move(leftRenames), std::move(rightRenames), flow, dict); \
  1570. } else { \
  1571. return new TMapJoinCoreWrapper<KIND, RIGHT_REQ, IS_TUPLE>(ctx.Mutables, \
  1572. std::move(leftKeyConverters), dictType, std::move(outputRepresentations), \
  1573. std::move(leftKeyColumns), std::move(leftRenames), std::move(rightRenames), flow, dict); \
  1574. } \
  1575. #define MAKE_WRAPPER(IS_TUPLE) \
  1576. switch (kind) { \
  1577. case EJoinKind::Inner: \
  1578. if (isMany) { \
  1579. NEW_WRAPPER(ERightKind::Many, true, IS_TUPLE); \
  1580. } else { \
  1581. NEW_WRAPPER(ERightKind::Once, true, IS_TUPLE); \
  1582. } \
  1583. case EJoinKind::Left: \
  1584. if (isMany) { \
  1585. NEW_WRAPPER(ERightKind::Many, false, IS_TUPLE); \
  1586. } else { \
  1587. NEW_WRAPPER(ERightKind::Once, false, IS_TUPLE); \
  1588. } \
  1589. case EJoinKind::LeftOnly: \
  1590. NEW_WRAPPER(ERightKind::None, false, IS_TUPLE); \
  1591. case EJoinKind::LeftSemi: \
  1592. NEW_WRAPPER(ERightKind::None, true, IS_TUPLE); \
  1593. default: \
  1594. ythrow yexception() << "Unsupported join kind"; \
  1595. } \
  1596. if (isTupleKey) {
  1597. MAKE_WRAPPER(true)
  1598. } else {
  1599. MAKE_WRAPPER(false)
  1600. }
  1601. #undef MAKE_WRAPPER
  1602. #undef NEW_WRAPPER
  1603. }
  1604. }
  1605. }