git.haldean.org cvm / cf9ec76
Function calling complete. You can now call functions with or without arguments, and with return values. Functions returning void with a 'return' statement in them can still confuse the VM and cause a register underflow error. Main functions with a return statement are also a little confusing. Will Brown 9 years ago
8 changed file(s) with 278 addition(s) and 46 deletion(s). Raw diff Collapse all Expand all
44 //#define DEBUG
55 #define MEMORY_SIZE 128
66 #define STACK_DEPTH 128
7 #define NIL 0
78
89 #define RS_PUSH(x) reg_stack_push(machine->reg_stack, (x))
910 #define RS_POP reg_stack_pop(machine->reg_stack)
1920 uint capacity;
2021 } typedef register_stack;
2122
23 struct memory_page_struct {
24 memory_cell *memory;
25 unsigned char *used;
26 uint size;
27 } typedef memory_page;
28
29 struct stack_frame_struct {
30 register_stack *return_addresses;
31 register_stack *stack_frame_addr;
32 register_stack *stack_frame_len;
33 } typedef stack_frames;
34
2235 struct machine_state_struct {
2336 register_stack *reg_stack;
24 register_stack *return_addresses;
25
26 memory_cell *memory;
27 uint memory_size;
37 stack_frames *stack;
38
39 memory_page *memory;
2840
2941 unsigned char* code;
3042 uint code_len;
3345 uint error;
3446 } typedef machine_state;
3547
36 memory_cell *mem_create(uint size) {
37 return malloc(size * sizeof(char));
48 memory_page *memory_page_create(uint size) {
49 memory_page *mem = malloc(sizeof(memory_page));
50 mem->memory = malloc(size * sizeof(memory_cell));
51 mem->used = malloc(size / 8);
52 mem->size = size;
53 return mem;
54 }
55
56 void memory_cell_set_used(memory_page *mem, uint addr) {
57 mem->used[addr / 8] |= 1 << (addr % 8);
58 }
59
60 unsigned char memory_cell_is_used(memory_page *mem, uint addr) {
61 return mem->used[addr / 8] & (1 << (addr % 8));
62 }
63
64 void memory_cell_write(memory_page *mem, uint addr, memory_cell value) {
65 if (addr >= mem->size || addr == NIL) {
66 printf("Illegal write to address %d (memory size %d)\n", addr, mem->size);
67 exit(1);
68 }
69
70 memory_cell_set_used(mem, addr);
71 mem->memory[addr] = value;
72 }
73
74 memory_cell memory_cell_read(memory_page *mem, uint addr) {
75 if (addr >= mem->size || addr == NIL) {
76 printf("Illegal read to address %d (memory size %d)\n", addr, mem->size);
77 exit(1);
78 }
79
80 if (memory_cell_is_used(mem, addr) == 0) {
81 printf("Warning: accessing memory that has not been initialized.");
82 }
83
84 return mem->memory[addr];
85 }
86
87 void memory_cell_free(memory_page *mem, uint addr) {
88 if ((mem->used[addr / 8] & (1 << (addr % 8))) == 0) {
89 printf("Warning: attempted to free memory that is not in use.");
90 return;
91 }
92
93 mem->used[addr / 8] ^= 1 << (addr % 8);
94 }
95
96 void memory_cell_free_range(memory_page *mem, uint addr, uint len) {
97 int i = 0;
98 for (; i < len; i++) {
99 memory_cell_free(mem, addr + i);
100 }
101 }
102
103 void memory_page_free(memory_page *mem) {
104 free(mem->memory);
105 free(mem->used);
106 free(mem);
38107 }
39108
40109 register_stack *reg_stack_create(uint initial_capacity) {
65134 }
66135 }
67136
137 memory_cell reg_stack_peek(register_stack *stack) {
138 if (stack->size > 0) {
139 return stack->stack[stack->size - 1];
140 } else {
141 fprintf(stderr, "Register stack underflow.");
142 exit(1);
143 }
144 }
145
68146 void reg_stack_free(register_stack *stack) {
69147 free(stack->stack);
70148 free(stack);
71149 }
72150
151 stack_frames *stack_frames_create(uint stack_depth) {
152 stack_frames *sfs = malloc(sizeof(stack_frames));
153 sfs->return_addresses = reg_stack_create(stack_depth);
154 sfs->stack_frame_addr = reg_stack_create(stack_depth);
155 sfs->stack_frame_len = reg_stack_create(stack_depth);
156 return sfs;
157 }
158
73159 machine_state *machine_create() {
74160 machine_state *machine = malloc(sizeof(machine_state));
75 machine->memory = mem_create(MEMORY_SIZE);
76 machine->memory_size = MEMORY_SIZE;
161 machine->memory = memory_page_create(MEMORY_SIZE);
77162 machine->reg_stack = reg_stack_create(32);
78 machine->return_addresses = reg_stack_create(STACK_DEPTH);
163 machine->stack = stack_frames_create(STACK_DEPTH);
79164 machine->code = NULL;
80165 machine->code_len = 0;
81166 machine->program_counter = 0;
84169
85170 void mem_print(machine_state *machine) {
86171 int i = 0;
87 for (; i < machine->memory_size; i++) {
88 printf("%02X ", machine->memory[i]);
172 for (; i < machine->memory->size; i++) {
173 printf("%02X ", machine->memory->memory[i]);
89174 if ((i + 1) % 32 == 0) {
90175 printf("\n");
91176 }
92177 }
93178 }
94179
95 void reg_stack_print(machine_state *machine) {
180 void reg_stack_print(register_stack *reg_stack) {
96181 int i = 0;
97 for (; i < machine->reg_stack->size; i++) {
98 printf("%d ", machine->reg_stack->stack[i]);
182 for (; i < reg_stack->size; i++) {
183 printf("%d ", reg_stack->stack[i]);
99184 }
100185 printf("\n");
101186 }
102187
103188 void machine_free(machine_state *machine) {
104 free(machine->memory);
105189 free(machine->code);
190 memory_page_free(machine->memory);
106191 reg_stack_free(machine->reg_stack);
107192 free(machine);
108193 }
122207 }
123208 }
124209
210 uint find_free_memory_of_size(uint size, memory_page *mem) {
211 if (size == 0) {
212 return NIL;
213 }
214
215 int start_addr = 1;
216 int offset;
217
218 while (start_addr + size < mem->size) {
219 if (!mem->used[start_addr]) {
220 offset = 0;
221 while (!memory_cell_is_used(mem, start_addr + offset) && offset < size) {
222 offset++;
223 }
224
225 if (offset == size) {
226 for (offset = 0; offset < size; offset++) {
227 memory_cell_set_used(mem, start_addr + offset);
228 }
229 return start_addr;
230 }
231 }
232
233 start_addr++;
234 }
235
236 printf("Out of memory: could not find a memory block of size %d\n", size);
237 return (uint) -1;
238 }
239
125240 void machine_run_instruction(
126241 machine_state *machine, instr_op opcode, instr_arg argument) {
127242 memory_cell operand1, operand2;
143258 break;
144259
145260 case CALL:
146 reg_stack_push(machine->return_addresses, machine->program_counter + 1);
261 reg_stack_push(
262 machine->stack->return_addresses, machine->program_counter + 1);
263 // operand1 is the length of the stack frame to be allocated.
264 operand1 = RS_POP;
265 reg_stack_push(machine->stack->stack_frame_len, operand1);
266 reg_stack_push(
267 machine->stack->stack_frame_addr,
268 find_free_memory_of_size(operand1, machine->memory));
147269 machine->program_counter = argument;
148270 goto skip_pc_incr;
149271
150272 case RETURN:
151 machine->program_counter = reg_stack_pop(machine->return_addresses);
273 machine->program_counter =
274 reg_stack_pop(machine->stack->return_addresses);
275 // operand1 is the start address of the stack frame
276 operand1 = reg_stack_pop(machine->stack->stack_frame_addr);
277 // operand2 is the length of the stack frame to free
278 operand2 = reg_stack_pop(machine->stack->stack_frame_len);
279 memory_cell_free_range(machine->memory, operand1, operand2);
152280 goto skip_pc_incr;
153281
282 case ALLOC:
283 RS_PUSH(find_free_memory_of_size(argument, machine->memory));
284 break;
285
286 case LSTORE:
287 if (argument == -1) {
288 operand1 = RS_POP + reg_stack_peek(machine->stack->stack_frame_addr);
289 } else {
290 operand1 = argument + reg_stack_peek(machine->stack->stack_frame_addr);
291 }
292 memory_cell_write(machine->memory, operand1, RS_POP);
293 break;
294
295 case LLOAD:
296 if (argument == -1) {
297 operand1 = RS_POP + reg_stack_peek(machine->stack->stack_frame_addr);
298 } else {
299 operand1 = argument + reg_stack_peek(machine->stack->stack_frame_addr);
300 }
301 RS_PUSH(memory_cell_read(machine->memory, operand1));
302 break;
303
154304 case STORE:
155 machine->memory[(uint) argument] = RS_POP;
156 break;
305 if (argument == -1) {
306 operand1 = RS_POP;
307 memory_cell_write(machine->memory, operand1, RS_POP);
308 } else {
309 memory_cell_write(machine->memory, argument, RS_POP);
310 }
311 break;
312
157313 case LOAD:
158 RS_PUSH(machine->memory[(uint) argument]);
314 if (argument == -1) {
315 RS_PUSH(memory_cell_read(machine->memory, RS_POP));
316 } else {
317 RS_PUSH(memory_cell_read(machine->memory, argument));
318 }
159319 break;
160320
161321 case INCR:
272432 case PRINT:
273433 printf("%c", (unsigned char) RS_POP);
274434 break;
435
436 default:
437 printf("Unknown opcode: 0x%02X / %d\n", opcode, opcode);
275438 }
276439
277440 machine->program_counter++;
279442 skip_pc_incr:
280443
281444 #ifdef DEBUG
282 printf("PC: %d, REG: ", machine->program_counter);
283 reg_stack_print(machine);
445 printf("PC: %d\nREG: ", machine->program_counter);
446 reg_stack_print(machine->reg_stack);
447 printf("ADDR: ");
448 reg_stack_print(machine->stack->stack_frame_addr);
449 printf("\n");
284450 #endif
285451
286452 return;
3838 #define PRINT 33
3939 #define RETURN 34
4040 #define CALL 35
41 #define LLOAD 36
42 #define LSTORE 37
43 #define ALLOC 38
4144 char **opcode_map() {
42 char **opcode_names = malloc(36 * sizeof(char *));
45 char **opcode_names = malloc(39 * sizeof(char *));
4346 opcode_names[NOP] = "NOP";
4447 opcode_names[HALT] = "HALT";
4548 opcode_names[PUSH] = "PUSH";
7679 opcode_names[PRINT] = "PRINT";
7780 opcode_names[RETURN] = "RETURN";
7881 opcode_names[CALL] = "CALL";
82 opcode_names[LLOAD] = "LLOAD";
83 opcode_names[LSTORE] = "LSTORE";
84 opcode_names[ALLOC] = "ALLOC";
7985 return opcode_names;
8086 }
8187 #endif
3636 'print',
3737 'return',
3838 'call',
39 'lload',
40 'lstore',
41 'alloc',
3942 ]
4043
4144 opcodes = dict(zip(ops, range(len(ops))))
44 self.args = args
55 self.retype = retype
66 self.loc = loc
7 self.frame_size = 0
78
89 def __str__(self):
910 return 'function %s: %s -> %s' % (self.name, self.args, self.retype)
1313 func_locations[func] = len(code)
1414 code.extend(codeobj.code)
1515
16 code = replace_function_addresses(code, func_locations)
16 code = replace_function_addresses(code, funcs, func_locations)
1717 code = static_allocate(code, glob)
1818 return code
1919
2020 def is_var(term):
2121 return isinstance(term, tuple) and len(term) == 2 and term[1] == 'var'
2222
23 def replace_function_addresses(code, func_locations):
24 # This is why people hate functional programmers. And this is why I hate that
25 # 'lambda' isn't a lambda.
26 def replace_for_instruction(instr):
27 def arg_sub(arg):
28 if isinstance(arg, tuple) and len(arg) == 2 and arg[1] == 'func':
29 return func_locations[arg[0]]
30 else: return arg
31 return tuple(map(arg_sub, instr))
32 return map(replace_for_instruction, code)
23 def is_func(term):
24 return isinstance(term, tuple) and len(term) == 2 and term[1] == 'func'
25
26 def replace_function_addresses(code, funcs, func_locations):
27 gen = []
28 for line in code:
29 if line[0] == 'call' and len(line) > 1 and is_func(line[1]):
30 gen.append((line[0], func_locations[line[1][0]]))
31 elif line[0] == 'ldconst' and len(line) > 1 and is_func(line[1]):
32 gen.append((line[0], funcs[line[1][0]].frame_size))
33 else:
34 gen.append(line)
35 return gen
3336
3437 def static_allocate(code, glob):
3538 glob_locations = {}
4447 terms = list(line)
4548 if len(line) > 1 and is_var(line[1]):
4649 terms[1] = glob_locations[line[1][0]]
47 if len(line) > 2 and is_var(line[2]):
48 terms[2] = glob_locations[line[2][0]]
50 elif len(line) > 2:
51 raise Exception('Only support 1 argument per instruction in linker')
4952 gen.append(tuple(terms))
5053 return gen
0 from binary import parse_instructions
01 from function import function
2 from link import is_var
13 import cvmtypes
24
35 def translate(root):
79 init_code = []
810 for item in root:
911 if item[0] == 'fun':
10 function = translate_function(item, glob, funcs)
11 funcs[item[2][0][0]] = function
12 func = translate_function(item, glob, funcs)
13 funcs[item[2][0][0]] = func
1214 elif item[0] == 'declare':
1315 for var, init in item[2]:
1416 cvmtype, name = cvmtypes.declarator((item[1], var))
2628 fname = ftree[2][0][0]
2729 fargs = list(map(cvmtypes.declarator, ftree[2][1]))
2830 fcode, flocals = translate_compound(ftree[3], glob, funcs)
29 fret = cvmtypes.typefor(ftree[1])
30 return function(fname, fcode, fargs, fret, flocals)
31 if 'void' in ftree[1]:
32 fret = None
33 else:
34 fret = cvmtypes.typefor(ftree[1])
35
36 func = function(fname, [], fargs, fret, flocals)
37
38 # Calculate storage required for stack frame
39 func.frame_size = 0
40 local_addr_offsets = {}
41
42 for cvmtype, name in fargs:
43 local_addr_offsets[name] = func.frame_size
44 func.frame_size += cvmtype.bytecount
45
46 for name, cvmtype in map(lambda x: (x[0], x[1][0]), flocals.items()):
47 local_addr_offsets[name] = func.frame_size
48 func.frame_size += cvmtype.bytecount
49
50 prepend = []
51 for name, init in map(lambda x: (x[0], x[1][1]), flocals.items()):
52 if init:
53 prepend += translate_statement(init, glob, funcs)
54 prepend.append(('lstore', local_addr_offsets[name]))
55
56 for cvmtype, name in reversed(fargs):
57 prepend.append(('lstore', local_addr_offsets[name]))
58 fcode = prepend + fcode
59
60 for line in fcode:
61 if len(line) > 1 and is_var(line[1]) and line[1][0] in local_addr_offsets:
62 if line[0] in ['load', 'store']:
63 func.code.append(('l%s' % line[0], local_addr_offsets[line[1][0]]))
64 else:
65 raise Exception('Accessed non-register memory in an instruction other'
66 ' than load and store')
67 else:
68 func.code.append(line)
69
70 return func
3171
3272 def add_declaration(loc, decl):
3373 for var, init in decl[2]:
68108 TODO:
69109
70110 BREAK
71 CALL
72111 GOTO
73112 '''
74113 # Assignment
82121
83122 # Call stack-related
84123 if ftree[0] == 'call':
124 # Calls to asm() skip translation and go straight to instruction parser.
125 if ftree[1][0] == 'asm':
126 return parse_instructions(ftree[2][0][0])
85127 func = funcs[ftree[1][0]]
86128 if len(func.args) != len(ftree[2]):
87129 raise Exception('Not enough arguments to function "%s"' % ftree[1][0])
89131 arg_evals = []
90132 for arg in ftree[2]:
91133 arg_evals.extend(translate_statement(arg, glob, funcs))
92 return arg_evals + [('call', (ftree[1][0], 'func'))]
134 funcpair = (ftree[1][0], 'func')
135 return arg_evals + [('ldconst', funcpair), ('call', funcpair)]
93136
94137 if ftree[0] == 'return':
95 return translate_statement(ftree[1], glob, funcs) + [('return',)]
138 if ftree[1]:
139 return translate_statement(ftree[1], glob, funcs) + [('return',)]
140 else:
141 return [('return',)]
96142
97143 # Unary operators
98144 if ftree[0] == '++':
0 int test(int ignore) {
1 return 7;
0 void putc(char c) {
1 /* loads c and puts it on the stack. */
2 c;
3 asm("PRINT");
4 return;
25 }
36
47 int main() {
5 test(5);
8 putc('a');
9 putc('\n');
610 }
0 int main() {
1 asm("LDCONST 'a'\nPRINT");
2 }