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