Skip to content
This repository has been archived by the owner on Jun 28, 2024. It is now read-only.

Commit

Permalink
Code Generator
Browse files Browse the repository at this point in the history
* Modify typechecker test cases to cover return type error

* Improve a boolean expression

* Add support for function rtype and prototype checking

* Condense Makefile

* Create scratch.c and scratch.h

* Create label.c and label.h

* Create codegen.c and codegen.h

* Protect type_print() from null pointer

* Implement decl_codegen()

* Implement symbol_codegen()

* Implement stmt_codegen()

* Implement expr_codegen()

* Format library.c

* Add --codegen option in bminor.c

* Add codegen_test

* Create codegen test cases
  • Loading branch information
sghuang19 authored Dec 8, 2023
1 parent 4252d9b commit aa8ed38
Show file tree
Hide file tree
Showing 43 changed files with 917 additions and 48 deletions.
20 changes: 8 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
CFLAGS =
MODS = encoder scanner parser printer resolver typechecker
SRCS = bminor.c $(addsuffix .c, $(MODS)) \
decl.c stmt.c expr.c type.c param_list.c \
symbol.c scope.c hash_table.c \
lex.yy.c grammar.tab.c
CFLAGS = -Wall -Wextra -g
MODS = encoder scanner parser printer resolver typechecker codegen
SRCS = $(shell find . -maxdepth 1 -type f -name "*.c")
OBJS = $(SRCS:.c=.o)

bminor: $(OBJS)
bminor: grammar.tab.o lex.yy.o $(OBJS)
gcc $(CFLAGS) $^ -o $@

scanner.o: scanner.c token.h
gcc $(CFLAGS) -c $< -o $@
lex.yy.o: lex.yy.c
gcc -c $< -o $@ # suppress warnings

%.o: %.c
gcc $(CFLAGS) -c $< -o $@

lex.yy.c: lex.yy.l
flex $<

grammar.tab.c token.h: grammar.y lex.yy.c
grammar.tab.c token.h: grammar.y
bison --defines=token.h $<

# Tests
Expand All @@ -30,8 +27,7 @@ test-%: bminor
clean-test:
rm -f ./test/*/*.bminor.out

clean:
rm -f ./test/*/*.bminor.out
clean: clean-test
rm -f lex.yy.c
rm -f token.h grammar.tab.c grammar.output
rm -f *.o
Expand Down
3 changes: 3 additions & 0 deletions bminor.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "printer.h"
#include "resolver.h"
#include "typechecker.h"
#include "codegen.h"

void usage(int exit_code)
{
Expand Down Expand Up @@ -69,6 +70,8 @@ int main(int argc, char* argv[])
return resolve(d);
else if (strcmp(option, "--typecheck") == 0)
return typecheck(d);
else if (strcmp(option, "--codegen") == 0)
return codegen(d);
else
{
fprintf(stderr, "Unknown option '%s'\n", option);
Expand Down
18 changes: 18 additions & 0 deletions codegen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include <stdio.h>
#include "typechecker.h"
#include "codegen.h"

int codegen_errors = 0;

int codegen(struct decl* d)
{
int type_errors = typecheck(d); // name resolution and type checking
if (type_errors)
{
fprintf(stderr, "CodeGen Error | Type errors occurred, aborting code generation\n");
return type_errors;
}

decl_codegen(d);
return codegen_errors;
}
9 changes: 9 additions & 0 deletions codegen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef CODEGEN_H
#define CODEGEN_H

#include "decl.h"

/** @return number of errors */
int codegen(struct decl* d);

#endif //CODEGEN_H
168 changes: 162 additions & 6 deletions decl.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#include <stdlib.h>
#include <stdio.h>

#include "encoder.h"
#include "stmt.h"
#include "expr.h"
#include "decl.h"
#include "type.h"
#include "param_list.h"
#include "symbol.h"
#include "scope.h"
#include "scratch.h"

struct decl* decl_create(char* name, struct type* type, struct expr* value, struct stmt* code, struct decl* next)
{
Expand Down Expand Up @@ -98,12 +100,16 @@ void decl_resolve(struct decl* d)
expr_resolve(d->value);
if (d->code)
{
d->symbol->prototype = 1;
d->symbol->prototype = 0;
scope_enter();
param_list_resolve(d->type->params);
stmt_resolve(d->code);
scope_exit();
}
else
d->symbol->prototype = 1;

d->symbol->locals = cur_local;

decl_resolve(d->next);
}
Expand Down Expand Up @@ -159,10 +165,29 @@ void type_typecheck(const struct type* t, const char* name)
}
}

struct type* rtype = NULL;

void decl_typecheck(struct decl* d)
{
if (!d) return;

// Global declarations must be constant
if (d->symbol->kind == SYMBOL_GLOBAL && !expr_is_constant(d->value))
{
printf("Type Error | global variable ('%s') cannot be initialized "
"with non-constant expression ", d->name);
if (d->value && d->value->kind == EXPR_LIST)
{
printf("{");
expr_print(d->value);
printf("}");
}
else
expr_print(d->value);
printf("\n");
type_errors++;
}

type_typecheck(d->type, d->name);
struct type* val_type = expr_typecheck(d->value);
if (d->value)
Expand All @@ -180,10 +205,8 @@ void decl_typecheck(struct decl* d)

// Check initializer type
struct expr* e = d->value;
int len = 0;
while (e)
{
len++;
struct type* t = expr_typecheck(e->left);
if (!type_equals(d->type->subtype, t))
{
Expand Down Expand Up @@ -212,12 +235,145 @@ void decl_typecheck(struct decl* d)
}
}

if (d->type->kind == TYPE_FUNCTION && d->symbol->kind == SYMBOL_LOCAL)
if (d->type->kind == TYPE_FUNCTION)
{
printf("Type Error | cannot declare function ('%s') inside function\n", d->name);
type_errors++;
rtype = d->type->subtype;
if (d->symbol->kind == SYMBOL_LOCAL)
{
printf("Type Error | cannot declare function ('%s') inside function\n", d->name);
type_errors++;
}
}

stmt_typecheck(d->code);
decl_typecheck(d->next);
}

/** Pass current function name to stmt_codegen */
const char* cur_func;

void decl_codegen_func(struct decl* d)
{
if (d->symbol->prototype)
{
fprintf(stderr, "is a prototype");
return;
}

// Prologue
printf(".text\n");
printf(".global %s\n", d->name);
printf("%s:\n", d->name);

// Save old base ptr and set new base ptr
printf("pushq %%rbp\n");
printf("movq %%rsp, %%rbp\n");

// Allocate space for local variables, all word size
printf("subq $%d, %%rsp\n", d->symbol->locals * 8);

// Push callee-saved registers
printf("pushq %%rbx\n");
printf("pushq %%r12\n");
printf("pushq %%r13\n");
printf("pushq %%r14\n");
printf("pushq %%r15\n");

cur_func = d->name;
stmt_codegen(d->code);

// Epilogue
printf(".%s_epilogue:\n", d->name);

// Restore callee-saved registers
printf("popq %%r15\n");
printf("popq %%r14\n");
printf("popq %%r13\n");
printf("popq %%r12\n");
printf("popq %%rbx\n");

// Restore old base ptr
printf("movq %%rbp, %%rsp\n");
printf("popq %%rbp\n");
printf("ret\n");

printf("\n");
}

/** Generate code for global initializer */
void decl_codegen_val(const struct expr* v)
{
if (!v)
{
printf(" .quad 0\n");
return;
}
printf(" ");
char es[MAX_STRING_LEN * 5 + 2];

switch (v->kind)
{
case EXPR_NEG:
// FIXME: handle global negative value
printf(".quad -");
expr_print(v->right);
printf("\n");
break;
case EXPR_INTEGER_LITERAL:
case EXPR_BOOLEAN_LITERAL:
printf(".quad %d\n", v->integer_literal);
break;
case EXPR_FLOAT_LITERAL:
printf(".float %f\n", v->float_literal);
break;
case EXPR_CHAR_LITERAL:
printf(".quad %d\n", v->char_literal);
case EXPR_STRING_LITERAL:
string_encode(v->string_literal, es);
printf(".string %s\n", es);
break;
case EXPR_LIST:
decl_codegen_val(v->left);
if (v->right)
decl_codegen_val(v->right);
break;
default:
// Invalid initializer
break;
}
}

/** Define global variables */
void decl_codegen_var(struct decl* d)
{
printf(".data\n");
printf(".global %s\n", d->name);
printf("%s:\n", d->name);

decl_codegen_val(d->value);
printf("\n");
}

void decl_codegen(struct decl* d)
{
if (!d) return;
switch (d->symbol->kind)
{
case SYMBOL_GLOBAL:
d->type->kind == TYPE_FUNCTION ? decl_codegen_func(d) : decl_codegen_var(d);
break;
case SYMBOL_LOCAL:
if (d->value)
{
expr_codegen(d->value);
printf("movq %s, -%d(%%rbp)\n", scratch_name(d->value->reg), (d->symbol->which + 1) * 8);
scratch_free(d->value->reg);
}
// Otherwise do nothing
break;
case SYMBOL_PARAM:
// Do nothing
break;
}
decl_codegen(d->next);
}
1 change: 1 addition & 0 deletions decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ struct decl* decl_create(char* name, struct type* type, struct expr* value, stru
void decl_print(struct decl* d, int indent);
void decl_resolve(struct decl* d);
void decl_typecheck(struct decl* d);
void decl_codegen(struct decl* d);

#endif
8 changes: 4 additions & 4 deletions encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

int is_hex(char c)
{
return c >= '0' && c <= '9' ||
c >= 'A' && c <= 'F' ||
c >= 'a' && c <= 'f';
return (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'F') ||
(c >= 'a' && c <= 'f');
}

int string_decode(const char* es, char* s)
Expand Down Expand Up @@ -202,7 +202,7 @@ int decode(FILE* fp)
{
// Find the size of the file
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
unsigned long file_size = ftell(fp);
if (file_size > (MAX_STRING_LEN * 5 + 2) * sizeof(char))
{
fprintf(stderr, "Invalid string: too long\n");
Expand Down
Loading

0 comments on commit aa8ed38

Please sign in to comment.