From 204dae921abff0c70e017215bb3c91fa6ca11aff Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Mon, 26 Dec 2016 11:44:14 -0800 Subject: 3710 Turns out we don't need to explicitly add anchors for each line. Vim's TOhtml has magic for that out of the box. --- html/036refcount.cc.html | 2028 +++++++++++++++++++++++----------------------- 1 file changed, 1014 insertions(+), 1014 deletions(-) (limited to 'html/036refcount.cc.html') diff --git a/html/036refcount.cc.html b/html/036refcount.cc.html index 60b63505..44ea40d6 100644 --- a/html/036refcount.cc.html +++ b/html/036refcount.cc.html @@ -59,1020 +59,1020 @@ if ('onhashchange' in window) {
-   1 //: Update refcounts when copying addresses.
-   2 //: The top of the address layer has more on refcounts.
-   3 
-   4 :(scenario refcounts)
-   5 def main [
-   6   1:address:num <- copy 1000/unsafe
-   7   2:address:num <- copy 1:address:num
-   8   1:address:num <- copy 0
-   9   2:address:num <- copy 0
-  10 ]
-  11 +run: {1: ("address" "number")} <- copy {1000: "literal", "unsafe": ()}
-  12 +mem: incrementing refcount of 1000: 0 -> 1
-  13 +run: {2: ("address" "number")} <- copy {1: ("address" "number")}
-  14 +mem: incrementing refcount of 1000: 1 -> 2
-  15 +run: {1: ("address" "number")} <- copy {0: "literal"}
-  16 +mem: decrementing refcount of 1000: 2 -> 1
-  17 +run: {2: ("address" "number")} <- copy {0: "literal"}
-  18 +mem: decrementing refcount of 1000: 1 -> 0
-  19 
-  20 :(before "End Globals")
-  21 //: escape hatch for a later layer
-  22 bool Update_refcounts_in_write_memory = true;
-  23 
-  24 :(before "End write_memory(x) Special-cases")
-  25 if (Update_refcounts_in_write_memory)
-  26   update_any_refcounts(x, data);
-  27 
-  28 :(code)
-  29 void update_any_refcounts(const reagent& canonized_x, const vector<double>& data) {
-  30   increment_any_refcounts(canonized_x, data);  // increment first so we don't reclaim on x <- copy x
-  31   decrement_any_refcounts(canonized_x);
-  32 }
-  33 
-  34 void increment_any_refcounts(const reagent& canonized_x, const vector<double>& data) {
-  35   if (is_mu_address(canonized_x)) {
-  36     assert(scalar(data));
-  37     assert(!canonized_x.metadata.size);
-  38     increment_refcount(data.at(0));
-  39   }
-  40   // End Increment Refcounts(canonized_x)
-  41 }
-  42 
-  43 void increment_refcount(int new_address) {
-  44   assert(new_address >= 0);
-  45   if (new_address == 0) return;
-  46   int new_refcount = get_or_insert(Memory, new_address);
-  47   trace(9999, "mem") << "incrementing refcount of " << new_address << ": " << new_refcount << " -> " << new_refcount+1 << end();
-  48   put(Memory, new_address, new_refcount+1);
-  49 }
-  50 
-  51 void decrement_any_refcounts(const reagent& canonized_x) {
-  52   if (is_mu_address(canonized_x)) {
-  53     assert(canonized_x.value);
-  54     assert(!canonized_x.metadata.size);
-  55     decrement_refcount(get_or_insert(Memory, canonized_x.value), payload_type(canonized_x.type), payload_size(canonized_x));
-  56   }
-  57   // End Decrement Refcounts(canonized_x)
-  58 }
-  59 
-  60 void decrement_refcount(int old_address, const type_tree* payload_type, int payload_size) {
-  61   assert(old_address >= 0);
-  62   if (old_address) {
-  63     int old_refcount = get_or_insert(Memory, old_address);
-  64     trace(9999, "mem") << "decrementing refcount of " << old_address << ": " << old_refcount << " -> " << old_refcount-1 << end();
-  65     --old_refcount;
-  66     put(Memory, old_address, old_refcount);
-  67     if (old_refcount < 0) {
-  68       tb_shutdown();
-  69       cerr << "Negative refcount!!! " << old_address << ' ' << old_refcount << '\n';
-  70       if (Trace_stream) {
-  71         cerr << "Saving trace to last_trace.\n";
-  72         ofstream fout("last_trace");
-  73         fout << Trace_stream->readable_contents("");
-  74         fout.close();
-  75       }
-  76       exit(0);
-  77     }
-  78     // End Decrement Refcount(old_address, payload_type, payload_size)
-  79   }
-  80 }
-  81 
-  82 int payload_size(reagent/*copy*/ x) {
-  83   x.properties.push_back(pair<string, string_tree*>("lookup", NULL));
-  84   lookup_memory_core(x, /*check for nulls*/false);
-  85   return size_of(x) + /*refcount*/1;
-  86 }
-  87 
-  88 :(scenario refcounts_reflexive)
-  89 def main [
-  90   1:address:num <- new number:type
-  91   # idempotent copies leave refcount unchanged
-  92   1:address:num <- copy 1:address:num
-  93 ]
-  94 +run: {1: ("address" "number")} <- new {number: "type"}
-  95 +mem: incrementing refcount of 1000: 0 -> 1
-  96 +run: {1: ("address" "number")} <- copy {1: ("address" "number")}
-  97 +mem: incrementing refcount of 1000: 1 -> 2
-  98 +mem: decrementing refcount of 1000: 2 -> 1
-  99 
- 100 :(scenario refcounts_call)
- 101 def main [
- 102   1:address:num <- new number:type
- 103   # passing in addresses to recipes increments refcount
- 104   foo 1:address:num
- 105   # return does NOT yet decrement refcount; memory must be explicitly managed
- 106   1:address:num <- new number:type
- 107 ]
- 108 def foo [
- 109   2:address:num <- next-ingredient
- 110 ]
- 111 +run: {1: ("address" "number")} <- new {number: "type"}
- 112 +mem: incrementing refcount of 1000: 0 -> 1
- 113 +run: foo {1: ("address" "number")}
- 114 # leave ambiguous precisely when the next increment happens
- 115 +mem: incrementing refcount of 1000: 1 -> 2
- 116 +run: {1: ("address" "number")} <- new {number: "type"}
- 117 +mem: decrementing refcount of 1000: 2 -> 1
- 118 
- 119 //: fix up any instructions that don't follow the usual flow of read_memory
- 120 //: before the RUN switch, and write_memory after
- 121 
- 122 :(scenario refcounts_put)
- 123 container foo [
- 124   x:address:num
- 125 ]
- 126 def main [
- 127   1:address:num <- new number:type
- 128   2:address:foo <- new foo:type
- 129   *2:address:foo <- put *2:address:foo, x:offset, 1:address:num
- 130 ]
- 131 +run: {1: ("address" "number")} <- new {number: "type"}
- 132 +mem: incrementing refcount of 1000: 0 -> 1
- 133 +run: {2: ("address" "foo")} <- new {foo: "type"}
- 134 +mem: incrementing refcount of 1002: 0 -> 1
- 135 +run: {2: ("address" "foo"), "lookup": ()} <- put {2: ("address" "foo"), "lookup": ()}, {x: "offset"}, {1: ("address" "number")}
- 136 # put increments refcount
- 137 +mem: incrementing refcount of 1000: 1 -> 2
- 138 
- 139 :(after "Write Memory in PUT in Run")
- 140 reagent/*copy*/ element = element_type(base.type, offset);
- 141 assert(!has_property(element, "lookup"));
- 142 element.set_value(address);
- 143 update_any_refcounts(element, ingredients.at(2));
- 144 
- 145 :(scenario refcounts_put_index)
- 146 def main [
- 147   1:address:num <- new number:type
- 148   2:address:array:address:num <- new {(address number): type}, 3
- 149   *2:address:array:address:num <- put-index *2:address:array:address:num, 0, 1:address:num
- 150 ]
- 151 +run: {1: ("address" "number")} <- new {number: "type"}
- 152 +mem: incrementing refcount of 1000: 0 -> 1
- 153 +run: {2: ("address" "array" "address" "number")} <- new {(address number): "type"}, {3: "literal"}
- 154 +mem: incrementing refcount of 1002: 0 -> 1
- 155 +run: {2: ("address" "array" "address" "number"), "lookup": ()} <- put-index {2: ("address" "array" "address" "number"), "lookup": ()}, {0: "literal"}, {1: ("address" "number")}
- 156 # put-index increments refcount
- 157 +mem: incrementing refcount of 1000: 1 -> 2
- 158 
- 159 :(after "Write Memory in PUT_INDEX in Run")
- 160 reagent/*local*/ element;
- 161 element.set_value(address);
- 162 element.type = copy_array_element(base.type);
- 163 update_any_refcounts(element, value);
- 164 
- 165 :(scenario refcounts_maybe_convert)
- 166 exclusive-container foo [
- 167   x:num
- 168   p:address:num
- 169 ]
- 170 def main [
- 171   1:address:num <- new number:type
- 172   2:foo <- merge 1/p, 1:address:num
- 173   4:address:num, 5:bool <- maybe-convert 2:foo, 1:variant/p
- 174 ]
- 175 +run: {1: ("address" "number")} <- new {number: "type"}
- 176 +mem: incrementing refcount of 1000: 0 -> 1
- 177 # merging in an address increments refcount
- 178 +run: {2: "foo"} <- merge {1: "literal", "p": ()}, {1: ("address" "number")}
- 179 +mem: incrementing refcount of 1000: 1 -> 2
- 180 +run: {4: ("address" "number")}, {5: "boolean"} <- maybe-convert {2: "foo"}, {1: "variant", "p": ()}
- 181 # maybe-convert increments refcount on success
- 182 +mem: incrementing refcount of 1000: 2 -> 3
- 183 
- 184 :(after "Write Memory in Successful MAYBE_CONVERT")
- 185 // todo: double-check data here as well
- 186 vector<double> data;
- 187 for (int i = 0;  i < size_of(product);  ++i)
- 188   data.push_back(get_or_insert(Memory, base_address+/*skip tag*/1+i));
- 189 update_any_refcounts(product, data);
- 190 
- 191 //:: manage refcounts in instructions that copy multiple locations at a time
- 192 
- 193 :(scenario refcounts_copy_nested)
- 194 container foo [
- 195   x:address:num  # address inside container
- 196 ]
- 197 def main [
- 198   1:address:num <- new number:type
- 199   2:address:foo <- new foo:type
- 200   *2:address:foo <- put *2:address:foo, x:offset, 1:address:num
- 201   3:foo <- copy *2:address:foo
- 202 ]
- 203 +transform: compute address offsets for container foo
- 204 +transform: checking container foo, element 0
- 205 +transform: address at offset 0
- 206 +run: {1: ("address" "number")} <- new {number: "type"}
- 207 +mem: incrementing refcount of 1000: 0 -> 1
- 208 +run: {2: ("address" "foo"), "lookup": ()} <- put {2: ("address" "foo"), "lookup": ()}, {x: "offset"}, {1: ("address" "number")}
- 209 +mem: incrementing refcount of 1000: 1 -> 2
- 210 # copying a container increments refcounts of any contained addresses
- 211 +run: {3: "foo"} <- copy {2: ("address" "foo"), "lookup": ()}
- 212 +mem: incrementing refcount of 1000: 2 -> 3
- 213 
- 214 :(after "End type_tree Definition")
- 215 struct address_element_info {
- 216   int offset;  // where inside a container type (after flattening nested containers!) the address lies
- 217   const type_tree* payload_type;  // all the information we need to compute sizes of items inside an address inside a container. Doesn't need to be a full-scale reagent, since an address inside a container can never be an array, and arrays are the only type that need to know their location to compute their size.
- 218   address_element_info(int o, const type_tree* p) {
- 219     offset = o;
- 220     payload_type = p;
- 221   }
- 222   address_element_info(const address_element_info& other) {
- 223     offset = other.offset;
- 224     payload_type = other.payload_type ? new type_tree(*other.payload_type) : NULL;
- 225   }
- 226   ~address_element_info() {
- 227     if (payload_type) {
- 228       delete payload_type;
- 229       payload_type = NULL;
- 230     }
- 231   }
- 232   address_element_info& operator=(const address_element_info& other) {
- 233     offset = other.offset;
- 234     if (payload_type) delete payload_type;
- 235     payload_type = other.payload_type ? new type_tree(*other.payload_type) : NULL;
- 236     return *this;
- 237   }
- 238 };
- 239 
- 240 // For exclusive containers we might sometimes have an address at some offset
- 241 // if some other offset has a specific tag. This struct encapsulates such
- 242 // guards.
- 243 struct tag_condition_info {
- 244   int offset;
- 245   int tag;
- 246   tag_condition_info(int o, int t) :offset(o), tag(t) {}
- 247 };
- 248 
- 249 :(before "End container_metadata Fields")
- 250 // a list of facts of the form:
- 251 //
- 252 //  IF offset o1 has tag t2 AND offset o2 has tag t2 AND .., THEN
- 253 //    for all address_element_infos:
- 254 //      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)
- 255 map<set<tag_condition_info>, set<address_element_info> > address;
- 256 :(code)
- 257 bool operator<(const set<tag_condition_info>& a, const set<tag_condition_info>& b) {
- 258   if (a.size() != b.size()) return a.size() < b.size();
- 259   for (set<tag_condition_info>::const_iterator pa = a.begin(), pb = b.begin();  pa != a.end();  ++pa, ++pb) {
- 260     if (pa->offset != pb->offset) return pa->offset < pb->offset;
- 261     if (pa->tag != pb->tag) return pa->tag < pb->tag;
- 262   }
- 263   return false;  // equal
- 264 }
- 265 bool operator<(const tag_condition_info& a, const tag_condition_info& b) {
- 266   if (a.offset != b.offset) return a.offset < b.offset;
- 267   if (a.tag != b.tag) return a.tag < b.tag;
- 268   return false;  // equal
- 269 }
- 270 bool operator<(const set<address_element_info>& a, const set<address_element_info>& b) {
- 271   if (a.size() != b.size()) return a.size() < b.size();
- 272   for (set<address_element_info>::const_iterator pa = a.begin(), pb = b.begin();  pa != a.end();  ++pa, ++pb) {
- 273     if (pa->offset != pb->offset) return pa->offset < pb->offset;
- 274   }
- 275   return false;  // equal
- 276 }
- 277 bool operator<(const address_element_info& a, const address_element_info& b) {
- 278   if (a.offset != b.offset) return a.offset < b.offset;
- 279   return false;  // equal
- 280 }
- 281 
- 282 //: populate metadata.address in a separate transform, because it requires
- 283 //: already knowing the sizes of all types
- 284 
- 285 :(after "Transform.push_back(compute_container_sizes)")
- 286 Transform.push_back(compute_container_address_offsets);  // idempotent
- 287 :(code)
- 288 void compute_container_address_offsets(const recipe_ordinal r) {
- 289   recipe& caller = get(Recipe, r);
- 290   trace(9992, "transform") << "--- compute address offsets for " << caller.name << end();
- 291   for (int i = 0;  i < SIZE(caller.steps);  ++i) {
- 292     instruction& inst = caller.steps.at(i);
- 293     trace(9993, "transform") << "- compute address offsets for " << to_string(inst) << end();
- 294     for (int i = 0;  i < SIZE(inst.ingredients);  ++i)
- 295       compute_container_address_offsets(inst.ingredients.at(i), " in '"+to_original_string(inst)+"'");
- 296     for (int i = 0;  i < SIZE(inst.products);  ++i)
- 297       compute_container_address_offsets(inst.products.at(i), " in '"+to_original_string(inst)+"'");
- 298   }
- 299 }
- 300 
- 301 void compute_container_address_offsets(reagent& r, const string& location_for_error_messages) {
- 302   if (is_literal(r) || is_dummy(r)) return;
- 303   compute_container_address_offsets(r.type, location_for_error_messages);
- 304   if (contains_key(Container_metadata, r.type))
- 305     r.metadata = get(Container_metadata, r.type);
- 306 }
- 307 
- 308 // the recursive structure of this function needs to exactly match
- 309 // compute_container_sizes
- 310 void compute_container_address_offsets(const type_tree* type, const string& location_for_error_messages) {
- 311   if (!type) return;
- 312   if (!type->atom) {
- 313     if (!type->left->atom) {
- 314       raise << "invalid type " << to_string(type) << location_for_error_messages << '\n' << end();
- 315       return;
- 316     }
- 317     if (type->left->name == "address")
- 318       compute_container_address_offsets(payload_type(type), location_for_error_messages);
- 319     else if (type->left->name == "array")
- 320       compute_container_address_offsets(array_element(type), location_for_error_messages);
- 321     // End compute_container_address_offsets Non-atom Special-cases
- 322   }
- 323   const type_tree* base_type = type;
- 324   // Update base_type in compute_container_address_offsets
- 325   if (!contains_key(Type, base_type->value)) return;  // error raised elsewhere
- 326   type_info& info = get(Type, base_type->value);
- 327   if (info.kind == CONTAINER) {
- 328     compute_container_address_offsets(info, type, location_for_error_messages);
- 329   }
- 330   if (info.kind == EXCLUSIVE_CONTAINER) {
- 331     compute_exclusive_container_address_offsets(info, type, location_for_error_messages);
- 332   }
- 333 }
- 334 
- 335 void compute_container_address_offsets(const type_info& container_info, const type_tree* full_type, const string& location_for_error_messages) {
- 336   container_metadata& metadata = get(Container_metadata, full_type);
- 337   if (!metadata.address.empty()) return;
- 338   trace(9994, "transform") << "compute address offsets for container " << container_info.name << end();
- 339   append_addresses(0, full_type, metadata.address, set<tag_condition_info>(), location_for_error_messages);
- 340 }
- 341 
- 342 void compute_exclusive_container_address_offsets(const type_info& exclusive_container_info, const type_tree* full_type, const string& location_for_error_messages) {
- 343   container_metadata& metadata = get(Container_metadata, full_type);
- 344   trace(9994, "transform") << "compute address offsets for exclusive container " << exclusive_container_info.name << end();
- 345   for (int tag = 0;  tag < SIZE(exclusive_container_info.elements);  ++tag) {
- 346     set<tag_condition_info> key;
- 347     key.insert(tag_condition_info(/*tag is at offset*/0, tag));
- 348     append_addresses(/*skip tag offset*/1, variant_type(full_type, tag).type, metadata.address, key, location_for_error_messages);
- 349   }
- 350 }
- 351 
- 352 void append_addresses(int base_offset, const type_tree* type, map<set<tag_condition_info>, set<address_element_info> >& out, const set<tag_condition_info>& key, const string& location_for_error_messages) {
- 353   if (is_mu_address(type)) {
- 354     get_or_insert(out, key).insert(address_element_info(base_offset, new type_tree(*payload_type(type))));
- 355     return;
- 356   }
- 357   const type_tree* base_type = type;
- 358   // Update base_type in append_container_address_offsets
- 359   const type_info& info = get(Type, base_type->value);
- 360   if (info.kind == CONTAINER) {
- 361     for (int curr_index = 0, curr_offset = base_offset;  curr_index < SIZE(info.elements);  ++curr_index) {
- 362       trace(9993, "transform") << "checking container " << base_type->name << ", element " << curr_index << end();
- 363       reagent/*copy*/ element = element_type(type, curr_index);  // not base_type
- 364       // Compute Container Address Offset(element)
- 365       if (is_mu_address(element)) {
- 366         trace(9993, "transform") << "address at offset " << curr_offset << end();
- 367         get_or_insert(out, key).insert(address_element_info(curr_offset, new type_tree(*payload_type(element.type))));
- 368         ++curr_offset;
- 369       }
- 370       else if (is_mu_array(element)) {
- 371         curr_offset += /*array length*/1;
- 372         const type_tree* array_element_type = array_element(element.type);
- 373         int array_element_size = size_of(array_element_type);
- 374         for (int i = 0; i < static_array_length(element.type); ++i) {
- 375           append_addresses(curr_offset, array_element_type, out, key, location_for_error_messages);
- 376           curr_offset += array_element_size;
- 377         }
- 378       }
- 379       else if (is_mu_container(element)) {
- 380         append_addresses(curr_offset, element.type, out, key, location_for_error_messages);
- 381         curr_offset += size_of(element);
- 382       }
- 383       else if (is_mu_exclusive_container(element)) {
- 384         const type_tree* element_base_type = element.type;
- 385         // Update element_base_type For Exclusive Container in append_addresses
- 386         const type_info& element_info = get(Type, element_base_type->value);
- 387         for (int tag = 0;  tag < SIZE(element_info.elements);  ++tag) {
- 388           set<tag_condition_info> new_key = key;
- 389           new_key.insert(tag_condition_info(curr_offset, tag));
- 390           if (!contains_key(out, new_key))
- 391             append_addresses(curr_offset+/*skip tag*/1, variant_type(element.type, tag).type, out, new_key, location_for_error_messages);
- 392         }
- 393         curr_offset += size_of(element);
- 394       }
- 395       else {
- 396         // non-address primitive
- 397         ++curr_offset;
- 398       }
- 399     }
- 400   }
- 401   else if (info.kind == EXCLUSIVE_CONTAINER) {
- 402     for (int tag = 0;  tag < SIZE(info.elements);  ++tag) {
- 403       set<tag_condition_info> new_key = key;
- 404       new_key.insert(tag_condition_info(base_offset, tag));
- 405       if (!contains_key(out, new_key))
- 406         append_addresses(base_offset+/*skip tag*/1, variant_type(type, tag).type, out, new_key, location_for_error_messages);
- 407     }
- 408   }
- 409 }
- 410 
- 411 //: for the following unit tests we'll do the work of the transform by hand
- 412 
- 413 :(before "End Unit Tests")
- 414 void test_container_address_offsets_empty() {
- 415   int old_size = SIZE(Container_metadata);
- 416   // define a container with no addresses
- 417   reagent r("x:point");
- 418   compute_container_sizes(r, "");  // need to first pre-populate the metadata
- 419   // scan
- 420   compute_container_address_offsets(r, "");
- 421   // global metadata contains just the entry for foo
- 422   // no entries for non-container types or other junk
- 423   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 424   // the reagent we scanned knows it has no addresses
- 425   CHECK(r.metadata.address.empty());
- 426   // the global table contains an identical entry
- 427   CHECK(contains_key(Container_metadata, r.type));
- 428   CHECK(get(Container_metadata, r.type).address.empty());
- 429   // compute_container_address_offsets creates no new entries
- 430   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 431 }
- 432 
- 433 void test_container_address_offsets() {
- 434   int old_size = SIZE(Container_metadata);
- 435   // define a container with an address at offset 0 that we have the size for
- 436   run("container foo [\n"
- 437       "  x:address:num\n"
- 438       "]\n");
- 439   reagent r("x:foo");
- 440   compute_container_sizes(r, "");  // need to first pre-populate the metadata
- 441   // scan
- 442   compute_container_address_offsets(r, "");
- 443   // global metadata contains just the entry for foo
- 444   // no entries for non-container types or other junk
- 445   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 446   // the reagent we scanned knows it has an address at offset 0
- 447   CHECK_EQ(SIZE(r.metadata.address), 1);
- 448   CHECK(contains_key(r.metadata.address, set<tag_condition_info>()));
- 449   const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>());  // unconditional for containers
- 450   CHECK_EQ(SIZE(address_offsets), 1);
- 451   CHECK_EQ(address_offsets.begin()->offset, 0);
- 452   CHECK(address_offsets.begin()->payload_type->atom);
- 453   CHECK_EQ(address_offsets.begin()->payload_type->name, "number");
- 454   // the global table contains an identical entry
- 455   CHECK(contains_key(Container_metadata, r.type));
- 456   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>());
- 457   CHECK_EQ(SIZE(address_offsets2), 1);
- 458   CHECK_EQ(address_offsets2.begin()->offset, 0);
- 459   CHECK(address_offsets2.begin()->payload_type->atom);
- 460   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
- 461   // compute_container_address_offsets creates no new entries
- 462   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 463 }
- 464 
- 465 void test_container_address_offsets_2() {
- 466   int old_size = SIZE(Container_metadata);
- 467   // define a container with an address at offset 1 that we have the size for
- 468   run("container foo [\n"
- 469       "  x:num\n"
- 470       "  y:address:num\n"
- 471       "]\n");
- 472   reagent r("x:foo");
- 473   compute_container_sizes(r, "");  // need to first pre-populate the metadata
- 474   // global metadata contains just the entry for foo
- 475   // no entries for non-container types or other junk
- 476   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 477   // scan
- 478   compute_container_address_offsets(r, "");
- 479   // compute_container_address_offsets creates no new entries
- 480   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 481   // the reagent we scanned knows it has an address at offset 1
- 482   CHECK_EQ(SIZE(r.metadata.address), 1);
- 483   CHECK(contains_key(r.metadata.address, set<tag_condition_info>()));
- 484   const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>());
- 485   CHECK_EQ(SIZE(address_offsets), 1);
- 486   CHECK_EQ(address_offsets.begin()->offset, 1);  //
- 487   CHECK(address_offsets.begin()->payload_type->atom);
- 488   CHECK_EQ(address_offsets.begin()->payload_type->name, "number");
- 489   // the global table contains an identical entry
- 490   CHECK(contains_key(Container_metadata, r.type));
- 491   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>());
- 492   CHECK_EQ(SIZE(address_offsets2), 1);
- 493   CHECK_EQ(address_offsets2.begin()->offset, 1);  //
- 494   CHECK(address_offsets2.begin()->payload_type->atom);
- 495   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
- 496 }
- 497 
- 498 void test_container_address_offsets_nested() {
- 499   int old_size = SIZE(Container_metadata);
- 500   // define a container with a nested container containing an address
- 501   run("container foo [\n"
- 502       "  x:address:num\n"
- 503       "  y:num\n"
- 504       "]\n"
- 505       "container bar [\n"
- 506       "  p:point\n"
- 507       "  f:foo\n"  // nested container containing address
- 508       "]\n");
- 509   reagent r("x:bar");
- 510   compute_container_sizes(r, "");  // need to first pre-populate the metadata
- 511   // global metadata contains entries for bar and included types: point and foo
- 512   // no entries for non-container types or other junk
- 513   CHECK_EQ(SIZE(Container_metadata)-old_size, 3);
- 514   // scan
- 515   compute_container_address_offsets(r, "");
- 516   // the reagent we scanned knows it has an address at offset 2
- 517   CHECK_EQ(SIZE(r.metadata.address), 1);
- 518   CHECK(contains_key(r.metadata.address, set<tag_condition_info>()));
- 519   const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>());
- 520   CHECK_EQ(SIZE(address_offsets), 1);
- 521   CHECK_EQ(address_offsets.begin()->offset, 2);  //
- 522   CHECK(address_offsets.begin()->payload_type->atom);
- 523   CHECK_EQ(address_offsets.begin()->payload_type->name, "number");
- 524   // the global table also knows its address offset
- 525   CHECK(contains_key(Container_metadata, r.type));
- 526   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>());
- 527   CHECK_EQ(SIZE(address_offsets2), 1);
- 528   CHECK_EQ(address_offsets2.begin()->offset, 2);  //
- 529   CHECK(address_offsets2.begin()->payload_type->atom);
- 530   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
- 531   // compute_container_address_offsets creates no new entries
- 532   CHECK_EQ(SIZE(Container_metadata)-old_size, 3);
- 533 }
- 534 
- 535 void test_container_address_offsets_from_address() {
- 536   int old_size = SIZE(Container_metadata);
- 537   // define a container with an address at offset 0
- 538   run("container foo [\n"
- 539       "  x:address:num\n"
- 540       "]\n");
- 541   reagent r("x:address:foo");
- 542   compute_container_sizes(r, "");  // need to first pre-populate the metadata
- 543   // global metadata contains just the entry for foo
- 544   // no entries for non-container types or other junk
- 545   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 546   // scan an address to the container
- 547   compute_container_address_offsets(r, "");
- 548   // compute_container_address_offsets creates no new entries
- 549   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 550   // scanning precomputed metadata for the container
- 551   reagent container("x:foo");
- 552   CHECK(contains_key(Container_metadata, container.type));
- 553   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
- 554   CHECK_EQ(SIZE(address_offsets2), 1);
- 555   CHECK_EQ(address_offsets2.begin()->offset, 0);
- 556   CHECK(address_offsets2.begin()->payload_type->atom);
- 557   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
- 558 }
- 559 
- 560 void test_container_address_offsets_from_array() {
- 561   int old_size = SIZE(Container_metadata);
- 562   // define a container with an address at offset 0
- 563   run("container foo [\n"
- 564       "  x:address:num\n"
- 565       "]\n");
- 566   reagent r("x:array:foo");
- 567   compute_container_sizes(r, "");  // need to first pre-populate the metadata
- 568   // global metadata contains just the entry for foo
- 569   // no entries for non-container types or other junk
- 570   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 571   // scan an array of the container
- 572   compute_container_address_offsets(r, "");
- 573   // compute_container_address_offsets creates no new entries
- 574   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 575   // scanning precomputed metadata for the container
- 576   reagent container("x:foo");
- 577   CHECK(contains_key(Container_metadata, container.type));
- 578   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
- 579   CHECK_EQ(SIZE(address_offsets2), 1);
- 580   CHECK_EQ(address_offsets2.begin()->offset, 0);
- 581   CHECK(address_offsets2.begin()->payload_type->atom);
- 582   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
- 583 }
- 584 
- 585 void test_container_address_offsets_from_address_to_array() {
- 586   int old_size = SIZE(Container_metadata);
- 587   // define a container with an address at offset 0
- 588   run("container foo [\n"
- 589       "  x:address:num\n"
- 590       "]\n");
- 591   reagent r("x:address:array:foo");
- 592   compute_container_sizes(r, "");  // need to first pre-populate the metadata
- 593   // global metadata contains just the entry for foo
- 594   // no entries for non-container types or other junk
- 595   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 596   // scan an address to an array of the container
- 597   compute_container_address_offsets(r, "");
- 598   // compute_container_address_offsets creates no new entries
- 599   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 600   // scanning precomputed metadata for the container
- 601   reagent container("x:foo");
- 602   CHECK(contains_key(Container_metadata, container.type));
- 603   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
- 604   CHECK_EQ(SIZE(address_offsets2), 1);
- 605   CHECK_EQ(address_offsets2.begin()->offset, 0);
- 606   CHECK(address_offsets2.begin()->payload_type->atom);
- 607   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
- 608 }
- 609 
- 610 void test_container_address_offsets_from_static_array() {
- 611   int old_size = SIZE(Container_metadata);
- 612   // define a container with an address at offset 0
- 613   run("container foo [\n"
- 614       "  x:address:num\n"
- 615       "]\n");
- 616   reagent r("x:array:foo:10");
- 617   compute_container_sizes(r, "");  // need to first pre-populate the metadata
- 618   // global metadata contains just the entry for foo
- 619   // no entries for non-container types or other junk
- 620   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 621   // scan a static array of the container
- 622   compute_container_address_offsets(r, "");
- 623   // compute_container_address_offsets creates no new entries
- 624   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 625   // scanning precomputed metadata for the container
- 626   reagent container("x:foo");
- 627   CHECK(contains_key(Container_metadata, container.type));
- 628   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
- 629   CHECK_EQ(SIZE(address_offsets2), 1);
- 630   CHECK_EQ(address_offsets2.begin()->offset, 0);
- 631   CHECK(address_offsets2.begin()->payload_type->atom);
- 632   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
- 633 }
- 634 
- 635 void test_container_address_offsets_from_address_to_static_array() {
- 636   int old_size = SIZE(Container_metadata);
- 637   // define a container with an address at offset 0
- 638   run("container foo [\n"
- 639       "  x:address:num\n"
- 640       "]\n");
- 641   reagent r("x:address:array:foo:10");
- 642   compute_container_sizes(r, "");  // need to first pre-populate the metadata
- 643   // global metadata contains just the entry for foo
- 644   // no entries for non-container types or other junk
- 645   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 646   // scan an address to a static array of the container
- 647   compute_container_address_offsets(r, "");
- 648   // compute_container_address_offsets creates no new entries
- 649   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 650   // scanning precomputed metadata for the container
- 651   reagent container("x:foo");
- 652   CHECK(contains_key(Container_metadata, container.type));
- 653   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
- 654   CHECK_EQ(SIZE(address_offsets2), 1);
- 655   CHECK_EQ(address_offsets2.begin()->offset, 0);
- 656   CHECK(address_offsets2.begin()->payload_type->atom);
- 657   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
- 658 }
- 659 
- 660 void test_container_address_offsets_from_repeated_address_and_array_types() {
- 661   int old_size = SIZE(Container_metadata);
- 662   // define a container with an address at offset 0
- 663   run("container foo [\n"
- 664       "  x:address:num\n"
- 665       "]\n");
- 666   // scan a deep nest of 'address' and 'array' types modifying a container
- 667   reagent r("x:address:array:address:address:array:foo:10");
- 668   compute_container_sizes(r, "");  // need to first pre-populate the metadata
- 669   // global metadata contains just the entry for foo
- 670   // no entries for non-container types or other junk
- 671   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 672   compute_container_address_offsets(r, "");
- 673   // compute_container_address_offsets creates no new entries
- 674   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
- 675   // scanning precomputed metadata for the container
- 676   reagent container("x:foo");
- 677   CHECK(contains_key(Container_metadata, container.type));
- 678   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
- 679   CHECK_EQ(SIZE(address_offsets2), 1);
- 680   CHECK_EQ(address_offsets2.begin()->offset, 0);
- 681   CHECK(address_offsets2.begin()->payload_type->atom);
- 682   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
- 683 }
- 684 
- 685 //: use metadata.address to update refcounts within containers, arrays and
- 686 //: exclusive containers
- 687 
- 688 :(before "End Increment Refcounts(canonized_x)")
- 689 if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) {
- 690   const container_metadata& metadata = get(Container_metadata, canonized_x.type);
- 691   for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin();  p != metadata.address.end();  ++p) {
- 692     if (!all_match(data, p->first)) continue;
- 693     for (set<address_element_info>::const_iterator info = p->second.begin();  info != p->second.end();  ++info)
- 694       increment_refcount(data.at(info->offset));
- 695   }
- 696 }
- 697 
- 698 :(before "End Decrement Refcounts(canonized_x)")
- 699 if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) {
- 700   trace(9999, "mem") << "need to read old value of '" << to_string(canonized_x) << "' to figure out what refcounts to decrement" << end();
- 701   // read from canonized_x but without canonizing again
- 702   // todo: inline without running canonize all over again
- 703   reagent/*copy*/ tmp = canonized_x;
- 704   tmp.properties.push_back(pair<string, string_tree*>("raw", NULL));
- 705   vector<double> data = read_memory(tmp);
- 706   trace(9999, "mem") << "done reading old value of '" << to_string(canonized_x) << "'" << end();
- 707   const container_metadata& metadata = get(Container_metadata, canonized_x.type);
- 708   for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin();  p != metadata.address.end();  ++p) {
- 709     if (!all_match(data, p->first)) continue;
- 710     for (set<address_element_info>::const_iterator info = p->second.begin();  info != p->second.end();  ++info) {
- 711       int element_address = get_or_insert(Memory, canonized_x.value + info->offset);
- 712       reagent/*local*/ element;
- 713       element.set_value(element_address+/*skip refcount*/1);
- 714       element.type = new type_tree(*info->payload_type);
- 715       decrement_refcount(element_address, info->payload_type, size_of(element)+/*refcount*/1);
- 716     }
- 717   }
- 718 }
- 719 
- 720 :(code)
- 721 bool all_match(const vector<double>& data, const set<tag_condition_info>& conditions) {
- 722   for (set<tag_condition_info>::const_iterator p = conditions.begin();  p != conditions.end();  ++p) {
- 723     if (data.at(p->offset) != p->tag)
- 724       return false;
- 725   }
- 726   return true;
- 727 }
- 728 
- 729 :(scenario refcounts_put_container)
- 730 container foo [
- 731   a:bar  # contains an address
- 732 ]
- 733 container bar [
- 734   x:address:num
- 735 ]
- 736 def main [
- 737   1:address:num <- new number:type
- 738   2:bar <- merge 1:address:num
- 739   3:address:foo <- new foo:type
- 740   *3:address:foo <- put *3:address:foo, a:offset, 2:bar
- 741 ]
- 742 +run: {1: ("address" "number")} <- new {number: "type"}
- 743 +mem: incrementing refcount of 1000: 0 -> 1
- 744 +run: {2: "bar"} <- merge {1: ("address" "number")}
- 745 +mem: incrementing refcount of 1000: 1 -> 2
- 746 +run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {a: "offset"}, {2: "bar"}
- 747 # put increments refcount inside container
- 748 +mem: incrementing refcount of 1000: 2 -> 3
- 749 
- 750 :(scenario refcounts_put_index_array)
- 751 container bar [
- 752   x:address:num
- 753 ]
- 754 def main [
- 755   1:address:num <- new number:type
- 756   2:bar <- merge 1:address:num
- 757   3:address:array:bar <- new bar:type, 3
- 758   *3:address:array:bar <- put-index *3:address:array:bar, 0, 2:bar
- 759 ]
- 760 +run: {1: ("address" "number")} <- new {number: "type"}
- 761 +mem: incrementing refcount of 1000: 0 -> 1
- 762 +run: {2: "bar"} <- merge {1: ("address" "number")}
- 763 +mem: incrementing refcount of 1000: 1 -> 2
- 764 +run: {3: ("address" "array" "bar"), "lookup": ()} <- put-index {3: ("address" "array" "bar"), "lookup": ()}, {0: "literal"}, {2: "bar"}
- 765 # put-index increments refcount inside container
- 766 +mem: incrementing refcount of 1000: 2 -> 3
- 767 
- 768 :(scenario refcounts_maybe_convert_container)
- 769 exclusive-container foo [
- 770   a:num
- 771   b:bar  # contains an address
- 772 ]
- 773 container bar [
- 774   x:address:num
- 775 ]
- 776 def main [
- 777   1:address:num <- new number:type
- 778   2:bar <- merge 1:address:num
- 779   3:foo <- merge 1/b, 2:bar
- 780   5:bar, 6:bool <- maybe-convert 3:foo, 1:variant/b
- 781 ]
- 782 +run: {1: ("address" "number")} <- new {number: "type"}
- 783 +mem: incrementing refcount of 1000: 0 -> 1
- 784 +run: {2: "bar"} <- merge {1: ("address" "number")}
- 785 +mem: incrementing refcount of 1000: 1 -> 2
- 786 +run: {3: "foo"} <- merge {1: "literal", "b": ()}, {2: "bar"}
- 787 +mem: incrementing refcount of 1000: 2 -> 3
- 788 +run: {5: "bar"}, {6: "boolean"} <- maybe-convert {3: "foo"}, {1: "variant", "b": ()}
- 789 +mem: incrementing refcount of 1000: 3 -> 4
- 790 
- 791 :(scenario refcounts_copy_doubly_nested)
- 792 container foo [
- 793   a:bar  # no addresses
- 794   b:curr  # contains addresses
- 795 ]
- 796 container bar [
- 797   x:num
- 798   y:num
- 799 ]
- 800 container curr [
- 801   x:num
- 802   y:address:num  # address inside container inside container
- 803 ]
- 804 def main [
- 805   1:address:num <- new number:type
- 806   2:address:curr <- new curr:type
- 807   *2:address:curr <- put *2:address:curr, 1:offset/y, 1:address:num
- 808   3:address:foo <- new foo:type
- 809   *3:address:foo <- put *3:address:foo, 1:offset/b, *2:address:curr
- 810   4:foo <- copy *3:address:foo
- 811 ]
- 812 +transform: compute address offsets for container foo
- 813 +transform: checking container foo, element 1
- 814 +transform: address at offset 3
- 815 +run: {1: ("address" "number")} <- new {number: "type"}
- 816 +mem: incrementing refcount of 1000: 0 -> 1
- 817 # storing an address in a container updates its refcount
- 818 +run: {2: ("address" "curr"), "lookup": ()} <- put {2: ("address" "curr"), "lookup": ()}, {1: "offset", "y": ()}, {1: ("address" "number")}
- 819 +mem: incrementing refcount of 1000: 1 -> 2
- 820 # storing a container in a container updates refcounts of any contained addresses
- 821 +run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {1: "offset", "b": ()}, {2: ("address" "curr"), "lookup": ()}
- 822 +mem: incrementing refcount of 1000: 2 -> 3
- 823 # copying a container containing a container containing an address updates refcount
- 824 +run: {4: "foo"} <- copy {3: ("address" "foo"), "lookup": ()}
- 825 +mem: incrementing refcount of 1000: 3 -> 4
- 826 
- 827 :(scenario refcounts_copy_exclusive_container_within_container)
- 828 container foo [
- 829   a:num
- 830   b:bar
- 831 ]
- 832 exclusive-container bar [
- 833   x:num
- 834   y:num
- 835   z:address:num
- 836 ]
- 837 def main [
- 838   1:address:num <- new number:type
- 839   2:bar <- merge 0/x, 34
- 840   3:foo <- merge 12, 2:bar
- 841   5:bar <- merge 1/y, 35
- 842   6:foo <- merge 13, 5:bar
- 843   8:bar <- merge 2/z, 1:address:num
- 844   9:foo <- merge 14, 8:bar
- 845   11:foo <- copy 9:foo
- 846 ]
- 847 +run: {1: ("address" "number")} <- new {number: "type"}
- 848 +mem: incrementing refcount of 1000: 0 -> 1
- 849 # no change while merging items of other types
- 850 +run: {8: "bar"} <- merge {2: "literal", "z": ()}, {1: ("address" "number")}
- 851 +mem: incrementing refcount of 1000: 1 -> 2
- 852 +run: {9: "foo"} <- merge {14: "literal"}, {8: "bar"}
- 853 +mem: incrementing refcount of 1000: 2 -> 3
- 854 +run: {11: "foo"} <- copy {9: "foo"}
- 855 +mem: incrementing refcount of 1000: 3 -> 4
- 856 
- 857 :(scenario refcounts_copy_container_within_exclusive_container)
- 858 exclusive-container foo [
- 859   a:num
- 860   b:bar
- 861 ]
- 862 container bar [
- 863   x:num
- 864   y:num
- 865   z:address:num
- 866 ]
- 867 def main [
- 868   1:address:num <- new number:type
- 869   2:foo <- merge 0/a, 34
- 870   6:foo <- merge 0/a, 35
- 871   10:bar <- merge 2/x, 15/y, 1:address:num
- 872   13:foo <- merge 1/b, 10:bar
- 873   17:foo <- copy 13:foo
- 874 ]
- 875 +run: {1: ("address" "number")} <- new {number: "type"}
- 876 +mem: incrementing refcount of 1000: 0 -> 1
- 877 # no change while merging items of other types
- 878 +run: {10: "bar"} <- merge {2: "literal", "x": ()}, {15: "literal", "y": ()}, {1: ("address" "number")}
- 879 +mem: incrementing refcount of 1000: 1 -> 2
- 880 +run: {13: "foo"} <- merge {1: "literal", "b": ()}, {10: "bar"}
- 881 +mem: incrementing refcount of 1000: 2 -> 3
- 882 +run: {17: "foo"} <- copy {13: "foo"}
- 883 +mem: incrementing refcount of 1000: 3 -> 4
- 884 
- 885 :(scenario refcounts_copy_exclusive_container_within_exclusive_container)
- 886 exclusive-container foo [
- 887   a:num
- 888   b:bar
- 889 ]
- 890 exclusive-container bar [
- 891   x:num
- 892   y:address:num
- 893 ]
- 894 def main [
- 895   1:address:num <- new number:type
- 896   10:foo <- merge 1/b, 1/y, 1:address:num
- 897   20:foo <- copy 10:foo
- 898 ]
- 899 +run: {1: ("address" "number")} <- new {number: "type"}
- 900 +mem: incrementing refcount of 1000: 0 -> 1
- 901 # no change while merging items of other types
- 902 +run: {10: "foo"} <- merge {1: "literal", "b": ()}, {1: "literal", "y": ()}, {1: ("address" "number")}
- 903 +mem: incrementing refcount of 1000: 1 -> 2
- 904 +run: {20: "foo"} <- copy {10: "foo"}
- 905 +mem: incrementing refcount of 1000: 2 -> 3
- 906 
- 907 :(scenario refcounts_copy_array_within_container)
- 908 container foo [
- 909   x:address:array:num
- 910 ]
- 911 def main [
- 912   1:address:array:num <- new number:type, 3
- 913   2:foo <- merge 1:address:array:num
- 914   3:address:array:num <- new number:type, 5
- 915   2:foo <- merge 3:address:array:num
- 916 ]
- 917 +run: {1: ("address" "array" "number")} <- new {number: "type"}, {3: "literal"}
- 918 +mem: incrementing refcount of 1000: 0 -> 1
- 919 +run: {2: "foo"} <- merge {1: ("address" "array" "number")}
- 920 +mem: incrementing refcount of 1000: 1 -> 2
- 921 +run: {2: "foo"} <- merge {3: ("address" "array" "number")}
- 922 +mem: decrementing refcount of 1000: 2 -> 1
- 923 
- 924 :(scenario refcounts_copy_address_within_static_array_within_container)
- 925 container foo [
- 926   a:array:bar:3
- 927   b:address:num
- 928 ]
- 929 container bar [
- 930   y:num
- 931   z:address:num
- 932 ]
- 933 def main [
- 934   1:address:num <- new number:type
- 935   2:bar <- merge 34, 1:address:num
- 936   10:array:bar:3 <- create-array
- 937   put-index 10:array:bar:3, 1, 2:bar
- 938   20:foo <- merge 10:array:bar:3, 1:address:num
- 939   1:address:num <- copy 0
- 940   2:bar <- merge 34, 1:address:num
- 941   put-index 10:array:bar:3, 1, 2:bar
- 942   20:foo <- merge 10:array:bar:3, 1:address:num
- 943 ]
- 944 +run: {1: ("address" "number")} <- new {number: "type"}
- 945 +mem: incrementing refcount of 1000: 0 -> 1
- 946 +run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")}
- 947 +mem: incrementing refcount of 1000: 1 -> 2
- 948 +run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"}
- 949 +mem: incrementing refcount of 1000: 2 -> 3
- 950 +run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")}
- 951 +mem: incrementing refcount of 1000: 3 -> 4
- 952 +mem: incrementing refcount of 1000: 4 -> 5
- 953 +run: {1: ("address" "number")} <- copy {0: "literal"}
- 954 +mem: decrementing refcount of 1000: 5 -> 4
- 955 +run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")}
- 956 +mem: decrementing refcount of 1000: 4 -> 3
- 957 +run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"}
- 958 +mem: decrementing refcount of 1000: 3 -> 2
- 959 +run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")}
- 960 +mem: decrementing refcount of 1000: 2 -> 1
- 961 +mem: decrementing refcount of 1000: 1 -> 0
- 962 
- 963 :(scenario refcounts_handle_exclusive_containers_with_different_tags)
- 964 container foo1 [
- 965   x:address:num
- 966   y:num
- 967 ]
- 968 container foo2 [
- 969   x:num
- 970   y:address:num
- 971 ]
- 972 exclusive-container bar [
- 973   a:foo1
- 974   b:foo2
- 975 ]
- 976 def main [
- 977   1:address:num <- copy 12000/unsafe  # pretend allocation
- 978   *1:address:num <- copy 34
- 979   2:bar <- merge 0/foo1, 1:address:num, 97
- 980   5:address:num <- copy 13000/unsafe  # pretend allocation
- 981   *5:address:num <- copy 35
- 982   6:bar <- merge 1/foo2, 98, 5:address:num
- 983   2:bar <- copy 6:bar
- 984 ]
- 985 +run: {2: "bar"} <- merge {0: "literal", "foo1": ()}, {1: ("address" "number")}, {97: "literal"}
- 986 +mem: incrementing refcount of 12000: 1 -> 2
- 987 +run: {6: "bar"} <- merge {1: "literal", "foo2": ()}, {98: "literal"}, {5: ("address" "number")}
- 988 +mem: incrementing refcount of 13000: 1 -> 2
- 989 +run: {2: "bar"} <- copy {6: "bar"}
- 990 +mem: incrementing refcount of 13000: 2 -> 3
- 991 +mem: decrementing refcount of 12000: 2 -> 1
- 992 
- 993 :(code)
- 994 bool is_mu_container(const reagent& r) {
- 995   return is_mu_container(r.type);
- 996 }
- 997 bool is_mu_container(const type_tree* type) {
- 998   if (!type) return false;
- 999   // End is_mu_container(type) Special-cases
-1000   if (type->value == 0) return false;
-1001   type_info& info = get(Type, type->value);
-1002   return info.kind == CONTAINER;
-1003 }
-1004 
-1005 bool is_mu_exclusive_container(const reagent& r) {
-1006   return is_mu_exclusive_container(r.type);
-1007 }
-1008 bool is_mu_exclusive_container(const type_tree* type) {
-1009   if (!type) return false;
-1010   // End is_mu_exclusive_container(type) Special-cases
-1011   if (type->value == 0) return false;
-1012   type_info& info = get(Type, type->value);
-1013   return info.kind == EXCLUSIVE_CONTAINER;
-1014 }
+   1 //: Update refcounts when copying addresses.
+   2 //: The top of the address layer has more on refcounts.
+   3 
+   4 :(scenario refcounts)
+   5 def main [
+   6   1:address:num <- copy 1000/unsafe
+   7   2:address:num <- copy 1:address:num
+   8   1:address:num <- copy 0
+   9   2:address:num <- copy 0
+  10 ]
+  11 +run: {1: ("address" "number")} <- copy {1000: "literal", "unsafe": ()}
+  12 +mem: incrementing refcount of 1000: 0 -> 1
+  13 +run: {2: ("address" "number")} <- copy {1: ("address" "number")}
+  14 +mem: incrementing refcount of 1000: 1 -> 2
+  15 +run: {1: ("address" "number")} <- copy {0: "literal"}
+  16 +mem: decrementing refcount of 1000: 2 -> 1
+  17 +run: {2: ("address" "number")} <- copy {0: "literal"}
+  18 +mem: decrementing refcount of 1000: 1 -> 0
+  19 
+  20 :(before "End Globals")
+  21 //: escape hatch for a later layer
+  22 bool Update_refcounts_in_write_memory = true;
+  23 
+  24 :(before "End write_memory(x) Special-cases")
+  25 if (Update_refcounts_in_write_memory)
+  26   update_any_refcounts(x, data);
+  27 
+  28 :(code)
+  29 void update_any_refcounts(const reagent& canonized_x, const vector<double>& data) {
+  30   increment_any_refcounts(canonized_x, data);  // increment first so we don't reclaim on x <- copy x
+  31   decrement_any_refcounts(canonized_x);
+  32 }
+  33 
+  34 void increment_any_refcounts(const reagent& canonized_x, const vector<double>& data) {
+  35   if (is_mu_address(canonized_x)) {
+  36     assert(scalar(data));
+  37     assert(!canonized_x.metadata.size);
+  38     increment_refcount(data.at(0));
+  39   }
+  40   // End Increment Refcounts(canonized_x)
+  41 }
+  42 
+  43 void increment_refcount(int new_address) {
+  44   assert(new_address >= 0);
+  45   if (new_address == 0) return;
+  46   int new_refcount = get_or_insert(Memory, new_address);
+  47   trace(9999, "mem") << "incrementing refcount of " << new_address << ": " << new_refcount << " -> " << new_refcount+1 << end();
+  48   put(Memory, new_address, new_refcount+1);
+  49 }
+  50 
+  51 void decrement_any_refcounts(const reagent& canonized_x) {
+  52   if (is_mu_address(canonized_x)) {
+  53     assert(canonized_x.value);
+  54     assert(!canonized_x.metadata.size);
+  55     decrement_refcount(get_or_insert(Memory, canonized_x.value), payload_type(canonized_x.type), payload_size(canonized_x));
+  56   }
+  57   // End Decrement Refcounts(canonized_x)
+  58 }
+  59 
+  60 void decrement_refcount(int old_address, const type_tree* payload_type, int payload_size) {
+  61   assert(old_address >= 0);
+  62   if (old_address) {
+  63     int old_refcount = get_or_insert(Memory, old_address);
+  64     trace(9999, "mem") << "decrementing refcount of " << old_address << ": " << old_refcount << " -> " << old_refcount-1 << end();
+  65     --old_refcount;
+  66     put(Memory, old_address, old_refcount);
+  67     if (old_refcount < 0) {
+  68       tb_shutdown();
+  69       cerr << "Negative refcount!!! " << old_address << ' ' << old_refcount << '\n';
+  70       if (Trace_stream) {
+  71         cerr << "Saving trace to last_trace.\n";
+  72         ofstream fout("last_trace");
+  73         fout << Trace_stream->readable_contents("");
+  74         fout.close();
+  75       }
+  76       exit(0);
+  77     }
+  78     // End Decrement Refcount(old_address, payload_type, payload_size)
+  79   }
+  80 }
+  81 
+  82 int payload_size(reagent/*copy*/ x) {
+  83   x.properties.push_back(pair<string, string_tree*>("lookup", NULL));
+  84   lookup_memory_core(x, /*check for nulls*/false);
+  85   return size_of(x) + /*refcount*/1;
+  86 }
+  87 
+  88 :(scenario refcounts_reflexive)
+  89 def main [
+  90   1:address:num <- new number:type
+  91   # idempotent copies leave refcount unchanged
+  92   1:address:num <- copy 1:address:num
+  93 ]
+  94 +run: {1: ("address" "number")} <- new {number: "type"}
+  95 +mem: incrementing refcount of 1000: 0 -> 1
+  96 +run: {1: ("address" "number")} <- copy {1: ("address" "number")}
+  97 +mem: incrementing refcount of 1000: 1 -> 2
+  98 +mem: decrementing refcount of 1000: 2 -> 1
+  99 
+ 100 :(scenario refcounts_call)
+ 101 def main [
+ 102   1:address:num <- new number:type
+ 103   # passing in addresses to recipes increments refcount
+ 104   foo 1:address:num
+ 105   # return does NOT yet decrement refcount; memory must be explicitly managed
+ 106   1:address:num <- new number:type
+ 107 ]
+ 108 def foo [
+ 109   2:address:num <- next-ingredient
+ 110 ]
+ 111 +run: {1: ("address" "number")} <- new {number: "type"}
+ 112 +mem: incrementing refcount of 1000: 0 -> 1
+ 113 +run: foo {1: ("address" "number")}
+ 114 # leave ambiguous precisely when the next increment happens
+ 115 +mem: incrementing refcount of 1000: 1 -> 2
+ 116 +run: {1: ("address" "number")} <- new {number: "type"}
+ 117 +mem: decrementing refcount of 1000: 2 -> 1
+ 118 
+ 119 //: fix up any instructions that don't follow the usual flow of read_memory
+ 120 //: before the RUN switch, and write_memory after
+ 121 
+ 122 :(scenario refcounts_put)
+ 123 container foo [
+ 124   x:address:num
+ 125 ]
+ 126 def main [
+ 127   1:address:num <- new number:type
+ 128   2:address:foo <- new foo:type
+ 129   *2:address:foo <- put *2:address:foo, x:offset, 1:address:num
+ 130 ]
+ 131 +run: {1: ("address" "number")} <- new {number: "type"}
+ 132 +mem: incrementing refcount of 1000: 0 -> 1
+ 133 +run: {2: ("address" "foo")} <- new {foo: "type"}
+ 134 +mem: incrementing refcount of 1002: 0 -> 1
+ 135 +run: {2: ("address" "foo"), "lookup": ()} <- put {2: ("address" "foo"), "lookup": ()}, {x: "offset"}, {1: ("address" "number")}
+ 136 # put increments refcount
+ 137 +mem: incrementing refcount of 1000: 1 -> 2
+ 138 
+ 139 :(after "Write Memory in PUT in Run")
+ 140 reagent/*copy*/ element = element_type(base.type, offset);
+ 141 assert(!has_property(element, "lookup"));
+ 142 element.set_value(address);
+ 143 update_any_refcounts(element, ingredients.at(2));
+ 144 
+ 145 :(scenario refcounts_put_index)
+ 146 def main [
+ 147   1:address:num <- new number:type
+ 148   2:address:array:address:num <- new {(address number): type}, 3
+ 149   *2:address:array:address:num <- put-index *2:address:array:address:num, 0, 1:address:num
+ 150 ]
+ 151 +run: {1: ("address" "number")} <- new {number: "type"}
+ 152 +mem: incrementing refcount of 1000: 0 -> 1
+ 153 +run: {2: ("address" "array" "address" "number")} <- new {(address number): "type"}, {3: "literal"}
+ 154 +mem: incrementing refcount of 1002: 0 -> 1
+ 155 +run: {2: ("address" "array" "address" "number"), "lookup": ()} <- put-index {2: ("address" "array" "address" "number"), "lookup": ()}, {0: "literal"}, {1: ("address" "number")}
+ 156 # put-index increments refcount
+ 157 +mem: incrementing refcount of 1000: 1 -> 2
+ 158 
+ 159 :(after "Write Memory in PUT_INDEX in Run")
+ 160 reagent/*local*/ element;
+ 161 element.set_value(address);
+ 162 element.type = copy_array_element(base.type);
+ 163 update_any_refcounts(element, value);
+ 164 
+ 165 :(scenario refcounts_maybe_convert)
+ 166 exclusive-container foo [
+ 167   x:num
+ 168   p:address:num
+ 169 ]
+ 170 def main [
+ 171   1:address:num <- new number:type
+ 172   2:foo <- merge 1/p, 1:address:num
+ 173   4:address:num, 5:bool <- maybe-convert 2:foo, 1:variant/p
+ 174 ]
+ 175 +run: {1: ("address" "number")} <- new {number: "type"}
+ 176 +mem: incrementing refcount of 1000: 0 -> 1
+ 177 # merging in an address increments refcount
+ 178 +run: {2: "foo"} <- merge {1: "literal", "p": ()}, {1: ("address" "number")}
+ 179 +mem: incrementing refcount of 1000: 1 -> 2
+ 180 +run: {4: ("address" "number")}, {5: "boolean"} <- maybe-convert {2: "foo"}, {1: "variant", "p": ()}
+ 181 # maybe-convert increments refcount on success
+ 182 +mem: incrementing refcount of 1000: 2 -> 3
+ 183 
+ 184 :(after "Write Memory in Successful MAYBE_CONVERT")
+ 185 // todo: double-check data here as well
+ 186 vector<double> data;
+ 187 for (int i = 0;  i < size_of(product);  ++i)
+ 188   data.push_back(get_or_insert(Memory, base_address+/*skip tag*/1+i));
+ 189 update_any_refcounts(product, data);
+ 190 
+ 191 //:: manage refcounts in instructions that copy multiple locations at a time
+ 192 
+ 193 :(scenario refcounts_copy_nested)
+ 194 container foo [
+ 195   x:address:num  # address inside container
+ 196 ]
+ 197 def main [
+ 198   1:address:num <- new number:type
+ 199   2:address:foo <- new foo:type
+ 200   *2:address:foo <- put *2:address:foo, x:offset, 1:address:num
+ 201   3:foo <- copy *2:address:foo
+ 202 ]
+ 203 +transform: compute address offsets for container foo
+ 204 +transform: checking container foo, element 0
+ 205 +transform: address at offset 0
+ 206 +run: {1: ("address" "number")} <- new {number: "type"}
+ 207 +mem: incrementing refcount of 1000: 0 -> 1
+ 208 +run: {2: ("address" "foo"), "lookup": ()} <- put {2: ("address" "foo"), "lookup": ()}, {x: "offset"}, {1: ("address" "number")}
+ 209 +mem: incrementing refcount of 1000: 1 -> 2
+ 210 # copying a container increments refcounts of any contained addresses
+ 211 +run: {3: "foo"} <- copy {2: ("address" "foo"), "lookup": ()}
+ 212 +mem: incrementing refcount of 1000: 2 -> 3
+ 213 
+ 214 :(after "End type_tree Definition")
+ 215 struct address_element_info {
+ 216   int offset;  // where inside a container type (after flattening nested containers!) the address lies
+ 217   const type_tree* payload_type;  // all the information we need to compute sizes of items inside an address inside a container. Doesn't need to be a full-scale reagent, since an address inside a container can never be an array, and arrays are the only type that need to know their location to compute their size.
+ 218   address_element_info(int o, const type_tree* p) {
+ 219     offset = o;
+ 220     payload_type = p;
+ 221   }
+ 222   address_element_info(const address_element_info& other) {
+ 223     offset = other.offset;
+ 224     payload_type = other.payload_type ? new type_tree(*other.payload_type) : NULL;
+ 225   }
+ 226   ~address_element_info() {
+ 227     if (payload_type) {
+ 228       delete payload_type;
+ 229       payload_type = NULL;
+ 230     }
+ 231   }
+ 232   address_element_info& operator=(const address_element_info& other) {
+ 233     offset = other.offset;
+ 234     if (payload_type) delete payload_type;
+ 235     payload_type = other.payload_type ? new type_tree(*other.payload_type) : NULL;
+ 236     return *this;
+ 237   }
+ 238 };
+ 239 
+ 240 // For exclusive containers we might sometimes have an address at some offset
+ 241 // if some other offset has a specific tag. This struct encapsulates such
+ 242 // guards.
+ 243 struct tag_condition_info {
+ 244   int offset;
+ 245   int tag;
+ 246   tag_condition_info(int o, int t) :offset(o), tag(t) {}
+ 247 };
+ 248 
+ 249 :(before "End container_metadata Fields")
+ 250 // a list of facts of the form:
+ 251 //
+ 252 //  IF offset o1 has tag t2 AND offset o2 has tag t2 AND .., THEN
+ 253 //    for all address_element_infos:
+ 254 //      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)
+ 255 map<set<tag_condition_info>, set<address_element_info> > address;
+ 256 :(code)
+ 257 bool operator<(const set<tag_condition_info>& a, const set<tag_condition_info>& b) {
+ 258   if (a.size() != b.size()) return a.size() < b.size();
+ 259   for (set<tag_condition_info>::const_iterator pa = a.begin(), pb = b.begin();  pa != a.end();  ++pa, ++pb) {
+ 260     if (pa->offset != pb->offset) return pa->offset < pb->offset;
+ 261     if (pa->tag != pb->tag) return pa->tag < pb->tag;
+ 262   }
+ 263   return false;  // equal
+ 264 }
+ 265 bool operator<(const tag_condition_info& a, const tag_condition_info& b) {
+ 266   if (a.offset != b.offset) return a.offset < b.offset;
+ 267   if (a.tag != b.tag) return a.tag < b.tag;
+ 268   return false;  // equal
+ 269 }
+ 270 bool operator<(const set<address_element_info>& a, const set<address_element_info>& b) {
+ 271   if (a.size() != b.size()) return a.size() < b.size();
+ 272   for (set<address_element_info>::const_iterator pa = a.begin(), pb = b.begin();  pa != a.end();  ++pa, ++pb) {
+ 273     if (pa->offset != pb->offset) return pa->offset < pb->offset;
+ 274   }
+ 275   return false;  // equal
+ 276 }
+ 277 bool operator<(const address_element_info& a, const address_element_info& b) {
+ 278   if (a.offset != b.offset) return a.offset < b.offset;
+ 279   return false;  // equal
+ 280 }
+ 281 
+ 282 //: populate metadata.address in a separate transform, because it requires
+ 283 //: already knowing the sizes of all types
+ 284 
+ 285 :(after "Transform.push_back(compute_container_sizes)")
+ 286 Transform.push_back(compute_container_address_offsets);  // idempotent
+ 287 :(code)
+ 288 void compute_container_address_offsets(const recipe_ordinal r) {
+ 289   recipe& caller = get(Recipe, r);
+ 290   trace(9992, "transform") << "--- compute address offsets for " << caller.name << end();
+ 291   for (int i = 0;  i < SIZE(caller.steps);  ++i) {
+ 292     instruction& inst = caller.steps.at(i);
+ 293     trace(9993, "transform") << "- compute address offsets for " << to_string(inst) << end();
+ 294     for (int i = 0;  i < SIZE(inst.ingredients);  ++i)
+ 295       compute_container_address_offsets(inst.ingredients.at(i), " in '"+to_original_string(inst)+"'");
+ 296     for (int i = 0;  i < SIZE(inst.products);  ++i)
+ 297       compute_container_address_offsets(inst.products.at(i), " in '"+to_original_string(inst)+"'");
+ 298   }
+ 299 }
+ 300 
+ 301 void compute_container_address_offsets(reagent& r, const string& location_for_error_messages) {
+ 302   if (is_literal(r) || is_dummy(r)) return;
+ 303   compute_container_address_offsets(r.type, location_for_error_messages);
+ 304   if (contains_key(Container_metadata, r.type))
+ 305     r.metadata = get(Container_metadata, r.type);
+ 306 }
+ 307 
+ 308 // the recursive structure of this function needs to exactly match
+ 309 // compute_container_sizes
+ 310 void compute_container_address_offsets(const type_tree* type, const string& location_for_error_messages) {
+ 311   if (!type) return;
+ 312   if (!type->atom) {
+ 313     if (!type->left->atom) {
+ 314       raise << "invalid type " << to_string(type) << location_for_error_messages << '\n' << end();
+ 315       return;
+ 316     }
+ 317     if (type->left->name == "address")
+ 318       compute_container_address_offsets(payload_type(type), location_for_error_messages);
+ 319     else if (type->left->name == "array")
+ 320       compute_container_address_offsets(array_element(type), location_for_error_messages);
+ 321     // End compute_container_address_offsets Non-atom Special-cases
+ 322   }
+ 323   const type_tree* base_type = type;
+ 324   // Update base_type in compute_container_address_offsets
+ 325   if (!contains_key(Type, base_type->value)) return;  // error raised elsewhere
+ 326   type_info& info = get(Type, base_type->value);
+ 327   if (info.kind == CONTAINER) {
+ 328     compute_container_address_offsets(info, type, location_for_error_messages);
+ 329   }
+ 330   if (info.kind == EXCLUSIVE_CONTAINER) {
+ 331     compute_exclusive_container_address_offsets(info, type, location_for_error_messages);
+ 332   }
+ 333 }
+ 334 
+ 335 void compute_container_address_offsets(const type_info& container_info, const type_tree* full_type, const string& location_for_error_messages) {
+ 336   container_metadata& metadata = get(Container_metadata, full_type);
+ 337   if (!metadata.address.empty()) return;
+ 338   trace(9994, "transform") << "compute address offsets for container " << container_info.name << end();
+ 339   append_addresses(0, full_type, metadata.address, set<tag_condition_info>(), location_for_error_messages);
+ 340 }
+ 341 
+ 342 void compute_exclusive_container_address_offsets(const type_info& exclusive_container_info, const type_tree* full_type, const string& location_for_error_messages) {
+ 343   container_metadata& metadata = get(Container_metadata, full_type);
+ 344   trace(9994, "transform") << "compute address offsets for exclusive container " << exclusive_container_info.name << end();
+ 345   for (int tag = 0;  tag < SIZE(exclusive_container_info.elements);  ++tag) {
+ 346     set<tag_condition_info> key;
+ 347     key.insert(tag_condition_info(/*tag is at offset*/0, tag));
+ 348     append_addresses(/*skip tag offset*/1, variant_type(full_type, tag).type, metadata.address, key, location_for_error_messages);
+ 349   }
+ 350 }
+ 351 
+ 352 void append_addresses(int base_offset, const type_tree* type, map<set<tag_condition_info>, set<address_element_info> >& out, const set<tag_condition_info>& key, const string& location_for_error_messages) {
+ 353   if (is_mu_address(type)) {
+ 354     get_or_insert(out, key).insert(address_element_info(base_offset, new type_tree(*payload_type(type))));
+ 355     return;
+ 356   }
+ 357   const type_tree* base_type = type;
+ 358   // Update base_type in append_container_address_offsets
+ 359   const type_info& info = get(Type, base_type->value);
+ 360   if (info.kind == CONTAINER) {
+ 361     for (int curr_index = 0, curr_offset = base_offset;  curr_index < SIZE(info.elements);  ++curr_index) {
+ 362       trace(9993, "transform") << "checking container " << base_type->name << ", element " << curr_index << end();
+ 363       reagent/*copy*/ element = element_type(type, curr_index);  // not base_type
+ 364       // Compute Container Address Offset(element)
+ 365       if (is_mu_address(element)) {
+ 366         trace(9993, "transform") << "address at offset " << curr_offset << end();
+ 367         get_or_insert(out, key).insert(address_element_info(curr_offset, new type_tree(*payload_type(element.type))));
+ 368         ++curr_offset;
+ 369       }
+ 370       else if (is_mu_array(element)) {
+ 371         curr_offset += /*array length*/1;
+ 372         const type_tree* array_element_type = array_element(element.type);
+ 373         int array_element_size = size_of(array_element_type);
+ 374         for (int i = 0; i < static_array_length(element.type); ++i) {
+ 375           append_addresses(curr_offset, array_element_type, out, key, location_for_error_messages);
+ 376           curr_offset += array_element_size;
+ 377         }
+ 378       }
+ 379       else if (is_mu_container(element)) {
+ 380         append_addresses(curr_offset, element.type, out, key, location_for_error_messages);
+ 381         curr_offset += size_of(element);
+ 382       }
+ 383       else if (is_mu_exclusive_container(element)) {
+ 384         const type_tree* element_base_type = element.type;
+ 385         // Update element_base_type For Exclusive Container in append_addresses
+ 386         const type_info& element_info = get(Type, element_base_type->value);
+ 387         for (int tag = 0;  tag < SIZE(element_info.elements);  ++tag) {
+ 388           set<tag_condition_info> new_key = key;
+ 389           new_key.insert(tag_condition_info(curr_offset, tag));
+ 390           if (!contains_key(out, new_key))
+ 391             append_addresses(curr_offset+/*skip tag*/1, variant_type(element.type, tag).type, out, new_key, location_for_error_messages);
+ 392         }
+ 393         curr_offset += size_of(element);
+ 394       }
+ 395       else {
+ 396         // non-address primitive
+ 397         ++curr_offset;
+ 398       }
+ 399     }
+ 400   }
+ 401   else if (info.kind == EXCLUSIVE_CONTAINER) {
+ 402     for (int tag = 0;  tag < SIZE(info.elements);  ++tag) {
+ 403       set<tag_condition_info> new_key = key;
+ 404       new_key.insert(tag_condition_info(base_offset, tag));
+ 405       if (!contains_key(out, new_key))
+ 406         append_addresses(base_offset+/*skip tag*/1, variant_type(type, tag).type, out, new_key, location_for_error_messages);
+ 407     }
+ 408   }
+ 409 }
+ 410 
+ 411 //: for the following unit tests we'll do the work of the transform by hand
+ 412 
+ 413 :(before "End Unit Tests")
+ 414 void test_container_address_offsets_empty() {
+ 415   int old_size = SIZE(Container_metadata);
+ 416   // define a container with no addresses
+ 417   reagent r("x:point");
+ 418   compute_container_sizes(r, "");  // need to first pre-populate the metadata
+ 419   // scan
+ 420   compute_container_address_offsets(r, "");
+ 421   // global metadata contains just the entry for foo
+ 422   // no entries for non-container types or other junk
+ 423   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 424   // the reagent we scanned knows it has no addresses
+ 425   CHECK(r.metadata.address.empty());
+ 426   // the global table contains an identical entry
+ 427   CHECK(contains_key(Container_metadata, r.type));
+ 428   CHECK(get(Container_metadata, r.type).address.empty());
+ 429   // compute_container_address_offsets creates no new entries
+ 430   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 431 }
+ 432 
+ 433 void test_container_address_offsets() {
+ 434   int old_size = SIZE(Container_metadata);
+ 435   // define a container with an address at offset 0 that we have the size for
+ 436   run("container foo [\n"
+ 437       "  x:address:num\n"
+ 438       "]\n");
+ 439   reagent r("x:foo");
+ 440   compute_container_sizes(r, "");  // need to first pre-populate the metadata
+ 441   // scan
+ 442   compute_container_address_offsets(r, "");
+ 443   // global metadata contains just the entry for foo
+ 444   // no entries for non-container types or other junk
+ 445   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 446   // the reagent we scanned knows it has an address at offset 0
+ 447   CHECK_EQ(SIZE(r.metadata.address), 1);
+ 448   CHECK(contains_key(r.metadata.address, set<tag_condition_info>()));
+ 449   const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>());  // unconditional for containers
+ 450   CHECK_EQ(SIZE(address_offsets), 1);
+ 451   CHECK_EQ(address_offsets.begin()->offset, 0);
+ 452   CHECK(address_offsets.begin()->payload_type->atom);
+ 453   CHECK_EQ(address_offsets.begin()->payload_type->name, "number");
+ 454   // the global table contains an identical entry
+ 455   CHECK(contains_key(Container_metadata, r.type));
+ 456   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>());
+ 457   CHECK_EQ(SIZE(address_offsets2), 1);
+ 458   CHECK_EQ(address_offsets2.begin()->offset, 0);
+ 459   CHECK(address_offsets2.begin()->payload_type->atom);
+ 460   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
+ 461   // compute_container_address_offsets creates no new entries
+ 462   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 463 }
+ 464 
+ 465 void test_container_address_offsets_2() {
+ 466   int old_size = SIZE(Container_metadata);
+ 467   // define a container with an address at offset 1 that we have the size for
+ 468   run("container foo [\n"
+ 469       "  x:num\n"
+ 470       "  y:address:num\n"
+ 471       "]\n");
+ 472   reagent r("x:foo");
+ 473   compute_container_sizes(r, "");  // need to first pre-populate the metadata
+ 474   // global metadata contains just the entry for foo
+ 475   // no entries for non-container types or other junk
+ 476   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 477   // scan
+ 478   compute_container_address_offsets(r, "");
+ 479   // compute_container_address_offsets creates no new entries
+ 480   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 481   // the reagent we scanned knows it has an address at offset 1
+ 482   CHECK_EQ(SIZE(r.metadata.address), 1);
+ 483   CHECK(contains_key(r.metadata.address, set<tag_condition_info>()));
+ 484   const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>());
+ 485   CHECK_EQ(SIZE(address_offsets), 1);
+ 486   CHECK_EQ(address_offsets.begin()->offset, 1);  //
+ 487   CHECK(address_offsets.begin()->payload_type->atom);
+ 488   CHECK_EQ(address_offsets.begin()->payload_type->name, "number");
+ 489   // the global table contains an identical entry
+ 490   CHECK(contains_key(Container_metadata, r.type));
+ 491   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>());
+ 492   CHECK_EQ(SIZE(address_offsets2), 1);
+ 493   CHECK_EQ(address_offsets2.begin()->offset, 1);  //
+ 494   CHECK(address_offsets2.begin()->payload_type->atom);
+ 495   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
+ 496 }
+ 497 
+ 498 void test_container_address_offsets_nested() {
+ 499   int old_size = SIZE(Container_metadata);
+ 500   // define a container with a nested container containing an address
+ 501   run("container foo [\n"
+ 502       "  x:address:num\n"
+ 503       "  y:num\n"
+ 504       "]\n"
+ 505       "container bar [\n"
+ 506       "  p:point\n"
+ 507       "  f:foo\n"  // nested container containing address
+ 508       "]\n");
+ 509   reagent r("x:bar");
+ 510   compute_container_sizes(r, "");  // need to first pre-populate the metadata
+ 511   // global metadata contains entries for bar and included types: point and foo
+ 512   // no entries for non-container types or other junk
+ 513   CHECK_EQ(SIZE(Container_metadata)-old_size, 3);
+ 514   // scan
+ 515   compute_container_address_offsets(r, "");
+ 516   // the reagent we scanned knows it has an address at offset 2
+ 517   CHECK_EQ(SIZE(r.metadata.address), 1);
+ 518   CHECK(contains_key(r.metadata.address, set<tag_condition_info>()));
+ 519   const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>());
+ 520   CHECK_EQ(SIZE(address_offsets), 1);
+ 521   CHECK_EQ(address_offsets.begin()->offset, 2);  //
+ 522   CHECK(address_offsets.begin()->payload_type->atom);
+ 523   CHECK_EQ(address_offsets.begin()->payload_type->name, "number");
+ 524   // the global table also knows its address offset
+ 525   CHECK(contains_key(Container_metadata, r.type));
+ 526   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>());
+ 527   CHECK_EQ(SIZE(address_offsets2), 1);
+ 528   CHECK_EQ(address_offsets2.begin()->offset, 2);  //
+ 529   CHECK(address_offsets2.begin()->payload_type->atom);
+ 530   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
+ 531   // compute_container_address_offsets creates no new entries
+ 532   CHECK_EQ(SIZE(Container_metadata)-old_size, 3);
+ 533 }
+ 534 
+ 535 void test_container_address_offsets_from_address() {
+ 536   int old_size = SIZE(Container_metadata);
+ 537   // define a container with an address at offset 0
+ 538   run("container foo [\n"
+ 539       "  x:address:num\n"
+ 540       "]\n");
+ 541   reagent r("x:address:foo");
+ 542   compute_container_sizes(r, "");  // need to first pre-populate the metadata
+ 543   // global metadata contains just the entry for foo
+ 544   // no entries for non-container types or other junk
+ 545   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 546   // scan an address to the container
+ 547   compute_container_address_offsets(r, "");
+ 548   // compute_container_address_offsets creates no new entries
+ 549   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 550   // scanning precomputed metadata for the container
+ 551   reagent container("x:foo");
+ 552   CHECK(contains_key(Container_metadata, container.type));
+ 553   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
+ 554   CHECK_EQ(SIZE(address_offsets2), 1);
+ 555   CHECK_EQ(address_offsets2.begin()->offset, 0);
+ 556   CHECK(address_offsets2.begin()->payload_type->atom);
+ 557   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
+ 558 }
+ 559 
+ 560 void test_container_address_offsets_from_array() {
+ 561   int old_size = SIZE(Container_metadata);
+ 562   // define a container with an address at offset 0
+ 563   run("container foo [\n"
+ 564       "  x:address:num\n"
+ 565       "]\n");
+ 566   reagent r("x:array:foo");
+ 567   compute_container_sizes(r, "");  // need to first pre-populate the metadata
+ 568   // global metadata contains just the entry for foo
+ 569   // no entries for non-container types or other junk
+ 570   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 571   // scan an array of the container
+ 572   compute_container_address_offsets(r, "");
+ 573   // compute_container_address_offsets creates no new entries
+ 574   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 575   // scanning precomputed metadata for the container
+ 576   reagent container("x:foo");
+ 577   CHECK(contains_key(Container_metadata, container.type));
+ 578   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
+ 579   CHECK_EQ(SIZE(address_offsets2), 1);
+ 580   CHECK_EQ(address_offsets2.begin()->offset, 0);
+ 581   CHECK(address_offsets2.begin()->payload_type->atom);
+ 582   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
+ 583 }
+ 584 
+ 585 void test_container_address_offsets_from_address_to_array() {
+ 586   int old_size = SIZE(Container_metadata);
+ 587   // define a container with an address at offset 0
+ 588   run("container foo [\n"
+ 589       "  x:address:num\n"
+ 590       "]\n");
+ 591   reagent r("x:address:array:foo");
+ 592   compute_container_sizes(r, "");  // need to first pre-populate the metadata
+ 593   // global metadata contains just the entry for foo
+ 594   // no entries for non-container types or other junk
+ 595   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 596   // scan an address to an array of the container
+ 597   compute_container_address_offsets(r, "");
+ 598   // compute_container_address_offsets creates no new entries
+ 599   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 600   // scanning precomputed metadata for the container
+ 601   reagent container("x:foo");
+ 602   CHECK(contains_key(Container_metadata, container.type));
+ 603   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
+ 604   CHECK_EQ(SIZE(address_offsets2), 1);
+ 605   CHECK_EQ(address_offsets2.begin()->offset, 0);
+ 606   CHECK(address_offsets2.begin()->payload_type->atom);
+ 607   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
+ 608 }
+ 609 
+ 610 void test_container_address_offsets_from_static_array() {
+ 611   int old_size = SIZE(Container_metadata);
+ 612   // define a container with an address at offset 0
+ 613   run("container foo [\n"
+ 614       "  x:address:num\n"
+ 615       "]\n");
+ 616   reagent r("x:array:foo:10");
+ 617   compute_container_sizes(r, "");  // need to first pre-populate the metadata
+ 618   // global metadata contains just the entry for foo
+ 619   // no entries for non-container types or other junk
+ 620   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 621   // scan a static array of the container
+ 622   compute_container_address_offsets(r, "");
+ 623   // compute_container_address_offsets creates no new entries
+ 624   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 625   // scanning precomputed metadata for the container
+ 626   reagent container("x:foo");
+ 627   CHECK(contains_key(Container_metadata, container.type));
+ 628   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
+ 629   CHECK_EQ(SIZE(address_offsets2), 1);
+ 630   CHECK_EQ(address_offsets2.begin()->offset, 0);
+ 631   CHECK(address_offsets2.begin()->payload_type->atom);
+ 632   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
+ 633 }
+ 634 
+ 635 void test_container_address_offsets_from_address_to_static_array() {
+ 636   int old_size = SIZE(Container_metadata);
+ 637   // define a container with an address at offset 0
+ 638   run("container foo [\n"
+ 639       "  x:address:num\n"
+ 640       "]\n");
+ 641   reagent r("x:address:array:foo:10");
+ 642   compute_container_sizes(r, "");  // need to first pre-populate the metadata
+ 643   // global metadata contains just the entry for foo
+ 644   // no entries for non-container types or other junk
+ 645   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 646   // scan an address to a static array of the container
+ 647   compute_container_address_offsets(r, "");
+ 648   // compute_container_address_offsets creates no new entries
+ 649   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 650   // scanning precomputed metadata for the container
+ 651   reagent container("x:foo");
+ 652   CHECK(contains_key(Container_metadata, container.type));
+ 653   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
+ 654   CHECK_EQ(SIZE(address_offsets2), 1);
+ 655   CHECK_EQ(address_offsets2.begin()->offset, 0);
+ 656   CHECK(address_offsets2.begin()->payload_type->atom);
+ 657   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
+ 658 }
+ 659 
+ 660 void test_container_address_offsets_from_repeated_address_and_array_types() {
+ 661   int old_size = SIZE(Container_metadata);
+ 662   // define a container with an address at offset 0
+ 663   run("container foo [\n"
+ 664       "  x:address:num\n"
+ 665       "]\n");
+ 666   // scan a deep nest of 'address' and 'array' types modifying a container
+ 667   reagent r("x:address:array:address:address:array:foo:10");
+ 668   compute_container_sizes(r, "");  // need to first pre-populate the metadata
+ 669   // global metadata contains just the entry for foo
+ 670   // no entries for non-container types or other junk
+ 671   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 672   compute_container_address_offsets(r, "");
+ 673   // compute_container_address_offsets creates no new entries
+ 674   CHECK_EQ(SIZE(Container_metadata)-old_size, 1);
+ 675   // scanning precomputed metadata for the container
+ 676   reagent container("x:foo");
+ 677   CHECK(contains_key(Container_metadata, container.type));
+ 678   const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>());
+ 679   CHECK_EQ(SIZE(address_offsets2), 1);
+ 680   CHECK_EQ(address_offsets2.begin()->offset, 0);
+ 681   CHECK(address_offsets2.begin()->payload_type->atom);
+ 682   CHECK_EQ(address_offsets2.begin()->payload_type->name, "number");
+ 683 }
+ 684 
+ 685 //: use metadata.address to update refcounts within containers, arrays and
+ 686 //: exclusive containers
+ 687 
+ 688 :(before "End Increment Refcounts(canonized_x)")
+ 689 if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) {
+ 690   const container_metadata& metadata = get(Container_metadata, canonized_x.type);
+ 691   for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin();  p != metadata.address.end();  ++p) {
+ 692     if (!all_match(data, p->first)) continue;
+ 693     for (set<address_element_info>::const_iterator info = p->second.begin();  info != p->second.end();  ++info)
+ 694       increment_refcount(data.at(info->offset));
+ 695   }
+ 696 }
+ 697 
+ 698 :(before "End Decrement Refcounts(canonized_x)")
+ 699 if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) {
+ 700   trace(9999, "mem") << "need to read old value of '" << to_string(canonized_x) << "' to figure out what refcounts to decrement" << end();
+ 701   // read from canonized_x but without canonizing again
+ 702   // todo: inline without running canonize all over again
+ 703   reagent/*copy*/ tmp = canonized_x;
+ 704   tmp.properties.push_back(pair<string, string_tree*>("raw", NULL));
+ 705   vector<double> data = read_memory(tmp);
+ 706   trace(9999, "mem") << "done reading old value of '" << to_string(canonized_x) << "'" << end();
+ 707   const container_metadata& metadata = get(Container_metadata, canonized_x.type);
+ 708   for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin();  p != metadata.address.end();  ++p) {
+ 709     if (!all_match(data, p->first)) continue;
+ 710     for (set<address_element_info>::const_iterator info = p->second.begin();  info != p->second.end();  ++info) {
+ 711       int element_address = get_or_insert(Memory, canonized_x.value + info->offset);
+ 712       reagent/*local*/ element;
+ 713       element.set_value(element_address+/*skip refcount*/1);
+ 714       element.type = new type_tree(*info->payload_type);
+ 715       decrement_refcount(element_address, info->payload_type, size_of(element)+/*refcount*/1);
+ 716     }
+ 717   }
+ 718 }
+ 719 
+ 720 :(code)
+ 721 bool all_match(const vector<double>& data, const set<tag_condition_info>& conditions) {
+ 722   for (set<tag_condition_info>::const_iterator p = conditions.begin();  p != conditions.end();  ++p) {
+ 723     if (data.at(p->offset) != p->tag)
+ 724       return false;
+ 725   }
+ 726   return true;
+ 727 }
+ 728 
+ 729 :(scenario refcounts_put_container)
+ 730 container foo [
+ 731   a:bar  # contains an address
+ 732 ]
+ 733 container bar [
+ 734   x:address:num
+ 735 ]
+ 736 def main [
+ 737   1:address:num <- new number:type
+ 738   2:bar <- merge 1:address:num
+ 739   3:address:foo <- new foo:type
+ 740   *3:address:foo <- put *3:address:foo, a:offset, 2:bar
+ 741 ]
+ 742 +run: {1: ("address" "number")} <- new {number: "type"}
+ 743 +mem: incrementing refcount of 1000: 0 -> 1
+ 744 +run: {2: "bar"} <- merge {1: ("address" "number")}
+ 745 +mem: incrementing refcount of 1000: 1 -> 2
+ 746 +run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {a: "offset"}, {2: "bar"}
+ 747 # put increments refcount inside container
+ 748 +mem: incrementing refcount of 1000: 2 -> 3
+ 749 
+ 750 :(scenario refcounts_put_index_array)
+ 751 container bar [
+ 752   x:address:num
+ 753 ]
+ 754 def main [
+ 755   1:address:num <- new number:type
+ 756   2:bar <- merge 1:address:num
+ 757   3:address:array:bar <- new bar:type, 3
+ 758   *3:address:array:bar <- put-index *3:address:array:bar, 0, 2:bar
+ 759 ]
+ 760 +run: {1: ("address" "number")} <- new {number: "type"}
+ 761 +mem: incrementing refcount of 1000: 0 -> 1
+ 762 +run: {2: "bar"} <- merge {1: ("address" "number")}
+ 763 +mem: incrementing refcount of 1000: 1 -> 2
+ 764 +run: {3: ("address" "array" "bar"), "lookup": ()} <- put-index {3: ("address" "array" "bar"), "lookup": ()}, {0: "literal"}, {2: "bar"}
+ 765 # put-index increments refcount inside container
+ 766 +mem: incrementing refcount of 1000: 2 -> 3
+ 767 
+ 768 :(scenario refcounts_maybe_convert_container)
+ 769 exclusive-container foo [
+ 770   a:num
+ 771   b:bar  # contains an address
+ 772 ]
+ 773 container bar [
+ 774   x:address:num
+ 775 ]
+ 776 def main [
+ 777   1:address:num <- new number:type
+ 778   2:bar <- merge 1:address:num
+ 779   3:foo <- merge 1/b, 2:bar
+ 780   5:bar, 6:bool <- maybe-convert 3:foo, 1:variant/b
+ 781 ]
+ 782 +run: {1: ("address" "number")} <- new {number: "type"}
+ 783 +mem: incrementing refcount of 1000: 0 -> 1
+ 784 +run: {2: "bar"} <- merge {1: ("address" "number")}
+ 785 +mem: incrementing refcount of 1000: 1 -> 2
+ 786 +run: {3: "foo"} <- merge {1: "literal", "b": ()}, {2: "bar"}
+ 787 +mem: incrementing refcount of 1000: 2 -> 3
+ 788 +run: {5: "bar"}, {6: "boolean"} <- maybe-convert {3: "foo"}, {1: "variant", "b": ()}
+ 789 +mem: incrementing refcount of 1000: 3 -> 4
+ 790 
+ 791 :(scenario refcounts_copy_doubly_nested)
+ 792 container foo [
+ 793   a:bar  # no addresses
+ 794   b:curr  # contains addresses
+ 795 ]
+ 796 container bar [
+ 797   x:num
+ 798   y:num
+ 799 ]
+ 800 container curr [
+ 801   x:num
+ 802   y:address:num  # address inside container inside container
+ 803 ]
+ 804 def main [
+ 805   1:address:num <- new number:type
+ 806   2:address:curr <- new curr:type
+ 807   *2:address:curr <- put *2:address:curr, 1:offset/y, 1:address:num
+ 808   3:address:foo <- new foo:type
+ 809   *3:address:foo <- put *3:address:foo, 1:offset/b, *2:address:curr
+ 810   4:foo <- copy *3:address:foo
+ 811 ]
+ 812 +transform: compute address offsets for container foo
+ 813 +transform: checking container foo, element 1
+ 814 +transform: address at offset 3
+ 815 +run: {1: ("address" "number")} <- new {number: "type"}
+ 816 +mem: incrementing refcount of 1000: 0 -> 1
+ 817 # storing an address in a container updates its refcount
+ 818 +run: {2: ("address" "curr"), "lookup": ()} <- put {2: ("address" "curr"), "lookup": ()}, {1: "offset", "y": ()}, {1: ("address" "number")}
+ 819 +mem: incrementing refcount of 1000: 1 -> 2
+ 820 # storing a container in a container updates refcounts of any contained addresses
+ 821 +run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {1: "offset", "b": ()}, {2: ("address" "curr"), "lookup": ()}
+ 822 +mem: incrementing refcount of 1000: 2 -> 3
+ 823 # copying a container containing a container containing an address updates refcount
+ 824 +run: {4: "foo"} <- copy {3: ("address" "foo"), "lookup": ()}
+ 825 +mem: incrementing refcount of 1000: 3 -> 4
+ 826 
+ 827 :(scenario refcounts_copy_exclusive_container_within_container)
+ 828 container foo [
+ 829   a:num
+ 830   b:bar
+ 831 ]
+ 832 exclusive-container bar [
+ 833   x:num
+ 834   y:num
+ 835   z:address:num
+ 836 ]
+ 837 def main [
+ 838   1:address:num <- new number:type
+ 839   2:bar <- merge 0/x, 34
+ 840   3:foo <- merge 12, 2:bar
+ 841   5:bar <- merge 1/y, 35
+ 842   6:foo <- merge 13, 5:bar
+ 843   8:bar <- merge 2/z, 1:address:num
+ 844   9:foo <- merge 14, 8:bar
+ 845   11:foo <- copy 9:foo
+ 846 ]
+ 847 +run: {1: ("address" "number")} <- new {number: "type"}
+ 848 +mem: incrementing refcount of 1000: 0 -> 1
+ 849 # no change while merging items of other types
+ 850 +run: {8: "bar"} <- merge {2: "literal", "z": ()}, {1: ("address" "number")}
+ 851 +mem: incrementing refcount of 1000: 1 -> 2
+ 852 +run: {9: "foo"} <- merge {14: "literal"}, {8: "bar"}
+ 853 +mem: incrementing refcount of 1000: 2 -> 3
+ 854 +run: {11: "foo"} <- copy {9: "foo"}
+ 855 +mem: incrementing refcount of 1000: 3 -> 4
+ 856 
+ 857 :(scenario refcounts_copy_container_within_exclusive_container)
+ 858 exclusive-container foo [
+ 859   a:num
+ 860   b:bar
+ 861 ]
+ 862 container bar [
+ 863   x:num
+ 864   y:num
+ 865   z:address:num
+ 866 ]
+ 867 def main [
+ 868   1:address:num <- new number:type
+ 869   2:foo <- merge 0/a, 34
+ 870   6:foo <- merge 0/a, 35
+ 871   10:bar <- merge 2/x, 15/y, 1:address:num
+ 872   13:foo <- merge 1/b, 10:bar
+ 873   17:foo <- copy 13:foo
+ 874 ]
+ 875 +run: {1: ("address" "number")} <- new {number: "type"}
+ 876 +mem: incrementing refcount of 1000: 0 -> 1
+ 877 # no change while merging items of other types
+ 878 +run: {10: "bar"} <- merge {2: "literal", "x": ()}, {15: "literal", "y": ()}, {1: ("address" "number")}
+ 879 +mem: incrementing refcount of 1000: 1 -> 2
+ 880 +run: {13: "foo"} <- merge {1: "literal", "b": ()}, {10: "bar"}
+ 881 +mem: incrementing refcount of 1000: 2 -> 3
+ 882 +run: {17: "foo"} <- copy {13: "foo"}
+ 883 +mem: incrementing refcount of 1000: 3 -> 4
+ 884 
+ 885 :(scenario refcounts_copy_exclusive_container_within_exclusive_container)
+ 886 exclusive-container foo [
+ 887   a:num
+ 888   b:bar
+ 889 ]
+ 890 exclusive-container bar [
+ 891   x:num
+ 892   y:address:num
+ 893 ]
+ 894 def main [
+ 895   1:address:num <- new number:type
+ 896   10:foo <- merge 1/b, 1/y, 1:address:num
+ 897   20:foo <- copy 10:foo
+ 898 ]
+ 899 +run: {1: ("address" "number")} <- new {number: "type"}
+ 900 +mem: incrementing refcount of 1000: 0 -> 1
+ 901 # no change while merging items of other types
+ 902 +run: {10: "foo"} <- merge {1: "literal", "b": ()}, {1: "literal", "y": ()}, {1: ("address" "number")}
+ 903 +mem: incrementing refcount of 1000: 1 -> 2
+ 904 +run: {20: "foo"} <- copy {10: "foo"}
+ 905 +mem: incrementing refcount of 1000: 2 -> 3
+ 906 
+ 907 :(scenario refcounts_copy_array_within_container)
+ 908 container foo [
+ 909   x:address:array:num
+ 910 ]
+ 911 def main [
+ 912   1:address:array:num <- new number:type, 3
+ 913   2:foo <- merge 1:address:array:num
+ 914   3:address:array:num <- new number:type, 5
+ 915   2:foo <- merge 3:address:array:num
+ 916 ]
+ 917 +run: {1: ("address" "array" "number")} <- new {number: "type"}, {3: "literal"}
+ 918 +mem: incrementing refcount of 1000: 0 -> 1
+ 919 +run: {2: "foo"} <- merge {1: ("address" "array" "number")}
+ 920 +mem: incrementing refcount of 1000: 1 -> 2
+ 921 +run: {2: "foo"} <- merge {3: ("address" "array" "number")}
+ 922 +mem: decrementing refcount of 1000: 2 -> 1
+ 923 
+ 924 :(scenario refcounts_copy_address_within_static_array_within_container)
+ 925 container foo [
+ 926   a:array:bar:3
+ 927   b:address:num
+ 928 ]
+ 929 container bar [
+ 930   y:num
+ 931   z:address:num
+ 932 ]
+ 933 def main [
+ 934   1:address:num <- new number:type
+ 935   2:bar <- merge 34, 1:address:num
+ 936   10:array:bar:3 <- create-array
+ 937   put-index 10:array:bar:3, 1, 2:bar
+ 938   20:foo <- merge 10:array:bar:3, 1:address:num
+ 939   1:address:num <- copy 0
+ 940   2:bar <- merge 34, 1:address:num
+ 941   put-index 10:array:bar:3, 1, 2:bar
+ 942   20:foo <- merge 10:array:bar:3, 1:address:num
+ 943 ]
+ 944 +run: {1: ("address" "number")} <- new {number: "type"}
+ 945 +mem: incrementing refcount of 1000: 0 -> 1
+ 946 +run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")}
+ 947 +mem: incrementing refcount of 1000: 1 -> 2
+ 948 +run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"}
+ 949 +mem: incrementing refcount of 1000: 2 -> 3
+ 950 +run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")}
+ 951 +mem: incrementing refcount of 1000: 3 -> 4
+ 952 +mem: incrementing refcount of 1000: 4 -> 5
+ 953 +run: {1: ("address" "number")} <- copy {0: "literal"}
+ 954 +mem: decrementing refcount of 1000: 5 -> 4
+ 955 +run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")}
+ 956 +mem: decrementing refcount of 1000: 4 -> 3
+ 957 +run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"}
+ 958 +mem: decrementing refcount of 1000: 3 -> 2
+ 959 +run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")}
+ 960 +mem: decrementing refcount of 1000: 2 -> 1
+ 961 +mem: decrementing refcount of 1000: 1 -> 0
+ 962 
+ 963 :(scenario refcounts_handle_exclusive_containers_with_different_tags)
+ 964 container foo1 [
+ 965   x:address:num
+ 966   y:num
+ 967 ]
+ 968 container foo2 [
+ 969   x:num
+ 970   y:address:num
+ 971 ]
+ 972 exclusive-container bar [
+ 973   a:foo1
+ 974   b:foo2
+ 975 ]
+ 976 def main [
+ 977   1:address:num <- copy 12000/unsafe  # pretend allocation
+ 978   *1:address:num <- copy 34
+ 979   2:bar <- merge 0/foo1, 1:address:num, 97
+ 980   5:address:num <- copy 13000/unsafe  # pretend allocation
+ 981   *5:address:num <- copy 35
+ 982   6:bar <- merge 1/foo2, 98, 5:address:num
+ 983   2:bar <- copy 6:bar
+ 984 ]
+ 985 +run: {2: "bar"} <- merge {0: "literal", "foo1": ()}, {1: ("address" "number")}, {97: "literal"}
+ 986 +mem: incrementing refcount of 12000: 1 -> 2
+ 987 +run: {6: "bar"} <- merge {1: "literal", "foo2": ()}, {98: "literal"}, {5: ("address" "number")}
+ 988 +mem: incrementing refcount of 13000: 1 -> 2
+ 989 +run: {2: "bar"} <- copy {6: "bar"}
+ 990 +mem: incrementing refcount of 13000: 2 -> 3
+ 991 +mem: decrementing refcount of 12000: 2 -> 1
+ 992 
+ 993 :(code)
+ 994 bool is_mu_container(const reagent& r) {
+ 995   return is_mu_container(r.type);
+ 996 }
+ 997 bool is_mu_container(const type_tree* type) {
+ 998   if (!type) return false;
+ 999   // End is_mu_container(type) Special-cases
+1000   if (type->value == 0) return false;
+1001   type_info& info = get(Type, type->value);
+1002   return info.kind == CONTAINER;
+1003 }
+1004 
+1005 bool is_mu_exclusive_container(const reagent& r) {
+1006   return is_mu_exclusive_container(r.type);
+1007 }
+1008 bool is_mu_exclusive_container(const type_tree* type) {
+1009   if (!type) return false;
+1010   // End is_mu_exclusive_container(type) Special-cases
+1011   if (type->value == 0) return false;
+1012   type_info& info = get(Type, type->value);
+1013   return info.kind == EXCLUSIVE_CONTAINER;
+1014 }
 
-- cgit 1.4.1-2-gfad0