103 #include <functional>
110 #include <type_traits>
118 using ram::analysis::IndexAnalysis;
120 using namespace stream_write_qualified_char_as_number;
125 auto pos = idxMap.find(txt);
126 if (pos == idxMap.end()) {
127 return idxMap[txt] = ctr++;
135 std::string modifiedTxt = txt;
136 std::replace(modifiedTxt.begin(), modifiedTxt.end(),
'-',
'.');
137 static unsigned counter;
138 auto pos = neIdxMap.find(modifiedTxt);
139 if (pos == neIdxMap.end()) {
140 return neIdxMap[modifiedTxt] = counter++;
142 return neIdxMap[modifiedTxt];
148 auto it = identifiers.find(name);
149 if (it != identifiers.end()) {
154 for (
i = 0;
i < name.length(); ++
i) {
155 if ((isalnum(name.at(
i)) != 0) || name.at(
i) ==
'_') {
160 for (
auto ch : std::to_string(identifiers.size() + 1) +
'_' + name.substr(
i)) {
162 if (isalnum(ch) != 0) {
168 else if (
id.empty() ||
id.back() !=
'_') {
174 id =
id.substr(0, 1024);
175 identifiers.insert(std::make_pair(name,
id));
181 return "rel_" + convertRamIdent(
rel.getName());
185 return "rel_" + convertRamIdent(
rel->getName());
190 return getRelationName(
rel) +
"_op_ctxt";
196 if (typeCache.find(relationType->getTypeName()) != typeCache.end()) {
199 typeCache.insert(relationType->getTypeName());
202 relationType->generateTypeStruct(out);
207 std::set<const ram::Relation*> res;
210 res.insert(lookup(scan->getRelation()));
211 }
else if (
auto agg =
dynamic_cast<const Aggregate*
>(&node)) {
212 res.insert(lookup(agg->getRelation()));
213 }
else if (
auto exists =
dynamic_cast<const ExistenceCheck*
>(&node)) {
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()));
225 class CodeEmitter :
public Visitor<void, std::ostream&> {
227 Synthesiser& synthesiser;
228 IndexAnalysis*
const isa = synthesiser.getTranslationUnit().getAnalysis<IndexAnalysis>();
231 #ifndef PRINT_BEGIN_COMMENT
232 #define PRINT_BEGIN_COMMENT(os) \
233 if (Global::config().has("debug-report") || Global::config().has("verbose")) \
237 #ifndef PRINT_END_COMMENT
238 #define PRINT_END_COMMENT(os) \
239 if (Global::config().has("debug-report") || Global::config().has("verbose")) \
244 std::function<void(std::ostream&,
const Expression*)> rec;
245 std::function<void(std::ostream&,
const Expression*)> recWithDefault;
247 std::ostringstream preamble;
248 bool preambleIssued =
false;
252 rec = [&](
auto& out,
const auto* value) {
253 out <<
"ramBitCast(";
257 recWithDefault = [&](
auto& out,
const auto* value) {
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;
273 size_t realArity =
rel.getArity();
274 size_t arity = rangePatternLower.size();
276 low <<
"Tuple<RamDomain," << realArity <<
">{{";
277 high <<
"Tuple<RamDomain," << realArity <<
">{{";
279 for (
size_t column = 0; column < arity; column++) {
280 std::string supremum;
283 switch (
rel.getAttributeTypes()[column][0]) {
285 supremum =
"ramBitCast<RamDomain>(MIN_RAM_FLOAT)";
286 infimum =
"ramBitCast<RamDomain>(MAX_RAM_FLOAT)";
289 supremum =
"ramBitCast<RamDomain>(MIN_RAM_UNSIGNED)";
290 infimum =
"ramBitCast<RamDomain>(MAX_RAM_UNSIGNED)";
293 supremum =
"ramBitCast<RamDomain>(MIN_RAM_SIGNED)";
294 infimum =
"ramBitCast<RamDomain>(MAX_RAM_SIGNED)";
306 low <<
"ramBitCast(";
307 visit(rangePatternLower[column],
low);
314 high <<
"ramBitCast(";
315 visit(rangePatternUpper[column],
high);
322 return std::make_pair(std::move(
low), std::move(
high));
327 void visitIO(
const IO& io, std::ostream& out)
override {
331 auto printDirectives = [&](
const std::map<std::string, std::string>& registry) {
332 auto cur = registry.begin();
333 if (cur == registry.end()) {
336 out <<
"{{\"" << cur->first <<
"\",\"" <<
escape(cur->second) <<
"\"}";
338 for (; cur != registry.end(); ++cur) {
339 out <<
",{\"" << cur->first <<
"\",\"" <<
escape(cur->second) <<
"\"}";
345 const std::string& op = io.get(
"operation");
346 out <<
"if (performIO) {\n";
351 out <<
"std::map<std::string, std::string> directiveMap(";
354 out << R
"_(if (!inputDirectory.empty()) {)_";
355 out << R"_(directiveMap["fact-dir"] = inputDirectory;)_";
357 out <<
"IOSystem::getInstance().getReader(";
358 out <<
"directiveMap, symTable, recordTable";
359 out <<
")->readAll(*" << synthesiser.getRelationName(synthesiser.lookup(io.getRelation()));
361 out <<
"} catch (std::exception& e) {std::cerr << \"Error loading data: \" << e.what() "
364 }
else if (op ==
"output" || op ==
"printsize") {
366 out <<
"std::map<std::string, std::string> directiveMap(";
369 out << R
"_(if (!outputDirectory.empty()) {)_";
370 out << R"_(directiveMap["output-dir"] = outputDirectory;)_";
372 out <<
"IOSystem::getInstance().getWriter(";
373 out <<
"directiveMap, symTable, recordTable";
374 out <<
")->writeAll(*" << synthesiser.getRelationName(synthesiser.lookup(io.getRelation()))
376 out <<
"} catch (std::exception& e) {std::cerr << e.what();exit(1);}\n";
378 assert(
"Wrong i/o operation");
384 void visitQuery(
const Query& query, std::ostream& out)
override {
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();
398 for (
auto const& cur : conditions) {
399 bool needContext =
false;
400 visitDepthFirst(*cur, [&](
const ExistenceCheck&) { needContext =
true; });
401 visitDepthFirst(*cur, [&](
const ProvenanceExistenceCheck&) { needContext =
true; });
409 if (freeOfCtx.size() > 0) {
422 bool isParallel =
false;
423 visitDepthFirst(*next, [&](
const AbstractParallel&) { isParallel =
true; });
428 preambleIssued =
false;
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";
439 if (requireCtx.size() > 0) {
449 out << preamble.str();
450 if (requireCtx.size() > 0) {
462 out <<
"PARALLEL_END\n";
468 if (freeOfCtx.size() > 0) {
475 void visitClear(
const Clear& clear, std::ostream& out)
override {
478 if (!synthesiser.lookup(clear.getRelation())->isTemp()) {
479 out <<
"if (performIO) ";
481 out << synthesiser.getRelationName(synthesiser.lookup(clear.getRelation())) <<
"->"
487 void visitLogSize(
const LogSize&
size, std::ostream& out)
override {
489 out <<
"ProfileEventSingleton::instance().makeQuantityEvent( R\"(";
490 out <<
size.getMessage() <<
")\",";
491 out << synthesiser.getRelationName(synthesiser.lookup(
size.getRelation())) <<
"->size(),iter);";
497 void visitSequence(
const Sequence& seq, std::ostream& out)
override {
499 for (
const auto& cur : seq.getStatements()) {
505 void visitParallel(
const Parallel& parallel, std::ostream& out)
override {
507 auto stmts = parallel.getStatements();
516 if (stmts.size() == 1) {
517 visit(stmts[0], out);
525 out <<
"SECTIONS_START;\n";
528 for (
const auto& cur : stmts) {
529 out <<
"SECTION_START;\n";
531 out <<
"SECTION_END\n";
535 out <<
"SECTIONS_END;\n";
539 void visitLoop(
const Loop& loop, std::ostream& out)
override {
541 out <<
"iter = 0;\n";
542 out <<
"for(;;) {\n";
543 visit(loop.getBody(), out);
546 out <<
"iter = 0;\n";
550 void visitSwap(
const Swap& swap, std::ostream& out)
override {
552 const std::string& deltaKnowledge =
553 synthesiser.getRelationName(synthesiser.lookup(swap.getFirstRelation()));
554 const std::string& newKnowledge =
555 synthesiser.getRelationName(synthesiser.lookup(swap.getSecondRelation()));
557 out <<
"std::swap(" << deltaKnowledge <<
", " << newKnowledge <<
");\n";
561 void visitExtend(
const Extend& extend, std::ostream& out)
override {
563 out << synthesiser.getRelationName(synthesiser.lookup(extend.getSourceRelation())) <<
"->"
565 <<
"*" << synthesiser.getRelationName(synthesiser.lookup(extend.getTargetRelation()))
570 void visitExit(
const Exit& exit, std::ostream& out)
override {
573 visit(exit.getCondition(), out);
578 void visitCall(
const Call& call, std::ostream& out)
override {
580 const Program& prog = synthesiser.getTranslationUnit().getProgram();
581 const auto& subs = prog.getSubroutines();
583 out <<
" std::vector<RamDomain> args, ret;\n";
584 out <<
"subroutine_" << distance(subs.begin(), subs.find(call.getName())) <<
"(args, ret);\n";
589 void visitLogRelationTimer(
const LogRelationTimer& timer, std::ostream& out)
override {
596 const auto*
rel = synthesiser.lookup(timer.getRelation());
597 auto relName = synthesiser.getRelationName(
rel);
599 out <<
"\tLogger logger(R\"_(" << timer.getMessage() <<
")_\",iter, [&](){return " << relName
602 visit(timer.getStatement(), out);
609 void visitLogTimer(
const LogTimer& timer, std::ostream& out)
override {
617 out <<
"\tLogger logger(R\"_(" << timer.getMessage() <<
")_\",iter);\n";
619 visit(timer.getStatement(), out);
626 void visitDebugInfo(
const DebugInfo& dbg, std::ostream& out)
override {
628 out <<
"signalHandler->setMsg(R\"_(";
629 out << dbg.getMessage();
633 visit(dbg.getStatement(), out);
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";
646 void visitTupleOperation(
const TupleOperation& search, std::ostream& out)
override {
648 visitNestedOperation(search, out);
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);
656 assert(pscan.getTupleId() == 0 &&
"not outer-most loop");
658 assert(
rel->getArity() > 0 &&
"AstToRamTranslator failed/no parallel scans for nullaries");
660 assert(!preambleIssued &&
"only first loop can be made parallel");
661 preambleIssued =
true;
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";
670 out <<
"for(const auto& env0 : *it) {\n";
672 visitTupleOperation(pscan, out);
675 out <<
"} catch(std::exception &e) { signalHandler->error(e.what());}\n";
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();
688 assert(
rel->getArity() > 0 &&
"AstToRamTranslator failed/no scans for nullaries");
690 out <<
"for(const auto& env" <<
id <<
" : "
691 <<
"*" << relName <<
") {\n";
693 visitTupleOperation(scan, out);
700 void visitChoice(
const Choice& choice, std::ostream& out)
override {
701 const auto*
rel = synthesiser.lookup(choice.getRelation());
702 auto relName = synthesiser.getRelationName(
rel);
705 assert(
rel->getArity() > 0 &&
"AstToRamTranslator failed/no choice for nullaries");
709 out <<
"for(const auto& env" <<
identifier <<
" : "
710 <<
"*" << relName <<
") {\n";
713 visit(choice.getCondition(), out);
717 visitTupleOperation(choice, out);
726 void visitParallelChoice(
const ParallelChoice& pchoice, std::ostream& out)
override {
727 const auto*
rel = synthesiser.lookup(pchoice.getRelation());
728 auto relName = synthesiser.getRelationName(
rel);
730 assert(pchoice.getTupleId() == 0 &&
"not outer-most loop");
732 assert(
rel->getArity() > 0 &&
"AstToRamTranslator failed/no parallel choice for nullaries");
734 assert(!preambleIssued &&
"only first loop can be made parallel");
735 preambleIssued =
true;
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";
744 out <<
"for(const auto& env0 : *it) {\n";
747 visit(pchoice.getCondition(), out);
751 visitTupleOperation(pchoice, out);
756 out <<
"} catch(std::exception &e) { signalHandler->error(e.what());}\n";
762 void visitIndexScan(
const IndexScan& iscan, std::ostream& out)
override {
763 const auto*
rel = synthesiser.lookup(iscan.getRelation());
764 auto relName = synthesiser.getRelationName(
rel);
766 auto keys = isa->getSearchSignature(&iscan);
767 auto arity =
rel->getArity();
769 const auto& rangePatternLower = iscan.getRangePattern().first;
770 const auto& rangePatternUpper = iscan.getRangePattern().second;
772 assert(arity > 0 &&
"AstToRamTranslator failed/no index scans for nullaries");
775 auto ctxName =
"READ_OP_CONTEXT(" + synthesiser.getOpContextName(*
rel) +
")";
776 auto rangeBounds = getPaddedRangeBounds(*
rel, rangePatternLower, rangePatternUpper);
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";
783 visitTupleOperation(iscan, out);
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);
795 const auto& rangePatternLower = piscan.getRangePattern().first;
796 const auto& rangePatternUpper = piscan.getRangePattern().second;
798 assert(piscan.getTupleId() == 0 &&
"not outer-most loop");
800 assert(arity > 0 &&
"AstToRamTranslator failed/no parallel index scan for nullaries");
802 assert(!preambleIssued &&
"only first loop can be made parallel");
803 preambleIssued =
true;
806 auto rangeBounds = getPaddedRangeBounds(*
rel, rangePatternLower, rangePatternUpper);
807 out <<
"auto range = " << relName
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";
817 out <<
"for(const auto& env0 : *it) {\n";
819 visitTupleOperation(piscan, out);
822 out <<
"} catch(std::exception &e) { signalHandler->error(e.what());}\n";
828 void visitIndexChoice(
const IndexChoice& ichoice, std::ostream& out)
override {
830 const auto*
rel = synthesiser.lookup(ichoice.getRelation());
831 auto relName = synthesiser.getRelationName(
rel);
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);
839 assert(arity > 0 &&
"AstToRamTranslator failed");
840 auto ctxName =
"READ_OP_CONTEXT(" + synthesiser.getOpContextName(*
rel) +
")";
841 auto rangeBounds = getPaddedRangeBounds(*
rel, rangePatternLower, rangePatternUpper);
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";
849 visit(ichoice.getCondition(), out);
853 visitTupleOperation(ichoice, out);
862 void visitParallelIndexChoice(
const ParallelIndexChoice& pichoice, std::ostream& out)
override {
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);
871 assert(pichoice.getTupleId() == 0 &&
"not outer-most loop");
873 assert(arity > 0 &&
"AstToRamTranslator failed");
875 assert(!preambleIssued &&
"only first loop can be made parallel");
876 preambleIssued =
true;
879 auto rangeBounds = getPaddedRangeBounds(*
rel, rangePatternLower, rangePatternUpper);
880 out <<
"auto range = " << relName
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";
890 out <<
"for(const auto& env0 : *it) {\n";
893 visit(pichoice.getCondition(), out);
897 visitTupleOperation(pichoice, out);
902 out <<
"} catch(std::exception &e) { signalHandler->error(e.what());}\n";
908 void visitUnpackRecord(
const UnpackRecord& unpack, std::ostream& out)
override {
910 auto arity = unpack.getArity();
913 out <<
"RamDomain const ref = ";
914 visit(unpack.getExpression(), out);
918 out <<
"if (ref == 0) continue;\n";
921 out <<
"const RamDomain *"
922 <<
"env" << unpack.getTupleId() <<
" = "
923 <<
"recordTable.unpack(ref," << arity <<
");"
929 visitTupleOperation(unpack, out);
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;
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) +
")";
949 std::string tuple_type =
"Tuple<RamDomain," +
toString(arity) +
">";
952 out <<
"Tuple<RamDomain,1> env" <<
identifier <<
";\n";
955 auto keys = isa->getSearchSignature(&aggregate);
959 isTrue(&aggregate.getCondition())) {
961 out <<
"env" <<
identifier <<
"[0] = " << relName <<
"->"
964 out << preamble.str();
965 visitTupleOperation(aggregate, out);
970 out <<
"bool shouldRunNested = false;\n";
974 switch (aggregate.getFunction()) {
983 out <<
"shouldRunNested = true;\n";
990 out <<
"shouldRunNested = true;\n";
996 switch (aggregate.getFunction()) {
1019 default:
fatal(
"Unhandled aggregate operation");
1022 std::string sharedVariable =
"res0";
1034 out <<
type <<
" res0 = " << init <<
";\n";
1036 out <<
"RamUnsigned res1 = 0;\n";
1037 sharedVariable +=
", res1";
1040 out << preamble.str();
1041 out <<
"PARALLEL_START\n";
1044 out <<
"#pragma omp for reduction(" << op <<
":" << sharedVariable <<
")\n";
1045 out <<
"for(const auto& env" <<
identifier <<
" : "
1046 <<
"*" << relName <<
") {\n";
1048 const auto& rangePatternLower = aggregate.getRangePattern().first;
1049 const auto& rangePatternUpper = aggregate.getRangePattern().second;
1051 auto rangeBounds = getPaddedRangeBounds(*
rel, rangePatternLower, rangePatternUpper);
1052 out <<
"auto range = " << relName <<
"->"
1053 <<
"lowerUpperRange_" << keys <<
"(" << rangeBounds.first.str() <<
","
1054 << rangeBounds.second.str() <<
"," << ctxName <<
");\n";
1056 out <<
"auto part = range.partition();\n";
1057 out <<
"#pragma omp for reduction(" << op <<
":" << sharedVariable <<
")\n";
1059 out <<
"for (auto it = part.begin(); it < part.end(); ++it) {\n";
1061 out <<
"for (const auto& env" <<
identifier <<
": *it) {\n";
1066 visit(aggregate.getCondition(), out);
1069 out <<
"shouldRunNested = true;\n";
1072 switch (aggregate.getFunction()) {
1076 out <<
"res0 = std::min(res0,ramBitCast<" <<
type <<
">(";
1077 visit(aggregate.getExpression(), out);
1083 out <<
"res0 = std::max(res0,ramBitCast<" <<
type <<
">(";
1084 visit(aggregate.getExpression(), out);
1092 <<
"ramBitCast<" <<
type <<
">(";
1093 visit(aggregate.getExpression(), out);
1099 <<
"ramBitCast<RamFloat>(";
1100 visit(aggregate.getExpression(), out);
1113 if (!keys.empty()) {
1118 out <<
"#pragma omp single\n{\n";
1121 out <<
"if (res1 != 0) {\n";
1122 out <<
"res0 = res0 / res1;\n";
1127 out <<
"env" <<
identifier <<
"[0] = ramBitCast(res0);\n";
1130 out <<
"if (shouldRunNested) {\n";
1131 visitTupleOperation(aggregate, out);
1138 bool isGuaranteedToBeMinimum(
const IndexAggregate& aggregate) {
1140 auto keys = isa->getSearchSignature(&aggregate);
1143 const auto* tupleElem =
dynamic_cast<const TupleElement*
>(&aggregate.getExpression());
1144 return tupleElem && tupleElem->getTupleId() ==
identifier &&
1149 void visitIndexAggregate(
const IndexAggregate& aggregate, std::ostream& out)
override {
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) +
")";
1159 std::string tuple_type =
"Tuple<RamDomain," +
toString(arity) +
">";
1162 out <<
"Tuple<RamDomain,1> env" <<
identifier <<
";\n";
1165 auto keys = isa->getSearchSignature(&aggregate);
1169 isTrue(&aggregate.getCondition())) {
1171 out <<
"env" <<
identifier <<
"[0] = " << relName <<
"->"
1173 visitTupleOperation(aggregate, out);
1178 out <<
"bool shouldRunNested = false;\n";
1182 switch (aggregate.getFunction()) {
1191 out <<
"shouldRunNested = true;\n";
1198 out <<
"shouldRunNested = true;\n";
1212 out <<
type <<
" res0 = " << init <<
";\n";
1215 out <<
"RamUnsigned res1 = 0;\n";
1220 out <<
"for(const auto& env" <<
identifier <<
" : "
1221 <<
"*" << relName <<
") {\n";
1223 const auto& rangePatternLower = aggregate.getRangePattern().first;
1224 const auto& rangePatternUpper = aggregate.getRangePattern().second;
1226 auto rangeBounds = getPaddedRangeBounds(*
rel, rangePatternLower, rangePatternUpper);
1228 out <<
"auto range = " << relName <<
"->"
1229 <<
"lowerUpperRange_" << keys <<
"(" << rangeBounds.first.str() <<
","
1230 << rangeBounds.second.str() <<
"," << ctxName <<
");\n";
1233 out <<
"for(const auto& env" <<
identifier <<
" : range) {\n";
1238 visit(aggregate.getCondition(), out);
1241 out <<
"shouldRunNested = true;\n";
1244 switch (aggregate.getFunction()) {
1248 out <<
"res0 = std::min(res0,ramBitCast<" <<
type <<
">(";
1249 visit(aggregate.getExpression(), out);
1251 if (isGuaranteedToBeMinimum(aggregate)) {
1258 out <<
"res0 = std::max(res0,ramBitCast<" <<
type <<
">(";
1259 visit(aggregate.getExpression(), out);
1267 <<
"ramBitCast<" <<
type <<
">(";
1268 visit(aggregate.getExpression(), out);
1274 <<
"ramBitCast<RamFloat>(";
1275 visit(aggregate.getExpression(), out);
1287 out <<
"if (res1 != 0) {\n";
1288 out <<
"res0 = res0 / res1;\n";
1293 out <<
"env" <<
identifier <<
"[0] = ramBitCast(res0);\n";
1296 out <<
"if (shouldRunNested) {\n";
1297 visitTupleOperation(aggregate, out);
1303 void visitParallelAggregate(
const ParallelAggregate& aggregate, std::ostream& out)
override {
1306 const auto*
rel = synthesiser.lookup(aggregate.getRelation());
1307 auto relName = synthesiser.getRelationName(
rel);
1308 auto ctxName =
"READ_OP_CONTEXT(" + synthesiser.getOpContextName(*
rel) +
")";
1311 assert(aggregate.getTupleId() == 0 &&
"not outer-most loop");
1312 assert(!preambleIssued &&
"only first loop can be made parallel");
1313 preambleIssued =
true;
1316 out <<
"Tuple<RamDomain,1> env" <<
identifier <<
";\n";
1321 out <<
"env" <<
identifier <<
"[0] = " << relName <<
"->"
1323 out <<
"PARALLEL_START\n";
1324 out << preamble.str();
1325 visitTupleOperation(aggregate, out);
1330 out <<
"bool shouldRunNested = false;\n";
1334 switch (aggregate.getFunction()) {
1343 out <<
"shouldRunNested = true;\n";
1352 out <<
"shouldRunNested = true;\n";
1358 switch (aggregate.getFunction()) {
1382 default:
fatal(
"Unhandled aggregate operation");
1395 out <<
type <<
" res0 = " << init <<
";\n";
1397 std::string sharedVariable =
"res0";
1399 out <<
"RamUnsigned res1 = " << init <<
";\n";
1400 sharedVariable +=
", res1";
1404 out <<
"auto part = " << relName <<
"->partition();\n";
1405 out <<
"PARALLEL_START\n";
1406 out << preamble.str();
1408 out <<
"#pragma omp for reduction(" << op <<
":" << sharedVariable <<
")\n";
1410 out <<
"for (auto it = part.begin(); it < part.end(); ++it) {\n";
1412 out <<
"for (const auto& env" <<
identifier <<
": *it) {\n";
1416 visit(aggregate.getCondition(), out);
1419 out <<
"shouldRunNested = true;\n";
1421 switch (aggregate.getFunction()) {
1425 out <<
"res0 = std::min(res0, ramBitCast<" <<
type <<
">(";
1426 visit(aggregate.getExpression(), out);
1432 out <<
"res0 = std::max(res0, ramBitCast<" <<
type <<
">(";
1433 visit(aggregate.getExpression(), out);
1440 out <<
"res0 += ramBitCast<" <<
type <<
">(";
1441 visit(aggregate.getExpression(), out);
1446 out <<
"res0 += ramBitCast<RamFloat>(";
1447 visit(aggregate.getExpression(), out);
1461 out <<
"#pragma omp single\n{\n";
1464 out <<
"if (res1 != 0) {\n";
1465 out <<
"res0 = res0 / res1;\n";
1470 out <<
"env" <<
identifier <<
"[0] = ramBitCast(res0);\n";
1473 out <<
"if (shouldRunNested) {\n";
1474 visitTupleOperation(aggregate, out);
1479 void visitAggregate(
const Aggregate& aggregate, std::ostream& out)
override {
1482 const auto*
rel = synthesiser.lookup(aggregate.getRelation());
1483 auto relName = synthesiser.getRelationName(
rel);
1484 auto ctxName =
"READ_OP_CONTEXT(" + synthesiser.getOpContextName(*
rel) +
")";
1488 out <<
"Tuple<RamDomain,1> env" <<
identifier <<
";\n";
1493 out <<
"env" <<
identifier <<
"[0] = " << relName <<
"->"
1495 visitTupleOperation(aggregate, out);
1500 out <<
"bool shouldRunNested = false;\n";
1504 switch (aggregate.getFunction()) {
1513 out <<
"shouldRunNested = true;\n";
1522 out <<
"shouldRunNested = true;\n";
1535 default:
type =
"RamDomain";
break;
1537 out <<
type <<
" res0 = " << init <<
";\n";
1540 out <<
"RamUnsigned res1 = 0;\n";
1544 out <<
"for(const auto& env" <<
identifier <<
" : "
1545 <<
"*" << relName <<
") {\n";
1549 visit(aggregate.getCondition(), out);
1552 out <<
"shouldRunNested = true;\n";
1554 switch (aggregate.getFunction()) {
1558 out <<
"res0 = std::min(res0, ramBitCast<" <<
type <<
">(";
1559 visit(aggregate.getExpression(), out);
1565 out <<
"res0 = std::max(res0,ramBitCast<" <<
type <<
">(";
1566 visit(aggregate.getExpression(), out);
1574 <<
"ramBitCast<" <<
type <<
">(";
1576 visit(aggregate.getExpression(), out);
1582 <<
"ramBitCast<RamFloat>(";
1583 visit(aggregate.getExpression(), out);
1595 out <<
"res0 = res0 / res1;\n";
1599 out <<
"env" <<
identifier <<
"[0] = ramBitCast(res0);\n";
1602 out <<
"if (shouldRunNested) {\n";
1603 visitTupleOperation(aggregate, out);
1609 void visitFilter(
const Filter&
filter, std::ostream& out)
override {
1612 visit(
filter.getCondition(), out);
1614 visitNestedOperation(
filter, out);
1619 void visitBreak(
const Break& breakOp, std::ostream& out)
override {
1622 visit(breakOp.getCondition(), out);
1623 out <<
") break;\n";
1624 visitNestedOperation(breakOp, out);
1628 void visitProject(
const Project& project, std::ostream& out)
override {
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) +
")";
1636 out <<
"Tuple<RamDomain," << arity <<
"> tuple{{" <<
join(project.getValues(),
",", rec)
1640 out << relName <<
"->"
1641 <<
"insert(tuple," << ctxName <<
");\n";
1648 void visitTrue(
const True&, std::ostream& out)
override {
1654 void visitFalse(
const False&, std::ostream& out)
override {
1660 void visitConjunction(
const Conjunction& conj, std::ostream& out)
override {
1662 visit(conj.getLHS(), out);
1664 visit(conj.getRHS(), out);
1668 void visitNegation(
const Negation& neg, std::ostream& out)
override {
1671 visit(neg.getOperand(), out);
1676 void visitConstraint(
const Constraint&
rel, std::ostream& out)
override {
1678 #define EVAL_CHILD(ty, idx) \
1679 out << "ramBitCast<" #ty ">("; \
1680 visit(rel.idx(), out); \
1682 #define COMPARE_NUMERIC(ty, op) \
1684 EVAL_CHILD(ty, getLHS); \
1685 out << " " #op " "; \
1686 EVAL_CHILD(ty, getRHS); \
1689 #define COMPARE_STRING(op) \
1690 out << "(symTable.resolve("; \
1691 EVAL_CHILD(RamDomain, getLHS); \
1692 out << ") " #op " symTable.resolve("; \
1693 EVAL_CHILD(RamDomain, getRHS); \
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);
1707 switch (
rel.getOperator()) {
1719 out <<
"regex_wrapper(symTable.resolve(";
1720 visit(
rel.getLHS(), out);
1721 out <<
"),symTable.resolve(";
1722 visit(
rel.getRHS(), out);
1727 out <<
"!regex_wrapper(symTable.resolve(";
1728 visit(
rel.getLHS(), out);
1729 out <<
"),symTable.resolve(";
1730 visit(
rel.getRHS(), out);
1735 out <<
"(symTable.resolve(";
1736 visit(
rel.getRHS(), out);
1737 out <<
").find(symTable.resolve(";
1738 visit(
rel.getLHS(), out);
1739 out <<
")) != std::string::npos)";
1743 out <<
"(symTable.resolve(";
1744 visit(
rel.getRHS(), out);
1745 out <<
").find(symTable.resolve(";
1746 visit(
rel.getLHS(), out);
1747 out <<
")) == std::string::npos)";
1755 #undef COMPARE_NUMERIC
1756 #undef COMPARE_STRING
1758 #undef COMPARE_EQ_NE
1761 void visitEmptinessCheck(
const EmptinessCheck& emptiness, std::ostream& out)
override {
1763 out << synthesiser.getRelationName(synthesiser.lookup(emptiness.getRelation())) <<
"->"
1768 void visitRelationSize(
const RelationSize&
size, std::ostream& out)
override {
1770 out <<
"(RamDomain)" << synthesiser.getRelationName(synthesiser.lookup(
size.getRelation()))
1776 void visitExistenceCheck(
const ExistenceCheck& exists, std::ostream& out)
override {
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");
1785 if (
Global::config().has(
"profile") && !synthesiser.lookup(exists.getRelation())->isTemp()) {
1786 out << R
"_((reads[)_" << synthesiser.lookupReadIdx(rel->getName()) << R"_(]++,)_";
1791 if (isa->isTotalSignature(&exists)) {
1792 out << relName <<
"->"
1793 <<
"contains(Tuple<RamDomain," << arity <<
">{{" <<
join(exists.getValues(),
",", rec)
1794 <<
"}}," << ctxName <<
")" << after;
1799 auto rangePatternLower = exists.getValues();
1800 auto rangePatternUpper = exists.getValues();
1802 auto rangeBounds = getPaddedRangeBounds(*
rel, rangePatternLower, rangePatternUpper);
1804 out <<
"!" << relName <<
"->"
1805 <<
"lowerUpperRange";
1806 out <<
"_" << isa->getSearchSignature(&exists);
1807 out <<
"(" << rangeBounds.first.str() <<
"," << rangeBounds.second.str() <<
"," << ctxName
1808 <<
").empty()" << after;
1812 void visitProvenanceExistenceCheck(
1813 const ProvenanceExistenceCheck& provExists, std::ostream& out)
override {
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();
1823 out <<
"[&]() -> bool {\n";
1824 out <<
"auto existenceCheck = " << relName <<
"->"
1825 <<
"lowerUpperRange";
1826 out <<
"_" << isa->getSearchSignature(&provExists);
1829 size_t parts = arity - auxiliaryArity + 1;
1833 auto vals = provExists.getValues();
1836 for (
size_t i = 0;
i < arity - auxiliaryArity;
i++) {
1838 "ProvenanceExistenceCheck should always be specified for payload");
1841 auto valsCopy = std::vector<Expression*>(vals.begin(), vals.begin() + parts);
1842 auto rangeBounds = getPaddedRangeBounds(*
rel, valsCopy, valsCopy);
1845 rangeBounds.first.seekp(-2, std::ios_base::end);
1846 rangeBounds.second.seekp(-2, std::ios_base::end);
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)";
1853 rangeBounds.first <<
",ramBitCast<RamDomain, RamSigned>(MIN_RAM_SIGNED)}}";
1854 rangeBounds.second <<
",ramBitCast<RamDomain, RamSigned>(MAX_RAM_SIGNED)}}";
1856 out <<
"(" << rangeBounds.first.str() <<
"," << rangeBounds.second.str() <<
"," << ctxName
1858 out <<
"if (existenceCheck.empty()) return false; else return ((*existenceCheck.begin())["
1859 << arity - auxiliaryArity + 1 <<
"] <= ";
1861 visit(*(provExists.getValues()[arity - auxiliaryArity + 1]), out);
1868 void visitUnsignedConstant(
const UnsignedConstant& constant, std::ostream& out)
override {
1870 out <<
"RamUnsigned(" << constant.getValue() <<
")";
1874 void visitFloatConstant(
const FloatConstant& constant, std::ostream& out)
override {
1876 out <<
"RamFloat(" << constant.getValue() <<
")";
1880 void visitSignedConstant(
const SignedConstant& constant, std::ostream& out)
override {
1882 out <<
"RamSigned(" << constant.getConstant() <<
")";
1886 void visitTupleElement(
const TupleElement& access, std::ostream& out)
override {
1888 out <<
"env" << access.getTupleId() <<
"[" << access.getElement() <<
"]";
1892 void visitAutoIncrement(
const AutoIncrement& , std::ostream& out)
override {
1898 void visitIntrinsicOperator(
const IntrinsicOperator& op, std::ostream& out)
override {
1899 #define MINMAX_SYMBOL(op) \
1901 out << "symTable.lookup(" #op "({"; \
1902 for (auto& cur : args) { \
1903 out << "symTable.resolve("; \
1914 #define UNARY_OP(opcode, ty, op) \
1915 case FunctorOp::opcode: { \
1916 out << "(" #op "(ramBitCast<" #ty ">("; \
1917 visit(args[0], out); \
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)
1929 #define BINARY_OP_EXPR_EX(ty, op, rhs_post) \
1931 out << "(ramBitCast<" #ty ">("; \
1932 visit(args[0], out); \
1933 out << ") " #op " ramBitCast<" #ty ">("; \
1934 visit(args[1], out); \
1935 out << rhs_post "))"; \
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)
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: \
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)
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); \
1968 #define NARY_OP(opcode, ty, op) \
1969 case FunctorOp::opcode: { \
1971 for (auto& cur : args) { \
1972 out << "ramBitCast<" #ty ">("; \
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)
1985 #define CONV_TO_STRING(opcode, ty) \
1986 case FunctorOp::opcode: { \
1987 out << "symTable.lookup(std::to_string("; \
1988 visit(args[0], out); \
1991 #define CONV_FROM_STRING(opcode, ty) \
1992 case FunctorOp::opcode: { \
1993 out << "souffle::evaluator::symbol2numeric<" #ty ">(symTable.resolve("; \
1994 visit(args[0], out); \
1999 auto args = op.getArguments();
2000 switch (op.getOperator()) {
2003 visit(args[0], out);
2008 out <<
"static_cast<RamSigned>(symTable.resolve(";
2009 visit(args[0], out);
2047 #if RAM_DOMAIN_SIZE == 32
2050 #elif RAM_DOMAIN_SIZE == 64
2054 #error "unhandled domain size"
2082 out <<
"symTable.lookup(";
2084 while (
i < args.size() - 1) {
2085 out <<
"symTable.resolve(";
2086 visit(args[
i], out);
2090 out <<
"symTable.resolve(";
2091 visit(args[
i], out);
2098 out <<
"symTable.lookup(";
2099 out <<
"substr_wrapper(symTable.resolve(";
2100 visit(args[0], out);
2102 visit(args[1], out);
2104 visit(args[2], out);
2112 fatal(
"ICE: functor `%s` must map onto `NestedIntrinsicOperator`", op.getOperator());
2116 #undef MINMAX_SYMBOL
2119 void visitNestedIntrinsicOperator(
const NestedIntrinsicOperator& op, std::ostream& out)
override {
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); }),
2126 visitTupleOperation(op, out);
2132 auto emitRange = [&](
char const* ty) {
2133 return emitHelper(
tfm::format(
"souffle::evaluator::runRange<%s>", ty));
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");
2145 void visitUserDefinedOperator(
const UserDefinedOperator& op, std::ostream& out)
override {
2146 const std::string& name = op.getName();
2148 auto args = op.getArguments();
2149 if (op.isStateful()) {
2150 out << name <<
"(&symTable, &recordTable";
2151 for (
auto& arg : args) {
2157 const std::vector<TypeAttribute>& argTypes = op.getArgsTypes();
2160 out <<
"symTable.lookup(";
2164 for (
size_t i = 0;
i < args.size();
i++) {
2168 switch (argTypes[
i]) {
2170 out <<
"((RamSigned)";
2171 visit(args[
i], out);
2175 out <<
"((RamUnsigned)";
2176 visit(args[
i], out);
2180 out <<
"((RamFloat)";
2181 visit(args[
i], out);
2185 out <<
"symTable.resolve(";
2186 visit(args[
i], out);
2202 void visitPackRecord(
const PackRecord&
pack, std::ostream& out)
override {
2205 out <<
"pack(recordTable,"
2206 <<
"Tuple<RamDomain," <<
pack.getArguments().size() <<
">";
2207 if (
pack.getArguments().size() == 0) {
2210 out <<
"{{ramBitCast(" <<
join(
pack.getArguments(),
"),ramBitCast(", rec) <<
")}}\n";
2219 void visitSubroutineArgument(
const SubroutineArgument& arg, std::ostream& out)
override {
2220 out <<
"(args)[" << arg.getArgument() <<
"]";
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()) {
2229 out <<
"ret.push_back(0);\n";
2231 out <<
"ret.push_back(";
2240 void visitUndefValue(
const UndefValue&, std::ostream& )
override {
2241 fatal(
"Compilation error");
2244 void visitNode(
const Node& node, std::ostream& )
override {
2245 fatal(
"Unsupported node type: %s",
typeid(node).name());
2249 out << std::setprecision(std::numeric_limits<RamFloat>::max_digits10);
2251 CodeEmitter(*this).visit(stmt, out);
2258 const SymbolTable& symTable = translationUnit.getSymbolTable();
2259 const Program& prog = translationUnit.getProgram();
2265 withSharedLibrary =
false;
2267 std::string classname =
"Sf_" +
id;
2272 os <<
"#define _SOUFFLE_STATS\n";
2274 os <<
"\n#include \"souffle/CompiledSouffle.h\"\n";
2276 os <<
"#include <mutex>\n";
2277 os <<
"#include \"souffle/provenance/Explain.h\"\n";
2281 os <<
"#include <thread>\n";
2282 os <<
"#include \"souffle/profile/Tui.h\"\n";
2286 std::map<std::string, std::tuple<TypeAttribute, std::vector<TypeAttribute>,
bool>> functors;
2288 if (functors.find(op.getName()) == functors.end()) {
2289 functors[op.getName()] = std::make_tuple(op.getReturnType(), op.getArgsTypes(), op.isStateful());
2291 withSharedLibrary =
true;
2293 os <<
"extern \"C\" {\n";
2294 for (
const auto& f : functors) {
2296 const std::string& name = f.first;
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);
2317 os <<
"souffle::RamDomain " << name <<
"(souffle::SymbolTable *, souffle::RecordTable *";
2318 for (
size_t i = 0;
i < argsTypes.size();
i++) {
2319 os <<
",souffle::RamDomain";
2323 tfm::format(os,
"%s %s(%s);\n", cppTypeDecl(returnType), name,
2324 join(
map(argsTypes, cppTypeDecl),
","));
2329 os <<
"namespace souffle {\n";
2330 os <<
"static const RamDomain RAM_BIT_SHIFT_MASK = RAM_DOMAIN_SIZE - 1;\n";
2333 for (
auto rel : prog.getRelations()) {
2335 auto relationType = Relation::getSynthesiserRelation(*
rel, idxAnalysis->getIndexes(
rel->getName()),
2338 generateRelationTypeStruct(os, std::move(relationType));
2342 os <<
"class " << classname <<
" : public SouffleProgram {\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";
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 << \") "
2362 os <<
" } return result;\n";
2366 os <<
"std::string profiling_fname;\n";
2372 os <<
"// -- initialize symbol table --\n";
2374 os <<
"SymbolTable symTable";
2375 if (symTable.size() > 0) {
2377 for (
size_t i = 0;
i < symTable.size();
i++) {
2378 os <<
"\tR\"_(" << symTable.resolve(
i) <<
")_\",\n";
2385 os <<
"// -- initialize record table --\n";
2387 os <<
"RecordTable recordTable;"
2394 os <<
" size_t freqs[" << numFreq <<
"]{};\n";
2396 for (
auto rel : prog.getRelations()) {
2397 if (!
rel->isTemp()) {
2401 os <<
" size_t reads[" << numRead <<
"]{};\n";
2405 std::stringstream initCons;
2406 std::stringstream registerRel;
2407 auto initConsSep = [&, empty =
true]()
mutable -> std::stringstream& {
2408 initCons << (empty ?
"\n: " :
"\n, ");
2415 initConsSep() <<
"profiling_fname(std::move(pf))";
2419 std::set<std::string> storeRelations;
2420 std::set<std::string> loadRelations;
2421 std::set<const IO*> loadIOs;
2422 std::set<const IO*> storeIOs;
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);
2434 assert(
"wrong I/O operation");
2438 for (
auto rel : prog.getRelations()) {
2440 const std::string& datalogName =
rel->getName();
2441 const std::string& cppName = getRelationName(*
rel);
2444 auto relationType = Relation::getSynthesiserRelation(*
rel, idxAnalysis->getIndexes(datalogName),
2446 const std::string&
type = relationType->getTypeName();
2449 os <<
"// -- Table: " << datalogName <<
"\n";
2451 os <<
"Own<" <<
type <<
"> " << cppName <<
" = mk<" <<
type <<
">();\n";
2452 if (!
rel->isTemp()) {
2453 tfm::format(os,
"souffle::RelationWrapper<%s> wrapper_%s;\n",
type, cppName);
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 <<
'"'; }) <<
"}}";
2462 auto foundIn = [&](
auto&& set) {
return contains(set,
rel->getName()) ?
"true" :
"false"; };
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));
2476 os << (
Global::config().has(
"profile") ?
"(std::string pf=\"profile.log\")" :
"()");
2477 os << initCons.str() <<
'\n';
2480 os <<
"ProfileEventSingleton::instance().setOutputFile(profiling_fname);\n";
2482 os << registerRel.str();
2486 os <<
"~" << classname <<
"() {\n";
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;
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;
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()); }
2516 signalHandler->set();
2519 os <<
"signalHandler->enableLogging();\n";
2523 os <<
"// -- query evaluation --\n";
2525 os <<
"ProfileEventSingleton::instance().startTimer();\n";
2526 os << R
"_(ProfileEventSingleton::instance().makeTimeEvent("@time;starttime");)_" << '\n';
2528 << R
"_(Logger logger("@runtime;", 0);)_" << '\n';
2530 size_t relationCount = 0;
2531 for (
auto rel : prog.getRelations()) {
2532 if (
rel->getName()[0] !=
'@') {
2537 os << R
"_(ProfileEventSingleton::instance().makeConfigRecord("relationCount", std::to_string()_"
2538 << relationCount << "));";
2542 emitCode(os, prog.getMain());
2546 os <<
"ProfileEventSingleton::instance().stopTimer();\n";
2547 os <<
"dumpFreqs();\n";
2551 os <<
"\n// -- relation hint statistics --\n";
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";
2562 os <<
"signalHandler->reset();\n";
2567 os <<
"public:\nvoid run() override { runFunction(\"\", \"\", "
2569 os <<
"public:\nvoid runAll(std::string inputDirectoryArg = \"\", std::string outputDirectoryArg = \"\") "
2572 os <<
"std::thread profiler([]() { profile::Tui().runProf(); });\n";
2574 os <<
"runFunction(inputDirectoryArg, outputDirectoryArg, true);\n";
2576 os <<
"if (profiler.joinable()) { profiler.join(); }\n";
2581 os <<
"void printAll(std::string outputDirectoryArg = \"\") override {\n";
2584 auto printDirectives = [&](
const std::map<std::string, std::string>& registry) {
2585 auto cur = registry.begin();
2586 if (cur == registry.end()) {
2589 os <<
"{{\"" << cur->first <<
"\",\"" <<
escape(cur->second) <<
"\"}";
2591 for (; cur != registry.end(); ++cur) {
2592 os <<
",{\"" << cur->first <<
"\",\"" <<
escape(cur->second) <<
"\"}";
2597 for (
auto store : storeIOs) {
2598 auto const& directive = store->getDirectives();
2600 os <<
"std::map<std::string, std::string> directiveMap(";
2601 printDirectives(directive);
2603 os << R
"_(if (!outputDirectoryArg.empty()) {)_";
2604 os << R"_(directiveMap["output-dir"] = outputDirectoryArg;)_";
2606 os <<
"IOSystem::getInstance().getWriter(";
2607 os <<
"directiveMap, symTable, recordTable";
2608 os <<
")->writeAll(*" << getRelationName(lookup(store->getRelation())) <<
");\n";
2610 os <<
"} catch (std::exception& e) {std::cerr << e.what();exit(1);}\n";
2616 os <<
"void loadAll(std::string inputDirectoryArg = \"\") override {\n";
2618 for (
auto load : loadIOs) {
2620 os <<
"std::map<std::string, std::string> directiveMap(";
2621 printDirectives(load->getDirectives());
2623 os << R
"_(if (!inputDirectoryArg.empty()) {)_";
2624 os << R"_(directiveMap["fact-dir"] = inputDirectoryArg;)_";
2626 os <<
"IOSystem::getInstance().getReader(";
2627 os <<
"directiveMap, symTable, recordTable";
2628 os <<
")->readAll(*" << getRelationName(lookup(load->getRelation()));
2630 os <<
"} catch (std::exception& e) {std::cerr << \"Error loading data: \" << e.what() << "
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();
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())}};
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\"] = ";
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";
2662 os <<
"void dumpInputs() override {\n";
2663 for (
auto load : loadIOs) {
2664 dumpRelation(*lookup(load->getRelation()));
2670 os <<
"void dumpOutputs() override {\n";
2671 for (
auto store : storeIOs) {
2672 dumpRelation(*lookup(store->getRelation()));
2677 os <<
"SymbolTable& getSymbolTable() override {\n";
2678 os <<
"return symTable;\n";
2681 os <<
"RecordTable& getRecordTable() override {\n";
2682 os <<
"return recordTable;\n";
2685 if (!prog.getSubroutines().empty()) {
2687 os <<
"void executeSubroutine(std::string name, const std::vector<RamDomain>& args, "
2688 "std::vector<RamDomain>& ret) override {\n";
2690 size_t subroutineNum = 0;
2691 for (
auto&
sub : prog.getSubroutines()) {
2692 os <<
"if (name == \"" <<
sub.first <<
"\") {\n"
2693 <<
"subroutine_" << subroutineNum
2699 os <<
"fatal(\"unknown subroutine\");\n";
2704 for (
auto&
sub : prog.getSubroutines()) {
2706 os <<
"#ifdef _MSC_VER\n";
2707 os <<
"#pragma warning(disable: 4100)\n";
2708 os <<
"#endif // _MSC_VER\n";
2712 <<
"subroutine_" << subroutineNum
2713 <<
"(const std::vector<RamDomain>& args, "
2714 "std::vector<RamDomain>& ret) {\n";
2717 bool needLock =
false;
2720 os <<
"std::mutex lock;\n";
2724 emitCode(os, *
sub.second);
2730 os <<
"#ifdef _MSC_VER\n";
2731 os <<
"#pragma warning(default: 4100)\n";
2732 os <<
"#endif // _MSC_VER\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";
2746 for (
auto const& cur : neIdxMap) {
2747 os <<
"\tProfileEventSingleton::instance().makeQuantityEvent(R\"_(@relation-reads;" << cur.first
2748 <<
")_\", reads[" << cur.second <<
"],0);\n";
2755 os <<
"SouffleProgram *newInstance_" <<
id <<
"(){return new " << classname <<
";}\n";
2756 os <<
"SymbolTable *getST_" <<
id <<
"(SouffleProgram *p){return &reinterpret_cast<" << classname
2757 <<
"*>(p)->symTable;}\n";
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";
2765 os <<
"factory_" << classname <<
"() : ProgramFactory(\"" <<
id <<
"\"){}\n";
2767 os <<
"extern \"C\" {\n";
2768 os <<
"factory_" << classname <<
" __factory_" << classname <<
"_instance;\n";
2773 os <<
"int main(int argc, char** argv)\n{\n";
2777 os <<
"souffle::CmdOptions opt(";
2791 os <<
"if (!opt.parse(argc,argv)) return 1;\n";
2795 os << classname +
" obj(opt.getProfileName());\n";
2797 os << classname +
" obj;\n";
2800 os <<
"#if defined(_OPENMP) \n";
2801 os <<
"obj.setNumThreads(opt.getNumJobs());\n";
2805 os << R
"_(souffle::ProfileEventSingleton::instance().makeConfigRecord("", opt.getSourceFileName());)_"
2807 os << R
"_(souffle::ProfileEventSingleton::instance().makeConfigRecord("fact-dir", opt.getInputFileDir());)_"
2809 os << R
"_(souffle::ProfileEventSingleton::instance().makeConfigRecord("jobs", std::to_string(opt.getNumJobs()));)_"
2811 os << R
"_(souffle::ProfileEventSingleton::instance().makeConfigRecord("output-dir", opt.getOutputFileDir());)_"
2813 os << R
"_(souffle::ProfileEventSingleton::instance().makeConfigRecord("version", ")_"
2816 os <<
"obj.runAll(opt.getInputFileDir(), opt.getOutputFileDir());\n";
2819 os <<
"explain(obj, false);\n";
2821 os <<
"explain(obj, true);\n";
2823 os <<
"return 0;\n";
2824 os <<
"} catch(std::exception &e) { souffle::SignalHandler::instance()->error(e.what());}\n";