/Users/deen/code/yugabyte-db/src/yb/bfql/codegen.cc
Line | Count | Source (jump to first uncovered line) |
1 | | //-------------------------------------------------------------------------------------------------- |
2 | | // Copyright (c) YugaByte, Inc. |
3 | | // |
4 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
5 | | // in compliance with the License. You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
10 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
11 | | // or implied. See the License for the specific language governing permissions and limitations |
12 | | // under the License. |
13 | | // |
14 | | //-------------------------------------------------------------------------------------------------- |
15 | | #include <fstream> |
16 | | #include <vector> |
17 | | |
18 | | #include <glog/logging.h> |
19 | | |
20 | | #include "yb/bfql/directory.h" |
21 | | #include "yb/common/ql_type.h" |
22 | | #include "yb/gutil/strings/substitute.h" |
23 | | |
24 | | using std::endl; |
25 | | using std::map; |
26 | | using std::ofstream; |
27 | | using std::string; |
28 | | using std::to_string; |
29 | | using std::vector; |
30 | | |
31 | | namespace yb { |
32 | | namespace bfql { |
33 | | |
34 | | static const char *kFileStart = |
35 | | "// Copyright (c) YugaByte, Inc.\n\n"; |
36 | | |
37 | | static const char *kFileNamespace = |
38 | | "namespace yb {\n" |
39 | | "namespace bfql {\n" |
40 | | "\n"; |
41 | | |
42 | | static const char *kFileEnd = |
43 | | "\n" |
44 | | "} // namespace bfql\n" |
45 | | "} // namespace yb\n"; |
46 | | |
47 | | struct BFClassInfo { |
48 | | BFClassInfo(const string& cname, const string& oname, const string& ovl_oname) |
49 | 159 | : class_name(cname), opname(oname), overloaded_opname(ovl_oname) { |
50 | 159 | } |
51 | | |
52 | | string class_name; |
53 | | string opname; |
54 | | string overloaded_opname; |
55 | | }; |
56 | | |
57 | | class BFCodegen { |
58 | | public: |
59 | | static const int kHasParamResult = 0; |
60 | | static const int kHasParamOnly = 1; |
61 | | static const int kHasResultOnly = 1; |
62 | | |
63 | | // Because SQL functions might need to support IN/OUT parameters, we won't provide const& option. |
64 | | // Each argument is either a shared_ptr, raw pointer, or reference. |
65 | | enum class BFApiParamOption { |
66 | | kSharedPtr, // Both arguments and returned-result are shared_ptr. |
67 | | kRawPtr, // Both arguments and returned-result are raw pointers. |
68 | | kRefAndRaw, // Arguments are references (Type &arg), and returned-result is a raw pointer. |
69 | | }; |
70 | | |
71 | | // Typeargs datatype. |
72 | 1 | void GenerateOpcodes(string build_dir) { |
73 | 1 | ofstream fopcode; |
74 | 1 | fopcode.open(build_dir + "/gen_opcodes.h"); |
75 | 1 | fopcode << kFileStart |
76 | 1 | << "#ifndef YB_UTIL_BFQL_GEN_OPCODES_H_" << endl |
77 | 1 | << "#define YB_UTIL_BFQL_GEN_OPCODES_H_" << endl |
78 | 1 | << endl |
79 | 1 | << "#include <unordered_map>" << endl |
80 | 1 | << endl |
81 | 1 | << kFileNamespace; |
82 | | |
83 | | // Start an enum class with a NO_OP. |
84 | 1 | operator_ids_.reserve(kBFDirectory.size()); |
85 | 1 | fopcode << "enum class BFOpcode : int32_t {" << endl; |
86 | | |
87 | | // All builtin operators should be prefix with "OP_". |
88 | 1 | string min_opcode; |
89 | 1 | int op_index = 0; |
90 | 159 | for (BFDecl entry : kBFDirectory) { |
91 | | // Form the opcode and print it. |
92 | 159 | string current_opcode = entry.bfopcode_name(); |
93 | 159 | if (current_opcode.empty()) { |
94 | 141 | current_opcode = strings::Substitute("OPCODE_$0_$1", entry.cpp_name(), op_index); |
95 | 141 | } |
96 | 159 | fopcode << " " << current_opcode << "," << endl; |
97 | 159 | if (op_index == 0) { |
98 | 1 | min_opcode = current_opcode; |
99 | 1 | } |
100 | | |
101 | | // Find the last generated opcode that this opcode is overloading. |
102 | 159 | string overloaded_opcode; |
103 | 159 | if (yql2opcode_.find(entry.ql_name()) == yql2opcode_.end()) { |
104 | 91 | overloaded_opcode = current_opcode; |
105 | 91 | } else { |
106 | 68 | overloaded_opcode = yql2opcode_[entry.ql_name()]; |
107 | 68 | } |
108 | 159 | yql2opcode_[entry.ql_name()] = current_opcode; |
109 | | |
110 | | // Use opcode enum value to create unique operator name. This operator keeps the last |
111 | | // overloaded opcode to form a chain between overloading opcodes for the same ql_name. |
112 | | // Using this chain we can track all opcodes that are mapped to the same ql_name. |
113 | 159 | operator_ids_.emplace_back(strings::Substitute("OPERATOR_$0_$1", entry.cpp_name(), op_index), |
114 | 159 | current_opcode, overloaded_opcode); |
115 | 159 | op_index++; |
116 | 159 | } |
117 | 1 | fopcode << " OPCODE_MAX_VALUE" << endl; |
118 | 1 | fopcode << "};" << endl; |
119 | | |
120 | 1 | fopcode << "const BFOpcode BFOPCODE_NOOP = BFOpcode::" << min_opcode << ";" << endl |
121 | 1 | << "const BFOpcode BFOPCODE_MIN_VALUE = BFOpcode::" << min_opcode << ";" << endl |
122 | 1 | << "const BFOpcode BFOPCODE_MAX_VALUE = BFOpcode::OPCODE_MAX_VALUE" << ";" << endl |
123 | 1 | << endl; |
124 | | |
125 | 1 | fopcode << "extern const std::unordered_map<std::string, BFOpcode> kBfqlName2Opcode;" << endl; |
126 | | |
127 | | // Ending the file. |
128 | 1 | fopcode << kFileEnd; |
129 | 1 | fopcode << "#endif" << endl; |
130 | 1 | fopcode.close(); |
131 | 1 | } |
132 | | |
133 | 1 | void GenerateOpcodeTable(string build_dir) { |
134 | 1 | ofstream fopcode; |
135 | 1 | fopcode.open(build_dir + "/gen_opcode_table.cc"); |
136 | | |
137 | 1 | fopcode << kFileStart |
138 | | // Including header files. |
139 | 1 | << "#include <iostream>" << endl |
140 | 1 | << "#include <unordered_map>" << endl |
141 | 1 | << "#include \"yb/bfql/gen_opcodes.h\"" << endl |
142 | 1 | << endl |
143 | | // Use namespaces. |
144 | 1 | << "using std::string;" << endl |
145 | 1 | << "using std::unordered_map;" << endl |
146 | 1 | << endl |
147 | 1 | << kFileNamespace; |
148 | | |
149 | | // Generating code. |
150 | 1 | fopcode << "// Defining table to map ql_name to opcodes." << endl; |
151 | 1 | fopcode << "const std::unordered_map<string, BFOpcode> kBfqlName2Opcode = {" << endl; |
152 | 91 | for (auto entry : yql2opcode_) { |
153 | | // For overload function only the opcode with max value is inserted. |
154 | | // string ql_name = entry.first; |
155 | | // string opname = strings::Substitute("BFOpcode::OPCODE_$0_$1", entry.first, entry.second); |
156 | | // string opname = entry.second; |
157 | 91 | fopcode << " { \"" << entry.first << "\", " << "BFOpcode::" << entry.second << " }," << endl; |
158 | 91 | } |
159 | 1 | fopcode << "};" << endl; |
160 | | |
161 | | // Ending the file. |
162 | 1 | fopcode << kFileEnd; |
163 | 1 | fopcode.close(); |
164 | 1 | } |
165 | | |
166 | 1 | void GenerateOperators(string build_dir) { |
167 | | // Create header file, "gen_operator.h", for operator declarations. |
168 | 1 | ofstream foper_h; |
169 | 1 | foper_h.open(build_dir + "/gen_operator.h"); |
170 | 1 | foper_h << kFileStart |
171 | 1 | << "#ifndef YB_UTIL_BFQL_GEN_OPERATOR_H_" << endl |
172 | 1 | << "#define YB_UTIL_BFQL_GEN_OPERATOR_H_" << endl |
173 | 1 | << endl |
174 | 1 | << "#include \"yb/bfql/base_operator.h\"" << endl |
175 | 1 | << "#include \"yb/bfql/bfunc.h\"" << endl |
176 | 1 | << endl |
177 | 1 | << "#include <vector>" << endl |
178 | 1 | << endl |
179 | | // Use namespaces. |
180 | 1 | << "using std::vector;" << endl |
181 | 1 | << endl |
182 | 1 | << kFileNamespace; |
183 | | |
184 | 1 | int op_index = 0; |
185 | 159 | for (BFDecl entry : kBFDirectory) { |
186 | | // Define operator class. |
187 | 159 | GenerateDecl(entry, foper_h, operator_ids_[op_index].class_name); |
188 | 159 | op_index++; |
189 | 159 | } |
190 | 1 | foper_h << endl; |
191 | | |
192 | 1 | foper_h << "extern const std::vector<BFOperator::SharedPtr> kBFOperators;" << endl |
193 | 1 | << endl; |
194 | | |
195 | | // Ending the header file. |
196 | 1 | foper_h << kFileEnd |
197 | 1 | << "#endif" << endl; |
198 | 1 | foper_h.close(); |
199 | 1 | } |
200 | | |
201 | 159 | void GenerateDecl(BFDecl entry, ofstream &foper_h, string class_name) { |
202 | | // Declare an operator with the following specification |
203 | | // class OPERATOR_xxx : public BFOperator { |
204 | | // public: |
205 | | // OPERATOR_xxx(...) : BFOperator(...); |
206 | | // static Status Exec(...); -- This takes mutable parameters. |
207 | | // }; |
208 | 159 | foper_h << "class " << class_name << " : public BFOperator {" << endl |
209 | 159 | << " public:" << endl |
210 | 159 | << " " << class_name << "(" << endl |
211 | 159 | << " BFOpcode opcode," << endl |
212 | 159 | << " BFOpcode overloaded_opcode," << endl |
213 | 159 | << " const BFDecl *op_decl)" << endl |
214 | 159 | << " : BFOperator(opcode, overloaded_opcode, op_decl) {" << endl |
215 | 159 | << " }" << endl |
216 | 159 | << endl; |
217 | | |
218 | 159 | GenerateExecFunc(entry, foper_h, BFApiParamOption::kSharedPtr); |
219 | 159 | foper_h << endl; |
220 | 159 | GenerateExecFunc(entry, foper_h, BFApiParamOption::kRawPtr); |
221 | 159 | foper_h << endl; |
222 | 159 | GenerateExecFunc(entry, foper_h, BFApiParamOption::kRefAndRaw); |
223 | | |
224 | | // End operator class. |
225 | 159 | foper_h << "};" << endl |
226 | 159 | << endl; |
227 | 159 | } |
228 | | |
229 | 477 | void GenerateExecFunc(BFDecl entry, ofstream &foper_h, BFApiParamOption param_option) { |
230 | | // Print function call. Four possible cases |
231 | | // - No parameter & no result: func() |
232 | | // |
233 | | // - No result: func(params[0]) |
234 | | // OR func(¶ms[0]) |
235 | | // |
236 | | // - No parameter: func(result) |
237 | | // |
238 | | // - Has parameter & result: func(params[0], params[1], result) |
239 | | // OR func(¶ms[0], ¶ms[1], result) |
240 | | |
241 | | // When arguments are ref, character "&" is used to convert it to pointer. For argument that |
242 | | // are already pointers, no conversion is needed. |
243 | 477 | const char *param_pointer = "params"; |
244 | | |
245 | 477 | switch (param_option) { |
246 | 159 | case BFApiParamOption::kSharedPtr: |
247 | | // For shared_ptr, the parameter would be "const std::shared_ptr<>&". |
248 | 159 | foper_h << " template<typename PType, typename RType>" << endl |
249 | 159 | << " static Status Exec(const std::vector<std::shared_ptr<PType>>& params," << endl |
250 | 159 | << " const std::shared_ptr<RType>& result) {" << endl |
251 | 159 | << " return " |
252 | 159 | << entry.cpp_name() << "("; |
253 | 159 | break; |
254 | 159 | case BFApiParamOption::kRawPtr: |
255 | | // Raw pointer. |
256 | 159 | foper_h << " template<typename PType, typename RType>" << endl |
257 | 159 | << " static Status ExecRaw(const std::vector<PType*>& params," << endl |
258 | 159 | << " RType *result) {" << endl |
259 | 159 | << " return " << entry.cpp_name() << "("; |
260 | 159 | break; |
261 | 159 | case BFApiParamOption::kRefAndRaw: |
262 | | // Reference of object. |
263 | 159 | foper_h << " template<typename PType, typename RType>" << endl |
264 | 159 | << " static Status ExecRefAndRaw(std::vector<PType> *params," << endl |
265 | 159 | << " RType *result) {" << endl; |
266 | | |
267 | 159 | if (entry.param_types().size() == 1 && entry.param_types()[0] == DataType::TYPEARGS95 ) { |
268 | | // If the caller used the kRefAndRaw option, we'll have to convert the params vector from |
269 | | // vector<object> to vector<object*>. |
270 | 10 | param_pointer = "local_params"; |
271 | 10 | foper_h << " const auto count = params->size();" << endl |
272 | 10 | << " std::vector<PType*> local_params(count);" << endl |
273 | 10 | << " for (size_t i = 0; i < count; i++) {" << endl |
274 | 10 | << " local_params[i] = &(*params)[i];" << endl |
275 | 10 | << " }" << endl; |
276 | 10 | foper_h << " return " << entry.cpp_name() << "("; |
277 | 149 | } else { |
278 | 149 | param_pointer = "&(*params)"; |
279 | 149 | foper_h << " return " << entry.cpp_name() << "("; |
280 | 149 | } |
281 | 477 | } |
282 | | |
283 | 477 | string param_end; |
284 | 477 | int pindex = 0; |
285 | 504 | for (DataType param_type : entry.param_types()) { |
286 | 504 | foper_h << param_end; |
287 | 504 | param_end = ", "; |
288 | | |
289 | 504 | if (param_type == DataType::TYPEARGS) { |
290 | | // Break from the loop as we don't allow other argument to be use to TYPE_ARGS. |
291 | 30 | foper_h << param_pointer; |
292 | 30 | break; |
293 | 30 | } |
294 | | |
295 | | // Deref the parameters and pass them. |
296 | 474 | foper_h << param_pointer << "[" << pindex << "]"; |
297 | 474 | pindex++; |
298 | | |
299 | | // SPECIAL CASE: For CAST operator, at compile time, we need two input parameter for type |
300 | | // resolution. However, at runtime, the associated function would take one parameter and |
301 | | // convert the value to proper result, so break the loop here. |
302 | 474 | if (strcmp(entry.ql_name(), "cast") == 0 || |
303 | 474 | strcmp(entry.ql_name(), "cql_cast") == 0378 ) { |
304 | 123 | break; |
305 | 123 | } |
306 | 474 | } |
307 | 477 | if (!QLType::IsUnknown(entry.return_type())) { |
308 | 474 | foper_h << param_end << "result"; |
309 | 474 | } |
310 | 477 | foper_h << ");" << endl; |
311 | | |
312 | | // End of function Exec() and operator class |
313 | 477 | foper_h << " }" << endl; |
314 | 477 | } |
315 | | |
316 | 1 | void GenerateOpspecTable(string build_dir) { |
317 | | // File headers, includes, namespaces, and other declarations. |
318 | 1 | ofstream ftable; |
319 | 1 | ftable.open(build_dir + "/gen_opspec_table.cc"); |
320 | | |
321 | 1 | ftable << kFileStart |
322 | 1 | << "#include \"yb/bfql/base_operator.h\"" << endl |
323 | 1 | << "#include \"yb/bfql/directory.h\"" << endl |
324 | 1 | << "#include \"yb/bfql/gen_operator.h\"" << endl |
325 | 1 | << endl |
326 | 1 | << "#include <iostream>" << endl |
327 | 1 | << "#include <vector>" << endl |
328 | 1 | << "#include <functional>" << endl |
329 | 1 | << endl |
330 | 1 | << "using std::function;" << endl |
331 | 1 | << "using std::make_shared;" << endl |
332 | 1 | << "using std::vector;" << endl |
333 | 1 | << "using std::shared_ptr;" << endl |
334 | 1 | << kFileNamespace; |
335 | | |
336 | | // Generating table of operators. |
337 | 1 | ftable << "const vector<BFOperator::SharedPtr> kBFOperators = {" << endl; |
338 | 1 | int op_index = 0; |
339 | 159 | for (BFDecl entry : kBFDirectory) { |
340 | 159 | const BFClassInfo& bfclass = operator_ids_[op_index]; |
341 | 159 | ftable << " make_shared<" << bfclass.class_name << ">(" |
342 | 159 | << "BFOpcode::" << bfclass.opname << ", " |
343 | 159 | << "BFOpcode::" << bfclass.overloaded_opname << ", " |
344 | 159 | << "&kBFDirectory[" << op_index << "])," << endl; |
345 | 159 | op_index++; |
346 | 159 | } |
347 | 1 | ftable << "};" << endl |
348 | 1 | << endl; |
349 | | |
350 | 1 | ftable << kFileEnd; |
351 | 1 | ftable.close(); |
352 | 1 | } |
353 | | |
354 | 1 | void GenerateExecTable(string build_dir) { |
355 | | // File headers, includes, namespaces, and other declarations. |
356 | 1 | ofstream ftable; |
357 | 1 | ftable.open(build_dir + "/gen_bfunc_table.h"); |
358 | | |
359 | 1 | ftable << kFileStart |
360 | 1 | << "#include <iostream>" << endl |
361 | 1 | << "#include <vector>" << endl |
362 | 1 | << "#include <functional>" << endl |
363 | 1 | << endl |
364 | 1 | << "#include \"yb/bfql/bfunc.h\"" << endl |
365 | 1 | << "#include \"yb/bfql/base_operator.h\"" << endl |
366 | 1 | << "#include \"yb/bfql/gen_operator.h\"" << endl |
367 | 1 | << kFileNamespace; |
368 | | |
369 | | // Generating table of functions whose outputs are shared pointers. |
370 | 1 | ftable << "template<typename PType, typename RType," << endl |
371 | 1 | << " template<typename, typename> class CType," << endl |
372 | 1 | << " template<typename> class AType>" << endl |
373 | 1 | << "const vector<std::function<Status(const std::vector<std::shared_ptr<PType>>&, " |
374 | 1 | << "const std::shared_ptr<RType>&)>>" << endl |
375 | 1 | << " BFExecApi<PType, RType, CType, AType>::kBFExecFuncs = {" << endl; |
376 | 160 | for (size_t op_index = 0; op_index < operator_ids_.size(); op_index++159 ) { |
377 | 159 | const BFClassInfo& bfclass = operator_ids_[op_index]; |
378 | 159 | ftable << " " << bfclass.class_name << "::" << "Exec<PType, RType>," << endl; |
379 | 159 | } |
380 | 1 | ftable << "};" << endl |
381 | 1 | << endl; |
382 | | |
383 | | // Generating table of functions whose outputs are raw pointers. |
384 | 1 | ftable << "template<typename PType, typename RType," << endl |
385 | 1 | << " template<typename, typename> class CType," << endl |
386 | 1 | << " template<typename> class AType>" << endl |
387 | 1 | << "const vector<std::function<Status(const std::vector<PType*>&, RType*)>>" |
388 | 1 | << endl |
389 | 1 | << " BFExecApi<PType, RType, CType, AType>::kBFExecFuncsRaw = {" << endl; |
390 | 160 | for (size_t op_index = 0; op_index < operator_ids_.size(); op_index++159 ) { |
391 | 159 | const BFClassInfo& bfclass = operator_ids_[op_index]; |
392 | 159 | ftable << " " << bfclass.class_name << "::" << "ExecRaw<PType, RType>," << endl; |
393 | 159 | } |
394 | 1 | ftable << "};" << endl |
395 | 1 | << endl; |
396 | | |
397 | | // Generating table of functions whose outputs are raw pointers. |
398 | 1 | ftable << "template<typename PType, typename RType," << endl |
399 | 1 | << " template<typename, typename> class CType," << endl |
400 | 1 | << " template<typename> class AType>" << endl |
401 | 1 | << "const vector<std::function<Status(std::vector<PType>*, RType*)>>" |
402 | 1 | << endl |
403 | 1 | << " BFExecApi<PType, RType, CType, AType>::kBFExecFuncsRefAndRaw = {" << endl; |
404 | 160 | for (size_t op_index = 0; op_index < operator_ids_.size(); op_index++159 ) { |
405 | 159 | const BFClassInfo& bfclass = operator_ids_[op_index]; |
406 | 159 | ftable << " " << bfclass.class_name << "::" << "ExecRefAndRaw<PType, RType>," << endl; |
407 | 159 | } |
408 | 1 | ftable << "};" << endl |
409 | 1 | << endl; |
410 | | |
411 | 1 | ftable << kFileEnd; |
412 | 1 | ftable.close(); |
413 | 1 | } |
414 | | |
415 | | private: |
416 | | map<string, string> yql2opcode_; |
417 | | vector<BFClassInfo> operator_ids_; |
418 | | }; |
419 | | |
420 | | } // namespace bfql |
421 | | } // namespace yb |
422 | | |
423 | | using yb::bfql::BFCodegen; |
424 | | |
425 | 2 | int main(int argc, char** argv) { |
426 | 2 | if (argc < 2) { |
427 | 0 | LOG(FATAL) << "Missing directory"; |
428 | 0 | } |
429 | | |
430 | 2 | BFCodegen coder; |
431 | 2 | string outdir = argv[1]; |
432 | | |
433 | | // Form table of opcodes. |
434 | 2 | coder.GenerateOpcodes(outdir); |
435 | 2 | coder.GenerateOpcodeTable(outdir); |
436 | | |
437 | | // Form table of operator specification. This is used to typecheck during compilation. |
438 | 2 | coder.GenerateOperators(outdir); |
439 | 2 | coder.GenerateOpspecTable(outdir); |
440 | | |
441 | | // Form table of exec function pointer. This template is used for builtin execution. |
442 | 2 | coder.GenerateExecTable(outdir); |
443 | | |
444 | 2 | return 0; |
445 | 2 | } |