diff --git a/aps2scala/Makefile b/aps2scala/Makefile index 1dbff83d..7512f34d 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 farrow-impl.o synth-util.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 synth-impl.o APS2SCALALIBS = ../lib/aps-lib.o ../lib/aps-ag.a ../utilities/utilities.o aps2scala : ${APS2SCALAOBJS} ${APS2SCALALIBS} ${CPP} ${CPPFLAGS} ${APS2SCALAOBJS} ${APS2SCALALIBS} -o aps2scala @@ -11,6 +11,9 @@ ${APS2SCALAOBJS} : dump-scala.h install: aps2scala mv aps2scala ../bin/. +synth-impl.o : ../codegen/synth-impl.cc ../codegen/synth-util.h + ${CPP} -c ${CPPFLAGS} $< -o $@ + static-impl.o : ../codegen/static-impl.cc ${CPP} -c ${CPPFLAGS} $< -o $@ diff --git a/aps2scala/aps2scala.cc b/aps2scala/aps2scala.cc index a6bcefe6..40c8bec7 100644 --- a/aps2scala/aps2scala.cc +++ b/aps2scala/aps2scala.cc @@ -5,6 +5,7 @@ extern "C" { #include #include #include "aps-ag.h" +#include "aps-analyze.h" } #include #include @@ -29,7 +30,8 @@ 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," -A, --synth optimized SYNTH evaluation\n"); + fprintf(stderr," -F, --synth-pure-farrow generate original Farrow SYNTH evaluation\n"); fprintf(stderr," -p path set the APSPATH (overriding env. variable)\n"); exit(1); } @@ -41,6 +43,7 @@ extern int aps_yyparse(void); Implementation* impl; bool static_schedule = false; bool is_tree_only_program = false; +bool synth_implementation = false; bool farrow_implementation = false; static void* program_is_tree_only(void *scope, void *node) { @@ -84,7 +87,11 @@ 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")) { + } else if (streq(argv[i],"-A") || streq(argv[i],"--synth")) { + synth_implementation = true; + anc_analysis = true; + continue; + } else if (streq(argv[i],"-F") || streq(argv[i],"--farrow")) { farrow_implementation = true; anc_analysis = true; continue; @@ -116,14 +123,16 @@ int main(int argc,char **argv) { type_Program(p); traverse_Program(program_is_tree_only, p, p); aps_check_error("type"); - if (static_schedule || farrow_implementation) { - if (farrow_implementation) { - impl = farrow_impl; - } else if (static_scc_schedule) { - impl = static_scc_impl; - } else { - impl = static_impl; - } + if (static_schedule || static_scc_schedule || farrow_implementation || synth_implementation) { + if (synth_implementation) { + impl = synth_impl; + } else 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) { diff --git a/aps2scala/dump-scala.cc b/aps2scala/dump-scala.cc index d2030b55..d148f886 100644 --- a/aps2scala/dump-scala.cc +++ b/aps2scala/dump-scala.cc @@ -11,6 +11,7 @@ extern "C" { #include #include "string.h" #include "aps-ag.h" +#include "aps-analyze.h" String get_code_name(Symbol); } diff --git a/codegen/farrow-impl.cc b/codegen/farrow-impl.cc index 10af43cf..acfbf72b 100644 --- a/codegen/farrow-impl.cc +++ b/codegen/farrow-impl.cc @@ -64,7 +64,7 @@ static void dump_farrow_functions(STATE* s, ostream& os) { ostream& oss = os; os << "\n"; - synth_functions_states = synth_util::build_synth_function_states(s); + synth_functions_states = synth_util::build_synth_function_states(s, false); bool needs_fixed_point = s->loop_required; for (auto state_it = synth_functions_states.begin(); state_it != synth_functions_states.end(); state_it++) { diff --git a/codegen/synth-impl.cc b/codegen/synth-impl.cc new file mode 100644 index 00000000..21648b18 --- /dev/null +++ b/codegen/synth-impl.cc @@ -0,0 +1,1164 @@ +#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; + +#define DEREF "." + +static SynthImplementation* synth_impl_ptr; + +static vector current_blocks; +static synth_util::BlockItem* current_scope_block; +static vector dumped_conditional_block_items; +static vector dumped_instances; +static bool tracking_fiber_convergence = false; + +static const string PREV_LOOP_VAR = "prevIsInsideFixedPoint"; + +class FiberDependencyDumper { + public: + static void dump(AUG_GRAPH* aug_graph, INSTANCE* sink, ostream& os) { + int n = aug_graph->instances.length; + vector relevant_instances; + + for (int i = 0; i < n; i++) { + INSTANCE* in = &aug_graph->instances.array[i]; + if (in->node != NULL && Declaration_KEY(in->node) == KEYpragma_call) { + continue; + } + if (in->index == sink->index) { + continue; + } + if (edgeset_kind(aug_graph->graph[in->index * n + sink->index]) && + in->fibered_attr.fiber != NULL && + (synth_util::instance_is_synthesized(in) || + fibered_attr_direction(&in->fibered_attr) == instance_local || + fiber_is_reverse(in->fibered_attr.fiber))) { + relevant_instances.push_back(in); + } + } + + if (relevant_instances.empty()) { + return; + } + + bool* scheduled = (bool*)alloca(sizeof(bool) * n); + memset(scheduled, 0, sizeof(bool) * n); + + SccGraph scc_graph; + scc_graph_initialize(&scc_graph, static_cast(relevant_instances.size())); + + for (auto it = relevant_instances.begin(); it != relevant_instances.end(); it++) { + scc_graph_add_vertex(&scc_graph, *it); + } + + for (auto it1 = relevant_instances.begin(); it1 != relevant_instances.end(); it1++) { + INSTANCE* in1 = *it1; + for (auto it2 = relevant_instances.begin(); it2 != relevant_instances.end(); it2++) { + INSTANCE* in2 = *it2; + if (in1->index == in2->index) { + continue; + } + if (edgeset_kind(aug_graph->graph[in1->index * n + in2->index])) { + scc_graph_add_edge(&scc_graph, in1, in2); + } + } + } + + SCC_COMPONENTS* components = scc_graph_components(&scc_graph); + + dump_scc_helper(aug_graph, components, scheduled, os); + + scc_graph_destroy(&scc_graph); + } + + private: + static bool already_scheduled(SCC_COMPONENT* component, bool* scheduled) { + for (int i = 0; i < component->length; i++) { + INSTANCE* in = (INSTANCE*)component->array[i]; + if (!scheduled[in->index]) { + return false; + } + } + return true; + } + + static SCC_COMPONENT* find_next_ready_component(AUG_GRAPH* aug_graph, SCC_COMPONENTS* components, bool* scheduled) { + int n = aug_graph->instances.length; + + for (int i = 0; i < components->length; i++) { + SCC_COMPONENT* component = components->array[i]; + if (already_scheduled(component, scheduled)) { + continue; + } + + bool component_ready = true; + for (int j = 0; j < component->length && component_ready; j++) { + INSTANCE* in = (INSTANCE*)component->array[j]; + + for (int k = 0; k < components->length && component_ready; k++) { + SCC_COMPONENT* other_component = components->array[k]; + if (other_component == component || already_scheduled(other_component, scheduled)) { + continue; + } + + for (int l = 0; l < other_component->length; l++) { + INSTANCE* other_in = (INSTANCE*)other_component->array[l]; + if (edgeset_kind(aug_graph->graph[other_in->index * n + in->index])) { + component_ready = false; + break; + } + } + } + } + + if (component_ready) { + return component; + } + } + + fatal_error("no more components to schedule"); + return NULL; + } + + static void dump_component(AUG_GRAPH* aug_graph, SCC_COMPONENT* component, bool* scheduled, ostream& os) { + int n = aug_graph->instances.length; + + if (component->length == 0 || already_scheduled(component, scheduled)) { + return; + } + + bool made_progress = false; + for (int i = 0; i < component->length; i++) { + INSTANCE* in = (INSTANCE*)component->array[i]; + if (scheduled[in->index]) { + continue; + } + + bool dependency_ready = true; + for (int j = 0; j < component->length && dependency_ready; j++) { + INSTANCE* dependency_instance = (INSTANCE*)component->array[j]; + if (dependency_instance == in) { + continue; + } + if (!scheduled[dependency_instance->index] && (edgeset_kind(aug_graph->graph[dependency_instance->index * n + in->index]) & DEPENDENCY_MAYBE_DIRECT)) { + dependency_ready = false; + } + } + if (!dependency_ready) { + continue; + } + + scheduled[in->index] = true; + made_progress = true; + os << indent(); + synth_impl_ptr->dump_synth_instance(in, os); + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + os << ";\n"; + + dump_component(aug_graph, component, scheduled, os); + } + + if (!made_progress) { + for (int i = 0; i < component->length; i++) { + INSTANCE* in = (INSTANCE*)component->array[i]; + if (!scheduled[in->index]) { + scheduled[in->index] = true; + os << indent(); + synth_impl_ptr->dump_synth_instance(in, os); + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + os << ";\n"; + dump_component(aug_graph, component, scheduled, os); + break; + } + } + } + } + + static void dump_scc_helper(AUG_GRAPH* aug_graph, SCC_COMPONENTS* components, bool* scheduled, ostream& os) { + int component_count = components->length; + + for (int i = 0; i < component_count; i++) { + SCC_COMPONENT* component = find_next_ready_component(aug_graph, components, scheduled); + dump_component(aug_graph, component, scheduled, os); + } + + for (int i = 0; i < component_count; i++) { + if (!already_scheduled(components->array[i], scheduled)) { + fatal_error("some instances were not scheduled"); + } + } + } +}; + +static void emit_fixed_point_loop_start(ostream& os, const string& flag, bool is_independent = false, INSTANCE* cycle_instance = NULL) { + if (is_independent) { + if (cycle_instance != NULL && cycle_instance->node != NULL) { + os << indent() << "if (" << synth_util::instance_to_attr(cycle_instance) << ".checkNode(v_" << decl_name(cycle_instance->node) << ").status != Evaluation.ASSIGNED) {\n"; + ++nesting_level; + } + } else { + os << indent() << "if (!" << synth_util::LOOP_VAR << ") {\n"; + ++nesting_level; + } + + synth_util::emit_fixed_point_loop_start(os, flag); +} + +static void emit_fixed_point_loop_end(ostream& os, bool is_independent = false, INSTANCE* cycle_instance = NULL) { + synth_util::emit_fixed_point_loop_end(os); + + if (is_independent) { + if (cycle_instance != NULL && cycle_instance->node != NULL) { + --nesting_level; + os << indent() << "}\n"; + } + } else { + --nesting_level; + os << indent() << "}\n"; + } +} + +static vector synthesized_component_instances(SCC_COMPONENT* component) { + vector result; + for (int i = 0; i < component->length; i++) { + INSTANCE* instance = (INSTANCE*)component->array[i]; + if (synth_util::instance_is_synthesized(instance)) { + result.push_back(instance); + } + } + return result; +} + +static void emit_root_evaluations(ostream& os, Declaration start_phylum, const vector& instances) { + os << indent() << "for (root <- t_" << decl_name(start_phylum) << ".nodes) {\n"; + ++nesting_level; + for (auto instance = instances.begin(); instance != instances.end(); instance++) { + string eval_name = synth_util::instance_to_string_with_nodetype(start_phylum, *instance); + os << indent() << "eval_" << eval_name << "(root);\n"; + } + --nesting_level; + os << indent() << "}\n"; +} + +static void emit_start_phylum_evaluations( + ostream& os, STATE* state, + const vector& function_states) { + PHY_GRAPH* start_graph = summary_graph_for(state, state->start_phylum); + bool needs_fixed_point = state->loop_required; + set_phylum_graph_components(start_graph); + auto is_side_effect = [&function_states](INSTANCE* instance) { + auto function_state = std::find_if( + function_states.begin(), function_states.end(), + [instance](synth_util::SynthFunctionState* candidate) { + return candidate->source == instance; + }); + return function_state != function_states.end() && + (*function_state)->is_side_effect_evaluation; + }; + + if (!needs_fixed_point) { + vector side_effect_instances; + vector value_instances; + for (int instance_index = 0; + instance_index < start_graph->instances.length; instance_index++) { + INSTANCE* instance = &start_graph->instances.array[instance_index]; + if (!synth_util::instance_is_synthesized(instance)) { + continue; + } + if (is_side_effect(instance)) { + side_effect_instances.push_back(instance); + } else { + value_instances.push_back(instance); + } + } + emit_root_evaluations(os, state->start_phylum, side_effect_instances); + emit_root_evaluations(os, state->start_phylum, value_instances); + return; + } + + os << indent() << "implicit val changed: AtomicBoolean = new AtomicBoolean(false);\n"; + os << indent() << "implicit val " << synth_util::LOOP_VAR << ": Boolean = false;\n"; + + for (bool emit_side_effects : {true, false}) { + for (int component_index = start_graph->components->length - 1; + component_index >= 0; component_index--) { + SCC_COMPONENT* component = start_graph->components->array[component_index]; + vector synthesized_instances; + for (INSTANCE* instance : synthesized_component_instances(component)) { + if (is_side_effect(instance) == emit_side_effects) { + synthesized_instances.push_back(instance); + } + } + if (synthesized_instances.empty()) { + continue; + } + + if (start_graph->component_cycle[component_index]) { + os << indent() << "{\n"; + ++nesting_level; + emit_fixed_point_loop_start(os, "componentChanged" + std::to_string(component_index)); + emit_root_evaluations(os, state->start_phylum, synthesized_instances); + emit_fixed_point_loop_end(os); + --nesting_level; + os << indent() << "}\n"; + } else { + emit_root_evaluations(os, state->start_phylum, synthesized_instances); + } + } + } +} + +struct ObjectFieldAssign { + Declaration field; + Expression rhs; + INSTANCE* instance; +}; + +static std::vector collect_object_field_assignments(AUG_GRAPH* aug_graph, Declaration obj_decl) { + std::vector result; + Block body = matcher_body(top_level_match_m(aug_graph->match_rule)); + for (Declaration d = first_Declaration(block_body(body)); d; d = DECL_NEXT(d)) { + if (Declaration_KEY(d) != KEYnormal_assign) { + continue; + } + Expression lhs = assign_lhs(d); + if (Expression_KEY(lhs) != KEYfuncall) { + continue; + } + Declaration field = field_ref_p(lhs); + if (field == NULL) { + continue; + } + Expression obj = field_ref_object(lhs); + if (Expression_KEY(obj) != KEYvalue_use) { + continue; + } + if (USE_DECL(value_use_use(obj)) != obj_decl) { + continue; + } + ObjectFieldAssign fa; + fa.field = field; + fa.rhs = assign_rhs(d); + fa.instance = Expression_info(assign_rhs(d))->value_for; + result.push_back(fa); + } + return result; +} + +static void dump_synth_functions(STATE* s, ostream& os) { + os << "\n"; + + synth_functions_states = synth_util::build_synth_function_states(s, true); + 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; + 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; + } + os << ",\n" << indent(nesting_level + 1) << "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++; + + 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 => 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() << "}\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++; + + 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; + } + + 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"; + } + + int src_idx = synth_functions_state->source->index; + string src_attr = synth_util::instance_to_attr(synth_functions_state->source); + + bool declared_is_circular = instance_circular(aug_graph_instance); + bool depends_on_itself = edgeset_kind(aug_graph->graph[src_idx * n + src_idx]) != 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()); + } + + bool dump_fixed_point_loop = synth_functions_state->is_side_effect_evaluation && declared_is_circular && !synth_util::instance_is_pure_shared_info(synth_functions_state->source); + string node_get = ATTR_DECL_IS_SHARED_INFO(synth_functions_state->source->fibered_attr.attr) ? "" : "node"; + string node_assign = ATTR_DECL_IS_SHARED_INFO(synth_functions_state->source->fibered_attr.attr) ? "" : "node, "; + + if (!synth_functions_state->is_side_effect_evaluation) { + std::vector child_cycle_instances = synth_util::collect_child_cycle_instances(aug_graph); + for (auto it2 = child_cycle_instances.begin(); it2 != child_cycle_instances.end(); it2++) { + INSTANCE* cycle_instance = *it2; + bool independent = synth_util::child_cycle_is_independent(aug_graph, cycle_instance); + INSTANCE* guarded_cycle = independent ? cycle_instance : NULL; + emit_fixed_point_loop_start(os, "localChanged", independent, guarded_cycle); + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + os << indent(); + synth_impl_ptr->dump_synth_instance(cycle_instance, os); + os << ";\n"; + emit_fixed_point_loop_end(os, independent, guarded_cycle); + } + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + } + + if (dump_fixed_point_loop) { + os << indent() << "{\n"; + nesting_level++; + os << indent() << "val " << PREV_LOOP_VAR << src_idx << " = " << synth_util::LOOP_VAR << ";\n"; + os << indent() << "val prevChanged" << src_idx << " = changed;\n"; + os << indent() << "val newChanged" << src_idx << " = new AtomicBoolean(false);\n"; + if (include_comments) { + os << indent() << "var iterCount" << src_idx << " = 0;\n"; + } + os << indent() << "do {\n"; + nesting_level++; + os << indent() << "newChanged" << src_idx << ".set(false);\n"; + tracking_fiber_convergence = true; + synth_util::emit_loop_implicits(os, "newChanged" + std::to_string(src_idx)); + } + + if (include_comments && synth_functions_state->is_side_effect_evaluation && !dump_fixed_point_loop) { + os << "\n"; + } + FiberDependencyDumper::dump(aug_graph, aug_graph_instance, os); + + if (synth_functions_state->is_side_effect_evaluation) { + os << indent(); + synth_impl_ptr->dump_synth_instance(aug_graph_instance, os); + os << "\n"; + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + } + + if (!synth_functions_state->is_side_effect_evaluation) { + if (dump_fixed_point_loop) { + os << indent() << src_attr << ".assign(" << node_assign; + synth_impl_ptr->dump_synth_instance(aug_graph_instance, os); + os << ", changed);\n"; + } else { + os << indent(); + synth_impl_ptr->dump_synth_instance(aug_graph_instance, os); + os << "\n"; + } + } + + if (dump_fixed_point_loop) { + tracking_fiber_convergence = false; + if (include_comments) { + os << indent() << "iterCount" << src_idx << " += 1;\n"; + os << indent() << "Debug.out(\"fixed-point " << synth_functions_state->fdecl_name << " node=\" + node + \" iteration=\" + iterCount" << src_idx << ");\n"; + } + nesting_level--; + os << indent() << "} while (newChanged" << src_idx << ".get && !" << PREV_LOOP_VAR << src_idx << ")\n"; + os << indent() << "prevChanged" << src_idx << ".compareAndSet(false, newChanged" << src_idx << ".get);\n"; + if (!synth_functions_state->is_side_effect_evaluation) { + os << indent() << src_attr << ".get(" << node_get << ")\n"; + } + nesting_level--; + os << indent() << "}\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 && !synth_functions_state->is_phylum_instance) { + Declaration obj_decl = synth_functions_state->source->fibered_attr.attr; + for (auto ag_it = synth_functions_state->aug_graphs.begin(); ag_it != synth_functions_state->aug_graphs.end(); ag_it++) { + AUG_GRAPH* aug_graph = *ag_it; + std::vector field_assigns = collect_object_field_assignments(aug_graph, obj_decl); + if (field_assigns.empty()) { + continue; + } + + current_aug_graph = aug_graph; + current_blocks.clear(); + current_blocks.push_back(matcher_body(top_level_match_m(aug_graph->match_rule))); + + os << indent() << "node match {\n"; + nesting_level++; + os << indent() << "case " << matcher_pat(top_level_match_m(aug_graph->match_rule)) << " => {\n"; + nesting_level++; + + for (auto fa = field_assigns.begin(); fa != field_assigns.end(); fa++) { + if (fa->instance == NULL) { + continue; + } + current_scope_block = synth_util::linearize_block(aug_graph, fa->instance); + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + os << indent() << "a_" << decl_name(fa->field) << DEREF; + if (debug) { + os << "assign"; + } else { + os << "set"; + } + os << "(" << result_var << ", "; + dump_Expression(fa->rhs, os); + os << ");\n"; + } + + nesting_level--; + os << indent() << "}\n"; + os << indent() << "case _ => ()\n"; + nesting_level--; + os << indent() << "};\n"; + + current_blocks.clear(); + dumped_conditional_block_items.clear(); + dumped_instances.clear(); + } + } + + if (!synth_functions_state->is_side_effect_evaluation) { + os << indent() << result_var << "\n"; + } + + nesting_level--; + os << indent() << "}\n\n"; + } + +} + +class SynthImpl : 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; + dump_synth_functions(s, os); + + os << indent() << "override def finish() : Unit = {\n"; + ++nesting_level; + emit_start_phylum_evaluations(os, s, synth_functions_states); + os << indent() << "super.finish();\n"; + --nesting_level; + os << indent() << "};\n"; + + synth_util::destroy_synth_function_states(synth_functions_states); + synth_functions_states.clear(); + + clear_implementation_marks(module_decl); + } + }; + + Super* get_module_info(Declaration m) { return new ModuleInfo(m); } + + 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) { + if (rhs == NULL) { + if (include_comments) { + o << "// " << in << "\n"; + } + return; + } + + Declaration assign = (Declaration)tnode_parent(rhs); + Expression lhs = assign_lhs(assign); + Declaration field = 0; + switch (Expression_KEY(lhs)) { + case KEYvalue_use: + field = USE_DECL(value_use_use(lhs)); + o << "a_" << decl_name(field) << "."; + if (debug) { + o << "assign"; + } else { + o << "set"; + } + o << "("; + dump_Expression(rhs, o); + if (tracking_fiber_convergence) { + o << ", changed"; + } + o << ")"; + break; + case KEYfuncall: + field = field_ref_p(lhs); + if (field == 0) { + fatal_error("what sort of assignment lhs: %d", tnode_line_number(assign)); + } + o << "a_" << decl_name(field) << DEREF; + if (debug) { + o << "assign"; + } else { + o << "set"; + } + o << "("; + dump_Expression(field_ref_object(lhs), o); + o << ", "; + dump_Expression(rhs, o); + if (tracking_fiber_convergence) { + o << ", changed"; + } + o << ");\n"; + break; + default: + fatal_error("what sort of assignment lhs: %d", tnode_line_number(assign)); + } + return; + } + + 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,"; + dump_Expression(rhs, o); + o << ");\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 << " = "; + dump_Expression(rhs, o); + o << "; // 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 << ","; + dump_Expression(rhs, o); + o << ");\n"; + } else { + int i = LOCAL_UNIQUE_PREFIX(ad); + if (i == 0) { + o << "v_" << asym << " = "; + dump_Expression(rhs, o); + o << "; // function\n"; + } else { + o << "v" << i << "_" << asym << " = "; + dump_Expression(rhs, o); + o << ";\n"; + } + } + } else { + o << "a_" << asym << DEREF; + if (debug) { + o << "assign"; + } else { + o << "set"; + } + o << "(v_" << decl_name(in->node) << ","; + dump_Expression(rhs, o); + o << ");\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) { + o << "a_" << asym << DEREF; + if (debug) { + o << "assign"; + } else { + o << "set"; + } + o << "(v_" << decl_name(in->node) << ","; + dump_Expression(rhs, o); + o << ");\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()) { + 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 (instance->fibered_attr.fiber != NULL) { + for (auto it = valid_rhs.begin(); it != valid_rhs.end(); it++) { + dump_assignment(instance, *it, o); + } + } else if (valid_rhs.size() == 1) { + dump_Expression(valid_rhs[0], o); + } else { + 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); + 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; + } + } + + if (instance->fibered_attr.fiber != NULL) { + return; + } + + Declaration attr = instance->fibered_attr.attr; + bool is_local_collection = direction_is_collection(some_value_decl_direction(attr)); + if (is_local_collection) { + 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) { + 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 (m == first_Match(case_stmt_matchers(header))) { + Expression e = case_stmt_expr(header); + o << "{\n"; + nesting_level++; + o << indent() << "val node" << instance->index << " = "; + dump_Expression(e, o); + o << ";\n"; + } + o << indent() << "node" << instance->index << " match {\n"; + nesting_level++; + o << indent() << "case " << p << " => {\n"; + nesting_level += 1; + + Block if_true = matcher_body(m); + Block if_false = MATCH_NEXT(m) ? 0 : 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) override { + 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 (instance->fibered_attr.fiber != NULL && + fiber_is_reverse(instance->fibered_attr.fiber)) { + dump_rhs_instance_helper(aug_graph, block, instance, o); + } else if (is_parent_instance) { + o << "v_" << synth_util::instance_to_string(instance, current_synth_functions_state->is_phylum_instance); + if (current_synth_functions_state->is_phylum_instance && + !synth_util::synth_function_has_regular_dependency( + current_synth_functions_state, instance)) { + o << "(node)"; + } + } 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 dep_it = dependencies.begin(); dep_it != dependencies.end(); dep_it++) { + INSTANCE* source_instance = *dep_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* synth_impl = synth_impl_ptr = new SynthImpl(); + +#endif // APS2SCALA diff --git a/codegen/synth-util.cc b/codegen/synth-util.cc index a5776be7..d5baec22 100644 --- a/codegen/synth-util.cc +++ b/codegen/synth-util.cc @@ -207,6 +207,43 @@ static std::vector collect_aug_graph_attr_dependencies(AUG_GRAPH* aug return result; } +static std::vector collect_field_assign_dependencies(AUG_GRAPH* graph, + INSTANCE* owner) { + std::vector result; + Block body = matcher_body(top_level_match_m(graph->match_rule)); + for (Declaration declaration = first_Declaration(block_body(body)); declaration; + declaration = DECL_NEXT(declaration)) { + if (Declaration_KEY(declaration) != KEYnormal_assign) { + continue; + } + Expression lhs = assign_lhs(declaration); + if (Expression_KEY(lhs) != KEYfuncall || field_ref_p(lhs) == NULL) { + continue; + } + Expression object = field_ref_object(lhs); + if (Expression_KEY(object) != KEYvalue_use || + USE_DECL(value_use_use(object)) != owner->fibered_attr.attr) { + continue; + } + INSTANCE* field_instance = Expression_info(assign_rhs(declaration))->value_for; + if (field_instance == NULL) { + continue; + } + std::vector dependencies = + collect_aug_graph_attr_dependencies(graph, field_instance); + for (INSTANCE* dependency_instance : dependencies) { + if (dependency_instance->index != owner->index && + !should_skip_synth_dependency(dependency_instance) && + instance_direction_for(dependency_instance) != instance_local && + !(dependency_instance->node != NULL && dependency_instance->node != graph->lhs_decl) && + std::find(result.begin(), result.end(), dependency_instance) == result.end()) { + result.push_back(dependency_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) { @@ -219,7 +256,8 @@ static std::vector collect_lhs_aug_graphs(STATE* state, PHY_GRAPH* p return result; } -std::vector build_synth_function_states(STATE* state) { +std::vector build_synth_function_states( + STATE* state, bool include_field_assign_dependencies) { std::vector result; for (int phylum_index = 0; phylum_index < state->phyla.length; ++phylum_index) { @@ -269,6 +307,17 @@ std::vector build_synth_function_states(STATE* state) { 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); + if (include_field_assign_dependencies) { + std::vector field_dependencies = + collect_field_assign_dependencies(aug_graph, instance); + for (INSTANCE* dependency_instance : field_dependencies) { + if (std::find(function_state->regular_dependencies.begin(), + function_state->regular_dependencies.end(), dependency_instance) == + function_state->regular_dependencies.end()) { + function_state->regular_dependencies.push_back(dependency_instance); + } + } + } function_state->aug_graphs.push_back(aug_graph); result.push_back(function_state); } @@ -403,10 +452,86 @@ bool synth_function_is_circular(SynthFunctionState* state) { return false; } +bool synth_function_has_regular_dependency(SynthFunctionState* state, + INSTANCE* instance) { + for (INSTANCE* dependency_instance : state->regular_dependencies) { + if (fibered_attr_equal(&dependency_instance->fibered_attr, + &instance->fibered_attr)) { + 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 is_local_cycle_candidate(INSTANCE* instance, AUG_GRAPH* graph) { + return instance_is_child(instance, graph) && + instance->fibered_attr.fiber == NULL && + !if_rule_p(instance->fibered_attr.attr) && + instance_is_synthesized(instance) && instance_circular(instance); +} + +static bool is_in_same_cycle(INSTANCE* inherited, INSTANCE* instance, + AUG_GRAPH* graph) { + int instance_count = graph->instances.length; + bool instance_feeds_inherited = + edgeset_kind(graph->graph[instance->index * instance_count + inherited->index]) & + DEPENDENCY_MAYBE_DIRECT; + return inherited != instance && inherited->node == instance->node && + inherited->fibered_attr.fiber == NULL && + instance_is_inherited(inherited) && instance_circular(inherited) && + instance_feeds_inherited; +} + +std::vector collect_child_cycle_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 (!is_local_cycle_candidate(instance, graph)) { + continue; + } + for (int inherited_index = 0; inherited_index < instance_count; + ++inherited_index) { + if (is_in_same_cycle(&graph->instances.array[inherited_index], instance, graph)) { + result.push_back(instance); + break; + } + } + } + return result; +} + +bool child_cycle_is_independent(AUG_GRAPH* graph, INSTANCE* cycle_instance) { + int instance_count = graph->instances.length; + std::vector cycle_indices; + cycle_indices.push_back(cycle_instance->index); + for (int index = 0; index < instance_count; ++index) { + if (is_in_same_cycle(&graph->instances.array[index], cycle_instance, graph)) { + cycle_indices.push_back(index); + } + } + + for (int index = 0; index < instance_count; ++index) { + INSTANCE* instance = &graph->instances.array[index]; + if ((instance->fibered_attr.fiber == NULL && + !ATTR_DECL_IS_SHARED_INFO(instance->fibered_attr.attr)) || + !instance_circular(instance)) { + continue; + } + for (int cycle_index : cycle_indices) { + if (edgeset_kind(graph->graph[index * instance_count + cycle_index]) || + edgeset_kind(graph->graph[cycle_index * instance_count + index])) { + return false; + } + } + } + return true; +} + 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) { @@ -560,6 +685,7 @@ std::vector> make_instance_assignments(AUG_GRAPH* graph, co return assignments; } +// We prefer to have IF statements at the beginning static std::vector sort_instances(AUG_GRAPH* graph) { std::vector result; int instance_count = graph->instances.length; @@ -580,7 +706,40 @@ static std::vector sort_instances(AUG_GRAPH* graph) { 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) { +static std::vector> direct_cycle_groups(AUG_GRAPH* graph) { + int instance_count = graph->instances.length; + SccGraph scc_graph; + scc_graph_initialize(&scc_graph, instance_count); + for (int index = 0; index < instance_count; ++index) { + scc_graph_add_vertex(&scc_graph, &graph->instances.array[index]); + } + for (int source = 0; source < instance_count; ++source) { + for (int sink = 0; sink < instance_count; ++sink) { + if (source != sink && + (edgeset_kind(graph->graph[source * instance_count + sink]) & + DEPENDENCY_MAYBE_DIRECT)) { + scc_graph_add_edge(&scc_graph, &graph->instances.array[source], + &graph->instances.array[sink]); + } + } + } + + SCC_COMPONENTS* components = scc_graph_components(&scc_graph); + std::vector> groups; + for (int component_index = 0; component_index < components->length; + ++component_index) { + SCC_COMPONENT* component = components->array[component_index]; + std::vector group; + for (int index = 0; index < component->length; ++index) { + group.push_back(static_cast(component->array[index])); + } + groups.push_back(group); + } + scc_graph_destroy(&scc_graph); + return groups; +} + +static BlockItem* linearize_block_helper(AUG_GRAPH* graph, const std::vector& sorted_instances, bool* scheduled, CONDITION* condition, BlockItem* previous, int remaining, INSTANCE* sink, const std::vector& component_of) { if (CONDITION_IS_IMPOSSIBLE(*condition)) { return NULL; } @@ -595,13 +754,13 @@ static BlockItem* linearize_block_helper(AUG_GRAPH* graph, const std::vectorgraph[index * instance_count + sink->index])) { scheduled[index] = true; - BlockItem* result = linearize_block_helper(graph, sorted_instances, scheduled, condition, previous, remaining - 1, sink); + BlockItem* result = linearize_block_helper(graph, sorted_instances, scheduled, condition, previous, remaining - 1, sink, component_of); scheduled[index] = false; return result; } @@ -612,6 +771,10 @@ static BlockItem* linearize_block_helper(AUG_GRAPH* graph, const std::vectorgraph[dependency_index * instance_count + index]) & DEPENDENCY_MAYBE_DIRECT)) { continue; } + // Ignore edges within the same direct cycle so its instances can be linearized. + if (component_of[dependency_index] == component_of[index]) { + continue; + } ready = false; } if (!ready) { @@ -630,10 +793,10 @@ static BlockItem* linearize_block_helper(AUG_GRAPH* graph, const std::vectorfibered_attr.attr); condition->positive |= condition_mask; - conditional->next_positive = linearize_block_helper(graph, sorted_instances, scheduled, condition, item, remaining - 1, sink); + conditional->next_positive = linearize_block_helper(graph, sorted_instances, scheduled, condition, item, remaining - 1, sink, component_of); condition->positive &= ~condition_mask; condition->negative |= condition_mask; - conditional->next_negative = linearize_block_helper(graph, sorted_instances, scheduled, condition, item, remaining - 1, sink); + conditional->next_negative = linearize_block_helper(graph, sorted_instances, scheduled, condition, item, remaining - 1, sink, component_of); condition->negative &= ~condition_mask; } else { BlockItemInstance* linear = static_cast(std::malloc(sizeof(BlockItemInstance))); @@ -641,7 +804,7 @@ static BlockItem* linearize_block_helper(AUG_GRAPH* graph, const std::vectorkey = KEY_BLOCK_ITEM_INSTANCE; linear->instance = instance; linear->prev = previous; - linear->next = linearize_block_helper(graph, sorted_instances, scheduled, condition, item, remaining - 1, sink); + linear->next = linearize_block_helper(graph, sorted_instances, scheduled, condition, item, remaining - 1, sink, component_of); } scheduled[index] = false; return item; @@ -653,6 +816,8 @@ static BlockItem* linearize_block_helper(AUG_GRAPH* graph, const std::vectorinstances.length; bool* scheduled = static_cast(alloca(sizeof(bool) * instance_count)); @@ -660,7 +825,15 @@ BlockItem* linearize_block(AUG_GRAPH* graph, INSTANCE* sink) { CONDITION condition = {0, 0}; std::vector sorted_instances = sort_instances(graph); - return linearize_block_helper(graph, sorted_instances, scheduled, &condition, NULL, instance_count, sink); + std::vector> groups = direct_cycle_groups(graph); + std::vector component_of(instance_count, -1); + for (size_t group_index = 0; group_index < groups.size(); ++group_index) { + for (INSTANCE* instance : groups[group_index]) { + component_of[instance->index] = static_cast(group_index); + } + } + return linearize_block_helper(graph, sorted_instances, scheduled, &condition, + NULL, instance_count, sink, component_of); } void print_linearized_block(BlockItem* block, std::ostream& output) { @@ -688,6 +861,9 @@ void print_linearized_block(BlockItem* block, std::ostream& output) { } } +// Given an instance it traverses the direct dependency schedule +// trying to find the instance and if it sees the condition +// along the way, it returns that condition. BlockItem* find_surrounding_block(BlockItem* block, INSTANCE* instance) { while (block != NULL) { if (block->key == KEY_BLOCK_ITEM_CONDITION) { diff --git a/codegen/synth-util.h b/codegen/synth-util.h index e5c2a1de..0d7d9b12 100644 --- a/codegen/synth-util.h +++ b/codegen/synth-util.h @@ -81,7 +81,8 @@ 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); +std::vector build_synth_function_states( + STATE* state, bool include_field_assign_dependencies); void destroy_synth_function_states(const std::vector& states); @@ -93,6 +94,13 @@ void dump_attribute_type(INSTANCE* instance, std::ostream& output); bool synth_function_is_circular(SynthFunctionState* state); +bool synth_function_has_regular_dependency(SynthFunctionState* state, + INSTANCE* instance); + +std::vector collect_child_cycle_instances(AUG_GRAPH* graph); + +bool child_cycle_is_independent(AUG_GRAPH* graph, INSTANCE* cycle_instance); + std::vector> collect_child_cycle_components(AUG_GRAPH* graph, INSTANCE* sink); std::vector> make_instance_assignments(AUG_GRAPH* graph, const std::vector& blocks); diff --git a/examples/Makefile b/examples/Makefile index e28dad0a..62e0061d 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -12,8 +12,11 @@ default: %.scc.sched : %.aps ${APSSCHED} -C ${DEBUG_FLAGS} -p ${BASE} $* -%.scc.scala : %.aps - ${APS2SCALA} -C ${DEBUG_FLAGS} -p ${BASE} $* +%.farrow.scala : %.aps + ${APS2SCALA} -F ${DEBUG_FLAGS} -p ${BASE} $* + +%.synth.scala : %.aps + ${APS2SCALA} -A ${DEBUG_FLAGS} -p ${BASE} $* %.scala : %.aps ${APS2SCALA} ${DEBUG_FLAGS} -p ${BASE} $* diff --git a/examples/scala/Makefile b/examples/scala/Makefile index e3ac97f3..0c969e38 100644 --- a/examples/scala/Makefile +++ b/examples/scala/Makefile @@ -17,8 +17,10 @@ EVALUATOR ?= DYNAMIC ifeq (${EVALUATOR},STATIC) IMPL_SUFFIX = .static.scala -else ifeq (${EVALUATOR},SYNTH_F0) -IMPL_SUFFIX = .synth-f0.scala +else ifeq (${EVALUATOR},SYNTH) +IMPL_SUFFIX = .synth.scala +else ifeq (${EVALUATOR},FARROW) +IMPL_SUFFIX = .farrow.scala else IMPL_SUFFIX = .scala endif @@ -32,9 +34,10 @@ AST_TREE_SCALAGEN = simple.scala tiny.scala grammar.scala \ EVALUATOR_EXAMPLES = classic-binding first follow nullable test-coll \ test-use-coll test-cycle test-fields use-global broad-fiber-cycle \ below-fiber-cycle below-single-fiber-cycle local-fiber-cycle nested-cycles \ - farrow-lv farrow-ubd farrow-ubd-fiber nested-ubd nested-ubd-fiber simple-snc + farrow-lv farrow-ubd farrow-ubd-fiber nested-ubd nested-ubd-fiber simple-snc \ + simple-snc-fiber SCALAGEN = ${AST_TREE_SCALAGEN} \ - $(foreach example,${EVALUATOR_EXAMPLES},$(example).scala $(example).static.scala) + $(foreach example,${EVALUATOR_EXAMPLES},$(example).scala $(example).static.scala $(example).synth.scala $(example).farrow.scala) MISCGEN = SimpleParser.scala SimpleScanner.scala \ GrammarTokens.scala SimpleTokens.scala GrammarScanner.scala GrammarParser.scala \ @@ -67,6 +70,7 @@ all : ${OUT}/below_single_fiber_cycle_implicit.class ${OUT}/BelowSingleFiberCycl all : ${OUT}/local_fiber_cycle_implicit.class ${OUT}/LocalFiberCycleDriver.class all : ${OUT}/test_fields_implicit.class ${OUT}/TestFieldsDriver.class all : ${OUT}/simple_snc_implicit.class ${OUT}/SimpleSncDriver.class +all : ${OUT}/simple_snc_fiber_implicit.class ${OUT}/SimpleSncFiberDriver.class .PHONY: run @@ -76,10 +80,14 @@ phony_explicit: %.static.scala : ../%.aps ${APS2SCALA} ${APS2SCALA} ${APS2SCALAFLAGS} -S -C $* + cp $*.scala $@ + +%.farrow.scala : ../%.aps ${APS2SCALA} + ${APS2SCALA} ${APS2SCALAFLAGS} -F $* mv $*.scala $@ -%.synth-f0.scala : ../%.aps ${APS2SCALA} - ${APS2SCALA} ${APS2SCALAFLAGS} -F0 $* +%.synth.scala : ../%.aps ${APS2SCALA} + ${APS2SCALA} ${APS2SCALAFLAGS} -A $* mv $*.scala $@ %.scala : ../%.aps ${APS2SCALA} @@ -369,9 +377,18 @@ ${OUT}/simple-snc.class: simple-snc${IMPL_SUFFIX} ${OUT}/simple_implicit.class .PHONY: ${OUT}/simple_snc_implicit.class ${OUT}/simple_snc_implicit.class : ${OUT}/simple-snc.class ${OUT}/simple_implicit.class ; +${OUT}/simple-snc-fiber.class: simple-snc-fiber${IMPL_SUFFIX} ${OUT}/simple_implicit.class + ${SCALAC} ${SCALACFLAGS} $< + +.PHONY: ${OUT}/simple_snc_fiber_implicit.class +${OUT}/simple_snc_fiber_implicit.class : ${OUT}/simple-snc-fiber.class ${OUT}/simple_implicit.class ; + ${OUT}/SimpleSncDriver.class: ${OUT}/simple_snc_implicit.class ${OUT}/SimpleParser.class ${SCALAC} ${SCALACFLAGS} simple-snc-driver.scala +${OUT}/SimpleSncFiberDriver.class: ${OUT}/simple_snc_fiber_implicit.class ${OUT}/SimpleParser.class + ${SCALAC} ${SCALACFLAGS} simple-snc-fiber-driver.scala + .PHONY: %.run %.run : ${OUT}/%.class @@ -379,5 +396,5 @@ ${OUT}/SimpleSncDriver.class: ${OUT}/simple_snc_implicit.class ${OUT}/SimplePars clean: rm -rf ${OUT_BASE} - rm -f *.class ${SCALAGEN} ${MISCGEN} *.synth-f0.scala + rm -f *.class ${SCALAGEN} ${MISCGEN} diff --git a/examples/scala/compare-evaluators.sh b/examples/scala/compare-evaluators.sh index 580b6e9d..6990fd52 100755 --- a/examples/scala/compare-evaluators.sh +++ b/examples/scala/compare-evaluators.sh @@ -5,8 +5,9 @@ cd "$SCRIPT_DIR" DYNAMIC_EVALUATOR="DYNAMIC" STATIC_EVALUATOR="STATIC" -ORIGINAL_FARROW_EVALUATOR="SYNTH_F0" -DEFAULT_EVALUATORS="$DYNAMIC_EVALUATOR,$STATIC_EVALUATOR" +FARROW_EVALUATOR="FARROW" +SYNTH_EVALUATOR="SYNTH" +DEFAULT_EVALUATORS="$DYNAMIC_EVALUATOR,$STATIC_EVALUATOR,$SYNTH_EVALUATOR" extract_results() { sed -n '/^Results:$/,$p' @@ -107,15 +108,16 @@ TESTS=( "TestUseCollDriver|tiny.program|$DEFAULT_EVALUATORS" "TestCycleDriver|tiny.program|$DEFAULT_EVALUATORS" "UseGlobal|tiny.program|$DEFAULT_EVALUATORS" - "FarrowUbdDriver|farrow-ubd.program|$DEFAULT_EVALUATORS,$ORIGINAL_FARROW_EVALUATOR" + "FarrowUbdDriver|farrow-ubd.program|$DEFAULT_EVALUATORS,$FARROW_EVALUATOR" "FarrowUbdFiberDriver|farrow-ubd.program|$DEFAULT_EVALUATORS" - "NestedUbdDriver|nested-ubd.program|$DEFAULT_EVALUATORS,$ORIGINAL_FARROW_EVALUATOR" + "NestedUbdDriver|nested-ubd.program|$DEFAULT_EVALUATORS,$FARROW_EVALUATOR" "NestedUbdFiberDriver|nested-ubd.program|$DEFAULT_EVALUATORS" "TestFieldsDriver|tiny.program|$DEFAULT_EVALUATORS" "FirstDriver|grammar.cfg|$DEFAULT_EVALUATORS" "FollowDriver|grammar.cfg|$DEFAULT_EVALUATORS" "NullableDriver|grammar.cfg|$DEFAULT_EVALUATORS" - "SimpleSncDriver|simple.program|$DYNAMIC_EVALUATOR,$ORIGINAL_FARROW_EVALUATOR" + "SimpleSncDriver|simple.program|$DYNAMIC_EVALUATOR,$FARROW_EVALUATOR,$SYNTH_EVALUATOR" + "SimpleSncFiberDriver|simple.program|$DYNAMIC_EVALUATOR,$SYNTH_EVALUATOR" ) failures=0 diff --git a/examples/scala/simple-snc-fiber-driver.scala b/examples/scala/simple-snc-fiber-driver.scala new file mode 100644 index 00000000..2379baae --- /dev/null +++ b/examples/scala/simple-snc-fiber-driver.scala @@ -0,0 +1,37 @@ +object SimpleSncFiberDriver extends App { + var simple_tree : M_SIMPLE = null; + var p: Any = _; + if (args.length == 0) { + simple_tree = new M_SIMPLE("Simple"); + val t_simple = simple_tree.t_Result; + val ds = t_simple.v_xcons_decls(t_simple.v_no_decls(), + t_simple.v_decl("x",t_simple.v_integer_type())); + val s = t_simple.v_assign_stmt(t_simple.v_intconstant(3), + t_simple.v_intconstant(5)); + val ss = t_simple.v_xcons_stmts(t_simple.v_no_stmts(),s); + p = t_simple.v_program(t_simple.v_block(ds,ss)); + } else { + val ss = new SimpleScanner(new java.io.FileReader(args(0))); + val sp = new SimpleParser(); + sp.reset(ss, args(0)); + if (!sp.yyparse()) { + println("Errors found.\n"); + System.exit(1); + } + simple_tree = sp.getTree(); + p = simple_tree.t_Program; + } + + val m_simple = simple_tree; + val m_snc = new M_SIMPLE_SNC_FIBER[m_simple.T_Result]("SimpleSNC", m_simple.t_Result); + val t_snc = m_snc.t_Result; + + if (args.contains("--debug")) Debug.activate(); + + m_simple.finish(); + m_snc.finish(); + + println("Results:"); + println("program_ok is " + t_snc.v_program_ok(t_snc.t_Program.nodes(0))); + println("program_names is " + t_snc.v_program_names(t_snc.t_Program.nodes(0)).toSeq.sorted); +} diff --git a/examples/simple-snc-fiber.aps b/examples/simple-snc-fiber.aps index c00fe453..736276fe 100644 --- a/examples/simple-snc-fiber.aps +++ b/examples/simple-snc-fiber.aps @@ -23,13 +23,17 @@ module SIMPLE_SNC_FIBER[T :: var SIMPLE[]] extends T begin attribute Stmts.stmts_ok : Boolean; attribute Block.block_ok : Boolean; attribute Program.program_ok : Boolean; + attribute Program.program_names : Names; pragma inherited(block_env,decl_env,stmts_env,stmt_env,expr_env1,expr_env2); - pragma synthesized(expr_found1,expr_found2,stmt_ok,stmts_ok,block_ok,program_ok); + pragma synthesized(expr_found1,expr_found2,stmt_ok,stmts_ok,block_ok,program_ok,program_names); match ?p:Program=program(?b:Block) begin - b.block_env := Env$nil; + scope : Scope := contour(); + scope.outer := Env$nil; + b.block_env := scope; p.program_ok := b.block_ok; + p.program_names := scope.names; end; match ?b:Block=block(?ds:Decls,?ss:Stmts) begin