-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcodegen.c
More file actions
407 lines (325 loc) · 12.1 KB
/
codegen.c
File metadata and controls
407 lines (325 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
#include <stdio.h>
#include "codegen.h"
#include "table.h"
FILE *codegenout; // the output of code generation
#define PUSH(arg1) fprintf(codegenout, "\tpush\t%s\n", arg1 )
#define POP(arg1) fprintf(codegenout, "\tpop\t%s\n", arg1 )
#define MOV(arg1, arg2) fprintf(codegenout, "\tmov\t%s, %s\n", arg1, arg2)
#define MOV_FROM_IMMEDIATE(arg1, arg2) fprintf(codegenout, "\tmov\t$%d, %s\n", arg1, arg2)
#define MOV_FROM_OFFSET(offset, reg) fprintf(codegenout, "\tmov\t-%d(%%rbp), %s\n", offset, reg)
#define MOV_TO_OFFSET(reg, offset) fprintf(codegenout, "\tmov\t%s, -%d(%%rbp)\n", reg, offset)
#define MOV_FROM_GLOBAL(reg, global) fprintf(codegenout, "\tmov\t%s(%%rip), %s\n", global, reg)
#define MOV_TO_GLOBAL(reg, global) fprintf(codegenout, "\tmov\t%s, %s(%%rip)\n", reg, global)
#define ADD(arg1, arg2) fprintf(codegenout, "\tadd\t%s, %s\n", arg1, arg2)
#define SUB(arg1, arg2) fprintf(codegenout, "\tsub\t%s, %s\n", arg1, arg2)
#define SUBCONST(arg1, arg2) fprintf(codegenout, "\tsub\t$%d, %s\n", arg1, arg2)
#define IMUL(arg1, arg2) fprintf(codegenout, "\timul\t%s, %s\n", arg1, arg2)
#define CDQ() fprintf(codegenout, "\tcdq\n")
#define IDIV(reg) fprintf(codegenout, "\tidiv\t%s\n", reg)
#define CALL(arg1) fprintf(codegenout, "\tcall\t%s\n", arg1)
#define RET fprintf(codegenout, "\tret\n")
#define COMMENT(arg1) fprintf(codegenout, "\t# %s\n", arg1)
#define CMP0(arg) fprintf(codegenout,"\tcmp $0, %s\n", arg)
#define CMP(arg1, arg2) fprintf(codegenout,"\tcmp %s, %s\n", arg1, arg2)
#define JE(labelnum) fprintf(codegenout,"\tje .L%d\n", labelnum)
#define JL(labelnum) fprintf(codegenout,"\tjl .L%d\n", labelnum)
#define JLE(labelnum) fprintf(codegenout,"\tjle .L%d\n", labelnum)
#define JG(labelnum) fprintf(codegenout,"\tjg .L%d\n", labelnum)
#define JGE(labelnum) fprintf(codegenout,"\tjge .L%d\n", labelnum)
#define JMP(labelnum) fprintf(codegenout,"\tjmp .L%d\n", labelnum)
#define LABEL(labelnum) fprintf(codegenout,".L%d:\n", labelnum)
const string const param_registers[] = { "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"}; // all const
static void codegen_main(T_main main);
static void codegen_decllist(T_decllist decllist);
static void codegen_stmtlist(T_stmtlist stmtlist);
static void codegen_funclist(T_funclist funclist);
static void codegen_func(T_func func);
static void codegen_stmtlist(T_stmtlist stmtlist);
static void codegen_stmt(T_stmt stmt);
static void codegen_assignstmt(T_stmt stmt);
static void codegen_ifstmt(T_stmt stmt);
static void codegen_ifelsestmt(T_stmt stmt);
static void codegen_whilestmt(T_stmt stmt);
static void codegen_compoundstmt(T_stmt stmt);
static void codegen_expr(T_expr expr);
static void codegen_identexpr(T_expr expr);
static void codegen_callexpr(T_expr expr);
static void codegen_intexpr(T_expr expr);
static void codegen_charexpr(T_expr expr);
static void codegen_strexpr(T_expr expr);
static void codegen_arrayexpr(T_expr expr);
static void codegen_unaryexpr(T_expr expr);
static void codegen_binaryexpr(T_expr expr);
static void codegen_castexpr(T_expr expr);
static void emit_prologue(int size);
static void emit_epilogue();
static int bitwidth(T_type type);
typedef struct S_offset_scope *T_offset_scope;
struct S_offset_scope {
T_table table;
int stack_size;
int current_offset;
T_offset_scope parent;
};
static T_offset_scope create_offset_scope(T_offset_scope parent) {
T_offset_scope offset_scope = xmalloc(sizeof(*offset_scope));
offset_scope->table = create_table();
// start after the dynamic link to the caller's %rbp
offset_scope->current_offset = 8;
offset_scope->stack_size = 0;
offset_scope->parent = parent;
return offset_scope;
}
static T_offset_scope destroy_offset_scope(T_offset_scope offset_scope) {
T_offset_scope parent_offset_scope = offset_scope->parent;
destroy_table(offset_scope->table);
free(offset_scope);
return parent_offset_scope;
}
struct S_offset {
int offset;
};
typedef struct S_offset * T_offset;
static T_table global_symbols;
static T_offset_scope current_offset_scope;
void insert_offset(T_offset_scope scope, string ident, int size) {
T_offset entry = xmalloc(sizeof(*entry));
entry->offset = scope->current_offset;
insert(scope->table, ident, (void*) entry);
scope->stack_size += size;
scope->current_offset += size;
}
static int lookup_offset_in_scope(T_offset_scope offset_scope, string ident) {
T_offset offset = (T_offset) lookup(offset_scope->table, ident);
if (NULL == offset) {
fprintf(stderr, "FATAL: symbol not in table. double-check the code that inserts the symbol into the offset scope.");
}
return offset->offset;
}
static T_offset lookup_offset_in_all_offset_scopes(T_offset_scope offset_scope, string ident) {
// loop over each offset_scope and check for a binding for ident
while (NULL != offset_scope) {
// check for a binding in this offset_scope
T_offset offset = (T_offset) lookup(offset_scope->table, ident);
if (NULL != offset) {
return offset;
}
// no binding in this offset_scope, so check the parent
offset_scope = offset_scope->parent;
}
// found no binding in any offset_scope
return NULL;
}
void codegen(T_prog prog) {
// no need to record symbols in the global offset_scope. the assembler and linker handle them.
current_offset_scope = NULL;
// emit assembly header
fprintf(codegenout, ".file \"stdin\"\n");
// emit a .comm for global vars
global_symbols = create_table();
// loop over each global symbol. emit .comm and add to global symtab
T_decllist decllist = prog->decllist;
while (NULL != decllist) {
if (E_functiontype != decllist->decl->type->kind) {
fprintf(codegenout, ".comm\t%s,%d\n", decllist->decl->ident, bitwidth(decllist->decl->type));
insert(global_symbols, decllist->decl->ident, decllist->decl->ident);
}
decllist = decllist->tail;
}
// optional: emit an .rodata section and label for strings
// go through each function
codegen_funclist(prog->funclist);
// generate the code for main
codegen_main(prog->main);
// free the global symbol table
free(global_symbols);
}
static void codegen_main(T_main main) {
// create a new scope
current_offset_scope = create_offset_scope(NULL);
// emit the pseudo ops for the function definition
fprintf(codegenout, ".text\n");
fprintf(codegenout, ".globl %s\n", "main");
fprintf(codegenout, ".type %s, @function\n", "main");
// emit a label for the function
fprintf(codegenout, "%s:\n", "main");
// add local declarations to the scope
codegen_decllist(main->decllist);
COMMENT("stack space for argc and argv");
insert_offset(current_offset_scope, "argc", 8); // int argc
insert_offset(current_offset_scope, "argv", 8); // char **argv
COMMENT("emit main's prologue");
emit_prologue(current_offset_scope->stack_size);
COMMENT("move argc and argv from parameter registers to the stack");
int offset;
offset = lookup_offset_in_scope(current_offset_scope, "argc");
MOV_TO_OFFSET("%rdi", offset);
offset = lookup_offset_in_scope(current_offset_scope, "argv");
MOV_TO_OFFSET("%rsi", offset);
COMMENT("generate code for the body");
codegen_stmtlist(main->stmtlist);
COMMENT("generate code for the return expression");
codegen_expr(main->returnexpr);
COMMENT("save the return expression into %rax per the abi");
POP("%rax");
COMMENT("emit main's epilogue");
emit_epilogue();
// exit the scope
current_offset_scope = destroy_offset_scope(current_offset_scope);
}
static void codegen_funclist(T_funclist funclist) {
while (NULL != funclist) {
codegen_func(funclist->func);
funclist = funclist->tail;
}
}
static void codegen_func(T_func func) {
fprintf(stderr, "TODO: codegen_func (remove this message when implemented).");
}
static void codegen_decllist(T_decllist decllist) {
fprintf(stderr, "TODO: codegen_decllist (remove this message when implemented).");
}
/* statements */
static void codegen_stmtlist(T_stmtlist stmtlist) {
while (NULL != stmtlist) {
codegen_stmt(stmtlist->stmt);
stmtlist = stmtlist->tail;
}
}
static void codegen_stmt(T_stmt stmt) {
if (NULL == stmt) {
fprintf(stderr, "FATAL: stmt is NULL in codegen_stmt\n");
exit(1);
}
switch (stmt->kind) {
case E_assignstmt: codegen_assignstmt(stmt); break;
case E_ifstmt: codegen_ifstmt(stmt); break;
case E_ifelsestmt: codegen_ifelsestmt(stmt); break;
case E_whilestmt: codegen_whilestmt(stmt); break;
case E_compoundstmt: codegen_compoundstmt(stmt); break;
default: fprintf(stderr, "FATAL: unexpected stmt kind in codegen_stmt\n"); exit(1); break;
}
}
static void codegen_assignstmt(T_stmt stmt) {
fprintf(stderr, "TODO: codegen_assignstmt (remove this message when implemented).");
}
static void codegen_ifstmt(T_stmt stmt) {
// pending project 4
fprintf(stderr, "TODO: codegen_ifstmt (project 4)\n");
}
static void codegen_ifelsestmt(T_stmt stmt) {
// pending project 4
fprintf(stderr, "TODO: codegen_ifelsestmt (project 4)\n");
}
static void codegen_whilestmt(T_stmt stmt) {
// pending project 4
fprintf(stderr, "TODO: codegen_whilestmt (project 4)\n");
}
static void codegen_compoundstmt(T_stmt stmt) {
fprintf(stderr, "TODO: codegen_compoundstmt (remove this message when implemented).");
}
/* expressions */
static void codegen_expr(T_expr expr) {
if (NULL == expr) {
fprintf(stderr, "FATAL: unexpected NULL in codegen_expr\n");
exit(1);
}
switch (expr->kind) {
case E_identexpr: codegen_identexpr(expr); break;
case E_callexpr: codegen_callexpr(expr); break;
case E_intexpr: codegen_intexpr(expr); break;
case E_charexpr: codegen_charexpr(expr); break;
case E_strexpr: codegen_strexpr(expr); break;
case E_arrayexpr: codegen_arrayexpr(expr); break;
case E_unaryexpr: codegen_unaryexpr(expr); break;
case E_binaryexpr: codegen_binaryexpr(expr); break;
case E_castexpr: codegen_castexpr(expr); break;
default: fprintf(stderr, "FATAL: unexpected expr kind in codegen_expr\n"); exit(1); break;
}
}
static void codegen_identexpr(T_expr expr) {
// todo: given in class
//look up the ident, then move it to an intermidate register
int offset = lookup_offset_in_scope(current_offset_scope, expr->identexpr);
MOV_FROM_OFFSET(offset, "%rax");
PUSH("%rax");
}
static void codegen_callexpr(T_expr expr) {
fprintf(stderr, "TODO: codegen_callexpr (remove this message when implemented).");
}
static void codegen_intexpr(T_expr expr) {
fprintf(stderr, "TODO: codegen_intexpr (remove this message when implemented).");
}
static void codegen_charexpr(T_expr expr) {
COMMENT("push the character");
MOV_FROM_IMMEDIATE((int) expr->charexpr, "%rax");
PUSH("%rax");
}
static void codegen_strexpr(T_expr expr) {
// bonus exercise
}
static void codegen_arrayexpr(T_expr expr) {
// bonus exercise
}
static void codegen_unaryexpr(T_expr expr) {
fprintf(stderr, "TODO: codegen_unaryexpr\n");
}
static void codegen_binaryexpr(T_expr expr) {
fprintf(stderr, "TODO: codegen_binaryexpr (remove this message when implemented).");
}
static void codegen_castexpr(T_expr expr) {
// bonus: truncate or extend data between bitwidths depending on type
}
/**
* Emit a function prologue, given some size in bytes of the local
* variables in the stack frame.
*/
static void emit_prologue(int size) {
//save stack
PUSH("%rbp");
MOV("%rsp", "%rbp");
if (size > 0) {
SUBCONST(size, "%rsp");
}
PUSH("%rbx");
}
static void emit_epilogue() {
POP("%rbx");
MOV("%rbp", "%rsp");
POP("%rbp");
RET;
}
/**
* This function returns the size of a type in bytes.
*/
static int bitwidth(T_type type) {
switch (type->kind) {
case E_primitivetype:
switch (type->primitivetype) {
case E_typename_int:
// 32-bit integers
return 4;
case E_typename_char:
// characters are 1 byte
return 1;
default:
fprintf(stderr, "FATAL: unexpected kind in compare_types\n");
exit(1);
}
break;
case E_pointertype:
// pointers are to 64-bit addresses
return 8;
case E_arraytype:
// arrays are the size times the bitwidth of the type
return type->arraytype.size * bitwidth(type->arraytype.type);
case E_functiontype:
fprintf(stderr, "FATAL: functions as values are not supported\n");
exit(1);
break;
default:
fprintf(stderr, "FATAL: unexpected kind in bitwidth\n");
exit(1);
break;
}
}