YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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
0
      : class_name(cname), opname(oname), overloaded_opname(ovl_oname) {
50
0
  }
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
0
  void GenerateOpcodes(string build_dir) {
73
0
    ofstream fopcode;
74
0
    fopcode.open(build_dir + "/gen_opcodes.h");
75
0
    fopcode << kFileStart
76
0
            << "#ifndef YB_UTIL_BFQL_GEN_OPCODES_H_" << endl
77
0
            << "#define YB_UTIL_BFQL_GEN_OPCODES_H_" << endl
78
0
            << endl
79
0
            << "#include <unordered_map>" << endl
80
0
            << endl
81
0
            << kFileNamespace;
82
83
    // Start an enum class with a NO_OP.
84
0
    operator_ids_.reserve(kBFDirectory.size());
85
0
    fopcode << "enum class BFOpcode : int32_t {" << endl;
86
87
    // All builtin operators should be prefix with "OP_".
88
0
    string min_opcode;
89
0
    int op_index = 0;
90
0
    for (BFDecl entry : kBFDirectory) {
91
      // Form the opcode and print it.
92
0
      string current_opcode = entry.bfopcode_name();
93
0
      if (current_opcode.empty()) {
94
0
        current_opcode = strings::Substitute("OPCODE_$0_$1", entry.cpp_name(), op_index);
95
0
      }
96
0
      fopcode << "  " << current_opcode << "," << endl;
97
0
      if (op_index == 0) {
98
0
        min_opcode = current_opcode;
99
0
      }
100
101
      // Find the last generated opcode that this opcode is overloading.
102
0
      string overloaded_opcode;
103
0
      if (yql2opcode_.find(entry.ql_name()) == yql2opcode_.end()) {
104
0
        overloaded_opcode = current_opcode;
105
0
      } else {
106
0
        overloaded_opcode = yql2opcode_[entry.ql_name()];
107
0
      }
108
0
      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
0
      operator_ids_.emplace_back(strings::Substitute("OPERATOR_$0_$1", entry.cpp_name(), op_index),
114
0
                                 current_opcode, overloaded_opcode);
115
0
      op_index++;
116
0
    }
117
0
    fopcode << "  OPCODE_MAX_VALUE" << endl;
118
0
    fopcode << "};" << endl;
119
120
0
    fopcode << "const BFOpcode BFOPCODE_NOOP = BFOpcode::" << min_opcode << ";" << endl
121
0
            << "const BFOpcode BFOPCODE_MIN_VALUE = BFOpcode::" << min_opcode << ";" << endl
122
0
            << "const BFOpcode BFOPCODE_MAX_VALUE = BFOpcode::OPCODE_MAX_VALUE" << ";" << endl
123
0
            << endl;
124
125
0
    fopcode << "extern const std::unordered_map<std::string, BFOpcode> kBfqlName2Opcode;" << endl;
126
127
    // Ending the file.
128
0
    fopcode << kFileEnd;
129
0
    fopcode << "#endif" << endl;
130
0
    fopcode.close();
131
0
  }
132
133
0
  void GenerateOpcodeTable(string build_dir) {
134
0
    ofstream fopcode;
135
0
    fopcode.open(build_dir + "/gen_opcode_table.cc");
136
137
0
    fopcode << kFileStart
138
            // Including header files.
139
0
            << "#include <iostream>" << endl
140
0
            << "#include <unordered_map>" << endl
141
0
            << "#include \"yb/bfql/gen_opcodes.h\"" << endl
142
0
            << endl
143
            // Use namespaces.
144
0
            << "using std::string;" << endl
145
0
            << "using std::unordered_map;" << endl
146
0
            << endl
147
0
            << kFileNamespace;
148
149
    // Generating code.
150
0
    fopcode << "// Defining table to map ql_name to opcodes." << endl;
151
0
    fopcode << "const std::unordered_map<string, BFOpcode> kBfqlName2Opcode = {" << endl;
152
0
    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
0
      fopcode << "  { \"" << entry.first << "\", " << "BFOpcode::" << entry.second << " }," << endl;
158
0
    }
159
0
    fopcode << "};" << endl;
160
161
    // Ending the file.
162
0
    fopcode << kFileEnd;
163
0
    fopcode.close();
164
0
  }
165
166
0
  void GenerateOperators(string build_dir) {
167
    // Create header file, "gen_operator.h", for operator declarations.
168
0
    ofstream foper_h;
169
0
    foper_h.open(build_dir + "/gen_operator.h");
170
0
    foper_h << kFileStart
171
0
            << "#ifndef YB_UTIL_BFQL_GEN_OPERATOR_H_" << endl
172
0
            << "#define YB_UTIL_BFQL_GEN_OPERATOR_H_" << endl
173
0
            << endl
174
0
            << "#include \"yb/bfql/base_operator.h\"" << endl
175
0
            << "#include \"yb/bfql/bfunc.h\"" << endl
176
0
            << endl
177
0
            << "#include <vector>" << endl
178
0
            << endl
179
            // Use namespaces.
180
0
            << "using std::vector;" << endl
181
0
            << endl
182
0
            << kFileNamespace;
183
184
0
    int op_index = 0;
185
0
    for (BFDecl entry : kBFDirectory) {
186
      // Define operator class.
187
0
      GenerateDecl(entry, foper_h, operator_ids_[op_index].class_name);
188
0
      op_index++;
189
0
    }
190
0
    foper_h << endl;
191
192
0
    foper_h << "extern const std::vector<BFOperator::SharedPtr> kBFOperators;" << endl
193
0
            << endl;
194
195
    // Ending the header file.
196
0
    foper_h << kFileEnd
197
0
            << "#endif" << endl;
198
0
    foper_h.close();
199
0
  }
200
201
0
  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
0
    foper_h << "class " << class_name << " : public BFOperator {" << endl
209
0
            << " public:" << endl
210
0
            << "  " << class_name << "(" << endl
211
0
            << "    BFOpcode opcode," << endl
212
0
            << "    BFOpcode overloaded_opcode," << endl
213
0
            << "    const BFDecl *op_decl)" << endl
214
0
            << "      : BFOperator(opcode, overloaded_opcode, op_decl) {" << endl
215
0
            << "  }" << endl
216
0
            << endl;
217
218
0
    GenerateExecFunc(entry, foper_h, BFApiParamOption::kSharedPtr);
219
0
    foper_h << endl;
220
0
    GenerateExecFunc(entry, foper_h, BFApiParamOption::kRawPtr);
221
0
    foper_h << endl;
222
0
    GenerateExecFunc(entry, foper_h, BFApiParamOption::kRefAndRaw);
223
224
    // End operator class.
225
0
    foper_h << "};" << endl
226
0
            << endl;
227
0
  }
228
229
0
  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(&params[0])
235
    //
236
    // - No parameter:                 func(result)
237
    //
238
    // - Has parameter & result:       func(params[0], params[1], result)
239
    //                              OR func(&params[0], &params[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
0
    const char *param_pointer = "params";
244
245
0
    switch (param_option) {
246
0
      case BFApiParamOption::kSharedPtr:
247
        // For shared_ptr, the parameter would be "const std::shared_ptr<>&".
248
0
        foper_h << "  template<typename PType, typename RType>" << endl
249
0
                << "  static Status Exec(const std::vector<std::shared_ptr<PType>>& params," << endl
250
0
                << "                     const std::shared_ptr<RType>& result) {" << endl
251
0
                << "    return "
252
0
                << entry.cpp_name() << "(";
253
0
        break;
254
0
      case BFApiParamOption::kRawPtr:
255
        // Raw pointer.
256
0
        foper_h << "  template<typename PType, typename RType>" << endl
257
0
                << "  static Status ExecRaw(const std::vector<PType*>& params," << endl
258
0
                << "                        RType *result) {" << endl
259
0
                << "    return " << entry.cpp_name() << "(";
260
0
        break;
261
0
      case BFApiParamOption::kRefAndRaw:
262
        // Reference of object.
263
0
        foper_h << "  template<typename PType, typename RType>" << endl
264
0
                << "  static Status ExecRefAndRaw(std::vector<PType> *params," << endl
265
0
                << "                              RType *result) {" << endl;
266
267
0
        if (entry.param_types().size() == 1 && entry.param_types()[0] == DataType::TYPEARGS) {
268
          // If the caller used the kRefAndRaw option, we'll have to convert the params vector from
269
          // vector<object> to vector<object*>.
270
0
          param_pointer = "local_params";
271
0
          foper_h << "    const auto count = params->size();" << endl
272
0
                  << "    std::vector<PType*> local_params(count);" << endl
273
0
                  << "    for (size_t i = 0; i < count; i++) {" << endl
274
0
                  << "      local_params[i] = &(*params)[i];" << endl
275
0
                  << "    }" << endl;
276
0
          foper_h << "    return " << entry.cpp_name() << "(";
277
0
        } else {
278
0
          param_pointer = "&(*params)";
279
0
          foper_h << "    return " << entry.cpp_name() << "(";
280
0
        }
281
0
    }
282
283
0
    string param_end;
284
0
    int pindex = 0;
285
0
    for (DataType param_type : entry.param_types()) {
286
0
      foper_h << param_end;
287
0
      param_end = ", ";
288
289
0
      if (param_type == DataType::TYPEARGS) {
290
        // Break from the loop as we don't allow other argument to be use to TYPE_ARGS.
291
0
        foper_h << param_pointer;
292
0
        break;
293
0
      }
294
295
      // Deref the parameters and pass them.
296
0
      foper_h << param_pointer << "[" << pindex << "]";
297
0
      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
0
      if (strcmp(entry.ql_name(), "cast") == 0 ||
303
0
          strcmp(entry.ql_name(), "cql_cast") == 0) {
304
0
        break;
305
0
      }
306
0
    }
307
0
    if (!QLType::IsUnknown(entry.return_type())) {
308
0
      foper_h << param_end << "result";
309
0
    }
310
0
    foper_h << ");" << endl;
311
312
    // End of function Exec() and operator class
313
0
    foper_h << "  }" << endl;
314
0
  }
315
316
0
  void GenerateOpspecTable(string build_dir) {
317
    // File headers, includes, namespaces, and other declarations.
318
0
    ofstream ftable;
319
0
    ftable.open(build_dir + "/gen_opspec_table.cc");
320
321
0
    ftable << kFileStart
322
0
           << "#include \"yb/bfql/base_operator.h\"" << endl
323
0
           << "#include \"yb/bfql/directory.h\"" << endl
324
0
           << "#include \"yb/bfql/gen_operator.h\"" << endl
325
0
           << endl
326
0
           << "#include <iostream>" << endl
327
0
           << "#include <vector>" << endl
328
0
           << "#include <functional>" << endl
329
0
           << endl
330
0
           << "using std::function;" << endl
331
0
           << "using std::make_shared;" << endl
332
0
           << "using std::vector;" << endl
333
0
           << "using std::shared_ptr;" << endl
334
0
           << kFileNamespace;
335
336
    // Generating table of operators.
337
0
    ftable << "const vector<BFOperator::SharedPtr> kBFOperators = {" << endl;
338
0
    int op_index = 0;
339
0
    for (BFDecl entry : kBFDirectory) {
340
0
      const BFClassInfo& bfclass = operator_ids_[op_index];
341
0
      ftable << "  make_shared<" << bfclass.class_name << ">("
342
0
             << "BFOpcode::" << bfclass.opname << ", "
343
0
             << "BFOpcode::" << bfclass.overloaded_opname << ", "
344
0
             << "&kBFDirectory[" << op_index << "])," << endl;
345
0
      op_index++;
346
0
    }
347
0
    ftable << "};" << endl
348
0
           << endl;
349
350
0
    ftable << kFileEnd;
351
0
    ftable.close();
352
0
  }
353
354
0
  void GenerateExecTable(string build_dir) {
355
    // File headers, includes, namespaces, and other declarations.
356
0
    ofstream ftable;
357
0
    ftable.open(build_dir + "/gen_bfunc_table.h");
358
359
0
    ftable << kFileStart
360
0
           << "#include <iostream>" << endl
361
0
           << "#include <vector>" << endl
362
0
           << "#include <functional>" << endl
363
0
           << endl
364
0
           << "#include \"yb/bfql/bfunc.h\"" << endl
365
0
           << "#include \"yb/bfql/base_operator.h\"" << endl
366
0
           << "#include \"yb/bfql/gen_operator.h\"" << endl
367
0
           << kFileNamespace;
368
369
    // Generating table of functions whose outputs are shared pointers.
370
0
    ftable << "template<typename PType, typename RType," << endl
371
0
           << "         template<typename, typename> class CType," << endl
372
0
           << "         template<typename> class AType>" << endl
373
0
           << "const vector<std::function<Status(const std::vector<std::shared_ptr<PType>>&, "
374
0
           << "const std::shared_ptr<RType>&)>>" << endl
375
0
           << "    BFExecApi<PType, RType, CType, AType>::kBFExecFuncs = {" << endl;
376
0
    for (size_t op_index = 0; op_index < operator_ids_.size(); op_index++) {
377
0
      const BFClassInfo& bfclass = operator_ids_[op_index];
378
0
      ftable << "  " << bfclass.class_name << "::" << "Exec<PType, RType>," << endl;
379
0
    }
380
0
    ftable << "};" << endl
381
0
           << endl;
382
383
    // Generating table of functions whose outputs are raw pointers.
384
0
    ftable << "template<typename PType, typename RType," << endl
385
0
           << "         template<typename, typename> class CType," << endl
386
0
           << "         template<typename> class AType>" << endl
387
0
           << "const vector<std::function<Status(const std::vector<PType*>&, RType*)>>"
388
0
           << endl
389
0
           << "    BFExecApi<PType, RType, CType, AType>::kBFExecFuncsRaw = {" << endl;
390
0
    for (size_t op_index = 0; op_index < operator_ids_.size(); op_index++) {
391
0
      const BFClassInfo& bfclass = operator_ids_[op_index];
392
0
      ftable << "  " << bfclass.class_name << "::" << "ExecRaw<PType, RType>," << endl;
393
0
    }
394
0
    ftable << "};" << endl
395
0
           << endl;
396
397
    // Generating table of functions whose outputs are raw pointers.
398
0
    ftable << "template<typename PType, typename RType," << endl
399
0
           << "         template<typename, typename> class CType," << endl
400
0
           << "         template<typename> class AType>" << endl
401
0
           << "const vector<std::function<Status(std::vector<PType>*, RType*)>>"
402
0
           << endl
403
0
           << "    BFExecApi<PType, RType, CType, AType>::kBFExecFuncsRefAndRaw = {" << endl;
404
0
    for (size_t op_index = 0; op_index < operator_ids_.size(); op_index++) {
405
0
      const BFClassInfo& bfclass = operator_ids_[op_index];
406
0
      ftable << "  " << bfclass.class_name << "::" << "ExecRefAndRaw<PType, RType>," << endl;
407
0
    }
408
0
    ftable << "};" << endl
409
0
           << endl;
410
411
0
    ftable << kFileEnd;
412
0
    ftable.close();
413
0
  }
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
int main(int argc,  char** argv) {
426
  if (argc < 2) {
427
    LOG(FATAL) << "Missing directory";
428
  }
429
430
  BFCodegen coder;
431
  string outdir = argv[1];
432
433
  // Form table of opcodes.
434
  coder.GenerateOpcodes(outdir);
435
  coder.GenerateOpcodeTable(outdir);
436
437
  // Form table of operator specification. This is used to typecheck during compilation.
438
  coder.GenerateOperators(outdir);
439
  coder.GenerateOpspecTable(outdir);
440
441
  // Form table of exec function pointer. This template is used for builtin execution.
442
  coder.GenerateExecTable(outdir);
443
444
  return 0;
445
}