YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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(&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
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
}