OmniSciDB  72c90bc290
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TableFunctionCompilationContext.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2022 HEAVY.AI, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
18 
19 #include <llvm/IR/InstIterator.h>
20 #include <llvm/IR/Verifier.h>
21 #include <llvm/Support/raw_os_ostream.h>
22 #include <llvm/Transforms/Utils/BasicBlockUtils.h>
23 #include <algorithm>
24 #include <boost/algorithm/string.hpp>
25 
28 
29 namespace {
30 
31 llvm::Function* generate_entry_point(const CgenState* cgen_state) {
32  auto& ctx = cgen_state->context_;
33  const auto pi8_type = llvm::PointerType::get(get_int_type(8, ctx), 0);
34  const auto ppi8_type = llvm::PointerType::get(pi8_type, 0);
35  const auto pi64_type = llvm::PointerType::get(get_int_type(64, ctx), 0);
36  const auto ppi64_type = llvm::PointerType::get(pi64_type, 0);
37  const auto i32_type = get_int_type(32, ctx);
38 
39  const auto func_type = llvm::FunctionType::get(
40  i32_type,
41  {pi8_type, ppi8_type, pi64_type, ppi8_type, ppi64_type, ppi8_type, pi64_type},
42  false);
43 
44  auto func = llvm::Function::Create(func_type,
45  llvm::Function::ExternalLinkage,
46  "call_table_function",
47  cgen_state->module_);
48  auto arg_it = func->arg_begin();
49  const auto mgr_arg = &*arg_it;
50  mgr_arg->setName("mgr_ptr");
51  const auto input_cols_arg = &*(++arg_it);
52  input_cols_arg->setName("input_col_buffers");
53  const auto input_row_counts = &*(++arg_it);
54  input_row_counts->setName("input_row_counts");
55  const auto input_str_dict_proxies = &*(++arg_it);
56  input_str_dict_proxies->setName("input_str_dict_proxies");
57  const auto output_buffers = &*(++arg_it);
58  output_buffers->setName("output_buffers");
59  const auto output_str_dict_proxies = &*(++arg_it);
60  output_str_dict_proxies->setName("output_str_dict_proxies");
61  const auto output_row_count = &*(++arg_it);
62  output_row_count->setName("output_row_count");
63  return func;
64 }
65 
67  llvm::LLVMContext& ctx) {
68  if (elem_ti.is_fp()) {
69  return get_fp_ptr_type(elem_ti.get_size() * 8, ctx);
70  } else if (elem_ti.is_boolean()) {
71  return get_int_ptr_type(8, ctx);
72  } else if (elem_ti.is_integer()) {
73  return get_int_ptr_type(elem_ti.get_size() * 8, ctx);
74  } else if (elem_ti.is_string()) {
75  if (elem_ti.get_compression() == kENCODING_DICT) {
76  return get_int_ptr_type(elem_ti.get_size() * 8, ctx);
77  }
78  CHECK(elem_ti.is_text_encoding_none());
79  return get_int_ptr_type(8, ctx);
80  } else if (elem_ti.is_timestamp()) {
81  return get_int_ptr_type(elem_ti.get_size() * 8, ctx);
82  } else if (elem_ti.usesFlatBuffer()) {
83  return get_int_ptr_type(8, ctx);
84  }
85  LOG(FATAL) << "get_llvm_type_from_sql_column_type: not implemented for "
86  << ::toString(elem_ti);
87  return nullptr;
88 }
89 
90 void initialize_ptr_member(llvm::Value* member_ptr,
91  llvm::Type* member_llvm_type,
92  llvm::Value* value_ptr,
93  llvm::IRBuilder<>& ir_builder) {
94  if (value_ptr != nullptr) {
95  if (value_ptr->getType() == member_llvm_type->getPointerElementType()) {
96  ir_builder.CreateStore(value_ptr, member_ptr);
97  } else {
98  auto tmp = ir_builder.CreateBitCast(value_ptr, member_llvm_type);
99  ir_builder.CreateStore(tmp, member_ptr);
100  }
101  } else {
102  ir_builder.CreateStore(llvm::Constant::getNullValue(member_llvm_type), member_ptr);
103  }
104 }
105 
106 template <typename T>
107 void initialize_int_member(llvm::Value* member_ptr,
108  llvm::Value* value,
109  int64_t default_value,
110  llvm::LLVMContext& ctx,
111  llvm::IRBuilder<>& ir_builder) {
112  llvm::Value* val = nullptr;
113  if (value != nullptr) {
114  auto value_type = value->getType();
115  if (value_type->isPointerTy()) {
116  CHECK(value_type->getPointerElementType()->isIntegerTy(sizeof(T) * 8));
117  val = ir_builder.CreateLoad(value->getType()->getPointerElementType(), value);
118  } else {
119  CHECK(value_type->isIntegerTy(sizeof(T) * 8));
120  val = value;
121  }
122  ir_builder.CreateStore(val, member_ptr);
123  } else {
124  auto const_default = ll_int<T>(default_value, ctx);
125  ir_builder.CreateStore(const_default, member_ptr);
126  }
127 }
128 
129 std::tuple<llvm::Value*, llvm::Value*> alloc_column(std::string col_name,
130  const size_t index,
131  const SQLTypeInfo& data_target_info,
132  llvm::Value* data_ptr,
133  llvm::Value* data_size,
134  llvm::Value* data_str_dict_proxy_ptr,
135  llvm::LLVMContext& ctx,
136  llvm::IRBuilder<>& ir_builder) {
137  /*
138  Creates a new Column instance of given element type and initialize
139  its data ptr and sz members when specified. If data ptr or sz are
140  unspecified (have nullptr values) then the corresponding members
141  are initialized with NULL and -1, respectively.
142 
143  If we are allocating a TextEncodingDict Column type, this function
144  adds and populates a int8* pointer to a StringDictProxy object.
145 
146  Return a pair of Column allocation (caller should apply
147  builder.CreateLoad to it in order to construct a Column instance
148  as a value) and a pointer to the Column instance.
149  */
150  const bool is_text_encoding_dict_type =
151  data_target_info.is_string() &&
152  data_target_info.get_compression() == kENCODING_DICT;
153  llvm::StructType* col_struct_type;
154  llvm::Type* data_ptr_llvm_type =
155  get_llvm_type_from_sql_column_type(data_target_info, ctx);
156  if (is_text_encoding_dict_type) {
157  col_struct_type = llvm::StructType::get(
158  ctx,
159  {
160  data_ptr_llvm_type, /* T* ptr */
161  llvm::Type::getInt64Ty(ctx), /* int64_t sz */
162  llvm::Type::getInt8PtrTy(ctx) /* int8_t* string_dictionary_ptr */
163  });
164  } else {
165  col_struct_type =
166  llvm::StructType::get(ctx,
167  {
168  data_ptr_llvm_type, /* T* ptr */
169  llvm::Type::getInt64Ty(ctx) /* int64_t sz */
170  });
171  }
172 
173  auto col = ir_builder.CreateAlloca(col_struct_type);
174  col->setName(col_name);
175  auto col_ptr_ptr = ir_builder.CreateStructGEP(col_struct_type, col, 0);
176  auto col_sz_ptr = ir_builder.CreateStructGEP(col_struct_type, col, 1);
177  auto col_str_dict_ptr = is_text_encoding_dict_type
178  ? ir_builder.CreateStructGEP(col_struct_type, col, 2)
179  : nullptr;
180  col_ptr_ptr->setName(col_name + ".ptr");
181  col_sz_ptr->setName(col_name + ".sz");
182  if (is_text_encoding_dict_type) {
183  col_str_dict_ptr->setName(col_name + ".string_dict_proxy");
184  }
185 
186  initialize_ptr_member(col_ptr_ptr, data_ptr_llvm_type, data_ptr, ir_builder);
187  initialize_int_member<int64_t>(col_sz_ptr, data_size, -1, ctx, ir_builder);
188  if (is_text_encoding_dict_type) {
189  initialize_ptr_member(col_str_dict_ptr,
190  llvm::Type::getInt8PtrTy(ctx),
191  data_str_dict_proxy_ptr,
192  ir_builder);
193  }
194  auto col_ptr = ir_builder.CreatePointerCast(
195  col_ptr_ptr, llvm::PointerType::get(llvm::Type::getInt8Ty(ctx), 0));
196  col_ptr->setName(col_name + "_ptr");
197  return {col, col_ptr};
198 }
199 
200 llvm::Value* alloc_column_list(std::string col_list_name,
201  const SQLTypeInfo& data_target_info,
202  llvm::Value* data_ptrs,
203  int length,
204  llvm::Value* data_size,
205  llvm::Value* data_str_dict_proxy_ptrs,
206  llvm::LLVMContext& ctx,
207  llvm::IRBuilder<>& ir_builder) {
208  /*
209  Creates a new ColumnList instance of given element type and initialize
210  its members. If data ptr or size are unspecified (have nullptr
211  values) then the corresponding members are initialized with NULL
212  and -1, respectively.
213  */
214  llvm::Type* data_ptrs_llvm_type = llvm::Type::getInt8PtrTy(ctx);
215  const bool is_text_encoding_dict_type =
216  data_target_info.is_string() &&
217  data_target_info.get_compression() == kENCODING_DICT;
218 
219  llvm::StructType* col_list_struct_type =
220  is_text_encoding_dict_type
221  ? llvm::StructType::get(
222  ctx,
223  {
224  data_ptrs_llvm_type, /* int8_t* ptrs */
225  llvm::Type::getInt64Ty(ctx), /* int64_t length */
226  llvm::Type::getInt64Ty(ctx), /* int64_t size */
227  data_ptrs_llvm_type /* int8_t* str_dict_proxy_ptrs */
228  })
229  : llvm::StructType::get(ctx,
230  {
231  data_ptrs_llvm_type, /* int8_t* ptrs */
232  llvm::Type::getInt64Ty(ctx), /* int64_t length */
233  llvm::Type::getInt64Ty(ctx) /* int64_t size */
234  });
235 
236  auto col_list = ir_builder.CreateAlloca(col_list_struct_type);
237  col_list->setName(col_list_name);
238  auto col_list_ptr_ptr = ir_builder.CreateStructGEP(col_list_struct_type, col_list, 0);
239  auto col_list_length_ptr =
240  ir_builder.CreateStructGEP(col_list_struct_type, col_list, 1);
241  auto col_list_size_ptr = ir_builder.CreateStructGEP(col_list_struct_type, col_list, 2);
242  auto col_str_dict_ptr_ptr =
243  is_text_encoding_dict_type
244  ? ir_builder.CreateStructGEP(col_list_struct_type, col_list, 3)
245  : nullptr;
246 
247  col_list_ptr_ptr->setName(col_list_name + ".ptrs");
248  col_list_length_ptr->setName(col_list_name + ".length");
249  col_list_size_ptr->setName(col_list_name + ".size");
250  if (is_text_encoding_dict_type) {
251  col_str_dict_ptr_ptr->setName(col_list_name + ".string_dict_proxies");
252  }
253 
254  initialize_ptr_member(col_list_ptr_ptr, data_ptrs_llvm_type, data_ptrs, ir_builder);
255 
256  CHECK(length >= 0);
257  auto const_length = llvm::ConstantInt::get(llvm::Type::getInt64Ty(ctx), length, true);
258  ir_builder.CreateStore(const_length, col_list_length_ptr);
259 
260  initialize_int_member<int64_t>(col_list_size_ptr, data_size, -1, ctx, ir_builder);
261 
262  if (is_text_encoding_dict_type) {
263  initialize_ptr_member(col_str_dict_ptr_ptr,
264  data_str_dict_proxy_ptrs->getType(),
265  data_str_dict_proxy_ptrs,
266  ir_builder);
267  }
268 
269  auto col_list_ptr = ir_builder.CreatePointerCast(
270  col_list_ptr_ptr, llvm::PointerType::get(llvm::Type::getInt8Ty(ctx), 0));
271  col_list_ptr->setName(col_list_name + "_ptrs");
272  return col_list_ptr;
273 }
274 
275 llvm::Value* alloc_array(std::string arr_name,
276  const size_t index,
277  const SQLTypeInfo& data_target_info,
278  llvm::Value* data_ptr,
279  llvm::Value* data_size,
280  llvm::Value* data_is_null,
281  llvm::LLVMContext& ctx,
282  llvm::IRBuilder<>& ir_builder) {
283  /*
284  Creates a new Array instance of given element type and initialize
285  its data ptr and sz members when specified. If data ptr or sz are
286  unspecified (have nullptr values) then the corresponding members
287  are initialized with NULL and -1, respectively.
288 
289  Return a pointer to Array instance.
290  */
291  llvm::StructType* arr_struct_type;
292  llvm::Type* data_ptr_llvm_type =
293  get_llvm_type_from_sql_column_type(data_target_info.get_elem_type(), ctx);
294  arr_struct_type =
295  llvm::StructType::get(ctx,
296  {
297  data_ptr_llvm_type, /* T* ptr_ */
298  llvm::Type::getInt64Ty(ctx), /* int64_t size_ */
299  llvm::Type::getInt8Ty(ctx) /* int8_t is_null_ */
300  });
301 
302  auto arr = ir_builder.CreateAlloca(arr_struct_type);
303  arr->setName(arr_name);
304  auto arr_ptr_ptr = ir_builder.CreateStructGEP(arr_struct_type, arr, 0);
305  auto arr_sz_ptr = ir_builder.CreateStructGEP(arr_struct_type, arr, 1);
306  auto arr_is_null_ptr = ir_builder.CreateStructGEP(arr_struct_type, arr, 2);
307  arr_ptr_ptr->setName(arr_name + ".ptr");
308  arr_sz_ptr->setName(arr_name + ".size");
309  arr_is_null_ptr->setName(arr_name + ".is_null");
310 
311  initialize_ptr_member(arr_ptr_ptr, data_ptr_llvm_type, data_ptr, ir_builder);
312  initialize_int_member<int64_t>(arr_sz_ptr, data_size, -1, ctx, ir_builder);
313  initialize_int_member<int8_t>(arr_is_null_ptr, data_is_null, -1, ctx, ir_builder);
314  auto arr_ptr = ir_builder.CreatePointerCast(
315  arr_ptr_ptr, llvm::PointerType::get(llvm::Type::getInt8Ty(ctx), 0));
316  arr_ptr->setName(arr_name + "_pointer");
317  return arr_ptr;
318 }
319 
320 std::string exprsKey(const std::vector<Analyzer::Expr*>& exprs) {
321  std::string result;
322  for (const auto& expr : exprs) {
323  const auto& ti = expr->get_type_info();
324  result += ti.to_string() + ", ";
325  }
326  return result;
327 }
328 
329 } // namespace
330 
331 std::shared_ptr<CompilationContext> TableFunctionCompilationContext::compile(
332  const TableFunctionExecutionUnit& exe_unit,
333  bool emit_only_preflight_fn) {
334  auto timer = DEBUG_TIMER(__func__);
335 
336  // Here we assume that Executor::tf_code_accessor is cleared when a
337  // UDTF implementation is changed. TODO: Ideally, the key should
338  // contain a hash of an UDTF implementation string. This could be
339  // achieved by including the hash value to the prefix of the UDTF
340  // name, for instance.
341  CodeCacheKey key{exe_unit.table_func.getName(),
342  exprsKey(exe_unit.input_exprs),
343  exprsKey(exe_unit.target_exprs),
344  std::to_string(emit_only_preflight_fn),
346 
347  auto cached_code = QueryEngine::getInstance()->tf_code_accessor->get_or_wait(key);
348  if (cached_code) {
349  return *cached_code;
350  }
351 
352  auto cgen_state = executor_->getCgenStatePtr();
353  CHECK(cgen_state);
354  CHECK(cgen_state->module_ == nullptr);
355  cgen_state->set_module_shallow_copy(executor_->get_rt_module());
356 
358 
359  generateEntryPoint(exe_unit, emit_only_preflight_fn);
360 
362  CHECK(!emit_only_preflight_fn);
364  }
365  std::shared_ptr<CompilationContext> code;
366  try {
367  code = finalize(emit_only_preflight_fn);
368  } catch (const std::exception& e) {
369  // Erase unsuccesful key and release lock from the get_or_wait(key) call above:
370  QueryEngine::getInstance()->tf_code_accessor->erase(key);
371  throw;
372  }
373  // get_or_wait added code with nullptr to cache, here we reset the
374  // cached code with the pointer to the generated code:
375  QueryEngine::getInstance()->tf_code_accessor->reset(key, code);
376  return code;
377 }
378 
380  const TableFunctionExecutionUnit& exe_unit) {
381  bool is_gpu = co_.device_type == ExecutorDeviceType::GPU;
382  auto mod = executor_->get_rt_udf_module(is_gpu).get();
383  if (mod != nullptr) {
384  auto* flag = mod->getModuleFlag("pass_column_arguments_by_value");
385  if (auto* cnt = llvm::mdconst::extract_or_null<llvm::ConstantInt>(flag)) {
386  return cnt->getZExtValue();
387  }
388  }
389 
390  // fallback to original behavior
391  return exe_unit.table_func.isRuntime();
392 }
393 
395  const TableFunctionExecutionUnit& exe_unit,
396  const std::vector<llvm::Value*>& func_args,
397  llvm::BasicBlock* bb_exit,
398  llvm::Value* output_row_count_ptr,
399  bool emit_only_preflight_fn) {
400  auto cgen_state = executor_->getCgenStatePtr();
401  // Emit llvm IR code to call the table function
402  llvm::LLVMContext& ctx = cgen_state->context_;
403  llvm::IRBuilder<>* ir_builder = &cgen_state->ir_builder_;
404 
405  std::string func_name =
406  (emit_only_preflight_fn ? exe_unit.table_func.getPreFlightFnName()
407  : exe_unit.table_func.getName(false, true));
408  llvm::Value* table_func_return =
409  cgen_state->emitExternalCall(func_name, get_int_type(32, ctx), func_args);
410 
411  table_func_return->setName(emit_only_preflight_fn ? "preflight_check_func_ret"
412  : "table_func_ret");
413 
414  // If table_func_return is non-negative then store the value in
415  // output_row_count and return zero. Otherwise, return
416  // table_func_return that negative value contains the error code.
417  llvm::BasicBlock* bb_exit_0 =
418  llvm::BasicBlock::Create(ctx, ".exit0", entry_point_func_);
419 
420  llvm::Constant* const_zero =
421  llvm::ConstantInt::get(table_func_return->getType(), 0, true);
422  llvm::Value* is_ok = ir_builder->CreateICmpSGE(table_func_return, const_zero);
423  ir_builder->CreateCondBr(is_ok, bb_exit_0, bb_exit);
424 
425  ir_builder->SetInsertPoint(bb_exit_0);
426  llvm::Value* r =
427  ir_builder->CreateIntCast(table_func_return, get_int_type(64, ctx), true);
428  ir_builder->CreateStore(r, output_row_count_ptr);
429  ir_builder->CreateRet(const_zero);
430 
431  ir_builder->SetInsertPoint(bb_exit);
432  // when table_func_return == TableFunctionErrorCode::NotAnError,
433  // then the table function is considered a success while
434  // output_row_count_ptr will be uninitialized and the output row
435  // count is defined by other means, see QE-877.
436  ir_builder->CreateRet(table_func_return);
437 }
438 
440  const TableFunctionExecutionUnit& exe_unit,
441  bool emit_only_preflight_fn) {
442  auto timer = DEBUG_TIMER(__func__);
444  CHECK_EQ(entry_point_func_->arg_size(), 7);
445  auto arg_it = entry_point_func_->arg_begin();
446  const auto mgr_ptr = &*arg_it;
447  const auto input_cols_arg = &*(++arg_it);
448  const auto input_row_counts_arg = &*(++arg_it);
449  const auto input_str_dict_proxies_arg = &*(++arg_it);
450  const auto output_buffers_arg = &*(++arg_it);
451  const auto output_str_dict_proxies_arg = &*(++arg_it);
452  const auto output_row_count_ptr = &*(++arg_it);
453  auto cgen_state = executor_->getCgenStatePtr();
454  CHECK(cgen_state);
455  auto& ctx = cgen_state->context_;
456 
457  llvm::BasicBlock* bb_entry =
458  llvm::BasicBlock::Create(ctx, ".entry", entry_point_func_, 0);
459  cgen_state->ir_builder_.SetInsertPoint(bb_entry);
460 
461  llvm::BasicBlock* bb_exit = llvm::BasicBlock::Create(ctx, ".exit", entry_point_func_);
462 
463  llvm::BasicBlock* func_body_bb = llvm::BasicBlock::Create(
464  ctx, ".func_body0", cgen_state->ir_builder_.GetInsertBlock()->getParent());
465 
466  cgen_state->ir_builder_.SetInsertPoint(bb_entry);
467  cgen_state->ir_builder_.CreateBr(func_body_bb);
468 
469  cgen_state->ir_builder_.SetInsertPoint(func_body_bb);
470  auto col_heads = generate_column_heads_load(
471  exe_unit.input_exprs.size(), input_cols_arg, cgen_state->ir_builder_, ctx);
472  CHECK_EQ(exe_unit.input_exprs.size(), col_heads.size());
473  auto row_count_heads = generate_column_heads_load(
474  exe_unit.input_exprs.size(), input_row_counts_arg, cgen_state->ir_builder_, ctx);
475 
476  auto input_str_dict_proxy_heads = std::vector<llvm::Value*>();
478  input_str_dict_proxy_heads = generate_column_heads_load(exe_unit.input_exprs.size(),
479  input_str_dict_proxies_arg,
480  cgen_state->ir_builder_,
481  ctx);
482  }
483  // The column arguments of C++ UDTFs processed by clang must be
484  // passed by reference, see rbc issues 200 and 289.
485  auto pass_column_by_value = passColumnsByValue(exe_unit);
486  std::vector<llvm::Value*> func_args;
487  size_t func_arg_index = 0;
488  if (exe_unit.table_func.usesManager()) {
489  func_args.push_back(mgr_ptr);
490  func_arg_index++;
491  }
492  int col_index = -1;
493 
494  for (size_t i = 0; i < exe_unit.input_exprs.size(); i++) {
495  const auto& expr = exe_unit.input_exprs[i];
496  const auto& ti = expr->get_type_info();
497  if (col_index == -1) {
498  func_arg_index += 1;
499  }
500  if (ti.is_fp()) {
501  auto r = cgen_state->ir_builder_.CreateBitCast(
502  col_heads[i], get_fp_ptr_type(get_bit_width(ti), ctx));
503  llvm::LoadInst* scalar_fp = cgen_state->ir_builder_.CreateLoad(
504  r->getType()->getPointerElementType(),
505  r,
506  "input_scalar_fp." + std::to_string(func_arg_index));
507  func_args.push_back(scalar_fp);
508  CHECK_EQ(col_index, -1);
509  } else if (ti.is_integer() || ti.is_boolean() || ti.is_timestamp() ||
510  ti.is_timeinterval()) {
511  auto r = cgen_state->ir_builder_.CreateBitCast(
512  col_heads[i], get_int_ptr_type(get_bit_width(ti), ctx));
513  llvm::LoadInst* scalar_int = cgen_state->ir_builder_.CreateLoad(
514  r->getType()->getPointerElementType(),
515  r,
516  "input_scalar_int." + std::to_string(func_arg_index));
517  func_args.push_back(scalar_int);
518  CHECK_EQ(col_index, -1);
519  } else if (ti.is_text_encoding_none()) {
520  auto varchar_size =
521  cgen_state->ir_builder_.CreateBitCast(col_heads[i], get_int_ptr_type(64, ctx));
522  auto varchar_ptr = cgen_state->ir_builder_.CreateGEP(
523  col_heads[i]->getType()->getScalarType()->getPointerElementType(),
524  col_heads[i],
525  cgen_state->llInt(8));
526  auto [varchar_struct, varchar_struct_ptr] = alloc_column(
527  std::string("input_varchar_literal.") + std::to_string(func_arg_index),
528  i,
529  ti,
530  varchar_ptr,
531  varchar_size,
532  nullptr,
533  ctx,
534  cgen_state->ir_builder_);
535  func_args.push_back(
536  (pass_column_by_value
537  ? cgen_state->ir_builder_.CreateLoad(
538  varchar_struct->getType()->getPointerElementType(), varchar_struct)
539  : varchar_struct_ptr));
540  CHECK_EQ(col_index, -1);
541  } else if (ti.is_column()) {
542  auto [col, col_ptr] = alloc_column(
543  std::string("input_col.") + std::to_string(func_arg_index),
544  i,
545  ti.get_elem_type(),
546  col_heads[i],
547  row_count_heads[i],
549  : input_str_dict_proxy_heads[i],
550  ctx,
551  cgen_state->ir_builder_);
552  func_args.push_back((pass_column_by_value
553  ? cgen_state->ir_builder_.CreateLoad(
554  col->getType()->getPointerElementType(), col)
555  : col_ptr));
556  CHECK_EQ(col_index, -1);
557  } else if (ti.is_column_list()) {
558  if (col_index == -1) {
559  auto col_list = alloc_column_list(
560  std::string("input_col_list.") + std::to_string(func_arg_index),
561  ti.get_elem_type(),
562  col_heads[i],
563  ti.get_dimension(),
564  row_count_heads[i],
565  input_str_dict_proxy_heads[i],
566  ctx,
567  cgen_state->ir_builder_);
568  func_args.push_back(col_list);
569  }
570  col_index++;
571  if (col_index + 1 == ti.get_dimension()) {
572  col_index = -1;
573  }
574  } else if (ti.is_array()) {
575  /*
576  Literal array expression is encoded in a contiguous buffer
577  with the following memory layout:
578 
579  | <array size> | <array is_null> | <array data> |
580  |<-- 8 bytes ->|<-- 8 bytes ---->|<-- <array size> * <array element size> -->|
581 
582  Notice that while is_null in the `struct Array` has type
583  int8_t, in the buffer we use int64_t value to hold the
584  is_null state in order to have the array data 64-bit
585  aligned.
586  */
587  auto array_size =
588  cgen_state->ir_builder_.CreateBitCast(col_heads[i], get_int_ptr_type(64, ctx));
589  auto array_is_null_ptr = cgen_state->ir_builder_.CreateGEP(
590  col_heads[i]->getType()->getScalarType()->getPointerElementType(),
591  col_heads[i],
592  cgen_state->llInt(8));
593  auto array_is_null = cgen_state->ir_builder_.CreateLoad(
594  array_is_null_ptr->getType()->getPointerElementType(), array_is_null_ptr);
595 
596  auto array_ptr = cgen_state->ir_builder_.CreateGEP(
597  col_heads[i]->getType()->getScalarType()->getPointerElementType(),
598  col_heads[i],
599  cgen_state->llInt(16));
600  array_size->setName(std::string("array_size.") + std::to_string(func_arg_index));
601  array_is_null->setName(std::string("array_is_null.") +
602  std::to_string(func_arg_index));
603  array_ptr->setName(std::string("array_ptr.") + std::to_string(func_arg_index));
604 
605  auto array_struct_ptr =
606  alloc_array(std::string("literal_array.") + std::to_string(func_arg_index),
607  i,
608  ti,
609  array_ptr,
610  array_size,
611  array_is_null,
612  ctx,
613  cgen_state->ir_builder_);
614 
615  // passing columns by value is a historical artifact, so no need
616  // to support it for array expressions:
617  CHECK_EQ(pass_column_by_value, false);
618  func_args.push_back(array_struct_ptr);
619  CHECK_EQ(col_index, -1);
620  } else {
621  throw std::runtime_error("Table function input has unsupported type: " +
622  ti.get_type_name());
623  }
624  }
625  auto output_str_dict_proxy_heads =
627  ? (generate_column_heads_load(exe_unit.target_exprs.size(),
628  output_str_dict_proxies_arg,
629  cgen_state->ir_builder_,
630  ctx))
631  : std::vector<llvm::Value*>();
632 
633  std::vector<llvm::Value*> output_col_args;
634  for (size_t i = 0; i < exe_unit.target_exprs.size(); i++) {
635  auto* gep = cgen_state->ir_builder_.CreateGEP(
636  output_buffers_arg->getType()->getScalarType()->getPointerElementType(),
637  output_buffers_arg,
638  cgen_state->llInt(i));
639  auto output_load =
640  cgen_state->ir_builder_.CreateLoad(gep->getType()->getPointerElementType(), gep);
641  const auto& expr = exe_unit.target_exprs[i];
642  const auto& ti = expr->get_type_info();
643  CHECK(!ti.is_column()); // UDTF output column type is its data type
644  CHECK(!ti.is_column_list()); // TODO: when UDTF outputs column_list, convert it to
645  // output columns
646  // UDTF output columns use FlatBuffer storage whenever type supports it
647  CHECK_EQ(ti.supportsFlatBuffer(), ti.usesFlatBuffer()) << ti;
648  auto [col, col_ptr] = alloc_column(
649  std::string("output_col.") + std::to_string(i),
650  i,
651  ti,
653  ? output_load
654  : nullptr), // CPU: set_output_row_size will set the output
655  // Column ptr member
656  output_row_count_ptr,
657  co_.device_type == ExecutorDeviceType::CPU ? output_str_dict_proxy_heads[i]
658  : nullptr,
659  ctx,
660  cgen_state->ir_builder_);
661  if (co_.device_type == ExecutorDeviceType::CPU && !emit_only_preflight_fn) {
662  cgen_state->emitExternalCall(
663  "TableFunctionManager_register_output_column",
664  llvm::Type::getVoidTy(ctx),
665  {mgr_ptr, llvm::ConstantInt::get(get_int_type(32, ctx), i, true), col_ptr});
666  }
667  output_col_args.push_back((pass_column_by_value ? col : col_ptr));
668  }
669 
670  // output column members must be set before loading column when
671  // column instances are passed by value
672  if ((exe_unit.table_func.hasOutputSizeKnownPreLaunch() ||
673  exe_unit.table_func.hasPreFlightOutputSizer()) &&
674  (co_.device_type == ExecutorDeviceType::CPU) && !emit_only_preflight_fn) {
675  cgen_state->emitExternalCall(
676  "TableFunctionManager_set_output_row_size",
677  llvm::Type::getVoidTy(ctx),
678  {mgr_ptr,
679  cgen_state->ir_builder_.CreateLoad(
680  output_row_count_ptr->getType()->getPointerElementType(),
681  output_row_count_ptr)});
682  }
683 
684  if (!emit_only_preflight_fn) {
685  for (auto& col : output_col_args) {
686  func_args.push_back((pass_column_by_value
687  ? cgen_state->ir_builder_.CreateLoad(
688  col->getType()->getPointerElementType(), col)
689  : col));
690  }
691  }
692 
694  exe_unit, func_args, bb_exit, output_row_count_ptr, emit_only_preflight_fn);
695 
696  // std::cout << "=================================" << std::endl;
697  // entry_point_func_->print(llvm::outs());
698  // std::cout << "=================================" << std::endl;
699 
701 }
702 
704  auto timer = DEBUG_TIMER(__func__);
706  std::vector<llvm::Type*> arg_types;
707  arg_types.reserve(entry_point_func_->arg_size());
708  std::for_each(entry_point_func_->arg_begin(),
709  entry_point_func_->arg_end(),
710  [&arg_types](const auto& arg) { arg_types.push_back(arg.getType()); });
711  CHECK_EQ(arg_types.size(), entry_point_func_->arg_size());
712 
713  auto cgen_state = executor_->getCgenStatePtr();
714  CHECK(cgen_state);
715  auto& ctx = cgen_state->context_;
716 
717  std::vector<llvm::Type*> wrapper_arg_types(arg_types.size() + 1);
718  wrapper_arg_types[0] = llvm::PointerType::get(get_int_type(32, ctx), 0);
719  wrapper_arg_types[1] = arg_types[0];
720 
721  for (size_t i = 1; i < arg_types.size(); ++i) {
722  wrapper_arg_types[i + 1] = arg_types[i];
723  }
724 
725  auto wrapper_ft =
726  llvm::FunctionType::get(llvm::Type::getVoidTy(ctx), wrapper_arg_types, false);
727  kernel_func_ = llvm::Function::Create(wrapper_ft,
728  llvm::Function::ExternalLinkage,
729  "table_func_kernel",
730  cgen_state->module_);
731 
732  auto wrapper_bb_entry = llvm::BasicBlock::Create(ctx, ".entry", kernel_func_, 0);
733  llvm::IRBuilder<> b(ctx);
734  b.SetInsertPoint(wrapper_bb_entry);
735  std::vector<llvm::Value*> loaded_args = {kernel_func_->arg_begin() + 1};
736  for (size_t i = 2; i < wrapper_arg_types.size(); ++i) {
737  loaded_args.push_back(kernel_func_->arg_begin() + i);
738  }
739  auto error_lv = b.CreateCall(entry_point_func_, loaded_args);
740  b.CreateStore(error_lv, kernel_func_->arg_begin());
741  b.CreateRetVoid();
742 }
743 
744 std::shared_ptr<CompilationContext> TableFunctionCompilationContext::finalize(
745  bool emit_only_preflight_fn) {
746  auto timer = DEBUG_TIMER(__func__);
747  /*
748  TODO 1: eliminate need for OverrideFromSrc
749  TODO 2: detect and link only the udf's that are needed
750  */
751  auto cgen_state = executor_->getCgenStatePtr();
752  auto is_gpu = co_.device_type == ExecutorDeviceType::GPU;
753  if (executor_->has_rt_udf_module(is_gpu)) {
754  CodeGenerator::link_udf_module(executor_->get_rt_udf_module(is_gpu),
755  *(cgen_state->module_),
756  cgen_state,
757  llvm::Linker::Flags::OverrideFromSrc);
758  }
759 
760  LOG(IR) << (emit_only_preflight_fn ? "Pre Flight Function Entry Point IR\n"
761  : "Table Function Entry Point IR\n")
763  std::shared_ptr<CompilationContext> code;
764  if (is_gpu) {
765  LOG(IR) << "Table Function Kernel IR\n" << serialize_llvm_object(kernel_func_);
766 
767  CHECK(executor_);
768  executor_->initializeNVPTXBackend();
769 
770  CodeGenerator::GPUTarget gpu_target{
771  executor_->nvptx_target_machine_.get(), executor_->cudaMgr(), cgen_state, false};
774  kernel_func_,
776  /*is_gpu_smem_used=*/false,
777  co_,
778  gpu_target);
779  } else {
780  auto ee =
782  auto cpu_code = std::make_shared<CpuCompilationContext>(std::move(ee));
783  cpu_code->setFunctionPointer(entry_point_func_);
784  code = cpu_code;
785  }
786  LOG(IR) << "End of IR";
787 
788  return code;
789 }
llvm::Type * get_fp_ptr_type(const int width, llvm::LLVMContext &context)
#define CHECK_EQ(x, y)
Definition: Logger.h:301
std::string exprsKey(const std::vector< Analyzer::Expr * > &exprs)
HOST DEVICE int get_size() const
Definition: sqltypes.h:403
bool is_timestamp() const
Definition: sqltypes.h:1044
void generateEntryPoint(const TableFunctionExecutionUnit &exe_unit, bool emit_only_preflight_fn)
void initialize_ptr_member(llvm::Value *member_ptr, llvm::Type *member_llvm_type, llvm::Value *value_ptr, llvm::IRBuilder<> &ir_builder)
bool passColumnsByValue(const TableFunctionExecutionUnit &exe_unit)
std::vector< Analyzer::Expr * > input_exprs
const table_functions::TableFunction table_func
#define LOG(tag)
Definition: Logger.h:285
bool is_fp() const
Definition: sqltypes.h:571
std::tuple< llvm::Value *, llvm::Value * > alloc_column(std::string col_name, const size_t index, const SQLTypeInfo &data_target_info, llvm::Value *data_ptr, llvm::Value *data_size, llvm::Value *data_str_dict_proxy_ptr, llvm::LLVMContext &ctx, llvm::IRBuilder<> &ir_builder)
llvm::Function * generate_entry_point(const CgenState *cgen_state)
std::shared_ptr< CompilationContext > finalize(bool emit_only_preflight_fn)
std::vector< std::string > CodeCacheKey
Definition: CodeCache.h:24
std::string toString(const QueryDescriptionType &type)
Definition: Types.h:64
static ExecutionEngineWrapper generateNativeCPUCode(llvm::Function *func, const std::unordered_set< llvm::Function * > &live_funcs, const CompilationOptions &co)
llvm::Type * get_int_type(const int width, llvm::LLVMContext &context)
llvm::Value * alloc_column_list(std::string col_list_name, const SQLTypeInfo &data_target_info, llvm::Value *data_ptrs, int length, llvm::Value *data_size, llvm::Value *data_str_dict_proxy_ptrs, llvm::LLVMContext &ctx, llvm::IRBuilder<> &ir_builder)
std::string to_string(char const *&&v)
llvm::Value * alloc_array(std::string arr_name, const size_t index, const SQLTypeInfo &data_target_info, llvm::Value *data_ptr, llvm::Value *data_size, llvm::Value *data_is_null, llvm::LLVMContext &ctx, llvm::IRBuilder<> &ir_builder)
llvm::Module * module_
Definition: CgenState.h:373
void verify_function_ir(const llvm::Function *func)
size_t get_bit_width(const SQLTypeInfo &ti)
llvm::LLVMContext & context_
Definition: CgenState.h:382
bool is_integer() const
Definition: sqltypes.h:565
bool usesFlatBuffer() const
Definition: sqltypes.h:1081
bool is_boolean() const
Definition: sqltypes.h:580
static void link_udf_module(const std::unique_ptr< llvm::Module > &udf_module, llvm::Module &module, CgenState *cgen_state, llvm::Linker::Flags flags=llvm::Linker::Flags::None)
void generateTableFunctionCall(const TableFunctionExecutionUnit &exe_unit, const std::vector< llvm::Value * > &func_args, llvm::BasicBlock *bb_exit, llvm::Value *output_row_count_ptr, bool emit_only_preflight_fn)
std::string getName(const bool drop_suffix=false, const bool lower=false) const
ExecutorDeviceType device_type
HOST DEVICE EncodingType get_compression() const
Definition: sqltypes.h:399
std::string serialize_llvm_object(const T *llvm_obj)
static std::shared_ptr< GpuCompilationContext > generateNativeGPUCode(Executor *executor, llvm::Function *func, llvm::Function *wrapper_func, const std::unordered_set< llvm::Function * > &live_funcs, const bool is_gpu_smem_used, const CompilationOptions &co, const GPUTarget &gpu_target)
std::vector< llvm::Value * > generate_column_heads_load(const int num_columns, llvm::Value *byte_stream_arg, llvm::IRBuilder<> &ir_builder, llvm::LLVMContext &ctx)
llvm::Type * get_llvm_type_from_sql_column_type(const SQLTypeInfo elem_ti, llvm::LLVMContext &ctx)
#define CHECK(condition)
Definition: Logger.h:291
#define DEBUG_TIMER(name)
Definition: Logger.h:412
static std::shared_ptr< QueryEngine > getInstance()
Definition: QueryEngine.h:89
std::shared_ptr< CompilationContext > compile(const TableFunctionExecutionUnit &exe_unit, bool emit_only_preflight_fn)
std::vector< Analyzer::Expr * > target_exprs
void initialize_int_member(llvm::Value *member_ptr, llvm::Value *value, int64_t default_value, llvm::LLVMContext &ctx, llvm::IRBuilder<> &ir_builder)
bool is_string() const
Definition: sqltypes.h:559
bool is_text_encoding_none() const
Definition: sqltypes.h:612
SQLTypeInfo get_elem_type() const
Definition: sqltypes.h:975
llvm::Type * get_int_ptr_type(const int width, llvm::LLVMContext &context)