OmniSciDB  72c90bc290
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DateTimeIR.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 
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_monday";
54  case kWEEK_SUNDAY:
55  return "extract_week_sunday";
56  case kWEEK_SATURDAY:
57  return "extract_week_saturday";
58  case kDOY:
59  return "extract_day_of_year";
60  case kMONTH:
61  return "extract_month";
62  case kQUARTER:
63  return "extract_quarter";
64  case kYEAR:
65  return "extract_year";
66  case kUNKNOWN_FIELD:
67  UNREACHABLE();
68  return "";
69  }
70  UNREACHABLE();
71  return "";
72 }
73 
74 } // namespace
75 
76 llvm::Value* CodeGenerator::codegen(const Analyzer::ExtractExpr* extract_expr,
77  const CompilationOptions& co) {
78  AUTOMATIC_IR_METADATA(cgen_state_);
79  auto from_expr = codegen(extract_expr->get_from_expr(), true, co).front();
80  const int32_t extract_field{extract_expr->get_field()};
81  const auto& extract_expr_ti = extract_expr->get_from_expr()->get_type_info();
82  if (extract_field == kEPOCH) {
83  CHECK(extract_expr_ti.get_type() == kTIMESTAMP ||
84  extract_expr_ti.get_type() == kDATE);
85  if (from_expr->getType()->isIntegerTy(32)) {
86  from_expr =
87  cgen_state_->ir_builder_.CreateCast(llvm::Instruction::CastOps::SExt,
88  from_expr,
89  get_int_type(64, cgen_state_->context_));
90  return from_expr;
91  }
92  }
93  CHECK(from_expr->getType()->isIntegerTy(64));
94  if (extract_expr_ti.is_high_precision_timestamp()) {
95  from_expr = codegenExtractHighPrecisionTimestamps(
96  from_expr, extract_expr_ti, extract_expr->get_field());
97  }
98  if (!extract_expr_ti.is_high_precision_timestamp() &&
99  is_subsecond_extract_field(extract_expr->get_field())) {
100  from_expr =
101  extract_expr_ti.get_notnull()
102  ? cgen_state_->ir_builder_.CreateMul(
103  from_expr,
104  cgen_state_->llInt(
106  : cgen_state_->emitCall(
107  "mul_int64_t_nullable_lhs",
108  {from_expr,
109  cgen_state_->llInt(
111  cgen_state_->inlineIntNull(extract_expr_ti)});
112  }
113  const auto extract_fname = get_extract_function_name(extract_expr->get_field());
114  if (!extract_expr_ti.get_notnull()) {
115  llvm::BasicBlock* extract_nullcheck_bb{nullptr};
116  llvm::PHINode* extract_nullcheck_value{nullptr};
117  {
118  DiamondCodegen null_check(cgen_state_->ir_builder_.CreateICmp(
119  llvm::ICmpInst::ICMP_EQ,
120  from_expr,
121  cgen_state_->inlineIntNull(extract_expr_ti)),
122  executor(),
123  false,
124  "extract_nullcheck",
125  nullptr,
126  false);
127  // generate a phi node depending on whether we got a null or not
128  extract_nullcheck_bb = llvm::BasicBlock::Create(
129  cgen_state_->context_, "extract_nullcheck_bb", cgen_state_->current_func_);
130 
131  // update the blocks created by diamond codegen to point to the newly created phi
132  // block
133  cgen_state_->ir_builder_.SetInsertPoint(null_check.cond_true_);
134  cgen_state_->ir_builder_.CreateBr(extract_nullcheck_bb);
135  cgen_state_->ir_builder_.SetInsertPoint(null_check.cond_false_);
136  auto extract_call =
137  cgen_state_->emitExternalCall(extract_fname,
138  get_int_type(64, cgen_state_->context_),
139  std::vector<llvm::Value*>{from_expr});
140  cgen_state_->ir_builder_.CreateBr(extract_nullcheck_bb);
141 
142  cgen_state_->ir_builder_.SetInsertPoint(extract_nullcheck_bb);
143  extract_nullcheck_value = cgen_state_->ir_builder_.CreatePHI(
144  get_int_type(64, cgen_state_->context_), 2, "extract_value");
145  extract_nullcheck_value->addIncoming(extract_call, null_check.cond_false_);
146  extract_nullcheck_value->addIncoming(cgen_state_->inlineIntNull(extract_expr_ti),
147  null_check.cond_true_);
148  }
149 
150  // diamond codegen will set the insert point in its destructor. override it to
151  // continue using the extract nullcheck bb
152  CHECK(extract_nullcheck_bb);
153  cgen_state_->ir_builder_.SetInsertPoint(extract_nullcheck_bb);
154  CHECK(extract_nullcheck_value);
155  return extract_nullcheck_value;
156  } else {
157  return cgen_state_->emitExternalCall(extract_fname,
158  get_int_type(64, cgen_state_->context_),
159  std::vector<llvm::Value*>{from_expr});
160  }
161 }
162 
163 llvm::Value* CodeGenerator::codegen(const Analyzer::DateaddExpr* dateadd_expr,
164  const CompilationOptions& co) {
166  const auto& dateadd_expr_ti = dateadd_expr->get_type_info();
167  CHECK(dateadd_expr_ti.get_type() == kTIMESTAMP || dateadd_expr_ti.get_type() == kDATE);
168  auto datetime = codegen(dateadd_expr->get_datetime_expr(), true, co).front();
169  CHECK(datetime->getType()->isIntegerTy(64));
170  auto number = codegen(dateadd_expr->get_number_expr(), true, co).front();
171 
172  const auto& datetime_ti = dateadd_expr->get_datetime_expr()->get_type_info();
173  std::vector<llvm::Value*> dateadd_args{
174  cgen_state_->llInt(static_cast<int32_t>(dateadd_expr->get_field())),
175  number,
176  datetime};
177  std::string dateadd_fname{"DateAdd"};
178  if (is_subsecond_dateadd_field(dateadd_expr->get_field()) ||
179  dateadd_expr_ti.is_high_precision_timestamp()) {
180  dateadd_fname += "HighPrecision";
181  dateadd_args.push_back(
182  cgen_state_->llInt(static_cast<int32_t>(datetime_ti.get_dimension())));
183  }
184  if (!datetime_ti.get_notnull()) {
185  auto null_val = dateadd_expr->use_fixed_encoding_null_val()
186  ? inline_fixed_encoding_null_val(datetime_ti)
187  : inline_int_null_val(datetime_ti);
188  dateadd_args.push_back(cgen_state_->llInt((int64_t)null_val));
189  dateadd_fname += "Nullable";
190  }
191  return cgen_state_->emitExternalCall(dateadd_fname,
193  dateadd_args,
194  {llvm::Attribute::NoUnwind,
195  llvm::Attribute::ReadNone,
196  llvm::Attribute::Speculatable});
197 }
198 
199 llvm::Value* CodeGenerator::codegen(const Analyzer::DatediffExpr* datediff_expr,
200  const CompilationOptions& co) {
202  auto start = codegen(datediff_expr->get_start_expr(), true, co).front();
203  CHECK(start->getType()->isIntegerTy(64));
204  auto end = codegen(datediff_expr->get_end_expr(), true, co).front();
205  CHECK(end->getType()->isIntegerTy(32) || end->getType()->isIntegerTy(64));
206  const auto& start_ti = datediff_expr->get_start_expr()->get_type_info();
207  const auto& end_ti = datediff_expr->get_end_expr()->get_type_info();
208  std::vector<llvm::Value*> datediff_args{
209  cgen_state_->llInt(static_cast<int32_t>(datediff_expr->get_field())), start, end};
210  std::string datediff_fname{"DateDiff"};
211  if (start_ti.is_high_precision_timestamp() || end_ti.is_high_precision_timestamp()) {
212  datediff_fname += "HighPrecision";
213  datediff_args.push_back(
214  cgen_state_->llInt(static_cast<int32_t>(start_ti.get_dimension())));
215  datediff_args.push_back(
216  cgen_state_->llInt(static_cast<int32_t>(end_ti.get_dimension())));
217  }
218  const auto& ret_ti = datediff_expr->get_type_info();
219  if (!start_ti.get_notnull() || !end_ti.get_notnull()) {
220  datediff_args.push_back(cgen_state_->inlineIntNull(ret_ti));
221  datediff_fname += "Nullable";
222  }
224  datediff_fname, get_int_type(64, cgen_state_->context_), datediff_args);
225 }
226 
227 llvm::Value* CodeGenerator::codegen(const Analyzer::DatetruncExpr* datetrunc_expr,
228  const CompilationOptions& co) {
230  auto from_expr = codegen(datetrunc_expr->get_from_expr(), true, co).front();
231  const auto& datetrunc_expr_ti = datetrunc_expr->get_from_expr()->get_type_info();
232  CHECK(from_expr->getType()->isIntegerTy(64));
233  DatetruncField const field = datetrunc_expr->get_field();
234  if (datetrunc_expr_ti.is_high_precision_timestamp()) {
235  return codegenDateTruncHighPrecisionTimestamps(from_expr, datetrunc_expr_ti, field);
236  }
237  static_assert(dtSECOND + 1 == dtMILLISECOND, "Please keep these consecutive.");
238  static_assert(dtMILLISECOND + 1 == dtMICROSECOND, "Please keep these consecutive.");
239  static_assert(dtMICROSECOND + 1 == dtNANOSECOND, "Please keep these consecutive.");
240  if (dtSECOND <= field && field <= dtNANOSECOND) {
241  return cgen_state_->ir_builder_.CreateCast(llvm::Instruction::CastOps::SExt,
242  from_expr,
244  }
245  std::unique_ptr<CodeGenerator::NullCheckCodegen> nullcheck_codegen;
246  const bool is_nullable = !datetrunc_expr_ti.get_notnull();
247  if (is_nullable) {
248  nullcheck_codegen = std::make_unique<NullCheckCodegen>(
249  cgen_state_, executor(), from_expr, datetrunc_expr_ti, "date_trunc_nullcheck");
250  }
251  char const* const fname = datetrunc_fname_lookup.at(field);
252  auto ret = cgen_state_->emitExternalCall(
253  fname, get_int_type(64, cgen_state_->context_), {from_expr});
254  if (is_nullable) {
255  ret = nullcheck_codegen->finalize(ll_int(NULL_BIGINT, cgen_state_->context_), ret);
256  }
257  return ret;
258 }
259 
261  llvm::Value* ts_lv,
262  const SQLTypeInfo& ti,
263  const ExtractField& field) {
266  CHECK(ts_lv->getType()->isIntegerTy(64));
267  if (is_subsecond_extract_field(field)) {
268  const auto result =
270  if (result.first == kMULTIPLY) {
271  return ti.get_notnull()
272  ? cgen_state_->ir_builder_.CreateMul(
273  ts_lv, cgen_state_->llInt(static_cast<int64_t>(result.second)))
275  "mul_int64_t_nullable_lhs",
276  {ts_lv,
277  cgen_state_->llInt(static_cast<int64_t>(result.second)),
278  cgen_state_->inlineIntNull(ti)});
279  } else if (result.first == kDIVIDE) {
280  return ti.get_notnull()
281  ? cgen_state_->ir_builder_.CreateSDiv(
282  ts_lv, cgen_state_->llInt(static_cast<int64_t>(result.second)))
284  "floor_div_nullable_lhs",
285  {ts_lv,
286  cgen_state_->llInt(static_cast<int64_t>(result.second)),
287  cgen_state_->inlineIntNull(ti)});
288  } else {
289  return ts_lv;
290  }
291  }
292  return ti.get_notnull()
293  ? cgen_state_->ir_builder_.CreateSDiv(
294  ts_lv,
295  cgen_state_->llInt(static_cast<int64_t>(
296  get_timestamp_precision_scale(ti.get_dimension()))))
297  : cgen_state_->emitCall(
298  "floor_div_nullable_lhs",
299  {ts_lv,
300  cgen_state_->llInt(get_timestamp_precision_scale(ti.get_dimension())),
301  cgen_state_->inlineIntNull(ti)});
302 }
303 
305  llvm::Value* ts_lv,
306  const SQLTypeInfo& ti,
307  const DatetruncField& field) {
308  // Only needed for i in { 0, 3, 6, 9 }.
309  constexpr int64_t pow10[10]{1, 0, 0, 1000, 0, 0, 1000000, 0, 0, 1000000000};
312  CHECK(ts_lv->getType()->isIntegerTy(64));
313  bool const is_nullable = !ti.get_notnull();
314  static_assert(dtSECOND + 1 == dtMILLISECOND, "Please keep these consecutive.");
315  static_assert(dtMILLISECOND + 1 == dtMICROSECOND, "Please keep these consecutive.");
316  static_assert(dtMICROSECOND + 1 == dtNANOSECOND, "Please keep these consecutive.");
317  if (dtSECOND <= field && field <= dtNANOSECOND) {
318  unsigned const start_dim = ti.get_dimension(); // 0, 3, 6, 9
319  unsigned const trunc_dim = (field - dtSECOND) * 3; // 0, 3, 6, 9
320  if (start_dim <= trunc_dim) {
321  return ts_lv; // Truncating to an equal or higher precision has no effect.
322  }
323  int64_t const dscale = pow10[start_dim - trunc_dim]; // 1e3, 1e6, 1e9
324  if (is_nullable) {
325  ts_lv = cgen_state_->emitCall(
326  "floor_div_nullable_lhs",
327  {ts_lv, cgen_state_->llInt(dscale), cgen_state_->inlineIntNull(ti)});
328  return cgen_state_->emitCall(
329  "mul_int64_t_nullable_lhs",
330  {ts_lv, cgen_state_->llInt(dscale), cgen_state_->inlineIntNull(ti)});
331  } else {
332  ts_lv = cgen_state_->ir_builder_.CreateSDiv(ts_lv, cgen_state_->llInt(dscale));
333  return cgen_state_->ir_builder_.CreateMul(ts_lv, cgen_state_->llInt(dscale));
334  }
335  }
336  int64_t const scale = pow10[ti.get_dimension()];
337  ts_lv = is_nullable
339  "floor_div_nullable_lhs",
340  {ts_lv, cgen_state_->llInt(scale), cgen_state_->inlineIntNull(ti)})
341  : cgen_state_->ir_builder_.CreateSDiv(ts_lv, cgen_state_->llInt(scale));
342 
343  std::unique_ptr<CodeGenerator::NullCheckCodegen> nullcheck_codegen;
344  if (is_nullable) {
345  nullcheck_codegen = std::make_unique<NullCheckCodegen>(
346  cgen_state_, executor(), ts_lv, ti, "date_trunc_hp_nullcheck");
347  }
348  char const* const fname = datetrunc_fname_lookup.at(field);
349  ts_lv = cgen_state_->emitExternalCall(
350  fname, get_int_type(64, cgen_state_->context_), {ts_lv});
351  if (is_nullable) {
352  ts_lv =
353  nullcheck_codegen->finalize(ll_int(NULL_BIGINT, cgen_state_->context_), ts_lv);
354  }
355 
356  return is_nullable
357  ? cgen_state_->emitCall(
358  "mul_int64_t_nullable_lhs",
359  {ts_lv, cgen_state_->llInt(scale), cgen_state_->inlineIntNull(ti)})
360  : cgen_state_->ir_builder_.CreateMul(ts_lv, cgen_state_->llInt(scale));
361 }
const Expr * get_from_expr() const
Definition: Analyzer.h:1432
constexpr int64_t get_extract_timestamp_precision_scale(const ExtractField field)
Definition: DateTimeUtils.h:81
CgenState * cgen_state_
#define NULL_BIGINT
ExtractField get_field() const
Definition: Analyzer.h:1431
llvm::ConstantInt * ll_int(const T v, llvm::LLVMContext &context)
llvm::IRBuilder ir_builder_
Definition: CgenState.h:384
const char * get_extract_function_name(ExtractField field)
Definition: DateTimeIR.cpp:26
#define UNREACHABLE()
Definition: Logger.h:338
llvm::Value * codegenExtractHighPrecisionTimestamps(llvm::Value *, const SQLTypeInfo &, const ExtractField &)
Definition: DateTimeIR.cpp:260
DatetruncField get_field() const
Definition: Analyzer.h:1566
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)
const rapidjson::Value & field(const rapidjson::Value &obj, const char field[]) noexcept
Definition: JsonAccessors.h:33
llvm::LLVMContext & context_
Definition: CgenState.h:382
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.cpp:395
DatetruncField
Definition: DateTruncate.h:27
llvm::ConstantInt * inlineIntNull(const SQLTypeInfo &)
Definition: CgenState.cpp:65
#define AUTOMATIC_IR_METADATA(CGENSTATE)
const Expr * get_start_expr() const
Definition: Analyzer.h:1525
const SQLTypeInfo & get_type_info() const
Definition: Analyzer.h:79
llvm::Value * emitCall(const std::string &fname, const std::vector< llvm::Value * > &args)
Definition: CgenState.cpp:217
std::vector< llvm::Value * > codegen(const Analyzer::Expr *, const bool fetch_columns, const CompilationOptions &)
Definition: IRCodegen.cpp:30
Definition: sqltypes.h:80
constexpr std::array< char const *, dtINVALID > datetrunc_fname_lookup
Definition: DateTruncate.h:49
const Expr * get_from_expr() const
Definition: Analyzer.h:1567
const Expr * get_datetime_expr() const
Definition: Analyzer.h:1478
HOST DEVICE int get_dimension() const
Definition: sqltypes.h:393
ExtractField
DateaddField get_field() const
Definition: Analyzer.h:1476
constexpr bool is_subsecond_dateadd_field(const DateaddField field)
Definition: DateTimeUtils.h:99
llvm::ConstantInt * llInt(const T v) const
Definition: CgenState.h:249
const Expr * get_end_expr() const
Definition: Analyzer.h:1526
#define CHECK(condition)
Definition: Logger.h:291
constexpr int64_t get_timestamp_precision_scale(const int32_t dimen)
Definition: DateTimeUtils.h:51
bool is_high_precision_timestamp() const
Definition: sqltypes.h:1034
int64_t inline_int_null_val(const SQL_TYPE_INFO &ti)
int64_t inline_fixed_encoding_null_val(const SQL_TYPE_INFO &ti)
llvm::Value * codegenDateTruncHighPrecisionTimestamps(llvm::Value *, const SQLTypeInfo &, const DatetruncField &)
Definition: DateTimeIR.cpp:304
HOST DEVICE bool get_notnull() const
Definition: sqltypes.h:398
DatetruncField get_field() const
Definition: Analyzer.h:1524
constexpr bool is_subsecond_extract_field(const ExtractField &field)
Definition: DateTimeUtils.h:95
bool use_fixed_encoding_null_val() const
Definition: Analyzer.h:1480
const Expr * get_number_expr() const
Definition: Analyzer.h:1477
Executor * executor() const