OmniSciDB  8a228a1076
DateTimeIR.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 "CodeGenerator.h"
18 
19 #include "DateTimeUtils.h"
20 #include "Execute.h"
21 
22 using namespace DateTimeUtils;
23 
24 namespace {
25 
27  switch (field) {
28  case kEPOCH:
29  return "extract_epoch";
30  case kDATEEPOCH:
31  return "extract_dateepoch";
32  case kQUARTERDAY:
33  return "extract_quarterday";
34  case kHOUR:
35  return "extract_hour";
36  case kMINUTE:
37  return "extract_minute";
38  case kSECOND:
39  return "extract_second";
40  case kMILLISECOND:
41  return "extract_millisecond";
42  case kMICROSECOND:
43  return "extract_microsecond";
44  case kNANOSECOND:
45  return "extract_nanosecond";
46  case kDOW:
47  return "extract_dow";
48  case kISODOW:
49  return "extract_isodow";
50  case kDAY:
51  return "extract_day";
52  case kWEEK:
53  return "extract_week";
54  case kDOY:
55  return "extract_day_of_year";
56  case kMONTH:
57  return "extract_month";
58  case kQUARTER:
59  return "extract_quarter";
60  case kYEAR:
61  return "extract_year";
62  }
63  UNREACHABLE();
64  return "";
65 }
66 
67 } // namespace
68 
69 llvm::Value* CodeGenerator::codegen(const Analyzer::ExtractExpr* extract_expr,
70  const CompilationOptions& co) {
71  AUTOMATIC_IR_METADATA(cgen_state_);
72  auto from_expr = codegen(extract_expr->get_from_expr(), true, co).front();
73  const int32_t extract_field{extract_expr->get_field()};
74  const auto& extract_expr_ti = extract_expr->get_from_expr()->get_type_info();
75  if (extract_field == kEPOCH) {
76  CHECK(extract_expr_ti.get_type() == kTIMESTAMP ||
77  extract_expr_ti.get_type() == kDATE);
78  if (from_expr->getType()->isIntegerTy(32)) {
79  from_expr =
80  cgen_state_->ir_builder_.CreateCast(llvm::Instruction::CastOps::SExt,
81  from_expr,
82  get_int_type(64, cgen_state_->context_));
83  return from_expr;
84  }
85  }
86  CHECK(from_expr->getType()->isIntegerTy(64));
87  if (extract_expr_ti.is_high_precision_timestamp()) {
88  from_expr = codegenExtractHighPrecisionTimestamps(
89  from_expr, extract_expr_ti, extract_expr->get_field());
90  }
91  if (!extract_expr_ti.is_high_precision_timestamp() &&
92  is_subsecond_extract_field(extract_expr->get_field())) {
93  from_expr =
94  extract_expr_ti.get_notnull()
95  ? cgen_state_->ir_builder_.CreateMul(
96  from_expr,
97  cgen_state_->llInt(
99  : cgen_state_->emitCall(
100  "mul_int64_t_nullable_lhs",
101  {from_expr,
102  cgen_state_->llInt(
103  get_extract_timestamp_precision_scale(extract_expr->get_field())),
104  cgen_state_->inlineIntNull(extract_expr_ti)});
105  }
106  const auto extract_fname = get_extract_function_name(extract_expr->get_field());
107  if (!extract_expr_ti.get_notnull()) {
108  llvm::BasicBlock* extract_nullcheck_bb{nullptr};
109  llvm::PHINode* extract_nullcheck_value{nullptr};
110  {
112  cgen_state_->ir_builder_.CreateICmp(
113  llvm::ICmpInst::ICMP_EQ,
114  from_expr,
115  cgen_state_->inlineIntNull(extract_expr_ti)),
116  executor(),
117  false,
118  "extract_nullcheck",
119  nullptr,
120  false);
121  // generate a phi node depending on whether we got a null or not
122  extract_nullcheck_bb = llvm::BasicBlock::Create(
123  cgen_state_->context_, "extract_nullcheck_bb", cgen_state_->row_func_);
124 
125  // update the blocks created by diamond codegen to point to the newly created phi
126  // block
127  cgen_state_->ir_builder_.SetInsertPoint(null_check.cond_true_);
128  cgen_state_->ir_builder_.CreateBr(extract_nullcheck_bb);
129  cgen_state_->ir_builder_.SetInsertPoint(null_check.cond_false_);
130  auto extract_call =
131  cgen_state_->emitExternalCall(extract_fname,
132  get_int_type(64, cgen_state_->context_),
133  std::vector<llvm::Value*>{from_expr});
134  cgen_state_->ir_builder_.CreateBr(extract_nullcheck_bb);
135 
136  cgen_state_->ir_builder_.SetInsertPoint(extract_nullcheck_bb);
137  extract_nullcheck_value = cgen_state_->ir_builder_.CreatePHI(
138  get_int_type(64, cgen_state_->context_), 2, "extract_value");
139  extract_nullcheck_value->addIncoming(extract_call, null_check.cond_false_);
140  extract_nullcheck_value->addIncoming(cgen_state_->inlineIntNull(extract_expr_ti),
141  null_check.cond_true_);
142  }
143 
144  // diamond codegen will set the insert point in its destructor. override it to
145  // continue using the extract nullcheck bb
146  CHECK(extract_nullcheck_bb);
147  cgen_state_->ir_builder_.SetInsertPoint(extract_nullcheck_bb);
148  CHECK(extract_nullcheck_value);
149  return extract_nullcheck_value;
150  } else {
151  return cgen_state_->emitExternalCall(extract_fname,
152  get_int_type(64, cgen_state_->context_),
153  std::vector<llvm::Value*>{from_expr});
154  }
155 }
156 
157 llvm::Value* CodeGenerator::codegen(const Analyzer::DateaddExpr* dateadd_expr,
158  const CompilationOptions& co) {
159  AUTOMATIC_IR_METADATA(cgen_state_);
160  const auto& dateadd_expr_ti = dateadd_expr->get_type_info();
161  CHECK(dateadd_expr_ti.get_type() == kTIMESTAMP || dateadd_expr_ti.get_type() == kDATE);
162  auto datetime = codegen(dateadd_expr->get_datetime_expr(), true, co).front();
163  CHECK(datetime->getType()->isIntegerTy(64));
164  auto number = codegen(dateadd_expr->get_number_expr(), true, co).front();
165 
166  const auto& datetime_ti = dateadd_expr->get_datetime_expr()->get_type_info();
167  std::vector<llvm::Value*> dateadd_args{
168  cgen_state_->llInt(static_cast<int32_t>(dateadd_expr->get_field())),
169  number,
170  datetime};
171  std::string dateadd_fname{"DateAdd"};
172  if (is_subsecond_dateadd_field(dateadd_expr->get_field()) ||
173  dateadd_expr_ti.is_high_precision_timestamp()) {
174  dateadd_fname += "HighPrecision";
175  dateadd_args.push_back(
176  cgen_state_->llInt(static_cast<int32_t>(datetime_ti.get_dimension())));
177  }
178  if (!datetime_ti.get_notnull()) {
179  dateadd_args.push_back(cgen_state_->inlineIntNull(datetime_ti));
180  dateadd_fname += "Nullable";
181  }
182  return cgen_state_->emitExternalCall(dateadd_fname,
183  get_int_type(64, cgen_state_->context_),
184  dateadd_args,
185  {llvm::Attribute::NoUnwind,
186  llvm::Attribute::ReadNone,
187  llvm::Attribute::Speculatable});
188 }
189 
190 llvm::Value* CodeGenerator::codegen(const Analyzer::DatediffExpr* datediff_expr,
191  const CompilationOptions& co) {
192  AUTOMATIC_IR_METADATA(cgen_state_);
193  auto start = codegen(datediff_expr->get_start_expr(), true, co).front();
194  CHECK(start->getType()->isIntegerTy(64));
195  auto end = codegen(datediff_expr->get_end_expr(), true, co).front();
196  CHECK(end->getType()->isIntegerTy(32) || end->getType()->isIntegerTy(64));
197  const auto& start_ti = datediff_expr->get_start_expr()->get_type_info();
198  const auto& end_ti = datediff_expr->get_end_expr()->get_type_info();
199  std::vector<llvm::Value*> datediff_args{
200  cgen_state_->llInt(static_cast<int32_t>(datediff_expr->get_field())), start, end};
201  std::string datediff_fname{"DateDiff"};
202  if (start_ti.is_high_precision_timestamp() || end_ti.is_high_precision_timestamp()) {
203  datediff_fname += "HighPrecision";
204  datediff_args.push_back(
205  cgen_state_->llInt(static_cast<int32_t>(start_ti.get_dimension())));
206  datediff_args.push_back(
207  cgen_state_->llInt(static_cast<int32_t>(end_ti.get_dimension())));
208  }
209  const auto& ret_ti = datediff_expr->get_type_info();
210  if (!start_ti.get_notnull() || !end_ti.get_notnull()) {
211  datediff_args.push_back(cgen_state_->inlineIntNull(ret_ti));
212  datediff_fname += "Nullable";
213  }
214  return cgen_state_->emitExternalCall(
215  datediff_fname, get_int_type(64, cgen_state_->context_), datediff_args);
216 }
217 
218 llvm::Value* CodeGenerator::codegen(const Analyzer::DatetruncExpr* datetrunc_expr,
219  const CompilationOptions& co) {
220  AUTOMATIC_IR_METADATA(cgen_state_);
221  auto from_expr = codegen(datetrunc_expr->get_from_expr(), true, co).front();
222  const auto& datetrunc_expr_ti = datetrunc_expr->get_from_expr()->get_type_info();
223  CHECK(from_expr->getType()->isIntegerTy(64));
224  DatetruncField const field = datetrunc_expr->get_field();
225  if (datetrunc_expr_ti.is_high_precision_timestamp()) {
226  return codegenDateTruncHighPrecisionTimestamps(from_expr, datetrunc_expr_ti, field);
227  }
228  static_assert(dtSECOND + 1 == dtMILLISECOND, "Please keep these consecutive.");
229  static_assert(dtMILLISECOND + 1 == dtMICROSECOND, "Please keep these consecutive.");
230  static_assert(dtMICROSECOND + 1 == dtNANOSECOND, "Please keep these consecutive.");
231  if (dtSECOND <= field && field <= dtNANOSECOND) {
232  return cgen_state_->ir_builder_.CreateCast(llvm::Instruction::CastOps::SExt,
233  from_expr,
234  get_int_type(64, cgen_state_->context_));
235  }
236  std::unique_ptr<CodeGenerator::NullCheckCodegen> nullcheck_codegen;
237  const bool is_nullable = !datetrunc_expr_ti.get_notnull();
238  if (is_nullable) {
239  nullcheck_codegen = std::make_unique<NullCheckCodegen>(
240  cgen_state_, executor(), from_expr, datetrunc_expr_ti, "date_trunc_nullcheck");
241  }
242  char const* const fname = datetrunc_fname_lookup.at(field);
243  auto ret = cgen_state_->emitExternalCall(
244  fname, get_int_type(64, cgen_state_->context_), {{from_expr}});
245  if (is_nullable) {
246  ret = nullcheck_codegen->finalize(ll_int(NULL_BIGINT, cgen_state_->context_), ret);
247  }
248  return ret;
249 }
250 
252  llvm::Value* ts_lv,
253  const SQLTypeInfo& ti,
254  const ExtractField& field) {
255  AUTOMATIC_IR_METADATA(cgen_state_);
257  CHECK(ts_lv->getType()->isIntegerTy(64));
258  if (is_subsecond_extract_field(field)) {
259  const auto result =
261  if (result.first == kMULTIPLY) {
262  return ti.get_notnull()
263  ? cgen_state_->ir_builder_.CreateMul(
264  ts_lv, cgen_state_->llInt(static_cast<int64_t>(result.second)))
265  : cgen_state_->emitCall(
266  "mul_int64_t_nullable_lhs",
267  {ts_lv,
268  cgen_state_->llInt(static_cast<int64_t>(result.second)),
269  cgen_state_->inlineIntNull(ti)});
270  } else if (result.first == kDIVIDE) {
271  return ti.get_notnull()
272  ? cgen_state_->ir_builder_.CreateSDiv(
273  ts_lv, cgen_state_->llInt(static_cast<int64_t>(result.second)))
274  : cgen_state_->emitCall(
275  "floor_div_nullable_lhs",
276  {ts_lv,
277  cgen_state_->llInt(static_cast<int64_t>(result.second)),
278  cgen_state_->inlineIntNull(ti)});
279  } else {
280  return ts_lv;
281  }
282  }
283  return ti.get_notnull()
284  ? cgen_state_->ir_builder_.CreateSDiv(
285  ts_lv,
286  cgen_state_->llInt(static_cast<int64_t>(
288  : cgen_state_->emitCall(
289  "floor_div_nullable_lhs",
290  {ts_lv,
291  cgen_state_->llInt(get_timestamp_precision_scale(ti.get_dimension())),
292  cgen_state_->inlineIntNull(ti)});
293 }
294 
296  llvm::Value* ts_lv,
297  const SQLTypeInfo& ti,
298  const DatetruncField& field) {
299  // Only needed for i in { 0, 3, 6, 9 }.
300  constexpr int64_t pow10[10]{1, 0, 0, 1000, 0, 0, 1000000, 0, 0, 1000000000};
301  AUTOMATIC_IR_METADATA(cgen_state_);
303  CHECK(ts_lv->getType()->isIntegerTy(64));
304  bool const is_nullable = !ti.get_notnull();
305  static_assert(dtSECOND + 1 == dtMILLISECOND, "Please keep these consecutive.");
306  static_assert(dtMILLISECOND + 1 == dtMICROSECOND, "Please keep these consecutive.");
307  static_assert(dtMICROSECOND + 1 == dtNANOSECOND, "Please keep these consecutive.");
308  if (dtSECOND <= field && field <= dtNANOSECOND) {
309  unsigned const start_dim = ti.get_dimension(); // 0, 3, 6, 9
310  unsigned const trunc_dim = (field - dtSECOND) * 3; // 0, 3, 6, 9
311  if (start_dim <= trunc_dim) {
312  return ts_lv; // Truncating to an equal or higher precision has no effect.
313  }
314  int64_t const dscale = pow10[start_dim - trunc_dim]; // 1e3, 1e6, 1e9
315  if (is_nullable) {
316  ts_lv = cgen_state_->emitCall(
317  "floor_div_nullable_lhs",
318  {ts_lv, cgen_state_->llInt(dscale), cgen_state_->inlineIntNull(ti)});
319  return cgen_state_->emitCall(
320  "mul_int64_t_nullable_lhs",
321  {ts_lv, cgen_state_->llInt(dscale), cgen_state_->inlineIntNull(ti)});
322  } else {
323  ts_lv = cgen_state_->ir_builder_.CreateSDiv(ts_lv, cgen_state_->llInt(dscale));
324  return cgen_state_->ir_builder_.CreateMul(ts_lv, cgen_state_->llInt(dscale));
325  }
326  }
327  int64_t const scale = pow10[ti.get_dimension()];
328  ts_lv = is_nullable
329  ? cgen_state_->emitCall(
330  "floor_div_nullable_lhs",
331  {ts_lv, cgen_state_->llInt(scale), cgen_state_->inlineIntNull(ti)})
332  : cgen_state_->ir_builder_.CreateSDiv(ts_lv, cgen_state_->llInt(scale));
333 
334  std::unique_ptr<CodeGenerator::NullCheckCodegen> nullcheck_codegen;
335  if (is_nullable) {
336  nullcheck_codegen = std::make_unique<NullCheckCodegen>(
337  cgen_state_, executor(), ts_lv, ti, "date_trunc_hp_nullcheck");
338  }
339  char const* const fname = datetrunc_fname_lookup.at(field);
340  ts_lv = cgen_state_->emitExternalCall(
341  fname, get_int_type(64, cgen_state_->context_), {{ts_lv}});
342  if (is_nullable) {
343  ts_lv =
344  nullcheck_codegen->finalize(ll_int(NULL_BIGINT, cgen_state_->context_), ts_lv);
345  }
346 
347  return is_nullable
348  ? cgen_state_->emitCall(
349  "mul_int64_t_nullable_lhs",
350  {ts_lv, cgen_state_->llInt(scale), cgen_state_->inlineIntNull(ti)})
351  : cgen_state_->ir_builder_.CreateMul(ts_lv, cgen_state_->llInt(scale));
352 }
constexpr int64_t get_extract_timestamp_precision_scale(const ExtractField field)
Definition: DateTimeUtils.h:81
const Expr * get_datetime_expr() const
Definition: Analyzer.h:1240
#define NULL_BIGINT
Definition: sqltypes.h:184
llvm::ConstantInt * ll_int(const T v, llvm::LLVMContext &context)
const char * get_extract_function_name(ExtractField field)
Definition: DateTimeIR.cpp:26
#define UNREACHABLE()
Definition: Logger.h:241
const Expr * get_start_expr() const
Definition: Analyzer.h:1281
DateaddField get_field() const
Definition: Analyzer.h:1238
llvm::Value * codegenExtractHighPrecisionTimestamps(llvm::Value *, const SQLTypeInfo &, const ExtractField &)
Definition: DateTimeIR.cpp:251
bool is_high_precision_timestamp() const
Definition: sqltypes.h:643
const Expr * get_from_expr() const
Definition: Analyzer.h:1198
const std::pair< SQLOps, int64_t > get_extract_high_precision_adjusted_scale(const ExtractField &field, const int32_t dimen)
llvm::Type * get_int_type(const int width, llvm::LLVMContext &context)
DatetruncField get_field() const
Definition: Analyzer.h:1322
HOST DEVICE bool get_notnull() const
Definition: sqltypes.h:266
const rapidjson::Value & field(const rapidjson::Value &obj, const char field[]) noexcept
Definition: JsonAccessors.h:31
DatetruncField
Definition: DateTruncate.h:27
const Expr * get_from_expr() const
Definition: Analyzer.h:1323
#define AUTOMATIC_IR_METADATA(CGENSTATE)
DatetruncField get_field() const
Definition: Analyzer.h:1280
std::vector< llvm::Value * > codegen(const Analyzer::Expr *, const bool fetch_columns, const CompilationOptions &)
Definition: IRCodegen.cpp:26
Definition: sqltypes.h:55
constexpr std::array< char const *, dtINVALID > datetrunc_fname_lookup
Definition: DateTruncate.h:46
HOST DEVICE int get_dimension() const
Definition: sqltypes.h:261
ExtractField
constexpr bool is_subsecond_dateadd_field(const DateaddField field)
Definition: DateTimeUtils.h:99
const SQLTypeInfo & get_type_info() const
Definition: Analyzer.h:79
#define CHECK(condition)
Definition: Logger.h:197
constexpr int64_t get_timestamp_precision_scale(const int32_t dimen)
Definition: DateTimeUtils.h:51
llvm::Value * codegenDateTruncHighPrecisionTimestamps(llvm::Value *, const SQLTypeInfo &, const DatetruncField &)
Definition: DateTimeIR.cpp:295
ExtractField get_field() const
Definition: Analyzer.h:1197
const Expr * get_end_expr() const
Definition: Analyzer.h:1282
const Expr * get_number_expr() const
Definition: Analyzer.h:1239
constexpr bool is_subsecond_extract_field(const ExtractField &field)
Definition: DateTimeUtils.h:95