about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2017-11-01 02:46:41 -0700
committerKartik K. Agaram <vc@akkartik.com>2017-11-01 02:46:41 -0700
commitaae198a93b03ca53a0eb2a661c0a8cc47780573f (patch)
tree77d8365b53ec06ba8b410dd0b3661c19c82eb745
parentaa68aeb34e2476af8bfea8ba11e08e3d5f1d19f1 (diff)
downloadmu-aae198a93b03ca53a0eb2a661c0a8cc47780573f.tar.gz
4099
Generalize commit 4089 to arbitrary closures, and not just the current
'space' or call frame. Now we should be treating spaces just like any
other data structure, and reclaiming all addresses inside them when we
need to.

The cost: all spaces must now specify what recipe generated them (so
they know how to interpret the array of locations) using the /names
property.

We can probably make this ergonomic with a little 'type inference'. But
at least things are safe now.
-rw-r--r--026call.cc47
-rw-r--r--039location_array.cc46
-rw-r--r--042name.cc7
-rw-r--r--043space.cc70
-rw-r--r--045closure_name.cc4
-rw-r--r--046check_type_by_name.cc17
-rw-r--r--057immutable.cc6
-rw-r--r--073scheduler.cc2
8 files changed, 128 insertions, 71 deletions
diff --git a/026call.cc b/026call.cc
index a82917f1..6b7a14f7 100644
--- a/026call.cc
+++ b/026call.cc
@@ -69,6 +69,8 @@ routine::routine(recipe_ordinal r) {
 
 //:: now update routine's helpers
 
+//: macro versions for a slight speedup
+
 :(delete{} "int& current_step_index()")
 :(delete{} "recipe_ordinal currently_running_recipe()")
 :(delete{} "const string& current_recipe_name()")
@@ -84,6 +86,51 @@ routine::routine(recipe_ordinal r) {
 #define to_instruction(call) get(Recipe, (call).running_recipe).steps.at((call).running_step_index)
 #define current_instruction() to_instruction(current_call())
 
+//: function versions for debugging
+
+:(code)
+//? :(before "End Globals")
+//? bool Foo2 = false;
+//? :(code)
+//? call& current_call() {
+//?   if (Foo2) cerr << __FUNCTION__ << '\n';
+//?   return Current_routine->calls.front();
+//? }
+//? :(replace{} "int& current_step_index()")
+//? int& current_step_index() {
+//?   assert(!Current_routine->calls.empty());
+//?   if (Foo2) cerr << __FUNCTION__ << '\n';
+//?   return current_call().running_step_index;
+//? }
+//? :(replace{} "recipe_ordinal currently_running_recipe()")
+//? recipe_ordinal currently_running_recipe() {
+//?   assert(!Current_routine->calls.empty());
+//?   if (Foo2) cerr << __FUNCTION__ << '\n';
+//?   return current_call().running_recipe;
+//? }
+//? :(replace{} "const string& current_recipe_name()")
+//? const string& current_recipe_name() {
+//?   assert(!Current_routine->calls.empty());
+//?   if (Foo2) cerr << __FUNCTION__ << '\n';
+//?   return get(Recipe, current_call().running_recipe).name;
+//? }
+//? :(replace{} "const recipe& current_recipe()")
+//? const recipe& current_recipe() {
+//?   assert(!Current_routine->calls.empty());
+//?   if (Foo2) cerr << __FUNCTION__ << '\n';
+//?   return get(Recipe, current_call().running_recipe);
+//? }
+//? :(replace{} "const instruction& current_instruction()")
+//? const instruction& current_instruction() {
+//?   assert(!Current_routine->calls.empty());
+//?   if (Foo2) cerr << __FUNCTION__ << '\n';
+//?   return to_instruction(current_call());
+//? }
+//? :(code)
+//? const instruction& to_instruction(const call& call) {
+//?   return get(Recipe, call.running_recipe).steps.at(call.running_step_index);
+//? }
+
 :(after "Defined Recipe Checks")
 // not a primitive; check that it's present in the book of recipes
 if (!contains_key(Recipe, inst.operation)) {
diff --git a/039location_array.cc b/039location_array.cc
deleted file mode 100644
index 1c12d316..00000000
--- a/039location_array.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-:(before "End Primitive Recipe Declarations")
-TO_LOCATION_ARRAY,
-:(before "End Primitive Recipe Numbers")
-put(Recipe_ordinal, "to-location-array", TO_LOCATION_ARRAY);
-:(before "End Primitive Recipe Checks")
-case TO_LOCATION_ARRAY: {
-  const recipe& caller = get(Recipe, r);
-  if (!is_address_of_array_of_numbers(inst.products.at(0))) {
-    raise << maybe(caller.name) << "product of 'to-location-array' has incorrect type: '" << to_original_string(inst) << "'\n" << end();
-    break;
-  }
-  break;
-}
-:(code)
-bool is_address_of_array_of_numbers(reagent/*copy*/ x) {
-  canonize_type(x);
-  if (!is_compound_type_starting_with(x.type, "address")) return false;
-  drop_from_type(x, "address");
-  if (!is_compound_type_starting_with(x.type, "array")) return false;
-  drop_from_type(x, "array");
-  return x.type && x.type->atom && x.type->value == get(Type_ordinal, "number");
-}
-bool is_compound_type_starting_with(const type_tree* type, const string& expected_name) {
-  if (!type) return false;
-  if (type->atom) return false;
-  if (!type->left->atom) return false;
-  return type->left->value == get(Type_ordinal, expected_name);
-}
-
-:(before "End Primitive Recipe Implementations")
-case TO_LOCATION_ARRAY: {
-  int array_size = SIZE(ingredients.at(0));
-  int allocation_size = array_size + /*refcount and length*/2;
-  ensure_space(allocation_size);
-  const int result = Current_routine->alloc;
-  products.resize(1);
-  products.at(0).push_back(result);
-  // initialize array refcount
-  put(Memory, result, 0);
-  // initialize array length
-  put(Memory, result+1, array_size);
-  // now copy over data
-  for (int i = 0;  i < array_size;  ++i)
-    put(Memory, result+2+i, ingredients.at(0).at(i));
-  break;
-}
diff --git a/042name.cc b/042name.cc
index 34e3efe0..6aaf6066 100644
--- a/042name.cc
+++ b/042name.cc
@@ -130,6 +130,13 @@ type_ordinal skip_addresses(type_tree* type) {
   return base_type->left->value;
 }
 
+bool is_compound_type_starting_with(const type_tree* type, const string& expected_name) {
+  if (!type) return false;
+  if (type->atom) return false;
+  if (!type->left->atom) return false;
+  return type->left->value == get(Type_ordinal, expected_name);
+}
+
 int find_element_name(const type_ordinal t, const string& name, const string& recipe_name) {
   const type_info& container = get(Type, t);
   for (int i = 0;  i < SIZE(container.elements);  ++i)
diff --git a/043space.cc b/043space.cc
index 2f11c6c2..ce6e5623 100644
--- a/043space.cc
+++ b/043space.cc
@@ -86,7 +86,9 @@ int address(int offset, int base) {
   int size = get_or_insert(Memory, base);
   if (offset >= size) {
     // todo: test
-    raise << "location " << offset << " is out of bounds " << size << " at " << base << '\n' << end();
+    raise << current_recipe_name() << ": location " << offset << " is out of bounds " << size << " at " << base << '\n' << end();
+    DUMP("");
+    exit(1);
     return 0;
   }
   return base + /*skip length*/1 + offset;
@@ -96,14 +98,19 @@ int address(int offset, int base) {
 
 :(after "Begin Preprocess write_memory(x, data)")
 if (x.name == "default-space") {
-  if (!scalar(data) || !is_space(x))
+  if (!scalar(data) || !is_mu_space(x))
     raise << maybe(current_recipe_name()) << "'default-space' should be of type address:array:location, but is " << to_string(x.type) << '\n' << end();
   current_call().default_space = data.at(0);
   return;
 }
 :(code)
-bool is_space(const reagent& r) {
-  return is_address_of_array_of_numbers(r);
+bool is_mu_space(reagent/*copy*/ x) {
+  canonize_type(x);
+  if (!is_compound_type_starting_with(x.type, "address")) return false;
+  drop_from_type(x, "address");
+  if (!is_compound_type_starting_with(x.type, "array")) return false;
+  drop_from_type(x, "array");
+  return x.type && x.type->atom && x.type->name == "location";
 }
 
 :(scenario get_default_space)
@@ -237,14 +244,34 @@ reclaim_default_space();
 :(code)
 void reclaim_default_space() {
   if (!Reclaim_memory) return;
-  const recipe_ordinal r = get(Recipe_ordinal, current_recipe_name());
-  const recipe& exiting_recipe = get(Recipe, r);
-  if (!starts_by_setting_default_space(exiting_recipe)) return;
-  // Reclaim default-space
-  decrement_refcount(current_call().default_space,
-      exiting_recipe.steps.at(0).products.at(0).type->right,
-      /*refcount*/1 + /*array length*/1 + /*number-of-locals*/Name[r][""]);
+  reagent default_space("default-space:address:array:location");
+  decrement_any_refcounts(default_space);
 }
+:(after "Begin Decrement Refcounts(canonized_x)")
+if (is_mu_space(canonized_x)) {
+  int space_address = (canonized_x.name == "default-space") ? current_call().default_space : get_or_insert(Memory, canonized_x.value);
+  if (space_address == 0) return;
+  // this branch relies on global state
+  string recipe_name;
+  if (has_property(canonized_x, "names")) {
+    assert(property(canonized_x, "names")->atom);
+    recipe_name = property(canonized_x, "names")->value;
+  }
+  else {
+    if (canonized_x.name != "default-space")
+      cerr << current_recipe_name() << ": " << to_string(canonized_x) << '\n';
+    assert(canonized_x.name == "default-space");
+    recipe_name = current_recipe_name();
+  }
+  const recipe_ordinal space_recipe_ordinal = get(Recipe_ordinal, recipe_name);
+  const recipe& space_recipe = get(Recipe, space_recipe_ordinal);
+  if (canonized_x.name == "default-space" && !has_property(canonized_x, "names") && !starts_by_setting_default_space(space_recipe)) return;
+  // Reclaim Space(space_address, space_recipe_ordinal, space_recipe)
+  decrement_refcount(space_address, canonized_x.type->right,
+      /*refcount*/1 + /*array length*/1 + /*number-of-locals*/Name[space_recipe_ordinal][""]);
+  return;
+}
+:(code)
 bool starts_by_setting_default_space(const recipe& r) {
   return !r.steps.empty()
       && !r.steps.at(0).products.empty()
@@ -263,12 +290,16 @@ def main [
 # local-scope
 +mem: automatically abandoning 1000
 
-:(before "Reclaim default-space")
-if (get_or_insert(Memory, current_call().default_space) <= 1) {
+:(before "Reclaim Space(space_address, space_recipe_ordinal, space_recipe)")
+if (get_or_insert(Memory, space_address) <= 1) {
   set<string> reclaimed_locals;
   trace(9999, "mem") << "trying to reclaim locals" << end();
-  for (int i = /*leave default space for last*/1;  i < SIZE(exiting_recipe.steps);  ++i) {
-    const instruction& inst = exiting_recipe.steps.at(i);
+  // update any refcounts for variables in the space -- in the context of the space
+  call_stack calls_stash = save_call_stack(space_address, space_recipe_ordinal);
+  Current_routine->calls.swap(calls_stash);
+  // no early returns until we restore 'calls' below
+  for (int i = /*leave default space for last*/1;  i < SIZE(space_recipe.steps);  ++i) {
+    const instruction& inst = space_recipe.steps.at(i);
     for (int i = 0;  i < SIZE(inst.products);  ++i) {
       reagent/*copy*/ product = inst.products.at(i);
       if (reclaimed_locals.find(product.name) != reclaimed_locals.end()) continue;
@@ -282,6 +313,15 @@ if (get_or_insert(Memory, current_call().default_space) <= 1) {
       decrement_any_refcounts(product);
     }
   }
+  Current_routine->calls.swap(calls_stash);  // restore
+}
+:(code)
+call_stack save_call_stack(int space_address, recipe_ordinal space_recipe_ordinal) {
+  call dummy_call(space_recipe_ordinal);
+  dummy_call.default_space = space_address;
+  call_stack result;
+  result.push_front(dummy_call);
+  return result;
 }
 
 :(scenario local_variables_can_outlive_call)
diff --git a/045closure_name.cc b/045closure_name.cc
index f7c0082f..0f67e2d2 100644
--- a/045closure_name.cc
+++ b/045closure_name.cc
@@ -47,7 +47,7 @@ void collect_surrounding_spaces(const recipe_ordinal r) {
     for (int j = 0;  j < SIZE(inst.products);  ++j) {
       if (is_literal(inst.products.at(j))) continue;
       if (inst.products.at(j).name != "0") continue;
-      if (!is_space(inst.products.at(j))) {
+      if (!is_mu_space(inst.products.at(j))) {
         raise << "slot 0 should always have type address:array:location, but is '" << to_string(inst.products.at(j)) << "'\n" << end();
         continue;
       }
@@ -159,7 +159,7 @@ def new-scope [
 ]
 def use-scope [
   local-scope
-  outer:space <- next-ingredient
+  outer:space/names:new-scope <- next-ingredient
   0:space/names:new-scope <- copy outer:space
   return *x:&:num/space:1
 ]
diff --git a/046check_type_by_name.cc b/046check_type_by_name.cc
index 83b80571..3fd3fdd6 100644
--- a/046check_type_by_name.cc
+++ b/046check_type_by_name.cc
@@ -25,25 +25,34 @@ void check_or_set_types_by_name(const recipe_ordinal r) {
   for (int i = 0;  i < SIZE(caller.steps);  ++i) {
     instruction& inst = caller.steps.at(i);
     for (int in = 0;  in < SIZE(inst.ingredients);  ++in) {
-      deduce_missing_type(known, inst.ingredients.at(in));
+      deduce_missing_type(known, inst.ingredients.at(in), caller);
       check_type(known, inst.ingredients.at(in), caller);
     }
     for (int out = 0;  out < SIZE(inst.products);  ++out) {
-      deduce_missing_type(known, inst.products.at(out));
+      deduce_missing_type(known, inst.products.at(out), caller);
       check_type(known, inst.products.at(out), caller);
     }
   }
 }
 
-void deduce_missing_type(set<reagent>& known, reagent& x) {
+void deduce_missing_type(set<reagent>& known, reagent& x, const recipe& caller) {
   if (x.type) return;
   if (is_jump_target(x.name)) {
     x.type = new type_tree("label");
     return;
   }
   if (known.find(x) == known.end()) return;
-  x.type = new type_tree(*known.find(x)->type);
+  const reagent& exemplar = *known.find(x);
+  x.type = new type_tree(*exemplar.type);
   trace(9992, "transform") << x.name << " <= " << names_to_string(x.type) << end();
+  // spaces are special; their type includes their /names property
+  if (is_mu_space(x) && !has_property(x, "names")) {
+    if (!has_property(exemplar, "names")) {
+      raise << maybe(caller.name) << "missing /names property for space variable '" << exemplar.name << "'\n" << end();
+      return;
+    }
+    x.properties.push_back(pair<string, string_tree*>("names", new string_tree(*property(exemplar, "names"))));
+  }
 }
 
 void check_type(set<reagent>& known, const reagent& x, const recipe& caller) {
diff --git a/057immutable.cc b/057immutable.cc
index 4657ade0..6cfcacef 100644
--- a/057immutable.cc
+++ b/057immutable.cc
@@ -316,16 +316,16 @@ def bar [
 //: when checking for immutable ingredients, remember to take space into account
 :(scenario check_space_of_reagents_in_immutability_checks)
 def main [
-  a:space <- new-closure
+  a:space/names:new-closure <- new-closure
   b:&:num <- new number:type
   run-closure b:&:num, a:space
 ]
 def new-closure [
   local-scope
   x:&:num <- new number:type
-  return default-space
+  return default-space/names:new-closure
 ]
-def run-closure x:&:num, s:space [
+def run-closure x:&:num, s:space/names:new-closure [
   local-scope
   load-ingredients
   0:space/names:new-closure <- copy s
diff --git a/073scheduler.cc b/073scheduler.cc
index fb1a3f51..d9ebef51 100644
--- a/073scheduler.cc
+++ b/073scheduler.cc
@@ -313,7 +313,7 @@ def main [
   local-scope
   n:&:num <- copy 12000/unsafe  # pretend allocation with a known address
   *n <- copy 23
-  space:space <- create-space n
+  space:space/names:create-space <- create-space n
   n2:&:num <- copy 13000/unsafe
   n3:num <- use-space space, n2
 ]