OmniSciDB  fe05a0c208
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GeoIR.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2019 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 "Geospatial/Compression.h"
19 #include "QueryEngine/Execute.h"
20 
22  llvm::Value* pos,
23  const SQLTypeInfo& ti) {
24  CHECK(byte_stream);
25 
26  const auto key = std::make_pair(byte_stream, pos);
27  auto cache_itr = cgen_state_->array_load_cache_.find(key);
28  if (cache_itr != cgen_state_->array_load_cache_.end()) {
29  return cache_itr->second;
30  }
31  const bool is_nullable = !ti.get_notnull();
32  CHECK(ti.get_type() == kPOINT); // TODO: lift this
33 
34  auto pt_arr_buf =
35  cgen_state_->emitExternalCall("array_buff",
36  llvm::Type::getInt8PtrTy(cgen_state_->context_),
37  {key.first, key.second});
38  llvm::Value* pt_is_null{nullptr};
39  if (is_nullable) {
40  pt_is_null =
41  cgen_state_->emitExternalCall("point_coord_array_is_null",
42  llvm::Type::getInt1Ty(cgen_state_->context_),
43  {key.first, key.second});
44  }
45  ArrayLoadCodegen arr_load{pt_arr_buf, nullptr, pt_is_null};
46  cgen_state_->array_load_cache_.insert(std::make_pair(key, arr_load));
47  return arr_load;
48 }
49 
50 std::vector<llvm::Value*> CodeGenerator::codegenGeoColumnVar(
51  const Analyzer::GeoColumnVar* geo_col_var,
52  const bool fetch_columns,
53  const CompilationOptions& co) {
54  const auto& ti = geo_col_var->get_type_info();
55  if (ti.get_type() == kPOINT) {
56  // create a new operand which is just the coords and codegen it
57  const auto catalog = executor()->getCatalog();
58  CHECK(catalog);
59  const auto coords_column_id = geo_col_var->get_column_id() + 1; // + 1 for coords
60  auto coords_cd =
61  get_column_descriptor(coords_column_id, geo_col_var->get_table_id(), *catalog);
62  CHECK(coords_cd);
63 
64  const auto coords_col_var = Analyzer::ColumnVar(coords_cd->columnType,
65  geo_col_var->get_table_id(),
66  coords_column_id,
67  geo_col_var->get_rte_idx());
68  const auto coords_lv = codegen(&coords_col_var, /*fetch_columns=*/true, co);
69  CHECK_EQ(coords_lv.size(), size_t(1)); // ptr
70  return coords_lv;
71  } else {
72  UNREACHABLE() << geo_col_var->toString();
73  }
74  return {};
75 }
76 
77 std::vector<llvm::Value*> CodeGenerator::codegenGeoExpr(const Analyzer::GeoExpr* expr,
78  const CompilationOptions& co) {
79  auto geo_constant = dynamic_cast<const Analyzer::GeoConstant*>(expr);
80  if (geo_constant) {
81  return codegenGeoConstant(geo_constant, co);
82  }
83  auto geo_operator = dynamic_cast<const Analyzer::GeoOperator*>(expr);
84  if (geo_operator) {
85  return codegenGeoOperator(geo_operator, co);
86  }
87  auto geo_function = dynamic_cast<const Analyzer::GeoFunctionOperator*>(expr);
88  if (geo_function) {
89  return codegenGeoFunctionOperator(geo_function, co);
90  }
91  UNREACHABLE() << expr->toString();
92  return {};
93 }
94 
95 std::vector<llvm::Value*> CodeGenerator::codegenGeoConstant(
96  const Analyzer::GeoConstant* geo_constant,
97  const CompilationOptions& co) {
98  std::vector<llvm::Value*> ret;
99  for (size_t i = 0; i < geo_constant->physicalCols(); i++) {
100  auto physical_constant = geo_constant->makePhysicalConstant(i);
101  auto operand_lvs = codegen(physical_constant.get(), /*fetch_columns=*/true, co);
102  CHECK_EQ(operand_lvs.size(), size_t(2));
103  ret.insert(ret.end(), operand_lvs.begin(), operand_lvs.end());
104  }
105  return ret;
106 }
107 
108 std::vector<llvm::Value*> CodeGenerator::codegenGeoOperator(
109  const Analyzer::GeoOperator* geo_operator,
110  const CompilationOptions& co) {
112 
113  if (geo_operator->getName() == "ST_PointN") {
114  CHECK_EQ(geo_operator->size(), size_t(2));
115  const auto geo_arg = geo_operator->getOperand(0);
116  const auto geo_arg_expr = dynamic_cast<const Analyzer::GeoExpr*>(geo_arg);
117  CHECK(geo_arg_expr) << geo_arg->toString();
118  auto geo_lvs = codegenGeoExpr(geo_arg_expr, co); // array_buff, array_size
119  CHECK_EQ(geo_lvs.size(), size_t(2));
120  const auto index_expr = geo_operator->getOperand(1);
121  const auto index_lv = codegen(index_expr, /*fetch_columns=*/true, co);
122 
123  auto& builder = cgen_state_->ir_builder_;
124 
125  // return a nullptr if index is out of bounds
126  const auto is_null_lv = builder.CreateNot(
127  builder.CreateICmp(llvm::ICmpInst::ICMP_SLT, index_lv.front(), geo_lvs.back()));
129  executor(),
130  is_null_lv,
132  "st_pointn_range_check");
133  const auto& geo_ti = geo_arg_expr->get_type_info();
134  llvm::Value* array_buff_cast{nullptr};
135  if (geo_ti.get_compression() == kENCODING_GEOINT) {
136  array_buff_cast = builder.CreateBitCast(
137  geo_lvs.front(), llvm::Type::getInt32PtrTy(cgen_state_->context_));
138  } else {
139  array_buff_cast = builder.CreateBitCast(
140  geo_lvs.front(), llvm::Type::getDoublePtrTy(cgen_state_->context_));
141  }
142 
143  auto array_offset_lv =
144  builder.CreateGEP(array_buff_cast, index_lv, "ST_PointN_Offset");
145  auto ret_lv = nullcheck_codegen.finalize(
146  llvm::ConstantPointerNull::get(
147  geo_ti.get_compression() == kENCODING_GEOINT
148  ? llvm::Type::getInt32PtrTy(cgen_state_->context_)
149  : llvm::Type::getDoublePtrTy(cgen_state_->context_)),
150  array_offset_lv);
151  return {ret_lv, geo_lvs.back()};
152  } else if (geo_operator->getName() == "ST_EndPoint" ||
153  geo_operator->getName() == "ST_StartPoint") {
154  CHECK_EQ(geo_operator->size(), size_t(1));
155  const auto geo_arg = geo_operator->getOperand(0);
156  const auto geo_arg_expr = dynamic_cast<const Analyzer::GeoExpr*>(geo_arg);
157  CHECK(geo_arg_expr) << geo_arg->toString();
158  auto geo_lvs = codegenGeoExpr(geo_arg_expr, co); // array_buff, array_size
159  CHECK_EQ(geo_lvs.size(), size_t(2));
160 
161  auto& builder = cgen_state_->ir_builder_;
162  const auto& geo_ti = geo_arg_expr->get_type_info();
163  llvm::Value* array_buff_cast{nullptr};
164  size_t elem_size_bytes = 0; // TODO: make int32_t
165  if (geo_ti.get_compression() == kENCODING_GEOINT) {
166  array_buff_cast = builder.CreateBitCast(
167  geo_lvs.front(), llvm::Type::getInt32PtrTy(cgen_state_->context_));
168  elem_size_bytes = 4; // 4-byte ints
169  } else {
170  array_buff_cast = builder.CreateBitCast(
171  geo_lvs.front(), llvm::Type::getDoublePtrTy(cgen_state_->context_));
172  elem_size_bytes = 8; // doubles
173  }
174  CHECK_GT(elem_size_bytes, 0);
175 
176  const auto num_elements_lv =
177  builder.CreateSDiv(geo_lvs.back(), cgen_state_->llInt(int32_t(elem_size_bytes)));
178  const auto end_index_lv =
179  builder.CreateSub(num_elements_lv, cgen_state_->llInt(int32_t(2)));
180  auto array_offset_lv = builder.CreateGEP(
181  array_buff_cast, end_index_lv, geo_operator->getName() + "_Offset");
182  return {array_offset_lv, geo_lvs.back()};
183  } else if (geo_operator->getName() == "ST_NPoints") {
184  const auto operand = geo_operator->getOperand(0);
185  auto col_var = dynamic_cast<const Analyzer::ColumnVar*>(operand);
186  CHECK(col_var);
187 
188  const auto& geo_ti = col_var->get_type_info();
189  CHECK(geo_ti.is_geometry());
190 
191  // create a new operand which is just the coords and codegen it
192  const auto catalog = executor()->getCatalog();
193  CHECK(catalog);
194  const auto coords_column_id = col_var->get_column_id() + 1; // + 1 for coords
195  auto coords_cd =
196  get_column_descriptor(coords_column_id, col_var->get_table_id(), *catalog);
197  CHECK(coords_cd);
198 
199  const auto coords_col_var = Analyzer::ColumnVar(coords_cd->columnType,
200  col_var->get_table_id(),
201  coords_column_id,
202  col_var->get_rte_idx());
203  const auto coords_lv = codegen(&coords_col_var, /*fetch_columns=*/true, co);
204  CHECK_EQ(coords_lv.size(), size_t(1)); // ptr, size
205 
206  std::string fn_name("array_size");
207 
208  const auto& elem_ti = coords_cd->columnType.get_elem_type();
209  std::vector<llvm::Value*> array_size_args{
210  coords_lv.front(),
211  posArg(&coords_col_var),
212  cgen_state_->llInt(log2_bytes(elem_ti.get_logical_size()))};
213 
214  const bool is_nullable = !geo_ti.get_notnull();
215 
216  if (is_nullable) {
217  fn_name += "_nullable";
218  array_size_args.push_back(
219  cgen_state_->inlineIntNull(geo_operator->get_type_info()));
220  }
221  const auto coords_arr_sz = cgen_state_->emitExternalCall(
222  fn_name, get_int_type(32, cgen_state_->context_), array_size_args);
223 
224  std::unique_ptr<CodeGenerator::NullCheckCodegen> nullcheck_codegen;
225  if (is_nullable) {
226  nullcheck_codegen = std::make_unique<NullCheckCodegen>(cgen_state_,
227  executor(),
228  coords_arr_sz,
229  SQLTypeInfo(kINT),
230  "st_npoints_nullcheck");
231  }
232 
233  // divide the coord size by the constant compression value and return it
234  auto& builder = cgen_state_->ir_builder_;
235  llvm::Value* conversion_constant{nullptr};
236  if (geo_ti.get_compression() == kENCODING_GEOINT) {
237  conversion_constant = cgen_state_->llInt(4);
238  } else {
239  conversion_constant = cgen_state_->llInt(8);
240  }
241  CHECK(conversion_constant);
242  const auto total_num_pts = builder.CreateUDiv(coords_arr_sz, conversion_constant);
243  auto ret = builder.CreateUDiv(total_num_pts, cgen_state_->llInt(2));
244  if (is_nullable) {
245  ret = nullcheck_codegen->finalize(
246  cgen_state_->inlineIntNull(geo_operator->get_type_info()), ret);
247  }
248  return {ret};
249  } else if (geo_operator->getName() == "ST_NRings") {
250  const auto operand = geo_operator->getOperand(0);
251  auto col_var = dynamic_cast<const Analyzer::ColumnVar*>(operand);
252  CHECK(col_var);
253 
254  const auto& geo_ti = col_var->get_type_info();
255  CHECK(geo_ti.is_geometry());
256 
257  // create a new operand which is just the ring sizes and codegen it
258  const auto catalog = executor()->getCatalog();
259  CHECK(catalog);
260  const auto ring_sizes_column_id = col_var->get_column_id() + 2; // + 2 for ring sizes
261  auto ring_sizes_cd =
262  get_column_descriptor(ring_sizes_column_id, col_var->get_table_id(), *catalog);
263  CHECK(ring_sizes_cd);
264 
265  const auto ring_sizes_col_var = Analyzer::ColumnVar(ring_sizes_cd->columnType,
266  col_var->get_table_id(),
267  ring_sizes_column_id,
268  col_var->get_rte_idx());
269  const auto ring_sizes_lv = codegen(&ring_sizes_col_var, /*fetch_columns=*/true, co);
270  CHECK_EQ(ring_sizes_lv.size(), size_t(1)); // ptr, size
271 
272  std::string fn_name("array_size");
273 
274  const auto& elem_ti = ring_sizes_cd->columnType.get_elem_type();
275  std::vector<llvm::Value*> array_size_args{
276  ring_sizes_lv.front(),
277  posArg(&ring_sizes_col_var),
278  cgen_state_->llInt(log2_bytes(elem_ti.get_logical_size()))};
279 
280  const bool is_nullable = !geo_ti.get_notnull();
281 
282  if (is_nullable) {
283  fn_name += "_nullable";
284  array_size_args.push_back(
285  cgen_state_->inlineIntNull(geo_operator->get_type_info()));
286  }
287  const auto total_num_rings_lv = cgen_state_->emitExternalCall(
288  fn_name, get_int_type(32, cgen_state_->context_), array_size_args);
289 
290  std::unique_ptr<CodeGenerator::NullCheckCodegen> nullcheck_codegen;
291  if (is_nullable) {
292  nullcheck_codegen = std::make_unique<NullCheckCodegen>(cgen_state_,
293  executor(),
294  total_num_rings_lv,
295  SQLTypeInfo(kINT),
296  "st_npoints_nullcheck");
297  }
298 
299  auto ret = total_num_rings_lv;
300  if (is_nullable) {
301  ret = nullcheck_codegen->finalize(
302  cgen_state_->inlineIntNull(geo_operator->get_type_info()), ret);
303  }
304  return {ret};
305  } else if (geo_operator->getName() == "ST_X" || geo_operator->getName() == "ST_Y") {
306  const auto key = geo_operator->toString();
307  auto geo_target_cache_it = cgen_state_->geo_target_cache_.find(key);
308  if (geo_target_cache_it != cgen_state_->geo_target_cache_.end()) {
309  return {geo_target_cache_it->second};
310  }
311 
312  auto& builder = cgen_state_->ir_builder_;
313 
314  const auto operand = geo_operator->getOperand(0);
315  const auto& geo_ti = operand->get_type_info();
316  const auto operand_lvs = codegen(operand, /*fetch_columns=*/true, co);
317  llvm::Value* array_buff_ptr{nullptr};
318  llvm::Value* is_null{nullptr};
319  if (operand_lvs.size() == 1) {
320  // col byte stream, get the array buffer ptr and is null attributes and cache
321  auto arr_load_lvs =
322  codegenGeoArrayLoadAndNullcheck(operand_lvs.front(), posArg(operand), geo_ti);
323  array_buff_ptr = arr_load_lvs.buffer;
324  is_null = arr_load_lvs.is_null;
325  } else {
326  // ptr and size
327  CHECK_EQ(operand_lvs.size(), size_t(2));
328  if (dynamic_cast<const Analyzer::GeoOperator*>(operand)) {
329  // null check will be if the ptr is a nullptr
330  is_null = builder.CreateICmp(
331  llvm::CmpInst::ICMP_EQ,
332  operand_lvs.front(),
333  llvm::ConstantPointerNull::get(
334  geo_ti.get_compression() == kENCODING_GEOINT
335  ? llvm::Type::getInt32PtrTy(cgen_state_->context_)
336  : llvm::Type::getDoublePtrTy(cgen_state_->context_)));
337  }
338 
339  // TODO: nulls from other types not yet supported
340  array_buff_ptr = operand_lvs.front();
341  }
342  CHECK(array_buff_ptr) << geo_operator->toString();
343 
344  const bool is_nullable = !geo_ti.get_notnull();
345  std::unique_ptr<CodeGenerator::NullCheckCodegen> nullcheck_codegen;
346  if (is_nullable) {
347  CHECK(is_null);
348  nullcheck_codegen =
349  std::make_unique<NullCheckCodegen>(cgen_state_,
350  executor(),
351  is_null,
353  geo_operator->getName() + "_nullcheck");
354  }
355 
356  const bool is_x = geo_operator->getName() == "ST_X";
357  const std::string expr_name = is_x ? "x" : "y";
358 
359  llvm::Value* coord_lv;
360  if (geo_ti.get_compression() == kENCODING_GEOINT) {
361  auto compressed_arr_ptr = builder.CreateBitCast(
362  array_buff_ptr, llvm::Type::getInt32PtrTy(cgen_state_->context_));
363  auto coord_index = is_x ? cgen_state_->llInt(0) : cgen_state_->llInt(1);
364  auto coord_lv_ptr =
365  builder.CreateGEP(compressed_arr_ptr, coord_index, expr_name + "_coord_ptr");
366  auto compressed_coord_lv =
367  builder.CreateLoad(coord_lv_ptr, expr_name + "_coord_compressed");
368 
369  coord_lv =
370  cgen_state_->emitExternalCall("decompress_" + expr_name + "_coord_geoint",
371  llvm::Type::getDoubleTy(cgen_state_->context_),
372  {compressed_coord_lv});
373  } else {
374  auto coord_arr_ptr = builder.CreateBitCast(
375  array_buff_ptr, llvm::Type::getDoublePtrTy(cgen_state_->context_));
376  auto coord_index = is_x ? cgen_state_->llInt(0) : cgen_state_->llInt(1);
377  auto coord_lv_ptr =
378  builder.CreateGEP(coord_arr_ptr, coord_index, expr_name + "_coord_ptr");
379  coord_lv = builder.CreateLoad(coord_lv_ptr, expr_name + "_coord");
380  }
381 
382  // TODO: do this with transformation nodes explicitly
383  if (geo_ti.get_input_srid() != geo_ti.get_output_srid()) {
384  if (geo_ti.get_input_srid() == 4326) {
385  if (geo_ti.get_output_srid() == 900913) {
386  // convert WGS 84 -> Web mercator
387  coord_lv = cgen_state_->emitExternalCall(
388  "conv_4326_900913_" + expr_name,
389  llvm::Type::getDoubleTy(cgen_state_->context_),
390  {coord_lv});
391  coord_lv->setName(expr_name + "_coord_transformed");
392  } else {
393  throw std::runtime_error("Unsupported geo transformation: " +
394  std::to_string(geo_ti.get_input_srid()) + " to " +
395  std::to_string(geo_ti.get_output_srid()));
396  }
397  } else {
398  throw std::runtime_error(
399  "Unsupported geo transformation: " + std::to_string(geo_ti.get_input_srid()) +
400  " to " + std::to_string(geo_ti.get_output_srid()));
401  }
402  }
403 
404  auto ret = coord_lv;
405  if (is_nullable) {
406  ret = nullcheck_codegen->finalize(cgen_state_->inlineFpNull(SQLTypeInfo(kDOUBLE)),
407  ret);
408  }
409  CHECK(cgen_state_->geo_target_cache_.insert(std::make_pair(key, ret)).second);
410  return {ret};
411  }
412  UNREACHABLE() << geo_operator->toString();
413  return {};
414 }
415 
416 namespace {
417 
418 // TODO: de-dupe
419 std::string suffix(SQLTypes type) {
420  if (type == kPOINT) {
421  return std::string("_Point");
422  }
423  if (type == kLINESTRING) {
424  return std::string("_LineString");
425  }
426  if (type == kPOLYGON) {
427  return std::string("_Polygon");
428  }
429  if (type == kMULTIPOLYGON) {
430  return std::string("_MultiPolygon");
431  }
432  throw std::runtime_error("Unsupported argument type");
433 }
434 
435 } // namespace
436 
438  const Analyzer::GeoFunctionOperator* geo_func_oper,
439  const CompilationOptions& co) {
441 
442  if (geo_func_oper->getName() == "ST_Perimeter" ||
443  geo_func_oper->getName() == "ST_Area") {
444  const auto operand = geo_func_oper->getArg(0);
445  const auto& arg_ti = operand->get_type_info();
446  const uint32_t coords_elem_sz_bytes =
447  arg_ti.get_compression() == kENCODING_GEOINT ? 1 : 8;
448 
449  const bool is_nullable = !arg_ti.get_notnull();
450  std::string size_fn_name = "array_size";
451  if (is_nullable) {
452  size_fn_name += "_nullable";
453  }
454 
455  auto operand_lvs = codegen(operand, /*fetch_columns=*/true, co);
456  if (dynamic_cast<const Analyzer::ColumnVar*>(operand)) {
457  CHECK_EQ(operand_lvs.size(),
458  size_t(arg_ti.get_physical_coord_cols())); // chunk iter ptr
459  // this will give us back the byte stream -- add codegen for the array loads
460  std::vector<llvm::Value*> array_operand_lvs;
461  for (size_t i = 0; i < operand_lvs.size(); i++) {
462  auto lv = operand_lvs[i];
463  array_operand_lvs.push_back(
464  cgen_state_->emitExternalCall("array_buff",
465  llvm::Type::getInt8PtrTy(cgen_state_->context_),
466  {lv, posArg(operand)}));
467  const auto ptr_type = llvm::dyn_cast_or_null<llvm::PointerType>(lv->getType());
468  CHECK(ptr_type);
469  const auto elem_type = ptr_type->getElementType();
470  CHECK(elem_type);
471  std::vector<llvm::Value*> array_sz_args{
472  lv,
473  posArg(operand),
474  cgen_state_->llInt(log2_bytes(i == 0 ? coords_elem_sz_bytes : 4))};
475  if (is_nullable) {
476  array_sz_args.push_back(
477  cgen_state_->llInt(static_cast<int32_t>(inline_int_null_value<int32_t>())));
478  }
479  array_operand_lvs.push_back(cgen_state_->emitExternalCall(
480  size_fn_name, get_int_type(32, cgen_state_->context_), array_sz_args));
481  }
482  operand_lvs = array_operand_lvs;
483  }
484  CHECK_EQ(operand_lvs.size(),
485  size_t(2 * arg_ti.get_physical_coord_cols())); // array ptr and size
486 
487  const bool is_geodesic =
488  arg_ti.get_subtype() == kGEOGRAPHY && arg_ti.get_output_srid() == 4326;
489  std::string func_name = geo_func_oper->getName();
490 
491  std::unique_ptr<CodeGenerator::NullCheckCodegen> nullcheck_codegen;
492  if (is_nullable) {
493  auto null_check_operand_lv = operand_lvs[1];
494  if (null_check_operand_lv->getType() !=
495  llvm::Type::getInt32Ty(cgen_state_->context_)) {
496  CHECK(null_check_operand_lv->getType() ==
497  llvm::Type::getInt64Ty(cgen_state_->context_));
498  // Geos functions come out 64-bit, cast down to 32 for now
499  auto& builder = cgen_state_->ir_builder_;
500  null_check_operand_lv = builder.CreateTrunc(
501  null_check_operand_lv, llvm::Type::getInt32Ty(cgen_state_->context_));
502  }
503  nullcheck_codegen =
504  std::make_unique<NullCheckCodegen>(cgen_state_,
505  executor(),
506  null_check_operand_lv, // coords size
507  SQLTypeInfo(kINT),
508  func_name + "_nullcheck");
509  }
510 
511  CHECK(arg_ti.get_type() == kPOLYGON || arg_ti.get_type() == kMULTIPOLYGON);
512  func_name += suffix(arg_ti.get_type());
513  if (is_geodesic && geo_func_oper->getName() == "ST_Perimeter") {
514  func_name += "_Geodesic";
515  }
516  // push back ic, isr, osr for now
517  operand_lvs.push_back(
519  operand_lvs.push_back(cgen_state_->llInt(arg_ti.get_input_srid())); // in srid
520  operand_lvs.push_back(cgen_state_->llInt(arg_ti.get_output_srid())); // out srid
521 
522  const auto& ret_ti = geo_func_oper->get_type_info();
523  CHECK(ret_ti.get_type() == kDOUBLE || ret_ti.get_type() == kFLOAT);
524  auto ret = cgen_state_->emitExternalCall(
525  func_name,
526  ret_ti.get_type() == kDOUBLE ? llvm::Type::getDoubleTy(cgen_state_->context_)
527  : llvm::Type::getFloatTy(cgen_state_->context_),
528  operand_lvs);
529  if (is_nullable) {
530  ret = nullcheck_codegen->finalize(
531  cgen_state_->inlineFpNull(geo_func_oper->get_type_info()), ret);
532  }
533  return {ret};
534  }
535  UNREACHABLE() << geo_func_oper->toString();
536  return {};
537 }
538 
539 std::vector<llvm::Value*> CodeGenerator::codegenGeoUOper(
540  const Analyzer::GeoUOper* geo_expr,
541  const CompilationOptions& co) {
544  if (geo_expr->getOp() != Geospatial::GeoBase::GeoOp::kPROJECTION) {
545  throw QueryMustRunOnCpu();
546  }
547  }
548 
549  auto argument_list = codegenGeoArgs(geo_expr->getArgs0(), co);
550 
551  if (geo_expr->getOp() == Geospatial::GeoBase::GeoOp::kPROJECTION) {
552  return argument_list;
553  }
554 
555 #ifndef ENABLE_GEOS
556  throw std::runtime_error("Geo operation requires GEOS support.");
557 #endif
558 
559  // Basic set of arguments is currently common to all Geos_* func invocations:
560  // op kind, type of the first geo arg0, geo arg0 components
561  std::string func = "Geos_Wkb"s;
562  if (geo_expr->getTypeInfo0().transforms()) {
563  throw std::runtime_error(
564  "GEOS runtime does not support transforms on geometry inputs.");
565  }
566  // Catch transforms applied to geometry construction only
567  if (geo_expr->get_type_info().transforms()) {
568  throw std::runtime_error(
569  "GEOS runtime does not support transforms on geometry outputs.");
570  }
571  // Prepend arg0 geo SQLType
572  argument_list.insert(
573  argument_list.begin(),
574  cgen_state_->llInt(static_cast<int>(geo_expr->getTypeInfo0().get_type())));
575  // Prepend geo expr op
576  argument_list.insert(argument_list.begin(),
577  cgen_state_->llInt(static_cast<int>(geo_expr->getOp())));
578  for (auto i = 3; i > geo_expr->getTypeInfo0().get_physical_coord_cols(); i--) {
579  argument_list.insert(argument_list.end(), cgen_state_->llInt(int64_t(0)));
580  argument_list.insert(argument_list.end(),
581  llvm::ConstantPointerNull::get(
582  llvm::Type::getInt32PtrTy(cgen_state_->context_, 0)));
583  }
584  // Append geo expr compression
585  argument_list.insert(
586  argument_list.end(),
587  cgen_state_->llInt(static_cast<int>(
589  // Append geo expr SRID
590  argument_list.insert(
591  argument_list.end(),
592  cgen_state_->llInt(static_cast<int>(geo_expr->getTypeInfo0().get_output_srid())));
593 
594  // Deal with unary geo predicates
595  if (geo_expr->getOp() == Geospatial::GeoBase::GeoOp::kISEMPTY ||
597  return codegenGeosPredicateCall(func, argument_list, co);
598  }
599 
600  throw std::runtime_error("Unsupported unary geo operation.");
601  return {};
602 }
603 
604 std::vector<llvm::Value*> CodeGenerator::codegenGeoBinOper(
605  Analyzer::GeoBinOper const* geo_expr,
606  CompilationOptions const& co) {
609  throw QueryMustRunOnCpu();
610  }
611 #ifndef ENABLE_GEOS
612  throw std::runtime_error("Geo operation requires GEOS support.");
613 #endif
614 
615  auto argument_list = codegenGeoArgs(geo_expr->getArgs0(), co);
616 
617  // Basic set of arguments is currently common to all Geos_* func invocations:
618  // op kind, type of the first geo arg0, geo arg0 components
619  std::string func = "Geos_Wkb"s;
620  if (geo_expr->getTypeInfo0().transforms() || geo_expr->getTypeInfo1().transforms()) {
621  throw std::runtime_error(
622  "GEOS runtime does not support transforms on geometry inputs.");
623  }
624  if (geo_expr->get_type_info().transforms()) {
625  throw std::runtime_error(
626  "GEOS runtime does not support transforms on geometry outputs.");
627  }
628  // Prepend arg0 geo SQLType
629  argument_list.insert(
630  argument_list.begin(),
631  cgen_state_->llInt(static_cast<int>(geo_expr->getTypeInfo0().get_type())));
632  // Prepend geo expr op
633  argument_list.insert(argument_list.begin(),
634  cgen_state_->llInt(static_cast<int>(geo_expr->getOp())));
635  for (auto i = 3; i > geo_expr->getTypeInfo0().get_physical_coord_cols(); i--) {
636  argument_list.insert(argument_list.end(), cgen_state_->llInt(int64_t(0)));
637  argument_list.insert(argument_list.end(),
638  llvm::ConstantPointerNull::get(
639  llvm::Type::getInt32PtrTy(cgen_state_->context_, 0)));
640  }
641  // Append geo expr compression
642  argument_list.insert(
643  argument_list.end(),
644  cgen_state_->llInt(static_cast<int>(
646  // Append geo expr SRID
647  argument_list.insert(
648  argument_list.end(),
649  cgen_state_->llInt(static_cast<int>(geo_expr->getTypeInfo0().get_output_srid())));
650 
651  auto arg1_list = codegenGeoArgs(geo_expr->getArgs1(), co);
652 
653  if (geo_expr->getOp() == Geospatial::GeoBase::GeoOp::kDIFFERENCE ||
655  geo_expr->getOp() == Geospatial::GeoBase::GeoOp::kUNION) {
656  func += "_Wkb"s;
657  // Prepend arg1 geo SQLType
658  arg1_list.insert(
659  arg1_list.begin(),
660  cgen_state_->llInt(static_cast<int>(geo_expr->getTypeInfo1().get_type())));
661  for (auto i = 3; i > geo_expr->getTypeInfo1().get_physical_coord_cols(); i--) {
662  arg1_list.insert(arg1_list.end(), cgen_state_->llInt(int64_t(0)));
663  arg1_list.insert(arg1_list.end(),
664  llvm::ConstantPointerNull::get(
665  llvm::Type::getInt32PtrTy(cgen_state_->context_, 0)));
666  }
667  // Append geo expr compression
668  arg1_list.insert(arg1_list.end(),
669  cgen_state_->llInt(static_cast<int>(
671  // Append geo expr compression
672  arg1_list.insert(arg1_list.end(),
674  } else if (geo_expr->getOp() == Geospatial::GeoBase::GeoOp::kBUFFER) {
675  // Extra argument in this case is double
676  func += "_double"s;
677  } else {
678  throw std::runtime_error("Unsupported binary geo operation.");
679  }
680 
681  // Append arg1 to the list
682  argument_list.insert(argument_list.end(), arg1_list.begin(), arg1_list.end());
683 
684  return codegenGeosConstructorCall(func, argument_list, co);
685 }
686 
687 std::vector<llvm::Value*> CodeGenerator::codegenGeoArgs(
688  const std::vector<std::shared_ptr<Analyzer::Expr>>& geo_args,
689  const CompilationOptions& co) {
691  std::vector<llvm::Value*> argument_list;
692  bool coord_col = true;
693  for (const auto& geo_arg : geo_args) {
694  const auto arg = geo_arg.get();
695  const auto& arg_ti = arg->get_type_info();
696  const auto elem_ti = arg_ti.get_elem_type();
697  const auto arg_lvs = codegen(arg, true, co);
698  if (arg_ti.is_number()) {
699  argument_list.emplace_back(arg_lvs.front());
700  continue;
701  }
702  if (arg_ti.is_geometry()) {
703  argument_list.insert(argument_list.end(), arg_lvs.begin(), arg_lvs.end());
704  continue;
705  }
706  CHECK(arg_ti.is_array());
707  if (arg_lvs.size() > 1) {
708  CHECK_EQ(size_t(2), arg_lvs.size());
709  auto ptr_lv = arg_lvs.front();
710  if (coord_col) {
711  coord_col = false;
712  } else {
713  ptr_lv = cgen_state_->ir_builder_.CreatePointerCast(
714  ptr_lv, llvm::Type::getInt32PtrTy(cgen_state_->context_));
715  }
716  argument_list.emplace_back(ptr_lv);
717  auto cast_len_lv = cgen_state_->ir_builder_.CreateZExt(
718  arg_lvs.back(), get_int_type(64, cgen_state_->context_));
719  argument_list.emplace_back(cast_len_lv);
720  } else {
721  CHECK_EQ(size_t(1), arg_lvs.size());
722  if (arg_ti.get_size() > 0) {
723  // Set up the pointer lv for a dynamically generated point
724  auto ptr_lv = arg_lvs.front();
725  auto col_var = dynamic_cast<const Analyzer::ColumnVar*>(arg);
726  // Override for point coord column access
727  if (col_var) {
728  ptr_lv = cgen_state_->emitExternalCall(
729  "fast_fixlen_array_buff",
730  llvm::Type::getInt8PtrTy(cgen_state_->context_),
731  {arg_lvs.front(), posArg(arg)});
732  }
733  if (coord_col) {
734  coord_col = false;
735  } else {
736  ptr_lv = cgen_state_->ir_builder_.CreatePointerCast(
737  ptr_lv, llvm::Type::getInt32PtrTy(cgen_state_->context_));
738  }
739  argument_list.emplace_back(ptr_lv);
740  argument_list.emplace_back(cgen_state_->llInt<int64_t>(arg_ti.get_size()));
741  } else {
742  auto ptr_lv =
743  cgen_state_->emitExternalCall("array_buff",
744  llvm::Type::getInt8PtrTy(cgen_state_->context_),
745  {arg_lvs.front(), posArg(arg)});
746  if (coord_col) {
747  coord_col = false;
748  } else {
749  ptr_lv = cgen_state_->ir_builder_.CreatePointerCast(
750  ptr_lv, llvm::Type::getInt32PtrTy(cgen_state_->context_));
751  }
752  argument_list.emplace_back(ptr_lv);
753  const auto len_lv = cgen_state_->emitExternalCall(
754  "array_size",
756  {arg_lvs.front(),
757  posArg(arg),
758  cgen_state_->llInt(log2_bytes(elem_ti.get_logical_size()))});
759  auto cast_len_lv = cgen_state_->ir_builder_.CreateZExt(
760  len_lv, get_int_type(64, cgen_state_->context_));
761  argument_list.emplace_back(cast_len_lv);
762  }
763  }
764  }
765  return argument_list;
766 }
767 
768 std::vector<llvm::Value*> CodeGenerator::codegenGeosPredicateCall(
769  const std::string& func,
770  std::vector<llvm::Value*> argument_list,
771  const CompilationOptions& co) {
773  auto i8_type = get_int_type(8, cgen_state_->context_);
774  auto result = cgen_state_->ir_builder_.CreateAlloca(i8_type, nullptr, "result");
775  argument_list.emplace_back(result);
776 
777  // Generate call to GEOS wrapper
778  cgen_state_->needs_geos_ = true;
779  auto status_lv = cgen_state_->emitExternalCall(
780  func, llvm::Type::getInt1Ty(cgen_state_->context_), argument_list);
781  // Need to check the status and throw an error if this call has failed.
782  llvm::BasicBlock* geos_pred_ok_bb{nullptr};
783  llvm::BasicBlock* geos_pred_fail_bb{nullptr};
784  geos_pred_ok_bb = llvm::BasicBlock::Create(
785  cgen_state_->context_, "geos_pred_ok_bb", cgen_state_->current_func_);
786  geos_pred_fail_bb = llvm::BasicBlock::Create(
787  cgen_state_->context_, "geos_pred_fail_bb", cgen_state_->current_func_);
788  if (!status_lv) {
789  status_lv = cgen_state_->llBool(false);
790  }
791  cgen_state_->ir_builder_.CreateCondBr(status_lv, geos_pred_ok_bb, geos_pred_fail_bb);
792  cgen_state_->ir_builder_.SetInsertPoint(geos_pred_fail_bb);
795  cgen_state_->ir_builder_.SetInsertPoint(geos_pred_ok_bb);
796  auto res = cgen_state_->ir_builder_.CreateLoad(result);
797  return {res};
798 }
799 
801  const std::string& func,
802  std::vector<llvm::Value*> argument_list,
803  const CompilationOptions& co) {
805  // Create output buffer pointers, append pointers to output args to
806  auto i8_type = get_int_type(8, cgen_state_->context_);
807  auto i32_type = get_int_type(32, cgen_state_->context_);
808  auto i64_type = get_int_type(64, cgen_state_->context_);
809  auto pi8_type = llvm::PointerType::get(i8_type, 0);
810  auto pi32_type = llvm::PointerType::get(i32_type, 0);
811 
812  auto result_type =
813  cgen_state_->ir_builder_.CreateAlloca(i32_type, nullptr, "result_type");
814  auto result_coords =
815  cgen_state_->ir_builder_.CreateAlloca(pi8_type, nullptr, "result_coords");
816  auto result_coords_size =
817  cgen_state_->ir_builder_.CreateAlloca(i64_type, nullptr, "result_coords_size");
818  auto result_ring_sizes =
819  cgen_state_->ir_builder_.CreateAlloca(pi32_type, nullptr, "result_ring_sizes");
820  auto result_ring_sizes_size =
821  cgen_state_->ir_builder_.CreateAlloca(i64_type, nullptr, "result_ring_sizes_size");
822  auto result_poly_rings =
823  cgen_state_->ir_builder_.CreateAlloca(pi32_type, nullptr, "result_poly_rings");
824  auto result_poly_rings_size =
825  cgen_state_->ir_builder_.CreateAlloca(i64_type, nullptr, "result_poly_rings_size");
826 
827  argument_list.emplace_back(result_type);
828  argument_list.emplace_back(result_coords);
829  argument_list.emplace_back(result_coords_size);
830  argument_list.emplace_back(result_ring_sizes);
831  argument_list.emplace_back(result_ring_sizes_size);
832  argument_list.emplace_back(result_poly_rings);
833  argument_list.emplace_back(result_poly_rings_size);
834 
835  // Generate call to GEOS wrapper
836  cgen_state_->needs_geos_ = true;
837  auto status_lv = cgen_state_->emitExternalCall(
838  func, llvm::Type::getInt1Ty(cgen_state_->context_), argument_list);
839  // Need to check the status and throw an error if this call has failed.
840  llvm::BasicBlock* geos_ok_bb{nullptr};
841  llvm::BasicBlock* geos_fail_bb{nullptr};
842  geos_ok_bb = llvm::BasicBlock::Create(
843  cgen_state_->context_, "geos_ok_bb", cgen_state_->current_func_);
844  geos_fail_bb = llvm::BasicBlock::Create(
845  cgen_state_->context_, "geos_fail_bb", cgen_state_->current_func_);
846  if (!status_lv) {
847  status_lv = cgen_state_->llBool(false);
848  }
849  cgen_state_->ir_builder_.CreateCondBr(status_lv, geos_ok_bb, geos_fail_bb);
850  cgen_state_->ir_builder_.SetInsertPoint(geos_fail_bb);
853  cgen_state_->ir_builder_.SetInsertPoint(geos_ok_bb);
854 
855  // TODO: Currently forcing the output to MULTIPOLYGON, but need to handle
856  // other possible geometries that geos may return, e.g. a POINT, a LINESTRING
857  // Need to handle empty result, e.g. empty intersection.
858  // The type of result is returned in `result_type`
859 
860  // Load return values
861  auto buf1 = cgen_state_->ir_builder_.CreateLoad(result_coords);
862  auto buf1s = cgen_state_->ir_builder_.CreateLoad(result_coords_size);
863  auto buf2 = cgen_state_->ir_builder_.CreateLoad(result_ring_sizes);
864  auto buf2s = cgen_state_->ir_builder_.CreateLoad(result_ring_sizes_size);
865  auto buf3 = cgen_state_->ir_builder_.CreateLoad(result_poly_rings);
866  auto buf3s = cgen_state_->ir_builder_.CreateLoad(result_poly_rings_size);
867 
868  // generate register_buffer_with_executor_rsm() calls to register all output buffers
870  "register_buffer_with_executor_rsm",
871  llvm::Type::getVoidTy(cgen_state_->context_),
872  {cgen_state_->llInt(reinterpret_cast<int64_t>(executor())),
873  cgen_state_->ir_builder_.CreatePointerCast(buf1, pi8_type)});
874  cgen_state_->emitExternalCall(
875  "register_buffer_with_executor_rsm",
876  llvm::Type::getVoidTy(cgen_state_->context_),
877  {cgen_state_->llInt(reinterpret_cast<int64_t>(executor())),
878  cgen_state_->ir_builder_.CreatePointerCast(buf2, pi8_type)});
879  cgen_state_->emitExternalCall(
880  "register_buffer_with_executor_rsm",
881  llvm::Type::getVoidTy(cgen_state_->context_),
882  {cgen_state_->llInt(reinterpret_cast<int64_t>(executor())),
883  cgen_state_->ir_builder_.CreatePointerCast(buf3, pi8_type)});
884 
885  return {cgen_state_->ir_builder_.CreatePointerCast(buf1, pi8_type),
886  buf1s,
887  cgen_state_->ir_builder_.CreatePointerCast(buf2, pi32_type),
888  buf2s,
889  cgen_state_->ir_builder_.CreatePointerCast(buf3, pi32_type),
890  buf3s};
891 }
int get_table_id() const
Definition: Analyzer.h:194
#define CHECK_EQ(x, y)
Definition: Logger.h:211
const SQLTypeInfo getTypeInfo0() const
Definition: Analyzer.h:1556
const std::vector< std::shared_ptr< Analyzer::Expr > > & getArgs0() const
Definition: Analyzer.h:1558
SQLTypes
Definition: sqltypes.h:37
const SQLTypeInfo getTypeInfo0() const
Definition: Analyzer.h:1527
CgenState * cgen_state_
std::map< std::pair< llvm::Value *, llvm::Value * >, ArrayLoadCodegen > array_load_cache_
Definition: CgenState.h:351
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:228
llvm::IRBuilder ir_builder_
Definition: CgenState.h:335
int32_t get_compression_scheme(const SQLTypeInfo &ti)
Definition: Compression.cpp:23
llvm::Value * posArg(const Analyzer::Expr *) const
Definition: ColumnIR.cpp:512
#define UNREACHABLE()
Definition: Logger.h:247
const SQLTypeInfo getTypeInfo1() const
Definition: Analyzer.h:1557
llvm::ConstantInt * llBool(const bool v) const
Definition: CgenState.h:320
bool needs_geos_
Definition: CgenState.h:354
std::string toString() const final
Definition: Analyzer.cpp:3417
static const int32_t ERR_GEOS
Definition: Execute.h:1126
llvm::Value * buffer
Definition: CgenState.h:32
HOST DEVICE SQLTypes get_type() const
Definition: sqltypes.h:314
llvm::Type * get_int_type(const int width, llvm::LLVMContext &context)
#define CHECK_GT(x, y)
Definition: Logger.h:215
std::vector< llvm::Value * > codegenGeoBinOper(const Analyzer::GeoBinOper *, const CompilationOptions &)
Definition: GeoIR.cpp:604
std::unordered_map< std::string, llvm::Value * > geo_target_cache_
Definition: CgenState.h:352
std::string to_string(char const *&&v)
Analyzer::Expr * getArg(const size_t index) const
Definition: Analyzer.cpp:3500
ArrayLoadCodegen codegenGeoArrayLoadAndNullcheck(llvm::Value *byte_stream, llvm::Value *pos, const SQLTypeInfo &ti)
Definition: GeoIR.cpp:21
std::string toString() const final
Definition: Analyzer.cpp:3468
std::vector< llvm::Value * > codegenGeoExpr(const Analyzer::GeoExpr *, const CompilationOptions &)
Definition: GeoIR.cpp:77
llvm::LLVMContext & context_
Definition: CgenState.h:333
std::vector< llvm::Value * > codegenGeoFunctionOperator(const Analyzer::GeoFunctionOperator *, const CompilationOptions &)
Definition: GeoIR.cpp:437
llvm::Function * current_func_
Definition: CgenState.h:327
CONSTEXPR DEVICE bool is_null(const T &value)
std::vector< llvm::Value * > codegenGeoOperator(const Analyzer::GeoOperator *, const CompilationOptions &)
Definition: GeoIR.cpp:108
std::vector< llvm::Value * > codegenGeoUOper(const Analyzer::GeoUOper *, const CompilationOptions &)
Definition: GeoIR.cpp:539
const std::vector< std::shared_ptr< Analyzer::Expr > > & getArgs0() const
Definition: Analyzer.h:1528
llvm::ConstantInt * inlineIntNull(const SQLTypeInfo &)
Definition: CgenState.cpp:28
std::string toString() const override
Definition: Analyzer.cpp:2456
const std::string & getName() const
Definition: Analyzer.h:1749
std::vector< llvm::Value * > codegenGeosConstructorCall(const std::string &, std::vector< llvm::Value * >, const CompilationOptions &)
Definition: GeoIR.cpp:800
bool needs_error_check_
Definition: CgenState.h:353
#define AUTOMATIC_IR_METADATA(CGENSTATE)
const SQLTypeInfo & get_type_info() const
Definition: Analyzer.h:78
ExecutorDeviceType device_type
const std::string & getName() const
Definition: Analyzer.h:1777
std::vector< llvm::Value * > codegen(const Analyzer::Expr *, const bool fetch_columns, const CompilationOptions &)
Definition: IRCodegen.cpp:28
virtual std::string toString() const =0
HOST DEVICE EncodingType get_compression() const
Definition: sqltypes.h:322
std::vector< llvm::Value * > codegenGeosPredicateCall(const std::string &, std::vector< llvm::Value * >, const CompilationOptions &)
Definition: GeoIR.cpp:768
int get_rte_idx() const
Definition: Analyzer.h:196
std::vector< llvm::Value * > codegenGeoColumnVar(const Analyzer::GeoColumnVar *, const bool fetch_columns, const CompilationOptions &co)
Definition: GeoIR.cpp:50
size_t size() const
Definition: Analyzer.cpp:3445
llvm::ConstantInt * llInt(const T v) const
Definition: CgenState.h:306
std::vector< llvm::Value * > codegenGeoArgs(const std::vector< std::shared_ptr< Analyzer::Expr >> &, const CompilationOptions &)
Definition: GeoIR.cpp:687
llvm::Value * finalize(llvm::Value *null_lv, llvm::Value *notnull_lv)
Definition: IRCodegen.cpp:1028
#define CHECK(condition)
Definition: Logger.h:203
Geospatial::GeoBase::GeoOp getOp() const
Definition: Analyzer.h:1526
Analyzer::Expr * getOperand(const size_t index) const
Definition: Analyzer.cpp:3449
std::vector< llvm::Value * > codegenGeoConstant(const Analyzer::GeoConstant *, const CompilationOptions &)
Definition: GeoIR.cpp:95
uint32_t log2_bytes(const uint32_t bytes)
Definition: Execute.h:177
Definition: sqltypes.h:44
std::shared_ptr< Analyzer::Constant > makePhysicalConstant(const size_t index) const
Definition: Analyzer.cpp:3368
int get_column_id() const
Definition: Analyzer.h:195
bool transforms() const
Definition: sqltypes.h:510
HOST DEVICE bool get_notnull() const
Definition: sqltypes.h:321
std::string suffix(SQLTypes type)
Definition: GeoIR.cpp:419
int get_physical_coord_cols() const
Definition: sqltypes.h:350
size_t physicalCols() const
Definition: Analyzer.cpp:3363
Geospatial::GeoBase::GeoOp getOp() const
Definition: Analyzer.h:1555
const std::vector< std::shared_ptr< Analyzer::Expr > > & getArgs1() const
Definition: Analyzer.h:1559
const ColumnDescriptor * get_column_descriptor(const int col_id, const int table_id, const Catalog_Namespace::Catalog &cat)
Definition: Execute.h:192
HOST DEVICE int get_output_srid() const
Definition: sqltypes.h:320
llvm::ConstantFP * inlineFpNull(const SQLTypeInfo &)
Definition: CgenState.cpp:66
Executor * executor() const