OmniSciDB  467d548b97
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
IRCodegen.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 MapD Technologies, 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 
17 #include "../Parser/ParserNode.h"
18 #include "CodeGenerator.h"
19 #include "Execute.h"
20 #include "ExternalExecutor.h"
21 #include "MaxwellCodegenPatch.h"
22 #include "RelAlgTranslator.h"
23 
24 // Driver methods for the IR generation.
25 
26 std::vector<llvm::Value*> CodeGenerator::codegen(const Analyzer::Expr* expr,
27  const bool fetch_columns,
28  const CompilationOptions& co) {
30  if (!expr) {
31  return {posArg(expr)};
32  }
33  auto bin_oper = dynamic_cast<const Analyzer::BinOper*>(expr);
34  if (bin_oper) {
35  return {codegen(bin_oper, co)};
36  }
37  auto u_oper = dynamic_cast<const Analyzer::UOper*>(expr);
38  if (u_oper) {
39  return {codegen(u_oper, co)};
40  }
41  auto col_var = dynamic_cast<const Analyzer::ColumnVar*>(expr);
42  if (col_var) {
43  return codegenColumn(col_var, fetch_columns, co);
44  }
45  auto constant = dynamic_cast<const Analyzer::Constant*>(expr);
46  if (constant) {
47  const auto& ti = constant->get_type_info();
48  if (ti.get_type() == kNULLT) {
49  throw std::runtime_error(
50  "NULL type literals are not currently supported in this context.");
51  }
52  if (constant->get_is_null()) {
53  return {ti.is_fp()
54  ? static_cast<llvm::Value*>(executor_->cgen_state_->inlineFpNull(ti))
55  : static_cast<llvm::Value*>(executor_->cgen_state_->inlineIntNull(ti))};
56  }
57  if (ti.get_compression() == kENCODING_DICT) {
58  // The dictionary encoding case should be handled by the parent expression
59  // (cast, for now), here is too late to know the dictionary id if not already set
60  CHECK_NE(ti.get_comp_param(), 0);
61  return {codegen(constant, ti.get_compression(), ti.get_comp_param(), co)};
62  }
63  return {codegen(constant, ti.get_compression(), 0, co)};
64  }
65  auto case_expr = dynamic_cast<const Analyzer::CaseExpr*>(expr);
66  if (case_expr) {
67  return {codegen(case_expr, co)};
68  }
69  auto extract_expr = dynamic_cast<const Analyzer::ExtractExpr*>(expr);
70  if (extract_expr) {
71  return {codegen(extract_expr, co)};
72  }
73  auto dateadd_expr = dynamic_cast<const Analyzer::DateaddExpr*>(expr);
74  if (dateadd_expr) {
75  return {codegen(dateadd_expr, co)};
76  }
77  auto datediff_expr = dynamic_cast<const Analyzer::DatediffExpr*>(expr);
78  if (datediff_expr) {
79  return {codegen(datediff_expr, co)};
80  }
81  auto datetrunc_expr = dynamic_cast<const Analyzer::DatetruncExpr*>(expr);
82  if (datetrunc_expr) {
83  return {codegen(datetrunc_expr, co)};
84  }
85  auto charlength_expr = dynamic_cast<const Analyzer::CharLengthExpr*>(expr);
86  if (charlength_expr) {
87  return {codegen(charlength_expr, co)};
88  }
89  auto keyforstring_expr = dynamic_cast<const Analyzer::KeyForStringExpr*>(expr);
90  if (keyforstring_expr) {
91  return {codegen(keyforstring_expr, co)};
92  }
93  auto sample_ratio_expr = dynamic_cast<const Analyzer::SampleRatioExpr*>(expr);
94  if (sample_ratio_expr) {
95  return {codegen(sample_ratio_expr, co)};
96  }
97  auto lower_expr = dynamic_cast<const Analyzer::LowerExpr*>(expr);
98  if (lower_expr) {
99  return {codegen(lower_expr, co)};
100  }
101  auto cardinality_expr = dynamic_cast<const Analyzer::CardinalityExpr*>(expr);
102  if (cardinality_expr) {
103  return {codegen(cardinality_expr, co)};
104  }
105  auto like_expr = dynamic_cast<const Analyzer::LikeExpr*>(expr);
106  if (like_expr) {
107  return {codegen(like_expr, co)};
108  }
109  auto regexp_expr = dynamic_cast<const Analyzer::RegexpExpr*>(expr);
110  if (regexp_expr) {
111  return {codegen(regexp_expr, co)};
112  }
113  auto likelihood_expr = dynamic_cast<const Analyzer::LikelihoodExpr*>(expr);
114  if (likelihood_expr) {
115  return {codegen(likelihood_expr->get_arg(), fetch_columns, co)};
116  }
117  auto in_expr = dynamic_cast<const Analyzer::InValues*>(expr);
118  if (in_expr) {
119  return {codegen(in_expr, co)};
120  }
121  auto in_integer_set_expr = dynamic_cast<const Analyzer::InIntegerSet*>(expr);
122  if (in_integer_set_expr) {
123  return {codegen(in_integer_set_expr, co)};
124  }
125  auto function_oper_with_custom_type_handling_expr =
126  dynamic_cast<const Analyzer::FunctionOperWithCustomTypeHandling*>(expr);
127  if (function_oper_with_custom_type_handling_expr) {
129  function_oper_with_custom_type_handling_expr, co)};
130  }
131  auto array_oper_expr = dynamic_cast<const Analyzer::ArrayExpr*>(expr);
132  if (array_oper_expr) {
133  return {codegenArrayExpr(array_oper_expr, co)};
134  }
135  auto geo_uop = dynamic_cast<const Analyzer::GeoUOper*>(expr);
136  if (geo_uop) {
137  return {codegenGeoUOper(geo_uop, co)};
138  }
139  auto geo_binop = dynamic_cast<const Analyzer::GeoBinOper*>(expr);
140  if (geo_binop) {
141  return {codegenGeoBinOper(geo_binop, co)};
142  }
143  auto function_oper_expr = dynamic_cast<const Analyzer::FunctionOper*>(expr);
144  if (function_oper_expr) {
145  return {codegenFunctionOper(function_oper_expr, co)};
146  }
147  if (dynamic_cast<const Analyzer::OffsetInFragment*>(expr)) {
148  return {posArg(nullptr)};
149  }
150  if (dynamic_cast<const Analyzer::WindowFunction*>(expr)) {
151  throw NativeExecutionError("Window expression not supported in this context");
152  }
153  abort();
154 }
155 
156 llvm::Value* CodeGenerator::codegen(const Analyzer::BinOper* bin_oper,
157  const CompilationOptions& co) {
159  const auto optype = bin_oper->get_optype();
160  if (IS_ARITHMETIC(optype)) {
161  return codegenArith(bin_oper, co);
162  }
163  if (IS_COMPARISON(optype)) {
164  return codegenCmp(bin_oper, co);
165  }
166  if (IS_LOGIC(optype)) {
167  return codegenLogical(bin_oper, co);
168  }
169  if (optype == kARRAY_AT) {
170  return codegenArrayAt(bin_oper, co);
171  }
172  abort();
173 }
174 
175 llvm::Value* CodeGenerator::codegen(const Analyzer::UOper* u_oper,
176  const CompilationOptions& co) {
178  const auto optype = u_oper->get_optype();
179  switch (optype) {
180  case kNOT: {
181  return codegenLogical(u_oper, co);
182  }
183  case kCAST: {
184  return codegenCast(u_oper, co);
185  }
186  case kUMINUS: {
187  return codegenUMinus(u_oper, co);
188  }
189  case kISNULL: {
190  return codegenIsNull(u_oper, co);
191  }
192  case kUNNEST:
193  return codegenUnnest(u_oper, co);
194  default:
195  abort();
196  }
197 }
198 
200  const CompilationOptions& co) {
202  auto input_expr = expr->get_arg();
203  CHECK(input_expr);
204 
205  auto double_lv = codegen(input_expr, true, co);
206  CHECK_EQ(size_t(1), double_lv.size());
207 
208  std::unique_ptr<CodeGenerator::NullCheckCodegen> nullcheck_codegen;
209  const bool is_nullable = !input_expr->get_type_info().get_notnull();
210  if (is_nullable) {
211  nullcheck_codegen = std::make_unique<NullCheckCodegen>(cgen_state_,
212  executor(),
213  double_lv.front(),
214  input_expr->get_type_info(),
215  "sample_ratio_nullcheck");
216  }
217  CHECK_EQ(input_expr->get_type_info().get_type(), kDOUBLE);
218  std::vector<llvm::Value*> args{double_lv[0], posArg(nullptr)};
219  auto ret = cgen_state_->emitCall("sample_ratio", args);
220  if (nullcheck_codegen) {
221  ret = nullcheck_codegen->finalize(ll_bool(false, cgen_state_->context_), ret);
222  }
223  return ret;
224 }
225 
226 namespace {
227 
229  const std::shared_ptr<Analyzer::Expr>& qual) {
230  const auto qual_cf = qual_to_conjunctive_form(qual);
231  ra_exe_unit.simple_quals.insert(ra_exe_unit.simple_quals.end(),
232  qual_cf.simple_quals.begin(),
233  qual_cf.simple_quals.end());
234  ra_exe_unit.quals.insert(
235  ra_exe_unit.quals.end(), qual_cf.quals.begin(), qual_cf.quals.end());
236 }
237 
239  const ExecutionOptions& eo,
240  const std::vector<InputTableInfo>& query_infos,
241  const size_t level_idx,
242  const std::string& fail_reason) {
243  if (eo.allow_loop_joins) {
244  return;
245  }
246  if (level_idx + 1 != ra_exe_unit.join_quals.size()) {
247  throw std::runtime_error(
248  "Hash join failed, reason(s): " + fail_reason +
249  " | Cannot fall back to loop join for intermediate join quals");
250  }
251  if (!is_trivial_loop_join(query_infos, ra_exe_unit)) {
252  throw std::runtime_error(
253  "Hash join failed, reason(s): " + fail_reason +
254  " | Cannot fall back to loop join for non-trivial inner table size");
255  }
256 }
257 
258 } // namespace
259 
260 std::vector<JoinLoop> Executor::buildJoinLoops(
261  RelAlgExecutionUnit& ra_exe_unit,
262  const CompilationOptions& co,
263  const ExecutionOptions& eo,
264  const std::vector<InputTableInfo>& query_infos,
265  ColumnCacheMap& column_cache) {
266  INJECT_TIMER(buildJoinLoops);
268  std::vector<JoinLoop> join_loops;
269  for (size_t level_idx = 0, current_hash_table_idx = 0;
270  level_idx < ra_exe_unit.join_quals.size();
271  ++level_idx) {
272  const auto& current_level_join_conditions = ra_exe_unit.join_quals[level_idx];
273  std::vector<std::string> fail_reasons;
274  const auto build_cur_level_hash_table = [&]() {
275  if (current_level_join_conditions.quals.size() > 1) {
276  const auto first_qual = *current_level_join_conditions.quals.begin();
277  auto qual_bin_oper =
278  std::dynamic_pointer_cast<const Analyzer::BinOper>(first_qual);
279  if (qual_bin_oper && qual_bin_oper->is_overlaps_oper() &&
280  current_level_join_conditions.type == JoinType::LEFT) {
281  JoinCondition join_condition{{first_qual}, current_level_join_conditions.type};
282 
283  return buildCurrentLevelHashTable(
284  join_condition, ra_exe_unit, co, query_infos, column_cache, fail_reasons);
285  }
286  }
287  return buildCurrentLevelHashTable(current_level_join_conditions,
288  ra_exe_unit,
289  co,
290  query_infos,
291  column_cache,
292  fail_reasons);
293  };
294  const auto current_level_hash_table = build_cur_level_hash_table();
295  const auto found_outer_join_matches_cb =
296  [this, level_idx](llvm::Value* found_outer_join_matches) {
300  found_outer_join_matches;
301  };
302  const auto is_deleted_cb = buildIsDeletedCb(ra_exe_unit, level_idx, co);
303  const auto outer_join_condition_multi_quals_cb =
304  [this, level_idx, &co, &current_level_join_conditions](
305  const std::vector<llvm::Value*>& prev_iters) {
306  // The values generated for the match path don't dominate all uses
307  // since on the non-match path nulls are generated. Reset the cache
308  // once the condition is generated to avoid incorrect reuse.
309  FetchCacheAnchor anchor(cgen_state_.get());
310  addJoinLoopIterator(prev_iters, level_idx + 1);
311  llvm::Value* left_join_cond = cgen_state_->llBool(true);
312  CodeGenerator code_generator(this);
313  // Do not want to look at all quals! only 1..N quals (ignore first qual)
314  // Note(jclay): this may need to support cases larger than 2
315  // are there any?
316  if (current_level_join_conditions.quals.size() >= 2) {
317  auto qual_it = std::next(current_level_join_conditions.quals.begin(), 1);
318  for (; qual_it != current_level_join_conditions.quals.end();
319  std::advance(qual_it, 1)) {
320  left_join_cond = cgen_state_->ir_builder_.CreateAnd(
321  left_join_cond,
322  code_generator.toBool(
323  code_generator.codegen(qual_it->get(), true, co).front()));
324  }
325  }
326  return left_join_cond;
327  };
328  if (current_level_hash_table) {
329  if (current_level_hash_table->getHashType() == JoinHashTable::HashType::OneToOne) {
330  join_loops.emplace_back(
331  /*kind=*/JoinLoopKind::Singleton,
332  /*type=*/current_level_join_conditions.type,
333  /*iteration_domain_codegen=*/
334  [this, current_hash_table_idx, level_idx, current_level_hash_table, &co](
335  const std::vector<llvm::Value*>& prev_iters) {
336  addJoinLoopIterator(prev_iters, level_idx);
337  JoinLoopDomain domain{{0}};
338  domain.slot_lookup_result =
339  current_level_hash_table->codegenSlot(co, current_hash_table_idx);
340  return domain;
341  },
342  /*outer_condition_match=*/nullptr,
343  /*found_outer_matches=*/current_level_join_conditions.type == JoinType::LEFT
344  ? std::function<void(llvm::Value*)>(found_outer_join_matches_cb)
345  : nullptr,
346  /*is_deleted=*/is_deleted_cb);
347  } else {
348  join_loops.emplace_back(
349  /*kind=*/JoinLoopKind::Set,
350  /*type=*/current_level_join_conditions.type,
351  /*iteration_domain_codegen=*/
352  [this, current_hash_table_idx, level_idx, current_level_hash_table, &co](
353  const std::vector<llvm::Value*>& prev_iters) {
354  addJoinLoopIterator(prev_iters, level_idx);
355  JoinLoopDomain domain{{0}};
356  const auto matching_set = current_level_hash_table->codegenMatchingSet(
357  co, current_hash_table_idx);
358  domain.values_buffer = matching_set.elements;
359  domain.element_count = matching_set.count;
360  return domain;
361  },
362  /*outer_condition_match=*/
363  current_level_join_conditions.type == JoinType::LEFT
364  ? std::function<llvm::Value*(const std::vector<llvm::Value*>&)>(
365  outer_join_condition_multi_quals_cb)
366  : nullptr,
367  /*found_outer_matches=*/current_level_join_conditions.type == JoinType::LEFT
368  ? std::function<void(llvm::Value*)>(found_outer_join_matches_cb)
369  : nullptr,
370  /*is_deleted=*/is_deleted_cb);
371  }
372  ++current_hash_table_idx;
373  } else {
374  const auto fail_reasons_str = current_level_join_conditions.quals.empty()
375  ? "No equijoin expression found"
376  : boost::algorithm::join(fail_reasons, " | ");
378  ra_exe_unit, eo, query_infos, level_idx, fail_reasons_str);
379  // Callback provided to the `JoinLoop` framework to evaluate the (outer) join
380  // condition.
381  VLOG(1) << "Unable to build hash table, falling back to loop join: "
382  << fail_reasons_str;
383  const auto outer_join_condition_cb =
384  [this, level_idx, &co, &current_level_join_conditions](
385  const std::vector<llvm::Value*>& prev_iters) {
386  // The values generated for the match path don't dominate all uses
387  // since on the non-match path nulls are generated. Reset the cache
388  // once the condition is generated to avoid incorrect reuse.
389  FetchCacheAnchor anchor(cgen_state_.get());
390  addJoinLoopIterator(prev_iters, level_idx + 1);
391  llvm::Value* left_join_cond = cgen_state_->llBool(true);
392  CodeGenerator code_generator(this);
393  for (auto expr : current_level_join_conditions.quals) {
394  left_join_cond = cgen_state_->ir_builder_.CreateAnd(
395  left_join_cond,
396  code_generator.toBool(
397  code_generator.codegen(expr.get(), true, co).front()));
398  }
399  return left_join_cond;
400  };
401  join_loops.emplace_back(
402  /*kind=*/JoinLoopKind::UpperBound,
403  /*type=*/current_level_join_conditions.type,
404  /*iteration_domain_codegen=*/
405  [this, level_idx](const std::vector<llvm::Value*>& prev_iters) {
406  addJoinLoopIterator(prev_iters, level_idx);
407  JoinLoopDomain domain{{0}};
408  const auto rows_per_scan_ptr = cgen_state_->ir_builder_.CreateGEP(
409  get_arg_by_name(cgen_state_->row_func_, "num_rows_per_scan"),
410  cgen_state_->llInt(int32_t(level_idx + 1)));
411  domain.upper_bound = cgen_state_->ir_builder_.CreateLoad(rows_per_scan_ptr,
412  "num_rows_per_scan");
413  return domain;
414  },
415  /*outer_condition_match=*/
416  current_level_join_conditions.type == JoinType::LEFT
417  ? std::function<llvm::Value*(const std::vector<llvm::Value*>&)>(
418  outer_join_condition_cb)
419  : nullptr,
420  /*found_outer_matches=*/
421  current_level_join_conditions.type == JoinType::LEFT
422  ? std::function<void(llvm::Value*)>(found_outer_join_matches_cb)
423  : nullptr,
424  /*is_deleted=*/is_deleted_cb);
425  }
426  }
427  return join_loops;
428 }
429 
430 std::function<llvm::Value*(const std::vector<llvm::Value*>&, llvm::Value*)>
432  const size_t level_idx,
433  const CompilationOptions& co) {
435  if (!co.filter_on_deleted_column) {
436  return nullptr;
437  }
438  CHECK_LT(level_idx + 1, ra_exe_unit.input_descs.size());
439  const auto input_desc = ra_exe_unit.input_descs[level_idx + 1];
440  if (input_desc.getSourceType() != InputSourceType::TABLE) {
441  return nullptr;
442  }
443 
444  const auto deleted_cd = plan_state_->getDeletedColForTable(input_desc.getTableId());
445  if (!deleted_cd) {
446  return nullptr;
447  }
448  CHECK(deleted_cd->columnType.is_boolean());
449  const auto deleted_expr = makeExpr<Analyzer::ColumnVar>(deleted_cd->columnType,
450  input_desc.getTableId(),
451  deleted_cd->columnId,
452  input_desc.getNestLevel());
453  return [this, deleted_expr, level_idx, &co](const std::vector<llvm::Value*>& prev_iters,
454  llvm::Value* have_more_inner_rows) {
455  const auto matching_row_index = addJoinLoopIterator(prev_iters, level_idx + 1);
456  // Avoid fetching the deleted column from a position which is not valid.
457  // An invalid position can be returned by a one to one hash lookup (negative)
458  // or at the end of iteration over a set of matching values.
459  llvm::Value* is_valid_it{nullptr};
460  if (have_more_inner_rows) {
461  is_valid_it = have_more_inner_rows;
462  } else {
463  is_valid_it = cgen_state_->ir_builder_.CreateICmp(
464  llvm::ICmpInst::ICMP_SGE, matching_row_index, cgen_state_->llInt<int64_t>(0));
465  }
466  const auto it_valid_bb = llvm::BasicBlock::Create(
468  const auto it_not_valid_bb = llvm::BasicBlock::Create(
469  cgen_state_->context_, "it_not_valid", cgen_state_->current_func_);
470  cgen_state_->ir_builder_.CreateCondBr(is_valid_it, it_valid_bb, it_not_valid_bb);
471  const auto row_is_deleted_bb = llvm::BasicBlock::Create(
472  cgen_state_->context_, "row_is_deleted", cgen_state_->current_func_);
473  cgen_state_->ir_builder_.SetInsertPoint(it_valid_bb);
474  CodeGenerator code_generator(this);
475  const auto row_is_deleted = code_generator.toBool(
476  code_generator.codegen(deleted_expr.get(), true, co).front());
477  cgen_state_->ir_builder_.CreateBr(row_is_deleted_bb);
478  cgen_state_->ir_builder_.SetInsertPoint(it_not_valid_bb);
479  const auto row_is_deleted_default = cgen_state_->llBool(false);
480  cgen_state_->ir_builder_.CreateBr(row_is_deleted_bb);
481  cgen_state_->ir_builder_.SetInsertPoint(row_is_deleted_bb);
482  auto row_is_deleted_or_default =
483  cgen_state_->ir_builder_.CreatePHI(row_is_deleted->getType(), 2);
484  row_is_deleted_or_default->addIncoming(row_is_deleted, it_valid_bb);
485  row_is_deleted_or_default->addIncoming(row_is_deleted_default, it_not_valid_bb);
486  return row_is_deleted_or_default;
487  };
488 }
489 
490 std::shared_ptr<JoinHashTableInterface> Executor::buildCurrentLevelHashTable(
491  const JoinCondition& current_level_join_conditions,
492  RelAlgExecutionUnit& ra_exe_unit,
493  const CompilationOptions& co,
494  const std::vector<InputTableInfo>& query_infos,
495  ColumnCacheMap& column_cache,
496  std::vector<std::string>& fail_reasons) {
498  if (current_level_join_conditions.type != JoinType::INNER &&
499  current_level_join_conditions.quals.size() > 1) {
500  fail_reasons.emplace_back("No equijoin expression found for outer join");
501  return nullptr;
502  }
503  std::shared_ptr<JoinHashTableInterface> current_level_hash_table;
504  for (const auto& join_qual : current_level_join_conditions.quals) {
505  auto qual_bin_oper = std::dynamic_pointer_cast<Analyzer::BinOper>(join_qual);
506  if (!qual_bin_oper || !IS_EQUIVALENCE(qual_bin_oper->get_optype())) {
507  fail_reasons.emplace_back("No equijoin expression found");
508  if (current_level_join_conditions.type == JoinType::INNER) {
509  add_qualifier_to_execution_unit(ra_exe_unit, join_qual);
510  }
511  continue;
512  }
513  JoinHashTableOrError hash_table_or_error;
514  if (!current_level_hash_table) {
515  hash_table_or_error = buildHashTableForQualifier(
516  qual_bin_oper,
517  query_infos,
521  column_cache);
522  current_level_hash_table = hash_table_or_error.hash_table;
523  }
524  if (hash_table_or_error.hash_table) {
525  plan_state_->join_info_.join_hash_tables_.push_back(hash_table_or_error.hash_table);
526  plan_state_->join_info_.equi_join_tautologies_.push_back(qual_bin_oper);
527  } else {
528  fail_reasons.push_back(hash_table_or_error.fail_reason);
529  if (current_level_join_conditions.type == JoinType::INNER) {
530  add_qualifier_to_execution_unit(ra_exe_unit, qual_bin_oper);
531  }
532  }
533  }
534  return current_level_hash_table;
535 }
536 
538  if (!cgen_state_->filter_func_) {
539  return;
540  }
541 
542  // Loop over all the instructions used in the filter func.
543  // The filter func instructions were generated as if for row func.
544  // Remap any values used by those instructions to filter func args
545  // and remember to forward them through the call in the row func.
546  for (auto bb_it = cgen_state_->filter_func_->begin();
547  bb_it != cgen_state_->filter_func_->end();
548  ++bb_it) {
549  for (auto instr_it = bb_it->begin(); instr_it != bb_it->end(); ++instr_it) {
550  size_t i = 0;
551  for (auto op_it = instr_it->value_op_begin(); op_it != instr_it->value_op_end();
552  ++op_it, ++i) {
553  llvm::Value* v = *op_it;
554 
555  // The last LLVM operand on a call instruction is the function to be called. Never
556  // remap it.
557  if (llvm::dyn_cast<const llvm::CallInst>(instr_it) &&
558  op_it == instr_it->value_op_end() - 1) {
559  continue;
560  }
561 
562  if (auto* instr = llvm::dyn_cast<llvm::Instruction>(v);
563  instr && instr->getParent() &&
564  instr->getParent()->getParent() == cgen_state_->row_func_) {
565  // Remember that this filter func arg is needed.
566  cgen_state_->filter_func_args_[v] = nullptr;
567  } else if (auto* argum = llvm::dyn_cast<llvm::Argument>(v);
568  argum && argum->getParent() == cgen_state_->row_func_) {
569  // Remember that this filter func arg is needed.
570  cgen_state_->filter_func_args_[v] = nullptr;
571  }
572  }
573  }
574  }
575 
576  // Create filter_func2 with parameters only for those row func values that are known to
577  // be used in the filter func code.
578  std::vector<llvm::Type*> filter_func_arg_types;
579  filter_func_arg_types.reserve(cgen_state_->filter_func_args_.v_.size());
580  for (auto& arg : cgen_state_->filter_func_args_.v_) {
581  filter_func_arg_types.push_back(arg->getType());
582  }
583  auto ft = llvm::FunctionType::get(
584  get_int_type(32, cgen_state_->context_), filter_func_arg_types, false);
585  cgen_state_->filter_func_->setName("old_filter_func");
586  auto filter_func2 = llvm::Function::Create(ft,
587  llvm::Function::ExternalLinkage,
588  "filter_func",
589  cgen_state_->filter_func_->getParent());
590  CHECK_EQ(filter_func2->arg_size(), cgen_state_->filter_func_args_.v_.size());
591  auto arg_it = cgen_state_->filter_func_args_.begin();
592  size_t i = 0;
593  for (llvm::Function::arg_iterator I = filter_func2->arg_begin(),
594  E = filter_func2->arg_end();
595  I != E;
596  ++I, ++arg_it) {
597  arg_it->second = &*I;
598  if (arg_it->first->hasName()) {
599  I->setName(arg_it->first->getName());
600  } else {
601  I->setName("extra" + std::to_string(i++));
602  }
603  }
604 
605  // copy the filter_func function body over
606  // see
607  // https://stackoverflow.com/questions/12864106/move-function-body-avoiding-full-cloning/18751365
608  filter_func2->getBasicBlockList().splice(
609  filter_func2->begin(), cgen_state_->filter_func_->getBasicBlockList());
610 
612  cgen_state_->current_func_ = filter_func2;
613  }
614  cgen_state_->filter_func_ = filter_func2;
615 
616  // loop over all the operands in the filter func
617  for (auto bb_it = cgen_state_->filter_func_->begin();
618  bb_it != cgen_state_->filter_func_->end();
619  ++bb_it) {
620  for (auto instr_it = bb_it->begin(); instr_it != bb_it->end(); ++instr_it) {
621  size_t i = 0;
622  for (auto op_it = instr_it->op_begin(); op_it != instr_it->op_end(); ++op_it, ++i) {
623  llvm::Value* v = op_it->get();
624  if (auto arg_it = cgen_state_->filter_func_args_.find(v);
625  arg_it != cgen_state_->filter_func_args_.end()) {
626  // replace row func value with a filter func arg
627  llvm::Use* use = &*op_it;
628  use->set(arg_it->second);
629  }
630  }
631  }
632  }
633 }
634 
635 llvm::Value* Executor::addJoinLoopIterator(const std::vector<llvm::Value*>& prev_iters,
636  const size_t level_idx) {
638  // Iterators are added for loop-outer joins when the head of the loop is generated,
639  // then once again when the body if generated. Allow this instead of special handling
640  // of call sites.
641  const auto it = cgen_state_->scan_idx_to_hash_pos_.find(level_idx);
642  if (it != cgen_state_->scan_idx_to_hash_pos_.end()) {
643  return it->second;
644  }
645  CHECK(!prev_iters.empty());
646  llvm::Value* matching_row_index = prev_iters.back();
647  const auto it_ok =
648  cgen_state_->scan_idx_to_hash_pos_.emplace(level_idx, matching_row_index);
649  CHECK(it_ok.second);
650  return matching_row_index;
651 }
652 
653 void Executor::codegenJoinLoops(const std::vector<JoinLoop>& join_loops,
654  const RelAlgExecutionUnit& ra_exe_unit,
655  GroupByAndAggregate& group_by_and_aggregate,
656  llvm::Function* query_func,
657  llvm::BasicBlock* entry_bb,
659  const CompilationOptions& co,
660  const ExecutionOptions& eo) {
662  const auto exit_bb =
663  llvm::BasicBlock::Create(cgen_state_->context_, "exit", cgen_state_->current_func_);
664  cgen_state_->ir_builder_.SetInsertPoint(exit_bb);
665  cgen_state_->ir_builder_.CreateRet(cgen_state_->llInt<int32_t>(0));
666  cgen_state_->ir_builder_.SetInsertPoint(entry_bb);
667  CodeGenerator code_generator(this);
668  const auto loops_entry_bb = JoinLoop::codegen(
669  join_loops,
670  [this,
671  query_func,
672  &query_mem_desc,
673  &co,
674  &eo,
675  &group_by_and_aggregate,
676  &join_loops,
677  &ra_exe_unit](const std::vector<llvm::Value*>& prev_iters) {
679  addJoinLoopIterator(prev_iters, join_loops.size());
680  auto& builder = cgen_state_->ir_builder_;
681  const auto loop_body_bb = llvm::BasicBlock::Create(
682  builder.getContext(), "loop_body", builder.GetInsertBlock()->getParent());
683  builder.SetInsertPoint(loop_body_bb);
684  const bool can_return_error =
685  compileBody(ra_exe_unit, group_by_and_aggregate, query_mem_desc, co);
686  if (can_return_error || cgen_state_->needs_error_check_ ||
688  createErrorCheckControlFlow(query_func,
691  co.device_type);
692  }
693  return loop_body_bb;
694  },
695  code_generator.posArg(nullptr),
696  exit_bb,
697  cgen_state_.get());
698  cgen_state_->ir_builder_.SetInsertPoint(entry_bb);
699  cgen_state_->ir_builder_.CreateBr(loops_entry_bb);
700 }
701 
703  Analyzer::Expr* group_by_col,
704  const size_t col_width,
705  const CompilationOptions& co,
706  const bool translate_null_val,
707  const int64_t translated_null_val,
708  GroupByAndAggregate::DiamondCodegen& diamond_codegen,
709  std::stack<llvm::BasicBlock*>& array_loops,
710  const bool thread_mem_shared) {
712  CHECK_GE(col_width, sizeof(int32_t));
713  CodeGenerator code_generator(this);
714  auto group_key = code_generator.codegen(group_by_col, true, co).front();
715  auto key_to_cache = group_key;
716  if (dynamic_cast<Analyzer::UOper*>(group_by_col) &&
717  static_cast<Analyzer::UOper*>(group_by_col)->get_optype() == kUNNEST) {
718  auto preheader = cgen_state_->ir_builder_.GetInsertBlock();
719  auto array_loop_head = llvm::BasicBlock::Create(cgen_state_->context_,
720  "array_loop_head",
722  preheader->getNextNode());
723  diamond_codegen.setFalseTarget(array_loop_head);
724  const auto ret_ty = get_int_type(32, cgen_state_->context_);
725  auto array_idx_ptr = cgen_state_->ir_builder_.CreateAlloca(ret_ty);
726  CHECK(array_idx_ptr);
727  cgen_state_->ir_builder_.CreateStore(cgen_state_->llInt(int32_t(0)), array_idx_ptr);
728  const auto arr_expr = static_cast<Analyzer::UOper*>(group_by_col)->get_operand();
729  const auto& array_ti = arr_expr->get_type_info();
730  CHECK(array_ti.is_array());
731  const auto& elem_ti = array_ti.get_elem_type();
732  auto array_len =
733  (array_ti.get_size() > 0)
734  ? cgen_state_->llInt(array_ti.get_size() / elem_ti.get_size())
736  "array_size",
737  ret_ty,
738  {group_key,
739  code_generator.posArg(arr_expr),
740  cgen_state_->llInt(log2_bytes(elem_ti.get_logical_size()))});
741  cgen_state_->ir_builder_.CreateBr(array_loop_head);
742  cgen_state_->ir_builder_.SetInsertPoint(array_loop_head);
743  CHECK(array_len);
744  auto array_idx = cgen_state_->ir_builder_.CreateLoad(array_idx_ptr);
745  auto bound_check = cgen_state_->ir_builder_.CreateICmp(
746  llvm::ICmpInst::ICMP_SLT, array_idx, array_len);
747  auto array_loop_body = llvm::BasicBlock::Create(
748  cgen_state_->context_, "array_loop_body", cgen_state_->current_func_);
749  cgen_state_->ir_builder_.CreateCondBr(
750  bound_check,
751  array_loop_body,
752  array_loops.empty() ? diamond_codegen.orig_cond_false_ : array_loops.top());
753  cgen_state_->ir_builder_.SetInsertPoint(array_loop_body);
754  cgen_state_->ir_builder_.CreateStore(
755  cgen_state_->ir_builder_.CreateAdd(array_idx, cgen_state_->llInt(int32_t(1))),
756  array_idx_ptr);
757  auto array_at_fname = "array_at_" + numeric_type_name(elem_ti);
758  if (array_ti.get_size() < 0) {
759  if (array_ti.get_notnull()) {
760  array_at_fname = "notnull_" + array_at_fname;
761  }
762  array_at_fname = "varlen_" + array_at_fname;
763  }
764  const auto ar_ret_ty =
765  elem_ti.is_fp()
766  ? (elem_ti.get_type() == kDOUBLE
767  ? llvm::Type::getDoubleTy(cgen_state_->context_)
768  : llvm::Type::getFloatTy(cgen_state_->context_))
769  : get_int_type(elem_ti.get_logical_size() * 8, cgen_state_->context_);
770  group_key = cgen_state_->emitExternalCall(
771  array_at_fname,
772  ar_ret_ty,
773  {group_key, code_generator.posArg(arr_expr), array_idx});
775  elem_ti, isArchMaxwell(co.device_type), thread_mem_shared)) {
776  key_to_cache = spillDoubleElement(group_key, ar_ret_ty);
777  } else {
778  key_to_cache = group_key;
779  }
780  CHECK(array_loop_head);
781  array_loops.push(array_loop_head);
782  }
783  cgen_state_->group_by_expr_cache_.push_back(key_to_cache);
784  llvm::Value* orig_group_key{nullptr};
785  if (translate_null_val) {
786  const std::string translator_func_name(
787  col_width == sizeof(int32_t) ? "translate_null_key_i32_" : "translate_null_key_");
788  const auto& ti = group_by_col->get_type_info();
789  const auto key_type = get_int_type(ti.get_logical_size() * 8, cgen_state_->context_);
790  orig_group_key = group_key;
791  group_key = cgen_state_->emitCall(
792  translator_func_name + numeric_type_name(ti),
793  {group_key,
794  static_cast<llvm::Value*>(
795  llvm::ConstantInt::get(key_type, inline_int_null_val(ti))),
796  static_cast<llvm::Value*>(llvm::ConstantInt::get(
797  llvm::Type::getInt64Ty(cgen_state_->context_), translated_null_val))});
798  }
799  group_key = cgen_state_->ir_builder_.CreateBitCast(
800  cgen_state_->castToTypeIn(group_key, col_width * 8),
801  get_int_type(col_width * 8, cgen_state_->context_));
802  if (orig_group_key) {
803  orig_group_key = cgen_state_->ir_builder_.CreateBitCast(
804  cgen_state_->castToTypeIn(orig_group_key, col_width * 8),
805  get_int_type(col_width * 8, cgen_state_->context_));
806  }
807  return {group_key, orig_group_key};
808 }
809 
811  Executor* executor,
812  llvm::Value* nullable_lv,
813  const SQLTypeInfo& nullable_ti,
814  const std::string& name)
815  : cgen_state(cgen_state), name(name) {
816  AUTOMATIC_IR_METADATA(cgen_state);
817  CHECK(nullable_ti.is_number() || nullable_ti.is_time());
818 
819  null_check = std::make_unique<GroupByAndAggregate::DiamondCodegen>(
820  nullable_ti.is_fp()
821  ? cgen_state->ir_builder_.CreateFCmp(llvm::FCmpInst::FCMP_OEQ,
822  nullable_lv,
823  cgen_state->inlineFpNull(nullable_ti))
824  : cgen_state->ir_builder_.CreateICmp(llvm::ICmpInst::ICMP_EQ,
825  nullable_lv,
826  cgen_state->inlineIntNull(nullable_ti)),
827  executor,
828  false,
829  name,
830  nullptr,
831  false);
832 
833  // generate a phi node depending on whether we got a null or not
834  nullcheck_bb = llvm::BasicBlock::Create(
835  cgen_state->context_, name + "_bb", cgen_state->current_func_);
836 
837  // update the blocks created by diamond codegen to point to the newly created phi
838  // block
839  cgen_state->ir_builder_.SetInsertPoint(null_check->cond_true_);
840  cgen_state->ir_builder_.CreateBr(nullcheck_bb);
841  cgen_state->ir_builder_.SetInsertPoint(null_check->cond_false_);
842 }
843 
844 llvm::Value* CodeGenerator::NullCheckCodegen::finalize(llvm::Value* null_lv,
845  llvm::Value* notnull_lv) {
846  AUTOMATIC_IR_METADATA(cgen_state);
847  CHECK(null_check);
848  cgen_state->ir_builder_.CreateBr(nullcheck_bb);
849 
850  CHECK_EQ(null_lv->getType(), notnull_lv->getType());
851 
852  cgen_state->ir_builder_.SetInsertPoint(nullcheck_bb);
853  nullcheck_value =
854  cgen_state->ir_builder_.CreatePHI(null_lv->getType(), 2, name + "_value");
855  nullcheck_value->addIncoming(notnull_lv, null_check->cond_false_);
856  nullcheck_value->addIncoming(null_lv, null_check->cond_true_);
857 
858  null_check.reset(nullptr);
859  cgen_state->ir_builder_.SetInsertPoint(nullcheck_bb);
860  return nullcheck_value;
861 }
JoinInfo join_info_
Definition: PlanState.h:62
#define CHECK_EQ(x, y)
Definition: Logger.h:205
llvm::Value * castToTypeIn(llvm::Value *val, const size_t bit_width)
Definition: CgenState.cpp:106
NullCheckCodegen(CgenState *cgen_state, Executor *executor, llvm::Value *nullable_lv, const SQLTypeInfo &nullable_ti, const std::string &name="")
Definition: IRCodegen.cpp:810
#define IS_LOGIC(X)
Definition: sqldefs.h:60
std::vector< llvm::Value * > outer_join_match_found_per_level_
Definition: CgenState.h:352
llvm::BasicBlock * nullcheck_bb
bool is_trivial_loop_join(const std::vector< InputTableInfo > &query_infos, const RelAlgExecutionUnit &ra_exe_unit)
Definition: Execute.cpp:1116
void codegenJoinLoops(const std::vector< JoinLoop > &join_loops, const RelAlgExecutionUnit &ra_exe_unit, GroupByAndAggregate &group_by_and_aggregate, llvm::Function *query_func, llvm::BasicBlock *entry_bb, const QueryMemoryDescriptor &query_mem_desc, const CompilationOptions &co, const ExecutionOptions &eo)
Definition: IRCodegen.cpp:653
llvm::Value * values_buffer
Definition: JoinLoop.h:48
#define IS_EQUIVALENCE(X)
Definition: sqldefs.h:67
llvm::Value * codegenArith(const Analyzer::BinOper *, const CompilationOptions &)
CgenState * cgen_state_
bool is_fp() const
Definition: sqltypes.h:421
llvm::Value * emitExternalCall(const std::string &fname, llvm::Type *ret_type, const std::vector< llvm::Value * > args, const std::vector< llvm::Attribute::AttrKind > &fnattrs={}, const bool has_struct_return=false)
Definition: CgenState.h:222
llvm::IRBuilder ir_builder_
Definition: CgenState.h:341
llvm::Value * posArg(const Analyzer::Expr *) const
Definition: ColumnIR.cpp:515
std::string join(T const &container, std::string const &delim)
std::vector< InputDescriptor > input_descs
#define CHECK_GE(x, y)
Definition: Logger.h:210
bool need_patch_unnest_double(const SQLTypeInfo &ti, const bool is_maxwell, const bool mem_shared)
Definition: sqldefs.h:49
llvm::ConstantInt * llBool(const bool v) const
Definition: CgenState.h:326
virtual std::vector< llvm::Value * > codegenColumn(const Analyzer::ColumnVar *, const bool fetch_column, const CompilationOptions &)
Definition: ColumnIR.cpp:93
InsertionOrderedMap filter_func_args_
Definition: CgenState.h:354
llvm::Value * codegenArrayAt(const Analyzer::BinOper *, const CompilationOptions &)
Definition: ArrayIR.cpp:26
QualsConjunctiveForm qual_to_conjunctive_form(const std::shared_ptr< Analyzer::Expr > qual_expr)
llvm::Type * get_int_type(const int width, llvm::LLVMContext &context)
bool is_number() const
Definition: sqltypes.h:422
std::vector< llvm::Value * > codegenGeoBinOper(const Analyzer::GeoBinOper *, const CompilationOptions &)
Definition: GeoIR.cpp:81
bool is_time() const
Definition: sqltypes.h:423
std::string to_string(char const *&&v)
llvm::Function * row_func_
Definition: CgenState.h:331
llvm::Value * codegenIsNull(const Analyzer::UOper *, const CompilationOptions &)
Definition: LogicalIR.cpp:372
SQLOps get_optype() const
Definition: Analyzer.h:439
std::vector< llvm::Value * > group_by_expr_cache_
Definition: CgenState.h:348
llvm::LLVMContext & context_
Definition: CgenState.h:339
llvm::Function * current_func_
Definition: CgenState.h:333
llvm::Value * get_arg_by_name(llvm::Function *func, const std::string &name)
Definition: Execute.h:129
std::vector< llvm::Value * > v_
#define INJECT_TIMER(DESC)
Definition: measure.h:93
#define CHECK_NE(x, y)
Definition: Logger.h:206
const bool with_dynamic_watchdog
const JoinQualsPerNestingLevel join_quals
std::vector< llvm::Value * > codegenGeoUOper(const Analyzer::GeoUOper *, const CompilationOptions &)
Definition: GeoIR.cpp:21
llvm::ConstantInt * inlineIntNull(const SQLTypeInfo &)
Definition: CgenState.cpp:27
void setFalseTarget(llvm::BasicBlock *cond_false)
llvm::Value * codegenFunctionOper(const Analyzer::FunctionOper *, const CompilationOptions &)
Executor * executor_
static llvm::BasicBlock * codegen(const std::vector< JoinLoop > &join_loops, const std::function< llvm::BasicBlock *(const std::vector< llvm::Value * > &)> &body_codegen, llvm::Value *outer_iter, llvm::BasicBlock *exit_bb, CgenState *cgen_state)
Definition: JoinLoop.cpp:46
void add_qualifier_to_execution_unit(RelAlgExecutionUnit &ra_exe_unit, const std::shared_ptr< Analyzer::Expr > &qual)
Definition: IRCodegen.cpp:228
std::unordered_map< int, llvm::Value * > scan_idx_to_hash_pos_
Definition: CgenState.h:353
bool needs_error_check_
Definition: CgenState.h:356
std::vector< llvm::Value * > codegenArrayExpr(const Analyzer::ArrayExpr *, const CompilationOptions &)
Definition: ArrayIR.cpp:91
#define AUTOMATIC_IR_METADATA(CGENSTATE)
const SQLTypeInfo & get_type_info() const
Definition: Analyzer.h:78
llvm::Value * codegenUMinus(const Analyzer::UOper *, const CompilationOptions &)
llvm::Value * emitCall(const std::string &fname, const std::vector< llvm::Value * > &args)
Definition: CgenState.cpp:137
llvm::Value * slot_lookup_result
Definition: JoinLoop.h:46
ExecutorDeviceType device_type
std::unordered_map< int, std::unordered_map< int, std::shared_ptr< const ColumnarResults >>> ColumnCacheMap
PlanState * plan_state_
std::shared_ptr< JoinHashTableInterface > hash_table
Definition: Execute.h:770
std::vector< llvm::Value * > codegen(const Analyzer::Expr *, const bool fetch_columns, const CompilationOptions &)
Definition: IRCodegen.cpp:26
#define CHECK_LT(x, y)
Definition: Logger.h:207
llvm::Function * filter_func_
Definition: CgenState.h:332
std::unique_ptr< GroupByAndAggregate::DiamondCodegen > null_check
#define IS_ARITHMETIC(X)
Definition: sqldefs.h:61
const ColumnDescriptor * getDeletedColForTable(const TableId table_id)
Definition: PlanState.h:82
const Expr * get_arg() const
Definition: Analyzer.h:750
auto find(llvm::Value *key)
Expression class for the LOWER (lowercase) string function. The &quot;arg&quot; constructor parameter must be a...
Definition: Analyzer.h:791
llvm::Value * toBool(llvm::Value *)
Definition: LogicalIR.cpp:335
llvm::Value * codegenFunctionOperWithCustomTypeHandling(const Analyzer::FunctionOperWithCustomTypeHandling *, const CompilationOptions &)
llvm::Value * codegenCmp(const Analyzer::BinOper *, const CompilationOptions &)
Definition: CompareIR.cpp:184
std::list< std::shared_ptr< Analyzer::Expr > > quals
const bool allow_loop_joins
llvm::ConstantInt * llInt(const T v) const
Definition: CgenState.h:312
llvm::Value * codegenUnnest(const Analyzer::UOper *, const CompilationOptions &)
Definition: ArrayIR.cpp:20
llvm::Value * addJoinLoopIterator(const std::vector< llvm::Value * > &prev_iters, const size_t level_idx)
Definition: IRCodegen.cpp:635
std::list< std::shared_ptr< Analyzer::Expr > > quals
llvm::Value * finalize(llvm::Value *null_lv, llvm::Value *notnull_lv)
Definition: IRCodegen.cpp:844
#define CHECK(condition)
Definition: Logger.h:197
llvm::Value * codegenLogical(const Analyzer::BinOper *, const CompilationOptions &)
Definition: LogicalIR.cpp:290
void check_if_loop_join_is_allowed(RelAlgExecutionUnit &ra_exe_unit, const ExecutionOptions &eo, const std::vector< InputTableInfo > &query_infos, const size_t level_idx, const std::string &fail_reason)
Definition: IRCodegen.cpp:238
int64_t inline_int_null_val(const SQL_TYPE_INFO &ti)
llvm::ConstantInt * ll_bool(const bool v, llvm::LLVMContext &context)
llvm::Value * codegenCast(const Analyzer::UOper *, const CompilationOptions &)
Definition: CastIR.cpp:20
GroupColLLVMValue groupByColumnCodegen(Analyzer::Expr *group_by_col, const size_t col_width, const CompilationOptions &, const bool translate_null_val, const int64_t translated_null_val, GroupByAndAggregate::DiamondCodegen &, std::stack< llvm::BasicBlock * > &, const bool thread_mem_shared)
Definition: IRCodegen.cpp:702
uint32_t log2_bytes(const uint32_t bytes)
Definition: Execute.h:139
std::string numeric_type_name(const SQLTypeInfo &ti)
Definition: Execute.h:172
void redeclareFilterFunction()
Definition: IRCodegen.cpp:537
std::vector< JoinLoop > buildJoinLoops(RelAlgExecutionUnit &ra_exe_unit, const CompilationOptions &co, const ExecutionOptions &eo, const std::vector< InputTableInfo > &query_infos, ColumnCacheMap &column_cache)
Definition: IRCodegen.cpp:260
std::function< llvm::Value *(const std::vector< llvm::Value * > &, llvm::Value *)> buildIsDeletedCb(const RelAlgExecutionUnit &ra_exe_unit, const size_t level_idx, const CompilationOptions &co)
Definition: IRCodegen.cpp:431
const bool allow_runtime_query_interrupt
Definition: sqldefs.h:39
SQLOps get_optype() const
Definition: Analyzer.h:370
#define VLOG(n)
Definition: Logger.h:291
std::shared_ptr< JoinHashTableInterface > buildCurrentLevelHashTable(const JoinCondition &current_level_join_conditions, RelAlgExecutionUnit &ra_exe_unit, const CompilationOptions &co, const std::vector< InputTableInfo > &query_infos, ColumnCacheMap &column_cache, std::vector< std::string > &fail_reasons)
Definition: IRCodegen.cpp:490
std::list< std::shared_ptr< Analyzer::Expr > > simple_quals
#define IS_COMPARISON(X)
Definition: sqldefs.h:57
llvm::ConstantFP * inlineFpNull(const SQLTypeInfo &)
Definition: CgenState.cpp:65
Executor * executor() const