From 17d0172ee25106acd8670c7ade6c75d60476c683 Mon Sep 17 00:00:00 2001 From: Amir Hesamian Date: Tue, 1 Sep 2026 17:06:44 +0000 Subject: [PATCH 1/7] Original farrow implementation --- aps2scala/Makefile | 8 +- aps2scala/aps2scala.cc | 12 +- aps2scala/dump-scala.cc | 20 +- codegen/dump.h | 3 + codegen/farrow-impl.cc | 762 ++++++++++++++++++++++++++++++++++++++++ codegen/implement.h | 10 + codegen/synth-util.cc | 704 +++++++++++++++++++++++++++++++++++++ codegen/synth-util.h | 108 ++++++ examples/scala/Makefile | 8 +- parse/aps-util.c | 9 + parse/aps-util.h | 1 + 11 files changed, 1640 insertions(+), 5 deletions(-) create mode 100644 codegen/farrow-impl.cc create mode 100644 codegen/synth-util.cc create mode 100644 codegen/synth-util.h diff --git a/aps2scala/Makefile b/aps2scala/Makefile index a3d6f91a..1dbff83d 100644 --- a/aps2scala/Makefile +++ b/aps2scala/Makefile @@ -1,7 +1,7 @@ CPP=g++ CPPFLAGS=-Wall -g -Wno-unused-variable -DUSING_CXX -DAPS2SCALA -I../parse -I../analyze -I../codegen -I../utilities -APS2SCALAOBJS = aps2scala.o dump-scala.o implement.o dyn-impl.o static-impl.o static-scc-impl.o +APS2SCALAOBJS = aps2scala.o dump-scala.o implement.o dyn-impl.o static-impl.o static-scc-impl.o farrow-impl.o synth-util.o APS2SCALALIBS = ../lib/aps-lib.o ../lib/aps-ag.a ../utilities/utilities.o aps2scala : ${APS2SCALAOBJS} ${APS2SCALALIBS} ${CPP} ${CPPFLAGS} ${APS2SCALAOBJS} ${APS2SCALALIBS} -o aps2scala @@ -17,6 +17,12 @@ static-impl.o : ../codegen/static-impl.cc static-scc-impl.o : ../codegen/static-scc-impl.cc ${CPP} -c ${CPPFLAGS} $< -o $@ +farrow-impl.o : ../codegen/farrow-impl.cc ../codegen/synth-util.h + ${CPP} -c ${CPPFLAGS} $< -o $@ + +synth-util.o : ../codegen/synth-util.cc ../codegen/synth-util.h + ${CPP} -c ${CPPFLAGS} $< -o $@ + implement.o : ../codegen/implement.cc ${CPP} -c ${CPPFLAGS} $< -o $@ diff --git a/aps2scala/aps2scala.cc b/aps2scala/aps2scala.cc index 5b026603..345295bf 100644 --- a/aps2scala/aps2scala.cc +++ b/aps2scala/aps2scala.cc @@ -29,6 +29,7 @@ void usage() { fprintf(stderr," -V increase verbosity of generation code\n"); fprintf(stderr," -G add Debug calls for every function\n"); fprintf(stderr," -C SCC chunk static scheduling\n"); + fprintf(stderr," -F0, --synth-pure-farrow generate original Farrow SYNTH evaluation\n"); fprintf(stderr," -p path set the APSPATH (overriding env. variable)\n"); exit(1); } @@ -40,6 +41,7 @@ extern int aps_yyparse(void); Implementation* impl; bool static_schedule = false; bool is_tree_only_program = false; +bool farrow_implementation = false; static void* program_is_tree_only(void *scope, void *node) { if (ABSTRACT_APS_tnode_phylum(node) == KEYDeclaration) { @@ -82,6 +84,10 @@ int main(int argc,char **argv) { static_schedule = true; static_scc_schedule = true; continue; + } else if (streq(argv[i],"-F0") || streq(argv[i],"--synth-pure-farrow")) { + farrow_implementation = true; + anc_analysis = true; + continue; } else if (streq(argv[i],"-V") || streq(argv[i],"--verbose")) { ++verbose; continue; @@ -110,8 +116,9 @@ int main(int argc,char **argv) { type_Program(p); traverse_Program(program_is_tree_only, p, p); aps_check_error("type"); - if (static_schedule) { - impl = static_scc_schedule ? static_scc_impl : static_impl; + if (static_schedule || farrow_implementation) { + impl = farrow_implementation ? farrow_impl + : (static_scc_schedule ? static_scc_impl : static_impl); analyze_Program(p); aps_check_error("analysis"); if (!impl) { @@ -121,6 +128,7 @@ int main(int argc,char **argv) { } else { impl = dynamic_impl; } + impl->validate_program(p); char* outfilename = str2cat(argv[i],".scala"); std::ofstream out(outfilename); diff --git a/aps2scala/dump-scala.cc b/aps2scala/dump-scala.cc index 7c633cde..2ccc49f3 100644 --- a/aps2scala/dump-scala.cc +++ b/aps2scala/dump-scala.cc @@ -1681,7 +1681,7 @@ void dump_scala_Declaration(Declaration decl,ostream& oss) ++nesting_level; STATE *s = (STATE*)Declaration_info(decl)->analysis_state; - if (static_scc_schedule && s != NULL) + if ((static_scc_schedule || dynamic_cast(impl) != NULL) && s != NULL) { activate_static_circular = s->loop_required; } @@ -2455,6 +2455,11 @@ void dump_Expression(Expression e, ostream& o) dump_collect_Actuals(infer_expr_type(e),funcall_actuals(e),o); return; } + + if (auto* synth = dynamic_cast(impl)) { + if (synth->try_dump_funcall(e, o)) return; + } + bool dump_anchor_actual = false; if (should_include_ast_for_objects()) { Expression fexpr = funcall_f(e); @@ -2705,3 +2710,16 @@ bool check_surrounding_decl(void* node, KEYTYPE_Declaration decl_key, Declaratio *result_decl = NULL; return false; } + +bool check_surrounding_node(void* node, KEYTYPE_ABSTRACT_APS_Phylum ast_key, void** result_node) { + while (node != NULL && ABSTRACT_APS_tnode_phylum(node) != KEY_ABSTRACT_APS_None) { + if (ABSTRACT_APS_tnode_phylum(node) == ast_key) { + *result_node = node; + return true; + } + node = tnode_parent(node); + } + + *result_node = NULL; + return false; +} diff --git a/codegen/dump.h b/codegen/dump.h index 7a024886..a345c370 100644 --- a/codegen/dump.h +++ b/codegen/dump.h @@ -134,6 +134,9 @@ inline ostream& operator<<(ostream&os, INSTANCE*i) { extern string operator+(string, int); +bool check_surrounding_decl(void* node, KEYTYPE_Declaration decl_key, Declaration* result_decl); +bool check_surrounding_node(void* node, KEYTYPE_ABSTRACT_APS_Phylum ast_key, void** result_node); + #ifndef APS2SCALA // special C++ generation code // sending to oss copies to cpps, ... diff --git a/codegen/farrow-impl.cc b/codegen/farrow-impl.cc new file mode 100644 index 00000000..837cda55 --- /dev/null +++ b/codegen/farrow-impl.cc @@ -0,0 +1,762 @@ +#include + +#include +#include +extern "C" { +#include + +#include "aps-ag.h" +} +#include +#include +#include + +#include "dump.h" +#include "implement.h" +#include "synth-util.h" + +#ifdef APS2SCALA + +static AUG_GRAPH* current_aug_graph = NULL; +static std::vector synth_functions_states; +static synth_util::SynthFunctionState* current_synth_functions_state = NULL; + +static void* detect_program_fibers(void* scope, void* node) { + bool* uses_fibers = static_cast(scope); + if (ABSTRACT_APS_tnode_phylum(node) == KEYDeclaration) { + STATE* state = (STATE*)Declaration_info((Declaration)node)->analysis_state; + if (state != NULL && synth_util::state_uses_fibers(state)) { + *uses_fibers = true; + } + } + return scope; +} + +#define DEREF "." + +static SynthImplementation* farrow_impl_ptr; + +static vector current_blocks; +static synth_util::BlockItem* current_scope_block; +static vector dumped_conditional_block_items; +static vector dumped_instances; + +static void emit_start_phylum_evaluations(ostream& os, STATE* state) { + PHY_GRAPH* start_graph = summary_graph_for(state, state->start_phylum); + if (state->loop_required) { + os << indent() << "implicit val " << synth_util::LOOP_VAR << ": Boolean = false;\n"; + os << indent() << "implicit val changed: AtomicBoolean = new AtomicBoolean(false);\n"; + } + os << indent() << "for (root <- t_" << decl_name(state->start_phylum) << ".nodes) {\n"; + ++nesting_level; + for (int index = 0; index < start_graph->instances.length; ++index) { + INSTANCE* instance = &start_graph->instances.array[index]; + if (!synth_util::instance_is_synthesized(instance)) { + continue; + } + os << indent() << "eval_" << synth_util::instance_to_string_with_nodetype(state->start_phylum, instance) << "(root);\n"; + } + --nesting_level; + os << indent() << "}\n"; +} + +static void dump_farrow_functions(STATE* s, ostream& os) { + ostream& oss = os; + // first dump all visit functions for each phylum: + + os << "\n"; + + synth_functions_states = synth_util::build_synth_function_states(s); + bool needs_fixed_point = s->loop_required; + + for (auto state_it = synth_functions_states.begin(); state_it != synth_functions_states.end(); state_it++) { + synth_util::SynthFunctionState* synth_functions_state = *state_it; + current_synth_functions_state = synth_functions_state; + + // result var has instance index suffix so each function has unique name + string result_var = synth_util::RESULT_VAR_PREFIX + std::to_string(synth_functions_state->source->index); + + if (include_comments) { + os << indent() << "// " << synth_functions_state->source << " (" << (synth_functions_state->is_phylum_instance ? "phylum" : "aug-graph") << ")\n"; + } + if (synth_functions_state->is_side_effect_evaluation) { + os << indent() << "val evaluated_map_" << synth_functions_state->fdecl_name << " = scala.collection.mutable.Map[Int" + << ", Boolean]()" + << "\n\n"; + } + + os << indent() << "def eval_" << synth_functions_state->fdecl_name << "("; + os << "node: T_" << decl_name(synth_functions_state->source_phy_graph->phylum); + + for (auto it = synth_functions_state->regular_dependencies.begin(); it != synth_functions_state->regular_dependencies.end(); it++) { + INSTANCE* source_instance = *it; + if (synth_util::should_skip_synth_dependency(source_instance)) { + continue; + } + + // for locals, it needs prefix in formals, not for fibers or regular + // attributes + + os << ",\n"; + os << indent(nesting_level + 1); + os << "v_"; + + if (!synth_functions_state->is_phylum_instance) { + os << synth_util::instance_to_string(source_instance) << ": "; + } else { + os << synth_util::instance_to_string(source_instance, true) << ": "; + } + + synth_util::dump_attribute_type(source_instance, os); + } + + os << ")"; + + if (needs_fixed_point) { + os << "(implicit " << synth_util::LOOP_VAR << ": Boolean, changed: AtomicBoolean)"; + } + + os << ": "; + + if (synth_functions_state->is_side_effect_evaluation) { + os << "Unit"; + } else { + synth_util::dump_attribute_type(synth_functions_state->source, os); + } + os << " = {\n"; + nesting_level++; + + // don't cache if we are in the loop. + if (needs_fixed_point) { + os << indent() << "if (!" << synth_util::LOOP_VAR << ") {\n"; + nesting_level++; + } + + if (synth_functions_state->is_side_effect_evaluation) { + os << indent() << "evaluated_map_" << synth_functions_state->fdecl_name << ".getOrElse(node.nodeNumber, false) match {\n"; + os << indent(nesting_level + 1) << "case true => "; + os << "return ()\n"; + } else { + os << indent() << synth_util::instance_to_attr(synth_functions_state->source) << ".checkNode(node).status match {\n"; + os << indent(nesting_level + 1) << "case Evaluation.ASSIGNED => "; + if (include_comments) { + os << "{\n"; + nesting_level++; + os << indent(nesting_level + 1) << "Debug.out(\"cache hit for \" + node + \" with value \" + " << synth_util::instance_to_attr(synth_functions_state->source) << ".get(node));\n"; + os << indent(nesting_level + 1); + } + + os << "return " << synth_util::instance_to_attr(synth_functions_state->source) << ".get(node)\n"; + + if (include_comments) { + nesting_level--; + os << indent(nesting_level + 1) << "}\n"; + } + } + + os << indent(nesting_level + 1) << "case _ => ()\n"; + os << indent() << "};\n"; + + if (needs_fixed_point) { + nesting_level--; + os << indent() << "}"; + os << "\n"; + } + + if (synth_functions_state->is_side_effect_evaluation) { + os << indent() << "node match {\n"; + } else { + os << indent() << "val " << result_var << " = node match {\n"; + } + nesting_level++; + + // True if this attribute is circular anywhere. Then every assignment uses + // the change-tracking overload so the closure-site loop can see it settle; + // only the closure site emits the do/while. + bool source_circular = needs_fixed_point && synth_util::synth_function_is_circular(synth_functions_state); + + for (auto it = synth_functions_state->aug_graphs.begin(); it != synth_functions_state->aug_graphs.end(); it++) { + AUG_GRAPH* aug_graph = *it; + int n = aug_graph->instances.length; + + current_aug_graph = aug_graph; + current_blocks.push_back(matcher_body(top_level_match_m(aug_graph->match_rule))); + + os << indent() << "case " << matcher_pat(top_level_match_m(aug_graph->match_rule)) << " => {\n"; + nesting_level++; + + INSTANCE* aug_graph_instance = NULL; + if (synth_functions_state->is_phylum_instance) { + if (!synth_util::find_instance(aug_graph, aug_graph->lhs_decl, synth_functions_state->source->fibered_attr, &aug_graph_instance)) { + fatal_error("something is wrong with instances in aug graph %s", aug_graph_name(aug_graph)); + } + } else { + aug_graph_instance = synth_functions_state->source; + } + + // Linearize the current scope block but make sure IF statements or + // conditional instances that have nothing to do with this instance don't + // appear in linearization + current_scope_block = synth_util::linearize_block(aug_graph, aug_graph_instance); + + if (include_comments) { + os << indent() << "/* Linearized schedule:\n"; + nesting_level++; + synth_util::print_linearized_block(current_scope_block, os); + nesting_level--; + os << indent() << "*/\n"; + } + + bool declared_is_circular = instance_circular(aug_graph_instance); + int instance_index = aug_graph_instance->index; + bool depends_on_itself = edgeset_kind(aug_graph->graph[instance_index * n + instance_index]) != 0; + + if (!declared_is_circular && depends_on_itself) { + aps_warning(aug_graph_instance->node, "Instance %s depends on itself but is not declared circular", synth_util::instance_to_string(aug_graph_instance).c_str()); + } + + // Non-fiber cycles are evaluated one circular dependency class at a time. + // converge child cycles first, so the value below sees stable results + if (!synth_functions_state->is_side_effect_evaluation) { + std::vector> child_cycle_components = synth_util::collect_child_cycle_components(aug_graph, aug_graph_instance); + for (size_t component_index = 0; component_index < child_cycle_components.size(); ++component_index) { + std::string changed_flag = "localChanged" + std::to_string(component_index); + synth_util::emit_fixed_point_loop_start(os, changed_flag); + for (auto instance_it = child_cycle_components[component_index].begin(); instance_it != child_cycle_components[component_index].end(); ++instance_it) { + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + os << indent(); + farrow_impl_ptr->dump_synth_instance(*instance_it, os); + os << ";\n"; + } + synth_util::emit_fixed_point_loop_end(os); + } + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + } + + if (synth_functions_state->is_side_effect_evaluation) { + if (include_comments) { + os << "\n"; + } + os << indent(); + farrow_impl_ptr->dump_synth_instance(aug_graph_instance, os); + os << "\n"; + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + } + + // Non-fiber instance computation + if (!synth_functions_state->is_side_effect_evaluation) { + os << indent(); + farrow_impl_ptr->dump_synth_instance(aug_graph_instance, os); + os << "\n"; + } + + current_blocks.clear(); + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + + nesting_level--; + os << indent() << "}\n"; + } + + os << indent() + << "case _ => throw new RuntimeException(\"failed pattern matching: \" " + "+ node)\n"; + + nesting_level--; + os << indent() << "};\n"; + + if (synth_functions_state->is_side_effect_evaluation) { + os << indent() << "evaluated_map_" << synth_functions_state->fdecl_name << ".update(node.nodeNumber, true);\n"; + } else { + if (source_circular) { + os << indent() << synth_util::instance_to_attr(synth_functions_state->source) << ".assign(node, " << result_var << ", changed);\n"; + } else { + os << indent() << synth_util::instance_to_attr(synth_functions_state->source) << ".assign(node, " << result_var << ");\n"; + } + os << indent() << synth_util::instance_to_attr(synth_functions_state->source) << ".get(node);\n"; + } + + if (!synth_functions_state->is_side_effect_evaluation) { + os << indent() << result_var << "\n"; + } + + nesting_level--; + os << indent() << "}\n\n"; + } + + synth_util::destroy_synth_function_states(synth_functions_states); + synth_functions_states.clear(); +} + +class FarrowImpl : public SynthImplementation { + public: + typedef Implementation::ModuleInfo Super; + class ModuleInfo : public Super { + public: + ModuleInfo(Declaration mdecl) : Implementation::ModuleInfo(mdecl) {} + + void note_top_level_match(Declaration tlm, GEN_OUTPUT& oss) { Super::note_top_level_match(tlm, oss); } + + void note_local_attribute(Declaration ld, GEN_OUTPUT& oss) { + Super::note_local_attribute(ld, oss); + Declaration_info(ld)->decl_flags |= LOCAL_ATTRIBUTE_FLAG; + } + + void note_attribute_decl(Declaration ad, GEN_OUTPUT& oss) { + Declaration_info(ad)->decl_flags |= ATTRIBUTE_DECL_FLAG; + Super::note_attribute_decl(ad, oss); + } + + void note_var_value_decl(Declaration vd, GEN_OUTPUT& oss) { Super::note_var_value_decl(vd, oss); } + + void implement(ostream& os) { + STATE* s = (STATE*)Declaration_info(module_decl)->analysis_state; + ostream& oss = os; + + dump_farrow_functions(s, oss); + + // same flag dump_farrow_functions uses to gate the implicit params, so + // finish() brings the matching implicit into scope for the eval calls below + bool needs_fixed_point = s->loop_required; + + // Implement finish routine: + os << indent() << "override def finish() : Unit = {\n"; + ++nesting_level; + + emit_start_phylum_evaluations(os, s); + + os << indent() << "super.finish();\n"; + --nesting_level; + os << indent() << "};\n"; + + clear_implementation_marks(module_decl); + } + }; + + Super* get_module_info(Declaration m) { return new ModuleInfo(m); } + + void validate_program(Program program) { + bool uses_fibers = false; + traverse_Program(detect_program_fibers, &uses_fibers, program); + if (uses_fibers) { + fatal_error( + "-F0 does not support fibers; Farrow's original algorithm is only " + "defined for non-fiber grammars"); + } + } + + void implement_function_body(Declaration f, ostream& os) { dynamic_impl->implement_function_body(f, os); } + + void implement_value_use(Declaration vd, ostream& os) { synth_util::implement_value_use(vd, current_aug_graph, synth_functions_states, this, os); } + + void dump_assignment(INSTANCE* in, Expression rhs, ostream& o) { + Declaration ad = in != NULL ? in->fibered_attr.attr : NULL; + Symbol asym = ad ? def_name(declaration_def(ad)) : 0; + bool node_is_syntax = in->node == current_aug_graph->lhs_decl; + + if (in->fibered_attr.fiber != NULL) { + fatal_error("internal error: -F0 attempted to emit a fiber assignment"); + } + + if (in->node == 0 && ad != NULL) { + if (rhs) { + if (Declaration_info(ad)->decl_flags & LOCAL_ATTRIBUTE_FLAG) { + o << "a" << LOCAL_UNIQUE_PREFIX(ad) << "_" << asym << DEREF; + if (debug) { + o << "assign"; + } else { + o << "set"; + } + o << "(anchor," << rhs << ");\n"; + } else { + int i = LOCAL_UNIQUE_PREFIX(ad); + if (i == 0) { + if (!def_is_constant(value_decl_def(ad))) { + if (include_comments) { + o << "// v_" << asym << " is assigned/initialized by default.\n"; + } + } else { + if (include_comments) { + o << "// v_" << asym << " is initialized in module.\n"; + } + } + } else { + o << "v" << i << "_" << asym << " = " << rhs << "; // local\n"; + } + } + } else { + if (!direction_is_collection(some_value_decl_direction(ad))) { + aps_warning(ad, "Local attribute %s is apparently undefined", decl_name(ad)); + } + if (include_comments) { + o << "// " << in << " is ready now\n"; + } + } + return; + } else if (node_is_syntax) { + if (ATTR_DECL_IS_SHARED_INFO(ad)) { + if (include_comments) { + o << "// shared info for " << decl_name(in->node) << " is ready.\n"; + } + } else if (ATTR_DECL_IS_UP_DOWN(ad)) { + if (include_comments) { + o << "// " << decl_name(in->node) << "." << decl_name(ad) << " implicit.\n"; + } + } else if (rhs) { + if (Declaration_KEY(in->node) == KEYfunction_decl) { + Direction ad_dir = some_value_decl_direction(ad); + if (direction_is_collection(ad_dir)) { + std::cout << "Not expecting collection here!\n"; + o << "v_" << asym << " = somehow_combine(v_" << asym << "," << rhs << ");\n"; + } else { + int i = LOCAL_UNIQUE_PREFIX(ad); + if (i == 0) { + o << "v_" << asym << " = " << rhs << "; // function\n"; + } else { + o << "v" << i << "_" << asym << " = " << rhs << ";\n"; + } + } + } else { + o << "a_" << asym << DEREF; + if (debug) { + o << "assign"; + } else { + o << "set"; + } + o << "(v_" << decl_name(in->node) << "," << rhs << ");\n"; + } + } else { + aps_warning(in->node, "Attribute %s.%s is apparently undefined", decl_name(in->node), symbol_name(asym)); + + if (include_comments) { + o << "// " << in << " is ready.\n"; + } + } + return; + } else if (Declaration_KEY(in->node) == KEYvalue_decl) { + if (rhs) { + // assigning field of object + o << "a_" << asym << DEREF; + if (debug) { + o << "assign"; + } else { + o << "set"; + } + o << "(v_" << decl_name(in->node) << "," << rhs << ");\n"; + } else { + if (include_comments) { + o << "// " << in << " is ready now.\n"; + } + } + return; + } + } + + void dump_rhs_instance_helper(AUG_GRAPH* aug_graph, synth_util::BlockItem* item, INSTANCE* instance, ostream& o) { + if (item == NULL) { + if (include_comments) { + o << "// " << instance << " is ready now.\n"; + } + return; + } + + if (item->key == synth_util::KEY_BLOCK_ITEM_INSTANCE) { + synth_util::BlockItemInstance* bi = reinterpret_cast(item); + + if (bi->instance != instance && bi->next != NULL) { + dump_rhs_instance_helper(aug_graph, bi->next, instance, o); + return; + } + + vector> all_assignments = synth_util::make_instance_assignments(current_aug_graph, current_blocks); + std::set relevant_assignments = all_assignments[instance->index]; + + if (!relevant_assignments.empty()) { + // Filter out NULLs first + vector valid_rhs; + for (auto it = relevant_assignments.begin(); it != relevant_assignments.end(); it++) { + if (*it != NULL) { + valid_rhs.push_back(*it); + } + } + + if (!valid_rhs.empty()) { + if (valid_rhs.size() == 1) { + dump_Expression(valid_rhs[0], o); + } else { + // Multiple RHS entries only occur from collect_assign (:>) + // contributions. Combine them pairwise using the type's v_combine. + Declaration attr = instance->fibered_attr.attr; + Direction attr_dir = some_value_decl_direction(attr); + if (!direction_is_collection(attr_dir)) { + fatal_error("Multiple RHS for non-collection attribute %s", decl_name(attr)); + } + Type vt = Declaration_KEY(attr) == KEYattribute_decl ? function_type_return_type(attribute_decl_type(attr)) : value_decl_type(attr); + // Nest v_combine calls for all contributions + for (size_t i = 0; i < valid_rhs.size() - 1; i++) { + o << as_val(vt) << ".v_combine("; + } + dump_Expression(valid_rhs[0], o); + for (size_t i = 1; i < valid_rhs.size(); i++) { + o << ", "; + dump_Expression(valid_rhs[i], o); + o << ")"; + } + } + return; + } + // valid_rhs is empty (all NULL defaults) -- fall through to default + // handling + } + + // A local collection attribute may be assigned only inside a case/match + // block, which make_instance_assignment() doesn't see at this level. + Declaration attr = instance->fibered_attr.attr; + bool is_local_collection = direction_is_collection(some_value_decl_direction(attr)); + if (is_local_collection) { + // Check the type is combinable: look for a "combine" declaration in any + // class in its canonical signature set. This covers COMBINABLE, + // MAKE_LATTICE, etc. without walking parent signature chains. + Type vt = infer_some_value_decl_type(attr); + CanonicalType* ctype = canonical_type(vt); + CanonicalSignatureSet csig_set = infer_canonical_signatures(ctype); + bool is_combinable = false; + for (int ci = 0; ci < csig_set->num_elements && !is_combinable; ci++) { + CanonicalSignature* csig = (CanonicalSignature*)csig_set->elements[ci]; + Block body = some_class_decl_contents(csig->source_class); + for (Declaration bd = first_Declaration(block_body(body)); bd; bd = DECL_NEXT(bd)) { + if (strcmp(decl_name(bd), "combine") == 0) { + is_combinable = true; + break; + } + } + } + if (is_combinable) { + // No direct assignment in this block (it's inside a + // conditional/case), so this branch contributes the type's + // initial/bottom value. + o << as_val(vt) << ".v_initial"; + if (include_comments) { + o << " /* local collection " << decl_name(attr) << ": no direct assignment, using initial */"; + } + return; + } + } + print_instance(instance, stdout); + printf( + " is a non-fiber instance, but no assignment found in this block. " + "%d\n", + if_rule_p(instance->fibered_attr.attr)); + fatal_error("crashed since non-fiber instance is missing an assignment"); + } else if (item->key == synth_util::KEY_BLOCK_ITEM_CONDITION) { + synth_util::BlockItemCondition* cond = reinterpret_cast(item); + bool visited_if_stmt = std::find(dumped_conditional_block_items.begin(), dumped_conditional_block_items.end(), item) != dumped_conditional_block_items.end(); + dumped_conditional_block_items.push_back(item); + + switch (ABSTRACT_APS_tnode_phylum(cond->condition)) { + case KEYDeclaration: { + Declaration if_stmt = (Declaration)cond->condition; + if (Declaration_KEY(if_stmt) != KEYif_stmt) { + fatal_error("expected if statement, got %s %d", decl_name(if_stmt), Declaration_info(if_stmt)); + } + + if (!edgeset_kind(current_aug_graph->graph[cond->instance->index * current_aug_graph->instances.length + instance->index])) { + printf("\n"); + print_instance(cond->instance, stdout); + printf(" does not affect "); + print_instance(instance, stdout); + printf("\n"); + fatal_error("crashed since instance not affected by condition"); + } + + if (!visited_if_stmt) { + o << "if ("; + dump_Expression(if_stmt_cond(if_stmt), o); + o << ") {\n"; + nesting_level++; + } + current_blocks.push_back(if_stmt_if_true(if_stmt)); + if (!visited_if_stmt) { + o << indent(); + } + + vector dumped_instanced_positive(dumped_instances); + dump_rhs_instance_helper(aug_graph, cond->next_positive, instance, o); + dumped_instances = dumped_instanced_positive; + + if (!visited_if_stmt) { + current_blocks.pop_back(); + o << "\n"; + nesting_level--; + o << indent() << "} else {\n"; + nesting_level++; + } + current_blocks.push_back(if_stmt_if_false(if_stmt)); + if (!visited_if_stmt) { + o << indent(); + } + + vector dumped_instanced_negative(dumped_instances); + dump_rhs_instance_helper(aug_graph, cond->next_negative, instance, o); + dumped_instances = dumped_instanced_negative; + + current_blocks.pop_back(); + if (!visited_if_stmt) { + nesting_level--; + o << "\n"; + o << indent() << "}"; + } + break; + } + case KEYMatch: { + Match m = (Match)cond->condition; + Pattern p = matcher_pat(m); + Declaration header = Match_info(m)->header; + // if first match in case, we evaluate variable: + if (m == first_Match(case_stmt_matchers(header))) { + Expression e = case_stmt_expr(header); + o << "{\n"; + nesting_level++; + o << indent() << "val node" << instance->index << " = " << e << ";\n"; + } + o << indent() << "node" << instance->index << " match {\n"; + nesting_level++; + o << indent() << "case " << p << " => {\n"; + nesting_level += 1; + Block if_true; + Block if_false; + if_true = matcher_body(m); + if (MATCH_NEXT(m)) { + if_false = 0; //? Why not the nxt match ? + } else { + if_false = case_stmt_default(header); + } + + current_blocks.push_back(if_true); + o << indent(); + dump_rhs_instance_helper(aug_graph, cond->next_positive, instance, o); + o << "\n"; + current_blocks.pop_back(); + + nesting_level--; + o << indent() << "}\n"; + o << indent() << "case _ => {\n"; + nesting_level++; + current_blocks.push_back(if_false); + o << indent(); + dump_rhs_instance_helper(aug_graph, cond->next_negative, instance, o); + o << "\n"; + current_blocks.pop_back(); + + nesting_level--; + o << indent() << "}\n"; + nesting_level--; + o << indent() << "}\n"; + if (m == first_Match(case_stmt_matchers(header))) { + nesting_level--; + o << indent() << "}"; + } + + break; + } + default: + fatal_error("unhandled if statement type"); + break; + } + } + } + + bool try_dump_funcall(Expression e, ostream& o) override { return synth_util::try_dump_funcall(e, current_aug_graph, this, o); } + + void dump_synth_instance(INSTANCE* instance, ostream& o) { + bool already_dumped = false; + if (std::find(dumped_instances.begin(), dumped_instances.end(), instance) != dumped_instances.end()) { + already_dumped = true; + } else { + dumped_instances.push_back(instance); + } + + AUG_GRAPH* aug_graph = current_aug_graph; + synth_util::BlockItem* block = synth_util::find_surrounding_block(current_scope_block, instance); + + Declaration node = instance->node; + bool is_parent_instance = synth_util::instance_is_parent(instance, current_aug_graph); + + bool is_synthesized = synth_util::instance_is_synthesized(instance); + bool is_inherited = synth_util::instance_is_inherited(instance); + bool is_circular = edgeset_kind(current_aug_graph->graph[instance->index * current_aug_graph->instances.length + instance->index]); + bool is_match_formal = synth_util::is_match_formal(instance->fibered_attr.attr); + bool is_available = is_match_formal || is_inherited; + + if (is_circular && already_dumped && !is_available) { + o << "/* circular dependency detected for " << instance << ", dumping as attribute access */ "; + + o << synth_util::instance_to_attr(instance) << ".get("; + if (instance->node == NULL) { + o << "node"; + } else { + o << "v_" << decl_name(instance->node); + } + + o << ")"; + return; + } else if (is_match_formal) { + o << "v_" << synth_util::instance_to_string(instance, current_synth_functions_state->is_phylum_instance); + } else if (is_inherited) { + if (is_parent_instance) { + o << "v_" << synth_util::instance_to_string(instance, current_synth_functions_state->is_phylum_instance); + } else { + dump_rhs_instance_helper(aug_graph, block, instance, o); + } + } else if (is_synthesized) { + if (is_parent_instance) { + dump_rhs_instance_helper(aug_graph, block, instance, o); + } else { + for (auto it = synth_functions_states.begin(); it != synth_functions_states.end(); it++) { + synth_util::SynthFunctionState* synth_function_state = *it; + if (fibered_attr_equal(&synth_function_state->source->fibered_attr, &instance->fibered_attr)) { + o << "eval_" << synth_function_state->fdecl_name << "(\n"; + int saved_nesting = nesting_level; + nesting_level = std::max(nesting_level + 2, 2); + o << indent() << "v_" << decl_name(node); + + const std::vector& dependencies = synth_function_state->regular_dependencies; + for (auto it = dependencies.begin(); it != dependencies.end(); it++) { + INSTANCE* source_instance = *it; + + if (synth_util::should_skip_synth_dependency(source_instance)) { + continue; + } + + for (int i = 0; i < current_aug_graph->instances.length; i++) { + INSTANCE* in = ¤t_aug_graph->instances.array[i]; + if (in->node == node && fibered_attr_equal(&in->fibered_attr, &source_instance->fibered_attr)) { + o << ",\n" << indent(); + dump_synth_instance(in, o); + } + } + } + nesting_level = saved_nesting; + + o << "\n" << indent() << ")"; + return; + } + } + + printf("failed to find synth function for instance "); + print_instance(instance, stdout); + printf("\n"); + fatal_error("internal error: failed to find synth function for instance"); + } + } else { + dump_rhs_instance_helper(aug_graph, block, instance, o); + } + } +}; + +Implementation* farrow_impl = farrow_impl_ptr = new FarrowImpl(); + +#endif /* APS2SCALA */ diff --git a/codegen/implement.h b/codegen/implement.h index 22314831..45ca03e8 100644 --- a/codegen/implement.h +++ b/codegen/implement.h @@ -59,6 +59,8 @@ class Implementation { virtual ModuleInfo* get_module_info(Declaration module) = 0; + virtual void validate_program(Program) {} + // header is done, and indentation is set. virtual void implement_function_body(Declaration f, ostream&) = 0; @@ -70,9 +72,17 @@ class Implementation { virtual void implement_value_use(Declaration vd, ostream&) = 0; }; +class SynthImplementation : public Implementation { + public: + virtual bool try_dump_funcall(Expression, ostream&) = 0; + virtual void dump_synth_instance(INSTANCE*, ostream&) = 0; +}; + extern Implementation *dynamic_impl; extern Implementation *static_impl; extern Implementation *static_scc_impl; +extern Implementation *farrow_impl; +extern Implementation *synth_impl; #define IMPLEMENTATION_MARKS (127<<24) diff --git a/codegen/synth-util.cc b/codegen/synth-util.cc new file mode 100644 index 00000000..a5776be7 --- /dev/null +++ b/codegen/synth-util.cc @@ -0,0 +1,704 @@ +#include "synth-util.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "aps-scc.h" +#include "dump.h" +#include "implement.h" + +namespace synth_util { + +const std::string LOOP_VAR = "isInsideFixedPoint"; +const std::string RESULT_VAR_PREFIX = "result"; + +void emit_loop_implicits(std::ostream& output, const std::string& flag) { + output << indent() << "implicit val " << LOOP_VAR << ": Boolean = true;\n"; + output << indent() << "implicit val changed: AtomicBoolean = " << flag << ";\n"; +} + +void emit_fixed_point_loop_start(std::ostream& output, const std::string& flag) { + output << indent() << "val " << flag << " = new AtomicBoolean(true);\n"; + output << indent() << "while (" << flag << ".get) {\n"; + ++nesting_level; + output << indent() << flag << ".set(false);\n"; + emit_loop_implicits(output, flag); +} + +void emit_fixed_point_loop_end(std::ostream& output) { + --nesting_level; + output << indent() << "}\n"; +} + +static enum instance_direction instance_direction_for(INSTANCE* instance) { + enum instance_direction direction = fibered_attr_direction(&instance->fibered_attr); + if (instance->node == NULL || DECL_IS_LHS(instance->node) || DECL_IS_RHS(instance->node)) { + return direction; + } + if (DECL_IS_LOCAL(instance->node)) { + return instance_local; + } + fatal_error("%d: unknown attributed node", tnode_line_number(instance->node)); + return direction; +} + +bool instance_is_synthesized(INSTANCE* instance) { return instance_direction_for(instance) == instance_outward; } + +bool instance_is_inherited(INSTANCE* instance) { return instance_direction_for(instance) == instance_inward; } + +bool find_instance(AUG_GRAPH* graph, Declaration node, const FIBERED_ATTRIBUTE& attribute, INSTANCE** result) { + for (int index = 0; index < graph->instances.length; ++index) { + INSTANCE* instance = &graph->instances.array[index]; + FIBERED_ATTRIBUTE candidate = attribute; + if (instance->node == node && fibered_attr_equal(&instance->fibered_attr, &candidate)) { + *result = instance; + return true; + } + } + return false; +} + +bool state_uses_fibers(STATE* state) { + for (int index = 0; index < state->match_rules.length; ++index) { + AUG_GRAPH* graph = &state->aug_graphs[index]; + for (int instance_index = 0; instance_index < graph->instances.length; ++instance_index) { + if (graph->instances.array[instance_index].fibered_attr.fiber != NULL) { + return true; + } + } + } + for (int index = 0; index < state->phyla.length; ++index) { + PHY_GRAPH* graph = &state->phy_graphs[index]; + for (int instance_index = 0; instance_index < graph->instances.length; ++instance_index) { + if (graph->instances.array[instance_index].fibered_attr.fiber != NULL) { + return true; + } + } + } + return false; +} + +bool instance_is_pure_shared_info(INSTANCE* instance) { return instance->fibered_attr.fiber == NULL && ATTR_DECL_IS_SHARED_INFO(instance->fibered_attr.attr); } + +static std::string attr_to_string(Declaration attribute) { return ATTR_DECL_IS_SHARED_INFO(attribute) ? "sharedinfo" : decl_name(attribute); } + +static std::string fiber_to_string(FIBER fiber) { + std::stringstream output; + while (fiber != NULL && fiber->field != NULL) { + std::string field = decl_name(fiber->field); + field.erase(std::remove(field.begin(), field.end(), '!'), field.end()); + output << field; + fiber = fiber->shorter; + if (fiber->field != NULL) { + output << "_"; + } + } + return output.str(); +} + +std::string instance_to_string(INSTANCE* instance, bool trim_node) { + std::vector parts; + Declaration node = instance->node; + if (!trim_node && node != NULL) { + parts.push_back(Declaration_KEY(node) == KEYpragma_call ? symbol_name(pragma_call_name(node)) : decl_name(node)); + } + if (instance->fibered_attr.attr != NULL) { + parts.push_back(attr_to_string(instance->fibered_attr.attr)); + } + if (instance->fibered_attr.fiber != NULL) { + parts.push_back(fiber_to_string(instance->fibered_attr.fiber)); + } + return std::accumulate(std::next(parts.begin()), parts.end(), parts[0], [](std::string left, std::string right) { return left + "_" + right; }); +} + +std::string instance_to_string_with_nodetype(Declaration node_type, INSTANCE* instance) { + std::stringstream output; + Declaration attribute = instance->fibered_attr.attr; + if (Declaration_KEY(attribute) == KEYvalue_decl && LOCAL_UNIQUE_PREFIX(attribute) != 0) { + output << "a" << LOCAL_UNIQUE_PREFIX(attribute) << "_"; + } + output << decl_name(node_type) << "_" << instance_to_string(instance); + return output.str(); +} + +std::string instance_to_attr(INSTANCE* instance) { + Declaration attribute = instance->fibered_attr.attr; + Declaration field = instance->fibered_attr.fiber != NULL ? instance->fibered_attr.fiber->field : NULL; + std::stringstream output; + if (Declaration_KEY(attribute) == KEYvalue_decl && LOCAL_UNIQUE_PREFIX(attribute) != 0) { + output << "a" << LOCAL_UNIQUE_PREFIX(attribute); + } else { + output << "a"; + } + if (attribute != NULL && !ATTR_DECL_IS_SHARED_INFO(attribute)) { + output << "_" << decl_name(attribute); + } + if (field != NULL) { + std::string field_name = decl_name(field); + field_name.erase(std::remove(field_name.begin(), field_name.end(), '!'), field_name.end()); + output << "_" << field_name; + } + return output.str(); +} + +bool is_match_formal(void* node) { + Declaration formal = NULL; + void* match = NULL; + return check_surrounding_decl(node, KEYnormal_formal, &formal) && check_surrounding_node(node, KEYMatch, &match); +} + +bool should_skip_synth_dependency(INSTANCE* instance) { return instance->fibered_attr.fiber != NULL || if_rule_p(instance->fibered_attr.attr) || is_match_formal(instance->fibered_attr.attr); } + +static std::vector collect_phylum_graph_attr_dependencies(PHY_GRAPH* phylum_graph, INSTANCE* sink_instance) { + std::vector result; + int instance_count = phylum_graph->instances.length; + + for (int index = 0; index < instance_count; ++index) { + INSTANCE* source_instance = &phylum_graph->instances.array[index]; + if (!instance_is_pure_shared_info(source_instance) && source_instance->index != sink_instance->index && phylum_graph->mingraph[source_instance->index * instance_count + sink_instance->index]) { + result.push_back(source_instance); + } + } + return result; +} + +static bool is_function_decl_attribute(INSTANCE* instance) { + if (instance->node == NULL || ABSTRACT_APS_tnode_phylum(instance->node) != KEYDeclaration || Declaration_KEY(instance->node) != KEYpragma_call) { + return false; + } + Declaration function = Declaration_info(instance->node)->proxy_fdecl; + return Declaration_KEY(function) == KEYfunction_decl; +} + +static bool is_some_function_decl(Declaration declaration) { + switch (Declaration_KEY(declaration)) { + case KEYsome_function_decl: + return true; + default: + return false; + } +} + +static bool is_formal(Declaration declaration) { + switch (Declaration_KEY(declaration)) { + case KEYformal: + return true; + default: + return false; + } +} + +static std::vector collect_aug_graph_attr_dependencies(AUG_GRAPH* aug_graph, INSTANCE* sink_instance) { + std::vector result; + int instance_count = aug_graph->instances.length; + + for (int index = 0; index < instance_count; ++index) { + INSTANCE* source_instance = &aug_graph->instances.array[index]; + if (!instance_is_pure_shared_info(source_instance) && source_instance->index != sink_instance->index && !is_function_decl_attribute(source_instance) && + edgeset_kind(aug_graph->graph[source_instance->index * instance_count + sink_instance->index])) { + result.push_back(source_instance); + } + } + return result; +} + +static std::vector collect_lhs_aug_graphs(STATE* state, PHY_GRAPH* phylum_graph) { + std::vector result; + for (int index = 0; index < state->match_rules.length; ++index) { + AUG_GRAPH* aug_graph = &state->aug_graphs[index]; + PHY_GRAPH* lhs_graph = Declaration_info(aug_graph->lhs_decl)->node_phy_graph; + if (lhs_graph == phylum_graph && !is_some_function_decl(aug_graph->lhs_decl)) { + result.push_back(aug_graph); + } + } + return result; +} + +std::vector build_synth_function_states(STATE* state) { + std::vector result; + + for (int phylum_index = 0; phylum_index < state->phyla.length; ++phylum_index) { + PHY_GRAPH* phylum_graph = &state->phy_graphs[phylum_index]; + if (Declaration_KEY(phylum_graph->phylum) != KEYphylum_decl) { + continue; + } + + for (int instance_index = 0; instance_index < phylum_graph->instances.length; ++instance_index) { + INSTANCE* instance = &phylum_graph->instances.array[instance_index]; + if (!instance_is_synthesized(instance)) { + continue; + } + + SynthFunctionState* function_state = new SynthFunctionState(); + function_state->fdecl_name = instance_to_string_with_nodetype(phylum_graph->phylum, instance); + function_state->source = instance; + function_state->source_phy_graph = phylum_graph; + function_state->is_phylum_instance = true; + function_state->is_side_effect_evaluation = instance->fibered_attr.fiber != NULL || ATTR_DECL_IS_SHARED_INFO(instance->fibered_attr.attr); + function_state->regular_dependencies = collect_phylum_graph_attr_dependencies(phylum_graph, instance); + function_state->aug_graphs = collect_lhs_aug_graphs(state, phylum_graph); + result.push_back(function_state); + } + } + + for (int graph_index = 0; graph_index < state->match_rules.length; ++graph_index) { + AUG_GRAPH* aug_graph = &state->aug_graphs[graph_index]; + if (is_some_function_decl(aug_graph->lhs_decl)) { + continue; + } + + for (int instance_index = 0; instance_index < aug_graph->instances.length; ++instance_index) { + INSTANCE* instance = &aug_graph->instances.array[instance_index]; + if (instance_direction_for(instance) != instance_local || instance->fibered_attr.fiber != NULL || if_rule_p(instance->fibered_attr.attr) || is_formal(instance->fibered_attr.attr)) { + continue; + } + + Declaration attribute = instance->fibered_attr.attr; + PHY_GRAPH* phylum_graph = Declaration_info(aug_graph->lhs_decl)->node_phy_graph; + Declaration type_decl = canonical_type_decl(canonical_type(value_decl_type(attribute))); + + SynthFunctionState* function_state = new SynthFunctionState(); + function_state->fdecl_name = instance_to_string_with_nodetype(type_decl, instance); + function_state->source = instance; + function_state->source_phy_graph = phylum_graph; + function_state->is_phylum_instance = false; + function_state->is_side_effect_evaluation = ATTR_DECL_IS_SHARED_INFO(instance->fibered_attr.attr); + function_state->regular_dependencies = collect_aug_graph_attr_dependencies(aug_graph, instance); + function_state->aug_graphs.push_back(aug_graph); + result.push_back(function_state); + } + } + + return result; +} + +void destroy_synth_function_states(const std::vector& states) { + for (auto iterator = states.begin(); iterator != states.end(); ++iterator) { + delete *iterator; + } +} + +void implement_value_use(Declaration declaration, AUG_GRAPH* graph, const std::vector& states, SynthImplementation* implementation, std::ostream& output) { + int flags = Declaration_info(declaration)->decl_flags; + if (flags & LOCAL_ATTRIBUTE_FLAG) { + int instance_index = Declaration_info(declaration)->instance_index; + INSTANCE* instance = &graph->instances.array[instance_index]; + Declaration type_decl = canonical_type_decl(canonical_type(value_decl_type(declaration))); + std::string target_name = instance_to_string_with_nodetype(type_decl, instance); + + output << "eval_" << target_name << "(\n"; + int saved_nesting = nesting_level; + nesting_level = std::max(nesting_level + 2, 1); + output << indent() << "node"; + + for (auto state_iterator = states.begin(); state_iterator != states.end(); ++state_iterator) { + SynthFunctionState* state = *state_iterator; + if (state->fdecl_name != target_name) { + continue; + } + for (auto dependency_iterator = state->regular_dependencies.begin(); dependency_iterator != state->regular_dependencies.end(); ++dependency_iterator) { + INSTANCE* source_instance = *dependency_iterator; + if (!should_skip_synth_dependency(source_instance)) { + output << ",\n" << indent(); + implementation->dump_synth_instance(source_instance, output); + } + } + break; + } + + nesting_level = saved_nesting; + output << "\n" << indent() << ")"; + } else if (flags & ATTRIBUTE_DECL_FLAG) { + if (ATTR_DECL_IS_INH(declaration)) { + output << "v_" << decl_name(declaration); + } else { + output << "a_" << decl_name(declaration) << ".get"; + } + } else if (flags & LOCAL_VALUE_FLAG) { + output << "v" << LOCAL_UNIQUE_PREFIX(declaration) << "_" << decl_name(declaration); + } else { + aps_error(declaration, "internal_error: What is special about this?"); + } +} + +bool try_dump_funcall(Expression expression, AUG_GRAPH* graph, SynthImplementation* implementation, std::ostream& output) { + Expression function = funcall_f(expression); + if (Expression_KEY(function) == KEYvalue_use) { + Declaration used_declaration = USE_DECL(value_use_use(function)); + if (Declaration_KEY(used_declaration) == KEYconstructor_decl) { + Type constructor_type = constructor_decl_type(used_declaration); + Declaration return_declaration = first_Declaration(function_type_return_values(constructor_type)); + Type return_type = value_decl_type(return_declaration); + Declaration type_decl = USE_DECL(type_use_use(return_type)); + if (Declaration_KEY(type_decl) == KEYphylum_decl) { + Declaration top_level_match; + bool inside_match = check_surrounding_decl(expression, KEYtop_level_match, &top_level_match); + dump_Expression(function, output); + output << "("; + bool first = true; + FOR_SEQUENCE(Expression, argument, Actuals, funcall_actuals(expression), if (first) first = false; else output << ",\n"; dump_Expression(argument, output)); + if (first_Actual(funcall_actuals(expression)) != NULL) { + output << ", "; + } + output << (inside_match ? "node" : "null") << ")"; + return true; + } + } + } + + Declaration attribute = attr_ref_p(expression); + if (attribute == NULL) { + return false; + } + Declaration node = USE_DECL(value_use_use(first_Actual(funcall_actuals(expression)))); + FIBERED_ATTRIBUTE fibered_attribute = {attribute, NULL}; + INSTANCE* instance; + if (find_instance(graph, node, fibered_attribute, &instance)) { + implementation->dump_synth_instance(instance, output); + return true; + } + fatal_error("failed to find instance"); + return false; +} + +void dump_attribute_type(INSTANCE* instance, std::ostream& output) { + CanonicalType* type = canonical_type(infer_some_value_decl_type(instance->fibered_attr.attr)); + switch (type->key) { + case KEY_CANONICAL_USE: + output << "T_" << decl_name(canonical_type_decl(type)); + break; + case KEY_CANONICAL_FUNC: { + struct Canonical_function_type* function_type = (struct Canonical_function_type*)type; + output << "T_" << decl_name(canonical_type_decl(function_type->return_type)); + break; + } + default: + break; + } +} + +bool synth_function_is_circular(SynthFunctionState* state) { + if (state->is_side_effect_evaluation || instance_is_pure_shared_info(state->source)) { + return false; + } + for (auto iterator = state->aug_graphs.begin(); iterator != state->aug_graphs.end(); ++iterator) { + AUG_GRAPH* graph = *iterator; + INSTANCE* instance = NULL; + if (state->is_phylum_instance) { + if (!find_instance(graph, graph->lhs_decl, state->source->fibered_attr, &instance)) { + continue; + } + } else { + instance = state->source; + } + if (instance_circular(instance)) { + return true; + } + } + return false; +} + +static bool instance_is_child(INSTANCE* instance, AUG_GRAPH* graph) { return instance->node != NULL && instance->node != graph->lhs_decl; } + +bool instance_is_parent(INSTANCE* instance, AUG_GRAPH* graph) { return instance->node != NULL && instance->node == graph->lhs_decl; } + +static bool component_precedes(AUG_GRAPH* graph, SCC_COMPONENT* source, SCC_COMPONENT* sink) { + int instance_count = graph->instances.length; + for (int source_index = 0; source_index < source->length; ++source_index) { + INSTANCE* source_instance = static_cast(source->array[source_index]); + for (int sink_index = 0; sink_index < sink->length; ++sink_index) { + INSTANCE* sink_instance = static_cast(sink->array[sink_index]); + if (!MERGED_CONDITION_IS_IMPOSSIBLE(instance_condition(source_instance), instance_condition(sink_instance)) && edgeset_kind(graph->graph[source_instance->index * instance_count + sink_instance->index])) { + return true; + } + } + } + return false; +} + +static bool component_is_relevant(AUG_GRAPH* graph, SCC_COMPONENT* component, INSTANCE* sink) { + int instance_count = graph->instances.length; + for (int index = 0; index < component->length; ++index) { + INSTANCE* instance = static_cast(component->array[index]); + if (instance == sink || edgeset_kind(graph->graph[instance->index * instance_count + sink->index])) { + return true; + } + } + return false; +} + +std::vector> collect_child_cycle_components(AUG_GRAPH* graph, INSTANCE* sink) { + std::vector> result; + if (graph->components == NULL) { + set_aug_graph_components(graph); + } + int component_count = graph->components->length; + std::vector scheduled(component_count, false); + + for (int scheduled_count = 0; scheduled_count < component_count;) { + bool found_ready = false; + for (int component_index = 0; component_index < component_count; ++component_index) { + if (scheduled[component_index]) { + continue; + } + + SCC_COMPONENT* component = graph->components->array[component_index]; + bool ready = true; + for (int predecessor_index = 0; predecessor_index < component_count && ready; ++predecessor_index) { + if (predecessor_index != component_index && !scheduled[predecessor_index] && component_precedes(graph, graph->components->array[predecessor_index], component)) { + ready = false; + } + } + if (!ready) { + continue; + } + + scheduled[component_index] = true; + ++scheduled_count; + found_ready = true; + + if (component->length < 2 || !component_is_relevant(graph, component, sink)) { + continue; + } + + std::vector child_instances; + for (int instance_index = 0; instance_index < component->length; ++instance_index) { + INSTANCE* instance = static_cast(component->array[instance_index]); + if (instance_is_child(instance, graph) && instance->fibered_attr.fiber == NULL && !if_rule_p(instance->fibered_attr.attr) && instance_is_synthesized(instance)) { + child_instances.push_back(instance); + } + } + if (!child_instances.empty()) { + result.push_back(child_instances); + } + } + if (!found_ready) { + fatal_error("failed to order circular dependency classes in %s", aug_graph_name(graph)); + } + } + return result; +} + +static Expression default_init(Default value) { + switch (Default_KEY(value)) { + case KEYsimple: + return simple_value(value); + case KEYcomposite: + return composite_initial(value); + default: + return 0; + } +} + +std::vector> make_instance_assignments(AUG_GRAPH* graph, const std::vector& blocks) { + int instance_count = graph->instances.length; + std::vector> assignments(instance_count); + for (int index = 0; index < instance_count; ++index) { + INSTANCE* instance = &graph->instances.array[index]; + Declaration attribute = instance->fibered_attr.attr; + if (attribute != NULL && instance->fibered_attr.fiber == NULL && ABSTRACT_APS_tnode_phylum(attribute) == KEYDeclaration) { + switch (Declaration_KEY(attribute)) { + case KEYattribute_decl: + assignments[instance->index].insert(default_init(attribute_decl_default(attribute))); + break; + case KEYvalue_decl: + assignments[instance->index].insert(default_init(value_decl_default(attribute))); + break; + default: + break; + } + } + } + + for (auto block_iterator = blocks.begin(); block_iterator != blocks.end(); ++block_iterator) { + bool is_outermost = block_iterator == blocks.begin(); + std::vector> scoped_assignments(assignments); + for (int step = 1; step <= 2; ++step) { + Declarations declarations = block_body(*block_iterator); + for (Declaration declaration = first_Declaration(declarations); declaration; declaration = DECL_NEXT(declaration)) { + switch (Declaration_KEY(declaration)) { + case KEYnormal_assign: { + INSTANCE* instance = Expression_info(assign_rhs(declaration))->value_for; + if (instance != NULL) { + if (instance->index >= instance_count) { + fatal_error("bad index [normal_assign] for instance"); + } + scoped_assignments[instance->index].clear(); + if (assign_rhs(declaration) == NULL) { + printf("Warning: assignment to %s is empty\n", instance_to_string(instance).c_str()); + } + scoped_assignments[instance->index].insert(assign_rhs(declaration)); + } + break; + } + case KEYcollect_assign: { + INSTANCE* instance = Expression_info(assign_rhs(declaration))->value_for; + if (instance != NULL) { + if (instance->index >= instance_count) { + fatal_error("bad index [collection_assign] for instance"); + } + if (step == 1 && is_outermost) { + scoped_assignments[instance->index].clear(); + } else if (step == 2) { + scoped_assignments[instance->index].insert(assign_rhs(declaration)); + } + } + break; + } + default: + break; + } + } + } + assignments = scoped_assignments; + } + return assignments; +} + +static std::vector sort_instances(AUG_GRAPH* graph) { + std::vector result; + int instance_count = graph->instances.length; + + for (int index = 0; index < instance_count; ++index) { + INSTANCE* instance = &graph->instances.array[index]; + if (!if_rule_p(instance->fibered_attr.attr)) { + result.push_back(instance); + } + } + for (int index = 0; index < instance_count; ++index) { + INSTANCE* instance = &graph->instances.array[index]; + if (if_rule_p(instance->fibered_attr.attr)) { + result.push_back(instance); + } + } + + return result; +} + +static BlockItem* linearize_block_helper(AUG_GRAPH* graph, const std::vector& sorted_instances, bool* scheduled, CONDITION* condition, BlockItem* previous, int remaining, INSTANCE* sink) { + if (CONDITION_IS_IMPOSSIBLE(*condition)) { + return NULL; + } + + int instance_count = graph->instances.length; + for (auto iterator = sorted_instances.begin(); iterator != sorted_instances.end(); ++iterator) { + INSTANCE* instance = *iterator; + int index = instance->index; + + if (scheduled[index]) { + continue; + } + if (MERGED_CONDITION_IS_IMPOSSIBLE(*condition, instance_condition(instance))) { + scheduled[index] = true; + BlockItem* result = linearize_block_helper(graph, sorted_instances, scheduled, condition, previous, remaining - 1, sink); + scheduled[index] = false; + return result; + } + if (sink != instance && !edgeset_kind(graph->graph[index * instance_count + sink->index])) { + scheduled[index] = true; + BlockItem* result = linearize_block_helper(graph, sorted_instances, scheduled, condition, previous, remaining - 1, sink); + scheduled[index] = false; + return result; + } + + bool ready = true; + for (int dependency_index = 0; dependency_index < instance_count && ready; ++dependency_index) { + INSTANCE* predecessor = &graph->instances.array[dependency_index]; + if (scheduled[dependency_index] || MERGED_CONDITION_IS_IMPOSSIBLE(instance_condition(instance), instance_condition(predecessor)) || !(edgeset_kind(graph->graph[dependency_index * instance_count + index]) & DEPENDENCY_MAYBE_DIRECT)) { + continue; + } + ready = false; + } + if (!ready) { + continue; + } + + scheduled[index] = true; + BlockItem* item; + if (if_rule_p(instance->fibered_attr.attr)) { + BlockItemCondition* conditional = static_cast(std::malloc(sizeof(BlockItemCondition))); + item = reinterpret_cast(conditional); + conditional->key = KEY_BLOCK_ITEM_CONDITION; + conditional->instance = instance; + conditional->condition = instance->fibered_attr.attr; + conditional->prev = previous; + + int condition_mask = 1 << if_rule_index(instance->fibered_attr.attr); + condition->positive |= condition_mask; + conditional->next_positive = linearize_block_helper(graph, sorted_instances, scheduled, condition, item, remaining - 1, sink); + condition->positive &= ~condition_mask; + condition->negative |= condition_mask; + conditional->next_negative = linearize_block_helper(graph, sorted_instances, scheduled, condition, item, remaining - 1, sink); + condition->negative &= ~condition_mask; + } else { + BlockItemInstance* linear = static_cast(std::malloc(sizeof(BlockItemInstance))); + item = reinterpret_cast(linear); + linear->key = KEY_BLOCK_ITEM_INSTANCE; + linear->instance = instance; + linear->prev = previous; + linear->next = linearize_block_helper(graph, sorted_instances, scheduled, condition, item, remaining - 1, sink); + } + scheduled[index] = false; + return item; + } + + if (remaining != 0) { + fatal_error("failed to schedule some instances, remaining: %d", remaining); + } + return NULL; +} + +BlockItem* linearize_block(AUG_GRAPH* graph, INSTANCE* sink) { + int instance_count = graph->instances.length; + bool* scheduled = static_cast(alloca(sizeof(bool) * instance_count)); + std::memset(scheduled, 0, sizeof(bool) * instance_count); + + CONDITION condition = {0, 0}; + std::vector sorted_instances = sort_instances(graph); + return linearize_block_helper(graph, sorted_instances, scheduled, &condition, NULL, instance_count, sink); +} + +void print_linearized_block(BlockItem* block, std::ostream& output) { + if (block == NULL) { + return; + } + + output << indent() << block->instance << "\n"; + if (block->key == KEY_BLOCK_ITEM_CONDITION) { + BlockItemCondition* condition = reinterpret_cast(block); + if (condition->prev != NULL && condition->prev->key != KEY_BLOCK_ITEM_CONDITION) { + output << indent() << condition->prev->instance << "\n"; + } + + output << indent() << "IF\n"; + ++nesting_level; + print_linearized_block(condition->next_positive, output); + --nesting_level; + output << indent() << "ELSE\n"; + ++nesting_level; + print_linearized_block(condition->next_negative, output); + --nesting_level; + } else { + print_linearized_block(reinterpret_cast(block)->next, output); + } +} + +BlockItem* find_surrounding_block(BlockItem* block, INSTANCE* instance) { + while (block != NULL) { + if (block->key == KEY_BLOCK_ITEM_CONDITION) { + return block; + } + if (block->instance == instance) { + return block; + } + block = reinterpret_cast(block)->next; + } + return NULL; +} + +} // namespace synth_util \ No newline at end of file diff --git a/codegen/synth-util.h b/codegen/synth-util.h new file mode 100644 index 00000000..e5c2a1de --- /dev/null +++ b/codegen/synth-util.h @@ -0,0 +1,108 @@ +#ifndef SYNTH_UTIL_H +#define SYNTH_UTIL_H + +#include +#include +#include +#include + +extern "C" { +#include + +#include "aps-ag.h" +} + +class SynthImplementation; + +namespace synth_util { +extern const std::string LOOP_VAR; +extern const std::string RESULT_VAR_PREFIX; +constexpr int LOCAL_VALUE_FLAG = 1 << 28; + +void emit_loop_implicits(std::ostream& output, const std::string& flag); + +void emit_fixed_point_loop_start(std::ostream& output, const std::string& flag); + +void emit_fixed_point_loop_end(std::ostream& output); + +struct SynthFunctionState { + std::string fdecl_name; + INSTANCE* source; + PHY_GRAPH* source_phy_graph; + std::vector regular_dependencies; + std::vector aug_graphs; + bool is_phylum_instance; + bool is_side_effect_evaluation; +}; + +enum BlockItemKind { KEY_BLOCK_ITEM_CONDITION, KEY_BLOCK_ITEM_INSTANCE }; + +struct BlockItem { + BlockItemKind key; + INSTANCE* instance; + BlockItem* prev; +}; + +struct BlockItemCondition { + BlockItemKind key; + INSTANCE* instance; + BlockItem* prev; + Declaration condition; + BlockItem* next_positive; + BlockItem* next_negative; +}; + +struct BlockItemInstance { + BlockItemKind key; + INSTANCE* instance; + BlockItem* prev; + BlockItem* next; +}; + +bool state_uses_fibers(STATE* state); + +bool instance_is_synthesized(INSTANCE* instance); + +bool instance_is_inherited(INSTANCE* instance); + +bool instance_is_pure_shared_info(INSTANCE* instance); + +bool instance_is_parent(INSTANCE* instance, AUG_GRAPH* graph); + +std::string instance_to_string(INSTANCE* instance, bool trim_node = false); + +std::string instance_to_string_with_nodetype(Declaration node_type, INSTANCE* instance); + +std::string instance_to_attr(INSTANCE* instance); + +bool is_match_formal(void* node); + +bool should_skip_synth_dependency(INSTANCE* instance); + +bool find_instance(AUG_GRAPH* graph, Declaration node, const FIBERED_ATTRIBUTE& attribute, INSTANCE** result); + +std::vector build_synth_function_states(STATE* state); + +void destroy_synth_function_states(const std::vector& states); + +void implement_value_use(Declaration declaration, AUG_GRAPH* graph, const std::vector& states, SynthImplementation* implementation, std::ostream& output); + +bool try_dump_funcall(Expression expression, AUG_GRAPH* graph, SynthImplementation* implementation, std::ostream& output); + +void dump_attribute_type(INSTANCE* instance, std::ostream& output); + +bool synth_function_is_circular(SynthFunctionState* state); + +std::vector> collect_child_cycle_components(AUG_GRAPH* graph, INSTANCE* sink); + +std::vector> make_instance_assignments(AUG_GRAPH* graph, const std::vector& blocks); + +BlockItem* linearize_block(AUG_GRAPH* graph, INSTANCE* sink); + +void print_linearized_block(BlockItem* block, std::ostream& output); + +BlockItem* find_surrounding_block(BlockItem* block, INSTANCE* instance); + +} // namespace synth_util + +#endif \ No newline at end of file diff --git a/examples/scala/Makefile b/examples/scala/Makefile index 638ea7d0..e3ac97f3 100644 --- a/examples/scala/Makefile +++ b/examples/scala/Makefile @@ -17,6 +17,8 @@ EVALUATOR ?= DYNAMIC ifeq (${EVALUATOR},STATIC) IMPL_SUFFIX = .static.scala +else ifeq (${EVALUATOR},SYNTH_F0) +IMPL_SUFFIX = .synth-f0.scala else IMPL_SUFFIX = .scala endif @@ -76,6 +78,10 @@ phony_explicit: ${APS2SCALA} ${APS2SCALAFLAGS} -S -C $* mv $*.scala $@ +%.synth-f0.scala : ../%.aps ${APS2SCALA} + ${APS2SCALA} ${APS2SCALAFLAGS} -F0 $* + mv $*.scala $@ + %.scala : ../%.aps ${APS2SCALA} ${APS2SCALA} ${APS2SCALAFLAGS} $* @@ -373,5 +379,5 @@ ${OUT}/SimpleSncDriver.class: ${OUT}/simple_snc_implicit.class ${OUT}/SimplePars clean: rm -rf ${OUT_BASE} - rm -f *.class ${SCALAGEN} ${MISCGEN} + rm -f *.class ${SCALAGEN} ${MISCGEN} *.synth-f0.scala diff --git a/parse/aps-util.c b/parse/aps-util.c index c9a8c059..6af8527e 100644 --- a/parse/aps-util.c +++ b/parse/aps-util.c @@ -216,6 +216,15 @@ Type some_value_decl_type(Declaration _node) { } } +Direction some_value_decl_direction(Declaration _node) { + switch (Declaration_KEY(_node)) { + default: fatal_error("some_value_decl_direction: called with %s", + Declaration_constructors[Declaration_KEY(_node)]); + case KEYvalue_decl: return value_decl_direction(_node); + case KEYattribute_decl: return attribute_decl_direction(_node); + } +} + Expression assign_lhs(Declaration _node) { switch (Declaration_KEY(_node)) { default: fatal_error("assign_lhs: called with %s", diff --git a/parse/aps-util.h b/parse/aps-util.h index c9b22239..8b6edcf0 100644 --- a/parse/aps-util.h +++ b/parse/aps-util.h @@ -76,6 +76,7 @@ extern Type some_type_decl_type(Declaration); case KEYconstructor_decl: \ case KEYformal extern Type some_value_decl_type(Declaration); +extern Direction some_value_decl_direction(Declaration); #define KEYassign KEYnormal_assign: case KEYcollect_assign extern Expression assign_lhs(Declaration); From d2406be65a6f68be39cadd6889cfd57d0de8d545 Mon Sep 17 00:00:00 2001 From: Amir Hesamian Date: Tue, 1 Sep 2026 17:24:32 +0000 Subject: [PATCH 2/7] compare the result of original farrow as well --- examples/scala/compare-evaluators.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/scala/compare-evaluators.sh b/examples/scala/compare-evaluators.sh index 739277b3..b4b8be7a 100755 --- a/examples/scala/compare-evaluators.sh +++ b/examples/scala/compare-evaluators.sh @@ -4,6 +4,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" cd "$SCRIPT_DIR" DEFAULT_EVALUATORS="DYNAMIC,STATIC" +ORIGINAL_FARROW_EVALUATOR="SYNTH_F0" extract_results() { sed -n '/^Results:$/,$p' @@ -104,9 +105,9 @@ TESTS=( "TestUseCollDriver|tiny.program|$DEFAULT_EVALUATORS" "TestCycleDriver|tiny.program|$DEFAULT_EVALUATORS" "UseGlobal|tiny.program|$DEFAULT_EVALUATORS" - "FarrowUbdDriver|farrow-ubd.program|$DEFAULT_EVALUATORS" + "FarrowUbdDriver|farrow-ubd.program|$DEFAULT_EVALUATORS,$ORIGINAL_FARROW_EVALUATOR" "FarrowUbdFiberDriver|farrow-ubd.program|$DEFAULT_EVALUATORS" - "NestedUbdDriver|nested-ubd.program|$DEFAULT_EVALUATORS" + "NestedUbdDriver|nested-ubd.program|$DEFAULT_EVALUATORS,$ORIGINAL_FARROW_EVALUATOR" "NestedUbdFiberDriver|nested-ubd.program|$DEFAULT_EVALUATORS" "TestFieldsDriver|tiny.program|$DEFAULT_EVALUATORS" "FirstDriver|grammar.cfg|$DEFAULT_EVALUATORS" From 21867cf19e23fd5de20d738e6c8752c3f1135310 Mon Sep 17 00:00:00 2001 From: Amir Hesamian Date: Tue, 1 Sep 2026 17:45:54 +0000 Subject: [PATCH 3/7] More fixes --- aps2scala/dump-scala.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aps2scala/dump-scala.cc b/aps2scala/dump-scala.cc index 2ccc49f3..d2030b55 100644 --- a/aps2scala/dump-scala.cc +++ b/aps2scala/dump-scala.cc @@ -1681,7 +1681,7 @@ void dump_scala_Declaration(Declaration decl,ostream& oss) ++nesting_level; STATE *s = (STATE*)Declaration_info(decl)->analysis_state; - if ((static_scc_schedule || dynamic_cast(impl) != NULL) && s != NULL) + if ((static_scc_schedule || anc_analysis) && s != NULL) { activate_static_circular = s->loop_required; } @@ -2696,7 +2696,7 @@ string operator+(string s, int i) string indent(int nl) { return string(indent_multiple*nl,' '); } bool check_surrounding_decl(void* node, KEYTYPE_Declaration decl_key, Declaration* result_decl) { - while (node != NULL) { + while (node != NULL && ABSTRACT_APS_tnode_phylum(node) != KEY_ABSTRACT_APS_None) { if (ABSTRACT_APS_tnode_phylum(node) == KEYDeclaration) { Declaration decl = (Declaration)node; if (Declaration_KEY(decl) == decl_key) { From c7e563d21db2afc984c2684f99aed188c9fc3c83 Mon Sep 17 00:00:00 2001 From: Amir Hesamian Date: Wed, 2 Sep 2026 15:39:43 +0000 Subject: [PATCH 4/7] Simple-SNC include in compare script --- examples/scala/compare-evaluators.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/scala/compare-evaluators.sh b/examples/scala/compare-evaluators.sh index 60319a50..74005b65 100755 --- a/examples/scala/compare-evaluators.sh +++ b/examples/scala/compare-evaluators.sh @@ -113,7 +113,7 @@ TESTS=( "FirstDriver|grammar.cfg|$DEFAULT_EVALUATORS" "FollowDriver|grammar.cfg|$DEFAULT_EVALUATORS" "NullableDriver|grammar.cfg|$DEFAULT_EVALUATORS" - "SimpleSncDriver|simple.program|DYNAMIC" + "SimpleSncDriver|simple.program|$DEFAULT_EVALUATORS,$ORIGINAL_FARROW_EVALUATOR" ) failures=0 From 36209bdbfc8b1c7d0f432c5f007ded07165a0adc Mon Sep 17 00:00:00 2001 From: Amir Hesamian Date: Wed, 2 Sep 2026 16:08:44 +0000 Subject: [PATCH 5/7] Another try --- examples/scala/compare-evaluators.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/scala/compare-evaluators.sh b/examples/scala/compare-evaluators.sh index 74005b65..580b6e9d 100755 --- a/examples/scala/compare-evaluators.sh +++ b/examples/scala/compare-evaluators.sh @@ -3,8 +3,10 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" cd "$SCRIPT_DIR" -DEFAULT_EVALUATORS="DYNAMIC,STATIC" +DYNAMIC_EVALUATOR="DYNAMIC" +STATIC_EVALUATOR="STATIC" ORIGINAL_FARROW_EVALUATOR="SYNTH_F0" +DEFAULT_EVALUATORS="$DYNAMIC_EVALUATOR,$STATIC_EVALUATOR" extract_results() { sed -n '/^Results:$/,$p' @@ -113,7 +115,7 @@ TESTS=( "FirstDriver|grammar.cfg|$DEFAULT_EVALUATORS" "FollowDriver|grammar.cfg|$DEFAULT_EVALUATORS" "NullableDriver|grammar.cfg|$DEFAULT_EVALUATORS" - "SimpleSncDriver|simple.program|$DEFAULT_EVALUATORS,$ORIGINAL_FARROW_EVALUATOR" + "SimpleSncDriver|simple.program|$DYNAMIC_EVALUATOR,$ORIGINAL_FARROW_EVALUATOR" ) failures=0 From 3cdedb4f2c94905cc630b048c529920e06b1543c Mon Sep 17 00:00:00 2001 From: Amir Hesamian Date: Thu, 3 Sep 2026 02:07:52 +0000 Subject: [PATCH 6/7] Removed unnessary comments --- codegen/farrow-impl.cc | 41 ++--------------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/codegen/farrow-impl.cc b/codegen/farrow-impl.cc index 837cda55..10af43cf 100644 --- a/codegen/farrow-impl.cc +++ b/codegen/farrow-impl.cc @@ -62,8 +62,6 @@ static void emit_start_phylum_evaluations(ostream& os, STATE* state) { static void dump_farrow_functions(STATE* s, ostream& os) { ostream& oss = os; - // first dump all visit functions for each phylum: - os << "\n"; synth_functions_states = synth_util::build_synth_function_states(s); @@ -73,7 +71,6 @@ static void dump_farrow_functions(STATE* s, ostream& os) { synth_util::SynthFunctionState* synth_functions_state = *state_it; current_synth_functions_state = synth_functions_state; - // result var has instance index suffix so each function has unique name string result_var = synth_util::RESULT_VAR_PREFIX + std::to_string(synth_functions_state->source->index); if (include_comments) { @@ -94,9 +91,6 @@ static void dump_farrow_functions(STATE* s, ostream& os) { continue; } - // for locals, it needs prefix in formals, not for fibers or regular - // attributes - os << ",\n"; os << indent(nesting_level + 1); os << "v_"; @@ -126,7 +120,6 @@ static void dump_farrow_functions(STATE* s, ostream& os) { os << " = {\n"; nesting_level++; - // don't cache if we are in the loop. if (needs_fixed_point) { os << indent() << "if (!" << synth_util::LOOP_VAR << ") {\n"; nesting_level++; @@ -170,9 +163,6 @@ static void dump_farrow_functions(STATE* s, ostream& os) { } nesting_level++; - // True if this attribute is circular anywhere. Then every assignment uses - // the change-tracking overload so the closure-site loop can see it settle; - // only the closure site emits the do/while. bool source_circular = needs_fixed_point && synth_util::synth_function_is_circular(synth_functions_state); for (auto it = synth_functions_state->aug_graphs.begin(); it != synth_functions_state->aug_graphs.end(); it++) { @@ -194,9 +184,6 @@ static void dump_farrow_functions(STATE* s, ostream& os) { aug_graph_instance = synth_functions_state->source; } - // Linearize the current scope block but make sure IF statements or - // conditional instances that have nothing to do with this instance don't - // appear in linearization current_scope_block = synth_util::linearize_block(aug_graph, aug_graph_instance); if (include_comments) { @@ -215,8 +202,6 @@ static void dump_farrow_functions(STATE* s, ostream& os) { aps_warning(aug_graph_instance->node, "Instance %s depends on itself but is not declared circular", synth_util::instance_to_string(aug_graph_instance).c_str()); } - // Non-fiber cycles are evaluated one circular dependency class at a time. - // converge child cycles first, so the value below sees stable results if (!synth_functions_state->is_side_effect_evaluation) { std::vector> child_cycle_components = synth_util::collect_child_cycle_components(aug_graph, aug_graph_instance); for (size_t component_index = 0; component_index < child_cycle_components.size(); ++component_index) { @@ -246,7 +231,6 @@ static void dump_farrow_functions(STATE* s, ostream& os) { dumped_instances.clear(); } - // Non-fiber instance computation if (!synth_functions_state->is_side_effect_evaluation) { os << indent(); farrow_impl_ptr->dump_synth_instance(aug_graph_instance, os); @@ -318,11 +302,8 @@ class FarrowImpl : public SynthImplementation { dump_farrow_functions(s, oss); - // same flag dump_farrow_functions uses to gate the implicit params, so - // finish() brings the matching implicit into scope for the eval calls below bool needs_fixed_point = s->loop_required; - // Implement finish routine: os << indent() << "override def finish() : Unit = {\n"; ++nesting_level; @@ -342,9 +323,7 @@ class FarrowImpl : public SynthImplementation { bool uses_fibers = false; traverse_Program(detect_program_fibers, &uses_fibers, program); if (uses_fibers) { - fatal_error( - "-F0 does not support fibers; Farrow's original algorithm is only " - "defined for non-fiber grammars"); + fatal_error("-F0 does not support fibers"); } } @@ -438,7 +417,6 @@ class FarrowImpl : public SynthImplementation { return; } else if (Declaration_KEY(in->node) == KEYvalue_decl) { if (rhs) { - // assigning field of object o << "a_" << asym << DEREF; if (debug) { o << "assign"; @@ -475,7 +453,6 @@ class FarrowImpl : public SynthImplementation { std::set relevant_assignments = all_assignments[instance->index]; if (!relevant_assignments.empty()) { - // Filter out NULLs first vector valid_rhs; for (auto it = relevant_assignments.begin(); it != relevant_assignments.end(); it++) { if (*it != NULL) { @@ -487,15 +464,12 @@ class FarrowImpl : public SynthImplementation { if (valid_rhs.size() == 1) { dump_Expression(valid_rhs[0], o); } else { - // Multiple RHS entries only occur from collect_assign (:>) - // contributions. Combine them pairwise using the type's v_combine. Declaration attr = instance->fibered_attr.attr; Direction attr_dir = some_value_decl_direction(attr); if (!direction_is_collection(attr_dir)) { fatal_error("Multiple RHS for non-collection attribute %s", decl_name(attr)); } Type vt = Declaration_KEY(attr) == KEYattribute_decl ? function_type_return_type(attribute_decl_type(attr)) : value_decl_type(attr); - // Nest v_combine calls for all contributions for (size_t i = 0; i < valid_rhs.size() - 1; i++) { o << as_val(vt) << ".v_combine("; } @@ -508,18 +482,11 @@ class FarrowImpl : public SynthImplementation { } return; } - // valid_rhs is empty (all NULL defaults) -- fall through to default - // handling } - // A local collection attribute may be assigned only inside a case/match - // block, which make_instance_assignment() doesn't see at this level. Declaration attr = instance->fibered_attr.attr; bool is_local_collection = direction_is_collection(some_value_decl_direction(attr)); if (is_local_collection) { - // Check the type is combinable: look for a "combine" declaration in any - // class in its canonical signature set. This covers COMBINABLE, - // MAKE_LATTICE, etc. without walking parent signature chains. Type vt = infer_some_value_decl_type(attr); CanonicalType* ctype = canonical_type(vt); CanonicalSignatureSet csig_set = infer_canonical_signatures(ctype); @@ -535,9 +502,6 @@ class FarrowImpl : public SynthImplementation { } } if (is_combinable) { - // No direct assignment in this block (it's inside a - // conditional/case), so this branch contributes the type's - // initial/bottom value. o << as_val(vt) << ".v_initial"; if (include_comments) { o << " /* local collection " << decl_name(attr) << ": no direct assignment, using initial */"; @@ -615,7 +579,6 @@ class FarrowImpl : public SynthImplementation { Match m = (Match)cond->condition; Pattern p = matcher_pat(m); Declaration header = Match_info(m)->header; - // if first match in case, we evaluate variable: if (m == first_Match(case_stmt_matchers(header))) { Expression e = case_stmt_expr(header); o << "{\n"; @@ -630,7 +593,7 @@ class FarrowImpl : public SynthImplementation { Block if_false; if_true = matcher_body(m); if (MATCH_NEXT(m)) { - if_false = 0; //? Why not the nxt match ? + if_false = 0; } else { if_false = case_stmt_default(header); } From dfdefbbe431cafcb21dd03c30d9aaa0557546a60 Mon Sep 17 00:00:00 2001 From: Seyedamirhossein Hesamian Date: Thu, 3 Sep 2026 14:38:09 -0500 Subject: [PATCH 7/7] Refactor implementation selection logic for clarity --- aps2scala/aps2scala.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/aps2scala/aps2scala.cc b/aps2scala/aps2scala.cc index 345295bf..a6bcefe6 100644 --- a/aps2scala/aps2scala.cc +++ b/aps2scala/aps2scala.cc @@ -117,8 +117,13 @@ int main(int argc,char **argv) { traverse_Program(program_is_tree_only, p, p); aps_check_error("type"); if (static_schedule || farrow_implementation) { - impl = farrow_implementation ? farrow_impl - : (static_scc_schedule ? static_scc_impl : static_impl); + if (farrow_implementation) { + impl = farrow_impl; + } else if (static_scc_schedule) { + impl = static_scc_impl; + } else { + impl = static_impl; + } analyze_Program(p); aps_check_error("analysis"); if (!impl) {