OmniSciDB  085a039ca4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CgenState.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2021 OmniSci, 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 "CgenState.h"
18 #include "CodeGenerator.h"
20 
21 #include <llvm/IR/InstIterator.h>
22 #include <llvm/Transforms/Utils/BasicBlockUtils.h>
23 #include <llvm/Transforms/Utils/Cloning.h>
24 
25 CgenState::CgenState(const size_t num_query_infos,
26  const bool contains_left_deep_outer_join,
27  Executor* executor)
28  : executor_id_(executor->getExecutorId())
29  , module_(nullptr)
30  , row_func_(nullptr)
31  , filter_func_(nullptr)
32  , current_func_(nullptr)
33  , row_func_bb_(nullptr)
34  , filter_func_bb_(nullptr)
35  , row_func_call_(nullptr)
36  , filter_func_call_(nullptr)
37  , context_(executor->getContext())
38  , ir_builder_(context_)
39  , contains_left_deep_outer_join_(contains_left_deep_outer_join)
40  , outer_join_match_found_per_level_(std::max(num_query_infos, size_t(1)) - 1)
41  , needs_error_check_(false)
42  , needs_geos_(false)
43  , query_func_(nullptr)
44  , query_func_entry_ir_builder_(context_){};
45 
46 CgenState::CgenState(const size_t num_query_infos,
47  const bool contains_left_deep_outer_join)
48  : CgenState(num_query_infos,
49  contains_left_deep_outer_join,
50  Executor::getExecutor(Executor::UNITARY_EXECUTOR_ID).get()) {}
51 
52 CgenState::CgenState(llvm::LLVMContext& context)
53  : executor_id_(Executor::INVALID_EXECUTOR_ID)
54  , module_(nullptr)
55  , row_func_(nullptr)
56  , context_(context)
57  , ir_builder_(context_)
58  , contains_left_deep_outer_join_(false)
59  , needs_error_check_(false)
60  , needs_geos_(false)
61  , query_func_(nullptr)
62  , query_func_entry_ir_builder_(context_){};
63 
64 llvm::ConstantInt* CgenState::inlineIntNull(const SQLTypeInfo& type_info) {
65  auto type = type_info.get_type();
66  if (type_info.is_string()) {
67  switch (type_info.get_compression()) {
68  case kENCODING_DICT:
69  return llInt(static_cast<int32_t>(inline_int_null_val(type_info)));
70  case kENCODING_NONE:
71  return llInt(int64_t(0));
72  default:
73  CHECK(false);
74  }
75  }
76  switch (type) {
77  case kBOOLEAN:
78  return llInt(static_cast<int8_t>(inline_int_null_val(type_info)));
79  case kTINYINT:
80  return llInt(static_cast<int8_t>(inline_int_null_val(type_info)));
81  case kSMALLINT:
82  return llInt(static_cast<int16_t>(inline_int_null_val(type_info)));
83  case kINT:
84  return llInt(static_cast<int32_t>(inline_int_null_val(type_info)));
85  case kBIGINT:
86  case kTIME:
87  case kTIMESTAMP:
88  case kDATE:
89  case kINTERVAL_DAY_TIME:
91  return llInt(inline_int_null_val(type_info));
92  case kDECIMAL:
93  case kNUMERIC:
94  return llInt(inline_int_null_val(type_info));
95  case kARRAY:
96  return llInt(int64_t(0));
97  default:
98  abort();
99  }
100 }
101 
102 llvm::ConstantFP* CgenState::inlineFpNull(const SQLTypeInfo& type_info) {
103  CHECK(type_info.is_fp());
104  switch (type_info.get_type()) {
105  case kFLOAT:
106  return llFp(NULL_FLOAT);
107  case kDOUBLE:
108  return llFp(NULL_DOUBLE);
109  default:
110  abort();
111  }
112 }
113 
114 llvm::Constant* CgenState::inlineNull(const SQLTypeInfo& ti) {
115  return ti.is_fp() ? static_cast<llvm::Constant*>(inlineFpNull(ti))
116  : static_cast<llvm::Constant*>(inlineIntNull(ti));
117 }
118 
119 std::pair<llvm::ConstantInt*, llvm::ConstantInt*> CgenState::inlineIntMaxMin(
120  const size_t byte_width,
121  const bool is_signed) {
122  int64_t max_int{0}, min_int{0};
123  if (is_signed) {
124  std::tie(max_int, min_int) = inline_int_max_min(byte_width);
125  } else {
126  uint64_t max_uint{0}, min_uint{0};
127  std::tie(max_uint, min_uint) = inline_uint_max_min(byte_width);
128  max_int = static_cast<int64_t>(max_uint);
129  CHECK_EQ(uint64_t(0), min_uint);
130  }
131  switch (byte_width) {
132  case 1:
133  return std::make_pair(::ll_int(static_cast<int8_t>(max_int), context_),
134  ::ll_int(static_cast<int8_t>(min_int), context_));
135  case 2:
136  return std::make_pair(::ll_int(static_cast<int16_t>(max_int), context_),
137  ::ll_int(static_cast<int16_t>(min_int), context_));
138  case 4:
139  return std::make_pair(::ll_int(static_cast<int32_t>(max_int), context_),
140  ::ll_int(static_cast<int32_t>(min_int), context_));
141  case 8:
142  return std::make_pair(::ll_int(max_int, context_), ::ll_int(min_int, context_));
143  default:
144  abort();
145  }
146 }
147 
148 llvm::Value* CgenState::castToTypeIn(llvm::Value* val, const size_t dst_bits) {
149  auto src_bits = val->getType()->getScalarSizeInBits();
150  if (src_bits == dst_bits) {
151  return val;
152  }
153  if (val->getType()->isIntegerTy()) {
154  return ir_builder_.CreateIntCast(
155  val, get_int_type(dst_bits, context_), src_bits != 1);
156  }
157  // real (not dictionary-encoded) strings; store the pointer to the payload
158  if (val->getType()->isPointerTy()) {
159  return ir_builder_.CreatePointerCast(val, get_int_type(dst_bits, context_));
160  }
161 
162  CHECK(val->getType()->isFloatTy() || val->getType()->isDoubleTy());
163 
164  llvm::Type* dst_type = nullptr;
165  switch (dst_bits) {
166  case 64:
167  dst_type = llvm::Type::getDoubleTy(context_);
168  break;
169  case 32:
170  dst_type = llvm::Type::getFloatTy(context_);
171  break;
172  default:
173  CHECK(false);
174  }
175 
176  return ir_builder_.CreateFPCast(val, dst_type);
177 }
178 
179 void CgenState::maybeCloneFunctionRecursive(llvm::Function* fn) {
180  CHECK(fn);
181  if (!fn->isDeclaration()) {
182  return;
183  }
184 
185  // Get the implementation from the runtime module.
186  auto func_impl = getExecutor()->get_rt_module()->getFunction(fn->getName());
187  CHECK(func_impl) << fn->getName().str();
188 
189  if (func_impl->isDeclaration()) {
190  return;
191  }
192 
193  auto DestI = fn->arg_begin();
194  for (auto arg_it = func_impl->arg_begin(); arg_it != func_impl->arg_end(); ++arg_it) {
195  DestI->setName(arg_it->getName());
196  vmap_[&*arg_it] = &*DestI++;
197  }
198 
199  llvm::SmallVector<llvm::ReturnInst*, 8> Returns; // Ignore returns cloned.
200 #if LLVM_VERSION_MAJOR > 12
201  llvm::CloneFunctionInto(
202  fn, func_impl, vmap_, llvm::CloneFunctionChangeType::DifferentModule, Returns);
203 #else
204  llvm::CloneFunctionInto(fn, func_impl, vmap_, /*ModuleLevelChanges=*/true, Returns);
205 #endif
206 
207  for (auto it = llvm::inst_begin(fn), e = llvm::inst_end(fn); it != e; ++it) {
208  if (llvm::isa<llvm::CallInst>(*it)) {
209  auto& call = llvm::cast<llvm::CallInst>(*it);
210  maybeCloneFunctionRecursive(call.getCalledFunction());
211  }
212  }
213 }
214 
215 llvm::Value* CgenState::emitCall(const std::string& fname,
216  const std::vector<llvm::Value*>& args) {
217  // Get the function reference from the query module.
218  auto func = module_->getFunction(fname);
219  CHECK(func);
220  // If the function called isn't external, clone the implementation from the runtime
221  // module.
223 
224  return ir_builder_.CreateCall(func, args);
225 }
226 
227 void CgenState::emitErrorCheck(llvm::Value* condition,
228  llvm::Value* errorCode,
229  std::string label) {
230  needs_error_check_ = true;
231  auto check_ok = llvm::BasicBlock::Create(context_, label + "_ok", current_func_);
232  auto check_fail = llvm::BasicBlock::Create(context_, label + "_fail", current_func_);
233  ir_builder_.CreateCondBr(condition, check_ok, check_fail);
234  ir_builder_.SetInsertPoint(check_fail);
235  ir_builder_.CreateRet(errorCode);
236  ir_builder_.SetInsertPoint(check_ok);
237 }
238 
239 namespace {
240 
241 // clang-format off
242 template <typename T>
243 llvm::Type* getTy(llvm::LLVMContext& ctx) { return getTy<std::remove_pointer_t<T>>(ctx)->getPointerTo(); }
244 // Commented out to avoid -Wunused-function warnings, but enable as needed.
245 // template<> llvm::Type* getTy<bool>(llvm::LLVMContext& ctx) { return llvm::Type::getInt1Ty(ctx); }
246 //template<> llvm::Type* getTy<int8_t>(llvm::LLVMContext& ctx) { return llvm::Type::getInt8Ty(ctx); }
247 // template<> llvm::Type* getTy<int16_t>(llvm::LLVMContext& ctx) { return llvm::Type::getInt16Ty(ctx); }
248 //template<> llvm::Type* getTy<int32_t>(llvm::LLVMContext& ctx) { return llvm::Type::getInt32Ty(ctx); }
249 // template<> llvm::Type* getTy<int64_t>(llvm::LLVMContext& ctx) { return llvm::Type::getInt64Ty(ctx); }
250 // template<> llvm::Type* getTy<float>(llvm::LLVMContext& ctx) { return llvm::Type::getFloatTy(ctx); }
251 template<> llvm::Type* getTy<double>(llvm::LLVMContext& ctx) { return llvm::Type::getDoubleTy(ctx); }
252 //template<> llvm::Type* getTy<void>(llvm::LLVMContext& ctx) { return llvm::Type::getVoidTy(ctx); }
253 // clang-format on
254 
256  GpuFunctionDefinition(char const* name) : name_(name) {}
257  char const* const name_;
258 
259  virtual ~GpuFunctionDefinition() = default;
260 
261  virtual llvm::FunctionCallee getFunction(llvm::Module* llvm_module,
262  llvm::LLVMContext& context) const = 0;
263 };
264 
265 // TYPES = return_type, arg0_type, arg1_type, arg2_type, ...
266 template <typename... TYPES>
267 struct GpuFunction final : public GpuFunctionDefinition {
268  GpuFunction(char const* name) : GpuFunctionDefinition(name) {}
269 
270  llvm::FunctionCallee getFunction(llvm::Module* llvm_module,
271  llvm::LLVMContext& context) const {
272  return llvm_module->getOrInsertFunction(name_, getTy<TYPES>(context)...);
273  }
274 };
275 
276 static const std::unordered_map<std::string, std::shared_ptr<GpuFunctionDefinition>>
278  {"asin", std::make_shared<GpuFunction<double, double>>("Asin")},
279  {"atanh", std::make_shared<GpuFunction<double, double>>("Atanh")},
280  {"atan", std::make_shared<GpuFunction<double, double>>("Atan")},
281  {"cosh", std::make_shared<GpuFunction<double, double>>("Cosh")},
282  {"cos", std::make_shared<GpuFunction<double, double>>("Cos")},
283  {"exp", std::make_shared<GpuFunction<double, double>>("Exp")},
284  {"log", std::make_shared<GpuFunction<double, double>>("ln")},
285  {"pow", std::make_shared<GpuFunction<double, double, double>>("power")},
286  {"sinh", std::make_shared<GpuFunction<double, double>>("Sinh")},
287  {"sin", std::make_shared<GpuFunction<double, double>>("Sin")},
288  {"sqrt", std::make_shared<GpuFunction<double, double>>("Sqrt")},
289  {"tan", std::make_shared<GpuFunction<double, double>>("Tan")}};
290 } // namespace
291 
292 std::vector<std::string> CgenState::gpuFunctionsToReplace(llvm::Function* fn) {
293  std::vector<std::string> ret;
294 
295  CHECK(fn);
296  CHECK(!fn->isDeclaration());
297 
298  for (auto& basic_block : *fn) {
299  auto& inst_list = basic_block.getInstList();
300  for (auto inst_itr = inst_list.begin(); inst_itr != inst_list.end(); ++inst_itr) {
301  if (auto call_inst = llvm::dyn_cast<llvm::CallInst>(inst_itr)) {
302  auto called_fcn = call_inst->getCalledFunction();
303  CHECK(called_fcn);
304 
305  if (gpu_replacement_functions.find(called_fcn->getName().str()) !=
307  ret.emplace_back(called_fcn->getName());
308  }
309  }
310  }
311  }
312  return ret;
313 }
314 
315 void CgenState::replaceFunctionForGpu(const std::string& fcn_to_replace,
316  llvm::Function* fn) {
317  CHECK(fn);
318  CHECK(!fn->isDeclaration());
319 
320  auto map_it = gpu_replacement_functions.find(fcn_to_replace);
321  if (map_it == gpu_replacement_functions.end()) {
322  throw QueryMustRunOnCpu("Codegen failed: Could not find replacement functon for " +
323  fcn_to_replace +
324  " to run on gpu. Query step must run in cpu mode.");
325  }
326  const auto& gpu_fcn_obj = map_it->second;
327  CHECK(gpu_fcn_obj);
328  VLOG(1) << "Replacing " << fcn_to_replace << " with " << gpu_fcn_obj->name_
329  << " for parent function " << fn->getName().str();
330 
331  for (auto& basic_block : *fn) {
332  auto& inst_list = basic_block.getInstList();
333  for (auto inst_itr = inst_list.begin(); inst_itr != inst_list.end(); ++inst_itr) {
334  if (auto call_inst = llvm::dyn_cast<llvm::CallInst>(inst_itr)) {
335  auto called_fcn = call_inst->getCalledFunction();
336  CHECK(called_fcn);
337 
338  if (called_fcn->getName() == fcn_to_replace) {
339  std::vector<llvm::Value*> args;
340  std::vector<llvm::Type*> arg_types;
341  for (auto& arg : call_inst->args()) {
342  arg_types.push_back(arg.get()->getType());
343  args.push_back(arg.get());
344  }
345  auto gpu_func = gpu_fcn_obj->getFunction(module_, context_);
346  CHECK(gpu_func);
347  auto gpu_func_type = gpu_func.getFunctionType();
348  CHECK(gpu_func_type);
349  CHECK_EQ(gpu_func_type->getReturnType(), called_fcn->getReturnType());
350  llvm::ReplaceInstWithInst(call_inst,
351  llvm::CallInst::Create(gpu_func, args, ""));
352  return;
353  }
354  }
355  }
356  }
357 }
358 
359 std::shared_ptr<Executor> CgenState::getExecutor() const {
362 }
363 
364 llvm::LLVMContext& CgenState::getExecutorContext() const {
365  return getExecutor()->getContext();
366 }
367 
368 void CgenState::set_module_shallow_copy(const std::unique_ptr<llvm::Module>& llvm_module,
369  bool always_clone) {
370  module_ =
371  llvm::CloneModule(*llvm_module, vmap_, [always_clone](const llvm::GlobalValue* gv) {
372  auto func = llvm::dyn_cast<llvm::Function>(gv);
373  if (!func) {
374  return true;
375  }
376  return (func->getLinkage() == llvm::GlobalValue::LinkageTypes::PrivateLinkage ||
377  func->getLinkage() == llvm::GlobalValue::LinkageTypes::InternalLinkage ||
378  (always_clone && CodeGenerator::alwaysCloneRuntimeFunction(func)));
379  }).release();
380 }
#define CHECK_EQ(x, y)
Definition: Logger.h:231
llvm::Value * castToTypeIn(llvm::Value *val, const size_t bit_width)
Definition: CgenState.cpp:148
llvm::FunctionCallee getFunction(llvm::Module *llvm_module, llvm::LLVMContext &context) const
Definition: CgenState.cpp:270
#define NULL_DOUBLE
Definition: sqltypes.h:49
llvm::LLVMContext & getExecutorContext() const
Definition: CgenState.cpp:364
std::shared_ptr< Executor > getExecutor() const
Definition: CgenState.cpp:359
#define NULL_FLOAT
void maybeCloneFunctionRecursive(llvm::Function *fn)
Definition: CgenState.cpp:179
bool is_fp() const
Definition: sqltypes.h:514
llvm::ConstantInt * ll_int(const T v, llvm::LLVMContext &context)
llvm::IRBuilder ir_builder_
Definition: CgenState.h:361
llvm::Type * getTy(llvm::LLVMContext &ctx)
Definition: CgenState.cpp:243
HOST DEVICE SQLTypes get_type() const
Definition: sqltypes.h:329
llvm::Type * get_int_type(const int width, llvm::LLVMContext &context)
llvm::Type * getTy< double >(llvm::LLVMContext &ctx)
Definition: CgenState.cpp:251
static std::shared_ptr< Executor > getExecutor(const ExecutorId id, const std::string &debug_dir="", const std::string &debug_file="", const SystemParameters &system_parameters=SystemParameters())
Definition: Execute.cpp:483
static const std::unordered_map< std::string, std::shared_ptr< GpuFunctionDefinition > > gpu_replacement_functions
Definition: CgenState.cpp:277
llvm::Module * module_
Definition: CgenState.h:350
size_t executor_id_
Definition: CgenState.h:323
llvm::LLVMContext & context_
Definition: CgenState.h:359
llvm::Function * current_func_
Definition: CgenState.h:353
llvm::ConstantInt * inlineIntNull(const SQLTypeInfo &)
Definition: CgenState.cpp:64
void replaceFunctionForGpu(const std::string &fcn_to_replace, llvm::Function *fn)
Definition: CgenState.cpp:315
bool needs_error_check_
Definition: CgenState.h:381
llvm::ConstantFP * llFp(const float v) const
Definition: CgenState.h:300
std::vector< std::string > gpuFunctionsToReplace(llvm::Function *fn)
Definition: CgenState.cpp:292
llvm::Value * emitCall(const std::string &fname, const std::vector< llvm::Value * > &args)
Definition: CgenState.cpp:215
static const ExecutorId INVALID_EXECUTOR_ID
Definition: Execute.h:377
std::pair< uint64_t, uint64_t > inline_uint_max_min(const size_t byte_width)
llvm::Constant * inlineNull(const SQLTypeInfo &)
Definition: CgenState.cpp:114
void set_module_shallow_copy(const std::unique_ptr< llvm::Module > &module, bool always_clone=false)
Definition: CgenState.cpp:368
Definition: sqltypes.h:53
HOST DEVICE EncodingType get_compression() const
Definition: sqltypes.h:337
static bool alwaysCloneRuntimeFunction(const llvm::Function *func)
void emitErrorCheck(llvm::Value *condition, llvm::Value *errorCode, std::string label)
Definition: CgenState.cpp:227
llvm::ConstantInt * llInt(const T v) const
Definition: CgenState.h:296
bool g_enable_watchdog false
Definition: Execute.cpp:79
#define CHECK(condition)
Definition: Logger.h:223
llvm::ValueToValueMapTy vmap_
Definition: CgenState.h:360
int64_t inline_int_null_val(const SQL_TYPE_INFO &ti)
std::pair< int64_t, int64_t > inline_int_max_min(const size_t byte_width)
Definition: sqltypes.h:45
bool is_string() const
Definition: sqltypes.h:510
string name
Definition: setup.in.py:72
CgenState(const size_t num_query_infos, const bool contains_left_deep_outer_join, Executor *executor)
Definition: CgenState.cpp:25
std::pair< llvm::ConstantInt *, llvm::ConstantInt * > inlineIntMaxMin(const size_t byte_width, const bool is_signed)
Definition: CgenState.cpp:119
#define VLOG(n)
Definition: Logger.h:317
llvm::ConstantFP * inlineFpNull(const SQLTypeInfo &)
Definition: CgenState.cpp:102