From 87c5b32925549f05d950ad07c935417ef7eeebb9 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Sun, 3 Sep 2017 17:35:22 -0700 Subject: 3990 --- html/043space.cc.html | 536 +++++++++++++++++++++++++------------------------- 1 file changed, 269 insertions(+), 267 deletions(-) (limited to 'html/043space.cc.html') diff --git a/html/043space.cc.html b/html/043space.cc.html index f7feb1ba..a513bd29 100644 --- a/html/043space.cc.html +++ b/html/043space.cc.html @@ -220,8 +220,8 @@ if ('onhashchange' in window) { 154 :(before "Read element" following "case INDEX:") 155 element.properties.push_back(pair<string, string_tree*>("raw", NULL)); 156 -157 //:: convenience operation to automatically deduce the amount of space to -158 //:: allocate in a default space with names +157 //:: 'new-default-space' is a convenience operation to automatically deduce +158 //:: the amount of space to allocate in a default space with names 159 160 :(scenario new_default_space) 161 def main [ @@ -239,272 +239,274 @@ if ('onhashchange' in window) { 173 if (s == "number-of-locals") return true; 174 175 :(before "End Rewrite Instruction(curr, recipe result)") -176 // rewrite `new-default-space` to -177 // `default-space:space <- new location:type, number-of-locals:literal` -178 // where N is Name[recipe][""] -179 if (curr.name == "new-default-space") { -180 rewrite_default_space_instruction(curr); -181 } -182 :(after "Begin Preprocess read_memory(x)") -183 if (x.name == "number-of-locals") { -184 vector<double> result; -185 result.push_back(Name[get(Recipe_ordinal, current_recipe_name())][""]); -186 if (result.back() == 0) -187 ¦ raise << "no space allocated for default-space in recipe " << current_recipe_name() << "; are you using names?\n" << end(); -188 return result; -189 } -190 :(after "Begin Preprocess write_memory(x, data)") -191 if (x.name == "number-of-locals") { -192 raise << maybe(current_recipe_name()) << "can't write to special name 'number-of-locals'\n" << end(); -193 return; -194 } -195 -196 //:: 'local-scope' is like 'new-default-space' except that we'll reclaim the -197 //:: default-space when the routine exits -198 -199 :(scenario local_scope) -200 def main [ -201 1:&:@:location <- foo -202 2:&:@:location <- foo -203 3:bool <- equal 1:&, 2:& -204 ] -205 def foo [ -206 local-scope -207 x:num <- copy 34 -208 return default-space:space -209 ] -210 # both calls to foo should have received the same default-space -211 +mem: storing 1 in location 3 -212 -213 :(scenario local_scope_frees_up_addresses) -214 def main [ -215 local-scope -216 x:text <- new [abc] -217 ] -218 +mem: clearing x:text -219 -220 :(before "End Rewrite Instruction(curr, recipe result)") -221 if (curr.name == "local-scope") { -222 rewrite_default_space_instruction(curr); -223 } -224 -225 //: todo: do this in a transform, rather than magically in the 'return' instruction -226 :(after "Falling Through End Of Recipe") -227 try_reclaim_locals(); -228 :(after "Starting Reply") -229 try_reclaim_locals(); -230 -231 :(code) -232 void try_reclaim_locals() { -233 if (!Reclaim_memory) return; -234 // only reclaim routines starting with 'local-scope' -235 const recipe_ordinal r = get(Recipe_ordinal, current_recipe_name()); -236 const recipe& exiting_recipe = get(Recipe, r); -237 if (exiting_recipe.steps.empty()) return; -238 const instruction& inst = exiting_recipe.steps.at(0); -239 if (inst.name_before_rewrite != "local-scope") return; -240 // reclaim any local variables unless they're being returned -241 vector<double> zeros; -242 for (int i = /*leave default space for last*/1; i < SIZE(exiting_recipe.steps); ++i) { -243 ¦ const instruction& inst = exiting_recipe.steps.at(i); -244 ¦ for (int i = 0; i < SIZE(inst.products); ++i) { -245 ¦ ¦ const reagent& product = inst.products.at(i); -246 ¦ ¦ // local variables only -247 ¦ ¦ if (has_property(product, "lookup")) continue; -248 ¦ ¦ if (has_property(product, "raw")) continue; // tests often want to check such locations after they run -249 ¦ ¦ if (escaping(product)) continue; -250 ¦ ¦ // End Checks For Reclaiming Locals -251 ¦ ¦ trace(9999, "mem") << "clearing " << product.original_string << end(); -252 ¦ ¦ zeros.resize(size_of(product)); -253 ¦ ¦ write_memory(product, zeros); -254 ¦ } -255 } -256 trace(9999, "mem") << "automatically abandoning " << current_call().default_space << end(); -257 abandon(current_call().default_space, -258 ¦ ¦ ¦ ¦ inst.products.at(0).type->right, -259 ¦ ¦ ¦ ¦ /*refcount*/1 + /*array length*/1 + /*number-of-locals*/Name[r][""]); -260 } -261 -262 //: Reclaiming local variables above requires remembering what name an -263 //: instruction had before any rewrites or transforms. -264 :(before "End instruction Fields") -265 string name_before_rewrite; -266 :(before "End instruction Clear") -267 name_before_rewrite.clear(); -268 :(before "End next_instruction(curr)") -269 curr->name_before_rewrite = curr->name; -270 -271 :(code) -272 // is this reagent one of the values returned by the current (return) instruction? -273 // is the corresponding ingredient saved in the caller? -274 bool escaping(const reagent& r) { -275 assert(Current_routine); // run-time only -276 // nothing escapes when you fall through past end of recipe -277 if (current_step_index() >= SIZE(Current_routine->steps())) return false; -278 for (long long i = 0; i < SIZE(current_instruction().ingredients); ++i) { -279 ¦ if (r == current_instruction().ingredients.at(i)) { -280 ¦ ¦ if (caller_uses_product(i)) -281 ¦ ¦ ¦ return true; -282 ¦ } -283 } -284 return false; -285 } -286 -287 //: since we don't decrement refcounts for escaping values above, make sure we -288 //: don't increment them when the caller saves them either -289 -290 :(before "End should_update_refcounts() Special-cases") -291 if (Writing_products_of_instruction) { -292 const instruction& inst = current_instruction(); -293 // should_update_refcounts() Special-cases When Writing Products Of Primitive Instructions -294 if (inst.operation < MAX_PRIMITIVE_RECIPES) return true; -295 if (!contains_key(Recipe, inst.operation)) return true; -296 const recipe& callee = get(Recipe, inst.operation); -297 if (callee.steps.empty()) return true; -298 return callee.steps.at(0).name_before_rewrite != "local-scope"; // callees that call local-scope are already dealt with before return -299 } -300 -301 :(code) -302 bool caller_uses_product(int product_index) { -303 assert(Current_routine); // run-time only -304 assert(!Current_routine->calls.empty()); -305 if (Current_routine->calls.size() == 1) return false; -306 const call& caller = *++Current_routine->calls.begin(); -307 const instruction& caller_inst = to_instruction(caller); -308 if (product_index >= SIZE(caller_inst.products)) return false; -309 return !is_dummy(caller_inst.products.at(product_index)); -310 } -311 -312 void rewrite_default_space_instruction(instruction& curr) { -313 if (!curr.ingredients.empty()) -314 ¦ raise << "'" << to_original_string(curr) << "' can't take any ingredients\n" << end(); -315 curr.name = "new"; -316 curr.ingredients.push_back(reagent("location:type")); -317 curr.ingredients.push_back(reagent("number-of-locals:literal")); -318 if (!curr.products.empty()) -319 ¦ raise << "new-default-space can't take any results\n" << end(); -320 curr.products.push_back(reagent("default-space:space")); -321 } -322 -323 :(scenario local_scope_frees_up_addresses_inside_containers) -324 container foo [ -325 x:num -326 y:&:num -327 ] -328 def main [ -329 local-scope -330 x:&:num <- new number:type -331 y:foo <- merge 34, x:&:num -332 # x and y are both cleared when main returns -333 ] -334 +mem: clearing x:&:num -335 +mem: decrementing refcount of 1006: 2 -> 1 -336 +mem: clearing y:foo -337 +mem: decrementing refcount of 1006: 1 -> 0 -338 +mem: automatically abandoning 1006 -339 -340 :(scenario local_scope_returns_addresses_inside_containers) -341 container foo [ -342 x:num -343 y:&:num -344 ] -345 def f [ -346 local-scope -347 x:&:num <- new number:type -348 *x:&:num <- copy 12 -349 y:foo <- merge 34, x:&:num -350 # since y is 'escaping' f, it should not be cleared -351 return y:foo -352 ] -353 def main [ -354 1:foo <- f -355 3:num <- get 1:foo, x:offset -356 4:&:num <- get 1:foo, y:offset -357 5:num <- copy *4:&:num -358 1:foo <- put 1:foo, y:offset, 0 -359 4:&:num <- copy 0 -360 ] -361 +mem: storing 34 in location 1 -362 +mem: storing 1006 in location 2 -363 +mem: storing 34 in location 3 -364 # refcount of 1:foo shouldn't include any stray ones from f -365 +run: {4: ("address" "number")} <- get {1: "foo"}, {y: "offset"} -366 +mem: incrementing refcount of 1006: 1 -> 2 -367 # 1:foo wasn't abandoned/cleared -368 +run: {5: "number"} <- copy {4: ("address" "number"), "lookup": ()} -369 +mem: storing 12 in location 5 -370 +run: {1: "foo"} <- put {1: "foo"}, {y: "offset"}, {0: "literal"} -371 +mem: decrementing refcount of 1006: 2 -> 1 -372 +run: {4: ("address" "number")} <- copy {0: "literal"} -373 +mem: decrementing refcount of 1006: 1 -> 0 -374 +mem: automatically abandoning 1006 -375 -376 :(scenario local_scope_claims_return_values_when_not_saved) -377 def f [ -378 local-scope -379 x:&:num <- new number:type -380 return x:&:num -381 ] -382 def main [ -383 f # doesn't save result -384 ] -385 # x reclaimed -386 +mem: automatically abandoning 1004 -387 # f's local scope reclaimed -388 +mem: automatically abandoning 1000 -389 -390 //:: all recipes must set default-space one way or another +176 // rewrite 'new-default-space' to +177 // ``` +178 // default-space:space <- new location:type, number-of-locals:literal +179 // ``` +180 // where number-of-locals is Name[recipe][""] +181 if (curr.name == "new-default-space") { +182 rewrite_default_space_instruction(curr); +183 } +184 :(after "Begin Preprocess read_memory(x)") +185 if (x.name == "number-of-locals") { +186 vector<double> result; +187 result.push_back(Name[get(Recipe_ordinal, current_recipe_name())][""]); +188 if (result.back() == 0) +189 ¦ raise << "no space allocated for default-space in recipe " << current_recipe_name() << "; are you using names?\n" << end(); +190 return result; +191 } +192 :(after "Begin Preprocess write_memory(x, data)") +193 if (x.name == "number-of-locals") { +194 raise << maybe(current_recipe_name()) << "can't write to special name 'number-of-locals'\n" << end(); +195 return; +196 } +197 +198 //:: 'local-scope' is like 'new-default-space' except that we'll reclaim the +199 //:: default-space when the routine exits +200 +201 :(scenario local_scope) +202 def main [ +203 1:&:@:location <- foo +204 2:&:@:location <- foo +205 3:bool <- equal 1:&, 2:& +206 ] +207 def foo [ +208 local-scope +209 x:num <- copy 34 +210 return default-space:space +211 ] +212 # both calls to foo should have received the same default-space +213 +mem: storing 1 in location 3 +214 +215 :(scenario local_scope_frees_up_addresses) +216 def main [ +217 local-scope +218 x:text <- new [abc] +219 ] +220 +mem: clearing x:text +221 +222 :(before "End Rewrite Instruction(curr, recipe result)") +223 if (curr.name == "local-scope") { +224 rewrite_default_space_instruction(curr); +225 } +226 +227 //: todo: do this in a transform, rather than magically in the 'return' instruction +228 :(after "Falling Through End Of Recipe") +229 try_reclaim_locals(); +230 :(after "Starting Reply") +231 try_reclaim_locals(); +232 +233 :(code) +234 void try_reclaim_locals() { +235 if (!Reclaim_memory) return; +236 // only reclaim routines starting with 'local-scope' +237 const recipe_ordinal r = get(Recipe_ordinal, current_recipe_name()); +238 const recipe& exiting_recipe = get(Recipe, r); +239 if (exiting_recipe.steps.empty()) return; +240 const instruction& inst = exiting_recipe.steps.at(0); +241 if (inst.name_before_rewrite != "local-scope") return; +242 // reclaim any local variables unless they're being returned +243 vector<double> zeros; +244 for (int i = /*leave default space for last*/1; i < SIZE(exiting_recipe.steps); ++i) { +245 ¦ const instruction& inst = exiting_recipe.steps.at(i); +246 ¦ for (int i = 0; i < SIZE(inst.products); ++i) { +247 ¦ ¦ const reagent& product = inst.products.at(i); +248 ¦ ¦ // local variables only +249 ¦ ¦ if (has_property(product, "lookup")) continue; +250 ¦ ¦ if (has_property(product, "raw")) continue; // tests often want to check such locations after they run +251 ¦ ¦ if (escaping(product)) continue; +252 ¦ ¦ // End Checks For Reclaiming Locals +253 ¦ ¦ trace(9999, "mem") << "clearing " << product.original_string << end(); +254 ¦ ¦ zeros.resize(size_of(product)); +255 ¦ ¦ write_memory(product, zeros); +256 ¦ } +257 } +258 trace(9999, "mem") << "automatically abandoning " << current_call().default_space << end(); +259 abandon(current_call().default_space, +260 ¦ ¦ ¦ ¦ inst.products.at(0).type->right, +261 ¦ ¦ ¦ ¦ /*refcount*/1 + /*array length*/1 + /*number-of-locals*/Name[r][""]); +262 } +263 +264 //: Reclaiming local variables above requires remembering what name an +265 //: instruction had before any rewrites or transforms. +266 :(before "End instruction Fields") +267 string name_before_rewrite; +268 :(before "End instruction Clear") +269 name_before_rewrite.clear(); +270 :(before "End next_instruction(curr)") +271 curr->name_before_rewrite = curr->name; +272 +273 :(code) +274 // is this reagent one of the values returned by the current (return) instruction? +275 // is the corresponding ingredient saved in the caller? +276 bool escaping(const reagent& r) { +277 assert(Current_routine); // run-time only +278 // nothing escapes when you fall through past end of recipe +279 if (current_step_index() >= SIZE(Current_routine->steps())) return false; +280 for (long long i = 0; i < SIZE(current_instruction().ingredients); ++i) { +281 ¦ if (r == current_instruction().ingredients.at(i)) { +282 ¦ ¦ if (caller_uses_product(i)) +283 ¦ ¦ ¦ return true; +284 ¦ } +285 } +286 return false; +287 } +288 +289 //: since we don't decrement refcounts for escaping values above, make sure we +290 //: don't increment them when the caller saves them either +291 +292 :(before "End should_update_refcounts() Special-cases") +293 if (Writing_products_of_instruction) { +294 const instruction& inst = current_instruction(); +295 // should_update_refcounts() Special-cases When Writing Products Of Primitive Instructions +296 if (inst.operation < MAX_PRIMITIVE_RECIPES) return true; +297 if (!contains_key(Recipe, inst.operation)) return true; +298 const recipe& callee = get(Recipe, inst.operation); +299 if (callee.steps.empty()) return true; +300 return callee.steps.at(0).name_before_rewrite != "local-scope"; // callees that call local-scope are already dealt with before return +301 } +302 +303 :(code) +304 bool caller_uses_product(int product_index) { +305 assert(Current_routine); // run-time only +306 assert(!Current_routine->calls.empty()); +307 if (Current_routine->calls.size() == 1) return false; +308 const call& caller = *++Current_routine->calls.begin(); +309 const instruction& caller_inst = to_instruction(caller); +310 if (product_index >= SIZE(caller_inst.products)) return false; +311 return !is_dummy(caller_inst.products.at(product_index)); +312 } +313 +314 void rewrite_default_space_instruction(instruction& curr) { +315 if (!curr.ingredients.empty()) +316 ¦ raise << "'" << to_original_string(curr) << "' can't take any ingredients\n" << end(); +317 curr.name = "new"; +318 curr.ingredients.push_back(reagent("location:type")); +319 curr.ingredients.push_back(reagent("number-of-locals:literal")); +320 if (!curr.products.empty()) +321 ¦ raise << "new-default-space can't take any results\n" << end(); +322 curr.products.push_back(reagent("default-space:space")); +323 } +324 +325 :(scenario local_scope_frees_up_addresses_inside_containers) +326 container foo [ +327 x:num +328 y:&:num +329 ] +330 def main [ +331 local-scope +332 x:&:num <- new number:type +333 y:foo <- merge 34, x:&:num +334 # x and y are both cleared when main returns +335 ] +336 +mem: clearing x:&:num +337 +mem: decrementing refcount of 1006: 2 -> 1 +338 +mem: clearing y:foo +339 +mem: decrementing refcount of 1006: 1 -> 0 +340 +mem: automatically abandoning 1006 +341 +342 :(scenario local_scope_returns_addresses_inside_containers) +343 container foo [ +344 x:num +345 y:&:num +346 ] +347 def f [ +348 local-scope +349 x:&:num <- new number:type +350 *x:&:num <- copy 12 +351 y:foo <- merge 34, x:&:num +352 # since y is 'escaping' f, it should not be cleared +353 return y:foo +354 ] +355 def main [ +356 1:foo <- f +357 3:num <- get 1:foo, x:offset +358 4:&:num <- get 1:foo, y:offset +359 5:num <- copy *4:&:num +360 1:foo <- put 1:foo, y:offset, 0 +361 4:&:num <- copy 0 +362 ] +363 +mem: storing 34 in location 1 +364 +mem: storing 1006 in location 2 +365 +mem: storing 34 in location 3 +366 # refcount of 1:foo shouldn't include any stray ones from f +367 +run: {4: ("address" "number")} <- get {1: "foo"}, {y: "offset"} +368 +mem: incrementing refcount of 1006: 1 -> 2 +369 # 1:foo wasn't abandoned/cleared +370 +run: {5: "number"} <- copy {4: ("address" "number"), "lookup": ()} +371 +mem: storing 12 in location 5 +372 +run: {1: "foo"} <- put {1: "foo"}, {y: "offset"}, {0: "literal"} +373 +mem: decrementing refcount of 1006: 2 -> 1 +374 +run: {4: ("address" "number")} <- copy {0: "literal"} +375 +mem: decrementing refcount of 1006: 1 -> 0 +376 +mem: automatically abandoning 1006 +377 +378 :(scenario local_scope_claims_return_values_when_not_saved) +379 def f [ +380 local-scope +381 x:&:num <- new number:type +382 return x:&:num +383 ] +384 def main [ +385 f # doesn't save result +386 ] +387 # x reclaimed +388 +mem: automatically abandoning 1004 +389 # f's local scope reclaimed +390 +mem: automatically abandoning 1000 391 -392 :(before "End Globals") -393 bool Hide_missing_default_space_errors = true; -394 :(before "End Checks") -395 Transform.push_back(check_default_space); // idempotent -396 :(code) -397 void check_default_space(const recipe_ordinal r) { -398 if (Hide_missing_default_space_errors) return; // skip previous core tests; this is only for Mu code -399 const recipe& caller = get(Recipe, r); -400 // End check_default_space Special-cases -401 // assume recipes with only numeric addresses know what they're doing (usually tests) -402 if (!contains_non_special_name(r)) return; -403 trace(9991, "transform") << "--- check that recipe " << caller.name << " sets default-space" << end(); -404 if (caller.steps.empty()) return; -405 if (caller.steps.at(0).products.empty() -406 ¦ ¦ || caller.steps.at(0).products.at(0).name != "default-space") { -407 ¦ raise << caller.name << " does not seem to start with 'local-scope' or 'default-space'\n" << end(); -408 } -409 } -410 :(after "Load Mu Prelude") -411 Hide_missing_default_space_errors = false; -412 :(after "Test Runs") -413 Hide_missing_default_space_errors = true; -414 :(after "Running Main") -415 Hide_missing_default_space_errors = false; -416 -417 :(code) -418 bool contains_non_special_name(const recipe_ordinal r) { -419 for (map<string, int>::iterator p = Name[r].begin(); p != Name[r].end(); ++p) { -420 ¦ if (p->first.empty()) continue; -421 ¦ if (p->first.find("stash_") == 0) continue; // generated by rewrite_stashes_to_text (cross-layer) -422 ¦ if (!is_special_name(p->first)) -423 ¦ ¦ return true; -424 } -425 return false; -426 } -427 -428 // reagent comparison -- only between reagents in a single recipe -429 bool operator==(const reagent& a, const reagent& b) { -430 if (a.name != b.name) return false; -431 if (property(a, "space") != property(b, "space")) return false; -432 return true; -433 } -434 -435 bool operator<(const reagent& a, const reagent& b) { -436 int aspace = 0, bspace = 0; -437 if (has_property(a, "space")) aspace = to_integer(property(a, "space")->value); -438 if (has_property(b, "space")) bspace = to_integer(property(b, "space")->value); -439 if (aspace != bspace) return aspace < bspace; -440 return a.name < b.name; -441 } +392 //:: all recipes must set default-space one way or another +393 +394 :(before "End Globals") +395 bool Hide_missing_default_space_errors = true; +396 :(before "End Checks") +397 Transform.push_back(check_default_space); // idempotent +398 :(code) +399 void check_default_space(const recipe_ordinal r) { +400 if (Hide_missing_default_space_errors) return; // skip previous core tests; this is only for Mu code +401 const recipe& caller = get(Recipe, r); +402 // End check_default_space Special-cases +403 // assume recipes with only numeric addresses know what they're doing (usually tests) +404 if (!contains_non_special_name(r)) return; +405 trace(9991, "transform") << "--- check that recipe " << caller.name << " sets default-space" << end(); +406 if (caller.steps.empty()) return; +407 if (caller.steps.at(0).products.empty() +408 ¦ ¦ || caller.steps.at(0).products.at(0).name != "default-space") { +409 ¦ raise << caller.name << " does not seem to start with 'local-scope' or 'default-space'\n" << end(); +410 } +411 } +412 :(after "Load Mu Prelude") +413 Hide_missing_default_space_errors = false; +414 :(after "Test Runs") +415 Hide_missing_default_space_errors = true; +416 :(after "Running Main") +417 Hide_missing_default_space_errors = false; +418 +419 :(code) +420 bool contains_non_special_name(const recipe_ordinal r) { +421 for (map<string, int>::iterator p = Name[r].begin(); p != Name[r].end(); ++p) { +422 ¦ if (p->first.empty()) continue; +423 ¦ if (p->first.find("stash_") == 0) continue; // generated by rewrite_stashes_to_text (cross-layer) +424 ¦ if (!is_special_name(p->first)) +425 ¦ ¦ return true; +426 } +427 return false; +428 } +429 +430 // reagent comparison -- only between reagents in a single recipe +431 bool operator==(const reagent& a, const reagent& b) { +432 if (a.name != b.name) return false; +433 if (property(a, "space") != property(b, "space")) return false; +434 return true; +435 } +436 +437 bool operator<(const reagent& a, const reagent& b) { +438 int aspace = 0, bspace = 0; +439 if (has_property(a, "space")) aspace = to_integer(property(a, "space")->value); +440 if (has_property(b, "space")) bspace = to_integer(property(b, "space")->value); +441 if (aspace != bspace) return aspace < bspace; +442 return a.name < b.name; +443 } -- cgit 1.4.1-2-gfad0