OmniSciDB  95562058bd
 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 
21 std::vector<llvm::Value*> CodeGenerator::codegenGeoUOper(
22  const Analyzer::GeoUOper* geo_expr,
23  const CompilationOptions& co) {
27  throw QueryMustRunOnCpu();
28  }
29  }
30 
31  auto argument_list = codegenGeoArgs(geo_expr->getArgs0(), co);
32 
34  return argument_list;
35  }
36 
37 #ifndef ENABLE_GEOS
38  throw std::runtime_error("Geo operation requires GEOS support.");
39 #endif
40 
41  // Basic set of arguments is currently common to all Geos_* func invocations:
42  // op kind, type of the first geo arg0, geo arg0 components
43  std::string func = "Geos_Wkb"s;
44  if (geo_expr->getTypeInfo0().get_output_srid() !=
45  geo_expr->get_type_info().get_output_srid()) {
46  throw std::runtime_error("GEOS runtime doesn't support geometry transforms.");
47  }
48  // Prepend arg0 geo SQLType
49  argument_list.insert(
50  argument_list.begin(),
51  cgen_state_->llInt(static_cast<int>(geo_expr->getTypeInfo0().get_type())));
52  // Prepend geo expr op
53  argument_list.insert(argument_list.begin(),
54  cgen_state_->llInt(static_cast<int>(geo_expr->getOp())));
55  for (auto i = 3; i > geo_expr->getTypeInfo0().get_physical_coord_cols(); i--) {
56  argument_list.insert(argument_list.end(), cgen_state_->llInt(int64_t(0)));
57  argument_list.insert(argument_list.end(),
58  llvm::ConstantPointerNull::get(
59  llvm::Type::getInt32PtrTy(cgen_state_->context_, 0)));
60  }
61  // Append geo expr compression
62  argument_list.insert(
63  argument_list.end(),
64  cgen_state_->llInt(static_cast<int>(
66  // Append geo expr SRID
67  argument_list.insert(
68  argument_list.end(),
69  cgen_state_->llInt(static_cast<int>(geo_expr->getTypeInfo0().get_output_srid())));
70 
71  // Deal with unary geo predicates
72  if (geo_expr->getOp() == Geospatial::GeoBase::GeoOp::kISEMPTY ||
74  return codegenGeosPredicateCall(func, argument_list, co);
75  }
76 
77  throw std::runtime_error("Unsupported unary geo operation.");
78  return {};
79 }
80 
81 std::vector<llvm::Value*> CodeGenerator::codegenGeoBinOper(
82  Analyzer::GeoBinOper const* geo_expr,
83  CompilationOptions const& co) {
86  throw QueryMustRunOnCpu();
87  }
88 #ifndef ENABLE_GEOS
89  throw std::runtime_error("Geo operation requires GEOS support.");
90 #endif
91 
92  auto argument_list = codegenGeoArgs(geo_expr->getArgs0(), co);
93 
94  // Basic set of arguments is currently common to all Geos_* func invocations:
95  // op kind, type of the first geo arg0, geo arg0 components
96  std::string func = "Geos_Wkb"s;
97  if (geo_expr->getTypeInfo0().get_output_srid() !=
98  geo_expr->get_type_info().get_output_srid()) {
99  throw std::runtime_error("GEOS runtime doesn't support geometry transforms.");
100  }
101  // Prepend arg0 geo SQLType
102  argument_list.insert(
103  argument_list.begin(),
104  cgen_state_->llInt(static_cast<int>(geo_expr->getTypeInfo0().get_type())));
105  // Prepend geo expr op
106  argument_list.insert(argument_list.begin(),
107  cgen_state_->llInt(static_cast<int>(geo_expr->getOp())));
108  for (auto i = 3; i > geo_expr->getTypeInfo0().get_physical_coord_cols(); i--) {
109  argument_list.insert(argument_list.end(), cgen_state_->llInt(int64_t(0)));
110  argument_list.insert(argument_list.end(),
111  llvm::ConstantPointerNull::get(
112  llvm::Type::getInt32PtrTy(cgen_state_->context_, 0)));
113  }
114  // Append geo expr compression
115  argument_list.insert(
116  argument_list.end(),
117  cgen_state_->llInt(static_cast<int>(
119  // Append geo expr SRID
120  argument_list.insert(
121  argument_list.end(),
122  cgen_state_->llInt(static_cast<int>(geo_expr->getTypeInfo0().get_output_srid())));
123 
124  auto arg1_list = codegenGeoArgs(geo_expr->getArgs1(), co);
125 
126  if (geo_expr->getOp() == Geospatial::GeoBase::GeoOp::kDIFFERENCE ||
128  geo_expr->getOp() == Geospatial::GeoBase::GeoOp::kUNION) {
129  func += "_Wkb"s;
130  if (geo_expr->getTypeInfo1().get_output_srid() !=
131  geo_expr->get_type_info().get_output_srid()) {
132  throw std::runtime_error("GEOS runtime doesn't support geometry transforms.");
133  }
134  // Prepend arg1 geo SQLType
135  arg1_list.insert(
136  arg1_list.begin(),
137  cgen_state_->llInt(static_cast<int>(geo_expr->getTypeInfo1().get_type())));
138  for (auto i = 3; i > geo_expr->getTypeInfo1().get_physical_coord_cols(); i--) {
139  arg1_list.insert(arg1_list.end(), cgen_state_->llInt(int64_t(0)));
140  arg1_list.insert(arg1_list.end(),
141  llvm::ConstantPointerNull::get(
142  llvm::Type::getInt32PtrTy(cgen_state_->context_, 0)));
143  }
144  // Append geo expr compression
145  arg1_list.insert(arg1_list.end(),
146  cgen_state_->llInt(static_cast<int>(
148  // Append geo expr compression
149  arg1_list.insert(arg1_list.end(),
151  } else if (geo_expr->getOp() == Geospatial::GeoBase::GeoOp::kBUFFER) {
152  // Extra argument in this case is double
153  func += "_double"s;
154  } else {
155  throw std::runtime_error("Unsupported binary geo operation.");
156  }
157 
158  // Append arg1 to the list
159  argument_list.insert(argument_list.end(), arg1_list.begin(), arg1_list.end());
160 
161  return codegenGeosConstructorCall(func, argument_list, co);
162 }
163 
164 std::vector<llvm::Value*> CodeGenerator::codegenGeoArgs(
165  const std::vector<std::shared_ptr<Analyzer::Expr>>& geo_args,
166  const CompilationOptions& co) {
168  std::vector<llvm::Value*> argument_list;
169  bool coord_col = true;
170  for (const auto& geo_arg : geo_args) {
171  const auto arg = geo_arg.get();
172  const auto& arg_ti = arg->get_type_info();
173  const auto elem_ti = arg_ti.get_elem_type();
174  const auto arg_lvs = codegen(arg, true, co);
175  if (arg_ti.is_number()) {
176  argument_list.emplace_back(arg_lvs.front());
177  continue;
178  }
179  if (arg_ti.is_geometry()) {
180  argument_list.insert(argument_list.end(), arg_lvs.begin(), arg_lvs.end());
181  continue;
182  }
183  CHECK(arg_ti.is_array());
184  if (arg_lvs.size() > 1) {
185  CHECK_EQ(size_t(2), arg_lvs.size());
186  auto ptr_lv = arg_lvs.front();
187  if (coord_col) {
188  coord_col = false;
189  } else {
190  ptr_lv = cgen_state_->ir_builder_.CreatePointerCast(
191  ptr_lv, llvm::Type::getInt32PtrTy(cgen_state_->context_));
192  }
193  argument_list.emplace_back(ptr_lv);
194  auto cast_len_lv = cgen_state_->ir_builder_.CreateZExt(
195  arg_lvs.back(), get_int_type(64, cgen_state_->context_));
196  argument_list.emplace_back(cast_len_lv);
197  } else {
198  CHECK_EQ(size_t(1), arg_lvs.size());
199  if (arg_ti.get_size() > 0) {
200  // Set up the pointer lv for a dynamically generated point
201  auto ptr_lv = arg_lvs.front();
202  auto col_var = dynamic_cast<const Analyzer::ColumnVar*>(arg);
203  // Override for point coord column access
204  if (col_var) {
205  ptr_lv = cgen_state_->emitExternalCall(
206  "fast_fixlen_array_buff",
207  llvm::Type::getInt8PtrTy(cgen_state_->context_),
208  {arg_lvs.front(), posArg(arg)});
209  }
210  if (coord_col) {
211  coord_col = false;
212  } else {
213  ptr_lv = cgen_state_->ir_builder_.CreatePointerCast(
214  ptr_lv, llvm::Type::getInt32PtrTy(cgen_state_->context_));
215  }
216  argument_list.emplace_back(ptr_lv);
217  argument_list.emplace_back(cgen_state_->llInt<int64_t>(arg_ti.get_size()));
218  } else {
219  auto ptr_lv =
220  cgen_state_->emitExternalCall("array_buff",
221  llvm::Type::getInt8PtrTy(cgen_state_->context_),
222  {arg_lvs.front(), posArg(arg)});
223  if (coord_col) {
224  coord_col = false;
225  } else {
226  ptr_lv = cgen_state_->ir_builder_.CreatePointerCast(
227  ptr_lv, llvm::Type::getInt32PtrTy(cgen_state_->context_));
228  }
229  argument_list.emplace_back(ptr_lv);
230  const auto len_lv = cgen_state_->emitExternalCall(
231  "array_size",
233  {arg_lvs.front(),
234  posArg(arg),
235  cgen_state_->llInt(log2_bytes(elem_ti.get_logical_size()))});
236  auto cast_len_lv = cgen_state_->ir_builder_.CreateZExt(
237  len_lv, get_int_type(64, cgen_state_->context_));
238  argument_list.emplace_back(cast_len_lv);
239  }
240  }
241  }
242  return argument_list;
243 }
244 
245 std::vector<llvm::Value*> CodeGenerator::codegenGeosPredicateCall(
246  const std::string& func,
247  std::vector<llvm::Value*> argument_list,
248  const CompilationOptions& co) {
250  auto i8_type = get_int_type(8, cgen_state_->context_);
251  auto result = cgen_state_->ir_builder_.CreateAlloca(i8_type, nullptr, "result");
252  argument_list.emplace_back(result);
253 
254  // Generate call to GEOS wrapper
255  cgen_state_->needs_geos_ = true;
256  auto status_lv = cgen_state_->emitExternalCall(
257  func, llvm::Type::getInt1Ty(cgen_state_->context_), argument_list);
258  // Need to check the status and throw an error if this call has failed.
259  llvm::BasicBlock* geos_pred_ok_bb{nullptr};
260  llvm::BasicBlock* geos_pred_fail_bb{nullptr};
261  geos_pred_ok_bb = llvm::BasicBlock::Create(
262  cgen_state_->context_, "geos_pred_ok_bb", cgen_state_->current_func_);
263  geos_pred_fail_bb = llvm::BasicBlock::Create(
264  cgen_state_->context_, "geos_pred_fail_bb", cgen_state_->current_func_);
265  if (!status_lv) {
266  status_lv = cgen_state_->llBool(false);
267  }
268  cgen_state_->ir_builder_.CreateCondBr(status_lv, geos_pred_ok_bb, geos_pred_fail_bb);
269  cgen_state_->ir_builder_.SetInsertPoint(geos_pred_fail_bb);
272  cgen_state_->ir_builder_.SetInsertPoint(geos_pred_ok_bb);
273  auto res = cgen_state_->ir_builder_.CreateLoad(result);
274  return {res};
275 }
276 
278  const std::string& func,
279  std::vector<llvm::Value*> argument_list,
280  const CompilationOptions& co) {
282  // Create output buffer pointers, append pointers to output args to
283  auto i8_type = get_int_type(8, cgen_state_->context_);
284  auto i32_type = get_int_type(32, cgen_state_->context_);
285  auto i64_type = get_int_type(64, cgen_state_->context_);
286  auto pi8_type = llvm::PointerType::get(i8_type, 0);
287  auto pi32_type = llvm::PointerType::get(i32_type, 0);
288 
289  auto result_type =
290  cgen_state_->ir_builder_.CreateAlloca(i32_type, nullptr, "result_type");
291  auto result_coords =
292  cgen_state_->ir_builder_.CreateAlloca(pi8_type, nullptr, "result_coords");
293  auto result_coords_size =
294  cgen_state_->ir_builder_.CreateAlloca(i64_type, nullptr, "result_coords_size");
295  auto result_ring_sizes =
296  cgen_state_->ir_builder_.CreateAlloca(pi32_type, nullptr, "result_ring_sizes");
297  auto result_ring_sizes_size =
298  cgen_state_->ir_builder_.CreateAlloca(i64_type, nullptr, "result_ring_sizes_size");
299  auto result_poly_rings =
300  cgen_state_->ir_builder_.CreateAlloca(pi32_type, nullptr, "result_poly_rings");
301  auto result_poly_rings_size =
302  cgen_state_->ir_builder_.CreateAlloca(i64_type, nullptr, "result_poly_rings_size");
303 
304  argument_list.emplace_back(result_type);
305  argument_list.emplace_back(result_coords);
306  argument_list.emplace_back(result_coords_size);
307  argument_list.emplace_back(result_ring_sizes);
308  argument_list.emplace_back(result_ring_sizes_size);
309  argument_list.emplace_back(result_poly_rings);
310  argument_list.emplace_back(result_poly_rings_size);
311 
312  // Generate call to GEOS wrapper
313  cgen_state_->needs_geos_ = true;
314  auto status_lv = cgen_state_->emitExternalCall(
315  func, llvm::Type::getInt1Ty(cgen_state_->context_), argument_list);
316  // Need to check the status and throw an error if this call has failed.
317  llvm::BasicBlock* geos_ok_bb{nullptr};
318  llvm::BasicBlock* geos_fail_bb{nullptr};
319  geos_ok_bb = llvm::BasicBlock::Create(
320  cgen_state_->context_, "geos_ok_bb", cgen_state_->current_func_);
321  geos_fail_bb = llvm::BasicBlock::Create(
322  cgen_state_->context_, "geos_fail_bb", cgen_state_->current_func_);
323  if (!status_lv) {
324  status_lv = cgen_state_->llBool(false);
325  }
326  cgen_state_->ir_builder_.CreateCondBr(status_lv, geos_ok_bb, geos_fail_bb);
327  cgen_state_->ir_builder_.SetInsertPoint(geos_fail_bb);
330  cgen_state_->ir_builder_.SetInsertPoint(geos_ok_bb);
331 
332  // TODO: Currently forcing the output to MULTIPOLYGON, but need to handle
333  // other possible geometries that geos may return, e.g. a POINT, a LINESTRING
334  // Need to handle empty result, e.g. empty intersection.
335  // The type of result is returned in `result_type`
336 
337  // Load return values
338  auto buf1 = cgen_state_->ir_builder_.CreateLoad(result_coords);
339  auto buf1s = cgen_state_->ir_builder_.CreateLoad(result_coords_size);
340  auto buf2 = cgen_state_->ir_builder_.CreateLoad(result_ring_sizes);
341  auto buf2s = cgen_state_->ir_builder_.CreateLoad(result_ring_sizes_size);
342  auto buf3 = cgen_state_->ir_builder_.CreateLoad(result_poly_rings);
343  auto buf3s = cgen_state_->ir_builder_.CreateLoad(result_poly_rings_size);
344 
345  // generate register_buffer_with_executor_rsm() calls to register all output buffers
347  "register_buffer_with_executor_rsm",
348  llvm::Type::getVoidTy(cgen_state_->context_),
349  {cgen_state_->llInt(reinterpret_cast<int64_t>(executor())),
350  cgen_state_->ir_builder_.CreatePointerCast(buf1, pi8_type)});
351  cgen_state_->emitExternalCall(
352  "register_buffer_with_executor_rsm",
353  llvm::Type::getVoidTy(cgen_state_->context_),
354  {cgen_state_->llInt(reinterpret_cast<int64_t>(executor())),
355  cgen_state_->ir_builder_.CreatePointerCast(buf2, pi8_type)});
356  cgen_state_->emitExternalCall(
357  "register_buffer_with_executor_rsm",
358  llvm::Type::getVoidTy(cgen_state_->context_),
359  {cgen_state_->llInt(reinterpret_cast<int64_t>(executor())),
360  cgen_state_->ir_builder_.CreatePointerCast(buf3, pi8_type)});
361 
362  return {cgen_state_->ir_builder_.CreatePointerCast(buf1, pi8_type),
363  buf1s,
364  cgen_state_->ir_builder_.CreatePointerCast(buf2, pi32_type),
365  buf2s,
366  cgen_state_->ir_builder_.CreatePointerCast(buf3, pi32_type),
367  buf3s};
368 }
#define CHECK_EQ(x, y)
Definition: Logger.h:205
const SQLTypeInfo getTypeInfo0() const
Definition: Analyzer.h:1550
const std::vector< std::shared_ptr< Analyzer::Expr > > & getArgs0() const
Definition: Analyzer.h:1552
const SQLTypeInfo getTypeInfo0() const
Definition: Analyzer.h:1521
CgenState * cgen_state_
llvm::Value * emitExternalCall(const std::string &fname, llvm::Type *ret_type, const std::vector< llvm::Value * > args, const std::vector< llvm::Attribute::AttrKind > &fnattrs={}, const bool has_struct_return=false)
Definition: CgenState.h:222
llvm::IRBuilder ir_builder_
Definition: CgenState.h:341
int32_t get_compression_scheme(const SQLTypeInfo &ti)
Definition: Compression.cpp:23
llvm::Value * posArg(const Analyzer::Expr *) const
Definition: ColumnIR.cpp:515
const SQLTypeInfo getTypeInfo1() const
Definition: Analyzer.h:1551
llvm::ConstantInt * llBool(const bool v) const
Definition: CgenState.h:326
bool needs_geos_
Definition: CgenState.h:357
static const int32_t ERR_GEOS
Definition: Execute.h:995
HOST DEVICE SQLTypes get_type() const
Definition: sqltypes.h:259
llvm::Type * get_int_type(const int width, llvm::LLVMContext &context)
std::vector< llvm::Value * > codegenGeoBinOper(const Analyzer::GeoBinOper *, const CompilationOptions &)
Definition: GeoIR.cpp:81
llvm::LLVMContext & context_
Definition: CgenState.h:339
llvm::Function * current_func_
Definition: CgenState.h:333
std::vector< llvm::Value * > codegenGeoUOper(const Analyzer::GeoUOper *, const CompilationOptions &)
Definition: GeoIR.cpp:21
const std::vector< std::shared_ptr< Analyzer::Expr > > & getArgs0() const
Definition: Analyzer.h:1522
std::vector< llvm::Value * > codegenGeosConstructorCall(const std::string &, std::vector< llvm::Value * >, const CompilationOptions &)
Definition: GeoIR.cpp:277
bool needs_error_check_
Definition: CgenState.h:356
#define AUTOMATIC_IR_METADATA(CGENSTATE)
const SQLTypeInfo & get_type_info() const
Definition: Analyzer.h:78
ExecutorDeviceType device_type
std::vector< llvm::Value * > codegen(const Analyzer::Expr *, const bool fetch_columns, const CompilationOptions &)
Definition: IRCodegen.cpp:26
std::vector< llvm::Value * > codegenGeosPredicateCall(const std::string &, std::vector< llvm::Value * >, const CompilationOptions &)
Definition: GeoIR.cpp:245
llvm::ConstantInt * llInt(const T v) const
Definition: CgenState.h:312
std::vector< llvm::Value * > codegenGeoArgs(const std::vector< std::shared_ptr< Analyzer::Expr >> &, const CompilationOptions &)
Definition: GeoIR.cpp:164
#define CHECK(condition)
Definition: Logger.h:197
Geospatial::GeoBase::GeoOp getOp() const
Definition: Analyzer.h:1520
uint32_t log2_bytes(const uint32_t bytes)
Definition: Execute.h:139
int get_physical_coord_cols() const
Definition: sqltypes.h:295
Geospatial::GeoBase::GeoOp getOp() const
Definition: Analyzer.h:1549
const std::vector< std::shared_ptr< Analyzer::Expr > > & getArgs1() const
Definition: Analyzer.h:1553
HOST DEVICE int get_output_srid() const
Definition: sqltypes.h:265
Executor * executor() const