YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/bfpg/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/bfpg/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 bfpg {
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 bfpg {\n"
40
  "\n";
41
42
static const char *kFileEnd =
43
  "\n"
44
  "} // namespace bfpg\n"
45
  "} // namespace yb\n";
46
47
struct BFClassInfo {
48
  BFClassInfo(const string& cname, const string& oname, const string& ovl_oname)
49
60
      : class_name(cname), opname(oname), overloaded_opname(ovl_oname) {
50
60
  }
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_BFPG_GEN_OPCODES_H_" << endl
77
1
            << "#define YB_UTIL_BFPG_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
60
    for (BFDecl entry : kBFDirectory) {
91
      // Form the opcode and print it.
92
60
      string current_opcode = strings::Substitute("OPCODE_$0_$1", entry.cpp_name(), op_index);
93
60
      fopcode << "  " << current_opcode << "," << endl;
94
60
      if (op_index == 0) {
95
1
        min_opcode = current_opcode;
96
1
      }
97
98
      // Find the last generated opcode that this opcode is overloading.
99
60
      string overloaded_opcode;
100
60
      if (yql2opcode_.find(entry.ql_name()) == yql2opcode_.end()) {
101
15
        overloaded_opcode = current_opcode;
102
45
      } else {
103
45
        overloaded_opcode = yql2opcode_[entry.ql_name()];
104
45
      }
105
60
      yql2opcode_[entry.ql_name()] = current_opcode;
106
107
      // Use opcode enum value to create unique operator name. This operator keeps the last
108
      // overloaded opcode to form a chain between overloading opcodes for the same ql_name.
109
      // Using this chain we can track all opcodes that are mapped to the same ql_name.
110
60
      operator_ids_.emplace_back(strings::Substitute("OPERATOR_$0_$1", entry.cpp_name(), op_index),
111
60
                                 current_opcode, overloaded_opcode);
112
60
      op_index++;
113
60
    }
114
1
    fopcode << "  OPCODE_MAX_VALUE" << endl;
115
1
    fopcode << "};" << endl;
116
117
1
    fopcode << "const BFOpcode BFOPCODE_NOOP = BFOpcode::" << min_opcode << ";" << endl
118
1
            << "const BFOpcode BFOPCODE_MIN_VALUE = BFOpcode::" << min_opcode << ";" << endl
119
1
            << "const BFOpcode BFOPCODE_MAX_VALUE = BFOpcode::OPCODE_MAX_VALUE" << ";" << endl
120
1
            << endl;
121
122
1
    fopcode << "extern const std::unordered_map<std::string, BFOpcode> kBfPgsqlName2Opcode;"
123
1
            << endl;
124
125
    // Ending the file.
126
1
    fopcode << kFileEnd;
127
1
    fopcode << "#endif" << endl;
128
1
    fopcode.close();
129
1
  }
130
131
1
  void GenerateOpcodeTable(string build_dir) {
132
1
    ofstream fopcode;
133
1
    fopcode.open(build_dir + "/gen_opcode_table.cc");
134
135
1
    fopcode << kFileStart
136
            // Including header files.
137
1
            << "#include <iostream>" << endl
138
1
            << "#include <unordered_map>" << endl
139
1
            << "#include \"yb/bfpg/gen_opcodes.h\"" << endl
140
1
            << endl
141
            // Use namespaces.
142
1
            << "using std::string;" << endl
143
1
            << "using std::unordered_map;" << endl
144
1
            << endl
145
1
            << kFileNamespace;
146
147
    // Generating code.
148
1
    fopcode << "// Defining table to map ql_name to opcodes." << endl;
149
1
    fopcode << "const std::unordered_map<string, BFOpcode> kBfPgsqlName2Opcode = {" << endl;
150
15
    for (auto entry : yql2opcode_) {
151
      // For overload function only the opcode with max value is inserted.
152
      // string ql_name = entry.first;
153
      // string opname = strings::Substitute("BFOpcode::OPCODE_$0_$1", entry.first, entry.second);
154
      // string opname = entry.second;
155
15
      fopcode << "  { \"" << entry.first << "\", " << "BFOpcode::" << entry.second << " }," << endl;
156
15
    }
157
1
    fopcode << "};" << endl;
158
159
    // Ending the file.
160
1
    fopcode << kFileEnd;
161
1
    fopcode.close();
162
1
  }
163
164
1
  void GenerateOperators(string build_dir) {
165
    // Create header file, "gen_operator.h", for operator declarations.
166
1
    ofstream foper_h;
167
1
    foper_h.open(build_dir + "/gen_operator.h");
168
1
    foper_h << kFileStart
169
1
            << "#ifndef YB_UTIL_BFPG_GEN_OPERATOR_H_" << endl
170
1
            << "#define YB_UTIL_BFPG_GEN_OPERATOR_H_" << endl
171
1
            << endl
172
1
            << "#include <vector>" << endl
173
1
            << endl
174
1
            << "#include \"yb/bfpg/base_operator.h\"" << endl
175
1
            << "#include \"yb/bfpg/bfunc.h\"" << endl
176
1
            << "#include \"yb/bfpg/bfunc_convert.h\"" << endl
177
1
            << "#include \"yb/bfpg/bfunc_standard.h\"" << endl
178
1
            << "#include \"yb/util/status.h\"" << endl
179
1
            << endl
180
            // Use namespaces.
181
1
            << "using std::vector;" << endl
182
1
            << endl
183
1
            << kFileNamespace;
184
185
1
    int op_index = 0;
186
60
    for (BFDecl entry : kBFDirectory) {
187
      // Define operator class.
188
60
      GenerateDecl(entry, foper_h, operator_ids_[op_index].class_name);
189
60
      op_index++;
190
60
    }
191
1
    foper_h << endl;
192
193
1
    foper_h << "extern const std::vector<BFOperator::SharedPtr> kBFOperators;" << endl
194
1
            << endl;
195
196
    // Ending the header file.
197
1
    foper_h << kFileEnd
198
1
            << "#endif" << endl;
199
1
    foper_h.close();
200
1
  }
201
202
60
  void GenerateDecl(BFDecl entry, ofstream &foper_h, string class_name) {
203
    // Declare an operator with the following specification
204
    // class OPERATOR_xxx : public BFOperator {
205
    //  public:
206
    //   OPERATOR_xxx(...) : BFOperator(...);
207
    //   static Status Exec(...); -- This takes mutable parameters.
208
    // };
209
60
    foper_h << "class " << class_name << " : public BFOperator {" << endl
210
60
            << " public:" << endl
211
60
            << "  " << class_name << "(" << endl
212
60
            << "    BFOpcode opcode," << endl
213
60
            << "    BFOpcode overloaded_opcode," << endl
214
60
            << "    const BFDecl *op_decl)" << endl
215
60
            << "      : BFOperator(opcode, overloaded_opcode, op_decl) {" << endl
216
60
            << "  }" << endl
217
60
            << endl;
218
219
60
    GenerateExecFunc(entry, foper_h, BFApiParamOption::kSharedPtr);
220
60
    foper_h << endl;
221
60
    GenerateExecFunc(entry, foper_h, BFApiParamOption::kRawPtr);
222
60
    foper_h << endl;
223
60
    GenerateExecFunc(entry, foper_h, BFApiParamOption::kRefAndRaw);
224
225
    // End operator class.
226
60
    foper_h << "};" << endl
227
60
            << endl;
228
60
  }
229
230
180
  void GenerateExecFunc(BFDecl entry, ofstream &foper_h, BFApiParamOption param_option) {
231
    // Print function call. Four possible cases
232
    // - No parameter & no result:     func()
233
    //
234
    // - No result:                    func(params[0])
235
    //                              OR func(&params[0])
236
    //
237
    // - No parameter:                 func(result)
238
    //
239
    // - Has parameter & result:       func(params[0], params[1], result)
240
    //                              OR func(&params[0], &params[1], result)
241
242
    // When arguments are ref, character "&" is used to convert it to pointer. For argument that
243
    // are already pointers, no conversion is needed.
244
180
    const char *param_pointer = "params";
245
246
180
    switch (param_option) {
247
60
      case BFApiParamOption::kSharedPtr:
248
        // For shared_ptr, the parameter would be "const std::shared_ptr<>&".
249
60
        foper_h << "  template<typename PType, typename RType>" << endl
250
60
                << "  static Status Exec(const std::vector<std::shared_ptr<PType>>& params," << endl
251
60
                << "                     const std::shared_ptr<RType>& result) {" << endl
252
60
                << "    return "
253
60
                << entry.cpp_name() << "(";
254
60
        break;
255
60
      case BFApiParamOption::kRawPtr:
256
        // Raw pointer.
257
60
        foper_h << "  template<typename PType, typename RType>" << endl
258
60
                << "  static Status ExecRaw(const std::vector<PType*>& params," << endl
259
60
                << "                        RType *result) {" << endl
260
60
                << "    return " << entry.cpp_name() << "(";
261
60
        break;
262
60
      case BFApiParamOption::kRefAndRaw:
263
        // Reference of object.
264
60
        foper_h << "  template<typename PType, typename RType>" << endl
265
60
                << "  static Status ExecRefAndRaw(std::vector<PType> *params," << endl
266
60
                << "                              RType *result) {" << endl;
267
268
60
        if (entry.param_types().size() == 1 && 
entry.param_types()[0] == DataType::TYPEARGS22
) {
269
          // If the caller used the kRefAndRaw option, we'll have to convert the params vector from
270
          // vector<object> to vector<object*>.
271
0
          param_pointer = "local_params";
272
0
          foper_h << "    const auto count = params->size();" << endl
273
0
                  << "    std::vector<PType*> local_params(count);" << endl
274
0
                  << "    for (size_t i = 0; i < count; i++) {" << endl
275
0
                  << "      local_params[i] = &(*params)[i];" << endl
276
0
                  << "    }" << endl;
277
0
          foper_h << "    return " << entry.cpp_name() << "(";
278
60
        } else {
279
60
          param_pointer = "&(*params)";
280
60
          foper_h << "    return " << entry.cpp_name() << "(";
281
60
        }
282
180
    }
283
284
180
    string param_end;
285
180
    int pindex = 0;
286
198
    for (DataType param_type : entry.param_types()) {
287
198
      foper_h << param_end;
288
198
      param_end = ", ";
289
290
198
      if (param_type == DataType::TYPEARGS) {
291
        // Break from the loop as we don't allow other argument to be use to TYPE_ARGS.
292
0
        foper_h << param_pointer;
293
0
        break;
294
0
      }
295
296
      // Deref the parameters and pass them.
297
198
      foper_h << param_pointer << "[" << pindex << "]";
298
198
      pindex++;
299
300
      // SPECIAL CASE: For CAST operator, at compile time, we need two input parameter for type
301
      // resolution. However, at runtime, the associated function would take one parameter and
302
      // convert the value to proper result, so break the loop here.
303
198
      if (strcmp(entry.ql_name(), "cast") == 0) {
304
84
        break;
305
84
      }
306
198
    }
307
180
    if (!QLType::IsUnknown(entry.return_type())) {
308
177
      foper_h << param_end << "result";
309
177
    }
310
180
    foper_h << ");" << endl;
311
312
    // End of function Exec() and operator class
313
180
    foper_h << "  }" << endl;
314
180
  }
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/bfpg/base_operator.h\"" << endl
323
1
           << "#include \"yb/bfpg/directory.h\"" << endl
324
1
           << "#include \"yb/bfpg/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
60
    for (BFDecl entry : kBFDirectory) {
340
60
      const BFClassInfo& bfclass = operator_ids_[op_index];
341
60
      ftable << "  make_shared<" << bfclass.class_name << ">("
342
60
             << "BFOpcode::" << bfclass.opname << ", "
343
60
             << "BFOpcode::" << bfclass.overloaded_opname << ", "
344
60
             << "&kBFDirectory[" << op_index << "])," << endl;
345
60
      op_index++;
346
60
    }
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/bfpg/bfunc.h\"" << endl
365
1
           << "#include \"yb/bfpg/base_operator.h\"" << endl
366
1
           << "#include \"yb/bfpg/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
61
    for (size_t op_index = 0; op_index < operator_ids_.size(); 
op_index++60
) {
377
60
      const BFClassInfo& bfclass = operator_ids_[op_index];
378
60
      ftable << "  " << bfclass.class_name << "::" << "Exec<PType, RType>," << endl;
379
60
    }
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
61
    for (size_t op_index = 0; op_index < operator_ids_.size(); 
op_index++60
) {
391
60
      const BFClassInfo& bfclass = operator_ids_[op_index];
392
60
      ftable << "  " << bfclass.class_name << "::" << "ExecRaw<PType, RType>," << endl;
393
60
    }
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
61
    for (size_t op_index = 0; op_index < operator_ids_.size(); 
op_index++60
) {
405
60
      const BFClassInfo& bfclass = operator_ids_[op_index];
406
60
      ftable << "  " << bfclass.class_name << "::" << "ExecRefAndRaw<PType, RType>," << endl;
407
60
    }
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 bfpg
421
} // namespace yb
422
423
using yb::bfpg::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
}