souffle  2.0.2-371-g6315b36
Public Member Functions | Protected Member Functions | Private Attributes
souffle::synthesiser::Synthesiser Class Reference

A RAM synthesiser: synthesises a C++ program from a RAM program. More...

#include <Synthesiser.h>

Collaboration diagram for souffle::synthesiser::Synthesiser:
Collaboration graph

Public Member Functions

void generateCode (std::ostream &os, const std::string &id, bool &withSharedLibrary)
 Generate code. More...
 
ram::TranslationUnitgetTranslationUnit ()
 Get translation unit. More...
 
 Synthesiser (ram::TranslationUnit &tUnit)
 
virtual ~Synthesiser ()=default
 

Protected Member Functions

const std::string convertRamIdent (const std::string &name)
 Convert RAM identifier. More...
 
void emitCode (std::ostream &out, const ram::Statement &stmt)
 Generate code. More...
 
void generateRelationTypeStruct (std::ostream &out, Own< Relation > relationType)
 Get relation struct definition. More...
 
const std::string getOpContextName (const ram::Relation &rel)
 Get context name. More...
 
const RecordTablegetRecordTable ()
 Get record table. More...
 
std::set< const ram::Relation * > getReferencedRelations (const ram::Operation &op)
 Get referenced relations. More...
 
const std::string getRelationName (const ram::Relation &rel)
 Get relation name. More...
 
const std::string getRelationName (const ram::Relation *rel)
 
const ram::Relationlookup (const std::string &relName)
 Lookup relation by relation name. More...
 
unsigned lookupFreqIdx (const std::string &txt)
 Lookup frequency counter. More...
 
size_t lookupReadIdx (const std::string &txt)
 Lookup read counter. More...
 

Private Attributes

std::map< const std::string, const std::string > identifiers
 RAM identifier to C++ identifier map. More...
 
std::map< std::string, unsigned > idxMap
 Frequency profiling of searches. More...
 
std::map< std::string, size_t > neIdxMap
 Frequency profiling of non-existence checks. More...
 
RecordTable recordTable
 Record Table. More...
 
std::map< std::string, const ram::Relation * > relationMap
 Relation map. More...
 
ram::TranslationUnittranslationUnit
 RAM translation unit. More...
 
std::set< std::string > typeCache
 Cache for generated types for relations. More...
 

Detailed Description

A RAM synthesiser: synthesises a C++ program from a RAM program.

Definition at line 45 of file Synthesiser.h.

Constructor & Destructor Documentation

◆ Synthesiser()

souffle::synthesiser::Synthesiser::Synthesiser ( ram::TranslationUnit tUnit)
inlineexplicit

Definition at line 111 of file Synthesiser.h.

◆ ~Synthesiser()

virtual souffle::synthesiser::Synthesiser::~Synthesiser ( )
virtualdefault

Member Function Documentation

◆ convertRamIdent()

const std::string souffle::synthesiser::Synthesiser::convertRamIdent ( const std::string &  name)
protected

Convert RAM identifier.

Definition at line 153 of file Synthesiser.cpp.

154  {
155  if ((isalnum(name.at(i)) != 0) || name.at(i) == '_') {
156  break;
157  }
158  }
159  std::string id;
160  for (auto ch : std::to_string(identifiers.size() + 1) + '_' + name.substr(i)) {
161  // alphanumeric characters are allowed
162  if (isalnum(ch) != 0) {
163  id += ch;
164  }
165  // all other characters are replaced by an underscore, except when
166  // the previous character was an underscore as double underscores
167  // in identifiers are reserved by the standard
168  else if (id.empty() || id.back() != '_') {
169  id += '_';
170  }
171  }
172  // most compilers have a limit of 2048 characters (if they have a limit at all) for
173  // identifiers; we use half of that for safety
174  id = id.substr(0, 1024);
175  identifiers.insert(std::make_pair(name, id));
176  return id;
177 }
178 
179 /** Get relation name */
180 const std::string Synthesiser::getRelationName(const ram::Relation& rel) {
181  return "rel_" + convertRamIdent(rel.getName());
182 }
183 

References i.

◆ emitCode()

void souffle::synthesiser::Synthesiser::emitCode ( std::ostream &  out,
const ram::Statement stmt 
)
protected

Generate code.

Unary Functor Operators

numeric coersions follow C++ semantics.

Binary Functor Operators

Ternary Functor Operators

Definition at line 230 of file Synthesiser.cpp.

250  :
251  CodeEmitter(Synthesiser& syn) : synthesiser(syn) {
252  rec = [&](auto& out, const auto* value) {
253  out << "ramBitCast(";
254  visit(*value, out);
255  out << ")";
256  };
257  recWithDefault = [&](auto& out, const auto* value) {
258  if (!isUndefValue(&*value)) {
259  rec(out, value);
260  } else {
261  out << "0";
262  }
263  };
264  }
265 
266  std::pair<std::stringstream, std::stringstream> getPaddedRangeBounds(const ram::Relation& rel,
267  const std::vector<Expression*>& rangePatternLower,
268  const std::vector<Expression*>& rangePatternUpper) {
269  std::stringstream low;
270  std::stringstream high;
271 
272  // making this distinction for provenance
273  size_t realArity = rel.getArity();
274  size_t arity = rangePatternLower.size();
275 
276  low << "Tuple<RamDomain," << realArity << ">{{";
277  high << "Tuple<RamDomain," << realArity << ">{{";
278 
279  for (size_t column = 0; column < arity; column++) {
280  std::string supremum;
281  std::string infimum;
282 
283  switch (rel.getAttributeTypes()[column][0]) {
284  case 'f':
285  supremum = "ramBitCast<RamDomain>(MIN_RAM_FLOAT)";
286  infimum = "ramBitCast<RamDomain>(MAX_RAM_FLOAT)";
287  break;
288  case 'u':
289  supremum = "ramBitCast<RamDomain>(MIN_RAM_UNSIGNED)";
290  infimum = "ramBitCast<RamDomain>(MAX_RAM_UNSIGNED)";
291  break;
292  default:
293  supremum = "ramBitCast<RamDomain>(MIN_RAM_SIGNED)";
294  infimum = "ramBitCast<RamDomain>(MAX_RAM_SIGNED)";
295  }
296 
297  // if we have an inequality where either side is not set
298  if (column != 0) {
299  low << ", ";
300  high << ", ";
301  }
302 
303  if (isUndefValue(rangePatternLower[column])) {
304  low << supremum;
305  } else {
306  low << "ramBitCast(";
307  visit(rangePatternLower[column], low);
308  low << ")";
309  }
310 
311  if (isUndefValue(rangePatternUpper[column])) {
312  high << infimum;
313  } else {
314  high << "ramBitCast(";
315  visit(rangePatternUpper[column], high);
316  high << ")";
317  }
318  }
319 
320  low << "}}";
321  high << "}}";
322  return std::make_pair(std::move(low), std::move(high));
323  }
324 
325  // -- relation statements --
326 
327  void visitIO(const IO& io, std::ostream& out) override {
328  PRINT_BEGIN_COMMENT(out);
329 
330  // print directives as C++ initializers
331  auto printDirectives = [&](const std::map<std::string, std::string>& registry) {
332  auto cur = registry.begin();
333  if (cur == registry.end()) {
334  return;
335  }
336  out << "{{\"" << cur->first << "\",\"" << escape(cur->second) << "\"}";
337  ++cur;
338  for (; cur != registry.end(); ++cur) {
339  out << ",{\"" << cur->first << "\",\"" << escape(cur->second) << "\"}";
340  }
341  out << '}';
342  };
343 
344  const auto& directives = io.getDirectives();
345  const std::string& op = io.get("operation");
346  out << "if (performIO) {\n";
347 
348  // get some table details
349  if (op == "input") {
350  out << "try {";
351  out << "std::map<std::string, std::string> directiveMap(";
352  printDirectives(directives);
353  out << ");\n";
354  out << R"_(if (!inputDirectory.empty()) {)_";
355  out << R"_(directiveMap["fact-dir"] = inputDirectory;)_";
356  out << "}\n";
357  out << "IOSystem::getInstance().getReader(";
358  out << "directiveMap, symTable, recordTable";
359  out << ")->readAll(*" << synthesiser.getRelationName(synthesiser.lookup(io.getRelation()));
360  out << ");\n";
361  out << "} catch (std::exception& e) {std::cerr << \"Error loading data: \" << e.what() "
362  "<< "
363  "'\\n';}\n";
364  } else if (op == "output" || op == "printsize") {
365  out << "try {";
366  out << "std::map<std::string, std::string> directiveMap(";
367  printDirectives(directives);
368  out << ");\n";
369  out << R"_(if (!outputDirectory.empty()) {)_";
370  out << R"_(directiveMap["output-dir"] = outputDirectory;)_";
371  out << "}\n";
372  out << "IOSystem::getInstance().getWriter(";
373  out << "directiveMap, symTable, recordTable";
374  out << ")->writeAll(*" << synthesiser.getRelationName(synthesiser.lookup(io.getRelation()))
375  << ");\n";
376  out << "} catch (std::exception& e) {std::cerr << e.what();exit(1);}\n";
377  } else {
378  assert("Wrong i/o operation");
379  }
380  out << "}\n";
381  PRINT_END_COMMENT(out);
382  }
383 
384  void visitQuery(const Query& query, std::ostream& out) override {
385  PRINT_BEGIN_COMMENT(out);
386 
387  // split terms of conditions of outer filter operation
388  // into terms that require a context and terms that
389  // do not require a context
390  const Operation* next = &query.getOperation();
391  VecOwn<Condition> requireCtx;
392  VecOwn<Condition> freeOfCtx;
393  if (const auto* filter = dynamic_cast<const Filter*>(&query.getOperation())) {
394  next = &filter->getOperation();
395  // Check terms of outer filter operation whether they can be pushed before
396  // the context-generation for speed imrovements
397  auto conditions = toConjunctionList(&filter->getCondition());
398  for (auto const& cur : conditions) {
399  bool needContext = false;
400  visitDepthFirst(*cur, [&](const ExistenceCheck&) { needContext = true; });
401  visitDepthFirst(*cur, [&](const ProvenanceExistenceCheck&) { needContext = true; });
402  if (needContext) {
403  requireCtx.push_back(souffle::clone(cur));
404  } else {
405  freeOfCtx.push_back(souffle::clone(cur));
406  }
407  }
408  // discharge conditions that do not require a context
409  if (freeOfCtx.size() > 0) {
410  out << "if(";
411  visit(*toCondition(freeOfCtx), out);
412  out << ") {\n";
413  }
414  }
415 
416  // outline each search operation to improve compilation time
417  out << "[&]()";
418  // enclose operation in its own scope
419  out << "{\n";
420 
421  // check whether loop nest can be parallelized
422  bool isParallel = false;
423  visitDepthFirst(*next, [&](const AbstractParallel&) { isParallel = true; });
424 
425  // reset preamble
426  preamble.str("");
427  preamble.clear();
428  preambleIssued = false;
429 
430  // create operation contexts for this operation
431  for (const ram::Relation* rel : synthesiser.getReferencedRelations(query.getOperation())) {
432  preamble << "CREATE_OP_CONTEXT(" << synthesiser.getOpContextName(*rel);
433  preamble << "," << synthesiser.getRelationName(*rel);
434  preamble << "->createContext());\n";
435  }
436 
437  // discharge conditions that require a context
438  if (isParallel) {
439  if (requireCtx.size() > 0) {
440  preamble << "if(";
441  visit(*toCondition(requireCtx), preamble);
442  preamble << ") {\n";
443  visit(*next, out);
444  out << "}\n";
445  } else {
446  visit(*next, out);
447  }
448  } else {
449  out << preamble.str();
450  if (requireCtx.size() > 0) {
451  out << "if(";
452  visit(*toCondition(requireCtx), out);
453  out << ") {\n";
454  visit(*next, out);
455  out << "}\n";
456  } else {
457  visit(*next, out);
458  }
459  }
460 
461  if (isParallel) {
462  out << "PARALLEL_END\n"; // end parallel
463  }
464 
465  out << "}\n";
466  out << "();"; // call lambda
467 
468  if (freeOfCtx.size() > 0) {
469  out << "}\n";
470  }
471 
472  PRINT_END_COMMENT(out);
473  }
474 
475  void visitClear(const Clear& clear, std::ostream& out) override {
476  PRINT_BEGIN_COMMENT(out);
477 
478  if (!synthesiser.lookup(clear.getRelation())->isTemp()) {
479  out << "if (performIO) ";
480  }
481  out << synthesiser.getRelationName(synthesiser.lookup(clear.getRelation())) << "->"
482  << "purge();\n";
483 
484  PRINT_END_COMMENT(out);
485  }
486 
487  void visitLogSize(const LogSize& size, std::ostream& out) override {
488  PRINT_BEGIN_COMMENT(out);
489  out << "ProfileEventSingleton::instance().makeQuantityEvent( R\"(";
490  out << size.getMessage() << ")\",";
491  out << synthesiser.getRelationName(synthesiser.lookup(size.getRelation())) << "->size(),iter);";
492  PRINT_END_COMMENT(out);
493  }
494 
495  // -- control flow statements --
496 
497  void visitSequence(const Sequence& seq, std::ostream& out) override {
498  PRINT_BEGIN_COMMENT(out);
499  for (const auto& cur : seq.getStatements()) {
500  visit(cur, out);
501  }
502  PRINT_END_COMMENT(out);
503  }
504 
505  void visitParallel(const Parallel& parallel, std::ostream& out) override {
506  PRINT_BEGIN_COMMENT(out);
507  auto stmts = parallel.getStatements();
508 
509  // special handling cases
510  if (stmts.empty()) {
511  PRINT_END_COMMENT(out);
512  return;
513  }
514 
515  // a single statement => save the overhead
516  if (stmts.size() == 1) {
517  visit(stmts[0], out);
518  PRINT_END_COMMENT(out);
519  return;
520  }
521 
522  // more than one => parallel sections
523 
524  // start parallel section
525  out << "SECTIONS_START;\n";
526 
527  // put each thread in another section
528  for (const auto& cur : stmts) {
529  out << "SECTION_START;\n";
530  visit(cur, out);
531  out << "SECTION_END\n";
532  }
533 
534  // done
535  out << "SECTIONS_END;\n";
536  PRINT_END_COMMENT(out);
537  }
538 
539  void visitLoop(const Loop& loop, std::ostream& out) override {
540  PRINT_BEGIN_COMMENT(out);
541  out << "iter = 0;\n";
542  out << "for(;;) {\n";
543  visit(loop.getBody(), out);
544  out << "iter++;\n";
545  out << "}\n";
546  out << "iter = 0;\n";
547  PRINT_END_COMMENT(out);
548  }
549 
550  void visitSwap(const Swap& swap, std::ostream& out) override {
551  PRINT_BEGIN_COMMENT(out);
552  const std::string& deltaKnowledge =
553  synthesiser.getRelationName(synthesiser.lookup(swap.getFirstRelation()));
554  const std::string& newKnowledge =
555  synthesiser.getRelationName(synthesiser.lookup(swap.getSecondRelation()));
556 
557  out << "std::swap(" << deltaKnowledge << ", " << newKnowledge << ");\n";
558  PRINT_END_COMMENT(out);
559  }
560 
561  void visitExtend(const Extend& extend, std::ostream& out) override {
562  PRINT_BEGIN_COMMENT(out);
563  out << synthesiser.getRelationName(synthesiser.lookup(extend.getSourceRelation())) << "->"
564  << "extend("
565  << "*" << synthesiser.getRelationName(synthesiser.lookup(extend.getTargetRelation()))
566  << ");\n";
567  PRINT_END_COMMENT(out);
568  }
569 
570  void visitExit(const Exit& exit, std::ostream& out) override {
571  PRINT_BEGIN_COMMENT(out);
572  out << "if(";
573  visit(exit.getCondition(), out);
574  out << ") break;\n";
575  PRINT_END_COMMENT(out);
576  }
577 
578  void visitCall(const Call& call, std::ostream& out) override {
579  PRINT_BEGIN_COMMENT(out);
580  const Program& prog = synthesiser.getTranslationUnit().getProgram();
581  const auto& subs = prog.getSubroutines();
582  out << "{\n";
583  out << " std::vector<RamDomain> args, ret;\n";
584  out << "subroutine_" << distance(subs.begin(), subs.find(call.getName())) << "(args, ret);\n";
585  out << "}\n";
586  PRINT_END_COMMENT(out);
587  }
588 
589  void visitLogRelationTimer(const LogRelationTimer& timer, std::ostream& out) override {
590  PRINT_BEGIN_COMMENT(out);
591  // create local scope for name resolution
592  out << "{\n";
593 
594  const std::string ext = fileExtension(Global::config().get("profile"));
595 
596  const auto* rel = synthesiser.lookup(timer.getRelation());
597  auto relName = synthesiser.getRelationName(rel);
598 
599  out << "\tLogger logger(R\"_(" << timer.getMessage() << ")_\",iter, [&](){return " << relName
600  << "->size();});\n";
601  // insert statement to be measured
602  visit(timer.getStatement(), out);
603 
604  // done
605  out << "}\n";
606  PRINT_END_COMMENT(out);
607  }
608 
609  void visitLogTimer(const LogTimer& timer, std::ostream& out) override {
610  PRINT_BEGIN_COMMENT(out);
611  // create local scope for name resolution
612  out << "{\n";
613 
614  const std::string ext = fileExtension(Global::config().get("profile"));
615 
616  // create local timer
617  out << "\tLogger logger(R\"_(" << timer.getMessage() << ")_\",iter);\n";
618  // insert statement to be measured
619  visit(timer.getStatement(), out);
620 
621  // done
622  out << "}\n";
623  PRINT_END_COMMENT(out);
624  }
625 
626  void visitDebugInfo(const DebugInfo& dbg, std::ostream& out) override {
627  PRINT_BEGIN_COMMENT(out);
628  out << "signalHandler->setMsg(R\"_(";
629  out << dbg.getMessage();
630  out << ")_\");\n";
631 
632  // insert statements of the rule
633  visit(dbg.getStatement(), out);
634  PRINT_END_COMMENT(out);
635  }
636 
637  // -- operations --
638 
639  void visitNestedOperation(const NestedOperation& nested, std::ostream& out) override {
640  visit(nested.getOperation(), out);
641  if (Global::config().has("profile") && !nested.getProfileText().empty()) {
642  out << "freqs[" << synthesiser.lookupFreqIdx(nested.getProfileText()) << "]++;\n";
643  }
644  }
645 
646  void visitTupleOperation(const TupleOperation& search, std::ostream& out) override {
647  PRINT_BEGIN_COMMENT(out);
648  visitNestedOperation(search, out);
649  PRINT_END_COMMENT(out);
650  }
651 
652  void visitParallelScan(const ParallelScan& pscan, std::ostream& out) override {
653  const auto* rel = synthesiser.lookup(pscan.getRelation());
654  const auto& relName = synthesiser.getRelationName(rel);
655 
656  assert(pscan.getTupleId() == 0 && "not outer-most loop");
657 
658  assert(rel->getArity() > 0 && "AstToRamTranslator failed/no parallel scans for nullaries");
659 
660  assert(!preambleIssued && "only first loop can be made parallel");
661  preambleIssued = true;
662 
663  PRINT_BEGIN_COMMENT(out);
664 
665  out << "auto part = " << relName << "->partition();\n";
666  out << "PARALLEL_START\n";
667  out << preamble.str();
668  out << "pfor(auto it = part.begin(); it<part.end();++it){\n";
669  out << "try{\n";
670  out << "for(const auto& env0 : *it) {\n";
671 
672  visitTupleOperation(pscan, out);
673 
674  out << "}\n";
675  out << "} catch(std::exception &e) { signalHandler->error(e.what());}\n";
676  out << "}\n";
677 
678  PRINT_END_COMMENT(out);
679  }
680 
681  void visitScan(const Scan& scan, std::ostream& out) override {
682  const auto* rel = synthesiser.lookup(scan.getRelation());
683  auto relName = synthesiser.getRelationName(rel);
684  auto id = scan.getTupleId();
685 
686  PRINT_BEGIN_COMMENT(out);
687 
688  assert(rel->getArity() > 0 && "AstToRamTranslator failed/no scans for nullaries");
689 
690  out << "for(const auto& env" << id << " : "
691  << "*" << relName << ") {\n";
692 
693  visitTupleOperation(scan, out);
694 
695  out << "}\n";
696 
697  PRINT_END_COMMENT(out);
698  }
699 
700  void visitChoice(const Choice& choice, std::ostream& out) override {
701  const auto* rel = synthesiser.lookup(choice.getRelation());
702  auto relName = synthesiser.getRelationName(rel);
703  auto identifier = choice.getTupleId();
704 
705  assert(rel->getArity() > 0 && "AstToRamTranslator failed/no choice for nullaries");
706 
707  PRINT_BEGIN_COMMENT(out);
708 
709  out << "for(const auto& env" << identifier << " : "
710  << "*" << relName << ") {\n";
711  out << "if( ";
712 
713  visit(choice.getCondition(), out);
714 
715  out << ") {\n";
716 
717  visitTupleOperation(choice, out);
718 
719  out << "break;\n";
720  out << "}\n";
721  out << "}\n";
722 
723  PRINT_END_COMMENT(out);
724  }
725 
726  void visitParallelChoice(const ParallelChoice& pchoice, std::ostream& out) override {
727  const auto* rel = synthesiser.lookup(pchoice.getRelation());
728  auto relName = synthesiser.getRelationName(rel);
729 
730  assert(pchoice.getTupleId() == 0 && "not outer-most loop");
731 
732  assert(rel->getArity() > 0 && "AstToRamTranslator failed/no parallel choice for nullaries");
733 
734  assert(!preambleIssued && "only first loop can be made parallel");
735  preambleIssued = true;
736 
737  PRINT_BEGIN_COMMENT(out);
738 
739  out << "auto part = " << relName << "->partition();\n";
740  out << "PARALLEL_START\n";
741  out << preamble.str();
742  out << "pfor(auto it = part.begin(); it<part.end();++it){\n";
743  out << "try{\n";
744  out << "for(const auto& env0 : *it) {\n";
745  out << "if( ";
746 
747  visit(pchoice.getCondition(), out);
748 
749  out << ") {\n";
750 
751  visitTupleOperation(pchoice, out);
752 
753  out << "break;\n";
754  out << "}\n";
755  out << "}\n";
756  out << "} catch(std::exception &e) { signalHandler->error(e.what());}\n";
757  out << "}\n";
758 
759  PRINT_END_COMMENT(out);
760  }
761 
762  void visitIndexScan(const IndexScan& iscan, std::ostream& out) override {
763  const auto* rel = synthesiser.lookup(iscan.getRelation());
764  auto relName = synthesiser.getRelationName(rel);
765  auto identifier = iscan.getTupleId();
766  auto keys = isa->getSearchSignature(&iscan);
767  auto arity = rel->getArity();
768 
769  const auto& rangePatternLower = iscan.getRangePattern().first;
770  const auto& rangePatternUpper = iscan.getRangePattern().second;
771 
772  assert(arity > 0 && "AstToRamTranslator failed/no index scans for nullaries");
773 
774  PRINT_BEGIN_COMMENT(out);
775  auto ctxName = "READ_OP_CONTEXT(" + synthesiser.getOpContextName(*rel) + ")";
776  auto rangeBounds = getPaddedRangeBounds(*rel, rangePatternLower, rangePatternUpper);
777 
778  out << "auto range = " << relName << "->"
779  << "lowerUpperRange_" << keys << "(" << rangeBounds.first.str() << ","
780  << rangeBounds.second.str() << "," << ctxName << ");\n";
781  out << "for(const auto& env" << identifier << " : range) {\n";
782 
783  visitTupleOperation(iscan, out);
784 
785  out << "}\n";
786  PRINT_END_COMMENT(out);
787  }
788 
789  void visitParallelIndexScan(const ParallelIndexScan& piscan, std::ostream& out) override {
790  const auto* rel = synthesiser.lookup(piscan.getRelation());
791  auto relName = synthesiser.getRelationName(rel);
792  auto arity = rel->getArity();
793  auto keys = isa->getSearchSignature(&piscan);
794 
795  const auto& rangePatternLower = piscan.getRangePattern().first;
796  const auto& rangePatternUpper = piscan.getRangePattern().second;
797 
798  assert(piscan.getTupleId() == 0 && "not outer-most loop");
799 
800  assert(arity > 0 && "AstToRamTranslator failed/no parallel index scan for nullaries");
801 
802  assert(!preambleIssued && "only first loop can be made parallel");
803  preambleIssued = true;
804 
805  PRINT_BEGIN_COMMENT(out);
806  auto rangeBounds = getPaddedRangeBounds(*rel, rangePatternLower, rangePatternUpper);
807  out << "auto range = " << relName
808  << "->"
809  // TODO (b-scholz): context may be missing here?
810  << "lowerUpperRange_" << keys << "(" << rangeBounds.first.str() << ","
811  << rangeBounds.second.str() << ");\n";
812  out << "auto part = range.partition();\n";
813  out << "PARALLEL_START\n";
814  out << preamble.str();
815  out << "pfor(auto it = part.begin(); it<part.end(); ++it) { \n";
816  out << "try{\n";
817  out << "for(const auto& env0 : *it) {\n";
818 
819  visitTupleOperation(piscan, out);
820 
821  out << "}\n";
822  out << "} catch(std::exception &e) { signalHandler->error(e.what());}\n";
823  out << "}\n";
824 
825  PRINT_END_COMMENT(out);
826  }
827 
828  void visitIndexChoice(const IndexChoice& ichoice, std::ostream& out) override {
829  PRINT_BEGIN_COMMENT(out);
830  const auto* rel = synthesiser.lookup(ichoice.getRelation());
831  auto relName = synthesiser.getRelationName(rel);
832  auto identifier = ichoice.getTupleId();
833  auto arity = rel->getArity();
834  const auto& rangePatternLower = ichoice.getRangePattern().first;
835  const auto& rangePatternUpper = ichoice.getRangePattern().second;
836  auto keys = isa->getSearchSignature(&ichoice);
837 
838  // check list of keys
839  assert(arity > 0 && "AstToRamTranslator failed");
840  auto ctxName = "READ_OP_CONTEXT(" + synthesiser.getOpContextName(*rel) + ")";
841  auto rangeBounds = getPaddedRangeBounds(*rel, rangePatternLower, rangePatternUpper);
842 
843  out << "auto range = " << relName << "->"
844  << "lowerUpperRange_" << keys << "(" << rangeBounds.first.str() << ","
845  << rangeBounds.second.str() << "," << ctxName << ");\n";
846  out << "for(const auto& env" << identifier << " : range) {\n";
847  out << "if( ";
848 
849  visit(ichoice.getCondition(), out);
850 
851  out << ") {\n";
852 
853  visitTupleOperation(ichoice, out);
854 
855  out << "break;\n";
856  out << "}\n";
857  out << "}\n";
858 
859  PRINT_END_COMMENT(out);
860  }
861 
862  void visitParallelIndexChoice(const ParallelIndexChoice& pichoice, std::ostream& out) override {
863  PRINT_BEGIN_COMMENT(out);
864  const auto* rel = synthesiser.lookup(pichoice.getRelation());
865  auto relName = synthesiser.getRelationName(rel);
866  auto arity = rel->getArity();
867  const auto& rangePatternLower = pichoice.getRangePattern().first;
868  const auto& rangePatternUpper = pichoice.getRangePattern().second;
869  auto keys = isa->getSearchSignature(&pichoice);
870 
871  assert(pichoice.getTupleId() == 0 && "not outer-most loop");
872 
873  assert(arity > 0 && "AstToRamTranslator failed");
874 
875  assert(!preambleIssued && "only first loop can be made parallel");
876  preambleIssued = true;
877 
878  PRINT_BEGIN_COMMENT(out);
879  auto rangeBounds = getPaddedRangeBounds(*rel, rangePatternLower, rangePatternUpper);
880  out << "auto range = " << relName
881  << "->"
882  // TODO (b-scholz): context may be missing here?
883  << "lowerUpperRange_" << keys << "(" << rangeBounds.first.str() << ","
884  << rangeBounds.second.str() << ");\n";
885  out << "auto part = range.partition();\n";
886  out << "PARALLEL_START\n";
887  out << preamble.str();
888  out << "pfor(auto it = part.begin(); it<part.end(); ++it) { \n";
889  out << "try{";
890  out << "for(const auto& env0 : *it) {\n";
891  out << "if( ";
892 
893  visit(pichoice.getCondition(), out);
894 
895  out << ") {\n";
896 
897  visitTupleOperation(pichoice, out);
898 
899  out << "break;\n";
900  out << "}\n";
901  out << "}\n";
902  out << "} catch(std::exception &e) { signalHandler->error(e.what());}\n";
903  out << "}\n";
904 
905  PRINT_END_COMMENT(out);
906  }
907 
908  void visitUnpackRecord(const UnpackRecord& unpack, std::ostream& out) override {
909  PRINT_BEGIN_COMMENT(out);
910  auto arity = unpack.getArity();
911 
912  // look up reference
913  out << "RamDomain const ref = ";
914  visit(unpack.getExpression(), out);
915  out << ";\n";
916 
917  // Handle nil case.
918  out << "if (ref == 0) continue;\n";
919 
920  // Unpack tuple
921  out << "const RamDomain *"
922  << "env" << unpack.getTupleId() << " = "
923  << "recordTable.unpack(ref," << arity << ");"
924  << "\n";
925 
926  out << "{\n";
927 
928  // continue with condition checks and nested body
929  visitTupleOperation(unpack, out);
930 
931  out << "}\n";
932  PRINT_END_COMMENT(out);
933  }
934 
935  void visitParallelIndexAggregate(
936  const ParallelIndexAggregate& aggregate, std::ostream& out) override {
937  assert(aggregate.getTupleId() == 0 && "not outer-most loop");
938  assert(!preambleIssued && "only first loop can be made parallel");
939  preambleIssued = true;
940  PRINT_BEGIN_COMMENT(out);
941  // get some properties
942  const auto* rel = synthesiser.lookup(aggregate.getRelation());
943  auto arity = rel->getArity();
944  auto relName = synthesiser.getRelationName(rel);
945  auto ctxName = "READ_OP_CONTEXT(" + synthesiser.getOpContextName(*rel) + ")";
946  auto identifier = aggregate.getTupleId();
947 
948  // aggregate tuple storing the result of aggregate
949  std::string tuple_type = "Tuple<RamDomain," + toString(arity) + ">";
950 
951  // declare environment variable
952  out << "Tuple<RamDomain,1> env" << identifier << ";\n";
953 
954  // get range to aggregate
955  auto keys = isa->getSearchSignature(&aggregate);
956 
957  // special case: counting number elements over an unrestricted predicate
958  if (aggregate.getFunction() == AggregateOp::COUNT && keys.empty() &&
959  isTrue(&aggregate.getCondition())) {
960  // shortcut: use relation size
961  out << "env" << identifier << "[0] = " << relName << "->"
962  << "size();\n";
963  out << "{\n"; // to match PARALLEL_END closing bracket
964  out << preamble.str();
965  visitTupleOperation(aggregate, out);
966  PRINT_END_COMMENT(out);
967  return;
968  }
969 
970  out << "bool shouldRunNested = false;\n";
971 
972  // init result and reduction operation
973  std::string init;
974  switch (aggregate.getFunction()) {
975  case AggregateOp::MIN: init = "MAX_RAM_SIGNED"; break;
976  case AggregateOp::FMIN: init = "MAX_RAM_FLOAT"; break;
977  case AggregateOp::UMIN: init = "MAX_RAM_UNSIGNED"; break;
978  case AggregateOp::MAX: init = "MIN_RAM_SIGNED"; break;
979  case AggregateOp::FMAX: init = "MIN_RAM_FLOAT"; break;
980  case AggregateOp::UMAX: init = "MIN_RAM_UNSIGNED"; break;
981  case AggregateOp::COUNT:
982  init = "0";
983  out << "shouldRunNested = true;\n";
984  break;
985  case AggregateOp::MEAN: init = "0"; break;
986  case AggregateOp::FSUM:
987  case AggregateOp::USUM:
988  case AggregateOp::SUM:
989  init = "0";
990  out << "shouldRunNested = true;\n";
991  break;
992  }
993 
994  // Set reduction operation
995  std::string op;
996  switch (aggregate.getFunction()) {
997  case AggregateOp::MIN:
998  case AggregateOp::FMIN:
999  case AggregateOp::UMIN: {
1000  op = "min";
1001  break;
1002  }
1003 
1004  case AggregateOp::MAX:
1005  case AggregateOp::FMAX:
1006  case AggregateOp::UMAX: {
1007  op = "max";
1008  break;
1009  }
1010 
1011  case AggregateOp::MEAN:
1012  case AggregateOp::FSUM:
1013  case AggregateOp::USUM:
1014  case AggregateOp::COUNT:
1015  case AggregateOp::SUM: {
1016  op = "+";
1017  break;
1018  }
1019  default: fatal("Unhandled aggregate operation");
1020  }
1021  // res0 stores the aggregate result
1022  std::string sharedVariable = "res0";
1023 
1024  std::string type;
1025  switch (getTypeAttributeAggregate(aggregate.getFunction())) {
1026  case TypeAttribute::Signed: type = "RamSigned"; break;
1027  case TypeAttribute::Unsigned: type = "RamUnsigned"; break;
1028  case TypeAttribute::Float: type = "RamFloat"; break;
1029 
1030  case TypeAttribute::Symbol:
1031  case TypeAttribute::ADT:
1032  case TypeAttribute::Record: type = "RamDomain"; break;
1033  }
1034  out << type << " res0 = " << init << ";\n";
1035  if (aggregate.getFunction() == AggregateOp::MEAN) {
1036  out << "RamUnsigned res1 = 0;\n";
1037  sharedVariable += ", res1";
1038  }
1039 
1040  out << preamble.str();
1041  out << "PARALLEL_START\n";
1042  // check whether there is an index to use
1043  if (keys.empty()) {
1044  out << "#pragma omp for reduction(" << op << ":" << sharedVariable << ")\n";
1045  out << "for(const auto& env" << identifier << " : "
1046  << "*" << relName << ") {\n";
1047  } else {
1048  const auto& rangePatternLower = aggregate.getRangePattern().first;
1049  const auto& rangePatternUpper = aggregate.getRangePattern().second;
1050 
1051  auto rangeBounds = getPaddedRangeBounds(*rel, rangePatternLower, rangePatternUpper);
1052  out << "auto range = " << relName << "->"
1053  << "lowerUpperRange_" << keys << "(" << rangeBounds.first.str() << ","
1054  << rangeBounds.second.str() << "," << ctxName << ");\n";
1055 
1056  out << "auto part = range.partition();\n";
1057  out << "#pragma omp for reduction(" << op << ":" << sharedVariable << ")\n";
1058  // iterate over each part
1059  out << "for (auto it = part.begin(); it < part.end(); ++it) {\n";
1060  // iterate over tuples in each part
1061  out << "for (const auto& env" << identifier << ": *it) {\n";
1062  }
1063 
1064  // produce condition inside the loop if necessary
1065  out << "if( ";
1066  visit(aggregate.getCondition(), out);
1067  out << ") {\n";
1068 
1069  out << "shouldRunNested = true;\n";
1070 
1071  // pick function
1072  switch (aggregate.getFunction()) {
1073  case AggregateOp::FMIN:
1074  case AggregateOp::UMIN:
1075  case AggregateOp::MIN:
1076  out << "res0 = std::min(res0,ramBitCast<" << type << ">(";
1077  visit(aggregate.getExpression(), out);
1078  out << "));\n";
1079  break;
1080  case AggregateOp::FMAX:
1081  case AggregateOp::UMAX:
1082  case AggregateOp::MAX:
1083  out << "res0 = std::max(res0,ramBitCast<" << type << ">(";
1084  visit(aggregate.getExpression(), out);
1085  out << "));\n";
1086  break;
1087  case AggregateOp::COUNT: out << "++res0\n;"; break;
1088  case AggregateOp::FSUM:
1089  case AggregateOp::USUM:
1090  case AggregateOp::SUM:
1091  out << "res0 += "
1092  << "ramBitCast<" << type << ">(";
1093  visit(aggregate.getExpression(), out);
1094  out << ");\n";
1095  break;
1096 
1097  case AggregateOp::MEAN:
1098  out << "res0 += "
1099  << "ramBitCast<RamFloat>(";
1100  visit(aggregate.getExpression(), out);
1101  out << ");\n";
1102  out << "++res1;\n";
1103  break;
1104  }
1105 
1106  // end if statement
1107  out << "}\n";
1108 
1109  // end aggregator loop
1110  out << "}\n";
1111 
1112  // if keys weren't empty then there'll be another loop to close off
1113  if (!keys.empty()) {
1114  out << "}\n";
1115  }
1116 
1117  // start single-threaded section
1118  out << "#pragma omp single\n{\n";
1119 
1120  if (aggregate.getFunction() == AggregateOp::MEAN) {
1121  out << "if (res1 != 0) {\n";
1122  out << "res0 = res0 / res1;\n";
1123  out << "}\n";
1124  }
1125 
1126  // write result into environment tuple
1127  out << "env" << identifier << "[0] = ramBitCast(res0);\n";
1128 
1129  // check whether there exists a min/max first before next loop
1130  out << "if (shouldRunNested) {\n";
1131  visitTupleOperation(aggregate, out);
1132  out << "}\n";
1133  // end single-threaded section
1134  out << "}\n";
1135  PRINT_END_COMMENT(out);
1136  }
1137 
1138  bool isGuaranteedToBeMinimum(const IndexAggregate& aggregate) {
1139  auto identifier = aggregate.getTupleId();
1140  auto keys = isa->getSearchSignature(&aggregate);
1141  RelationRepresentation repr = synthesiser.lookup(aggregate.getRelation())->getRepresentation();
1142 
1143  const auto* tupleElem = dynamic_cast<const TupleElement*>(&aggregate.getExpression());
1144  return tupleElem && tupleElem->getTupleId() == identifier &&
1145  keys[tupleElem->getElement()] != ram::analysis::AttributeConstraint::None &&
1147  }
1148 
1149  void visitIndexAggregate(const IndexAggregate& aggregate, std::ostream& out) override {
1150  PRINT_BEGIN_COMMENT(out);
1151  // get some properties
1152  const auto* rel = synthesiser.lookup(aggregate.getRelation());
1153  auto arity = rel->getArity();
1154  auto relName = synthesiser.getRelationName(rel);
1155  auto ctxName = "READ_OP_CONTEXT(" + synthesiser.getOpContextName(*rel) + ")";
1156  auto identifier = aggregate.getTupleId();
1157 
1158  // aggregate tuple storing the result of aggregate
1159  std::string tuple_type = "Tuple<RamDomain," + toString(arity) + ">";
1160 
1161  // declare environment variable
1162  out << "Tuple<RamDomain,1> env" << identifier << ";\n";
1163 
1164  // get range to aggregate
1165  auto keys = isa->getSearchSignature(&aggregate);
1166 
1167  // special case: counting number elements over an unrestricted predicate
1168  if (aggregate.getFunction() == AggregateOp::COUNT && keys.empty() &&
1169  isTrue(&aggregate.getCondition())) {
1170  // shortcut: use relation size
1171  out << "env" << identifier << "[0] = " << relName << "->"
1172  << "size();\n";
1173  visitTupleOperation(aggregate, out);
1174  PRINT_END_COMMENT(out);
1175  return;
1176  }
1177 
1178  out << "bool shouldRunNested = false;\n";
1179 
1180  // init result
1181  std::string init;
1182  switch (aggregate.getFunction()) {
1183  case AggregateOp::MIN: init = "MAX_RAM_SIGNED"; break;
1184  case AggregateOp::FMIN: init = "MAX_RAM_FLOAT"; break;
1185  case AggregateOp::UMIN: init = "MAX_RAM_UNSIGNED"; break;
1186  case AggregateOp::MAX: init = "MIN_RAM_SIGNED"; break;
1187  case AggregateOp::FMAX: init = "MIN_RAM_FLOAT"; break;
1188  case AggregateOp::UMAX: init = "MIN_RAM_UNSIGNED"; break;
1189  case AggregateOp::COUNT:
1190  init = "0";
1191  out << "shouldRunNested = true;\n";
1192  break;
1193  case AggregateOp::MEAN: init = "0"; break;
1194  case AggregateOp::FSUM:
1195  case AggregateOp::USUM:
1196  case AggregateOp::SUM:
1197  init = "0";
1198  out << "shouldRunNested = true;\n";
1199  break;
1200  }
1201 
1202  std::string type;
1203  switch (getTypeAttributeAggregate(aggregate.getFunction())) {
1204  case TypeAttribute::Signed: type = "RamSigned"; break;
1205  case TypeAttribute::Unsigned: type = "RamUnsigned"; break;
1206  case TypeAttribute::Float: type = "RamFloat"; break;
1207 
1208  case TypeAttribute::Symbol:
1209  case TypeAttribute::ADT:
1210  case TypeAttribute::Record: type = "RamDomain"; break;
1211  }
1212  out << type << " res0 = " << init << ";\n";
1213 
1214  if (aggregate.getFunction() == AggregateOp::MEAN) {
1215  out << "RamUnsigned res1 = 0;\n";
1216  }
1217 
1218  // check whether there is an index to use
1219  if (keys.empty()) {
1220  out << "for(const auto& env" << identifier << " : "
1221  << "*" << relName << ") {\n";
1222  } else {
1223  const auto& rangePatternLower = aggregate.getRangePattern().first;
1224  const auto& rangePatternUpper = aggregate.getRangePattern().second;
1225 
1226  auto rangeBounds = getPaddedRangeBounds(*rel, rangePatternLower, rangePatternUpper);
1227 
1228  out << "auto range = " << relName << "->"
1229  << "lowerUpperRange_" << keys << "(" << rangeBounds.first.str() << ","
1230  << rangeBounds.second.str() << "," << ctxName << ");\n";
1231 
1232  // aggregate result
1233  out << "for(const auto& env" << identifier << " : range) {\n";
1234  }
1235 
1236  // produce condition inside the loop
1237  out << "if( ";
1238  visit(aggregate.getCondition(), out);
1239  out << ") {\n";
1240 
1241  out << "shouldRunNested = true;\n";
1242 
1243  // pick function
1244  switch (aggregate.getFunction()) {
1245  case AggregateOp::FMIN:
1246  case AggregateOp::UMIN:
1247  case AggregateOp::MIN:
1248  out << "res0 = std::min(res0,ramBitCast<" << type << ">(";
1249  visit(aggregate.getExpression(), out);
1250  out << "));\n";
1251  if (isGuaranteedToBeMinimum(aggregate)) {
1252  out << "break;\n";
1253  }
1254  break;
1255  case AggregateOp::FMAX:
1256  case AggregateOp::UMAX:
1257  case AggregateOp::MAX:
1258  out << "res0 = std::max(res0,ramBitCast<" << type << ">(";
1259  visit(aggregate.getExpression(), out);
1260  out << "));\n";
1261  break;
1262  case AggregateOp::COUNT: out << "++res0\n;"; break;
1263  case AggregateOp::FSUM:
1264  case AggregateOp::USUM:
1265  case AggregateOp::SUM:
1266  out << "res0 += "
1267  << "ramBitCast<" << type << ">(";
1268  visit(aggregate.getExpression(), out);
1269  out << ");\n";
1270  break;
1271 
1272  case AggregateOp::MEAN:
1273  out << "res0 += "
1274  << "ramBitCast<RamFloat>(";
1275  visit(aggregate.getExpression(), out);
1276  out << ");\n";
1277  out << "++res1;\n";
1278  break;
1279  }
1280 
1281  out << "}\n";
1282 
1283  // end aggregator loop
1284  out << "}\n";
1285 
1286  if (aggregate.getFunction() == AggregateOp::MEAN) {
1287  out << "if (res1 != 0) {\n";
1288  out << "res0 = res0 / res1;\n";
1289  out << "}\n";
1290  }
1291 
1292  // write result into environment tuple
1293  out << "env" << identifier << "[0] = ramBitCast(res0);\n";
1294 
1295  // check whether there exists a min/max first before next loop
1296  out << "if (shouldRunNested) {\n";
1297  visitTupleOperation(aggregate, out);
1298  out << "}\n";
1299 
1300  PRINT_END_COMMENT(out);
1301  }
1302 
1303  void visitParallelAggregate(const ParallelAggregate& aggregate, std::ostream& out) override {
1304  PRINT_BEGIN_COMMENT(out);
1305  // get some properties
1306  const auto* rel = synthesiser.lookup(aggregate.getRelation());
1307  auto relName = synthesiser.getRelationName(rel);
1308  auto ctxName = "READ_OP_CONTEXT(" + synthesiser.getOpContextName(*rel) + ")";
1309  auto identifier = aggregate.getTupleId();
1310 
1311  assert(aggregate.getTupleId() == 0 && "not outer-most loop");
1312  assert(!preambleIssued && "only first loop can be made parallel");
1313  preambleIssued = true;
1314 
1315  // declare environment variable
1316  out << "Tuple<RamDomain,1> env" << identifier << ";\n";
1317 
1318  // special case: counting number elements over an unrestricted predicate
1319  if (aggregate.getFunction() == AggregateOp::COUNT && isTrue(&aggregate.getCondition())) {
1320  // shortcut: use relation size
1321  out << "env" << identifier << "[0] = " << relName << "->"
1322  << "size();\n";
1323  out << "PARALLEL_START\n";
1324  out << preamble.str();
1325  visitTupleOperation(aggregate, out);
1326  PRINT_END_COMMENT(out);
1327  return;
1328  }
1329 
1330  out << "bool shouldRunNested = false;\n";
1331 
1332  // init result
1333  std::string init;
1334  switch (aggregate.getFunction()) {
1335  case AggregateOp::MIN: init = "MAX_RAM_SIGNED"; break;
1336  case AggregateOp::FMIN: init = "MAX_RAM_FLOAT"; break;
1337  case AggregateOp::UMIN: init = "MAX_RAM_UNSIGNED"; break;
1338  case AggregateOp::MAX: init = "MIN_RAM_SIGNED"; break;
1339  case AggregateOp::FMAX: init = "MIN_RAM_FLOAT"; break;
1340  case AggregateOp::UMAX: init = "MIN_RAM_UNSIGNED"; break;
1341  case AggregateOp::COUNT:
1342  init = "0";
1343  out << "shouldRunNested = true;\n";
1344  break;
1345 
1346  case AggregateOp::MEAN: init = "0"; break;
1347 
1348  case AggregateOp::FSUM:
1349  case AggregateOp::USUM:
1350  case AggregateOp::SUM:
1351  init = "0";
1352  out << "shouldRunNested = true;\n";
1353  break;
1354  }
1355 
1356  // Set reduction operation
1357  std::string op;
1358  switch (aggregate.getFunction()) {
1359  case AggregateOp::MIN:
1360  case AggregateOp::FMIN:
1361  case AggregateOp::UMIN: {
1362  op = "min";
1363  break;
1364  }
1365 
1366  case AggregateOp::MAX:
1367  case AggregateOp::FMAX:
1368  case AggregateOp::UMAX: {
1369  op = "max";
1370  break;
1371  }
1372 
1373  case AggregateOp::MEAN:
1374  case AggregateOp::FSUM:
1375  case AggregateOp::USUM:
1376  case AggregateOp::COUNT:
1377  case AggregateOp::SUM: {
1378  op = "+";
1379  break;
1380  }
1381 
1382  default: fatal("Unhandled aggregate operation");
1383  }
1384 
1385  char const* type;
1386  switch (getTypeAttributeAggregate(aggregate.getFunction())) {
1387  case TypeAttribute::Signed: type = "RamSigned"; break;
1388  case TypeAttribute::Unsigned: type = "RamUnsigned"; break;
1389  case TypeAttribute::Float: type = "RamFloat"; break;
1390 
1391  case TypeAttribute::Symbol:
1392  case TypeAttribute::ADT:
1393  case TypeAttribute::Record: type = "RamDomain"; break;
1394  }
1395  out << type << " res0 = " << init << ";\n";
1396 
1397  std::string sharedVariable = "res0";
1398  if (aggregate.getFunction() == AggregateOp::MEAN) {
1399  out << "RamUnsigned res1 = " << init << ";\n";
1400  sharedVariable += ", res1";
1401  }
1402 
1403  // create a partitioning of the relation to iterate over simeltaneously
1404  out << "auto part = " << relName << "->partition();\n";
1405  out << "PARALLEL_START\n";
1406  out << preamble.str();
1407  // pragma statement
1408  out << "#pragma omp for reduction(" << op << ":" << sharedVariable << ")\n";
1409  // iterate over each part
1410  out << "for (auto it = part.begin(); it < part.end(); ++it) {\n";
1411  // iterate over tuples in each part
1412  out << "for (const auto& env" << identifier << ": *it) {\n";
1413 
1414  // produce condition inside the loop
1415  out << "if( ";
1416  visit(aggregate.getCondition(), out);
1417  out << ") {\n";
1418 
1419  out << "shouldRunNested = true;\n";
1420  // pick function
1421  switch (aggregate.getFunction()) {
1422  case AggregateOp::FMIN:
1423  case AggregateOp::UMIN:
1424  case AggregateOp::MIN:
1425  out << "res0 = std::min(res0, ramBitCast<" << type << ">(";
1426  visit(aggregate.getExpression(), out);
1427  out << "));\n";
1428  break;
1429  case AggregateOp::FMAX:
1430  case AggregateOp::UMAX:
1431  case AggregateOp::MAX:
1432  out << "res0 = std::max(res0, ramBitCast<" << type << ">(";
1433  visit(aggregate.getExpression(), out);
1434  out << "));\n";
1435  break;
1436  case AggregateOp::COUNT: out << "++res0\n;"; break;
1437  case AggregateOp::FSUM:
1438  case AggregateOp::USUM:
1439  case AggregateOp::SUM:
1440  out << "res0 += ramBitCast<" << type << ">(";
1441  visit(aggregate.getExpression(), out);
1442  out << ");\n";
1443  break;
1444 
1445  case AggregateOp::MEAN:
1446  out << "res0 += ramBitCast<RamFloat>(";
1447  visit(aggregate.getExpression(), out);
1448  out << ");\n";
1449  out << "++res1;\n";
1450  break;
1451  }
1452 
1453  out << "}\n";
1454 
1455  // end aggregator loop
1456  out << "}\n";
1457  // end partition loop
1458  out << "}\n";
1459 
1460  // the rest shouldn't be run in parallel
1461  out << "#pragma omp single\n{\n";
1462 
1463  if (aggregate.getFunction() == AggregateOp::MEAN) {
1464  out << "if (res1 != 0) {\n";
1465  out << "res0 = res0 / res1;\n";
1466  out << "}\n";
1467  }
1468 
1469  // write result into environment tuple
1470  out << "env" << identifier << "[0] = ramBitCast(res0);\n";
1471 
1472  // check whether there exists a min/max first before next loop
1473  out << "if (shouldRunNested) {\n";
1474  visitTupleOperation(aggregate, out);
1475  out << "}\n";
1476  out << "}\n"; // to close off pragma omp single section
1477  PRINT_END_COMMENT(out);
1478  }
1479  void visitAggregate(const Aggregate& aggregate, std::ostream& out) override {
1480  PRINT_BEGIN_COMMENT(out);
1481  // get some properties
1482  const auto* rel = synthesiser.lookup(aggregate.getRelation());
1483  auto relName = synthesiser.getRelationName(rel);
1484  auto ctxName = "READ_OP_CONTEXT(" + synthesiser.getOpContextName(*rel) + ")";
1485  auto identifier = aggregate.getTupleId();
1486 
1487  // declare environment variable
1488  out << "Tuple<RamDomain,1> env" << identifier << ";\n";
1489 
1490  // special case: counting number elements over an unrestricted predicate
1491  if (aggregate.getFunction() == AggregateOp::COUNT && isTrue(&aggregate.getCondition())) {
1492  // shortcut: use relation size
1493  out << "env" << identifier << "[0] = " << relName << "->"
1494  << "size();\n";
1495  visitTupleOperation(aggregate, out);
1496  PRINT_END_COMMENT(out);
1497  return;
1498  }
1499 
1500  out << "bool shouldRunNested = false;\n";
1501 
1502  // init result
1503  std::string init;
1504  switch (aggregate.getFunction()) {
1505  case AggregateOp::MIN: init = "MAX_RAM_SIGNED"; break;
1506  case AggregateOp::FMIN: init = "MAX_RAM_FLOAT"; break;
1507  case AggregateOp::UMIN: init = "MAX_RAM_UNSIGNED"; break;
1508  case AggregateOp::MAX: init = "MIN_RAM_SIGNED"; break;
1509  case AggregateOp::FMAX: init = "MIN_RAM_FLOAT"; break;
1510  case AggregateOp::UMAX: init = "MIN_RAM_UNSIGNED"; break;
1511  case AggregateOp::COUNT:
1512  init = "0";
1513  out << "shouldRunNested = true;\n";
1514  break;
1515 
1516  case AggregateOp::MEAN: init = "0"; break;
1517 
1518  case AggregateOp::FSUM:
1519  case AggregateOp::USUM:
1520  case AggregateOp::SUM:
1521  init = "0";
1522  out << "shouldRunNested = true;\n";
1523  break;
1524  }
1525 
1526  std::string type;
1527  switch (getTypeAttributeAggregate(aggregate.getFunction())) {
1528  case TypeAttribute::Signed: type = "RamSigned"; break;
1529  case TypeAttribute::Unsigned: type = "RamUnsigned"; break;
1530  case TypeAttribute::Float: type = "RamFloat"; break;
1531 
1532  case TypeAttribute::Symbol:
1533  case TypeAttribute::ADT:
1534  case TypeAttribute::Record:
1535  default: type = "RamDomain"; break;
1536  }
1537  out << type << " res0 = " << init << ";\n";
1538 
1539  if (aggregate.getFunction() == AggregateOp::MEAN) {
1540  out << "RamUnsigned res1 = 0;\n";
1541  }
1542 
1543  // check whether there is an index to use
1544  out << "for(const auto& env" << identifier << " : "
1545  << "*" << relName << ") {\n";
1546 
1547  // produce condition inside the loop
1548  out << "if( ";
1549  visit(aggregate.getCondition(), out);
1550  out << ") {\n";
1551 
1552  out << "shouldRunNested = true;\n";
1553  // pick function
1554  switch (aggregate.getFunction()) {
1555  case AggregateOp::FMIN:
1556  case AggregateOp::UMIN:
1557  case AggregateOp::MIN:
1558  out << "res0 = std::min(res0, ramBitCast<" << type << ">(";
1559  visit(aggregate.getExpression(), out);
1560  out << "));\n";
1561  break;
1562  case AggregateOp::FMAX:
1563  case AggregateOp::UMAX:
1564  case AggregateOp::MAX:
1565  out << "res0 = std::max(res0,ramBitCast<" << type << ">(";
1566  visit(aggregate.getExpression(), out);
1567  out << "));\n";
1568  break;
1569  case AggregateOp::COUNT: out << "++res0\n;"; break;
1570  case AggregateOp::FSUM:
1571  case AggregateOp::USUM:
1572  case AggregateOp::SUM:
1573  out << "res0 += "
1574  << "ramBitCast<" << type << ">(";
1575  ;
1576  visit(aggregate.getExpression(), out);
1577  out << ");\n";
1578  break;
1579 
1580  case AggregateOp::MEAN:
1581  out << "res0 += "
1582  << "ramBitCast<RamFloat>(";
1583  visit(aggregate.getExpression(), out);
1584  out << ");\n";
1585  out << "++res1;\n";
1586  break;
1587  }
1588 
1589  out << "}\n";
1590 
1591  // end aggregator loop
1592  out << "}\n";
1593 
1594  if (aggregate.getFunction() == AggregateOp::MEAN) {
1595  out << "res0 = res0 / res1;\n";
1596  }
1597 
1598  // write result into environment tuple
1599  out << "env" << identifier << "[0] = ramBitCast(res0);\n";
1600 
1601  // check whether there exists a min/max first before next loop
1602  out << "if (shouldRunNested) {\n";
1603  visitTupleOperation(aggregate, out);
1604  out << "}\n";
1605 
1606  PRINT_END_COMMENT(out);
1607  }
1608 
1609  void visitFilter(const Filter& filter, std::ostream& out) override {
1610  PRINT_BEGIN_COMMENT(out);
1611  out << "if( ";
1612  visit(filter.getCondition(), out);
1613  out << ") {\n";
1614  visitNestedOperation(filter, out);
1615  out << "}\n";
1616  PRINT_END_COMMENT(out);
1617  }
1618 
1619  void visitBreak(const Break& breakOp, std::ostream& out) override {
1620  PRINT_BEGIN_COMMENT(out);
1621  out << "if( ";
1622  visit(breakOp.getCondition(), out);
1623  out << ") break;\n";
1624  visitNestedOperation(breakOp, out);
1625  PRINT_END_COMMENT(out);
1626  }
1627 
1628  void visitProject(const Project& project, std::ostream& out) override {
1629  PRINT_BEGIN_COMMENT(out);
1630  const auto* rel = synthesiser.lookup(project.getRelation());
1631  auto arity = rel->getArity();
1632  auto relName = synthesiser.getRelationName(rel);
1633  auto ctxName = "READ_OP_CONTEXT(" + synthesiser.getOpContextName(*rel) + ")";
1634 
1635  // create projected tuple
1636  out << "Tuple<RamDomain," << arity << "> tuple{{" << join(project.getValues(), ",", rec)
1637  << "}};\n";
1638 
1639  // insert tuple
1640  out << relName << "->"
1641  << "insert(tuple," << ctxName << ");\n";
1642 
1643  PRINT_END_COMMENT(out);
1644  }
1645 
1646  // -- conditions --
1647 
1648  void visitTrue(const True&, std::ostream& out) override {
1649  PRINT_BEGIN_COMMENT(out);
1650  out << "true";
1651  PRINT_END_COMMENT(out);
1652  }
1653 
1654  void visitFalse(const False&, std::ostream& out) override {
1655  PRINT_BEGIN_COMMENT(out);
1656  out << "false";
1657  PRINT_END_COMMENT(out);
1658  }
1659 
1660  void visitConjunction(const Conjunction& conj, std::ostream& out) override {
1661  PRINT_BEGIN_COMMENT(out);
1662  visit(conj.getLHS(), out);
1663  out << " && ";
1664  visit(conj.getRHS(), out);
1665  PRINT_END_COMMENT(out);
1666  }
1667 
1668  void visitNegation(const Negation& neg, std::ostream& out) override {
1669  PRINT_BEGIN_COMMENT(out);
1670  out << "!(";
1671  visit(neg.getOperand(), out);
1672  out << ")";
1673  PRINT_END_COMMENT(out);
1674  }
1675 
1676  void visitConstraint(const Constraint& rel, std::ostream& out) override {
1677  // clang-format off
1678 #define EVAL_CHILD(ty, idx) \
1679  out << "ramBitCast<" #ty ">("; \
1680  visit(rel.idx(), out); \
1681  out << ")"
1682 #define COMPARE_NUMERIC(ty, op) \
1683  out << "("; \
1684  EVAL_CHILD(ty, getLHS); \
1685  out << " " #op " "; \
1686  EVAL_CHILD(ty, getRHS); \
1687  out << ")"; \
1688  break
1689 #define COMPARE_STRING(op) \
1690  out << "(symTable.resolve("; \
1691  EVAL_CHILD(RamDomain, getLHS); \
1692  out << ") " #op " symTable.resolve("; \
1693  EVAL_CHILD(RamDomain, getRHS); \
1694  out << "))"; \
1695  break
1696 #define COMPARE_EQ_NE(opCode, op) \
1697  case BinaryConstraintOp:: opCode: COMPARE_NUMERIC(RamDomain , op); \
1698  case BinaryConstraintOp::F##opCode: COMPARE_NUMERIC(RamFloat , op);
1699 #define COMPARE(opCode, op) \
1700  case BinaryConstraintOp:: opCode: COMPARE_NUMERIC(RamSigned , op); \
1701  case BinaryConstraintOp::U##opCode: COMPARE_NUMERIC(RamUnsigned, op); \
1702  case BinaryConstraintOp::F##opCode: COMPARE_NUMERIC(RamFloat , op); \
1703  case BinaryConstraintOp::S##opCode: COMPARE_STRING(op);
1704  // clang-format on
1705 
1706  PRINT_BEGIN_COMMENT(out);
1707  switch (rel.getOperator()) {
1708  // comparison operators
1709  COMPARE_EQ_NE(EQ, ==)
1710  COMPARE_EQ_NE(NE, !=)
1711 
1712  COMPARE(LT, <)
1713  COMPARE(LE, <=)
1714  COMPARE(GT, >)
1715  COMPARE(GE, >=)
1716 
1717  // strings
1718  case BinaryConstraintOp::MATCH: {
1719  out << "regex_wrapper(symTable.resolve(";
1720  visit(rel.getLHS(), out);
1721  out << "),symTable.resolve(";
1722  visit(rel.getRHS(), out);
1723  out << "))";
1724  break;
1725  }
1727  out << "!regex_wrapper(symTable.resolve(";
1728  visit(rel.getLHS(), out);
1729  out << "),symTable.resolve(";
1730  visit(rel.getRHS(), out);
1731  out << "))";
1732  break;
1733  }
1735  out << "(symTable.resolve(";
1736  visit(rel.getRHS(), out);
1737  out << ").find(symTable.resolve(";
1738  visit(rel.getLHS(), out);
1739  out << ")) != std::string::npos)";
1740  break;
1741  }
1743  out << "(symTable.resolve(";
1744  visit(rel.getRHS(), out);
1745  out << ").find(symTable.resolve(";
1746  visit(rel.getLHS(), out);
1747  out << ")) == std::string::npos)";
1748  break;
1749  }
1750  }
1751 
1752  PRINT_END_COMMENT(out);
1753 
1754 #undef EVAL_CHILD
1755 #undef COMPARE_NUMERIC
1756 #undef COMPARE_STRING
1757 #undef COMPARE
1758 #undef COMPARE_EQ_NE
1759  }
1760 
1761  void visitEmptinessCheck(const EmptinessCheck& emptiness, std::ostream& out) override {
1762  PRINT_BEGIN_COMMENT(out);
1763  out << synthesiser.getRelationName(synthesiser.lookup(emptiness.getRelation())) << "->"
1764  << "empty()";
1765  PRINT_END_COMMENT(out);
1766  }
1767 
1768  void visitRelationSize(const RelationSize& size, std::ostream& out) override {
1769  PRINT_BEGIN_COMMENT(out);
1770  out << "(RamDomain)" << synthesiser.getRelationName(synthesiser.lookup(size.getRelation()))
1771  << "->"
1772  << "size()";
1773  PRINT_END_COMMENT(out);
1774  }
1775 
1776  void visitExistenceCheck(const ExistenceCheck& exists, std::ostream& out) override {
1777  PRINT_BEGIN_COMMENT(out);
1778  // get some details
1779  const auto* rel = synthesiser.lookup(exists.getRelation());
1780  auto relName = synthesiser.getRelationName(rel);
1781  auto ctxName = "READ_OP_CONTEXT(" + synthesiser.getOpContextName(*rel) + ")";
1782  auto arity = rel->getArity();
1783  assert(arity > 0 && "AstToRamTranslator failed");
1784  std::string after;
1785  if (Global::config().has("profile") && !synthesiser.lookup(exists.getRelation())->isTemp()) {
1786  out << R"_((reads[)_" << synthesiser.lookupReadIdx(rel->getName()) << R"_(]++,)_";
1787  after = ")";
1788  }
1789 
1790  // if it is total we use the contains function
1791  if (isa->isTotalSignature(&exists)) {
1792  out << relName << "->"
1793  << "contains(Tuple<RamDomain," << arity << ">{{" << join(exists.getValues(), ",", rec)
1794  << "}}," << ctxName << ")" << after;
1795  PRINT_END_COMMENT(out);
1796  return;
1797  }
1798 
1799  auto rangePatternLower = exists.getValues();
1800  auto rangePatternUpper = exists.getValues();
1801 
1802  auto rangeBounds = getPaddedRangeBounds(*rel, rangePatternLower, rangePatternUpper);
1803  // else we conduct a range query
1804  out << "!" << relName << "->"
1805  << "lowerUpperRange";
1806  out << "_" << isa->getSearchSignature(&exists);
1807  out << "(" << rangeBounds.first.str() << "," << rangeBounds.second.str() << "," << ctxName
1808  << ").empty()" << after;
1809  PRINT_END_COMMENT(out);
1810  }
1811 
1812  void visitProvenanceExistenceCheck(
1813  const ProvenanceExistenceCheck& provExists, std::ostream& out) override {
1814  PRINT_BEGIN_COMMENT(out);
1815  // get some details
1816  const auto* rel = synthesiser.lookup(provExists.getRelation());
1817  auto relName = synthesiser.getRelationName(rel);
1818  auto ctxName = "READ_OP_CONTEXT(" + synthesiser.getOpContextName(*rel) + ")";
1819  auto arity = rel->getArity();
1820  auto auxiliaryArity = rel->getAuxiliaryArity();
1821 
1822  // provenance not exists is never total, conduct a range query
1823  out << "[&]() -> bool {\n";
1824  out << "auto existenceCheck = " << relName << "->"
1825  << "lowerUpperRange";
1826  out << "_" << isa->getSearchSignature(&provExists);
1827 
1828  // parts refers to payload + rule number
1829  size_t parts = arity - auxiliaryArity + 1;
1830 
1831  // make a copy of provExists.getValues() so we can be sure that vals is always the same vector
1832  // since provExists.getValues() creates a new vector on the stack each time
1833  auto vals = provExists.getValues();
1834 
1835  // sanity check to ensure that all payload values are specified
1836  for (size_t i = 0; i < arity - auxiliaryArity; i++) {
1837  assert(!isUndefValue(vals[i]) &&
1838  "ProvenanceExistenceCheck should always be specified for payload");
1839  }
1840 
1841  auto valsCopy = std::vector<Expression*>(vals.begin(), vals.begin() + parts);
1842  auto rangeBounds = getPaddedRangeBounds(*rel, valsCopy, valsCopy);
1843 
1844  // remove the ending }} from both strings
1845  rangeBounds.first.seekp(-2, std::ios_base::end);
1846  rangeBounds.second.seekp(-2, std::ios_base::end);
1847 
1848  // extra bounds for provenance height annotations
1849  for (size_t i = 0; i < auxiliaryArity - 2; i++) {
1850  rangeBounds.first << ",ramBitCast<RamDomain, RamSigned>(MIN_RAM_SIGNED)";
1851  rangeBounds.second << ",ramBitCast<RamDomain, RamSigned>(MAX_RAM_SIGNED)";
1852  }
1853  rangeBounds.first << ",ramBitCast<RamDomain, RamSigned>(MIN_RAM_SIGNED)}}";
1854  rangeBounds.second << ",ramBitCast<RamDomain, RamSigned>(MAX_RAM_SIGNED)}}";
1855 
1856  out << "(" << rangeBounds.first.str() << "," << rangeBounds.second.str() << "," << ctxName
1857  << ");\n";
1858  out << "if (existenceCheck.empty()) return false; else return ((*existenceCheck.begin())["
1859  << arity - auxiliaryArity + 1 << "] <= ";
1860 
1861  visit(*(provExists.getValues()[arity - auxiliaryArity + 1]), out);
1862  out << ")";
1863  out << ";}()\n";
1864  PRINT_END_COMMENT(out);
1865  }
1866 
1867  // -- values --
1868  void visitUnsignedConstant(const UnsignedConstant& constant, std::ostream& out) override {
1869  PRINT_BEGIN_COMMENT(out);
1870  out << "RamUnsigned(" << constant.getValue() << ")";
1871  PRINT_END_COMMENT(out);
1872  }
1873 
1874  void visitFloatConstant(const FloatConstant& constant, std::ostream& out) override {
1875  PRINT_BEGIN_COMMENT(out);
1876  out << "RamFloat(" << constant.getValue() << ")";
1877  PRINT_END_COMMENT(out);
1878  }
1879 
1880  void visitSignedConstant(const SignedConstant& constant, std::ostream& out) override {
1881  PRINT_BEGIN_COMMENT(out);
1882  out << "RamSigned(" << constant.getConstant() << ")";
1883  PRINT_END_COMMENT(out);
1884  }
1885 
1886  void visitTupleElement(const TupleElement& access, std::ostream& out) override {
1887  PRINT_BEGIN_COMMENT(out);
1888  out << "env" << access.getTupleId() << "[" << access.getElement() << "]";
1889  PRINT_END_COMMENT(out);
1890  }
1891 
1892  void visitAutoIncrement(const AutoIncrement& /*inc*/, std::ostream& out) override {
1893  PRINT_BEGIN_COMMENT(out);
1894  out << "(ctr++)";
1895  PRINT_END_COMMENT(out);
1896  }
1897 
1898  void visitIntrinsicOperator(const IntrinsicOperator& op, std::ostream& out) override {
1899 #define MINMAX_SYMBOL(op) \
1900  { \
1901  out << "symTable.lookup(" #op "({"; \
1902  for (auto& cur : args) { \
1903  out << "symTable.resolve("; \
1904  visit(cur, out); \
1905  out << "), "; \
1906  } \
1907  out << "}))"; \
1908  break; \
1909  }
1910 
1911  PRINT_BEGIN_COMMENT(out);
1912 
1913 // clang-format off
1914 #define UNARY_OP(opcode, ty, op) \
1915  case FunctorOp::opcode: { \
1916  out << "(" #op "(ramBitCast<" #ty ">("; \
1917  visit(args[0], out); \
1918  out << ")))"; \
1919  break; \
1920  }
1921 #define UNARY_OP_I(opcode, op) UNARY_OP( opcode, RamSigned , op)
1922 #define UNARY_OP_U(opcode, op) UNARY_OP(U##opcode, RamUnsigned, op)
1923 #define UNARY_OP_F(opcode, op) UNARY_OP(F##opcode, RamFloat , op)
1924 #define UNARY_OP_INTEGRAL(opcode, op) \
1925  UNARY_OP_I(opcode, op) \
1926  UNARY_OP_U(opcode, op)
1927 
1928 
1929 #define BINARY_OP_EXPR_EX(ty, op, rhs_post) \
1930  { \
1931  out << "(ramBitCast<" #ty ">("; \
1932  visit(args[0], out); \
1933  out << ") " #op " ramBitCast<" #ty ">("; \
1934  visit(args[1], out); \
1935  out << rhs_post "))"; \
1936  break; \
1937  }
1938 #define BINARY_OP_EXPR(ty, op) BINARY_OP_EXPR_EX(ty, op, "")
1939 #define BINARY_OP_EXPR_SHIFT(ty, op) BINARY_OP_EXPR_EX(ty, op, " & RAM_BIT_SHIFT_MASK")
1940 #define BINARY_OP_EXPR_LOGICAL(ty, op) out << "RamDomain"; BINARY_OP_EXPR(ty, op)
1941 
1942 #define BINARY_OP_INTEGRAL(opcode, op) \
1943  case FunctorOp:: opcode: BINARY_OP_EXPR(RamSigned , op) \
1944  case FunctorOp::U##opcode: BINARY_OP_EXPR(RamUnsigned, op)
1945 #define BINARY_OP_LOGICAL(opcode, op) \
1946  case FunctorOp:: opcode: BINARY_OP_EXPR_LOGICAL(RamSigned , op) \
1947  case FunctorOp::U##opcode: BINARY_OP_EXPR_LOGICAL(RamUnsigned, op)
1948 #define BINARY_OP_NUMERIC(opcode, op) \
1949  BINARY_OP_INTEGRAL(opcode, op) \
1950  case FunctorOp::F##opcode: BINARY_OP_EXPR(RamFloat , op)
1951 #define BINARY_OP_BITWISE(opcode, op) \
1952  case FunctorOp:: opcode: /* fall through */ \
1953  case FunctorOp::U##opcode: BINARY_OP_EXPR(RamDomain, op)
1954 #define BINARY_OP_INTEGRAL_SHIFT(opcode, op, tySigned, tyUnsigned) \
1955  case FunctorOp:: opcode: BINARY_OP_EXPR_SHIFT(tySigned , op) \
1956  case FunctorOp::U##opcode: BINARY_OP_EXPR_SHIFT(tyUnsigned, op)
1957 
1958 #define BINARY_OP_EXP(opcode, ty, tyTemp) \
1959  case FunctorOp::opcode: { \
1960  out << "static_cast<" #ty ">(static_cast<" #tyTemp ">(std::pow(ramBitCast<" #ty ">("; \
1961  visit(args[0], out); \
1962  out << "), ramBitCast<" #ty ">("; \
1963  visit(args[1], out); \
1964  out << "))))"; \
1965  break; \
1966  }
1967 
1968 #define NARY_OP(opcode, ty, op) \
1969  case FunctorOp::opcode: { \
1970  out << #op "({"; \
1971  for (auto& cur : args) { \
1972  out << "ramBitCast<" #ty ">("; \
1973  visit(cur, out); \
1974  out << "), "; \
1975  } \
1976  out << "})"; \
1977  break; \
1978  }
1979 #define NARY_OP_ORDERED(opcode, op) \
1980  NARY_OP( opcode, RamSigned , op) \
1981  NARY_OP(U##opcode, RamUnsigned, op) \
1982  NARY_OP(F##opcode, RamFloat , op)
1983 
1984 
1985 #define CONV_TO_STRING(opcode, ty) \
1986  case FunctorOp::opcode: { \
1987  out << "symTable.lookup(std::to_string("; \
1988  visit(args[0], out); \
1989  out << "))"; \
1990  } break;
1991 #define CONV_FROM_STRING(opcode, ty) \
1992  case FunctorOp::opcode: { \
1993  out << "souffle::evaluator::symbol2numeric<" #ty ">(symTable.resolve("; \
1994  visit(args[0], out); \
1995  out << "))"; \
1996  } break;
1997  // clang-format on
1998 
1999  auto args = op.getArguments();
2000  switch (op.getOperator()) {
2001  /** Unary Functor Operators */
2002  case FunctorOp::ORD: {
2003  visit(args[0], out);
2004  break;
2005  }
2006  // TODO: change the signature of `STRLEN` to return an unsigned?
2007  case FunctorOp::STRLEN: {
2008  out << "static_cast<RamSigned>(symTable.resolve(";
2009  visit(args[0], out);
2010  out << ").size())";
2011  break;
2012  }
2013 
2014  // clang-format off
2015  UNARY_OP_I(NEG, -)
2016  UNARY_OP_F(NEG, -)
2017 
2018  UNARY_OP_INTEGRAL(BNOT, ~)
2019  UNARY_OP_INTEGRAL(LNOT, (RamDomain)!)
2020 
2021  /** numeric coersions follow C++ semantics. */
2022  UNARY_OP(F2I, RamFloat , static_cast<RamSigned>)
2023  UNARY_OP(F2U, RamFloat , static_cast<RamUnsigned>)
2024  UNARY_OP(I2U, RamSigned , static_cast<RamUnsigned>)
2025  UNARY_OP(I2F, RamSigned , static_cast<RamFloat>)
2026  UNARY_OP(U2I, RamUnsigned, static_cast<RamSigned>)
2027  UNARY_OP(U2F, RamUnsigned, static_cast<RamFloat>)
2028 
2029  CONV_TO_STRING(F2S, RamFloat)
2032 
2036 
2037  /** Binary Functor Operators */
2038  // arithmetic
2039 
2040  BINARY_OP_NUMERIC(ADD, +)
2041  BINARY_OP_NUMERIC(SUB, -)
2042  BINARY_OP_NUMERIC(MUL, *)
2043  BINARY_OP_NUMERIC(DIV, /)
2044  BINARY_OP_INTEGRAL(MOD, %)
2045 
2047 #if RAM_DOMAIN_SIZE == 32
2048  BINARY_OP_EXP(UEXP, RamUnsigned, int64_t)
2049  BINARY_OP_EXP( EXP, RamSigned , int64_t)
2050 #elif RAM_DOMAIN_SIZE == 64
2053 #else
2054 #error "unhandled domain size"
2055 #endif
2056 
2057  BINARY_OP_LOGICAL(LAND, &&)
2058  BINARY_OP_LOGICAL(LOR , ||)
2059  BINARY_OP_LOGICAL(LXOR, + souffle::evaluator::lxor_infix() +)
2060 
2061  BINARY_OP_BITWISE(BAND, &)
2062  BINARY_OP_BITWISE(BOR , |)
2063  BINARY_OP_BITWISE(BXOR, ^)
2064  // Handle left-shift as unsigned to match Java semantics of `<<`, namely:
2065  // "... `n << s` is `n` left-shifted `s` bit positions; ..."
2066  // Using `RamSigned` would imply UB due to signed overflow when shifting negatives.
2068  // For right-shift, we do need sign extension.
2070  BINARY_OP_INTEGRAL_SHIFT(BSHIFT_R_UNSIGNED, >>, RamUnsigned, RamUnsigned)
2071 
2072  NARY_OP_ORDERED(MAX, std::max)
2073  NARY_OP_ORDERED(MIN, std::min)
2074  // clang-format on
2075 
2076  case FunctorOp::SMAX: MINMAX_SYMBOL(std::max)
2077 
2078  case FunctorOp::SMIN: MINMAX_SYMBOL(std::min)
2079 
2080  // strings
2081  case FunctorOp::CAT: {
2082  out << "symTable.lookup(";
2083  size_t i = 0;
2084  while (i < args.size() - 1) {
2085  out << "symTable.resolve(";
2086  visit(args[i], out);
2087  out << ") + ";
2088  i++;
2089  }
2090  out << "symTable.resolve(";
2091  visit(args[i], out);
2092  out << "))";
2093  break;
2094  }
2095 
2096  /** Ternary Functor Operators */
2097  case FunctorOp::SUBSTR: {
2098  out << "symTable.lookup(";
2099  out << "substr_wrapper(symTable.resolve(";
2100  visit(args[0], out);
2101  out << "),(";
2102  visit(args[1], out);
2103  out << "),(";
2104  visit(args[2], out);
2105  out << ")))";
2106  break;
2107  }
2108 
2109  case FunctorOp::RANGE:
2110  case FunctorOp::URANGE:
2111  case FunctorOp::FRANGE:
2112  fatal("ICE: functor `%s` must map onto `NestedIntrinsicOperator`", op.getOperator());
2113  }
2114  PRINT_END_COMMENT(out);
2115 
2116 #undef MINMAX_SYMBOL
2117  }
2118 
2119  void visitNestedIntrinsicOperator(const NestedIntrinsicOperator& op, std::ostream& out) override {
2120  PRINT_BEGIN_COMMENT(out);
2121 
2122  auto emitHelper = [&](auto&& func) {
2123  tfm::format(out, "%s(%s, [&](auto&& env%d) {\n", func,
2124  join(op.getArguments(), ",", [&](auto& os, auto* arg) { return visit(arg, os); }),
2125  op.getTupleId());
2126  visitTupleOperation(op, out);
2127  out << "});\n";
2128 
2129  PRINT_END_COMMENT(out);
2130  };
2131 
2132  auto emitRange = [&](char const* ty) {
2133  return emitHelper(tfm::format("souffle::evaluator::runRange<%s>", ty));
2134  };
2135 
2136  switch (op.getFunction()) {
2137  case NestedIntrinsicOp::RANGE: return emitRange("RamSigned");
2138  case NestedIntrinsicOp::URANGE: return emitRange("RamUnsigned");
2139  case NestedIntrinsicOp::FRANGE: return emitRange("RamFloat");
2140  }
2141 
2143  }
2144 
2145  void visitUserDefinedOperator(const UserDefinedOperator& op, std::ostream& out) override {
2146  const std::string& name = op.getName();
2147 
2148  auto args = op.getArguments();
2149  if (op.isStateful()) {
2150  out << name << "(&symTable, &recordTable";
2151  for (auto& arg : args) {
2152  out << ",";
2153  visit(arg, out);
2154  }
2155  out << ")";
2156  } else {
2157  const std::vector<TypeAttribute>& argTypes = op.getArgsTypes();
2158 
2159  if (op.getReturnType() == TypeAttribute::Symbol) {
2160  out << "symTable.lookup(";
2161  }
2162  out << name << "(";
2163 
2164  for (size_t i = 0; i < args.size(); i++) {
2165  if (i > 0) {
2166  out << ",";
2167  }
2168  switch (argTypes[i]) {
2169  case TypeAttribute::Signed:
2170  out << "((RamSigned)";
2171  visit(args[i], out);
2172  out << ")";
2173  break;
2175  out << "((RamUnsigned)";
2176  visit(args[i], out);
2177  out << ")";
2178  break;
2179  case TypeAttribute::Float:
2180  out << "((RamFloat)";
2181  visit(args[i], out);
2182  out << ")";
2183  break;
2184  case TypeAttribute::Symbol:
2185  out << "symTable.resolve(";
2186  visit(args[i], out);
2187  out << ").c_str()";
2188  break;
2189  case TypeAttribute::ADT:
2190  case TypeAttribute::Record: fatal("unhandled type");
2191  }
2192  }
2193  out << ")";
2194  if (op.getReturnType() == TypeAttribute::Symbol) {
2195  out << ")";
2196  }
2197  }
2198  }
2199 
2200  // -- records --
2201 
2202  void visitPackRecord(const PackRecord& pack, std::ostream& out) override {
2203  PRINT_BEGIN_COMMENT(out);
2204 
2205  out << "pack(recordTable,"
2206  << "Tuple<RamDomain," << pack.getArguments().size() << ">";
2207  if (pack.getArguments().size() == 0) {
2208  out << "{{}}";
2209  } else {
2210  out << "{{ramBitCast(" << join(pack.getArguments(), "),ramBitCast(", rec) << ")}}\n";
2211  }
2212  out << ")";
2213 
2214  PRINT_END_COMMENT(out);
2215  }
2216 
2217  // -- subroutine argument --
2218 
2219  void visitSubroutineArgument(const SubroutineArgument& arg, std::ostream& out) override {
2220  out << "(args)[" << arg.getArgument() << "]";
2221  }
2222 
2223  // -- subroutine return --
2224 
2225  void visitSubroutineReturn(const SubroutineReturn& ret, std::ostream& out) override {
2226  out << "std::lock_guard<std::mutex> guard(lock);\n";
2227  for (auto val : ret.getValues()) {
2228  if (isUndefValue(val)) {
2229  out << "ret.push_back(0);\n";
2230  } else {
2231  out << "ret.push_back(";
2232  visit(val, out);
2233  out << ");\n";
2234  }
2235  }
2236  }
2237 
2238  // -- safety net --
2239 
2240  void visitUndefValue(const UndefValue&, std::ostream& /*out*/) override {
2241  fatal("Compilation error");
2242  }
2243 
2244  void visitNode(const Node& node, std::ostream& /*out*/) override {
2245  fatal("Unsupported node type: %s", typeid(node).name());
2246  }
2247  };
2248 
2249  out << std::setprecision(std::numeric_limits<RamFloat>::max_digits10);
2250  // emit code
2251  CodeEmitter(*this).visit(stmt, out);
2252 }
2253 
2254 void Synthesiser::generateCode(std::ostream& os, const std::string& id, bool& withSharedLibrary) {
2255  // ---------------------------------------------------------------
2256  // Auto-Index Generation
2257  // ---------------------------------------------------------------
2258  const SymbolTable& symTable = translationUnit.getSymbolTable();

References souffle::ram::isUndefValue().

Here is the call graph for this function:

◆ generateCode()

void souffle::synthesiser::Synthesiser::generateCode ( std::ostream &  os,
const std::string &  id,
bool &  withSharedLibrary 
)

Generate code.

Definition at line 2260 of file Synthesiser.cpp.

2271  {
2272  os << "#define _SOUFFLE_STATS\n";
2273  }
2274  os << "\n#include \"souffle/CompiledSouffle.h\"\n";
2275  if (Global::config().has("provenance")) {
2276  os << "#include <mutex>\n";
2277  os << "#include \"souffle/provenance/Explain.h\"\n";
2278  }
2279 
2280  if (Global::config().has("live-profile")) {
2281  os << "#include <thread>\n";
2282  os << "#include \"souffle/profile/Tui.h\"\n";
2283  }
2284  os << "\n";
2285  // produce external definitions for user-defined functors
2286  std::map<std::string, std::tuple<TypeAttribute, std::vector<TypeAttribute>, bool>> functors;
2287  visitDepthFirst(prog, [&](const UserDefinedOperator& op) {
2288  if (functors.find(op.getName()) == functors.end()) {
2289  functors[op.getName()] = std::make_tuple(op.getReturnType(), op.getArgsTypes(), op.isStateful());
2290  }
2291  withSharedLibrary = true;
2292  });
2293  os << "extern \"C\" {\n";
2294  for (const auto& f : functors) {
2295  // size_t arity = f.second.length() - 1;
2296  const std::string& name = f.first;
2297 
2298  const auto& functorTypes = f.second;
2299  const auto& returnType = std::get<0>(functorTypes);
2300  const auto& argsTypes = std::get<1>(functorTypes);
2301  const auto& stateful = std::get<2>(functorTypes);
2302 
2303  auto cppTypeDecl = [](TypeAttribute ty) -> char const* {
2304  switch (ty) {
2305  case TypeAttribute::Signed: return "souffle::RamSigned";
2306  case TypeAttribute::Unsigned: return "souffle::RamUnsigned";
2307  case TypeAttribute::Float: return "souffle::RamFloat";
2308  case TypeAttribute::Symbol: return "const char *";
2309  case TypeAttribute::ADT: fatal("adts cannot be used by user-defined functors");
2310  case TypeAttribute::Record: fatal("records cannot be used by user-defined functors");
2311  }
2312 
2314  };
2315 
2316  if (stateful) {
2317  os << "souffle::RamDomain " << name << "(souffle::SymbolTable *, souffle::RecordTable *";
2318  for (size_t i = 0; i < argsTypes.size(); i++) {
2319  os << ",souffle::RamDomain";
2320  }
2321  os << ");\n";
2322  } else {
2323  tfm::format(os, "%s %s(%s);\n", cppTypeDecl(returnType), name,
2324  join(map(argsTypes, cppTypeDecl), ","));
2325  }
2326  }
2327  os << "}\n";
2328  os << "\n";
2329  os << "namespace souffle {\n";
2330  os << "static const RamDomain RAM_BIT_SHIFT_MASK = RAM_DOMAIN_SIZE - 1;\n";
2331 
2332  // synthesise data-structures for relations
2333  for (auto rel : prog.getRelations()) {
2334  bool isProvInfo = rel->getRepresentation() == RelationRepresentation::INFO;
2335  auto relationType = Relation::getSynthesiserRelation(*rel, idxAnalysis->getIndexes(rel->getName()),
2336  Global::config().has("provenance") && !isProvInfo);
2337 
2338  generateRelationTypeStruct(os, std::move(relationType));
2339  }
2340  os << '\n';
2341 
2342  os << "class " << classname << " : public SouffleProgram {\n";
2343 
2344  // regex wrapper
2345  os << "private:\n";
2346  os << "static inline bool regex_wrapper(const std::string& pattern, const std::string& text) {\n";
2347  os << " bool result = false; \n";
2348  os << " try { result = std::regex_match(text, std::regex(pattern)); } catch(...) { \n";
2349  os << " std::cerr << \"warning: wrong pattern provided for match(\\\"\" << pattern << \"\\\",\\\"\" "
2350  "<< text << \"\\\").\\n\";\n}\n";
2351  os << " return result;\n";
2352  os << "}\n";
2353 
2354  // substring wrapper
2355  os << "private:\n";
2356  os << "static inline std::string substr_wrapper(const std::string& str, size_t idx, size_t len) {\n";
2357  os << " std::string result; \n";
2358  os << " try { result = str.substr(idx,len); } catch(...) { \n";
2359  os << " std::cerr << \"warning: wrong index position provided by substr(\\\"\";\n";
2360  os << " std::cerr << str << \"\\\",\" << (int32_t)idx << \",\" << (int32_t)len << \") "
2361  "functor.\\n\";\n";
2362  os << " } return result;\n";
2363  os << "}\n";
2364 
2365  if (Global::config().has("profile")) {
2366  os << "std::string profiling_fname;\n";
2367  }
2368 
2369  os << "public:\n";
2370 
2371  // declare symbol table
2372  os << "// -- initialize symbol table --\n";
2373 
2374  os << "SymbolTable symTable";
2375  if (symTable.size() > 0) {
2376  os << "{\n";
2377  for (size_t i = 0; i < symTable.size(); i++) {
2378  os << "\tR\"_(" << symTable.resolve(i) << ")_\",\n";
2379  }
2380  os << "}";
2381  }
2382  os << ";";
2383 
2384  // declare record table
2385  os << "// -- initialize record table --\n";
2386 
2387  os << "RecordTable recordTable;"
2388  << "\n";
2389 
2390  if (Global::config().has("profile")) {
2391  os << "private:\n";
2392  size_t numFreq = 0;
2393  visitDepthFirst(prog, [&](const Statement&) { numFreq++; });
2394  os << " size_t freqs[" << numFreq << "]{};\n";
2395  size_t numRead = 0;
2396  for (auto rel : prog.getRelations()) {
2397  if (!rel->isTemp()) {
2398  numRead++;
2399  }
2400  }
2401  os << " size_t reads[" << numRead << "]{};\n";
2402  }
2403 
2404  // print relation definitions
2405  std::stringstream initCons; // initialization of constructor
2406  std::stringstream registerRel; // registration of relations
2407  auto initConsSep = [&, empty = true]() mutable -> std::stringstream& {
2408  initCons << (empty ? "\n: " : "\n, ");
2409  empty = false;
2410  return initCons;
2411  };
2412 
2413  // `pf` must be a ctor param (see below)
2414  if (Global::config().has("profile")) {
2415  initConsSep() << "profiling_fname(std::move(pf))";
2416  }
2417 
2418  int relCtr = 0;
2419  std::set<std::string> storeRelations;
2420  std::set<std::string> loadRelations;
2421  std::set<const IO*> loadIOs;
2422  std::set<const IO*> storeIOs;
2423 
2424  // collect load/store operations/relations
2425  visitDepthFirst(prog, [&](const IO& io) {
2426  auto op = io.get("operation");
2427  if (op == "input") {
2428  loadRelations.insert(io.getRelation());
2429  loadIOs.insert(&io);
2430  } else if (op == "printsize" || op == "output") {
2431  storeRelations.insert(io.getRelation());
2432  storeIOs.insert(&io);
2433  } else {
2434  assert("wrong I/O operation");
2435  }
2436  });
2437 
2438  for (auto rel : prog.getRelations()) {
2439  // get some table details
2440  const std::string& datalogName = rel->getName();
2441  const std::string& cppName = getRelationName(*rel);
2442 
2443  bool isProvInfo = rel->getRepresentation() == RelationRepresentation::INFO;
2444  auto relationType = Relation::getSynthesiserRelation(*rel, idxAnalysis->getIndexes(datalogName),
2445  Global::config().has("provenance") && !isProvInfo);
2446  const std::string& type = relationType->getTypeName();
2447 
2448  // defining table
2449  os << "// -- Table: " << datalogName << "\n";
2450 
2451  os << "Own<" << type << "> " << cppName << " = mk<" << type << ">();\n";
2452  if (!rel->isTemp()) {
2453  tfm::format(os, "souffle::RelationWrapper<%s> wrapper_%s;\n", type, cppName);
2454 
2455  auto strLitAry = [](auto&& xs) {
2456  std::stringstream ss;
2457  ss << "std::array<const char *," << xs.size() << ">{{"
2458  << join(xs, ",", [](auto&& os, auto&& x) { os << '"' << x << '"'; }) << "}}";
2459  return ss.str();
2460  };
2461 
2462  auto foundIn = [&](auto&& set) { return contains(set, rel->getName()) ? "true" : "false"; };
2463 
2464  tfm::format(initConsSep(), "wrapper_%s(%s, *%s, *this, \"%s\", %s, %s, %s)", cppName, relCtr++,
2465  cppName, datalogName, strLitAry(rel->getAttributeTypes()),
2466  strLitAry(rel->getAttributeNames()), rel->getAuxiliaryArity());
2467  tfm::format(registerRel, "addRelation(\"%s\", wrapper_%s, %s, %s);\n", datalogName, cppName,
2468  foundIn(loadRelations), foundIn(storeRelations));
2469  }
2470  }
2471  os << "public:\n";
2472 
2473  // -- constructor --
2474 
2475  os << classname;
2476  os << (Global::config().has("profile") ? "(std::string pf=\"profile.log\")" : "()");
2477  os << initCons.str() << '\n';
2478  os << "{\n";
2479  if (Global::config().has("profile")) {
2480  os << "ProfileEventSingleton::instance().setOutputFile(profiling_fname);\n";
2481  }
2482  os << registerRel.str();
2483  os << "}\n";
2484  // -- destructor --
2485 
2486  os << "~" << classname << "() {\n";
2487  os << "}\n";
2488 
2489  // issue state variables for the evaluation
2490  //
2491  // Improve compile time by storing the signal handler in one loc instead of
2492  // emitting thousands of `SignalHandler::instance()`. The volume of calls
2493  // makes GVN and register alloc very expensive, even if the call is inlined.
2494  os << R"_(
2495 private:
2496 std::string inputDirectory;
2497 std::string outputDirectory;
2498 SignalHandler* signalHandler {SignalHandler::instance()};
2499 std::atomic<RamDomain> ctr {};
2500 std::atomic<size_t> iter {};
2501 bool performIO = false;
2502 
2503 void runFunction(std::string inputDirectoryArg = "",
2504  std::string outputDirectoryArg = "",
2505  bool performIOArg = false) {
2506  this->inputDirectory = std::move(inputDirectoryArg);
2507  this->outputDirectory = std::move(outputDirectoryArg);
2508  this->performIO = performIOArg;
2509 
2510  // set default threads (in embedded mode)
2511  // if this is not set, and omp is used, the default omp setting of number of cores is used.
2512 #if defined(_OPENMP)
2513  if (0 < getNumThreads()) { omp_set_num_threads(getNumThreads()); }
2514 #endif
2515 
2516  signalHandler->set();
2517 )_";
2518  if (Global::config().has("verbose")) {
2519  os << "signalHandler->enableLogging();\n";
2520  }
2521 
2522  // add actual program body
2523  os << "// -- query evaluation --\n";
2524  if (Global::config().has("profile")) {
2525  os << "ProfileEventSingleton::instance().startTimer();\n";
2526  os << R"_(ProfileEventSingleton::instance().makeTimeEvent("@time;starttime");)_" << '\n';
2527  os << "{\n"
2528  << R"_(Logger logger("@runtime;", 0);)_" << '\n';
2529  // Store count of relations
2530  size_t relationCount = 0;
2531  for (auto rel : prog.getRelations()) {
2532  if (rel->getName()[0] != '@') {
2533  ++relationCount;
2534  }
2535  }
2536  // Store configuration
2537  os << R"_(ProfileEventSingleton::instance().makeConfigRecord("relationCount", std::to_string()_"
2538  << relationCount << "));";
2539  }
2540 
2541  // emit code
2542  emitCode(os, prog.getMain());
2543 
2544  if (Global::config().has("profile")) {
2545  os << "}\n";
2546  os << "ProfileEventSingleton::instance().stopTimer();\n";
2547  os << "dumpFreqs();\n";
2548  }
2549 
2550  // add code printing hint statistics
2551  os << "\n// -- relation hint statistics --\n";
2552 
2553  if (Global::config().has("verbose")) {
2554  for (auto rel : prog.getRelations()) {
2555  auto name = getRelationName(*rel);
2556  os << "std::cout << \"Statistics for Relation " << name << ":\\n\";\n";
2557  os << name << "->printStatistics(std::cout);\n";
2558  os << "std::cout << \"\\n\";\n";
2559  }
2560  }
2561 
2562  os << "signalHandler->reset();\n";
2563 
2564  os << "}\n"; // end of runFunction() method
2565 
2566  // add methods to run with and without performing IO (mainly for the interface)
2567  os << "public:\nvoid run() override { runFunction(\"\", \"\", "
2568  "false); }\n";
2569  os << "public:\nvoid runAll(std::string inputDirectoryArg = \"\", std::string outputDirectoryArg = \"\") "
2570  "override { ";
2571  if (Global::config().has("live-profile")) {
2572  os << "std::thread profiler([]() { profile::Tui().runProf(); });\n";
2573  }
2574  os << "runFunction(inputDirectoryArg, outputDirectoryArg, true);\n";
2575  if (Global::config().has("live-profile")) {
2576  os << "if (profiler.joinable()) { profiler.join(); }\n";
2577  }
2578  os << "}\n";
2579  // issue printAll method
2580  os << "public:\n";
2581  os << "void printAll(std::string outputDirectoryArg = \"\") override {\n";
2582 
2583  // print directives as C++ initializers
2584  auto printDirectives = [&](const std::map<std::string, std::string>& registry) {
2585  auto cur = registry.begin();
2586  if (cur == registry.end()) {
2587  return;
2588  }
2589  os << "{{\"" << cur->first << "\",\"" << escape(cur->second) << "\"}";
2590  ++cur;
2591  for (; cur != registry.end(); ++cur) {
2592  os << ",{\"" << cur->first << "\",\"" << escape(cur->second) << "\"}";
2593  }
2594  os << '}';
2595  };
2596 
2597  for (auto store : storeIOs) {
2598  auto const& directive = store->getDirectives();
2599  os << "try {";
2600  os << "std::map<std::string, std::string> directiveMap(";
2601  printDirectives(directive);
2602  os << ");\n";
2603  os << R"_(if (!outputDirectoryArg.empty()) {)_";
2604  os << R"_(directiveMap["output-dir"] = outputDirectoryArg;)_";
2605  os << "}\n";
2606  os << "IOSystem::getInstance().getWriter(";
2607  os << "directiveMap, symTable, recordTable";
2608  os << ")->writeAll(*" << getRelationName(lookup(store->getRelation())) << ");\n";
2609 
2610  os << "} catch (std::exception& e) {std::cerr << e.what();exit(1);}\n";
2611  }
2612  os << "}\n"; // end of printAll() method
2613 
2614  // issue loadAll method
2615  os << "public:\n";
2616  os << "void loadAll(std::string inputDirectoryArg = \"\") override {\n";
2617 
2618  for (auto load : loadIOs) {
2619  os << "try {";
2620  os << "std::map<std::string, std::string> directiveMap(";
2621  printDirectives(load->getDirectives());
2622  os << ");\n";
2623  os << R"_(if (!inputDirectoryArg.empty()) {)_";
2624  os << R"_(directiveMap["fact-dir"] = inputDirectoryArg;)_";
2625  os << "}\n";
2626  os << "IOSystem::getInstance().getReader(";
2627  os << "directiveMap, symTable, recordTable";
2628  os << ")->readAll(*" << getRelationName(lookup(load->getRelation()));
2629  os << ");\n";
2630  os << "} catch (std::exception& e) {std::cerr << \"Error loading data: \" << e.what() << "
2631  "'\\n';}\n";
2632  }
2633 
2634  os << "}\n"; // end of loadAll() method
2635  // issue dump methods
2636  auto dumpRelation = [&](const ram::Relation& ramRelation) {
2637  const auto& relName = getRelationName(ramRelation);
2638  const auto& name = ramRelation.getName();
2639  const auto& attributesTypes = ramRelation.getAttributeTypes();
2640 
2641  Json relJson = Json::object{{"arity", static_cast<long long>(attributesTypes.size())},
2642  {"auxArity", static_cast<long long>(0)},
2643  {"types", Json::array(attributesTypes.begin(), attributesTypes.end())}};
2644 
2645  Json types = Json::object{{"relation", relJson}};
2646 
2647  os << "try {";
2648  os << "std::map<std::string, std::string> rwOperation;\n";
2649  os << "rwOperation[\"IO\"] = \"stdout\";\n";
2650  os << R"(rwOperation["name"] = ")" << name << "\";\n";
2651  os << "rwOperation[\"types\"] = ";
2652  os << "\"" << escapeJSONstring(types.dump()) << "\"";
2653  os << ";\n";
2654  os << "IOSystem::getInstance().getWriter(";
2655  os << "rwOperation, symTable, recordTable";
2656  os << ")->writeAll(*" << relName << ");\n";
2657  os << "} catch (std::exception& e) {std::cerr << e.what();exit(1);}\n";
2658  };
2659 
2660  // dump inputs
2661  os << "public:\n";
2662  os << "void dumpInputs() override {\n";
2663  for (auto load : loadIOs) {
2664  dumpRelation(*lookup(load->getRelation()));
2665  }
2666  os << "}\n"; // end of dumpInputs() method
2667 
2668  // dump outputs
2669  os << "public:\n";
2670  os << "void dumpOutputs() override {\n";
2671  for (auto store : storeIOs) {
2672  dumpRelation(*lookup(store->getRelation()));
2673  }
2674  os << "}\n"; // end of dumpOutputs() method
2675 
2676  os << "public:\n";
2677  os << "SymbolTable& getSymbolTable() override {\n";
2678  os << "return symTable;\n";
2679  os << "}\n"; // end of getSymbolTable() method
2680 
2681  os << "RecordTable& getRecordTable() override {\n";
2682  os << "return recordTable;\n";
2683  os << "}\n"; // end of getRecordTable() method
2684 
2685  if (!prog.getSubroutines().empty()) {
2686  // generate subroutine adapter
2687  os << "void executeSubroutine(std::string name, const std::vector<RamDomain>& args, "
2688  "std::vector<RamDomain>& ret) override {\n";
2689  // subroutine number
2690  size_t subroutineNum = 0;
2691  for (auto& sub : prog.getSubroutines()) {
2692  os << "if (name == \"" << sub.first << "\") {\n"
2693  << "subroutine_" << subroutineNum
2694  << "(args, ret);\n" // subroutine_<i> to deal with special characters in relation names
2695  << "return;"
2696  << "}\n";
2697  subroutineNum++;
2698  }
2699  os << "fatal(\"unknown subroutine\");\n";
2700  os << "}\n"; // end of executeSubroutine
2701 
2702  // generate method for each subroutine
2703  subroutineNum = 0;
2704  for (auto& sub : prog.getSubroutines()) {
2705  // silence unused argument warnings on MSVC
2706  os << "#ifdef _MSC_VER\n";
2707  os << "#pragma warning(disable: 4100)\n";
2708  os << "#endif // _MSC_VER\n";
2709 
2710  // issue method header
2711  os << "void "
2712  << "subroutine_" << subroutineNum
2713  << "(const std::vector<RamDomain>& args, "
2714  "std::vector<RamDomain>& ret) {\n";
2715 
2716  // issue lock variable for return statements
2717  bool needLock = false;
2718  visitDepthFirst(*sub.second, [&](const SubroutineReturn&) { needLock = true; });
2719  if (needLock) {
2720  os << "std::mutex lock;\n";
2721  }
2722 
2723  // emit code for subroutine
2724  emitCode(os, *sub.second);
2725 
2726  // issue end of subroutine
2727  os << "}\n";
2728 
2729  // restore unused argument warning
2730  os << "#ifdef _MSC_VER\n";
2731  os << "#pragma warning(default: 4100)\n";
2732  os << "#endif // _MSC_VER\n";
2733  subroutineNum++;
2734  }
2735  }
2736  // dumpFreqs method
2737  // Frequency counts must be emitted after subroutines otherwise lookup tables
2738  // are not populated.
2739  if (Global::config().has("profile")) {
2740  os << "private:\n";
2741  os << "void dumpFreqs() {\n";
2742  for (auto const& cur : idxMap) {
2743  os << "\tProfileEventSingleton::instance().makeQuantityEvent(R\"_(" << cur.first << ")_\", freqs["
2744  << cur.second << "],0);\n";
2745  }
2746  for (auto const& cur : neIdxMap) {
2747  os << "\tProfileEventSingleton::instance().makeQuantityEvent(R\"_(@relation-reads;" << cur.first
2748  << ")_\", reads[" << cur.second << "],0);\n";
2749  }
2750  os << "}\n"; // end of dumpFreqs() method
2751  }
2752  os << "};\n"; // end of class declaration
2753 
2754  // hidden hooks
2755  os << "SouffleProgram *newInstance_" << id << "(){return new " << classname << ";}\n";
2756  os << "SymbolTable *getST_" << id << "(SouffleProgram *p){return &reinterpret_cast<" << classname
2757  << "*>(p)->symTable;}\n";
2758 
2759  os << "\n#ifdef __EMBEDDED_SOUFFLE__\n";
2760  os << "class factory_" << classname << ": public souffle::ProgramFactory {\n";
2761  os << "SouffleProgram *newInstance() {\n";
2762  os << "return new " << classname << "();\n";
2763  os << "};\n";
2764  os << "public:\n";
2765  os << "factory_" << classname << "() : ProgramFactory(\"" << id << "\"){}\n";
2766  os << "};\n";
2767  os << "extern \"C\" {\n";
2768  os << "factory_" << classname << " __factory_" << classname << "_instance;\n";
2769  os << "}\n";
2770  os << "}\n";
2771  os << "#else\n";
2772  os << "}\n";
2773  os << "int main(int argc, char** argv)\n{\n";
2774  os << "try{\n";
2775 
2776  // parse arguments
2777  os << "souffle::CmdOptions opt(";
2778  os << "R\"(" << Global::config().get("") << ")\",\n";
2779  os << "R\"()\",\n";
2780  os << "R\"()\",\n";
2781  if (Global::config().has("profile")) {
2782  os << "true,\n";
2783  os << "R\"(" << Global::config().get("profile") << ")\",\n";
2784  } else {
2785  os << "false,\n";
2786  os << "R\"()\",\n";
2787  }
2788  os << std::stoi(Global::config().get("jobs"));
2789  os << ");\n";
2790 
2791  os << "if (!opt.parse(argc,argv)) return 1;\n";
2792 
2793  os << "souffle::";
2794  if (Global::config().has("profile")) {
2795  os << classname + " obj(opt.getProfileName());\n";
2796  } else {
2797  os << classname + " obj;\n";
2798  }
2799 
2800  os << "#if defined(_OPENMP) \n";
2801  os << "obj.setNumThreads(opt.getNumJobs());\n";
2802  os << "\n#endif\n";
2803 
2804  if (Global::config().has("profile")) {
2805  os << R"_(souffle::ProfileEventSingleton::instance().makeConfigRecord("", opt.getSourceFileName());)_"
2806  << '\n';
2807  os << R"_(souffle::ProfileEventSingleton::instance().makeConfigRecord("fact-dir", opt.getInputFileDir());)_"
2808  << '\n';
2809  os << R"_(souffle::ProfileEventSingleton::instance().makeConfigRecord("jobs", std::to_string(opt.getNumJobs()));)_"
2810  << '\n';

◆ generateRelationTypeStruct()

void souffle::synthesiser::Synthesiser::generateRelationTypeStruct ( std::ostream &  out,
Own< Relation relationType 
)
protected

Get relation struct definition.

Get relation type struct.

Definition at line 200 of file Synthesiser.cpp.

206  {
207  std::set<const ram::Relation*> res;
208  visitDepthFirst(op, [&](const Node& node) {
209  if (auto scan = dynamic_cast<const RelationOperation*>(&node)) {

◆ getOpContextName()

const std::string souffle::synthesiser::Synthesiser::getOpContextName ( const ram::Relation rel)
protected

Get context name.

Definition at line 195 of file Synthesiser.cpp.

196  {
197  return;

◆ getRecordTable()

const RecordTable& souffle::synthesiser::Synthesiser::getRecordTable ( )
protected

Get record table.

◆ getReferencedRelations()

std::set< const ram::Relation * > souffle::synthesiser::Synthesiser::getReferencedRelations ( const ram::Operation op)
protected

Get referenced relations.

Definition at line 212 of file Synthesiser.cpp.

213  {
214  res.insert(lookup(exists->getRelation()));
215  } else if (auto provExists = dynamic_cast<const ProvenanceExistenceCheck*>(&node)) {
216  res.insert(lookup(provExists->getRelation()));
217  } else if (auto project = dynamic_cast<const Project*>(&node)) {
218  res.insert(lookup(project->getRelation()));
219  }
220  });
221  return res;
222 }
223 
224 void Synthesiser::emitCode(std::ostream& out, const Statement& stmt) {
225  class CodeEmitter : public Visitor<void, std::ostream&> {
226  private:
227  Synthesiser& synthesiser;
228  IndexAnalysis* const isa = synthesiser.getTranslationUnit().getAnalysis<IndexAnalysis>();

◆ getRelationName() [1/2]

const std::string souffle::synthesiser::Synthesiser::getRelationName ( const ram::Relation rel)
protected

Get relation name.

Definition at line 186 of file Synthesiser.cpp.

◆ getRelationName() [2/2]

const std::string souffle::synthesiser::Synthesiser::getRelationName ( const ram::Relation rel)
protected

Definition at line 190 of file Synthesiser.cpp.

◆ getTranslationUnit()

ram::TranslationUnit& souffle::synthesiser::Synthesiser::getTranslationUnit ( )
inline

Get translation unit.

Definition at line 119 of file Synthesiser.h.

◆ lookup()

const ram::Relation* souffle::synthesiser::Synthesiser::lookup ( const std::string &  relName)
inlineprotected

Lookup relation by relation name.

Definition at line 104 of file Synthesiser.h.

107  {
108  return translationUnit;

References translationUnit.

◆ lookupFreqIdx()

unsigned souffle::synthesiser::Synthesiser::lookupFreqIdx ( const std::string &  txt)
protected

Lookup frequency counter.

Definition at line 129 of file Synthesiser.cpp.

134  {
135  std::string modifiedTxt = txt;
136  std::replace(modifiedTxt.begin(), modifiedTxt.end(), '-', '.');
137  static unsigned counter;

◆ lookupReadIdx()

size_t souffle::synthesiser::Synthesiser::lookupReadIdx ( const std::string &  txt)
protected

Lookup read counter.

Lookup frequency counter.

Definition at line 140 of file Synthesiser.cpp.

141  {
142  return neIdxMap[modifiedTxt];
143  }
144 }
145 
146 /** Convert RAM identifier */
147 const std::string Synthesiser::convertRamIdent(const std::string& name) {
148  auto it = identifiers.find(name);
149  if (it != identifiers.end()) {
150  return it->second;

Field Documentation

◆ identifiers

std::map<const std::string, const std::string> souffle::synthesiser::Synthesiser::identifiers
private

RAM identifier to C++ identifier map.

Definition at line 60 of file Synthesiser.h.

◆ idxMap

std::map<std::string, unsigned> souffle::synthesiser::Synthesiser::idxMap
private

Frequency profiling of searches.

Definition at line 63 of file Synthesiser.h.

◆ neIdxMap

std::map<std::string, size_t> souffle::synthesiser::Synthesiser::neIdxMap
private

Frequency profiling of non-existence checks.

Definition at line 66 of file Synthesiser.h.

◆ recordTable

RecordTable souffle::synthesiser::Synthesiser::recordTable
private

Record Table.

Definition at line 54 of file Synthesiser.h.

◆ relationMap

std::map<std::string, const ram::Relation*> souffle::synthesiser::Synthesiser::relationMap
private

Relation map.

Definition at line 72 of file Synthesiser.h.

◆ translationUnit

ram::TranslationUnit& souffle::synthesiser::Synthesiser::translationUnit
private

RAM translation unit.

Definition at line 57 of file Synthesiser.h.

Referenced by lookup().

◆ typeCache

std::set<std::string> souffle::synthesiser::Synthesiser::typeCache
private

Cache for generated types for relations.

Definition at line 69 of file Synthesiser.h.


The documentation for this class was generated from the following files:
souffle::synthesiser::Synthesiser::generateRelationTypeStruct
void generateRelationTypeStruct(std::ostream &out, Own< Relation > relationType)
Get relation struct definition.
Definition: Synthesiser.cpp:200
souffle::synthesiser::Synthesiser::emitCode
void emitCode(std::ostream &out, const ram::Statement &stmt)
Generate code.
Definition: Synthesiser.cpp:230
souffle::synthesiser::Synthesiser::lookup
const ram::Relation * lookup(const std::string &relName)
Lookup relation by relation name.
Definition: Synthesiser.h:104
souffle::RamUnsigned
uint32_t RamUnsigned
Definition: RamTypes.h:58
souffle::AggregateOp::MIN
@ MIN
TCB_SPAN_NAMESPACE_NAME::detail::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.h:198
CONV_TO_STRING
#define CONV_TO_STRING(opcode, ty)
souffle::AggregateOp::USUM
@ USUM
souffle::synthesiser::Synthesiser::getRelationName
const std::string getRelationName(const ram::Relation &rel)
Get relation name.
Definition: Synthesiser.cpp:186
UNREACHABLE_BAD_CASE_ANALYSIS
#define UNREACHABLE_BAD_CASE_ANALYSIS
Definition: MiscUtil.h:206
souffle::TypeAttribute::Record
@ Record
PRINT_END_COMMENT
#define PRINT_END_COMMENT(os)
souffle::AggregateOp::FSUM
@ FSUM
souffle::ram::toCondition
Own< Condition > toCondition(const VecOwn< Condition > &conds)
Convert list of conditions to a conjunction.
Definition: Utils.h:84
TypeAttribute
Type attribute class.
souffle::RamDomain
int32_t RamDomain
Definition: RamTypes.h:56
souffle::ram::isUndefValue
bool isUndefValue(const Expression *expr)
Determines if an expression represents an undefined value.
Definition: Utils.h:40
BINARY_OP_BITWISE
#define BINARY_OP_BITWISE(opcode, op)
souffle::ram::toConjunctionList
VecOwn< Condition > toConjunctionList(const Condition *condition)
Convert terms of a conjunction to a list.
Definition: Utils.h:57
tinyformat::format
void format(std::ostream &out, const char *fmt)
Definition: tinyformat.h:1089
souffle::ast::analysis::sub
std::shared_ptr< Constraint< Var > > sub(const Var &a, const Var &b, const std::string &symbol="⊑")
A generic factory for constraints of the form.
Definition: ConstraintSystem.h:228
COMPARE_EQ_NE
#define COMPARE_EQ_NE(opCode, op)
souffle::escapeJSONstring
std::string escapeJSONstring(const std::string &JSONstr)
Escape JSON string.
Definition: StringUtil.h:373
UNARY_OP_F
#define UNARY_OP_F(opcode, op)
low
d d low
Definition: htmlJsChartistMin.h:15
souffle::FunctorOp::URANGE
@ URANGE
high
d high
Definition: htmlJsChartistMin.h:15
souffle::contains
bool contains(const C &container, const typename C::value_type &element)
A utility to check generically whether a given element is contained in a given container.
Definition: ContainerUtil.h:75
souffle::TypeAttribute::Symbol
@ Symbol
souffle::synthesiser::Synthesiser::generateCode
void generateCode(std::ostream &os, const std::string &id, bool &withSharedLibrary)
Generate code.
Definition: Synthesiser.cpp:2260
souffle::RamFloat
float RamFloat
Definition: RamTypes.h:60
types
std::vector< Own< ast::Type > > types
Definition: ComponentInstantiation.cpp:64
souffle::map
auto map(const std::vector< A > &xs, F &&f)
Applies a function to each element of a vector and returns the results.
Definition: ContainerUtil.h:158
souffle::FunctorOp::STRLEN
@ STRLEN
PRINT_BEGIN_COMMENT
#define PRINT_BEGIN_COMMENT(os)
BINARY_OP_LOGICAL
#define BINARY_OP_LOGICAL(opcode, op)
json11::Json::object
std::map< std::string, Json > object
Definition: json11.h:94
souffle::ram::TranslationUnit::getSymbolTable
souffle::SymbolTable & getSymbolTable()
Obtain symbol table
Definition: TranslationUnit.h:112
souffle::identifier
std::string identifier(std::string id)
Valid C++ identifier, note that this does not ensure the uniqueness of identifiers returned.
Definition: StringUtil.h:387
souffle::FunctorOp::ORD
@ ORD
Unary Functor Operators.
souffle::synthesiser::Synthesiser::Synthesiser
Synthesiser(ram::TranslationUnit &tUnit)
Definition: Synthesiser.h:111
BINARY_OP_EXP
#define BINARY_OP_EXP(opcode, ty, tyTemp)
souffle::fileExtension
std::string fileExtension(const std::string &path)
File extension, with all else removed.
Definition: FileUtil.h:238
MINMAX_SYMBOL
#define MINMAX_SYMBOL(op)
souffle::TypeAttribute::Signed
@ Signed
souffle::ram::analysis::AttributeConstraint::None
@ None
souffle::toString
const std::string & toString(const std::string &str)
A generic function converting strings into strings (trivial case).
Definition: StringUtil.h:234
souffle::AggregateOp::UMIN
@ UMIN
souffle::FunctorOp::FRANGE
@ FRANGE
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
souffle::synthesiser::Synthesiser::neIdxMap
std::map< std::string, size_t > neIdxMap
Frequency profiling of non-existence checks.
Definition: Synthesiser.h:66
souffle::TypeAttribute::Unsigned
@ Unsigned
i
size_t i
Definition: json11.h:663
souffle::filter
std::vector< A > filter(std::vector< A > xs, F &&f)
Filter a vector to include certain elements.
Definition: FunctionalUtil.h:155
NARY_OP_ORDERED
#define NARY_OP_ORDERED(opcode, op)
BINARY_OP_INTEGRAL
#define BINARY_OP_INTEGRAL(opcode, op)
souffle::AggregateOp::MAX
@ MAX
souffle::join
detail::joined_sequence< Iter, Printer > join(const Iter &a, const Iter &b, const std::string &sep, const Printer &p)
Creates an object to be forwarded to some output stream for printing sequences of elements interspers...
Definition: StreamUtil.h:175
BINARY_OP_INTEGRAL_SHIFT
#define BINARY_OP_INTEGRAL_SHIFT(opcode, op, tySigned, tyUnsigned)
souffle::FunctorOp::RANGE
@ RANGE
souffle::AggregateOp::UMAX
@ UMAX
souffle::AggregateOp::FMIN
@ FMIN
souffle::AggregateOp::FMAX
@ FMAX
souffle::pack
RamDomain pack(RecordTable &recordTab, Tuple< RamDomain, Arity > const &tuple)
helper to convert tuple to record reference for the synthesiser
Definition: RecordTable.h:153
TCB_SPAN_NAMESPACE_NAME::get
constexpr auto get(span< E, S > s) -> decltype(s[N])
Definition: span.h:599
json11::Json::array
std::vector< Json > array
Definition: json11.h:93
souffle::synthesiser::Synthesiser::translationUnit
ram::TranslationUnit & translationUnit
RAM translation unit.
Definition: Synthesiser.h:57
souffle::BinaryConstraintOp
BinaryConstraintOp
Binary Constraint Operators.
Definition: BinaryConstraintOps.h:41
CONV_FROM_STRING
#define CONV_FROM_STRING(opcode, ty)
souffle::ram::isTrue
bool isTrue(const Condition *cond)
Determines if a condition represents true.
Definition: Utils.h:45
souffle::TypeAttribute::ADT
@ ADT
souffle::FunctorOp
FunctorOp
Definition: FunctorOps.h:35
souffle::getTypeAttributeAggregate
TypeAttribute getTypeAttributeAggregate(const AggregateOp op)
Get return type of the aggregate.
Definition: AggregateOp.h:96
souffle::synthesiser::Synthesiser::convertRamIdent
const std::string convertRamIdent(const std::string &name)
Convert RAM identifier.
Definition: Synthesiser.cpp:153
souffle::AggregateOp::COUNT
@ COUNT
souffle::AggregateOp::SUM
@ SUM
directives
std::vector< Own< Directive > > directives
Definition: ComponentInstantiation.cpp:66
souffle::Global::config
static MainConfig & config()
Definition: Global.h:141
souffle::BinaryConstraintOp::CONTAINS
@ CONTAINS
souffle::AggregateOp::MEAN
@ MEAN
souffle::escape
std::string escape(const std::string &inputString, const std::string &needle, const std::string &replacement)
Definition: StringUtil.h:417
BINARY_OP_NUMERIC
#define BINARY_OP_NUMERIC(opcode, op)
std
Definition: Brie.h:3053
souffle::RelationRepresentation::DEFAULT
@ DEFAULT
souffle::synthesiser::Synthesiser::idxMap
std::map< std::string, unsigned > idxMap
Frequency profiling of searches.
Definition: Synthesiser.h:63
souffle::fatal
void fatal(const char *format, const Args &... args)
Definition: MiscUtil.h:198
souffle::RelationRepresentation::INFO
@ INFO
souffle
Definition: AggregateOp.h:25
UNARY_OP_I
#define UNARY_OP_I(opcode, op)
souffle::synthesiser::Synthesiser::identifiers
std::map< const std::string, const std::string > identifiers
RAM identifier to C++ identifier map.
Definition: Synthesiser.h:60
souffle::ast::visitDepthFirst
void visitDepthFirst(const Node &root, Visitor< R, Ps... > &visitor, Args &... args)
A utility function visiting all nodes within the ast rooted by the given node recursively in a depth-...
Definition: Visitor.h:273
souffle::RamSigned
RamDomain RamSigned
Definition: RamTypes.h:57
souffle::RelationRepresentation::BTREE
@ BTREE
UNARY_OP_INTEGRAL
#define UNARY_OP_INTEGRAL(opcode, op)
souffle::TypeAttribute::Float
@ Float
rel
void rel(size_t limit, bool showLimit=true)
Definition: Tui.h:1086
souffle::RelationRepresentation
RelationRepresentation
Space of internal representations that a relation can have.
Definition: RelationTag.h:56
souffle::FunctorOp::SUBSTR
@ SUBSTR
Ternary Functor Operators.
souffle::BinaryConstraintOp::NOT_MATCH
@ NOT_MATCH
id
void id(std::string col)
Definition: Tui.h:1124
COMPARE
#define COMPARE(opCode, op)
std::type
ElementType type
Definition: span.h:640
souffle::BinaryConstraintOp::NOT_CONTAINS
@ NOT_CONTAINS
souffle::profile::ss
class souffle::profile::Tui ss
Definition: Tui.h:336
UNARY_OP
#define UNARY_OP(opcode, ty, op)