about summary refs log tree commit diff stats
path: root/036refcount.cc
diff options
context:
space:
mode:
Diffstat (limited to '036refcount.cc')
-rw-r--r--036refcount.cc554
1 files changed, 2 insertions, 552 deletions
diff --git a/036refcount.cc b/036refcount.cc
index 418e22b0..f9668be4 100644
--- a/036refcount.cc
+++ b/036refcount.cc
@@ -1,218 +1,9 @@
-//: Update refcounts when copying addresses.
-//: The top of the address layer has more on refcounts.
-
-:(scenario refcounts)
-def main [
-  1:address:num <- copy 1000/unsafe
-  2:address:num <- copy 1:address:num
-  1:address:num <- copy 0
-  2:address:num <- copy 0
-]
-+run: {1: ("address" "number")} <- copy {1000: "literal", "unsafe": ()}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {2: ("address" "number")} <- copy {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {1: ("address" "number")} <- copy {0: "literal"}
-+mem: decrementing refcount of 1000: 2 -> 1
-+run: {2: ("address" "number")} <- copy {0: "literal"}
-+mem: decrementing refcount of 1000: 1 -> 0
-
-:(after "Writing Instruction Product(i)")
-if (is_primitive(current_instruction().operation)) {
-  reagent/*copy*/ tmp = current_instruction().products.at(i);
-  canonize(tmp);
-  update_any_refcounts(tmp, products.at(i));
-}
-
-:(before "End Globals")
-bool Reclaim_memory = true;
-:(before "End Commandline Options(*arg)")
-else if (is_equal(*arg, "--no-reclaim")) {
-  cerr << "Disabling memory reclamation. Some tests will fail.\n";
-  Reclaim_memory = false;
-}
-:(code)
-void update_any_refcounts(const reagent& canonized_x, const vector<double>& data) {
-  if (!Reclaim_memory) return;
-  increment_any_refcounts(canonized_x, data);  // increment first so we don't reclaim on x <- copy x
-  decrement_any_refcounts(canonized_x);
-}
-
-void increment_any_refcounts(const reagent& canonized_x, const vector<double>& data) {
-  if (is_mu_address(canonized_x)) {
-    assert(scalar(data));
-    assert(!canonized_x.metadata.size);
-    increment_refcount(data.at(0));
-  }
-  // End Increment Refcounts(canonized_x)
-}
-
-void increment_refcount(int new_address) {
-  assert(new_address >= 0);
-  if (new_address == 0) return;
-  ++Total_refcount_updates;
-  int new_refcount = get_or_insert(Memory, new_address);
-  trace("mem") << "incrementing refcount of " << new_address << ": " << new_refcount << " -> " << new_refcount+1 << end();
-  put(Memory, new_address, new_refcount+1);
-}
-
-void decrement_any_refcounts(const reagent& canonized_x) {
-  // Begin Decrement Refcounts(canonized_x)
-  if (is_mu_address(canonized_x) && canonized_x.value != 0) {
-    assert(!canonized_x.metadata.size);
-    decrement_refcount(get_or_insert(Memory, canonized_x.value), payload_type(canonized_x.type), payload_size(canonized_x));
-  }
-  // End Decrement Refcounts(canonized_x)
-}
-
-void decrement_refcount(int old_address, const type_tree* payload_type, int payload_size) {
-  assert(old_address >= 0);
-  if (old_address == 0) return;
-  ++Total_refcount_updates;
-  int old_refcount = get_or_insert(Memory, old_address);
-  trace("mem") << "decrementing refcount of " << old_address << ": " << old_refcount << " -> " << old_refcount-1 << end();
-  --old_refcount;
-  put(Memory, old_address, old_refcount);
-  if (old_refcount < 0) {
-    cerr << "Negative refcount!!! " << old_address << ' ' << old_refcount << '\n';
-    if (Trace_stream) Trace_stream->dump();
-    exit(1);
-  }
-  // End Decrement Refcount(old_address, payload_type, payload_size)
-}
-
 int payload_size(reagent/*copy*/ x) {
   x.properties.push_back(pair<string, string_tree*>("lookup", NULL));
   lookup_memory_core(x, /*check_for_null*/false);
-  return size_of(x) + /*refcount*/1;
+  return size_of(x);
 }
 
-:(scenario refcounts_reflexive)
-def main [
-  1:address:num <- new number:type
-  # idempotent copies leave refcount unchanged
-  1:address:num <- copy 1:address:num
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {1: ("address" "number")} <- copy {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+mem: decrementing refcount of 1000: 2 -> 1
-
-:(scenario refcounts_call)
-def main [
-  1:address:num <- new number:type
-  # passing in addresses to recipes increments refcount
-  foo 1:address:num
-  # return does NOT yet decrement refcount; memory must be explicitly managed
-  1:address:num <- new number:type
-]
-def foo [
-  2:address:num <- next-ingredient
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: foo {1: ("address" "number")}
-# leave ambiguous precisely when the next increment happens
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: decrementing refcount of 1000: 2 -> 1
-
-//: fix up any instructions that don't follow the usual flow of read_memory
-//: before the RUN switch, and write_memory after
-
-:(scenario refcounts_put)
-container foo [
-  x:address:num
-]
-def main [
-  1:address:num <- new number:type
-  2:address:foo <- new foo:type
-  *2:address:foo <- put *2:address:foo, x:offset, 1:address:num
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {2: ("address" "foo")} <- new {foo: "type"}
-+mem: incrementing refcount of 1002: 0 -> 1
-+run: {2: ("address" "foo"), "lookup": ()} <- put {2: ("address" "foo"), "lookup": ()}, {x: "offset"}, {1: ("address" "number")}
-# put increments refcount
-+mem: incrementing refcount of 1000: 1 -> 2
-
-:(after "Write Memory in PUT in Run")
-reagent/*copy*/ element = element_type(base.type, offset);
-assert(!has_property(element, "lookup"));
-element.set_value(address);
-update_any_refcounts(element, ingredients.at(2));
-
-:(scenario refcounts_put_index)
-def main [
-  1:address:num <- new number:type
-  2:address:array:address:num <- new {(address number): type}, 3
-  *2:address:array:address:num <- put-index *2:address:array:address:num, 0, 1:address:num
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {2: ("address" "array" "address" "number")} <- new {(address number): "type"}, {3: "literal"}
-+mem: incrementing refcount of 1002: 0 -> 1
-+run: {2: ("address" "array" "address" "number"), "lookup": ()} <- put-index {2: ("address" "array" "address" "number"), "lookup": ()}, {0: "literal"}, {1: ("address" "number")}
-# put-index increments refcount
-+mem: incrementing refcount of 1000: 1 -> 2
-
-:(after "Write Memory in PUT_INDEX in Run")
-reagent/*local*/ element;
-element.set_value(address);
-element.type = copy_array_element(base.type);
-update_any_refcounts(element, value);
-
-:(scenario refcounts_maybe_convert)
-exclusive-container foo [
-  x:num
-  p:address:num
-]
-def main [
-  1:address:num <- new number:type
-  2:foo <- merge 1/p, 1:address:num
-  4:address:num, 5:bool <- maybe-convert 2:foo, 1:variant/p
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-# merging in an address increments refcount
-+run: {2: "foo"} <- merge {1: "literal", "p": ()}, {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {4: ("address" "number")}, {5: "boolean"} <- maybe-convert {2: "foo"}, {1: "variant", "p": ()}
-# maybe-convert increments refcount on success
-+mem: incrementing refcount of 1000: 2 -> 3
-
-:(after "Write Memory in Successful MAYBE_CONVERT")
-// todo: double-check data here as well
-vector<double> data;
-for (int i = 0;  i < size_of(product);  ++i)
-  data.push_back(get_or_insert(Memory, base_address+/*skip tag*/1+i));
-update_any_refcounts(product, data);
-
-//:: manage refcounts in instructions that copy multiple locations at a time
-
-:(scenario refcounts_copy_nested)
-container foo [
-  x:address:num  # address inside container
-]
-def main [
-  1:address:num <- new number:type
-  2:address:foo <- new foo:type
-  *2:address:foo <- put *2:address:foo, x:offset, 1:address:num
-  3:foo <- copy *2:address:foo
-]
-+transform: compute address offsets for container foo
-+transform: checking container foo, element 0
-+transform: address at offset 0
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {2: ("address" "foo"), "lookup": ()} <- put {2: ("address" "foo"), "lookup": ()}, {x: "offset"}, {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-# copying a container increments refcounts of any contained addresses
-+run: {3: "foo"} <- copy {2: ("address" "foo"), "lookup": ()}
-+mem: incrementing refcount of 1000: 2 -> 3
-
 :(before "End type_tree Definition")
 struct address_element_info {
   // Where inside a container type (after flattening nested containers!) the
@@ -268,7 +59,7 @@ struct tag_condition_info {
 //
 //  IF offset o1 has tag t2 AND offset o2 has tag t2 AND .., THEN
 //    for all address_element_infos:
-//      you need to update refcounts for the address at offset pointing to a payload of type payload_type (just in case we need to abandon something in the process)
+//      there is an address at 'offset' pointing to a payload of type payload_type
 map<set<tag_condition_info>, set<address_element_info> > address;
 :(code)
 bool operator<(const set<tag_condition_info>& a, const set<tag_condition_info>& b) {
@@ -699,40 +490,6 @@ void test_container_address_offsets_from_repeated_address_and_array_types() {
   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
 }
 
-//: use metadata.address to update refcounts within containers, arrays and
-//: exclusive containers
-
-:(before "End Increment Refcounts(canonized_x)")
-if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) {
-  const container_metadata& metadata = get(Container_metadata, canonized_x.type);
-  for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin();  p != metadata.address.end();  ++p) {
-    if (!all_match(data, p->first)) continue;
-    for (set<address_element_info>::const_iterator info = p->second.begin();  info != p->second.end();  ++info)
-      increment_refcount(data.at(info->offset));
-  }
-}
-
-:(before "End Decrement Refcounts(canonized_x)")
-if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) {
-  trace("mem") << "need to read old value of '" << to_string(canonized_x) << "' to figure out what refcounts to decrement" << end();
-  // read from canonized_x but without canonizing again
-  reagent/*copy*/ tmp = canonized_x;
-  tmp.properties.push_back(pair<string, string_tree*>("raw", NULL));
-  vector<double> data = read_memory(tmp);
-  trace("mem") << "done reading old value of '" << to_string(canonized_x) << "'" << end();
-  const container_metadata& metadata = get(Container_metadata, canonized_x.type);
-  for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin();  p != metadata.address.end();  ++p) {
-    if (!all_match(data, p->first)) continue;
-    for (set<address_element_info>::const_iterator info = p->second.begin();  info != p->second.end();  ++info) {
-      int element_address = get_or_insert(Memory, canonized_x.value + info->offset);
-      reagent/*local*/ element;
-      element.set_value(element_address+/*skip refcount*/1);
-      element.type = new type_tree(*info->payload_type);
-      decrement_refcount(element_address, info->payload_type, size_of(element)+/*refcount*/1);
-    }
-  }
-}
-
 :(code)
 bool all_match(const vector<double>& data, const set<tag_condition_info>& conditions) {
   for (set<tag_condition_info>::const_iterator p = conditions.begin();  p != conditions.end();  ++p) {
@@ -742,271 +499,6 @@ bool all_match(const vector<double>& data, const set<tag_condition_info>& condit
   return true;
 }
 
-:(scenario refcounts_put_container)
-container foo [
-  a:bar  # contains an address
-]
-container bar [
-  x:address:num
-]
-def main [
-  1:address:num <- new number:type
-  2:bar <- merge 1:address:num
-  3:address:foo <- new foo:type
-  *3:address:foo <- put *3:address:foo, a:offset, 2:bar
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {2: "bar"} <- merge {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {a: "offset"}, {2: "bar"}
-# put increments refcount inside container
-+mem: incrementing refcount of 1000: 2 -> 3
-
-:(scenario refcounts_put_index_array)
-container bar [
-  x:address:num
-]
-def main [
-  1:address:num <- new number:type
-  2:bar <- merge 1:address:num
-  3:address:array:bar <- new bar:type, 3
-  *3:address:array:bar <- put-index *3:address:array:bar, 0, 2:bar
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {2: "bar"} <- merge {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {3: ("address" "array" "bar"), "lookup": ()} <- put-index {3: ("address" "array" "bar"), "lookup": ()}, {0: "literal"}, {2: "bar"}
-# put-index increments refcount inside container
-+mem: incrementing refcount of 1000: 2 -> 3
-
-:(scenario refcounts_maybe_convert_container)
-exclusive-container foo [
-  a:num
-  b:bar  # contains an address
-]
-container bar [
-  x:address:num
-]
-def main [
-  1:address:num <- new number:type
-  2:bar <- merge 1:address:num
-  3:foo <- merge 1/b, 2:bar
-  5:bar, 6:bool <- maybe-convert 3:foo, 1:variant/b
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {2: "bar"} <- merge {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {3: "foo"} <- merge {1: "literal", "b": ()}, {2: "bar"}
-+mem: incrementing refcount of 1000: 2 -> 3
-+run: {5: "bar"}, {6: "boolean"} <- maybe-convert {3: "foo"}, {1: "variant", "b": ()}
-+mem: incrementing refcount of 1000: 3 -> 4
-
-:(scenario refcounts_copy_doubly_nested)
-container foo [
-  a:bar  # no addresses
-  b:curr  # contains addresses
-]
-container bar [
-  x:num
-  y:num
-]
-container curr [
-  x:num
-  y:address:num  # address inside container inside container
-]
-def main [
-  1:address:num <- new number:type
-  2:address:curr <- new curr:type
-  *2:address:curr <- put *2:address:curr, 1:offset/y, 1:address:num
-  3:address:foo <- new foo:type
-  *3:address:foo <- put *3:address:foo, 1:offset/b, *2:address:curr
-  4:foo <- copy *3:address:foo
-]
-+transform: compute address offsets for container foo
-+transform: checking container foo, element 1
-+transform: address at offset 3
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-# storing an address in a container updates its refcount
-+run: {2: ("address" "curr"), "lookup": ()} <- put {2: ("address" "curr"), "lookup": ()}, {1: "offset", "y": ()}, {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-# storing a container in a container updates refcounts of any contained addresses
-+run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {1: "offset", "b": ()}, {2: ("address" "curr"), "lookup": ()}
-+mem: incrementing refcount of 1000: 2 -> 3
-# copying a container containing a container containing an address updates refcount
-+run: {4: "foo"} <- copy {3: ("address" "foo"), "lookup": ()}
-+mem: incrementing refcount of 1000: 3 -> 4
-
-:(scenario refcounts_copy_exclusive_container_within_container)
-container foo [
-  a:num
-  b:bar
-]
-exclusive-container bar [
-  x:num
-  y:num
-  z:address:num
-]
-def main [
-  1:address:num <- new number:type
-  2:bar <- merge 0/x, 34
-  3:foo <- merge 12, 2:bar
-  5:bar <- merge 1/y, 35
-  6:foo <- merge 13, 5:bar
-  8:bar <- merge 2/z, 1:address:num
-  9:foo <- merge 14, 8:bar
-  11:foo <- copy 9:foo
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-# no change while merging items of other types
-+run: {8: "bar"} <- merge {2: "literal", "z": ()}, {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {9: "foo"} <- merge {14: "literal"}, {8: "bar"}
-+mem: incrementing refcount of 1000: 2 -> 3
-+run: {11: "foo"} <- copy {9: "foo"}
-+mem: incrementing refcount of 1000: 3 -> 4
-
-:(scenario refcounts_copy_container_within_exclusive_container)
-exclusive-container foo [
-  a:num
-  b:bar
-]
-container bar [
-  x:num
-  y:num
-  z:address:num
-]
-def main [
-  1:address:num <- new number:type
-  2:foo <- merge 0/a, 34
-  6:foo <- merge 0/a, 35
-  10:bar <- merge 2/x, 15/y, 1:address:num
-  13:foo <- merge 1/b, 10:bar
-  17:foo <- copy 13:foo
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-# no change while merging items of other types
-+run: {10: "bar"} <- merge {2: "literal", "x": ()}, {15: "literal", "y": ()}, {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {13: "foo"} <- merge {1: "literal", "b": ()}, {10: "bar"}
-+mem: incrementing refcount of 1000: 2 -> 3
-+run: {17: "foo"} <- copy {13: "foo"}
-+mem: incrementing refcount of 1000: 3 -> 4
-
-:(scenario refcounts_copy_exclusive_container_within_exclusive_container)
-exclusive-container foo [
-  a:num
-  b:bar
-]
-exclusive-container bar [
-  x:num
-  y:address:num
-]
-def main [
-  1:address:num <- new number:type
-  10:foo <- merge 1/b, 1/y, 1:address:num
-  20:foo <- copy 10:foo
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-# no change while merging items of other types
-+run: {10: "foo"} <- merge {1: "literal", "b": ()}, {1: "literal", "y": ()}, {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {20: "foo"} <- copy {10: "foo"}
-+mem: incrementing refcount of 1000: 2 -> 3
-
-:(scenario refcounts_copy_array_within_container)
-container foo [
-  x:address:array:num
-]
-def main [
-  1:address:array:num <- new number:type, 3
-  2:foo <- merge 1:address:array:num
-  3:address:array:num <- new number:type, 5
-  2:foo <- merge 3:address:array:num
-]
-+run: {1: ("address" "array" "number")} <- new {number: "type"}, {3: "literal"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {2: "foo"} <- merge {1: ("address" "array" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {2: "foo"} <- merge {3: ("address" "array" "number")}
-+mem: decrementing refcount of 1000: 2 -> 1
-
-:(scenario refcounts_copy_address_within_static_array_within_container)
-container foo [
-  a:array:bar:3
-  b:address:num
-]
-container bar [
-  y:num
-  z:address:num
-]
-def main [
-  1:address:num <- new number:type
-  2:bar <- merge 34, 1:address:num
-  10:array:bar:3 <- create-array
-  put-index 10:array:bar:3, 1, 2:bar
-  20:foo <- merge 10:array:bar:3, 1:address:num
-  1:address:num <- copy 0
-  2:bar <- merge 34, 1:address:num
-  put-index 10:array:bar:3, 1, 2:bar
-  20:foo <- merge 10:array:bar:3, 1:address:num
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"}
-+mem: incrementing refcount of 1000: 2 -> 3
-+run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 3 -> 4
-+mem: incrementing refcount of 1000: 4 -> 5
-+run: {1: ("address" "number")} <- copy {0: "literal"}
-+mem: decrementing refcount of 1000: 5 -> 4
-+run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")}
-+mem: decrementing refcount of 1000: 4 -> 3
-+run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"}
-+mem: decrementing refcount of 1000: 3 -> 2
-+run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")}
-+mem: decrementing refcount of 1000: 2 -> 1
-+mem: decrementing refcount of 1000: 1 -> 0
-
-:(scenario refcounts_handle_exclusive_containers_with_different_tags)
-container foo1 [
-  x:address:num
-  y:num
-]
-container foo2 [
-  x:num
-  y:address:num
-]
-exclusive-container bar [
-  a:foo1
-  b:foo2
-]
-def main [
-  1:address:num <- copy 12000/unsafe  # pretend allocation
-  *1:address:num <- copy 34
-  2:bar <- merge 0/foo1, 1:address:num, 97
-  5:address:num <- copy 13000/unsafe  # pretend allocation
-  *5:address:num <- copy 35
-  6:bar <- merge 1/foo2, 98, 5:address:num
-  2:bar <- copy 6:bar
-]
-+run: {2: "bar"} <- merge {0: "literal", "foo1": ()}, {1: ("address" "number")}, {97: "literal"}
-+mem: incrementing refcount of 12000: 1 -> 2
-+run: {6: "bar"} <- merge {1: "literal", "foo2": ()}, {98: "literal"}, {5: ("address" "number")}
-+mem: incrementing refcount of 13000: 1 -> 2
-+run: {2: "bar"} <- copy {6: "bar"}
-+mem: incrementing refcount of 13000: 2 -> 3
-+mem: decrementing refcount of 12000: 2 -> 1
-
-:(code)
 bool is_mu_container(const reagent& r) {
   return is_mu_container(r.type);
 }
@@ -1030,45 +522,3 @@ bool is_mu_exclusive_container(const type_tree* type) {
   type_info& info = get(Type, type->value);
   return info.kind == EXCLUSIVE_CONTAINER;
 }
-
-//:: Counters for trying to understand where Mu programs are spending time
-//:: updating refcounts.
-
-:(before "End Globals")
-int Total_refcount_updates = 0;
-map<recipe_ordinal, map</*step index*/int, /*num refcount updates*/int> > Num_refcount_updates;
-:(after "Running One Instruction")
-int initial_num_refcount_updates = Total_refcount_updates;
-:(before "End Running One Instruction")
-if (Run_profiler) {
-  Num_refcount_updates[current_call().running_recipe][current_call().running_step_index]
-      += (Total_refcount_updates - initial_num_refcount_updates);
-  initial_num_refcount_updates = Total_refcount_updates;
-}
-:(before "End Non-primitive Call(caller_frame)")
-if (Run_profiler) {
-  Num_refcount_updates[caller_frame.running_recipe][caller_frame.running_step_index]
-      += (Total_refcount_updates - initial_num_refcount_updates);
-  initial_num_refcount_updates = Total_refcount_updates;
-}
-:(after "Begin Return")
-if (Run_profiler) {
-  Num_refcount_updates[current_call().running_recipe][current_call().running_step_index]
-      += (Total_refcount_updates - initial_num_refcount_updates);
-  initial_num_refcount_updates = Total_refcount_updates;
-}
-:(before "End dump_profile")
-fout.open("profile.refcounts");
-if (fout) {
-  for (map<recipe_ordinal, recipe>::iterator p = Recipe.begin();  p != Recipe.end();  ++p)
-    dump_recipe_profile(p->first, p->second, fout);
-}
-fout.close();
-:(code)
-void dump_recipe_profile(recipe_ordinal ridx, const recipe& r, ostream& out) {
-  out << "recipe " << r.name << " [\n";
-  for (int i = 0;  i < SIZE(r.steps);  ++i) {
-    out << std::setw(6) << Num_refcount_updates[ridx][i] << ' ' << to_string(r.steps.at(i)) << '\n';
-  }
-  out << "]\n\n";
-}