From 5fe060d582d4a82444243a28b18085c971a85628 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Fri, 27 Jul 2018 17:07:52 -0700 Subject: 4447 --- html/034address.cc.html | 732 +++++++++++++++++++++++++++--------------------- 1 file changed, 419 insertions(+), 313 deletions(-) (limited to 'html/034address.cc.html') diff --git a/html/034address.cc.html b/html/034address.cc.html index 48fcd0aa..a1beb9aa 100644 --- a/html/034address.cc.html +++ b/html/034address.cc.html @@ -81,320 +81,426 @@ if ('onhashchange' in window) { 18 //: write to the payload of an ingredient rather than its value, simply add 19 //: the /lookup property to it. Modern computers provide efficient support for 20 //: addresses and lookups, making this a realistic feature. - 21 - 22 //: todo: give 'new' a custodian ingredient. Following malloc/free is a temporary hack. - 23 - 24 :(scenario new) - 25 # call 'new' two times with identical types without modifying the results; you - 26 # should get back different results - 27 def main [ - 28 1:address:num/raw <- new number:type - 29 2:address:num/raw <- new number:type - 30 3:bool/raw <- equal 1:address:num/raw, 2:address:num/raw - 31 ] - 32 +mem: storing 0 in location 3 + 21 //: + 22 //: To create addresses and allocate memory exclusively for their use, use + 23 //: 'new'. Memory is a finite resource so if the computer can't satisfy your + 24 //: request, 'new' may return a 0 (null) address. + 25 //: + 26 //: Computers these days have lots of memory so in practice we can often + 27 //: assume we'll never run out. If you start running out however, say in a + 28 //: long-running program, you'll need to switch mental gears and start + 29 //: husbanding our memory more carefully. The most important tool to avoid + 30 //: wasting memory is to 'abandon' an address when you don't need it anymore. + 31 //: That frees up the memory allocated to it to be reused in future calls to + 32 //: 'new'. 33 - 34 :(scenario new_array) - 35 # call 'new' with a second ingredient to allocate an array of some type rather than a single copy - 36 def main [ - 37 1:address:array:num/raw <- new number:type, 5 - 38 2:address:num/raw <- new number:type - 39 3:num/raw <- subtract 2:address:num/raw, 1:address:array:num/raw - 40 ] - 41 +run: {1: ("address" "array" "number"), "raw": ()} <- new {number: "type"}, {5: "literal"} - 42 +mem: array length is 5 - 43 # don't forget the extra location for array length - 44 +mem: storing 6 in location 3 - 45 - 46 :(scenario dilated_reagent_with_new) - 47 def main [ - 48 1:address:address:num <- new {(address number): type} - 49 ] - 50 +new: size of '(address number)' is 1 - 51 - 52 //: 'new' takes a weird 'type' as its first ingredient; don't error on it - 53 :(before "End Mu Types Initialization") - 54 put(Type_ordinal, "type", 0); - 55 :(code) - 56 bool is_mu_type_literal(const reagent& r) { - 57 return is_literal(r) && r.type && r.type->name == "type"; - 58 } - 59 - 60 :(before "End Primitive Recipe Declarations") - 61 NEW, - 62 :(before "End Primitive Recipe Numbers") - 63 put(Recipe_ordinal, "new", NEW); - 64 :(before "End Primitive Recipe Checks") - 65 case NEW: { - 66 const recipe& caller = get(Recipe, r); - 67 if (inst.ingredients.empty() || SIZE(inst.ingredients) > 2) { - 68 raise << maybe(caller.name) << "'new' requires one or two ingredients, but got '" << to_original_string(inst) << "'\n" << end(); - 69 break; - 70 } - 71 // End NEW Check Special-cases - 72 const reagent& type = inst.ingredients.at(0); - 73 if (!is_mu_type_literal(type)) { - 74 raise << maybe(caller.name) << "first ingredient of 'new' should be a type, but got '" << type.original_string << "'\n" << end(); - 75 break; - 76 } - 77 if (SIZE(inst.ingredients) > 1 && !is_mu_number(inst.ingredients.at(1))) { - 78 raise << maybe(caller.name) << "second ingredient of 'new' should be a number (array length), but got '" << type.original_string << "'\n" << end(); - 79 break; - 80 } - 81 if (inst.products.empty()) { - 82 raise << maybe(caller.name) << "result of 'new' should never be ignored\n" << end(); - 83 break; - 84 } - 85 if (!product_of_new_is_valid(inst)) { - 86 raise << maybe(caller.name) << "product of 'new' has incorrect type: '" << to_original_string(inst) << "'\n" << end(); - 87 break; - 88 } - 89 break; - 90 } - 91 :(code) - 92 bool product_of_new_is_valid(const instruction& inst) { - 93 reagent/*copy*/ product = inst.products.at(0); - 94 // Update NEW product in Check - 95 if (!product.type || product.type->atom || product.type->left->value != Address_type_ordinal) - 96 return false; - 97 drop_from_type(product, "address"); - 98 if (SIZE(inst.ingredients) > 1) { - 99 // array allocation -100 if (!product.type || product.type->atom || product.type->left->value != Array_type_ordinal) -101 return false; -102 drop_from_type(product, "array"); -103 } -104 reagent/*local*/ expected_product(new_type_tree(inst.ingredients.at(0).name)); -105 return types_strictly_match(product, expected_product); -106 } -107 -108 void drop_from_type(reagent& r, string expected_type) { -109 assert(!r.type->atom); -110 if (r.type->left->name != expected_type) { -111 raise << "can't drop2 " << expected_type << " from '" << to_string(r) << "'\n" << end(); -112 return; -113 } -114 // r.type = r.type->right -115 type_tree* tmp = r.type; -116 r.type = tmp->right; -117 tmp->right = NULL; -118 delete tmp; -119 // if (!r.type->right) r.type = r.type->left -120 assert(!r.type->atom); -121 if (r.type->right) return; -122 tmp = r.type; -123 r.type = tmp->left; -124 tmp->left = NULL; -125 delete tmp; -126 } -127 -128 :(scenario new_returns_incorrect_type) -129 % Hide_errors = true; -130 def main [ -131 1:bool <- new num:type -132 ] -133 +error: main: product of 'new' has incorrect type: '1:bool <- new num:type' -134 -135 :(scenario new_discerns_singleton_list_from_atom_container) -136 % Hide_errors = true; -137 def main [ -138 1:address:num/raw <- new {(num): type} # should be '{num: type}' -139 ] -140 +error: main: product of 'new' has incorrect type: '1:address:num/raw <- new {(num): type}' -141 -142 :(scenario new_with_type_abbreviation) -143 def main [ -144 1:address:num/raw <- new num:type -145 ] -146 $error: 0 -147 -148 :(scenario new_with_type_abbreviation_inside_compound) -149 def main [ -150 {1: (address address number), raw: ()} <- new {(& num): type} -151 ] -152 $error: 0 -153 -154 //: To implement 'new', a Mu transform turns all 'new' instructions into -155 //: 'allocate' instructions that precompute the amount of memory they want to -156 //: allocate. -157 -158 //: Ensure that we never call 'allocate' directly, and that there's no 'new' -159 //: instructions left after the transforms have run. -160 :(before "End Primitive Recipe Checks") -161 case ALLOCATE: { -162 raise << "never call 'allocate' directly'; always use 'new'\n" << end(); -163 break; -164 } -165 :(before "End Primitive Recipe Implementations") -166 case NEW: { -167 raise << "no implementation for 'new'; why wasn't it translated to 'allocate'? Please save a copy of your program and send it to Kartik.\n" << end(); -168 break; -169 } -170 -171 :(after "Transform.push_back(check_instruction)") // check_instruction will guard against direct 'allocate' instructions below -172 Transform.push_back(transform_new_to_allocate); // idempotent -173 -174 :(code) -175 void transform_new_to_allocate(const recipe_ordinal r) { -176 trace(9991, "transform") << "--- convert 'new' to 'allocate' for recipe " << get(Recipe, r).name << end(); -177 for (int i = 0; i < SIZE(get(Recipe, r).steps); ++i) { -178 instruction& inst = get(Recipe, r).steps.at(i); -179 // Convert 'new' To 'allocate' -180 if (inst.name == "new") { -181 if (inst.ingredients.empty()) return; // error raised elsewhere -182 inst.operation = ALLOCATE; -183 type_tree* type = new_type_tree(inst.ingredients.at(0).name); -184 inst.ingredients.at(0).set_value(size_of(type)); -185 trace(9992, "new") << "size of '" << inst.ingredients.at(0).name << "' is " << inst.ingredients.at(0).value << end(); -186 delete type; -187 } -188 } -189 } -190 -191 //: implement 'allocate' based on size -192 -193 :(before "End Globals") -194 extern const int Reserved_for_tests = 1000; -195 int Memory_allocated_until = Reserved_for_tests; -196 int Initial_memory_per_routine = 100000; -197 :(before "End Reset") -198 Memory_allocated_until = Reserved_for_tests; -199 Initial_memory_per_routine = 100000; -200 :(before "End routine Fields") -201 int alloc, alloc_max; -202 :(before "End routine Constructor") -203 alloc = Memory_allocated_until; -204 Memory_allocated_until += Initial_memory_per_routine; -205 alloc_max = Memory_allocated_until; -206 trace("new") << "routine allocated memory from " << alloc << " to " << alloc_max << end(); -207 -208 :(before "End Primitive Recipe Declarations") -209 ALLOCATE, -210 :(before "End Primitive Recipe Numbers") -211 put(Recipe_ordinal, "allocate", ALLOCATE); -212 :(before "End Primitive Recipe Implementations") -213 case ALLOCATE: { -214 // compute the space we need -215 int size = ingredients.at(0).at(0); -216 if (SIZE(ingredients) > 1) { -217 // array allocation -218 trace("mem") << "array length is " << ingredients.at(1).at(0) << end(); -219 size = /*space for length*/1 + size*ingredients.at(1).at(0); -220 } -221 int result = allocate(size); -222 if (SIZE(current_instruction().ingredients) > 1) { -223 // initialize array length -224 trace("mem") << "storing " << ingredients.at(1).at(0) << " in location " << result << end(); -225 put(Memory, result, ingredients.at(1).at(0)); -226 } -227 products.resize(1); -228 products.at(0).push_back(result); -229 break; -230 } -231 :(code) -232 int allocate(int size) { -233 trace("mem") << "allocating size " << size << end(); -234 //? Total_alloc += size; -235 //? ++Num_alloc; -236 // Allocate Special-cases -237 // compute the region of memory to return -238 // really crappy at the moment -239 ensure_space(size); -240 const int result = Current_routine->alloc; -241 trace("mem") << "new alloc: " << result << end(); -242 // initialize allocated space -243 for (int address = result; address < result+size; ++address) { -244 trace("mem") << "storing 0 in location " << address << end(); -245 put(Memory, address, 0); -246 } -247 Current_routine->alloc += size; -248 // no support yet for reclaiming memory between routines -249 assert(Current_routine->alloc <= Current_routine->alloc_max); -250 return result; -251 } -252 -253 //: statistics for debugging -254 //? :(before "End Globals") -255 //? int Total_alloc = 0; -256 //? int Num_alloc = 0; -257 //? int Total_free = 0; -258 //? int Num_free = 0; -259 //? :(before "End Reset") -260 //? if (!Memory.empty()) { -261 //? cerr << Total_alloc << "/" << Num_alloc -262 //? << " vs " << Total_free << "/" << Num_free << '\n'; -263 //? cerr << SIZE(Memory) << '\n'; -264 //? } -265 //? Total_alloc = Num_alloc = Total_free = Num_free = 0; -266 -267 :(code) -268 void ensure_space(int size) { -269 if (size > Initial_memory_per_routine) { -270 cerr << "can't allocate " << size << " locations, that's too much compared to " << Initial_memory_per_routine << ".\n"; -271 exit(1); -272 } -273 if (Current_routine->alloc + size > Current_routine->alloc_max) { -274 // waste the remaining space and create a new chunk -275 Current_routine->alloc = Memory_allocated_until; -276 Memory_allocated_until += Initial_memory_per_routine; -277 Current_routine->alloc_max = Memory_allocated_until; -278 trace("new") << "routine allocated memory from " << Current_routine->alloc << " to " << Current_routine->alloc_max << end(); -279 } -280 } -281 -282 :(scenario new_initializes) -283 % Memory_allocated_until = 10; -284 % put(Memory, Memory_allocated_until, 1); -285 def main [ -286 1:address:num <- new number:type -287 ] -288 +mem: storing 0 in location 10 -289 -290 :(scenario new_size) -291 def main [ -292 11:address:num/raw <- new number:type -293 12:address:num/raw <- new number:type -294 13:num/raw <- subtract 12:address:num/raw, 11:address:num/raw -295 ] -296 # size of number -297 +mem: storing 1 in location 13 -298 -299 :(scenario new_array_size) -300 def main [ -301 1:address:array:num/raw <- new number:type, 5 -302 2:address:num/raw <- new number:type -303 3:num/raw <- subtract 2:address:num/raw, 1:address:array:num/raw -304 ] -305 # 5 locations for array contents + array length -306 +mem: storing 6 in location 3 -307 -308 :(scenario new_empty_array) -309 def main [ -310 1:address:array:num/raw <- new number:type, 0 -311 2:address:num/raw <- new number:type -312 3:num/raw <- subtract 2:address:num/raw, 1:address:array:num/raw -313 ] -314 +run: {1: ("address" "array" "number"), "raw": ()} <- new {number: "type"}, {0: "literal"} -315 +mem: array length is 0 -316 # one location for array length -317 +mem: storing 1 in location 3 -318 -319 //: If a routine runs out of its initial allocation, it should allocate more. -320 :(scenario new_overflow) -321 % Initial_memory_per_routine = 2; // barely enough room for point allocation below -322 def main [ -323 1:address:num/raw <- new number:type -324 2:address:point/raw <- new point:type # not enough room in initial page -325 ] -326 +new: routine allocated memory from 1000 to 1002 -327 +new: routine allocated memory from 1002 to 1004 -328 -329 :(scenario new_without_ingredient) -330 % Hide_errors = true; -331 def main [ -332 1:address:number <- new # missing ingredient -333 ] -334 +error: main: 'new' requires one or two ingredients, but got '1:address:number <- new' + 34 //: Since memory can be reused multiple times, it can happen that you have a + 35 //: stale copy to an address that has since been abandoned and reused. Using + 36 //: the stale address is almost never safe, but it can be very hard to track + 37 //: down such copies because any errors caused by them may occur even millions + 38 //: of instructions after the copy or abandon instruction. To help track down + 39 //: such issues, Mu tracks an 'alloc id' for each allocation it makes. The + 40 //: first call to 'new' has an alloc id of 1, the second gets 2, and so on. + 41 //: The alloc id is never reused. + 42 :(before "End Globals") + 43 long long Next_alloc_id = 0; + 44 :(before "End Reset") + 45 Next_alloc_id = 0; + 46 + 47 //: The 'new' instruction records alloc ids both in the memory being allocated + 48 //: and *also* in the address. The 'abandon' instruction clears alloc ids in + 49 //: both places as well. Tracking alloc ids in this manner allows us to raise + 50 //: errors about stale addresses much earlier: 'lookup' operations always + 51 //: compare alloc ids between the address and its payload. + 52 + 53 //: todo: give 'new' a custodian ingredient. Following malloc/free is a temporary hack. + 54 + 55 :(scenario new) + 56 # call 'new' two times with identical types without modifying the results; you + 57 # should get back different results + 58 def main [ + 59 10:&:num <- new num:type + 60 12:&:num <- new num:type + 61 20:bool <- equal 10:&:num, 12:&:num + 62 ] + 63 +mem: storing 1000 in location 11 + 64 +mem: storing 0 in location 20 + 65 + 66 :(scenario new_array) + 67 # call 'new' with a second ingredient to allocate an array of some type rather than a single copy + 68 def main [ + 69 10:&:@:num <- new num:type, 5 + 70 12:&:num <- new num:type + 71 20:num/alloc2, 21:num/alloc1 <- deaddress 10:&:@:num, 12:&:num + 72 30:num <- subtract 21:num/alloc2, 20:num/alloc1 + 73 ] + 74 +run: {10: ("address" "array" "number")} <- new {num: "type"}, {5: "literal"} + 75 +mem: array length is 5 + 76 # skip alloc id in allocation + 77 +mem: storing 1000 in location 11 + 78 # don't forget the extra locations for alloc id and array length + 79 +mem: storing 7 in location 30 + 80 + 81 :(scenario dilated_reagent_with_new) + 82 def main [ + 83 10:&:&:num <- new {(& num): type} + 84 ] + 85 +new: size of '(& num)' is 2 + 86 + 87 //: 'new' takes a weird 'type' as its first ingredient; don't error on it + 88 :(before "End Mu Types Initialization") + 89 put(Type_ordinal, "type", 0); + 90 :(code) + 91 bool is_mu_type_literal(const reagent& r) { + 92 return is_literal(r) && r.type && r.type->name == "type"; + 93 } + 94 + 95 :(before "End Primitive Recipe Declarations") + 96 NEW, + 97 :(before "End Primitive Recipe Numbers") + 98 put(Recipe_ordinal, "new", NEW); + 99 :(before "End Primitive Recipe Checks") +100 case NEW: { +101 const recipe& caller = get(Recipe, r); +102 if (inst.ingredients.empty() || SIZE(inst.ingredients) > 2) { +103 raise << maybe(caller.name) << "'new' requires one or two ingredients, but got '" << to_original_string(inst) << "'\n" << end(); +104 break; +105 } +106 // End NEW Check Special-cases +107 const reagent& type = inst.ingredients.at(0); +108 if (!is_mu_type_literal(type)) { +109 raise << maybe(caller.name) << "first ingredient of 'new' should be a type, but got '" << type.original_string << "'\n" << end(); +110 break; +111 } +112 if (SIZE(inst.ingredients) > 1 && !is_mu_number(inst.ingredients.at(1))) { +113 raise << maybe(caller.name) << "second ingredient of 'new' should be a number (array length), but got '" << type.original_string << "'\n" << end(); +114 break; +115 } +116 if (inst.products.empty()) { +117 raise << maybe(caller.name) << "result of 'new' should never be ignored\n" << end(); +118 break; +119 } +120 if (!product_of_new_is_valid(inst)) { +121 raise << maybe(caller.name) << "product of 'new' has incorrect type: '" << to_original_string(inst) << "'\n" << end(); +122 break; +123 } +124 break; +125 } +126 :(code) +127 bool product_of_new_is_valid(const instruction& inst) { +128 reagent/*copy*/ product = inst.products.at(0); +129 // Update NEW product in Check +130 if (!product.type || product.type->atom || product.type->left->value != Address_type_ordinal) +131 return false; +132 drop_from_type(product, "address"); +133 if (SIZE(inst.ingredients) > 1) { +134 // array allocation +135 if (!product.type || product.type->atom || product.type->left->value != Array_type_ordinal) +136 return false; +137 drop_from_type(product, "array"); +138 } +139 reagent/*local*/ expected_product(new_type_tree(inst.ingredients.at(0).name)); +140 return types_strictly_match(product, expected_product); +141 } +142 +143 void drop_from_type(reagent& r, string expected_type) { +144 assert(!r.type->atom); +145 if (r.type->left->name != expected_type) { +146 raise << "can't drop2 " << expected_type << " from '" << to_string(r) << "'\n" << end(); +147 return; +148 } +149 // r.type = r.type->right +150 type_tree* tmp = r.type; +151 r.type = tmp->right; +152 tmp->right = NULL; +153 delete tmp; +154 // if (!r.type->right) r.type = r.type->left +155 assert(!r.type->atom); +156 if (r.type->right) return; +157 tmp = r.type; +158 r.type = tmp->left; +159 tmp->left = NULL; +160 delete tmp; +161 } +162 +163 :(scenario new_returns_incorrect_type) +164 % Hide_errors = true; +165 def main [ +166 1:bool <- new num:type +167 ] +168 +error: main: product of 'new' has incorrect type: '1:bool <- new num:type' +169 +170 :(scenario new_discerns_singleton_list_from_atom_container) +171 % Hide_errors = true; +172 def main [ +173 1:&:num <- new {(num): type} # should be '{num: type}' +174 ] +175 +error: main: product of 'new' has incorrect type: '1:&:num <- new {(num): type}' +176 +177 :(scenario new_with_type_abbreviation) +178 def main [ +179 1:&:num <- new num:type +180 ] +181 $error: 0 +182 +183 :(scenario new_with_type_abbreviation_inside_compound) +184 def main [ +185 {1: (address address number), raw: ()} <- new {(& num): type} +186 ] +187 $error: 0 +188 +189 :(scenario equal_result_of_new_with_null) +190 def main [ +191 1:&:num <- new num:type +192 10:bool <- equal 1:&:num, null +193 ] +194 +mem: storing 0 in location 10 +195 +196 //: To implement 'new', a Mu transform turns all 'new' instructions into +197 //: 'allocate' instructions that precompute the amount of memory they want to +198 //: allocate. +199 +200 //: Ensure that we never call 'allocate' directly, and that there's no 'new' +201 //: instructions left after the transforms have run. +202 :(before "End Primitive Recipe Checks") +203 case ALLOCATE: { +204 raise << "never call 'allocate' directly'; always use 'new'\n" << end(); +205 break; +206 } +207 :(before "End Primitive Recipe Implementations") +208 case NEW: { +209 raise << "no implementation for 'new'; why wasn't it translated to 'allocate'? Please save a copy of your program and send it to Kartik.\n" << end(); +210 break; +211 } +212 +213 :(after "Transform.push_back(check_instruction)") // check_instruction will guard against direct 'allocate' instructions below +214 Transform.push_back(transform_new_to_allocate); // idempotent +215 +216 :(code) +217 void transform_new_to_allocate(const recipe_ordinal r) { +218 trace(9991, "transform") << "--- convert 'new' to 'allocate' for recipe " << get(Recipe, r).name << end(); +219 for (int i = 0; i < SIZE(get(Recipe, r).steps); ++i) { +220 instruction& inst = get(Recipe, r).steps.at(i); +221 // Convert 'new' To 'allocate' +222 if (inst.name == "new") { +223 if (inst.ingredients.empty()) return; // error raised elsewhere +224 inst.operation = ALLOCATE; +225 type_tree* type = new_type_tree(inst.ingredients.at(0).name); +226 inst.ingredients.at(0).set_value(size_of(type)); +227 trace(9992, "new") << "size of '" << inst.ingredients.at(0).name << "' is " << inst.ingredients.at(0).value << end(); +228 delete type; +229 } +230 } +231 } +232 +233 //: implement 'allocate' based on size +234 +235 :(before "End Globals") +236 extern const int Reserved_for_tests = 1000; +237 int Memory_allocated_until = Reserved_for_tests; +238 int Initial_memory_per_routine = 100000; +239 :(before "End Reset") +240 Memory_allocated_until = Reserved_for_tests; +241 Initial_memory_per_routine = 100000; +242 :(before "End routine Fields") +243 int alloc, alloc_max; +244 :(before "End routine Constructor") +245 alloc = Memory_allocated_until; +246 Memory_allocated_until += Initial_memory_per_routine; +247 alloc_max = Memory_allocated_until; +248 trace("new") << "routine allocated memory from " << alloc << " to " << alloc_max << end(); +249 +250 :(before "End Primitive Recipe Declarations") +251 ALLOCATE, +252 :(before "End Primitive Recipe Numbers") +253 put(Recipe_ordinal, "allocate", ALLOCATE); +254 :(before "End Primitive Recipe Implementations") +255 case ALLOCATE: { +256 // compute the space we need +257 int size = ingredients.at(0).at(0); +258 int alloc_id = Next_alloc_id; +259 Next_alloc_id++; +260 if (SIZE(ingredients) > 1) { +261 // array allocation +262 trace("mem") << "array length is " << ingredients.at(1).at(0) << end(); +263 size = /*space for length*/1 + size*ingredients.at(1).at(0); +264 } +265 int result = allocate(size); +266 // initialize alloc-id in payload +267 trace("mem") << "storing alloc-id " << alloc_id << " in location " << result << end(); +268 put(Memory, result, alloc_id); +269 if (SIZE(current_instruction().ingredients) > 1) { +270 // initialize array length +271 trace("mem") << "storing array length " << ingredients.at(1).at(0) << " in location " << result+/*skip alloc id*/1 << end(); +272 put(Memory, result+/*skip alloc id*/1, ingredients.at(1).at(0)); +273 } +274 products.resize(1); +275 products.at(0).push_back(alloc_id); +276 products.at(0).push_back(result); +277 break; +278 } +279 :(code) +280 int allocate(int size) { +281 // include space for alloc id +282 ++size; +283 trace("mem") << "allocating size " << size << end(); +284 //? Total_alloc += size; +285 //? ++Num_alloc; +286 // Allocate Special-cases +287 // compute the region of memory to return +288 // really crappy at the moment +289 ensure_space(size); +290 const int result = Current_routine->alloc; +291 trace("mem") << "new alloc: " << result << end(); +292 // initialize allocated space +293 for (int address = result; address < result+size; ++address) { +294 trace("mem") << "storing 0 in location " << address << end(); +295 put(Memory, address, 0); +296 } +297 Current_routine->alloc += size; +298 // no support yet for reclaiming memory between routines +299 assert(Current_routine->alloc <= Current_routine->alloc_max); +300 return result; +301 } +302 +303 //: statistics for debugging +304 //? :(before "End Globals") +305 //? int Total_alloc = 0; +306 //? int Num_alloc = 0; +307 //? int Total_free = 0; +308 //? int Num_free = 0; +309 //? :(before "End Reset") +310 //? if (!Memory.empty()) { +311 //? cerr << Total_alloc << "/" << Num_alloc +312 //? << " vs " << Total_free << "/" << Num_free << '\n'; +313 //? cerr << SIZE(Memory) << '\n'; +314 //? } +315 //? Total_alloc = Num_alloc = Total_free = Num_free = 0; +316 +317 :(code) +318 void ensure_space(int size) { +319 if (size > Initial_memory_per_routine) { +320 cerr << "can't allocate " << size << " locations, that's too much compared to " << Initial_memory_per_routine << ".\n"; +321 exit(1); +322 } +323 if (Current_routine->alloc + size > Current_routine->alloc_max) { +324 // waste the remaining space and create a new chunk +325 Current_routine->alloc = Memory_allocated_until; +326 Memory_allocated_until += Initial_memory_per_routine; +327 Current_routine->alloc_max = Memory_allocated_until; +328 trace("new") << "routine allocated memory from " << Current_routine->alloc << " to " << Current_routine->alloc_max << end(); +329 } +330 } +331 +332 :(scenario new_initializes) +333 % Memory_allocated_until = 10; +334 % put(Memory, Memory_allocated_until, 1); +335 def main [ +336 1:&:num <- new num:type +337 ] +338 +mem: storing 0 in location 10 +339 +mem: storing 0 in location 11 +340 +mem: storing 10 in location 2 +341 +342 :(scenario new_initializes_alloc_id) +343 % Memory_allocated_until = 10; +344 % put(Memory, Memory_allocated_until, 1); +345 % Next_alloc_id = 23; +346 def main [ +347 1:&:num <- new num:type +348 ] +349 # initialize memory +350 +mem: storing 0 in location 10 +351 +mem: storing 0 in location 11 +352 # alloc-id in payload +353 +mem: storing alloc-id 23 in location 10 +354 # alloc-id in address +355 +mem: storing 23 in location 1 +356 +357 :(scenario new_size) +358 def main [ +359 10:&:num <- new num:type +360 12:&:num <- new num:type +361 20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:num, 12:&:num +362 30:num <- subtract 21:num/alloc2, 20:num/alloc1 +363 ] +364 # size of number + alloc id +365 +mem: storing 2 in location 30 +366 +367 :(scenario new_array_size) +368 def main [ +369 10:&:@:num <- new num:type, 5 +370 12:&:num <- new num:type +371 20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:num, 12:&:num +372 30:num <- subtract 21:num/alloc2, 20:num/alloc1 +373 ] +374 # 5 locations for array contents + array length + alloc id +375 +mem: storing 7 in location 30 +376 +377 :(scenario new_empty_array) +378 def main [ +379 10:&:@:num <- new num:type, 0 +380 12:&:num <- new num:type +381 20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:@:num, 12:&:num +382 30:num <- subtract 21:num/alloc2, 20:num/alloc1 +383 ] +384 +run: {10: ("address" "array" "number")} <- new {num: "type"}, {0: "literal"} +385 +mem: array length is 0 +386 # one location for array length +387 +mem: storing 2 in location 30 +388 +389 //: If a routine runs out of its initial allocation, it should allocate more. +390 :(scenario new_overflow) +391 % Initial_memory_per_routine = 3; // barely enough room for point allocation below +392 def main [ +393 10:&:num <- new num:type +394 12:&:point <- new point:type # not enough room in initial page +395 ] +396 +new: routine allocated memory from 1000 to 1003 +397 +new: routine allocated memory from 1003 to 1006 +398 +399 :(scenario new_without_ingredient) +400 % Hide_errors = true; +401 def main [ +402 1:&:num <- new # missing ingredient +403 ] +404 +error: main: 'new' requires one or two ingredients, but got '1:&:num <- new' +405 +406 //: a little helper: convert address to number +407 +408 :(before "End Primitive Recipe Declarations") +409 DEADDRESS, +410 :(before "End Primitive Recipe Numbers") +411 put(Recipe_ordinal, "deaddress", DEADDRESS); +412 :(before "End Primitive Recipe Checks") +413 case DEADDRESS: { +414 // primary goal of these checks is to forbid address arithmetic +415 for (int i = 0; i < SIZE(inst.ingredients); ++i) { +416 if (!is_mu_address(inst.ingredients.at(i))) { +417 raise << maybe(get(Recipe, r).name) << "'deaddress' requires address ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end(); +418 goto finish_checking_instruction; +419 } +420 } +421 if (SIZE(inst.products) > SIZE(inst.ingredients)) { +422 raise << maybe(get(Recipe, r).name) << "too many products in '" << to_original_string(inst) << "'\n" << end(); +423 break; +424 } +425 for (int i = 0; i < SIZE(inst.products); ++i) { +426 if (!is_real_mu_number(inst.products.at(i))) { +427 raise << maybe(get(Recipe, r).name) << "'deaddress' requires number products, but got '" << inst.products.at(i).original_string << "'\n" << end(); +428 goto finish_checking_instruction; +429 } +430 } +431 break; +432 } +433 :(before "End Primitive Recipe Implementations") +434 case DEADDRESS: { +435 products.resize(SIZE(ingredients)); +436 for (int i = 0; i < SIZE(ingredients); ++i) { +437 products.at(i).push_back(ingredients.at(i).at(/*skip alloc id*/1)); +438 } +439 break; +440 } -- cgit 1.4.1-2-gfad0