OmniSciDB  1dac507f6e
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ResultSetReductionInterpreterStubs.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 
18 #include "CodeGenerator.h"
20 
21 namespace {
22 
23 // Creates an empty stub function, with the fixed signature required by the interpreter.
24 llvm::Function* create_stub_function(const std::string& name, CgenState* cgen_state) {
25  auto void_type = llvm::Type::getVoidTy(cgen_state->context_);
26  auto int8_ptr_type = llvm::PointerType::get(get_int_type(8, cgen_state->context_), 0);
27  std::vector<llvm::Type*> parameter_types(2, int8_ptr_type);
28  const auto func_type = llvm::FunctionType::get(void_type, parameter_types, false);
29  auto function = llvm::Function::Create(
30  func_type, llvm::Function::ExternalLinkage, name, cgen_state->module_);
31  const auto bb_entry =
32  llvm::BasicBlock::Create(cgen_state->context_, ".entry", function, 0);
33  cgen_state->ir_builder_.SetInsertPoint(bb_entry);
34  return function;
35 }
36 
37 // Returns the name of runtime function which reads the value wrapped by a
38 // ReductionInterpreter::EvalValue.
39 std::string get_stub_read_argument_name(const Type arg_type) {
40  std::string read_arg_name{"read_stub_arg_"};
41  switch (arg_type) {
42  case Type::Int32:
43  case Type::Int64: {
44  read_arg_name += "int";
45  break;
46  }
47  case Type::Float: {
48  read_arg_name += "float";
49  break;
50  }
51  case Type::Double: {
52  read_arg_name += "double";
53  break;
54  }
55  case Type::Int8Ptr: {
56  read_arg_name += "pi8";
57  break;
58  }
59  case Type::Int32Ptr: {
60  read_arg_name += "pi32";
61  break;
62  }
63  case Type::Int64Ptr: {
64  read_arg_name += "pi64";
65  break;
66  }
67  case Type::VoidPtr: {
68  read_arg_name += "pvoid";
69  break;
70  }
71  case Type::Int64PtrPtr: {
72  read_arg_name += "ppi64";
73  break;
74  }
75  default: {
76  LOG(FATAL) << "Invalid type: " << static_cast<int>(arg_type);
77  }
78  }
79  return read_arg_name;
80 }
81 
82 } // namespace
83 
84 bool is_integer_type(const Type type) {
85  switch (type) {
86  case Type::Int1:
87  case Type::Int8:
88  case Type::Int32:
89  case Type::Int64: {
90  return true;
91  }
92  default: {
93  return false;
94  }
95  }
96 }
97 
98 bool is_pointer_type(const Type type) {
99  switch (type) {
100  case Type::Int8Ptr:
101  case Type::Int32Ptr:
102  case Type::Int64Ptr:
103  case Type::FloatPtr:
104  case Type::DoublePtr:
105  case Type::VoidPtr:
106  case Type::Int64PtrPtr: {
107  return true;
108  }
109  default: {
110  return false;
111  }
112  }
113 }
114 
115 // The following read_stub_arg_* functions read the argument at the given position from
116 // the list of wrapped inputs passed from the interpreter.
117 
118 extern "C" int64_t read_stub_arg_int(const void* inputs_handle, const int32_t i) {
119  const auto& inputs = *reinterpret_cast<const StubGenerator::InputsType*>(inputs_handle);
120  CHECK_LT(static_cast<size_t>(i), inputs.size());
121  return inputs[i].int_val;
122 }
123 
124 extern "C" float read_stub_arg_float(const void* inputs_handle, const int32_t i) {
125  const auto& inputs = *reinterpret_cast<const StubGenerator::InputsType*>(inputs_handle);
126  CHECK_LT(static_cast<size_t>(i), inputs.size());
127  return inputs[i].float_val;
128 }
129 
130 extern "C" double read_stub_arg_double(const void* inputs_handle, const int32_t i) {
131  const auto& inputs = *reinterpret_cast<const StubGenerator::InputsType*>(inputs_handle);
132  CHECK_LT(static_cast<size_t>(i), inputs.size());
133  return inputs[i].double_val;
134 }
135 
136 extern "C" const void* read_stub_arg_pvoid(const void* inputs_handle, const int32_t i) {
137  const auto& inputs = *reinterpret_cast<const StubGenerator::InputsType*>(inputs_handle);
138  CHECK_LT(static_cast<size_t>(i), inputs.size());
139  return inputs[i].ptr;
140 }
141 
142 extern "C" const int8_t* read_stub_arg_pi8(const void* inputs_handle, const int32_t i) {
143  return static_cast<const int8_t*>(read_stub_arg_pvoid(inputs_handle, i));
144 }
145 
146 extern "C" const int32_t* read_stub_arg_pi32(const void* inputs_handle, const int32_t i) {
147  return static_cast<const int32_t*>(read_stub_arg_pvoid(inputs_handle, i));
148 }
149 
150 extern "C" const int32_t* read_stub_arg_pi64(const void* inputs_handle, const int32_t i) {
151  return static_cast<const int32_t*>(read_stub_arg_pvoid(inputs_handle, i));
152 }
153 
154 extern "C" const int64_t* const* read_stub_arg_ppi64(const void* inputs_handle,
155  const int32_t i) {
156  return static_cast<const int64_t* const*>(read_stub_arg_pvoid(inputs_handle, i));
157 }
158 
159 // Writes back the value returned by the runtime function to the wrapped output value used
160 // from the interpreter.
161 extern "C" void write_stub_result_int(void* output_handle, const int64_t int_val) {
162  auto output = reinterpret_cast<ReductionInterpreter::EvalValue*>(output_handle);
163  output->int_val = int_val;
164 }
165 
166 // Generates a stub function with a fixed signature which can be called from the
167 // interpreter. The generated function extracts the values from the list of wrapped values
168 // from the interpreter, consistent with the provided argument types.
170  const std::vector<Type>& arg_types,
171  const Type ret_type,
172  const bool is_external) {
173  const auto stub_name = name + "_stub";
174  CodeCacheKey key{stub_name};
175  std::lock_guard<std::mutex> s_stubs_cache_lock(s_stubs_cache_mutex);
176  const auto val_ptr = s_stubs_cache.get(key);
177  if (val_ptr) {
178  return reinterpret_cast<StubGenerator::Stub>(std::get<0>(val_ptr->first.front()));
179  }
180  auto cgen_state = std::make_unique<CgenState>(std::vector<InputTableInfo>{}, false);
181  std::unique_ptr<llvm::Module> module(runtime_module_shallow_copy(cgen_state.get()));
182  cgen_state->module_ = module.get();
183  const auto function = create_stub_function(stub_name, cgen_state.get());
184  CHECK(function);
185  auto& ctx = cgen_state->context_;
186  std::vector<llvm::Value*> callee_args;
187  auto inputs_it = function->arg_begin() + 1;
188  for (size_t i = 0; i < arg_types.size(); ++i) {
189  const auto arg_type = arg_types[i];
190  const auto read_arg_name = get_stub_read_argument_name(arg_type);
191  const auto llvm_arg_type = llvm_type(arg_type, ctx);
192  auto callee_arg = cgen_state->emitExternalCall(
193  read_arg_name, llvm_arg_type, {&*inputs_it, cgen_state->llInt<int32_t>(i)});
194  if (is_integer_type(arg_type)) {
195  CHECK(llvm_arg_type->isIntegerTy());
196  callee_arg = cgen_state->ir_builder_.CreateTrunc(callee_arg, llvm_arg_type);
197  } else if (is_pointer_type(arg_type)) {
198  CHECK(llvm_arg_type->isPointerTy());
199  callee_arg = cgen_state->ir_builder_.CreateBitCast(callee_arg, llvm_arg_type);
200  }
201  callee_args.push_back(callee_arg);
202  }
203  const auto llvm_ret_type = llvm_type(ret_type, ctx);
204  auto value = is_external
205  ? cgen_state->emitExternalCall(name, llvm_ret_type, callee_args)
206  : cgen_state->emitCall(name, callee_args);
207  auto output = &*(function->arg_begin());
208  auto void_type = llvm::Type::getVoidTy(ctx);
209  std::string write_arg_name{"write_stub_result_"};
210  switch (ret_type) {
211  case Type::Int8: {
212  write_arg_name += "int";
213  const auto i64_type = get_int_type(64, cgen_state->context_);
214  value = cgen_state->ir_builder_.CreateSExt(value, i64_type);
215  break;
216  }
217  case Type::Void: {
218  value = nullptr;
219  break;
220  }
221  default: {
222  LOG(FATAL) << "Invalid type: " << static_cast<int>(ret_type);
223  }
224  }
225  if (value) {
226  cgen_state->emitExternalCall(write_arg_name, void_type, {output, value});
227  }
228  cgen_state->ir_builder_.CreateRetVoid();
229  verify_function_ir(function);
232  module.release();
233  auto ee = CodeGenerator::generateNativeCPUCode(function, {function}, co);
234  auto func_ptr =
235  reinterpret_cast<StubGenerator::Stub>(ee->getPointerToFunction(function));
236  auto cache_val = std::make_tuple(reinterpret_cast<void*>(func_ptr), std::move(ee));
237  std::vector<std::tuple<void*, ExecutionEngineWrapper>> cache_vals;
238  cache_vals.emplace_back(std::move(cache_val));
240  key, std::move(cache_vals), function->getParent(), s_stubs_cache);
241  return func_ptr;
242 }
243 
245  std::lock_guard<std::mutex> s_stubs_cache_lock(s_stubs_cache_mutex);
247 }
248 
void clear()
Definition: LruCache.hpp:57
std::unique_ptr< llvm::Module > module(runtime_module_shallow_copy(cgen_state))
static void addCodeToCache(const CodeCacheKey &, std::vector< std::tuple< void *, ExecutionEngineWrapper >>, llvm::Module *, CodeCache &)
std::unique_ptr< llvm::Module > runtime_module_shallow_copy(CgenState *cgen_state)
#define LOG(tag)
Definition: Logger.h:185
std::vector< ReductionInterpreter::EvalValue > InputsType
llvm::IRBuilder ir_builder_
Definition: CgenState.h:269
int64_t read_stub_arg_int(const void *inputs_handle, const int32_t i)
std::vector< std::string > CodeCacheKey
Definition: CodeCache.h:61
static ExecutionEngineWrapper generateNativeCPUCode(llvm::Function *func, const std::unordered_set< llvm::Function * > &live_funcs, const CompilationOptions &co)
llvm::Type * get_int_type(const int width, llvm::LLVMContext &context)
float read_stub_arg_float(const void *inputs_handle, const int32_t i)
const void * read_stub_arg_pvoid(const void *inputs_handle, const int32_t i)
false auto cgen_state
static std::mutex s_stubs_cache_mutex
llvm::Module * module_
Definition: CgenState.h:264
void verify_function_ir(const llvm::Function *func)
llvm::LLVMContext & context_
Definition: CgenState.h:267
CHECK(cgen_state)
double read_stub_arg_double(const void *inputs_handle, const int32_t i)
static Stub generateStub(const std::string &name, const std::vector< Type > &arg_types, const Type ret_type, const bool is_external)
llvm::Type * llvm_type(const Type type, llvm::LLVMContext &ctx)
#define CHECK_LT(x, y)
Definition: Logger.h:200
const int32_t * read_stub_arg_pi32(const void *inputs_handle, const int32_t i)
ReductionInterpreter::EvalValue(*)(void *output_handle, const void *inputs_handle) Stub
const int64_t *const * read_stub_arg_ppi64(const void *inputs_handle, const int32_t i)
const int32_t * read_stub_arg_pi64(const void *inputs_handle, const int32_t i)
bool is_pointer_type(const Type type)
value_t * get(const key_t &key)
Definition: LruCache.hpp:39
const int8_t * read_stub_arg_pi8(const void *inputs_handle, const int32_t i)
bool is_integer_type(const Type type)
llvm::Function * create_stub_function(const std::string &name, CgenState *cgen_state)
void write_stub_result_int(void *output_handle, const int64_t int_val)