1
2
3
4
5
6 :(before "End Mu Types Initialization")
7 put(Type_abbreviations, "space", new_type_tree("address:array:location"));
8
9 put(Type_abbreviations, "scope", new_type_tree("address:array:location"));
10
11 :(scenario set_default_space)
12
13
14 def main [
15
16 10:num <- copy 0
17 11:num <- copy 5
18 default-space:space <- copy 10/unsafe
19 1:num <- copy 23
20 ]
21 +mem: storing 23 in location 13
22
23 :(scenario lookup_sidesteps_default_space)
24 def main [
25
26 2001:num <- copy 34
27
28 1000:num <- copy 0
29 1001:num <- copy 5
30
31 default-space:space <- copy 1000/unsafe
32 1:&:num <- copy 2000/unsafe
33 8:num/raw <- copy *1:&:num
34 ]
35 +mem: storing 34 in location 8
36
37
38 :(scenario convert_names_passes_default_space)
39 % Hide_errors = true;
40 def main [
41 default-space:num, x:num <- copy 0, 1
42 ]
43 +name: assign x 1
44 -name: assign default-space 1
45
46 :(before "End is_disqualified Special-cases")
47 if (x.name == "default-space")
48 x.initialized = true;
49 :(before "End is_special_name Special-cases")
50 if (s == "default-space") return true;
51
52
53 :(before "End call Fields")
54 int default_space;
55 :(before "End call Constructor")
56 default_space = 0;
57
58 :(before "End canonize(x) Special-cases")
59 absolutize(x);
60 :(code)
61 void absolutize(reagent& x) {
62 if (is_raw(x) || is_dummy(x)) return;
63 if (x.name == "default-space") return;
64 if (!x.initialized)
65 raise << to_original_string(current_instruction()) << ": reagent not initialized: '" << x.original_string << "'\n" << end();
66 x.set_value(address(x.value, space_base(x)));
67 x.properties.push_back(pair<string, string_tree*>("raw", NULL));
68 assert(is_raw(x));
69 }
70
71
72 int space_base(const reagent& x) {
73 return current_call().default_space ? (current_call().default_space+1) : 0;
74 }
75
76 int address(int offset, int base) {
77 assert(offset >= 0);
78 if (base == 0) return offset;
79 int size = get_or_insert(Memory, base);
80 if (offset >= size) {
81
82 raise << "location " << offset << " is out of bounds " << size << " at " << base << '\n' << end();
83 return 0;
84 }
85 return base + 1 + offset;
86 }
87
88
89
90 :(after "Begin Preprocess write_memory(x, data)")
91 if (x.name == "default-space") {
92 if (!scalar(data) || !is_space(x))
93 raise << maybe(current_recipe_name()) << "'default-space' should be of type address:array:location, but is " << to_string(x.type) << '\n' << end();
94 current_call().default_space = data.at(0);
95 return;
96 }
97 :(code)
98 bool is_space(const reagent& r) {
99 return is_address_of_array_of_numbers(r);
100 }
101
102 :(scenario get_default_space)
103 def main [
104 default-space:space <- copy 10/unsafe
105 1:space/raw <- copy default-space:space
106 ]
107 +mem: storing 10 in location 1
108
109 :(after "Begin Preprocess read_memory(x)")
110 if (x.name == "default-space") {
111 vector<double> result;
112 result.push_back(current_call().default_space);
113 return result;
114 }
115
116
117
118 :(scenario lookup_sidesteps_default_space_in_get)
119 def main [
120
121 2001:num <- copy 34
122 2002:num <- copy 35
123
124 1000:num <- copy 0
125 1001:num <- copy 5
126
127 default-space:space <- copy 1000/unsafe
128 1:&:point <- copy 2000/unsafe
129 9:num/raw <- get *1:&:point, 1:offset
130 ]
131 +mem: storing 35 in location 9
132
133 :(before "Read element" following "case GET:")
134 element.properties.push_back(pair<string, string_tree*>("raw", NULL));
135
136
137
138 :(scenario lookup_sidesteps_default_space_in_index)
139 def main [
140
141 2001:num <- copy 2
142 2002:num <- copy 34
143 2003:num <- copy 35
144
145 1000:num <- copy 0
146 1001:num <- copy 5
147
148 default-space:space <- copy 1000/unsafe
149 1:&:@:num <- copy 2000/unsafe
150 9:num/raw <- index *1:&:@:num, 1
151 ]
152 +mem: storing 35 in location 9
153
154 :(before "Read element" following "case INDEX:")
155 element.properties.push_back(pair<string, string_tree*>("raw", NULL));
156
157
158
159
160 :(scenario new_default_space)
161 def main [
162 new-default-space
163 x:num <- copy 0
164 y:num <- copy 3
165 ]
166
167 +mem: array length is 3
168
169 :(before "End is_disqualified Special-cases")
170 if (x.name == "number-of-locals")
171 x.initialized = true;
172 :(before "End is_special_name Special-cases")
173 if (s == "number-of-locals") return true;
174
175 :(before "End Rewrite Instruction(curr, recipe result)")
176
177
178
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
197
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
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
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
234 const recipe_ordinal r = get(Recipe_ordinal, current_recipe_name());
235 const recipe& exiting_recipe = get(Recipe, r);
236 if (exiting_recipe.steps.empty()) return;
237 const instruction& inst = exiting_recipe.steps.at(0);
238 if (inst.name_before_rewrite != "local-scope") return;
239
240 vector<double> zeros;
241 for (int i = 1; i < SIZE(exiting_recipe.steps); ++i) {
242 const instruction& inst = exiting_recipe.steps.at(i);
243 for (int i = 0; i < SIZE(inst.products); ++i) {
244 const reagent& product = inst.products.at(i);
245
246 if (has_property(product, "lookup")) continue;
247 if (has_property(product, "raw")) continue;
248 if (escaping(product)) continue;
249
250 trace(9999, "mem") << "clearing " << product.original_string << end();
251 zeros.resize(size_of(product));
252 write_memory(product, zeros);
253 }
254 }
255 trace(9999, "mem") << "automatically abandoning " << current_call().default_space << end();
256 abandon(current_call().default_space,
257 inst.products.at(0).type->right,
258 1 + 1 + Name[r][""]);
259 }
260
261
262
263 :(before "End instruction Fields")
264 string name_before_rewrite;
265 :(before "End instruction Clear")
266 name_before_rewrite.clear();
267 :(before "End next_instruction(curr)")
268 curr->name_before_rewrite = curr->name;
269
270 :(code)
271
272
273 bool escaping(const reagent& r) {
274 assert(Current_routine);
275
276 if (current_step_index() >= SIZE(Current_routine->steps())) return false;
277 for (long long i = 0; i < SIZE(current_instruction().ingredients); ++i) {
278 if (r == current_instruction().ingredients.at(i)) {
279 if (caller_uses_product(i))
280 return true;
281 }
282 }
283 return false;
284 }
285
286
287
288
289 :(after "Write Products of Instruction")
290 Update_refcounts_in_write_memory = should_update_refcounts_in_write_memory();
291 :(before "End Write Products of Instruction")
292 Update_refcounts_in_write_memory = true;
293 :(code)
294 bool should_update_refcounts_in_write_memory() {
295 const instruction& inst = current_instruction();
296
297 if (inst.operation < MAX_PRIMITIVE_RECIPES) return true;
298 if (!contains_key(Recipe, inst.operation)) return true;
299 const recipe& callee = get(Recipe, inst.operation);
300 if (callee.steps.empty()) return true;
301 return callee.steps.at(0).name_before_rewrite != "local-scope";
302 }
303
304 bool caller_uses_product(int product_index) {
305 assert(Current_routine);
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
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
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
367 +run: {4: ("address" "number")} <- get {1: "foo"}, {y: "offset"}
368 +mem: incrementing refcount of 1006: 1 -> 2
369
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
386 ]
387
388 +mem: automatically abandoning 1004
389
390 +mem: automatically abandoning 1000
391
392
393
394 :(before "End Globals")
395 bool Hide_missing_default_space_errors = true;
396 :(before "End Checks")
397 Transform.push_back(check_default_space);
398 :(code)
399 void check_default_space(const recipe_ordinal r) {
400 if (Hide_missing_default_space_errors) return;
401 const recipe& caller = get(Recipe, r);
402
403
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;
424 if (!is_special_name(p->first))
425 return true;
426 }
427 return false;
428 }
429
430
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 }