about summary refs log tree commit diff stats
path: root/036abandon.cc
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-05-03 10:15:17 -0700
committerKartik K. Agaram <vc@akkartik.com>2016-05-03 10:15:17 -0700
commitdc9afcbd7d7f1dcfae7b9ae659ccea4944b95a29 (patch)
tree90141172495d4f607d2ac03da912b98556b6686f /036abandon.cc
parent02909fecf6fba87604ff73fe3067e43e0ad068ee (diff)
downloadmu-dc9afcbd7d7f1dcfae7b9ae659ccea4944b95a29.tar.gz
2894
Reorganize the 'address' layer and split it up before we start greatly
expanding them to manage refcounts in nested objects.
Diffstat (limited to '036abandon.cc')
-rw-r--r--036abandon.cc143
1 files changed, 143 insertions, 0 deletions
diff --git a/036abandon.cc b/036abandon.cc
new file mode 100644
index 00000000..22d93ec9
--- /dev/null
+++ b/036abandon.cc
@@ -0,0 +1,143 @@
+//: Reclaiming memory when it's no longer used.
+//: The top of layer 34 has the complete life cycle of memory.
+
+:(scenario new_reclaim)
+def main [
+  1:address:number <- new number:type
+  2:number <- copy 1:address:number  # because 1 will get reset during abandon below
+  1:address:number <- copy 0  # abandon
+  3:address:number <- new number:type  # must be same size as abandoned memory to reuse
+  4:boolean <- equal 2:number, 3:address:number
+]
+# both allocations should have returned the same address
++mem: storing 1 in location 4
+
+:(before "End Update Reference Count")
+// abandon old address if necessary
+// do this after all refcount updates are done, just in case old and new are identical
+assert(old_address >= 0);
+if (old_address == 0) return;
+if (get_or_insert(Memory, old_address) < 0) {
+  tb_shutdown();
+  DUMP("");
+  cerr << "Negative refcount: " << old_address << ' ' << get_or_insert(Memory, old_address) << '\n';
+  exit(0);
+}
+if (get_or_insert(Memory, old_address) > 0) return;
+// old_address has a 0 refcount
+// lookup_memory without drop_one_lookup {
+trace(9999, "mem") << "automatically abandoning " << old_address << end();
+trace(9999, "mem") << "computing size to abandon at " << x.value << end();
+x.set_value(old_address+/*skip refcount*/1);
+drop_from_type(x, "address");
+// }
+abandon(old_address, size_of(x)+/*refcount*/1);
+
+//: 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, int size) {
+  trace(9999, "abandon") << "saving in free-list of size " << size << end();
+//?   Total_free += size;
+//?   Num_free++;
+//?   cerr << "abandon: " << size << '\n';
+  // clear memory
+  for (int curr = address; curr < address+size; ++curr)
+    put(Memory, curr, 0);
+  // append existing free list to address
+  put(Memory, address, get_or_insert(Current_routine->free_list, size));
+  put(Current_routine->free_list, size, address);
+}
+
+:(before "ensure_space(size)" following "case ALLOCATE")
+if (get_or_insert(Current_routine->free_list, size)) {
+  trace(9999, "abandon") << "picking up space from free-list of size " << size << end();
+  int result = get_or_insert(Current_routine->free_list, size);
+  trace(9999, "mem") << "new alloc from free list: " << result << end();
+  put(Current_routine->free_list, size, get_or_insert(Memory, result));
+  for (int curr = result+1; curr < result+size; ++curr) {
+    if (get_or_insert(Memory, curr) != 0) {
+      raise << maybe(current_recipe_name()) << "memory in free list was not zeroed out: " << curr << '/' << result << "; somebody wrote to us after free!!!\n" << end();
+      break;  // always fatal
+    }
+  }
+  if (SIZE(current_instruction().ingredients) > 1)
+    put(Memory, result+/*skip refcount*/1, ingredients.at(1).at(0));
+  else
+    put(Memory, result, 0);
+  products.resize(1);
+  products.at(0).push_back(result);
+  break;
+}
+
+:(scenario new_differing_size_no_reclaim)
+def main [
+  1:address:number <- new number:type
+  2:number <- copy 1:address:number
+  1:address:number <- copy 0  # abandon
+  3:address:array:number <- new number:type, 2  # different size
+  4:boolean <- equal 2:number, 3:address:array:number
+]
+# no reuse
++mem: storing 0 in location 4
+
+:(scenario new_reclaim_array)
+def main [
+  1:address:array:number <- new number:type, 2
+  2:number <- copy 1:address:array:number
+  1:address:array:number <- copy 0  # abandon
+  3:address:array:number <- new number:type, 2  # same size
+  4:boolean <- equal 2:number, 3:address:array:number
+]
+# reuse
++mem: storing 1 in location 4
+
+:(scenario refcounts_2)
+def main [
+  1:address:number <- new number:type
+  # over-writing one allocation with another
+  1:address:number <- new number:type
+  1:address:number <- 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 refcounts_3)
+def main [
+  1:address:number <- new number:type
+  # passing in addresses to recipes increments refcount
+  foo 1:address:number
+  1:address:number <- copy 0
+]
+def foo [
+  2:address:number <- next-ingredient
+  # return does NOT yet decrement refcount; memory must be explicitly managed
+  2:address:number <- copy 0
+]
++run: {1: ("address" "number")} <- new {number: "type"}
++mem: incrementing refcount of 1000: 0 -> 1
++run: {2: ("address" "number")} <- next-ingredient
++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 refcounts_array)
+def main [
+  1:number <- copy 30
+  # allocate an array
+  10:address:array:number <- new number:type, 20
+  11:number <- copy 10:address:array:number  # doesn't increment refcount
+  # allocate another array in its place, implicitly freeing the previous allocation
+  10:address:array:number <- 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 in free-list of size 22