      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 int payload_size(reagent/*copy*/ x) {
     59   x.properties.push_back(pair<string, string_tree*>("lookup", NULL));
     60   lookup_memory_core(x, /*check_for_null*/false);
     61   return size_of(x);
     62 }
     63
     64 :(after "Allocate Special-cases")
     65 if (get_or_insert(Current_routine->free_list, size)) {
     66   trace("abandon") << "picking up space from free-list of size " << size << end();
     67   int result = get_or_insert(Current_routine->free_list, size);
     68   trace("mem") << "new alloc from free list: " << result << end();
     69   put(Current_routine->free_list, size, get_or_insert(Memory, result));
     70   put(Memory, result, 0);
     71   for (int curr = result;  curr < result+size;  ++curr) {
     72     if (get_or_insert(Memory, curr) != 0) {
     73       raise << maybe(current_recipe_name()) << "memory in free list was not zeroed out: " << curr << '/' << result << "; somebody wrote to us after free!!!\n" << end();
     74       break;  // always fatal
     75     }
     76   }
     77   return result;
     78 }
     79
     80 :(scenario new_differing_size_no_reclaim)
     81 def main [
     82   1:address:num <- new number:type
     83   2:num <- copy 1:address:num
     84   abandon 1:address:num
     85   3:address:array:num <- new number:type, 2  # different size
     86   4:num <- copy 3:address:array:num
     87   5:bool <- equal 2:num, 4:num
     88 ]
     89 # no reuse
     90 +mem: storing 0 in location 5
     91
     92 :(scenario new_reclaim_array)
     93 def main [
     94   1:address:array:num <- new number:type, 2
     95   2:num <- copy 1:address:array:num
     96   abandon 1:address:array:num
     97   3:address:array:num <- new number:type, 2  # same size
     98   4:num <- copy 3:address:array:num
     99   5:bool <- equal 2:num, 4:num
    100 ]
    101 # both calls to new returned identical addresses
    102 +mem: storing 1 in location 5