:(scenario new)
def main [
1:address:num/raw <- new number:type
2:address:num/raw <- new number:type
3:bool/raw <- equal 1:address:num/raw, 2:address:num/raw
]
+mem: storing 0 in location 3
:(scenario dilated_reagent_with_new)
def main [
1:address:address:num <- new {(address number): type}
]
+new: size of ("address" "number") is 1
:(before "End Mu Types Initialization")
put(Type_ordinal, "type", 0);
:(code)
bool is_mu_type_literal(const reagent& r) {
return is_literal(r) && r.type && r.type->name == "type";
}
:(before "End Primitive Recipe Declarations")
NEW,
:(before "End Primitive Recipe Numbers")
put(Recipe_ordinal, "new", NEW);
:(before "End Primitive Recipe Checks")
case NEW: {
const recipe& caller = get(Recipe, r);
if (inst.ingredients.empty() || SIZE(inst.ingredients) > 2) {
raise << maybe(caller.name) << "'new' requires one or two ingredients, but got '" << inst.original_string << "'\n" << end();
break;
}
const reagent& type = inst.ingredients.at(0);
if (!is_mu_type_literal(type)) {
raise << maybe(caller.name) << "first ingredient of 'new' should be a type, but got '" << type.original_string << "'\n" << end();
break;
}
if (inst.products.empty()) {
raise << maybe(caller.name) << "result of 'new' should never be ignored\n" << end();
break;
}
if (!product_of_new_is_valid(inst)) {
raise << maybe(caller.name) << "product of 'new' has incorrect type: '" << inst.original_string << "'\n" << end();
break;
}
break;
}
:(code)
bool product_of_new_is_valid(const instruction& inst) {
reagent product = inst.products.at(0);
if (!product.type || product.type->atom || product.type->left->value != get(Type_ordinal, "address"))
return false;
drop_from_type(product, "address");
if (SIZE(inst.ingredients) > 1) {
if (!product.type || product.type->atom || product.type->left->value != get(Type_ordinal, "array"))
return false;
drop_from_type(product, "array");
}
reagent expected_product("x:"+inst.ingredients.at(0).name);
{
string_tree* tmp_type_names = parse_string_tree(expected_product.type->name);
delete expected_product.type;
expected_product.type = new_type_tree(tmp_type_names);
delete tmp_type_names;
}
return types_strictly_match(product, expected_product);
}
void drop_from_type(reagent& r, string expected_type) {
assert(!r.type->atom);
if (r.type->left->name != expected_type) {
raise << "can't drop2 " << expected_type << " from '" << to_string(r) << "'\n" << end();
return;
}
type_tree* tmp = r.type;
r.type = tmp->right;
tmp->right = NULL;
delete tmp;
}
:(before "End Primitive Recipe Checks")
case ALLOCATE: {
raise << "never call 'allocate' directly'; always use 'new'\n" << end();
break;
}
:(before "End Primitive Recipe Implementations")
case NEW: {
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();
break;
}
:(after "Transform.push_back(check_instruction)")
Transform.push_back(transform_new_to_allocate);
:(code)
void transform_new_to_allocate(const recipe_ordinal r) {
trace(9991, "transform") << "--- convert 'new' to 'allocate' for recipe " << get(Recipe, r).name << end();
for (int i = 0; i < SIZE(get(Recipe, r).steps); ++i) {
instruction& inst = get(Recipe, r).steps.at(i);
if (inst.name == "new") {
inst.operation = ALLOCATE;
string_tree* type_name = new string_tree(inst.ingredients.at(0).name);
type_name = parse_string_tree(type_name);
type_tree* type = new_type_tree(type_name);
inst.ingredients.at(0).set_value(size_of(type));
trace(9992, "new") << "size of " << to_string(type_name) << " is " << inst.ingredients.at(0).value << end();
delete type;
delete type_name;
}
}
}
:(before "End Globals")
extern const int Reserved_for_tests = 1000;
int Memory_allocated_until = Reserved_for_tests;
int Initial_memory_per_routine = 100000;
:(before "End Setup")
Memory_allocated_until = Reserved_for_tests;
Initial_memory_per_routine = 100000;
:(before "End routine Fields")
int alloc, alloc_max;
:(before "End routine Constructor")
alloc = Memory_allocated_until;
Memory_allocated_until += Initial_memory_per_routine;
alloc_max = Memory_allocated_until;
trace(9999, "new") << "routine allocated memory from " << alloc << " to " << alloc_max << end();
:(before "End Primitive Recipe Declarations")
ALLOCATE,
:(before "End Primitive Recipe Numbers")
put(Recipe_ordinal, "allocate", ALLOCATE);
:(before "End Primitive Recipe Implementations")
case ALLOCATE: {
int size = ingredients.at(0).at(0);
if (SIZE(ingredients) > 1) {
trace(9999, "mem") << "array length is " << ingredients.at(1).at(0) << end();
size = 1 + size*ingredients.at(1).at(0);
}
int result = allocate(size);
if (SIZE(current_instruction().ingredients) > 1) {
trace(9999, "mem") << "storing " << ingredients.at(1).at(0) << " in location " << result+1 << end();
put(Memory, result+1, ingredients.at(1).at(0));
}
products.resize(1);
products.at(0).push_back(result);
break;
}
:(code)
int allocate(int size) {
++size;
trace(9999, "mem") << "allocating size " << size << end();
ensure_space(size);
const int result = Current_routine->alloc;
trace(9999, "mem") << "new alloc: " << result << end();
for (int address = result; address < result+size; ++address) {
trace(9999, "mem") << "storing 0 in location " << address << end();
put(Memory, address, 0);
}
Current_routine->alloc += size;
assert(Current_routine->alloc <= Current_routine->alloc_max);
return result;
}
:(code)
void ensure_space(int size) {
if (size > Initial_memory_per_routine) {
tb_shutdown();
cerr << "can't allocate " << size << " locations, that's too much compared to " << Initial_memory_per_routine << ".\n";
exit(0);
}
if (Current_routine->alloc + size > Current_routine->alloc_max) {
Current_routine->alloc = Memory_allocated_until;
Memory_allocated_until += Initial_memory_per_routine;
Current_routine->alloc_max = Memory_allocated_until;
trace(9999, "new") << "routine allocated memory from " << Current_routine->alloc << " to " << Current_routine->alloc_max << end();
}
}
:(scenario new_initializes)
% Memory_allocated_until = 10;
% put(Memory, Memory_allocated_until, 1);
def main [
1:address:num <- new number:type
]
+mem: storing 0 in location 10
:(scenario new_array)
def main [
1:address:array:num/raw <- new number:type, 5
2:address:num/raw <- new number:type
3:num/raw <- subtract 2:address:num/raw, 1:address:array:num/raw
]
+run: {1: ("address" "array" "number"), "raw": ()} <- new {number: "type"}, {5: "literal"}
+mem: array length is 5
+mem: storing 7 in location 3
:(scenario new_empty_array)
def main [
1:address:array:num/raw <- new number:type, 0
2:address:num/raw <- new number:type
3:num/raw <- subtract 2:address:num/raw, 1:address:array:num/raw
]
+run: {1: ("address" "array" "number"), "raw": ()} <- new {number: "type"}, {0: "literal"}
+mem: array length is 0
+mem: storing 2 in location 3
:(scenario new_overflow)
% Initial_memory_per_routine = 3; // barely enough room for point allocation below
def main [
1:address:num/raw <- new number:type
2:address:point/raw <- new point:type
]
+new: routine allocated memory from 1000 to 1003
+new: routine allocated memory from 1003 to 1006