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