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