Generate code.
numeric coersions follow C++ semantics.
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);