1 //: Reclaiming memory when it's no longer used.
 2 
 3 :(scenario new_reclaim)
 4 def main [
 5   1:address:num <- new number:type
 6   2:num <- copy 1:address:num  # because 1 will get reset during abandon below
 7   abandon 1:address:num
 8   3:address:num <- new number:type  # must be same size as abandoned memory to reuse
 9   4:num <- copy 3:address:num
10   5:bool <- equal 2:num, 4:num
11 ]
12 # both allocations should have returned the same address
13 +mem: storing 1 in location 5
14 
15 //: When abandoning addresses we'll save them to a 'free list', segregated by size.
16 
17 :(before "End routine Fields")
18 map<int, int> free_list;
19 
20 :(before "End Primitive Recipe Declarations")
21 ABANDON,
22 :(before "End Primitive Recipe Numbers")
23 put(Recipe_ordinal, "abandon", ABANDON);
24 :(before "End Primitive Recipe Checks")
25 case ABANDON: {
26   if (!inst.products.empty()) {
27     raise << maybe(get(Recipe, r).name) << "'abandon' shouldn't write to any products in '" << to_original_string(inst) << "'\n" << end();
28     break;
29   }
30   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
31     if (!is_mu_address(inst.ingredients.at(i)))
32       raise << maybe(get(Recipe, r).name) << "ingredients of 'abandon' should be addresses, but ingredient " << i << " is '" << to_string(inst.ingredients.at(i)) << '\n' << end();
33     break;
34   }
35   break;
36 }
37 :(before "End Primitive Recipe Implementations")
38 case ABANDON: {
39   for (int i = 0;  i < SIZE(current_instruction().ingredients);  ++i) {
40     reagent/*copy*/ ingredient = current_instruction().ingredients.at(i);
41     canonize(ingredient);
42     abandon(get_or_insert(Memory, ingredient.value), payload_size(ingredient));
43   }
44   break;
45 }
46 
47 :(code)
48 void abandon(int address, int payload_size) {
49   // clear memory
50   for (int curr = address;  curr < address+payload_size;  ++curr)
51     put(Memory, curr, 0);
52   // append existing free list to address
53   trace("abandon") << "saving " << address << " in free-list of size " << payload_size << end();
54   put(Memory, address, get_or_insert(Current_routine->free_list, payload_size));
55   put(Current_routine->free_list, payload_size, address);
56 }
57 
58 :(after "Allocate Special-cases")
59 if (get_or_insert(Current_routine->free_list, size)) {
60   trace("abandon") << "picking up space from free-list of size " << size << end();
61   int result = get_or_insert(Current_routine->free_list, size);
62   trace("mem") << "new alloc from free list: " << result << end();
63   put(Current_routine->free_list, size, get_or_insert(Memory, result));
64   put(Memory, result, 0);
65   for (int curr = result;  curr < result+size;  ++curr) {
66     if (get_or_insert(Memory, curr) != 0) {
67       raise << maybe(current_recipe_name()) << "memory in free list was not zeroed out: " << curr << '/' << result << "; somebody wrote to us after free!!!\n" << end();
68       break;  // always fatal
69     }
70   }
71   return result;
72 }
73 
74 :(scenario new_differing_size_no_reclaim)
75 def main [
76   1:address:num <- new number:type
77   2:num <- copy 1:address:num
78   abandon 1:address:num
79   3:address:array:num <- new number:type, 2  # different size
80   4:num <- copy 3:address:array:num
81   5:bool <- equal 2:num, 4:num
82 ]
83 # no reuse
84 +mem: storing 0 in location 5
85 
86 :(scenario new_reclaim_array)
87 def main [
88   1:address:array:num <- new number:type, 2
89   2:num <- copy 1:address:array:num
90   abandon 1:address:array:num
91   3:address:array:num <- new number:type, 2  # same size
92   4:num <- copy 3:address:array:num
93   5:bool <- equal 2:num, 4:num
94 ]
95 # both calls to new returned identical addresses
96 +mem: storing 1 in location 5