OmniSciDB  a5dc49c757
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CgenState.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 "CgenState.h"
18 #include "CodeGenerator.h"
19 #include "CodegenHelper.h"
21 
22 #include <llvm/IR/InstIterator.h>
23 #include <llvm/Transforms/Utils/BasicBlockUtils.h>
24 #include <llvm/Transforms/Utils/Cloning.h>
25 
26 CgenState::CgenState(const size_t num_query_infos,
27  const bool contains_left_deep_outer_join,
28  Executor* executor)
29  : executor_id_(executor->getExecutorId())
30  , module_(nullptr)
31  , row_func_(nullptr)
32  , filter_func_(nullptr)
33  , current_func_(nullptr)
34  , row_func_bb_(nullptr)
35  , filter_func_bb_(nullptr)
36  , row_func_call_(nullptr)
37  , filter_func_call_(nullptr)
38  , context_(executor->getContext())
39  , ir_builder_(context_)
40  , contains_left_deep_outer_join_(contains_left_deep_outer_join)
41  , outer_join_match_found_per_level_(std::max(num_query_infos, size_t(1)) - 1)
42  , needs_error_check_(false)
43  , needs_geos_(false)
44  , query_func_(nullptr)
45  , query_func_entry_ir_builder_(context_){};
46 
47 CgenState::CgenState(const size_t num_query_infos,
48  const bool contains_left_deep_outer_join)
49  : CgenState(num_query_infos,
50  contains_left_deep_outer_join,
51  Executor::getExecutor(Executor::UNITARY_EXECUTOR_ID).get()) {}
52 
53 CgenState::CgenState(llvm::LLVMContext& context)
54  : executor_id_(Executor::INVALID_EXECUTOR_ID)
55  , module_(nullptr)
56  , row_func_(nullptr)
57  , context_(context)
58  , ir_builder_(context_)
59  , contains_left_deep_outer_join_(false)
60  , needs_error_check_(false)
61  , needs_geos_(false)
62  , query_func_(nullptr)
63  , query_func_entry_ir_builder_(context_){};
64 
65 llvm::ConstantInt* CgenState::inlineIntNull(const SQLTypeInfo& type_info) {
66  auto type = type_info.get_type();
67  if (type_info.is_string()) {
68  switch (type_info.get_compression()) {
69  case kENCODING_DICT:
70  return llInt(static_cast<int32_t>(inline_int_null_val(type_info)));
71  case kENCODING_NONE:
72  return llInt(int64_t(0));
73  default:
74  CHECK(false);
75  }
76  }
77  switch (type) {
78  case kBOOLEAN:
79  return llInt(static_cast<int8_t>(inline_int_null_val(type_info)));
80  case kTINYINT:
81  return llInt(static_cast<int8_t>(inline_int_null_val(type_info)));
82  case kSMALLINT:
83  return llInt(static_cast<int16_t>(inline_int_null_val(type_info)));
84  case kINT:
85  return llInt(static_cast<int32_t>(inline_int_null_val(type_info)));
86  case kBIGINT:
87  return llInt(static_cast<int64_t>(inline_int_null_val(type_info)));
88  case kTIME:
89  case kTIMESTAMP:
90  case kDATE:
91  case kINTERVAL_DAY_TIME:
93  return llInt(inline_int_null_val(type_info));
94  case kDECIMAL:
95  case kNUMERIC:
96  return llInt(inline_int_null_val(type_info));
97  case kARRAY:
98  return llInt(int64_t(0));
99  default:
100  abort();
101  }
102 }
103 
104 llvm::ConstantFP* CgenState::inlineFpNull(const SQLTypeInfo& type_info) {
105  CHECK(type_info.is_fp());
106  switch (type_info.get_type()) {
107  case kFLOAT:
108  return llFp(NULL_FLOAT);
109  case kDOUBLE:
110  return llFp(NULL_DOUBLE);
111  default:
112  abort();
113  }
114 }
115 
116 llvm::Constant* CgenState::inlineNull(const SQLTypeInfo& ti) {
117  return ti.is_fp() ? static_cast<llvm::Constant*>(inlineFpNull(ti))
118  : static_cast<llvm::Constant*>(inlineIntNull(ti));
119 }
120 
121 std::pair<llvm::ConstantInt*, llvm::ConstantInt*> CgenState::inlineIntMaxMin(
122  const size_t byte_width,
123  const bool is_signed) {
124  int64_t max_int{0}, min_int{0};
125  if (is_signed) {
126  std::tie(max_int, min_int) = inline_int_max_min(byte_width);
127  } else {
128  uint64_t max_uint{0}, min_uint{0};
129  std::tie(max_uint, min_uint) = inline_uint_max_min(byte_width);
130  max_int = static_cast<int64_t>(max_uint);
131  CHECK_EQ(uint64_t(0), min_uint);
132  }
133  switch (byte_width) {
134  case 1:
135  return std::make_pair(::ll_int(static_cast<int8_t>(max_int), context_),
136  ::ll_int(static_cast<int8_t>(min_int), context_));
137  case 2:
138  return std::make_pair(::ll_int(static_cast<int16_t>(max_int), context_),
139  ::ll_int(static_cast<int16_t>(min_int), context_));
140  case 4:
141  return std::make_pair(::ll_int(static_cast<int32_t>(max_int), context_),
142  ::ll_int(static_cast<int32_t>(min_int), context_));
143  case 8:
144  return std::make_pair(::ll_int(max_int, context_), ::ll_int(min_int, context_));
145  default:
146  abort();
147  }
148 }
149 
150 llvm::Value* CgenState::castToTypeIn(llvm::Value* val, const size_t dst_bits) {
151  auto src_bits = val->getType()->getScalarSizeInBits();
152  if (src_bits == dst_bits) {
153  return val;
154  }
155  if (val->getType()->isIntegerTy()) {
156  return ir_builder_.CreateIntCast(
157  val, get_int_type(dst_bits, context_), src_bits != 1);
158  }
159  // real (not dictionary-encoded) strings; store the pointer to the payload
160  if (val->getType()->isPointerTy()) {
161  return ir_builder_.CreatePointerCast(val, get_int_type(dst_bits, context_));
162  }
163 
164  CHECK(val->getType()->isFloatTy() || val->getType()->isDoubleTy());
165 
166  llvm::Type* dst_type = nullptr;
167  switch (dst_bits) {
168  case 64:
169  dst_type = llvm::Type::getDoubleTy(context_);
170  break;
171  case 32:
172  dst_type = llvm::Type::getFloatTy(context_);
173  break;
174  default:
175  CHECK(false);
176  }
177 
178  return ir_builder_.CreateFPCast(val, dst_type);
179 }
180 
181 void CgenState::maybeCloneFunctionRecursive(llvm::Function* fn) {
182  CHECK(fn);
183  if (!fn->isDeclaration()) {
184  return;
185  }
186 
187  // Get the implementation from the runtime module.
188  auto func_impl = getExecutor()->get_rt_module()->getFunction(fn->getName());
189  CHECK(func_impl) << fn->getName().str();
190 
191  if (func_impl->isDeclaration()) {
192  return;
193  }
194 
195  auto DestI = fn->arg_begin();
196  for (auto arg_it = func_impl->arg_begin(); arg_it != func_impl->arg_end(); ++arg_it) {
197  DestI->setName(arg_it->getName());
198  vmap_[&*arg_it] = &*DestI++;
199  }
200 
201  llvm::SmallVector<llvm::ReturnInst*, 8> Returns; // Ignore returns cloned.
202 #if LLVM_VERSION_MAJOR > 12
203  llvm::CloneFunctionInto(
204  fn, func_impl, vmap_, llvm::CloneFunctionChangeType::DifferentModule, Returns);
205 #else
206  llvm::CloneFunctionInto(fn, func_impl, vmap_, /*ModuleLevelChanges=*/true, Returns);
207 #endif
208 
209  for (auto it = llvm::inst_begin(fn), e = llvm::inst_end(fn); it != e; ++it) {
210  if (llvm::isa<llvm::CallInst>(*it)) {
211  auto& call = llvm::cast<llvm::CallInst>(*it);
212  maybeCloneFunctionRecursive(call.getCalledFunction());
213  }
214  }
215 }
216 
217 llvm::Value* CgenState::emitCall(const std::string& fname,
218  const std::vector<llvm::Value*>& args) {
219  // Get the function reference from the query module.
220  auto func = module_->getFunction(fname);
221  CHECK(func) << fname;
222  // If the function called isn't external, clone the implementation from the runtime
223  // module.
225 
226  return ir_builder_.CreateCall(func, args);
227 }
228 
229 llvm::Value* CgenState::emitEntryCall(const std::string& fname,
230  const std::vector<llvm::Value*>& args) {
231  // Get the function reference from the query module.
232  auto func = module_->getFunction(fname);
233  CHECK(func);
234  // If the function called isn't external, clone the implementation from the runtime
235  // module.
237 
238  return query_func_entry_ir_builder_.CreateCall(func, args);
239 }
240 
241 void CgenState::emitErrorCheck(llvm::Value* condition,
242  llvm::Value* errorCode,
243  std::string label) {
244  needs_error_check_ = true;
245  auto check_ok = llvm::BasicBlock::Create(context_, label + "_ok", current_func_);
246  auto check_fail = llvm::BasicBlock::Create(context_, label + "_fail", current_func_);
247  ir_builder_.CreateCondBr(condition, check_ok, check_fail);
248  ir_builder_.SetInsertPoint(check_fail);
249  ir_builder_.CreateRet(errorCode);
250  ir_builder_.SetInsertPoint(check_ok);
251 }
252 
253 namespace {
254 
255 // clang-format off
256 template <typename T>
257 llvm::Type* getTy(llvm::LLVMContext& ctx) { return getTy<std::remove_pointer_t<T>>(ctx)->getPointerTo(); }
258 // Commented out to avoid -Wunused-function warnings, but enable as needed.
259 // template<> llvm::Type* getTy<bool>(llvm::LLVMContext& ctx) { return llvm::Type::getInt1Ty(ctx); }
260 //template<> llvm::Type* getTy<int8_t>(llvm::LLVMContext& ctx) { return llvm::Type::getInt8Ty(ctx); }
261 // template<> llvm::Type* getTy<int16_t>(llvm::LLVMContext& ctx) { return llvm::Type::getInt16Ty(ctx); }
262 //template<> llvm::Type* getTy<int32_t>(llvm::LLVMContext& ctx) { return llvm::Type::getInt32Ty(ctx); }
263 // template<> llvm::Type* getTy<int64_t>(llvm::LLVMContext& ctx) { return llvm::Type::getInt64Ty(ctx); }
264 // template<> llvm::Type* getTy<float>(llvm::LLVMContext& ctx) { return llvm::Type::getFloatTy(ctx); }
265 template<> llvm::Type* getTy<double>(llvm::LLVMContext& ctx) { return llvm::Type::getDoubleTy(ctx); }
266 //template<> llvm::Type* getTy<void>(llvm::LLVMContext& ctx) { return llvm::Type::getVoidTy(ctx); }
267 // clang-format on
268 
270  GpuFunctionDefinition(char const* name) : name_(name) {}
271  char const* const name_;
272 
273  virtual ~GpuFunctionDefinition() = default;
274 
275  virtual llvm::FunctionCallee getFunction(llvm::Module* llvm_module,
276  llvm::LLVMContext& context) const = 0;
277 };
278 
279 // TYPES = return_type, arg0_type, arg1_type, arg2_type, ...
280 template <typename... TYPES>
281 struct GpuFunction final : public GpuFunctionDefinition {
282  GpuFunction(char const* name) : GpuFunctionDefinition(name) {}
283 
284  llvm::FunctionCallee getFunction(llvm::Module* llvm_module,
285  llvm::LLVMContext& context) const override {
286  return llvm_module->getOrInsertFunction(name_, getTy<TYPES>(context)...);
287  }
288 };
289 
290 static const std::unordered_map<std::string, std::shared_ptr<GpuFunctionDefinition>>
292  {"asin", std::make_shared<GpuFunction<double, double>>("Asin")},
293  {"atanh", std::make_shared<GpuFunction<double, double>>("Atanh")},
294  {"atan", std::make_shared<GpuFunction<double, double>>("Atan")},
295  {"cosh", std::make_shared<GpuFunction<double, double>>("Cosh")},
296  {"cos", std::make_shared<GpuFunction<double, double>>("Cos")},
297  {"exp", std::make_shared<GpuFunction<double, double>>("Exp")},
298  {"log", std::make_shared<GpuFunction<double, double>>("ln")},
299  {"pow", std::make_shared<GpuFunction<double, double, double>>("power")},
300  {"sinh", std::make_shared<GpuFunction<double, double>>("Sinh")},
301  {"sin", std::make_shared<GpuFunction<double, double>>("Sin")},
302  {"sqrt", std::make_shared<GpuFunction<double, double>>("Sqrt")},
303  {"tan", std::make_shared<GpuFunction<double, double>>("Tan")}};
304 } // namespace
305 
306 std::vector<std::string> CgenState::gpuFunctionsToReplace(llvm::Function* fn) {
307  std::vector<std::string> ret;
308 
309  CHECK(fn);
310  CHECK(!fn->isDeclaration());
311 
312  for (auto& basic_block : *fn) {
313  auto& inst_list = basic_block.getInstList();
314  for (auto inst_itr = inst_list.begin(); inst_itr != inst_list.end(); ++inst_itr) {
315  if (auto call_inst = llvm::dyn_cast<llvm::CallInst>(inst_itr)) {
316  auto const called_func_name = CodegenUtil::getCalledFunctionName(*call_inst);
317  CHECK(called_func_name);
318 
319  if (gpu_replacement_functions.find(std::string(*called_func_name)) !=
321  ret.emplace_back(*called_func_name);
322  }
323  }
324  }
325  }
326  return ret;
327 }
328 
329 void CgenState::replaceFunctionForGpu(const std::string& fcn_to_replace,
330  llvm::Function* fn) {
331  CHECK(fn);
332  CHECK(!fn->isDeclaration());
333 
334  auto map_it = gpu_replacement_functions.find(fcn_to_replace);
335  if (map_it == gpu_replacement_functions.end()) {
336  throw QueryMustRunOnCpu("Codegen failed: Could not find replacement functon for " +
337  fcn_to_replace +
338  " to run on gpu. Query step must run in cpu mode.");
339  }
340  const auto& gpu_fcn_obj = map_it->second;
341  CHECK(gpu_fcn_obj);
342  VLOG(1) << "Replacing " << fcn_to_replace << " with " << gpu_fcn_obj->name_
343  << " for parent function " << fn->getName().str();
344 
345  for (auto& basic_block : *fn) {
346  auto& inst_list = basic_block.getInstList();
347  for (auto inst_itr = inst_list.begin(); inst_itr != inst_list.end(); ++inst_itr) {
348  if (auto call_inst = llvm::dyn_cast<llvm::CallInst>(inst_itr)) {
349  auto called_func = CodegenUtil::findCalledFunction(*call_inst);
350  if (called_func && called_func->getName().str() == fcn_to_replace) {
351  std::vector<llvm::Value*> args;
352  std::vector<llvm::Type*> arg_types;
353  for (auto& arg : call_inst->args()) {
354  arg_types.push_back(arg.get()->getType());
355  args.push_back(arg.get());
356  }
357  auto gpu_func = gpu_fcn_obj->getFunction(module_, context_);
358  CHECK(gpu_func);
359  auto gpu_func_type = gpu_func.getFunctionType();
360  CHECK(gpu_func_type);
361  CHECK_EQ(gpu_func_type->getReturnType(), called_func->getReturnType());
362  llvm::ReplaceInstWithInst(call_inst,
363  llvm::CallInst::Create(gpu_func, args, ""));
364  return;
365  }
366  }
367  }
368  }
369 }
370 
371 std::shared_ptr<Executor> CgenState::getExecutor() const {
374 }
375 
376 llvm::LLVMContext& CgenState::getExecutorContext() const {
377  return getExecutor()->getContext();
378 }
379 
380 void CgenState::set_module_shallow_copy(const std::unique_ptr<llvm::Module>& llvm_module,
381  bool always_clone) {
382  module_ =
383  llvm::CloneModule(*llvm_module, vmap_, [always_clone](const llvm::GlobalValue* gv) {
384  auto func = llvm::dyn_cast<llvm::Function>(gv);
385  if (!func) {
386  return true;
387  }
388  return (func->getLinkage() == llvm::GlobalValue::LinkageTypes::PrivateLinkage ||
389  func->getLinkage() == llvm::GlobalValue::LinkageTypes::InternalLinkage ||
390  (always_clone && CodeGenerator::alwaysCloneRuntimeFunction(func)));
391  }).release();
392 }
393 
394 
396  const std::string& fname,
397  llvm::Type* ret_type,
398  const std::vector<llvm::Value*> args,
399  const std::vector<llvm::Attribute::AttrKind>& fnattrs,
400  const bool has_struct_return) {
401  std::vector<llvm::Type*> arg_types;
402  for (const auto arg : args) {
403  CHECK(arg);
404  arg_types.push_back(arg->getType());
405  }
406  auto func_ty = llvm::FunctionType::get(ret_type, arg_types, false);
407  llvm::AttributeList attrs;
408  if (!fnattrs.empty()) {
409  std::vector<std::pair<unsigned, llvm::Attribute>> indexedAttrs;
410  indexedAttrs.reserve(fnattrs.size());
411  for (auto attr : fnattrs) {
412  indexedAttrs.emplace_back(llvm::AttributeList::FunctionIndex,
413  llvm::Attribute::get(context_, attr));
414  }
415  attrs = llvm::AttributeList::get(context_,
416  {&indexedAttrs.front(), indexedAttrs.size()});
417  }
418 
419  auto func_p = module_->getOrInsertFunction(fname, func_ty, attrs);
420  CHECK(func_p);
421  auto callee = func_p.getCallee();
422  llvm::Function* func{nullptr};
423  if (auto callee_cast = llvm::dyn_cast<llvm::ConstantExpr>(callee)) {
424  // Get or insert function automatically adds a ConstantExpr cast if the return type
425  // of the existing function does not match the supplied return type.
426  CHECK(callee_cast->isCast());
427  CHECK_EQ(callee_cast->getNumOperands(), size_t(1));
428  func = llvm::dyn_cast<llvm::Function>(callee_cast->getOperand(0));
429  } else {
430  func = llvm::dyn_cast<llvm::Function>(callee);
431  }
432  CHECK(func);
433  llvm::FunctionType* func_type = func_p.getFunctionType();
434  CHECK(func_type);
435  if (has_struct_return) {
436  const auto arg_ti = func_type->getParamType(0);
437  CHECK(arg_ti->isPointerTy() && arg_ti->getPointerElementType()->isStructTy());
438  auto attr_list = func->getAttributes();
439 #if 14 <= LLVM_VERSION_MAJOR
440  llvm::AttrBuilder arr_arg_builder(context_, attr_list.getParamAttrs(0));
441 #else
442  llvm::AttrBuilder arr_arg_builder(attr_list.getParamAttributes(0));
443 #endif
444  arr_arg_builder.addAttribute(llvm::Attribute::StructRet);
445  func->addParamAttrs(0, arr_arg_builder);
446  }
447  llvm::Value* result = ir_builder_.CreateCall(func_p, args);
448  // check the assumed type
449  CHECK_EQ(result->getType(), ret_type);
450  return result;
451 }
std::optional< std::string_view > getCalledFunctionName(llvm::CallInst &call_inst)
#define CHECK_EQ(x, y)
Definition: Logger.h:301
llvm::Value * castToTypeIn(llvm::Value *val, const size_t bit_width)
Definition: CgenState.cpp:150
llvm::Value * emitEntryCall(const std::string &fname, const std::vector< llvm::Value * > &args)
Definition: CgenState.cpp:229
#define NULL_DOUBLE
Definition: sqltypes.h:76
llvm::LLVMContext & getExecutorContext() const
Definition: CgenState.cpp:376
std::shared_ptr< Executor > getExecutor() const
Definition: CgenState.cpp:371
#define NULL_FLOAT
void maybeCloneFunctionRecursive(llvm::Function *fn)
Definition: CgenState.cpp:181
bool is_fp() const
Definition: sqltypes.h:573
llvm::ConstantInt * ll_int(const T v, llvm::LLVMContext &context)
llvm::IRBuilder ir_builder_
Definition: CgenState.h:384
static constexpr ExecutorId INVALID_EXECUTOR_ID
Definition: Execute.h:424
llvm::Type * getTy(llvm::LLVMContext &ctx)
Definition: CgenState.cpp:257
HOST DEVICE SQLTypes get_type() const
Definition: sqltypes.h:391
llvm::Type * get_int_type(const int width, llvm::LLVMContext &context)
llvm::Type * getTy< double >(llvm::LLVMContext &ctx)
Definition: CgenState.cpp:265
static std::shared_ptr< Executor > getExecutor(const ExecutorId id, const std::string &debug_dir="", const std::string &debug_file="", const SystemParameters &system_parameters=SystemParameters())
Definition: Execute.cpp:513
static const std::unordered_map< std::string, std::shared_ptr< GpuFunctionDefinition > > gpu_replacement_functions
Definition: CgenState.cpp:291
llvm::Module * module_
Definition: CgenState.h:373
size_t executor_id_
Definition: CgenState.h:276
llvm::LLVMContext & context_
Definition: CgenState.h:382
llvm::Function * current_func_
Definition: CgenState.h:376
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
llvm::ConstantInt * inlineIntNull(const SQLTypeInfo &)
Definition: CgenState.cpp:65
llvm::FunctionCallee getFunction(llvm::Module *llvm_module, llvm::LLVMContext &context) const override
Definition: CgenState.cpp:284
void replaceFunctionForGpu(const std::string &fcn_to_replace, llvm::Function *fn)
Definition: CgenState.cpp:329
bool needs_error_check_
Definition: CgenState.h:405
llvm::ConstantFP * llFp(const float v) const
Definition: CgenState.h:253
std::vector< std::string > gpuFunctionsToReplace(llvm::Function *fn)
Definition: CgenState.cpp:306
llvm::IRBuilder query_func_entry_ir_builder_
Definition: CgenState.h:409
llvm::Value * emitCall(const std::string &fname, const std::vector< llvm::Value * > &args)
Definition: CgenState.cpp:217
std::pair< uint64_t, uint64_t > inline_uint_max_min(const size_t byte_width)
llvm::Constant * inlineNull(const SQLTypeInfo &)
Definition: CgenState.cpp:116
void set_module_shallow_copy(const std::unique_ptr< llvm::Module > &module, bool always_clone=false)
Definition: CgenState.cpp:380
llvm::Function * findCalledFunction(llvm::CallInst &call_inst)
Definition: sqltypes.h:80
HOST DEVICE EncodingType get_compression() const
Definition: sqltypes.h:399
static bool alwaysCloneRuntimeFunction(const llvm::Function *func)
void emitErrorCheck(llvm::Value *condition, llvm::Value *errorCode, std::string label)
Definition: CgenState.cpp:241
llvm::ConstantInt * llInt(const T v) const
Definition: CgenState.h:249
bool g_enable_watchdog false
Definition: Execute.cpp:80
#define CHECK(condition)
Definition: Logger.h:291
llvm::ValueToValueMapTy vmap_
Definition: CgenState.h:383
int64_t inline_int_null_val(const SQL_TYPE_INFO &ti)
std::pair< int64_t, int64_t > inline_int_max_min(const size_t byte_width)
Definition: sqltypes.h:72
bool is_string() const
Definition: sqltypes.h:561
string name
Definition: setup.in.py:72
CgenState(const size_t num_query_infos, const bool contains_left_deep_outer_join, Executor *executor)
Definition: CgenState.cpp:26
std::pair< llvm::ConstantInt *, llvm::ConstantInt * > inlineIntMaxMin(const size_t byte_width, const bool is_signed)
Definition: CgenState.cpp:121
#define VLOG(n)
Definition: Logger.h:388
llvm::ConstantFP * inlineFpNull(const SQLTypeInfo &)
Definition: CgenState.cpp:104