about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--034address.cc126
-rw-r--r--035lookup.cc164
-rw-r--r--036refcount.cc554
-rw-r--r--037abandon.cc210
-rw-r--r--038new_text.cc13
-rw-r--r--043space.cc249
-rw-r--r--044space_surround.cc25
-rw-r--r--069hash.cc19
-rw-r--r--071deep_copy.cc15
-rw-r--r--072recipe.cc3
-rw-r--r--073scheduler.cc69
-rw-r--r--074wait.cc14
-rw-r--r--076continuation.cc113
-rw-r--r--082scenario_screen.cc8
-rw-r--r--085scenario_console.cc16
-rw-r--r--089scenario_filesystem.cc16
16 files changed, 151 insertions, 1463 deletions
diff --git a/034address.cc b/034address.cc
index 217562a3..0b2b9797 100644
--- a/034address.cc
+++ b/034address.cc
@@ -18,104 +18,6 @@
 //: write to the payload of an ingredient rather than its value, simply add
 //: the /lookup property to it. Modern computers provide efficient support for
 //: addresses and lookups, making this a realistic feature.
-//:
-//: To recap: an address is a bookmark to some potentially large payload, and
-//: you can replace any ingredient or product with a lookup to an address of
-//: the appropriate type. But how do we get addresses to begin with? That
-//: requires a little more explanation. Once we introduce the notion of
-//: bookmarks to data, we have to think about the life cycle of a piece of
-//: data and its bookmarks (because remember, bookmarks can be copied around
-//: just like anything else). Otherwise several bad outcomes can result (and
-//: indeed *have* resulted in past languages like C):
-//:
-//:   a) You can run out of memory if you don't have a way to reclaim
-//:   data.
-//:   b) If you allow data to be reclaimed, you have to be careful not to
-//:   leave any stale addresses pointing at it. Otherwise your program might
-//:   try to lookup such an address and find something unexpected. Such
-//:   "memory corruption" problems can be very hard to track down, and they
-//:   can also be exploited to break into your computer over the network, etc.
-//:
-//: To avoid these problems, we introduce the notion of a *reference count* or
-//: refcount. The life cycle of a bit of data accessed through addresses looks
-//: like this.
-//:
-//:    We create space in computer memory for it using the 'new' instruction.
-//:    The 'new' instruction takes a type as an ingredient, allocates
-//:    sufficient space to hold that type, and returns an address (bookmark)
-//:    to the allocated space.
-//:
-//:      x:address:num <- new number:type
-//:
-//:                     +------------+
-//:          x -------> |  number    |
-//:                     +------------+
-//:
-//:    That isn't entirely accurate. Under the hood, 'new' allocates an extra
-//:    number -- the refcount:
-//:
-//:                     +------------+------------+
-//:          x -------> | refcount   |  number    |
-//:                     +------------+------------+
-//:
-//:    This probably seems like a waste of space. In practice it isn't worth
-//:    allocating individual numbers and our payload will tend to be larger,
-//:    so the picture would look more like this (zooming out a bit):
-//:
-//:                         +-------------------------+
-//:                     +---+                         |
-//:          x -------> | r |                         |
-//:                     +---+        DATA             |
-//:                         |                         |
-//:                         |                         |
-//:                         +-------------------------+
-//:
-//:    (Here 'r' denotes the refcount. It occupies a tiny amount of space
-//:    compared to the payload.)
-//:
-//:    Anyways, back to our example where the data is just a single number.
-//:    After the call to 'new', Mu's map of memory looks like this:
-//:
-//:                     +---+------------+
-//:          x -------> | 1 |  number    |
-//:                     +---+------------+
-//:
-//:    The refcount of 1 here indicates that this number has one bookmark
-//:    outstanding. If you then make a copy of x, the refcount increments:
-//:
-//:      y:address:num <- copy x
-//:
-//:          x ---+     +---+------------+
-//:               +---> | 2 |  number    |
-//:          y ---+     +---+------------+
-//:
-//:    Whether you access the payload through x or y, Mu knows how many
-//:    bookmarks are outstanding to it. When you change x or y, the refcount
-//:    transparently decrements:
-//:
-//:      x <- copy 0  # an address is just a number, you can always write 0 to it
-//:
-//:                     +---+------------+
-//:          y -------> | 1 |  number    |
-//:                     +---+------------+
-//:
-//:    The final flourish is what happens when the refcount goes down to 0: Mu
-//:    reclaims the space occupied by both refcount and payload in memory, and
-//:    they're ready to be reused by later calls to 'new'.
-//:
-//:      y <- copy 0
-//:
-//:                     +---+------------+
-//:                     | 0 |  XXXXXXX   |
-//:                     +---+------------+
-//:
-//: Using refcounts fixes both our problems a) and b) above: you can use
-//: memory for many different purposes as many times as you want without
-//: running out of memory, and you don't have to worry about ever leaving a
-//: dangling bookmark when you reclaim memory.
-//:
-//: This layer implements creating addresses using 'new'. The next few layers
-//: will flesh out the rest of the life cycle.
 
 //: todo: give 'new' a custodian ingredient. Following malloc/free is a temporary hack.
 
@@ -138,8 +40,8 @@ def main [
 ]
 +run: {1: ("address" "array" "number"), "raw": ()} <- new {number: "type"}, {5: "literal"}
 +mem: array length is 5
-# don't forget the extra location for array length, and the second extra location for the refcount
-+mem: storing 7 in location 3
+# don't forget the extra location for array length
++mem: storing 6 in location 3
 
 :(scenario dilated_reagent_with_new)
 def main [
@@ -320,8 +222,8 @@ case ALLOCATE: {
   int result = allocate(size);
   if (SIZE(current_instruction().ingredients) > 1) {
     // initialize array length
-    trace("mem") << "storing " << ingredients.at(1).at(0) << " in location " << result+/*skip refcount*/1 << end();
-    put(Memory, result+/*skip refcount*/1, ingredients.at(1).at(0));
+    trace("mem") << "storing " << ingredients.at(1).at(0) << " in location " << result << end();
+    put(Memory, result, ingredients.at(1).at(0));
   }
   products.resize(1);
   products.at(0).push_back(result);
@@ -329,8 +231,6 @@ case ALLOCATE: {
 }
 :(code)
 int allocate(int size) {
-  // include space for refcount
-  ++size;
   trace("mem") << "allocating size " << size << end();
 //?   Total_alloc += size;
 //?   ++Num_alloc;
@@ -394,8 +294,8 @@ def main [
   12:address:num/raw <- new number:type
   13:num/raw <- subtract 12:address:num/raw, 11:address:num/raw
 ]
-# size of number + refcount
-+mem: storing 2 in location 13
+# size of number
++mem: storing 1 in location 13
 
 :(scenario new_array_size)
 def main [
@@ -403,8 +303,8 @@ def main [
   2:address:num/raw <- new number:type
   3:num/raw <- subtract 2:address:num/raw, 1:address:array:num/raw
 ]
-# 5 locations for array contents + array length + refcount
-+mem: storing 7 in location 3
+# 5 locations for array contents + array length
++mem: storing 6 in location 3
 
 :(scenario new_empty_array)
 def main [
@@ -414,18 +314,18 @@ def main [
 ]
 +run: {1: ("address" "array" "number"), "raw": ()} <- new {number: "type"}, {0: "literal"}
 +mem: array length is 0
-# one location for array length, and one for the refcount
-+mem: storing 2 in location 3
+# one location for array length
++mem: storing 1 in location 3
 
 //: If a routine runs out of its initial allocation, it should allocate more.
 :(scenario new_overflow)
-% Initial_memory_per_routine = 3;  // barely enough room for point allocation below
+% Initial_memory_per_routine = 2;  // barely enough room for point allocation below
 def main [
   1:address:num/raw <- new number:type
   2:address:point/raw <- new point:type  # not enough room in initial page
 ]
-+new: routine allocated memory from 1000 to 1003
-+new: routine allocated memory from 1003 to 1006
++new: routine allocated memory from 1000 to 1002
++new: routine allocated memory from 1002 to 1004
 
 :(scenario new_without_ingredient)
 % Hide_errors = true;
diff --git a/035lookup.cc b/035lookup.cc
index 343d7900..c72a2938 100644
--- a/035lookup.cc
+++ b/035lookup.cc
@@ -1,33 +1,4 @@
-//: Go from an address to the payload it points at (skipping the refcount)
-//: using /lookup.
-//:
-//: Let's say we have this address (read the top of the address layer for
-//: details on these diagrams):
-//:
-//:                     +---+------------+
-//:          x -------> | 1 |  number    |
-//:                     +---+------------+
-//:
-//: Once you have an address you can read or modify its payload by performing
-//: a lookup:
-//:
-//:     x/lookup <- copy 34
-//:
-//: or more concisely:
-//:
-//:     *x <- copy 34
-//:
-//: This modifies not x, but the payload x points to:
-//:
-//:                     +---+------------+
-//:          x -------> | 1 |         34 |
-//:                     +---+------------+
-//:
-//: You can also read from the payload in instructions like this:
-//:
-//:     z:num <- add *x, 1
-//:
-//: After this instruction runs the value of z will be 35.
+//: Go from an address to the payload it points at using /lookup.
 //:
 //: The tests in this layer use unsafe operations so as to stay decoupled from
 //: 'new'.
@@ -35,11 +6,10 @@
 :(scenario copy_indirect)
 def main [
   1:address:num <- copy 10/unsafe
-  11:num <- copy 34
+  10:num <- copy 34
   # This loads location 1 as an address and looks up *that* location.
   2:num <- copy 1:address:num/lookup
 ]
-# 1 contains 10. Skip refcount and lookup location 11.
 +mem: storing 34 in location 2
 
 :(before "End Preprocess read_memory(x)")
@@ -52,7 +22,7 @@ def main [
   1:address:num <- copy 10/unsafe
   1:address:num/lookup <- copy 34
 ]
-+mem: storing 34 in location 11
++mem: storing 34 in location 10
 
 :(before "End Preprocess write_memory(x, data)")
 canonize(x);
@@ -102,11 +72,7 @@ void lookup_memory_core(reagent& x, bool check_for_null) {
   trace("mem") << "location " << x.value << " is " << no_scientific(get_or_insert(Memory, x.value)) << end();
   x.set_value(get_or_insert(Memory, x.value));
   drop_from_type(x, "address");
-  if (x.value) {
-    trace("mem") << "skipping refcount at " << x.value << end();
-    x.set_value(x.value+1);  // skip refcount
-  }
-  else if (check_for_null) {
+  if (check_for_null && x.value == 0) {
     if (Current_routine)
       raise << maybe(current_recipe_name()) << "tried to /lookup 0 in '" << to_original_string(current_instruction()) << "'\n" << end();
     else
@@ -115,25 +81,6 @@ void lookup_memory_core(reagent& x, bool check_for_null) {
   drop_one_lookup(x);
 }
 
-void test_lookup_address_skips_refcount() {
-  reagent x("*x:address:num");
-  x.set_value(34);  // unsafe
-  put(Memory, 34, 1000);
-  lookup_memory(x);
-  CHECK_TRACE_CONTENTS("mem: skipping refcount at 1000");
-  CHECK_EQ(x.value, 1001);
-}
-
-void test_lookup_zero_address_does_not_skip_refcount() {
-  Hide_errors = true;
-  reagent x("*x:address:num");
-  x.set_value(34);  // unsafe
-  put(Memory, 34, 0);
-  lookup_memory(x);
-  CHECK_TRACE_DOESNT_CONTAIN("mem: skipping refcount at 0");
-  CHECK_EQ(x.value, 0);
-}
-
 :(before "End Preprocess types_strictly_match(reagent to, reagent from)")
 if (!canonize_type(to)) return false;
 if (!canonize_type(from)) return false;
@@ -200,9 +147,8 @@ void drop_one_lookup(reagent& r) {
 :(scenario get_indirect)
 def main [
   1:address:point <- copy 10/unsafe
-  # 10 reserved for refcount
-  11:num <- copy 34
-  12:num <- copy 35
+  10:num <- copy 34
+  11:num <- copy 35
   2:num <- get 1:address:point/lookup, 0:offset
 ]
 +mem: storing 34 in location 2
@@ -210,20 +156,18 @@ def main [
 :(scenario get_indirect2)
 def main [
   1:address:point <- copy 10/unsafe
-  # 10 reserved for refcount
-  11:num <- copy 34
-  12:num <- copy 35
+  10:num <- copy 34
+  11:num <- copy 35
   2:address:num <- copy 20/unsafe
   2:address:num/lookup <- get 1:address:point/lookup, 0:offset
 ]
-+mem: storing 34 in location 21
++mem: storing 34 in location 20
 
 :(scenario include_nonlookup_properties)
 def main [
   1:address:point <- copy 10/unsafe
-  # 10 reserved for refcount
-  11:num <- copy 34
-  12:num <- copy 35
+  10:num <- copy 34
+  11:num <- copy 35
   2:num <- get 1:address:point/lookup/foo, 0:offset
 ]
 +mem: storing 34 in location 2
@@ -238,12 +182,11 @@ canonize(base);
 :(scenario put_indirect)
 def main [
   1:address:point <- copy 10/unsafe
-  # 10 reserved for refcount
-  11:num <- copy 34
-  12:num <- copy 35
+  10:num <- copy 34
+  11:num <- copy 35
   1:address:point/lookup <- put 1:address:point/lookup, 0:offset, 36
 ]
-+mem: storing 36 in location 11
++mem: storing 36 in location 10
 
 :(after "Update PUT base in Check")
 if (!canonize_type(base)) break;
@@ -256,9 +199,8 @@ canonize(base);
 % Hide_errors = true;
 def main [
   1:address:point <- copy 10/unsafe
-  # 10 reserved for refcount
-  11:num <- copy 34
-  12:num <- copy 35
+  10:num <- copy 34
+  11:num <- copy 35
   1:address:point <- put 1:address:point/lookup, x:offset, 36
 ]
 +error: main: product of 'put' must be first ingredient '1:address:point/lookup', but got '1:address:point'
@@ -285,11 +227,10 @@ canonize_type(product);
 
 :(scenario copy_array_indirect)
 def main [
-  # 10 reserved for refcount
-  11:array:num:3 <- create-array
-  12:num <- copy 14
-  13:num <- copy 15
-  14:num <- copy 16
+  10:array:num:3 <- create-array
+  11:num <- copy 14
+  12:num <- copy 15
+  13:num <- copy 16
   1:address:array:num <- copy 10/unsafe
   2:array:num <- copy 1:address:array:num/lookup
 ]
@@ -300,11 +241,10 @@ def main [
 
 :(scenario create_array_indirect)
 def main [
-  1000:num/raw <- copy 1  # pretend refcount
   1:address:array:num:3 <- copy 1000/unsafe  # pretend allocation
   1:address:array:num:3/lookup <- create-array
 ]
-+mem: storing 3 in location 1001
++mem: storing 3 in location 1000
 
 :(after "Update CREATE_ARRAY product in Check")
 if (!canonize_type(product)) break;
@@ -313,11 +253,10 @@ canonize(product);
 
 :(scenario index_indirect)
 def main [
-  # 10 reserved for refcount
-  11:array:num:3 <- create-array
-  12:num <- copy 14
-  13:num <- copy 15
-  14:num <- copy 16
+  10:array:num:3 <- create-array
+  11:num <- copy 14
+  12:num <- copy 15
+  13:num <- copy 16
   1:address:array:num <- copy 10/unsafe
   2:num <- index 1:address:array:num/lookup, 1
 ]
@@ -337,15 +276,14 @@ canonize(index);
 
 :(scenario put_index_indirect)
 def main [
-  # 10 reserved for refcount
-  11:array:num:3 <- create-array
-  12:num <- copy 14
-  13:num <- copy 15
-  14:num <- copy 16
+  10:array:num:3 <- create-array
+  11:num <- copy 14
+  12:num <- copy 15
+  13:num <- copy 16
   1:address:array:num <- copy 10/unsafe
   1:address:array:num/lookup <- put-index 1:address:array:num/lookup, 1, 34
 ]
-+mem: storing 34 in location 13
++mem: storing 34 in location 12
 
 :(scenario put_index_indirect_2)
 def main [
@@ -354,8 +292,7 @@ def main [
   3:num <- copy 15
   4:num <- copy 16
   5:address:num <- copy 10/unsafe
-  # 10 reserved for refcount
-  11:num <- copy 1
+  10:num <- copy 1
   1:array:num:3 <- put-index 1:array:num:3, 5:address:num/lookup, 34
 ]
 +mem: storing 34 in location 3
@@ -363,11 +300,10 @@ def main [
 :(scenario put_index_product_error_with_lookup)
 % Hide_errors = true;
 def main [
-  # 10 reserved for refcount
-  11:array:num:3 <- create-array
-  12:num <- copy 14
-  13:num <- copy 15
-  14:num <- copy 16
+  10:array:num:3 <- create-array
+  11:num <- copy 14
+  12:num <- copy 15
+  13:num <- copy 16
   1:address:array:num <- copy 10/unsafe
   1:address:array:num <- put-index 1:address:array:num/lookup, 1, 34
 ]
@@ -408,11 +344,10 @@ canonize(index);
 
 :(scenario length_indirect)
 def main [
-  # 10 reserved for refcount
-  11:array:num:3 <- create-array
-  12:num <- copy 14
-  13:num <- copy 15
-  14:num <- copy 16
+  10:array:num:3 <- create-array
+  11:num <- copy 14
+  12:num <- copy 15
+  13:num <- copy 16
   1:address:array:num <- copy 10/unsafe
   2:num <- length 1:address:array:num/lookup
 ]
@@ -425,8 +360,7 @@ canonize(array);
 
 :(scenario maybe_convert_indirect)
 def main [
-  # 10 reserved for refcount
-  11:number-or-point <- merge 0/number, 34
+  10:number-or-point <- merge 0/number, 34
   1:address:number-or-point <- copy 10/unsafe
   2:num, 3:bool <- maybe-convert 1:address:number-or-point/lookup, i:variant
 ]
@@ -435,24 +369,22 @@ def main [
 
 :(scenario maybe_convert_indirect_2)
 def main [
-  # 10 reserved for refcount
-  11:number-or-point <- merge 0/number, 34
+  10:number-or-point <- merge 0/number, 34
   1:address:number-or-point <- copy 10/unsafe
   2:address:num <- copy 20/unsafe
   2:address:num/lookup, 3:bool <- maybe-convert 1:address:number-or-point/lookup, i:variant
 ]
 +mem: storing 1 in location 3
-+mem: storing 34 in location 21
++mem: storing 34 in location 20
 
 :(scenario maybe_convert_indirect_3)
 def main [
-  # 10 reserved for refcount
-  11:number-or-point <- merge 0/number, 34
+  10:number-or-point <- merge 0/number, 34
   1:address:number-or-point <- copy 10/unsafe
   2:address:bool <- copy 20/unsafe
   3:num, 2:address:bool/lookup <- maybe-convert 1:address:number-or-point/lookup, i:variant
 ]
-+mem: storing 1 in location 21
++mem: storing 1 in location 20
 +mem: storing 34 in location 3
 
 :(before "Update MAYBE_CONVERT base in Check")
@@ -474,9 +406,8 @@ def main [
   1:address:number-or-point <- copy 10/unsafe
   1:address:number-or-point/lookup <- merge 0/number, 34
 ]
-# skip 10 for refcount
-+mem: storing 0 in location 11
-+mem: storing 34 in location 12
++mem: storing 0 in location 10
++mem: storing 34 in location 11
 
 :(before "Update size_mismatch Check for MERGE(x)
 canonize(x);
@@ -486,8 +417,7 @@ canonize(x);
 :(scenario lookup_abbreviation)
 def main [
   1:address:number <- copy 10/unsafe
-  # 10 reserved for refcount
-  11:number <- copy 34
+  10:number <- copy 34
   3:number <- copy *1:address:number
 ]
 +parse: ingredient: {1: ("address" "number"), "lookup": ()}
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";
-}
diff --git a/037abandon.cc b/037abandon.cc
index 4a1517c5..b60fd946 100644
--- a/037abandon.cc
+++ b/037abandon.cc
@@ -1,11 +1,10 @@
 //: Reclaiming memory when it's no longer used.
-//: The top of the address layer has the complete life cycle of memory.
 
 :(scenario new_reclaim)
 def main [
   1:address:num <- new number:type
   2:num <- copy 1:address:num  # because 1 will get reset during abandon below
-  1:address:num <- copy 0  # abandon
+  abandon 1:address:num
   3:address:num <- new number:type  # must be same size as abandoned memory to reuse
   4:num <- copy 3:address:num
   5:bool <- equal 2:num, 4:num
@@ -13,41 +12,40 @@ def main [
 # both allocations should have returned the same address
 +mem: storing 1 in location 5
 
-:(before "End Decrement Refcount(old_address, payload_type, payload_size)")
-if (old_refcount == 0) {
-  trace("mem") << "automatically abandoning " << old_address << end();
-  abandon(old_address, payload_type, payload_size);
-}
-
 //: When abandoning addresses we'll save them to a 'free list', segregated by size.
 
 :(before "End routine Fields")
 map<int, int> free_list;
 
-:(code)
-void abandon(int address, const type_tree* payload_type, int payload_size) {
-  trace("abandon") << "updating refcounts inside " << address << ": " << to_string(payload_type) << end();
-//?   Total_free += size;
-//?   ++Num_free;
-//?   cerr << "abandon: " << size << '\n';
-  // decrement any contained refcounts
-  if (is_mu_array(payload_type)) {
-    reagent/*local*/ element;
-    element.type = copy_array_element(payload_type);
-    int array_length = get_or_insert(Memory, address+/*skip refcount*/1);
-    assert(element.type->name != "array");
-    int element_size = size_of(element);
-    for (int i = 0;  i < array_length;  ++i) {
-      element.set_value(address + /*skip refcount and length*/2 + i*element_size);
-      decrement_any_refcounts(element);
-    }
+:(before "End Primitive Recipe Declarations")
+ABANDON,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "abandon", ABANDON);
+:(before "End Primitive Recipe Checks")
+case ABANDON: {
+  if (!inst.products.empty()) {
+    raise << maybe(get(Recipe, r).name) << "'abandon' shouldn't write to any products in '" << to_original_string(inst) << "'\n" << end();
+    break;
+  }
+  for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
+    if (!is_mu_address(inst.ingredients.at(i)))
+      raise << maybe(get(Recipe, r).name) << "ingredients of 'abandon' should be addresses, but ingredient " << i << " is '" << to_string(inst.ingredients.at(i)) << '\n' << end();
+    break;
   }
-  else if (is_mu_container(payload_type) || is_mu_exclusive_container(payload_type)) {
-    reagent tmp;
-    tmp.type = new type_tree(*payload_type);
-    tmp.set_value(address + /*skip refcount*/1);
-    decrement_any_refcounts(tmp);
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case ABANDON: {
+  for (int i = 0;  i < SIZE(current_instruction().ingredients);  ++i) {
+    reagent/*copy*/ ingredient = current_instruction().ingredients.at(i);
+    canonize(ingredient);
+    abandon(get_or_insert(Memory, ingredient.value), payload_size(ingredient));
   }
+  break;
+}
+
+:(code)
+void abandon(int address, int payload_size) {
   // clear memory
   for (int curr = address;  curr < address+payload_size;  ++curr)
     put(Memory, curr, 0);
@@ -77,7 +75,7 @@ if (get_or_insert(Current_routine->free_list, size)) {
 def main [
   1:address:num <- new number:type
   2:num <- copy 1:address:num
-  1:address:num <- copy 0  # abandon
+  abandon 1:address:num
   3:address:array:num <- new number:type, 2  # different size
   4:num <- copy 3:address:array:num
   5:bool <- equal 2:num, 4:num
@@ -89,158 +87,10 @@ def main [
 def main [
   1:address:array:num <- new number:type, 2
   2:num <- copy 1:address:array:num
-  1:address:array:num <- copy 0  # abandon
+  abandon 1:address:array:num
   3:address:array:num <- new number:type, 2  # same size
   4:num <- copy 3:address:array:num
   5:bool <- equal 2:num, 4:num
 ]
 # both calls to new returned identical addresses
 +mem: storing 1 in location 5
-
-:(scenario abandon_on_overwrite)
-def main [
-  1:address:num <- new number:type
-  # over-writing one allocation with another
-  1:address:num <- new number:type
-  1:address:num <- copy 0
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: automatically abandoning 1000
-
-:(scenario abandon_after_call)
-def main [
-  1:address:num <- new number:type
-  # passing in addresses to recipes increments refcount
-  foo 1:address:num
-  1:address:num <- copy 0
-]
-def foo [
-  2:address:num <- next-ingredient
-  # return does NOT yet decrement refcount; memory must be explicitly managed
-  2:address:num <- copy 0
-]
-+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: {2: ("address" "number")} <- copy {0: "literal"}
-+mem: decrementing refcount of 1000: 2 -> 1
-+run: {1: ("address" "number")} <- copy {0: "literal"}
-+mem: decrementing refcount of 1000: 1 -> 0
-+mem: automatically abandoning 1000
-
-:(scenario abandon_on_overwrite_array)
-def main [
-  1:num <- copy 30
-  # allocate an array
-  10:address:array:num <- new number:type, 20
-  11:num <- copy 10:address:array:num  # doesn't increment refcount
-  # allocate another array in its place, implicitly freeing the previous allocation
-  10:address:array:num <- new number:type, 25
-]
-+run: {10: ("address" "array" "number")} <- new {number: "type"}, {25: "literal"}
-# abandoned array is of old size (20, not 25)
-+abandon: saving 1000 in free-list of size 22
-
-:(scenario refcounts_abandon_address_in_container)
-# container containing an address
-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
-  1:address:num <- copy 0
-  2:address:foo <- copy 0
-]
-+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")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {1: ("address" "number")} <- copy {0: "literal"}
-+mem: decrementing refcount of 1000: 2 -> 1
-+run: {2: ("address" "foo")} <- copy {0: "literal"}
-# start abandoning container containing address
-+mem: decrementing refcount of 1002: 1 -> 0
-# nested abandon
-+mem: decrementing refcount of 1000: 1 -> 0
-+abandon: saving 1000 in free-list of size 2
-# actually abandon the container containing address
-+abandon: saving 1002 in free-list of size 2
-
-# todo: move past dilated reagent
-:(scenario refcounts_abandon_address_in_array)
-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, 1, 1:address:num
-  1:address:num <- copy 0
-  2:address:array:address:num <- copy 0
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {2: ("address" "array" "address" "number"), "lookup": ()} <- put-index {2: ("address" "array" "address" "number"), "lookup": ()}, {1: "literal"}, {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" "array" "address" "number")} <- copy {0: "literal"}
-# nested abandon
-+mem: decrementing refcount of 1000: 1 -> 0
-+abandon: saving 1000 in free-list of size 2
-
-:(scenario refcounts_abandon_address_in_container_in_array)
-# container containing an address
-container foo [
-  x:address:num
-]
-def main [
-  1:address:num <- new number:type
-  2:address:array:foo <- new foo:type, 3
-  3:foo <- merge 1:address:num
-  *2:address:array:foo <- put-index *2:address:array:foo, 1, 3:foo
-  1:address:num <- copy 0
-  3:foo <- merge 0
-  2:address:array:foo <- copy 0
-]
-+run: {1: ("address" "number")} <- new {number: "type"}
-+mem: incrementing refcount of 1000: 0 -> 1
-+run: {3: "foo"} <- merge {1: ("address" "number")}
-+mem: incrementing refcount of 1000: 1 -> 2
-+run: {2: ("address" "array" "foo"), "lookup": ()} <- put-index {2: ("address" "array" "foo"), "lookup": ()}, {1: "literal"}, {3: "foo"}
-+mem: incrementing refcount of 1000: 2 -> 3
-+run: {1: ("address" "number")} <- copy {0: "literal"}
-+mem: decrementing refcount of 1000: 3 -> 2
-+run: {3: "foo"} <- merge {0: "literal"}
-+mem: decrementing refcount of 1000: 2 -> 1
-+run: {2: ("address" "array" "foo")} <- copy {0: "literal"}
-# nested abandon
-+mem: decrementing refcount of 1000: 1 -> 0
-+abandon: saving 1000 in free-list of size 2
-
-:(scenario refcounts_abandon_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
-  1:address:array:num <- copy 0
-  2:foo <- copy 0
-]
-+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: {1: ("address" "array" "number")} <- copy {0: "literal"}
-+mem: decrementing refcount of 1000: 2 -> 1
-+run: {2: "foo"} <- copy {0: "literal"}
-+mem: decrementing refcount of 1000: 1 -> 0
-+mem: automatically abandoning 1000
-# make sure we save it in a free-list of the appropriate size
-+abandon: saving 1000 in free-list of size 5
diff --git a/038new_text.cc b/038new_text.cc
index ea73dac6..4b666f1c 100644
--- a/038new_text.cc
+++ b/038new_text.cc
@@ -41,9 +41,7 @@ int new_mu_text(const string& contents) {
 //?   Total_alloc += string_length+1;
 //?   ++Num_alloc;
   int result = allocate(string_length+/*array length*/1);
-  trace("mem") << "storing string refcount 0 in location " << result << end();
-  put(Memory, result, 0);
-  int curr_address = result+/*skip refcount*/1;
+  int curr_address = result;
   trace("mem") << "storing string length " << string_length << " in location " << curr_address << end();
   put(Memory, curr_address, string_length);
   ++curr_address;  // skip length
@@ -118,13 +116,13 @@ if (!canonize_type(x)) return false;
 
 //: Allocate more to routine when initializing a literal string
 :(scenario new_string_overflow)
-% Initial_memory_per_routine = 3;
+% Initial_memory_per_routine = 2;
 def main [
   1:address:num/raw <- new number:type
-  2:text/raw <- new [a]  # not enough room in initial page, if you take the refcount and array length into account
+  2:text/raw <- new [a]  # not enough room in initial page, if you take the array length into account
 ]
-+new: routine allocated memory from 1000 to 1003
-+new: routine allocated memory from 1003 to 1006
++new: routine allocated memory from 1000 to 1002
++new: routine allocated memory from 1002 to 1004
 
 //: helpers
 :(code)
@@ -142,7 +140,6 @@ int unicode_length(const string& s) {
 
 string read_mu_text(int address) {
   if (address == 0) return "";
-  ++address;  // skip refcount
   int length = get_or_insert(Memory, address);
   if (length == 0) return "";
   return read_mu_characters(address+1, length);
diff --git a/043space.cc b/043space.cc
index 41c7b41d..f290a0b9 100644
--- a/043space.cc
+++ b/043space.cc
@@ -17,20 +17,18 @@ put(Type_abbreviations, "space", new_type_tree("address:array:location"));
 # then local 0 is really location 12, local 1 is really location 13, and so on.
 def main [
   # pretend address:array:location; in practice we'll use 'new'
-  10:num <- copy 0  # refcount
-  11:num <- copy 5  # length
+  10:num <- copy 5  # length
   default-space:space <- copy 10/unsafe
   1:num <- copy 23
 ]
-+mem: storing 23 in location 13
++mem: storing 23 in location 12
 
 :(scenario lookup_sidesteps_default_space)
 def main [
-  # pretend pointer from outside (2000 reserved for refcount)
-  2001:num <- copy 34
+  # pretend pointer from outside
+  2000:num <- copy 34
   # pretend address:array:location; in practice we'll use 'new'
-  1000:num <- copy 0  # refcount
-  1001:num <- copy 5  # length
+  1000:num <- copy 5  # length
   # actual start of this recipe
   default-space:space <- copy 1000/unsafe
   1:&:num <- copy 2000/unsafe  # even local variables always contain raw addresses
@@ -76,7 +74,7 @@ void absolutize(reagent& x) {
 
 //: hook replaced in a later layer
 int space_base(const reagent& x) {
-  return current_call().default_space ? (current_call().default_space+/*skip refcount*/1) : 0;
+  return current_call().default_space ? current_call().default_space : 0;
 }
 
 int address(int offset, int base) {
@@ -130,12 +128,11 @@ if (x.name == "default-space") {
 
 :(scenario lookup_sidesteps_default_space_in_get)
 def main [
-  # pretend pointer to container from outside (2000 reserved for refcount)
-  2001:num <- copy 34
-  2002:num <- copy 35
+  # pretend pointer to container from outside
+  2000:num <- copy 34
+  2001:num <- copy 35
   # pretend address:array:location; in practice we'll use 'new'
-  1000:num <- copy 0  # refcount
-  1001:num <- copy 5  # length
+  1000:num <- copy 5  # length
   # actual start of this recipe
   default-space:space <- copy 1000/unsafe
   1:&:point <- copy 2000/unsafe
@@ -150,13 +147,12 @@ element.properties.push_back(pair<string, string_tree*>("raw", NULL));
 
 :(scenario lookup_sidesteps_default_space_in_index)
 def main [
-  # pretend pointer to array from outside (2000 reserved for refcount)
-  2001:num <- copy 2  # length
-  2002:num <- copy 34
-  2003:num <- copy 35
+  # pretend pointer to array from outside
+  2000:num <- copy 2  # length
+  2001:num <- copy 34
+  2002:num <- copy 35
   # pretend address:array:location; in practice we'll use 'new'
-  1000:num <- copy 0  # refcount
-  1001:num <- copy 5  # length
+  1000:num <- copy 5  # length
   # actual start of this recipe
   default-space:space <- copy 1000/unsafe
   1:&:@:num <- copy 2000/unsafe
@@ -219,215 +215,6 @@ if (x.name == "number-of-locals") {
   return;
 }
 
-//:: try to reclaim the default-space when a recipe returns
-
-:(scenario local_scope_reclaimed_on_return)
-def main [
-  1:num <- foo
-  2:num <- foo
-  3:bool <- equal 1:num, 2:num
-]
-def foo [
-  local-scope
-  result:num <- copy default-space:space
-  return result:num
-]
-# both calls to foo should have received the same default-space
-+mem: storing 1 in location 3
-
-//: todo: do this in a transform, rather than magically in the 'return' instruction
-:(after "Falling Through End Of Recipe")
-reclaim_default_space();
-:(after "Begin Return")
-reclaim_default_space();
-:(code)
-void reclaim_default_space() {
-  if (!Reclaim_memory) return;
-  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()
-      && r.steps.at(0).products.at(0).name == "default-space";
-}
-
-//:
-
-:(scenario local_scope_reclaims_locals)
-def main [
-  local-scope
-  x:text <- new [abc]
-]
-# x
-+mem: automatically abandoning 1004
-# local-scope
-+mem: automatically abandoning 1000
-
-:(before "Reclaim Space(space_address, space_recipe_ordinal, space_recipe)")
-if (get_or_insert(Memory, space_address) <= 1) {
-  set<string> reclaimed_locals;
-  trace("mem") << "trying to reclaim locals" << end();
-  // 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;
-      reclaimed_locals.insert(product.name);
-      // local variables only
-      if (has_property(product, "lookup")) continue;
-      if (has_property(product, "raw")) continue;  // tests often want to check such locations after they run
-      // End Checks For Reclaiming Locals
-      trace("mem") << "trying to reclaim local " << product.original_string << end();
-      canonize(product);
-      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)
-def main [
-  local-scope
-  x:&:num <- new num:type
-  y:space <- copy default-space:space
-]
--mem: automatically abandoning 1005
-
-//:
-
-:(scenario local_scope_does_not_reclaim_escaping_locals)
-def main [
-  1:text <- foo
-]
-def foo [
-  local-scope
-  x:text <- new [abc]
-  return x:text
-]
-# local-scope
-+mem: automatically abandoning 1000
-# x
--mem: automatically abandoning 1004
-
-:(after "Begin Return")  // before reclaiming default-space
-increment_refcounts_of_return_ingredients(ingredients);
-:(code)
-void increment_refcounts_of_return_ingredients(const vector<vector<double> >& ingredients) {
-  assert(current_instruction().operation == RETURN);
-  if (SIZE(Current_routine->calls) == 1)  // no caller to receive result
-    return;
-  const instruction& caller_instruction = to_instruction(*++Current_routine->calls.begin());
-  for (int i = 0;  i < min(SIZE(current_instruction().ingredients), SIZE(caller_instruction.products));  ++i) {
-    if (!is_dummy(caller_instruction.products.at(i))) {
-      // no need to canonize ingredient because we ignore its value
-      increment_any_refcounts(current_instruction().ingredients.at(i), ingredients.at(i));
-    }
-  }
-}
-
-//:
-
-:(scenario local_scope_frees_up_addresses_inside_containers)
-container foo [
-  x:num
-  y:&:num
-]
-def main [
-  local-scope
-  x:&:num <- new number:type
-  y:foo <- merge 34, x:&:num
-  # x and y are both cleared when main returns
-]
-+mem: automatically abandoning 1006
-
-:(scenario local_scope_returns_addresses_inside_containers)
-container foo [
-  x:num
-  y:&:num
-]
-def f [
-  local-scope
-  x:&:num <- new number:type
-  *x:&:num <- copy 12
-  y:foo <- merge 34, x:&:num
-  # since y is 'escaping' f, it should not be cleared
-  return y:foo
-]
-def main [
-  1:foo <- f
-  3:num <- get 1:foo, x:offset
-  4:&:num <- get 1:foo, y:offset
-  5:num <- copy *4:&:num
-  1:foo <- put 1:foo, y:offset, 0
-  4:&:num <- copy 0
-]
-+mem: storing 34 in location 1
-+mem: storing 1006 in location 2
-+mem: storing 34 in location 3
-# refcount of 1:foo shouldn't include any stray ones from f
-+run: {4: ("address" "number")} <- get {1: "foo"}, {y: "offset"}
-+mem: incrementing refcount of 1006: 1 -> 2
-# 1:foo wasn't abandoned/cleared
-+run: {5: "number"} <- copy {4: ("address" "number"), "lookup": ()}
-+mem: storing 12 in location 5
-+run: {1: "foo"} <- put {1: "foo"}, {y: "offset"}, {0: "literal"}
-+mem: decrementing refcount of 1006: 2 -> 1
-+run: {4: ("address" "number")} <- copy {0: "literal"}
-+mem: decrementing refcount of 1006: 1 -> 0
-+mem: automatically abandoning 1006
-
-:(scenario local_scope_claims_return_values_when_not_saved)
-def f [
-  local-scope
-  x:&:num <- new number:type
-  return x:&:num
-]
-def main [
-  f  # doesn't save result
-]
-# x reclaimed
-+mem: automatically abandoning 1004
-# f's local scope reclaimed
-+mem: automatically abandoning 1000
-
 //:: all recipes must set default-space one way or another
 
 :(before "End Globals")
@@ -446,6 +233,12 @@ void check_default_space(const recipe_ordinal r) {
   if (!starts_by_setting_default_space(caller))
     raise << caller.name << " does not seem to start with 'local-scope' or 'default-space'\n" << end();
 }
+bool starts_by_setting_default_space(const recipe& r) {
+  return !r.steps.empty()
+      && !r.steps.at(0).products.empty()
+      && r.steps.at(0).products.at(0).name == "default-space";
+}
+
 :(after "Load Mu Prelude")
 Hide_missing_default_space_errors = false;
 :(after "Test Runs")
diff --git a/044space_surround.cc b/044space_surround.cc
index f3d8319c..310672be 100644
--- a/044space_surround.cc
+++ b/044space_surround.cc
@@ -8,11 +8,9 @@
 # location 1 in space 1 refers to the space surrounding the default space, here 20.
 def main [
   # pretend address:array:location; in practice we'll use 'new'
-  10:num <- copy 0  # refcount
-  11:num <- copy 5  # length
+  10:num <- copy 5  # length
   # pretend address:array:location; in practice we'll use 'new"
-  20:num <- copy 0  # refcount
-  21:num <- copy 5  # length
+  20:num <- copy 5  # length
   # actual start of this recipe
   default-space:space <- copy 10/unsafe
   #: later layers will explain the /names: property
@@ -22,15 +20,12 @@ def main [
 ]
 def dummy [  # just for the /names: property above
 ]
-# chain space: 10 + (refcount and length) 2
-+mem: storing 20 in location 12
-# store to default space: 10 + (skip refcount and length) 2 + (index) 1
-+mem: storing 32 in location 13
-# store to chained space: (contents of location 12) 20 + (refcount and length) 2 + (index) 1
-+mem: storing 33 in location 23
-
-:(before "End Checks For Reclaiming Locals")
-if (space_index(inst.products.at(i)) > 0) continue;
+# chain space: 10 + (length) 1
++mem: storing 20 in location 11
+# store to default space: 10 + (skip length) 1 + (index) 1
++mem: storing 32 in location 12
+# store to chained space: (contents of location 12) 20 + (length) 1 + (index) 1
++mem: storing 33 in location 22
 
 //: If you think of a space as a collection of variables with a common
 //: lifetime, surrounding allows managing shorter lifetimes inside a longer
@@ -38,14 +33,14 @@ if (space_index(inst.products.at(i)) > 0) continue;
 
 :(replace{} "int space_base(const reagent& x)")
 int space_base(const reagent& x) {
-  int base = current_call().default_space ? (current_call().default_space+/*skip refcount*/1) : 0;
+  int base = current_call().default_space ? current_call().default_space : 0;
   return space_base(x, space_index(x), base);
 }
 
 int space_base(const reagent& x, int space_index, int base) {
   if (space_index == 0)
     return base;
-  return space_base(x, space_index-1, get_or_insert(Memory, base+/*skip length*/1))+/*skip refcount*/1;
+  return space_base(x, space_index-1, get_or_insert(Memory, base+/*skip length*/1));
 }
 
 int space_index(const reagent& x) {
diff --git a/069hash.cc b/069hash.cc
index 8334e80c..4400c1e8 100644
--- a/069hash.cc
+++ b/069hash.cc
@@ -57,10 +57,6 @@ size_t hash_mu_address(size_t h, reagent& r) {
   if (r.value == 0) return 0;
   trace("mem") << "location " << r.value << " is " << no_scientific(get_or_insert(Memory, r.value)) << end();
   r.set_value(get_or_insert(Memory, r.value));
-  if (r.value != 0) {
-    trace("mem") << "skipping refcount at " << r.value << end();
-    r.set_value(r.value+1);  // skip refcount
-  }
   drop_from_type(r, "address");
   return hash(h, r);
 }
@@ -230,21 +226,6 @@ def main [
 # different addresses hash to the same result as long as the values the point to do so
 +mem: storing 1 in location 5
 
-:(scenario hash_ignores_address_refcount)
-def main [
-  1:&:num <- new number:type
-  *1:&:num <- copy 34
-  2:num <- hash 1:&:num
-  return-unless 2:num
-  # increment refcount
-  3:&:num <- copy 1:&:num
-  4:num <- hash 3:&:num
-  return-unless 4:num
-  5:bool <- equal 2:num, 4:num
-]
-# hash doesn't change when refcount changes
-+mem: storing 1 in location 5
-
 :(scenario hash_container_depends_only_on_elements)
 container foo [
   x:num
diff --git a/071deep_copy.cc b/071deep_copy.cc
index ffeb00bd..f5f0f1eb 100644
--- a/071deep_copy.cc
+++ b/071deep_copy.cc
@@ -3,9 +3,6 @@
 //
 // Invariant: After a deep-copy its ingredient and result will point to no
 // common addresses.
-// Implications: Refcounts of all data pointed to by the original ingredient
-// will remain unchanged. Refcounts of all data pointed to by the (newly
-// created) result will be 1, in the absence of cycles.
 //
 // We do handle cycles in the ingredient, however. All cycles are translated
 // to new cycles in the product.
@@ -60,11 +57,6 @@ def main [
 +mem: storing 0 in location 10
 # however, the contents are identical
 +mem: storing 1 in location 11
-# the result of deep-copy gets a refcount of 1
-# (its address 202 = 200 base + 2 for temporary space inside deep-copy)
-+run: {2: ("address" "number")} <- copy {0: "literal"}
-+mem: decrementing refcount of 202: 1 -> 0
-+abandon: saving 202 in free-list of size 2
 
 :(scenario deep_copy_address_to_container)
 % Memory_allocated_until = 200;
@@ -107,8 +99,7 @@ def main [
 def main [
   # avoid all memory allocations except the implicit ones inside deep-copy, so
   # that the result is deterministic
-  100:num <- copy 1  # pretend refcount
-  101:num <- copy 3  # pretend array length
+  100:num <- copy 3  # pretend array length
   1:&:@:num <- copy 100/unsafe  # pretend allocation
   put-index *1:&:@:num, 0, 34
   put-index *1:&:@:num, 1, 35
@@ -234,7 +225,7 @@ vector<double> deep_copy(const reagent& in) {
   vector<double> result = deep_copy(in, addresses_copied, tmp);
   // reclaim Mu memory allocated for tmp
   trace(9991, "run") << "deep-copy: reclaiming temporary" << end();
-  abandon(tmp.value, payload_type(tmp.type), payload_size(tmp));
+  abandon(tmp.value, payload_size(tmp));
   // reclaim host memory allocated for tmp.type when tmp goes out of scope
   return result;
 }
@@ -263,8 +254,6 @@ int deep_copy_address(const reagent& canonized_in, map<int, int>& addresses_copi
   if (contains_key(addresses_copied, in_address)) {
     int out = get(addresses_copied, in_address);
     trace(9991, "run") << "deep-copy: copy already exists: " << out << end();
-    assert(contains_key(Memory, out));  // refcount must already be incremented
-    ++get(Memory, out);
     return out;
   }
   int out = allocate(payload_size(canonized_in));
diff --git a/072recipe.cc b/072recipe.cc
index e76db812..fc53df09 100644
--- a/072recipe.cc
+++ b/072recipe.cc
@@ -97,9 +97,6 @@ case CALL: {
   Current_routine->calls.push_front(call(ingredients.at(0).at(0)));
   ingredients.erase(ingredients.begin());  // drop the callee
   finish_call_housekeeping(call_instruction, ingredients);
-  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;
   // not done with caller
   write_products = false;
   fall_through_to_next_instruction = false;
diff --git a/073scheduler.cc b/073scheduler.cc
index a8af8853..51b3584f 100644
--- a/073scheduler.cc
+++ b/073scheduler.cc
@@ -130,7 +130,6 @@ void run_main(int argc, char* argv[]) {
     vector<double> arg;
     arg.push_back(new_mu_text(argv[i]));
     assert(get(Memory, arg.back()) == 0);
-    put(Memory, arg.back(), 1);  // update refcount
     current_call().ingredient_atoms.push_back(arg);
   }
   run(main_routine);
@@ -253,74 +252,6 @@ def f2 n:&:num [
 :(before "End is_indirect_call_with_ingredients Special-cases")
 if (r == START_RUNNING) return true;
 
-//: refcounting management when starting up new routines
-
-:(scenario start_running_immediately_updates_refcounts_of_ingredients)
-% Scheduling_interval = 1;
-def main [
-  local-scope
-  create-new-routine
-  # padding to make sure we run new-routine before returning
-  dummy:num <- copy 0
-  dummy:num <- copy 0
-]
-def create-new-routine [
-  local-scope
-  n:&:num <- new number:type
-  *n <- copy 34
-  start-running new-routine, n
-  # refcount of n decremented
-]
-def new-routine n:&:num [
-  local-scope
-  load-ingredients
-  1:num/raw <- copy *n
-]
-# check that n was successfully passed into new-routine before being reclaimed
-+mem: storing 34 in location 1
-
-//: ensure this works with indirect calls using 'call' as well
-:(scenario start_running_immediately_updates_refcounts_of_ingredients_of_indirect_calls)
-% Scheduling_interval = 1;
-def main [
-  local-scope
-  n:&:num <- new number:type
-  *n <- copy 34
-  call f1, n
-  1:num/raw <- copy *n
-]
-def f1 n:&:num [
-  local-scope
-  load-ingredients
-]
-# check that n was successfully passed into new-routine before being reclaimed
-+mem: storing 34 in location 1
-
-:(scenario next_ingredient_never_leaks_refcounts)
-def create-space n:&:num -> default-space:space [
-  default-space <- new location:type, 2
-  load-ingredients
-]
-def use-space [
-  local-scope
-  0:space/names:create-space <- next-ingredient
-  n:&:num/space:1 <- next-ingredient  # should decrement refcount
-  *n/space:1 <- copy 34
-  n2:num <- add *n/space:1, 1
-  return n2
-]
-def main [
-  local-scope
-  n:&:num <- copy 12000/unsafe  # pretend allocation with a known address
-  *n <- copy 23
-  space:space/names:create-space <- create-space n
-  n2:&:num <- copy 13000/unsafe
-  n3:num <- use-space space, n2
-]
-+run: {n: ("address" "number"), "space": "1"} <- next-ingredient
-+mem: decrementing refcount of 12000: 2 -> 1
-+run: {n: ("address" "number"), "space": "1", "lookup": ()} <- copy {34: "literal"}
-
 //: back to testing 'start-running'
 
 :(scenario start_running_returns_routine_id)
diff --git a/074wait.cc b/074wait.cc
index cf8df58c..eb17c8aa 100644
--- a/074wait.cc
+++ b/074wait.cc
@@ -263,23 +263,21 @@ def main [
 # 'get-location' can read from container address
 def main [
   1:num <- copy 10
-  # 10 reserved for refcount
-  11:num <- copy 34
-  12:num <- copy 35
+  10:num <- copy 34
+  11:num <- copy 35
   4:location <- get-location 1:&:point/lookup, 0:offset
 ]
-+mem: storing 11 in location 4
++mem: storing 10 in location 4
 
 :(scenario get_location_indirect_2)
 def main [
   1:num <- copy 10
-  # 10 reserved for refcount
-  11:num <- copy 34
-  12:num <- copy 35
+  10:num <- copy 34
+  11:num <- copy 35
   4:&:num <- copy 20/unsafe
   4:&:location/lookup <- get-location 1:&:point/lookup, 0:offset
 ]
-+mem: storing 11 in location 21
++mem: storing 10 in location 20
 
 //: allow waiting on a routine to complete
 
diff --git a/076continuation.cc b/076continuation.cc
index d9a29737..38e0185b 100644
--- a/076continuation.cc
+++ b/076continuation.cc
@@ -213,11 +213,8 @@ if (is_mu_continuation(current_instruction().ingredients.at(0))) {
   if (!contains_key(Delimited_continuation, ingredients.at(0).at(0)))
     raise << maybe(current_recipe_name()) << "no such delimited continuation " << current_instruction().ingredients.at(0).original_string << '\n' << end();
   const call_stack& new_frames = get(Delimited_continuation, ingredients.at(0).at(0)).frames;
-  for (call_stack::const_reverse_iterator p = new_frames.rbegin(); p != new_frames.rend(); ++p) {
+  for (call_stack::const_reverse_iterator p = new_frames.rbegin(); p != new_frames.rend(); ++p)
     Current_routine->calls.push_front(*p);
-    // ensure that the presence of a continuation keeps its stack frames from being reclaimed
-    increment_refcount(Current_routine->calls.front().default_space);
-  }
   if (Trace_stream) {
     Trace_stream->callstack_depth += SIZE(new_frames);
     trace("trace") << "calling delimited continuation; growing callstack depth to " << Trace_stream->callstack_depth << end();
@@ -242,25 +239,7 @@ def g [
   return-continuation-until-mark 233/mark, 34
   stash [continuation called]
 ]
-# entering main
-+mem: new alloc: 1000
-+run: {k: "continuation"}, {1: "number", "raw": ()} <- call-with-continuation-mark {233: "literal", "mark": ()}, {f: "recipe-literal"}
-# entering f
-+mem: new alloc: 1004
-# entering g
-+mem: new alloc: 1007
-# return control to main
-+run: return-continuation-until-mark {233: "literal", "mark": ()}, {34: "literal"}
-# no allocs abandoned yet
 +mem: storing 34 in location 1
-# end of main
-# make sure no memory leaks..
-+mem: trying to reclaim local k:continuation
-+mem: automatically abandoning 1007
-+mem: automatically abandoning 1004
-+mem: automatically abandoning 1000
-# ..even though we never called the continuation
--app: continuation called
 
 :(scenario continuations_continue_to_matching_mark)
 def main [
@@ -340,96 +319,6 @@ def create-yielder -> n:num [
 +mem: storing 1 in location 10
 $error: 0
 
-//: Ensure that the presence of a continuation keeps its stack frames from being reclaimed.
-
-:(scenario continuations_preserve_local_scopes)
-def main [
-  local-scope
-  k:continuation <- call-with-continuation-mark 233/mark, f
-  call k
-  return 34
-]
-def f [
-  local-scope
-  g
-]
-def g [
-  local-scope
-  return-continuation-until-mark 233/mark
-  add 1, 1
-]
-# entering main
-+mem: new alloc: 1000
-+run: {k: "continuation"} <- call-with-continuation-mark {233: "literal", "mark": ()}, {f: "recipe-literal"}
-# entering f
-+mem: new alloc: 1004
-# entering g
-+mem: new alloc: 1007
-# return control to main
-+run: return-continuation-until-mark {233: "literal", "mark": ()}
-# no allocs abandoned yet
-# finish running main
-+run: call {k: "continuation"}
-+run: add {1: "literal"}, {1: "literal"}
-+run: return {34: "literal"}
-# now k is reclaimed
-+mem: trying to reclaim local k:continuation
-# at this point all allocs in the continuation are abandoned
-+mem: automatically abandoning 1007
-+mem: automatically abandoning 1004
-# finally the alloc for main is abandoned
-+mem: automatically abandoning 1000
-
-:(before "End Increment Refcounts(canonized_x)")
-if (is_mu_continuation(canonized_x)) {
-  int continuation_id = data.at(0);
-  if (continuation_id == 0) return;
-  if (!contains_key(Delimited_continuation, continuation_id)) {
-    raise << maybe(current_recipe_name()) << "missing delimited continuation: " << canonized_x.name << '\n';
-    return;
-  }
-  delimited_continuation& curr = get(Delimited_continuation, continuation_id);
-  trace("run") << "incrementing refcount of continuation " << continuation_id << ": " << curr.nrefs << " -> " << 1+curr.nrefs << end();
-  ++curr.nrefs;
-  return;
-}
-
-:(before "End Decrement Refcounts(canonized_x)")
-if (is_mu_continuation(canonized_x)) {
-  int continuation_id = get_or_insert(Memory, canonized_x.value);
-  if (continuation_id == 0) return;
-  delimited_continuation& curr = get(Delimited_continuation, continuation_id);
-  assert(curr.nrefs > 0);
-  --curr.nrefs;
-  trace("run") << "decrementing refcount of continuation " << continuation_id << ": " << 1+curr.nrefs << " -> " << curr.nrefs << end();
-  if (curr.nrefs > 0) return;
-  trace("run") << "reclaiming continuation " << continuation_id << end();
-  // temporarily push the stack frames for the continuation to the call stack before reclaiming their spaces
-  // (because reclaim_default_space() relies on the default-space being reclaimed being at the top of the stack)
-  for (call_stack::const_iterator p = curr.frames.begin(); p != curr.frames.end(); ++p) {
-    Current_routine->calls.push_front(*p);
-    reclaim_default_space();
-    Current_routine->calls.pop_front();
-  }
-  Delimited_continuation.erase(continuation_id);
-  return;
-}
-
-:(scenario continuations_can_be_copied)
-def main [
-  local-scope
-  k:continuation <- call-with-continuation-mark 233/mark, f
-  k2:continuation <- copy k
-  # reclaiming k and k2 shouldn't delete f's local scope twice
-]
-def f [
-  local-scope
-  load-ingredients
-  return-continuation-until-mark 233/mark
-  return 0
-]
-$error: 0
-
 :(code)
 bool is_mu_continuation(reagent/*copy*/ x) {
   canonize_type(x);
diff --git a/082scenario_screen.cc b/082scenario_screen.cc
index 0c75fb6c..31cbfcc9 100644
--- a/082scenario_screen.cc
+++ b/082scenario_screen.cc
@@ -250,11 +250,11 @@ struct raw_string_stream {
 
 :(code)
 void check_screen(const string& expected_contents, const int color) {
-  int screen_location = get_or_insert(Memory, SCREEN)+/*skip refcount*/1;
+  int screen_location = get_or_insert(Memory, SCREEN);
   int data_offset = find_element_name(get(Type_ordinal, "screen"), "data", "");
   assert(data_offset >= 0);
   int screen_data_location = screen_location+data_offset;  // type: address:array:character
-  int screen_data_start = get_or_insert(Memory, screen_data_location) + /*skip refcount*/1;  // type: array:character
+  int screen_data_start = get_or_insert(Memory, screen_data_location);  // type: array:character
   int width_offset = find_element_name(get(Type_ordinal, "screen"), "num-columns", "");
   int screen_width = get_or_insert(Memory, screen_location+width_offset);
   int height_offset = find_element_name(get(Type_ordinal, "screen"), "num-rows", "");
@@ -385,7 +385,7 @@ case _DUMP_SCREEN: {
 
 :(code)
 void dump_screen() {
-  int screen_location = get_or_insert(Memory, SCREEN) + /*skip refcount*/1;
+  int screen_location = get_or_insert(Memory, SCREEN);
   int width_offset = find_element_name(get(Type_ordinal, "screen"), "num-columns", "");
   int screen_width = get_or_insert(Memory, screen_location+width_offset);
   int height_offset = find_element_name(get(Type_ordinal, "screen"), "num-rows", "");
@@ -393,7 +393,7 @@ void dump_screen() {
   int data_offset = find_element_name(get(Type_ordinal, "screen"), "data", "");
   assert(data_offset >= 0);
   int screen_data_location = screen_location+data_offset;  // type: address:array:character
-  int screen_data_start = get_or_insert(Memory, screen_data_location) + /*skip refcount*/1;  // type: array:character
+  int screen_data_start = get_or_insert(Memory, screen_data_location);  // type: array:character
   assert(get_or_insert(Memory, screen_data_start) == screen_width*screen_height);
   int top_index_offset = find_element_name(get(Type_ordinal, "screen"), "top-idx", "");
   int top_index = get_or_insert(Memory, screen_location+top_index_offset);
diff --git a/085scenario_console.cc b/085scenario_console.cc
index cb69bdb0..2c3ab4bc 100644
--- a/085scenario_console.cc
+++ b/085scenario_console.cc
@@ -58,11 +58,11 @@ case ASSUME_CONSOLE: {
   slurp_body(in, r);
   int num_events = count_events(r);
   // initialize the events like in new-fake-console
-  int size = /*space for refcount and length*/2 + num_events*size_of_event();
+  int size = /*length*/1 + num_events*size_of_event();
   int event_data_address = allocate(size);
   // store length
-  put(Memory, event_data_address+/*skip refcount*/1, num_events);
-  int curr_address = event_data_address + /*skip refcount and length*/2;
+  put(Memory, event_data_address, num_events);
+  int curr_address = event_data_address + /*skip length*/1;
   for (int i = 0;  i < SIZE(r.steps);  ++i) {
     const instruction& inst = r.steps.at(i);
     if (inst.name == "left-click") {
@@ -118,12 +118,8 @@ case ASSUME_CONSOLE: {
   int console_address = allocate(size_of_console());
   trace("mem") << "storing console in " << console_address << end();
   put(Memory, CONSOLE, console_address);
-  trace("mem") << "storing console data in " << console_address+/*skip refcount*/1+/*offset of 'data' in container 'events'*/1 << end();
-  put(Memory, console_address+/*skip refcount*/1+/*offset of 'data' in container 'events'*/1, event_data_address);
-  // increment refcount for event data
-  put(Memory, event_data_address, 1);
-  // increment refcount for console
-  put(Memory, console_address, 1);
+  trace("mem") << "storing console data in " << console_address+/*offset of 'data' in container 'events'*/1 << end();
+  put(Memory, console_address+/*offset of 'data' in container 'events'*/1, event_data_address);
   break;
 }
 
@@ -309,7 +305,7 @@ int size_of_console() {
   if (result) return result;
   assert(get(Type_ordinal, "console"));
   type_tree* type = new type_tree("console");
-  result = size_of(type)+/*refcount*/1;
+  result = size_of(type);
   delete type;
   return result;
 }
diff --git a/089scenario_filesystem.cc b/089scenario_filesystem.cc
index fa0946bf..f14534ac 100644
--- a/089scenario_filesystem.cc
+++ b/089scenario_filesystem.cc
@@ -204,31 +204,23 @@ string munge_resources_contents(const string& data, const string& filename, cons
 
 void construct_resources_object(const map<string, string>& contents) {
   int resources_data_address = allocate(SIZE(contents)*2 + /*array length*/1);
-  int curr = resources_data_address + /*skip refcount and length*/2;
+  int curr = resources_data_address + /*skip length*/1;
   for (map<string, string>::const_iterator p = contents.begin();  p != contents.end();  ++p) {
     put(Memory, curr, new_mu_text(p->first));
     trace("mem") << "storing file name " << get(Memory, curr) << " in location " << curr << end();
-    put(Memory, get(Memory, curr), 1);
-    trace("mem") << "storing refcount 1 in location " << get(Memory, curr) << end();
     ++curr;
     put(Memory, curr, new_mu_text(p->second));
     trace("mem") << "storing file contents " << get(Memory, curr) << " in location " << curr << end();
-    put(Memory, get(Memory, curr), 1);
-    trace("mem") << "storing refcount 1 in location " << get(Memory, curr) << end();
     ++curr;
   }
-  curr = resources_data_address+/*skip refcount*/1;
+  curr = resources_data_address;
   put(Memory, curr, SIZE(contents));  // size of array
   trace("mem") << "storing resources size " << get(Memory, curr) << " in location " << curr << end();
-  put(Memory, resources_data_address, 1);  // initialize refcount
-  trace("mem") << "storing refcount 1 in location " << resources_data_address << end();
   // wrap the resources data in a 'resources' object
   int resources_address = allocate(size_of_resources());
-  curr = resources_address+/*skip refcount*/1+/*offset of 'data' element*/1;
+  curr = resources_address+/*offset of 'data' element*/1;
   put(Memory, curr, resources_data_address);
   trace("mem") << "storing resources data address " << resources_data_address << " in location " << curr << end();
-  put(Memory, resources_address, 1);  // initialize refcount
-  trace("mem") << "storing refcount 1 in location " << resources_address << end();
   // save in product
   put(Memory, RESOURCES, resources_address);
   trace("mem") << "storing resources address " << resources_address << " in location " << RESOURCES << end();
@@ -240,7 +232,7 @@ int size_of_resources() {
   if (result) return result;
   assert(get(Type_ordinal, "resources"));
   type_tree* type = new type_tree("resources");
-  result = size_of(type)+/*refcount*/1;
+  result = size_of(type);
   delete type;
   return result;
 }