mkql_computation_node_holders_codegen.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. #include "mkql_computation_node_holders_codegen.h"
  2. #include "mkql_computation_node_codegen.h" // Y_IGNORE
  3. #include "mkql_computation_node_pack.h"
  4. #include <yql/essentials/minikql/mkql_string_util.h>
  5. namespace NKikimr {
  6. namespace NMiniKQL {
  7. TContainerCacheOnContext::TContainerCacheOnContext(TComputationMutables& mutables)
  8. : Index(mutables.CurValueIndex++)
  9. {
  10. ++++mutables.CurValueIndex;
  11. mutables.CachedValues.insert(mutables.CachedValues.end(), {Index, Index + 1, Index + 2});
  12. }
  13. NUdf::TUnboxedValuePod TContainerCacheOnContext::NewArray(TComputationContext& ctx, ui64 size, NUdf::TUnboxedValue*& items) const {
  14. if (!size)
  15. return ctx.HolderFactory.GetEmptyContainerLazy();
  16. auto& index = ctx.MutableValues[Index];
  17. if (index.IsInvalid())
  18. index = NUdf::TUnboxedValuePod::Zero();
  19. {
  20. auto& val = ctx.MutableValues[Index + (index.Get<bool>() ? 1U : 2U)];
  21. if (val.IsInvalid() || !val.UniqueBoxed()) {
  22. index = NUdf::TUnboxedValuePod(!index.Get<bool>());
  23. auto& value = ctx.MutableValues[Index + (index.Get<bool>() ? 1U : 2U)];
  24. if (value.IsInvalid() || !value.UniqueBoxed()) {
  25. return value = ctx.HolderFactory.CreateDirectArrayHolder(size, items);
  26. }
  27. }
  28. }
  29. auto& value = ctx.MutableValues[Index + (index.Get<bool>() ? 1U : 2U)];
  30. items = static_cast<const TDirectArrayHolderInplace*>(value.AsBoxed().Get())->GetPtr();
  31. std::fill_n(items, size, NUdf::TUnboxedValue());
  32. return value;
  33. }
  34. #ifndef MKQL_DISABLE_CODEGEN
  35. namespace {
  36. Value* GenerateCheckNotUniqueBoxed(Value* value, LLVMContext& context, Function* function, BasicBlock*& block) {
  37. const auto invalid = ConstantInt::get(value->getType(), 0xFFFFFFFFFFFFFFFFULL);
  38. const auto empty = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_ULE, value, invalid, "empty", block);
  39. const auto have = BasicBlock::Create(context, "have", function);
  40. const auto done = BasicBlock::Create(context, "done", function);
  41. const auto result = PHINode::Create(empty->getType(), 2, "result", done);
  42. result->addIncoming(empty, block);
  43. BranchInst::Create(done, have, empty, block);
  44. block = have;
  45. const auto half = CastInst::Create(Instruction::Trunc, value, Type::getInt64Ty(context), "half", block);
  46. const auto type = StructType::get(context, {PointerType::getUnqual(StructType::get(context)), Type::getInt32Ty(context), Type::getInt16Ty(context)});
  47. const auto boxptr = CastInst::Create(Instruction::IntToPtr, half, PointerType::getUnqual(type), "boxptr", block);
  48. const auto cntptr = GetElementPtrInst::CreateInBounds(type, boxptr, {ConstantInt::get(Type::getInt32Ty(context), 0), ConstantInt::get(Type::getInt32Ty(context), 1)}, "cntptr", block);
  49. const auto refs = new LoadInst(Type::getInt32Ty(context), cntptr, "refs", block);
  50. const auto many = CmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_UGT, refs, ConstantInt::get(refs->getType(), 1U), "many", block);
  51. result->addIncoming(many, block);
  52. BranchInst::Create(done, block);
  53. block = done;
  54. return result;
  55. }
  56. }
  57. Value* TContainerCacheOnContext::GenNewArray(ui64 sz, Value* items, const TCodegenContext& ctx, BasicBlock*& block) const {
  58. auto& context = ctx.Codegen.GetContext();
  59. const auto valueType = Type::getInt128Ty(context);
  60. const auto arrayType = ArrayType::get(valueType, sz);
  61. const auto pointerType = PointerType::getUnqual(arrayType);
  62. const auto idxType = Type::getInt32Ty(context);
  63. const auto values = ctx.GetMutables();
  64. const auto indexPtr = GetElementPtrInst::CreateInBounds(valueType, values, {ConstantInt::get(idxType, Index)}, "index_ptr", block);
  65. const auto raw = new LoadInst(valueType, indexPtr, "raw", block);
  66. const auto indb = GetterFor<bool>(raw, context, block);
  67. const auto indf = CastInst::Create(Instruction::ZExt, indb, idxType, "indf", block);
  68. const auto ind_one = BinaryOperator::CreateAdd(indf, ConstantInt::get(idxType, Index + 1U), "ind_one", block);
  69. const auto tpfirst = GetElementPtrInst::CreateInBounds(valueType, values, {ind_one}, "tpfirst", block);
  70. const auto tfirst = new LoadInst(valueType, tpfirst, "tfirst", block);
  71. const auto cfirst = GenerateCheckNotUniqueBoxed(tfirst, context, ctx.Func, block);
  72. const auto scnd = BasicBlock::Create(context, "scnd", ctx.Func);
  73. const auto make = BasicBlock::Create(context, "make", ctx.Func);
  74. const auto have = BasicBlock::Create(context, "have", ctx.Func);
  75. const auto exit = BasicBlock::Create(context, "exit", ctx.Func);
  76. const auto result = PHINode::Create(valueType, 2, "result", exit);
  77. const auto has = PHINode::Create(tfirst->getType(), 2, "has", have);
  78. has->addIncoming(tfirst, block);
  79. BranchInst::Create(scnd, have, cfirst, block);
  80. block = scnd;
  81. const auto neg = BinaryOperator::CreateXor(indb, ConstantInt::get(indb->getType(), 1), "xor", block);
  82. const auto newInd = SetterFor<bool>(neg, context, block);
  83. new StoreInst(newInd, indexPtr, block);
  84. const auto inds = CastInst::Create(Instruction::ZExt, neg, idxType, "inds", block);
  85. const auto ind_two = BinaryOperator::CreateAdd(inds, ConstantInt::get(idxType, Index + 1U), "ind_two", block);
  86. const auto tpsecond = GetElementPtrInst::CreateInBounds(valueType, values, {ind_two}, "tpsecond", block);
  87. const auto tsecond = new LoadInst(valueType, tpsecond, "tsecond", block);
  88. const auto csecond = GenerateCheckNotUniqueBoxed(tsecond, context, ctx.Func, block);
  89. has->addIncoming(tsecond, block);
  90. BranchInst::Create(make, have, csecond, block);
  91. {
  92. block = make;
  93. ValueUnRef(EValueRepresentation::Boxed, tpsecond, ctx, block);
  94. const auto fact = ctx.GetFactory();
  95. const auto func = ConstantInt::get(Type::getInt64Ty(context), GetMethodPtr(&THolderFactory::CreateDirectArrayHolder));
  96. const auto size = ConstantInt::get(Type::getInt64Ty(context), sz);
  97. const auto funType = FunctionType::get(valueType, {fact->getType(), size->getType(), items->getType()}, false);
  98. const auto funcPtr = CastInst::Create(Instruction::IntToPtr, func, PointerType::getUnqual(funType), "function", block);
  99. const auto array = CallInst::Create(funType, funcPtr, {fact, size, items}, "array", block);
  100. AddRefBoxed(array, ctx, block);
  101. result->addIncoming(array, block);
  102. new StoreInst(array, tpsecond, block);
  103. BranchInst::Create(exit, block);
  104. }
  105. {
  106. block = have;
  107. const auto half = CastInst::Create(Instruction::Trunc, has, Type::getInt64Ty(context), "half", block);
  108. const auto offs = BinaryOperator::CreateAdd(half, ConstantInt::get(half->getType(), sizeof(TDirectArrayHolderInplace)), "offs", block);
  109. const auto itemsPtr = CastInst::Create(Instruction::IntToPtr, offs, pointerType, "items_ptr", block);
  110. for (ui64 i = 0; i < sz; ++i) {
  111. const auto itemp = GetElementPtrInst::CreateInBounds(arrayType, itemsPtr, {ConstantInt::get(idxType, 0), ConstantInt::get(idxType, i)}, "itemp", block);
  112. ValueUnRef(EValueRepresentation::Any, itemp, ctx, block);
  113. }
  114. result->addIncoming(has, block);
  115. new StoreInst(ConstantAggregateZero::get(arrayType), itemsPtr, block);
  116. new StoreInst(itemsPtr, items, block);
  117. BranchInst::Create(exit, block);
  118. }
  119. block = exit;
  120. return result;
  121. }
  122. #endif
  123. class TEmptyNode : public TMutableCodegeneratorNode<TEmptyNode> {
  124. typedef TMutableCodegeneratorNode<TEmptyNode> TBaseComputation;
  125. public:
  126. TEmptyNode(TComputationMutables& mutables)
  127. : TBaseComputation(mutables, EValueRepresentation::Boxed)
  128. {}
  129. NUdf::TUnboxedValuePod DoCalculate(TComputationContext& ctx) const {
  130. return ctx.HolderFactory.GetEmptyContainerLazy();
  131. }
  132. #ifndef MKQL_DISABLE_CODEGEN
  133. Value* DoGenerateGetValue(const TCodegenContext& ctx, BasicBlock*& block) const {
  134. auto& context = ctx.Codegen.GetContext();
  135. const auto valueType = Type::getInt128Ty(context);
  136. const auto factory = ctx.GetFactory();
  137. const auto func = ConstantInt::get(Type::getInt64Ty(context), GetMethodPtr(&THolderFactory::GetEmptyContainerLazy));
  138. const auto funType = FunctionType::get(valueType, {factory->getType()}, false);
  139. const auto funcPtr = CastInst::Create(Instruction::IntToPtr, func, PointerType::getUnqual(funType), "function", block);
  140. const auto res = CallInst::Create(funType, funcPtr, {factory}, "res", block);
  141. return res;
  142. }
  143. #endif
  144. private:
  145. void RegisterDependencies() const final {}
  146. };
  147. class TOptionalNode: public TDecoratorCodegeneratorNode<TOptionalNode> {
  148. typedef TDecoratorCodegeneratorNode<TOptionalNode> TBaseComputation;
  149. public:
  150. TOptionalNode(IComputationNode* itemNode)
  151. : TBaseComputation(itemNode)
  152. {}
  153. NUdf::TUnboxedValuePod DoCalculate(TComputationContext&, const NUdf::TUnboxedValuePod& value) const {
  154. return value.MakeOptional();
  155. }
  156. #ifndef MKQL_DISABLE_CODEGEN
  157. Value* DoGenerateGetValue(const TCodegenContext& ctx, Value* arg, BasicBlock*& block) const {
  158. return MakeOptional(ctx.Codegen.GetContext(), arg, block);
  159. }
  160. #endif
  161. };
  162. class TArrayNode: public TMutableCodegeneratorFallbackNode<TArrayNode> {
  163. typedef TMutableCodegeneratorFallbackNode<TArrayNode> TBaseComputation;
  164. public:
  165. TArrayNode(TComputationMutables& mutables, TComputationNodePtrVector&& valueNodes)
  166. : TBaseComputation(mutables, EValueRepresentation::Boxed)
  167. , ValueNodes(std::move(valueNodes))
  168. , Cache(mutables)
  169. {
  170. }
  171. NUdf::TUnboxedValuePod DoCalculate(TComputationContext& ctx) const {
  172. NUdf::TUnboxedValue *items = nullptr;
  173. const auto result = Cache.NewArray(ctx, ValueNodes.size(), items);
  174. if (!ValueNodes.empty()) {
  175. Y_ABORT_UNLESS(items);
  176. for (const auto& node : ValueNodes) {
  177. *items++ = node->GetValue(ctx);
  178. }
  179. }
  180. return result;
  181. }
  182. #ifndef MKQL_DISABLE_CODEGEN
  183. Value* DoGenerateGetValue(const TCodegenContext& ctx, BasicBlock*& block) const {
  184. if (ValueNodes.size() > CodegenArraysFallbackLimit)
  185. return TBaseComputation::DoGenerateGetValue(ctx, block);
  186. auto& context = ctx.Codegen.GetContext();
  187. const auto valType = Type::getInt128Ty(context);
  188. const auto idxType = Type::getInt32Ty(context);
  189. const auto type = ArrayType::get(valType, ValueNodes.size());
  190. const auto ptrType = PointerType::getUnqual(type);
  191. /// TODO: how to get computation context or other workaround
  192. const auto itms = *Stateless || ctx.AlwaysInline ?
  193. new AllocaInst(ptrType, 0U, "itms", &ctx.Func->getEntryBlock().back()):
  194. new AllocaInst(ptrType, 0U, "itms", block);
  195. const auto result = Cache.GenNewArray(ValueNodes.size(), itms, ctx, block);
  196. const auto itemsPtr = new LoadInst(ptrType, itms, "items", block);
  197. ui32 i = 0U;
  198. for (const auto node : ValueNodes) {
  199. const auto itemPtr = GetElementPtrInst::CreateInBounds(type, itemsPtr, {ConstantInt::get(idxType, 0), ConstantInt::get(idxType, i++)}, "item", block);
  200. GetNodeValue(itemPtr, node, ctx, block);
  201. }
  202. return result;
  203. }
  204. #endif
  205. private:
  206. void RegisterDependencies() const final {
  207. std::for_each(ValueNodes.cbegin(), ValueNodes.cend(), std::bind(&TArrayNode::DependsOn, this, std::placeholders::_1));
  208. }
  209. const TComputationNodePtrVector ValueNodes;
  210. const TContainerCacheOnContext Cache;
  211. };
  212. class TVariantNode : public TMutableCodegeneratorNode<TVariantNode> {
  213. typedef TMutableCodegeneratorNode<TVariantNode> TBaseComputation;
  214. public:
  215. TVariantNode(TComputationMutables& mutables, IComputationNode* itemNode, ui32 index)
  216. : TBaseComputation(mutables, EValueRepresentation::Any)
  217. , ItemNode(itemNode)
  218. , Index(index)
  219. {}
  220. NUdf::TUnboxedValuePod DoCalculate(TComputationContext& ctx) const {
  221. if (auto item = ItemNode->GetValue(ctx); item.TryMakeVariant(Index))
  222. return item.Release();
  223. else
  224. return ctx.HolderFactory.CreateBoxedVariantHolder(item.Release(), Index);
  225. }
  226. #ifndef MKQL_DISABLE_CODEGEN
  227. Value* DoGenerateGetValue(const TCodegenContext& ctx, BasicBlock*& block) const {
  228. const auto value = GetNodeValue(ItemNode, ctx, block);
  229. return MakeVariant(value, ConstantInt::get(Type::getInt32Ty(ctx.Codegen.GetContext()), Index), ctx, block);
  230. }
  231. #endif
  232. private:
  233. void RegisterDependencies() const final {
  234. DependsOn(ItemNode);
  235. }
  236. IComputationNode *const ItemNode;
  237. const ui32 Index;
  238. };
  239. class TDictNode: public TMutableComputationNode<TDictNode> {
  240. typedef TMutableComputationNode<TDictNode> TBaseComputation;
  241. public:
  242. TDictNode(TComputationMutables& mutables,
  243. std::vector<std::pair<IComputationNode*, IComputationNode*>>&& itemNodes,
  244. const TKeyTypes& types, bool isTuple, TType* encodedType,
  245. NUdf::IHash::TPtr hash, NUdf::IEquate::TPtr equate,
  246. NUdf::ICompare::TPtr compare, bool isSorted)
  247. : TBaseComputation(mutables)
  248. , ItemNodes(std::move(itemNodes))
  249. , Types(types)
  250. , IsTuple(isTuple)
  251. , EncodedType(encodedType)
  252. , Hash(hash)
  253. , Equate(equate)
  254. , Compare(compare)
  255. , IsSorted(isSorted)
  256. {}
  257. NUdf::TUnboxedValuePod DoCalculate(TComputationContext& ctx) const {
  258. TKeyPayloadPairVector items;
  259. items.reserve(ItemNodes.size());
  260. for (const auto& node : ItemNodes) {
  261. items.emplace_back(node.first->GetValue(ctx), node.second->GetValue(ctx));
  262. }
  263. std::optional<TValuePacker> packer;
  264. if (EncodedType) {
  265. packer.emplace(true, EncodedType);
  266. }
  267. if (IsSorted) {
  268. const TSortedDictFiller filler = [&](TKeyPayloadPairVector& values) {
  269. values = std::move(items);
  270. };
  271. return ctx.HolderFactory.CreateDirectSortedDictHolder(filler, Types, IsTuple, EDictSortMode::RequiresSorting,
  272. true, EncodedType, Compare.Get(), Equate.Get());
  273. } else {
  274. THashedDictFiller filler =
  275. [&items, &packer](TValuesDictHashMap& map) {
  276. for (auto& value : items) {
  277. auto key = std::move(value.first);
  278. if (packer) {
  279. key = MakeString(packer->Pack(key));
  280. }
  281. map.emplace(std::move(key), std::move(value.second));
  282. }
  283. };
  284. return ctx.HolderFactory.CreateDirectHashedDictHolder(
  285. filler, Types, IsTuple, true, EncodedType, Hash.Get(), Equate.Get());
  286. }
  287. }
  288. private:
  289. void RegisterDependencies() const final {
  290. for (const auto& itemNode : ItemNodes) {
  291. DependsOn(itemNode.first);
  292. DependsOn(itemNode.second);
  293. }
  294. }
  295. const std::vector<std::pair<IComputationNode*, IComputationNode*>> ItemNodes;
  296. const TKeyTypes Types;
  297. const bool IsTuple;
  298. TType* EncodedType;
  299. NUdf::IHash::TPtr Hash;
  300. NUdf::IEquate::TPtr Equate;
  301. NUdf::ICompare::TPtr Compare;
  302. const bool IsSorted;
  303. };
  304. //////////////////////////////////////////////////////////////////////////////
  305. // TNodeFactory
  306. //////////////////////////////////////////////////////////////////////////////
  307. TNodeFactory::TNodeFactory(TMemoryUsageInfo& memInfo, TComputationMutables& mutables)
  308. : MemInfo(memInfo)
  309. , Mutables(mutables)
  310. {
  311. }
  312. IComputationNode* TNodeFactory::CreateEmptyNode() const
  313. {
  314. return new TEmptyNode(Mutables);
  315. }
  316. IComputationNode* TNodeFactory::CreateOptionalNode(IComputationNode* item) const
  317. {
  318. return item ? new TOptionalNode(item) : CreateImmutableNode(NUdf::TUnboxedValuePod());
  319. }
  320. IComputationNode* TNodeFactory::CreateArrayNode(TComputationNodePtrVector&& values) const
  321. {
  322. if (values.empty()) {
  323. return new TEmptyNode(Mutables);
  324. }
  325. return new TArrayNode(Mutables, std::move(values));
  326. }
  327. IComputationNode* TNodeFactory::CreateDictNode(
  328. std::vector<std::pair<IComputationNode*, IComputationNode*>>&& items,
  329. const TKeyTypes& types, bool isTuple, TType* encodedType,
  330. NUdf::IHash::TPtr hash, NUdf::IEquate::TPtr equate, NUdf::ICompare::TPtr compare, bool isSorted) const
  331. {
  332. if (items.empty()) {
  333. return new TEmptyNode(Mutables);
  334. }
  335. return new TDictNode(Mutables, std::move(items), types, isTuple, encodedType, hash, equate, compare, isSorted);
  336. }
  337. IComputationNode* TNodeFactory::CreateVariantNode(IComputationNode* item, ui32 index) const {
  338. return new TVariantNode(Mutables, item, index);
  339. }
  340. IComputationNode* TNodeFactory::CreateTypeNode(TType* type) const {
  341. return CreateImmutableNode(NUdf::TUnboxedValuePod(new TTypeHolder(&MemInfo, type)));
  342. }
  343. IComputationNode* TNodeFactory::CreateImmutableNode(NUdf::TUnboxedValue&& value) const {
  344. return new TUnboxedImmutableCodegeneratorNode(&MemInfo, std::move(value));
  345. }
  346. } // namespace NMiniKQL
  347. } // namespace NKikimr