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