You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SQA-Homework/afl_fuzz/compile.c

650 lines
19 KiB

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "dictionary.h"
#include "expression.h"
#include "allocate.h"
#include "compile.h"
#define MAX_SOURCEFILES 32
int num_vars = 0;
int num_args = 0;
int num_strs = 0;
int current_line;
type return_type;
static char *program;
static char *program_beginning;
static char *program_pointer;
static char *source_files[MAX_SOURCEFILES] = {NULL};
static char **current_source_file;
static FILE *output_file;
static int continue_label = -1;
static int break_label = -1;
static int loop_variables_size;
char warning_message[256] = {0};
char error_message[256] = {0};
enum cmd_argtype{
cmd_source_file,
cmd_output_file
};
static void print_line(){
char *line_pointer;
char *line_beginning;
line_pointer = program_pointer;
fprintf(stderr, "Line %d in %s:\n", current_line, *current_source_file);
while(line_pointer != program_beginning && line_pointer[-1] != '\n'){
line_pointer--;
}
while(*line_pointer && line_pointer != program_pointer && (*line_pointer == ' ' || *line_pointer == '\t')){
line_pointer++;
}
line_beginning = line_pointer;
while(*line_pointer && *line_pointer != '\n'){
fputc(*line_pointer, stderr);
line_pointer++;
}
fputc('\n', stderr);
while(line_beginning != program_pointer){
fputc(' ', stderr);
line_beginning++;
}
fprintf(stderr, "^\n");
}
void do_warning(){
if(do_print){
warning_message[255] = '\0';
print_line();
fprintf(stderr, "Warning: %s\n", warning_message);
}
}
void do_error(int status){
fclose(output_file);
error_message[255] = '\0';
print_line();
fprintf(stderr, "Error: %s\n", error_message);
free(program);
free_local_variables();
free_global_variables();
exit(status);
}
void compile_file(char *program, char *identifier_name, char *arguments, unsigned int identifier_length, unsigned int num_arguments, FILE *output_file){
int current_line_temp;
program_pointer = program;
program_beginning = program;
fprintf(output_file, ".data\n");
current_line = 1;
skip_whitespace(&program_pointer);
current_line_temp = current_line;
compile_string_constants(program_pointer, output_file);
current_line = current_line_temp;
fprintf(output_file, ".text\n\n");
while(*program_pointer){
compile_function(&program_pointer, identifier_name, arguments, identifier_length, num_arguments, output_file);
free_local_variables();
skip_whitespace(&program_pointer);
}
}
void compile_function(char **c, char *identifier_name, char *arguments, unsigned int identifier_length, unsigned int num_arguments, FILE *output_file){
variable *var;
variable *local_var;
unsigned int current_argument = 0;
num_vars = 0;
variables_size = 0;
return_type = EMPTY_TYPE;
parse_type(&return_type, c, identifier_name, arguments, identifier_length, num_arguments);
if(!identifier_name){
snprintf(error_message, sizeof(error_message), "Expected identifier name");
do_error(1);
}
skip_whitespace(c);
if(peek_type(&return_type) != type_function){
skip_whitespace(c);
if(**c != ';'){
snprintf(error_message, sizeof(error_message), "Expected ';'");
do_error(1);
}
++*c;
var = read_dictionary(global_variables, identifier_name, 0);
if(var){
snprintf(error_message, sizeof(error_message), "Duplicate definitions of non-function data");
do_error(1);
}
var = malloc(sizeof(variable));
var->var_type = return_type;
var->varname = malloc(strlen(identifier_name) + 1);
var->leave_as_address = 0;
strcpy(var->varname, identifier_name);
write_dictionary(&global_variables, var->varname, var, 0);
fprintf(output_file, ".data\n.align 2\n%s:\n.space %d\n.text\n", identifier_name, align4(type_size(&return_type, 0)));
} else {
if(!*identifier_name){
snprintf(error_message, sizeof(error_message), "Expected function name");
do_error(1);
}
var = read_dictionary(global_variables, identifier_name, 0);
if(!var){
var = malloc(sizeof(variable));
var->var_type = return_type;
var->varname = malloc(strlen(identifier_name) + 1);
var->leave_as_address = 1;
strcpy(var->varname, identifier_name);
write_dictionary(&global_variables, var->varname, var, 0);
} else {
if(!types_equal(&(var->var_type), &return_type)){
snprintf(error_message, sizeof(error_message), "Incompatible function definitions");
do_error(1);
}
}
if(**c != '{'){
if(**c != ';'){
snprintf(error_message, sizeof(error_message), "Expected '{' or ';' instead of '%c'", **c);
do_error(1);
}
++*c;
return;
}
++*c;
local_variables[0] = malloc(sizeof(dictionary));
*local_variables[0] = create_dictionary(NULL);
fprintf(output_file, "\n.globl %s\n%s:\n", identifier_name, identifier_name);
pop_type(&return_type);
while(peek_type(&return_type) != type_returns){
if(current_argument >= num_arguments){
snprintf(error_message, sizeof(error_message), "Too many arguments!");
do_error(1);
}
if(read_dictionary(*local_variables[0], arguments, 0)){
snprintf(error_message, sizeof(error_message), "Duplicate argument names");
do_error(1);
}
local_var = malloc(sizeof(variable));
local_var->var_type = get_argument_type(&return_type);
local_var->varname = malloc(strlen(arguments) + 1);
strcpy(local_var->varname, arguments);
local_var->stack_pos = variables_size;
write_dictionary(local_variables[0], local_var->varname, local_var, 0);
variables_size += 4;
arguments += identifier_length;
current_argument++;
num_vars++;
}
pop_type(&return_type);
if(peek_type(&return_type) == type_function){
snprintf(error_message, sizeof(error_message), "Function cannot return function");
do_error(1);
}
num_args = num_vars;
compile_block(c, 1, output_file);
++*c;
var->num_args = num_vars;
fprintf(output_file, "lw $ra, %d($sp)\n", 4 + num_args*4);
fprintf(output_file, "jr $ra\n");
}
}
static void free_var(void *v){
variable *var;
var = (variable *) v;
free(var->varname);
free(var);
}
void compile_block(char **c, unsigned char function_beginning, FILE *output_file){
char *temp;
unsigned char new_scope = 0;
int variables_size_before;
variables_size_before = variables_size;
skip_whitespace(c);
temp = *c;
if(function_beginning || parse_datatype(NULL, &temp)){
new_scope = 1;
current_scope++;
if(current_scope >= MAX_SCOPE){
snprintf(error_message, sizeof(error_message), "Scope depth too large");
do_error(1);
}
if(!local_variables[current_scope]){
local_variables[current_scope] = malloc(sizeof(dictionary));
*local_variables[current_scope] = create_dictionary(NULL);
}
}
temp = *c;
while(parse_datatype(NULL, &temp)){
compile_variable_initializer(c);
skip_whitespace(c);
temp = *c;
num_vars++;
}
if(new_scope){
fprintf(output_file, "addi $sp, $sp, %d\n", -variables_size + variables_size_before);
}
while(**c != '}'){
compile_statement(c, output_file);
skip_whitespace(c);
}
if(new_scope){
fprintf(output_file, "addi $sp, $sp, %d\n", variables_size - variables_size_before);
free_dictionary(*local_variables[current_scope], free_var);
free(local_variables[current_scope]);
local_variables[current_scope] = NULL;
current_scope--;
variables_size = variables_size_before;
}
}
void compile_statement(char **c, FILE *output_file){
value expression_output;
unsigned int label_num0;
unsigned int label_num1;
unsigned int label_num2;
unsigned int label_num3;
int prev_break_label;
int prev_continue_label;
int prev_loop_variables_size;
skip_whitespace(c);
if(!strncmp(*c, "if", 2) && !alphanumeric((*c)[2])){
*c += 2;
skip_whitespace(c);
if(**c != '('){
snprintf(error_message, sizeof(error_message), "Expected '('");
do_error(1);;
}
++*c;
compile_root_expression(&expression_output, c, 1, 0, output_file);
cast(&expression_output, INT_TYPE, 0, output_file);
reset_stack_pos(&expression_output, output_file);
label_num0 = num_labels;
num_labels++;
if(expression_output.data.type == data_register){
fprintf(output_file, "beq $s%d, $zero, __L%d\n", expression_output.data.reg, label_num0);
} else if(expression_output.data.type == data_stack){
fprintf(output_file, "beq $t0, $zero, __L%d\n", label_num0);
}
deallocate(expression_output.data);
skip_whitespace(c);
if(**c != ')'){
snprintf(error_message, sizeof(error_message), "Expected ')'");
do_error(1);
}
++*c;
skip_whitespace(c);
compile_statement(c, output_file);
skip_whitespace(c);
if(!strncmp(*c, "else", 2) && !alphanumeric((*c)[4])){
*c += 4;
label_num1 = num_labels;
num_labels++;
fprintf(output_file, "j __L%d\n", label_num1);
fprintf(output_file, "\n__L%d:\n", label_num0);
skip_whitespace(c);
compile_statement(c, output_file);
fprintf(output_file, "\n__L%d:\n", label_num1);
} else {
fprintf(output_file, "\n__L%d:\n", label_num0);
}
} else if(!strncmp(*c, "while", 5) && !alphanumeric((*c)[5])){
*c += 5;
skip_whitespace(c);
if(**c != '('){
snprintf(error_message, sizeof(error_message), "Expected '('");
do_error(1);
}
++*c;
label_num0 = num_labels;
label_num1 = num_labels + 1;
num_labels += 2;
prev_break_label = break_label;
prev_continue_label = continue_label;
prev_loop_variables_size = loop_variables_size;
break_label = label_num1;
continue_label = label_num0;
loop_variables_size = variables_size;
fprintf(output_file, "\n__L%d:\n", label_num0);
compile_root_expression(&expression_output, c, 1, 0, output_file);
cast(&expression_output, INT_TYPE, 0, output_file);
reset_stack_pos(&expression_output, output_file);
if(expression_output.data.type == data_register){
fprintf(output_file, "beq $s%d, $zero, __L%d\n", expression_output.data.reg, label_num1);
} else if(expression_output.data.type == data_stack){
fprintf(output_file, "beq $t0, $zero, __L%d\n", label_num1);
}
deallocate(expression_output.data);
skip_whitespace(c);
if(**c != ')'){
snprintf(error_message, sizeof(error_message), "Expected ')'");
do_error(1);
}
++*c;
skip_whitespace(c);
compile_statement(c, output_file);
fprintf(output_file, "j __L%d\n", label_num0);
fprintf(output_file, "\n__L%d:\n", label_num1);
loop_variables_size = prev_loop_variables_size;
break_label = prev_break_label;
continue_label = prev_continue_label;
} else if(!strncmp(*c, "for", 3) && !alphanumeric((*c)[3])){
*c += 3;
skip_whitespace(c);
if(**c != '('){
snprintf(error_message, sizeof(error_message), "Expected '('");
do_error(1);
}
++*c;
label_num0 = num_labels;
label_num1 = num_labels + 1;
label_num2 = num_labels + 2;
label_num3 = num_labels + 3;
num_labels += 4;
prev_break_label = break_label;
prev_continue_label = continue_label;
prev_loop_variables_size = loop_variables_size;
break_label = label_num1;
continue_label = label_num3;
loop_variables_size = variables_size;
skip_whitespace(c);
if(**c != ';'){
compile_root_expression(&expression_output, c, 1, 0, output_file);
reset_stack_pos(&expression_output, output_file);
deallocate(expression_output.data);
skip_whitespace(c);
if(**c != ';'){
snprintf(error_message, sizeof(error_message), "Expected ';'");
do_error(1);
}
}
++*c;
fprintf(output_file, "\n__L%d:\n", label_num0);
skip_whitespace(c);
if(**c != ';'){
compile_root_expression(&expression_output, c, 1, 0, output_file);
cast(&expression_output, INT_TYPE, 0, output_file);
reset_stack_pos(&expression_output, output_file);
if(expression_output.data.type == data_register){
fprintf(output_file, "beq $s%d, $zero, __L%d\n", expression_output.data.reg, label_num1);
} else if(expression_output.data.type == data_stack){
fprintf(output_file, "beq $t0, $zero, __L%d\n", label_num1);
}
deallocate(expression_output.data);
skip_whitespace(c);
if(**c != ';'){
snprintf(error_message, sizeof(error_message), "Expected ';'");
do_error(1);
}
}
++*c;
fprintf(output_file, "j __L%d\n\n__L%d:\n", label_num2, label_num3);
skip_whitespace(c);
if(**c != ')'){
compile_root_expression(&expression_output, c, 1, 0, output_file);
reset_stack_pos(&expression_output, output_file);
deallocate(expression_output.data);
skip_whitespace(c);
if(**c != ')'){
snprintf(error_message, sizeof(error_message), "Expected '('");
do_error(1);
}
}
++*c;
fprintf(output_file, "j __L%d\n\n__L%d:\n", label_num0, label_num2);
skip_whitespace(c);
compile_statement(c, output_file);
fprintf(output_file, "j __L%d\n\n__L%d:\n", label_num3, label_num1);
loop_variables_size = prev_loop_variables_size;
break_label = prev_break_label;
continue_label = prev_continue_label;
} else if(!strncmp(*c, "return", 6) && !alphanumeric((*c)[6])){
*c += 6;
skip_whitespace(c);
if(types_equal(&return_type, &VOID_TYPE)){
if(**c != ';'){
snprintf(error_message, sizeof(error_message), "Expected ';' for return in void function");
do_error(1);
}
++*c;
} else {
compile_root_expression(&expression_output, c, 1, 0, output_file);
if(**c != ';'){
snprintf(error_message, sizeof(error_message), "Expected ';'");
do_error(1);
}
++*c;
cast(&expression_output, return_type, 1, output_file);
reset_stack_pos(&expression_output, output_file);
if(expression_output.data.type == data_register){
fprintf(output_file, "sw $s%d, %d($sp)\n", expression_output.data.reg, variables_size);
} else if(expression_output.data.type == data_stack){
fprintf(output_file, "sw $t0, %d($sp)\n", variables_size);
}
deallocate(expression_output.data);
}
fprintf(output_file, "addi $sp, $sp, %d\n", variables_size - num_args*4);
fprintf(output_file, "lw $ra, %d($sp)\n", 4 + num_args*4);
fprintf(output_file, "jr $ra\n");
} else if(!strncmp(*c, "break", 5) && !alphanumeric((*c)[5])){
*c += 5;
skip_whitespace(c);
if(**c != ';'){
snprintf(error_message, sizeof(error_message), "Expected ';' after break");
do_error(1);
}
if(break_label < 0){
snprintf(error_message, sizeof(error_message), "No loop to break out of");
do_error(1);
}
if(variables_size != loop_variables_size)
fprintf(output_file, "addi $sp, $sp, %d\n", variables_size - loop_variables_size);
fprintf(output_file, "j __L%d\n", break_label);
} else if(!strncmp(*c, "continue", 8) && !alphanumeric((*c)[8])){
*c += 8;
skip_whitespace(c);
if(**c != ';'){
snprintf(error_message, sizeof(error_message), "Expected ';' after continue");
do_error(1);
}
if(continue_label < 0){
snprintf(error_message, sizeof(error_message), "No loop to continue in");
do_error(1);
}
if(variables_size != loop_variables_size)
fprintf(output_file, "addi $sp, $sp, %d\n", variables_size - loop_variables_size);
fprintf(output_file, "j __L%d\n", continue_label);
} else if(**c == ';'){
//Empty statement, so pass
++*c;
} else if(**c == '{'){
++*c;
compile_block(c, 0, output_file);
if(**c != '}'){
snprintf(error_message, sizeof(error_message), "Expected '}'");
do_error(1);
}
++*c;
} else {
compile_root_expression(&expression_output, c, 1, 0, output_file);
reset_stack_pos(&expression_output, output_file);
deallocate(expression_output.data);
if(**c == ';'){
++*c;
} else {
snprintf(error_message, sizeof(error_message), "Expected ';'");
do_error(1);
}
}
}
void place_string_constant(char **c, FILE *output_file){
unsigned char ignore_next = 0;
char *beginning;
beginning = *c;
while(**c && (**c != '"' || ignore_next)){
fprintf(output_file, "%c", **c);
if(**c == '\\'){
ignore_next = 1;
} else {
ignore_next = 0;
}
if(**c == '\n'){
program_pointer = *c;
snprintf(error_message, sizeof(error_message), "Expected closing '\"'");
do_error(1);
}
++*c;
}
if(!**c){
program_pointer = beginning;
snprintf(error_message, sizeof(error_message), "Expected closing '\"'");
do_error(1);
}
fprintf(output_file, "\"");
++*c;
}
void compile_string_constants(char *c, FILE *output_file){
unsigned char ignore;
ignore = 0;
skip_whitespace(&c);
while(*c){
if(ignore || *c != '"'){
ignore = 0;
if(*c == '\\' || *c == '\''){
ignore = 1;
} else if(*c == '\n'){
current_line++;
}
c++;
} else {
c++;
fprintf(output_file, "__str%d:\n", num_strs);
fprintf(output_file, ".asciiz \"");
place_string_constant(&c, output_file);
fprintf(output_file, "\n");
num_strs++;
}
skip_whitespace(&c);
}
}
void parse_arguments(int argc, char **argv, char *filenames[], unsigned int num_filenames, char **output_filename){
unsigned int current_arg;
unsigned int current_filename = 0;
enum cmd_argtype argtype = cmd_source_file;
for(current_arg = 1; current_arg < argc; current_arg++){
switch(argtype){
case cmd_source_file:
if(argv[current_arg][0] == '-'){
if(argv[current_arg][1] == 'h' || (argv[current_arg][1] == '-' && argv[current_arg][2] == 'h')){//Display help message
printf("Arguments:\n-h --help Displays this help message\n-o Specify output file\n\nTo compile:\nmcc [SOURCE1] [SOURCE2] [SOURCE3] ... -o [OUTPUT_FILE]\n");
exit(1);
} else if(argv[current_arg][1] == 'o'){
argtype = cmd_output_file;
} else {
fprintf(stderr, "Unrecognized option '%s'\n", argv[current_arg]);
exit(1);
}
} else {
if(current_filename < num_filenames){
filenames[current_filename] = argv[current_arg];
current_filename++;
} else {
fprintf(stderr, "Too many source files\n");
exit(1);
}
}
break;
case cmd_output_file:
if(argv[current_arg][0] == '-'){
fprintf(stderr, "Expected output file name after '-o'\n");
exit(1);
}
*output_filename = argv[current_arg];
argtype = cmd_source_file;
break;
}
}
}
int main(int argc, char **argv){
char identifier_name[32];
char arguments[32*32];
FILE *fp;
unsigned int file_size;
char *output_filename = NULL;
if(argc < 2){
printf("Minimal C Compiler for MIPS\nby Ben Jones\n2/29/2020\n");
return 0;
}
parse_arguments(argc, argv, source_files, MAX_SOURCEFILES - 1, &output_filename);
if(!output_filename){
output_filename = "a.s";
}
output_file = fopen(output_filename, "w");
if(!output_file){
fprintf(stderr, "Could not open file '%s' for writing\n", output_filename);
exit(1);
}
global_variables = create_dictionary(NULL);
current_source_file = source_files;
current_scope = -1;
memset(local_variables, 0, sizeof(local_variables));
while(*current_source_file){
fp = fopen(*current_source_file, "rb");
if(!fp){
fprintf(stderr, "Could not open file '%s' for reading\n", *current_source_file);
free_global_variables();
exit(1);
}
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
rewind(fp);
program = calloc(file_size + 1, sizeof(char));
if(fread(program, sizeof(char), file_size, fp) < file_size){
fclose(fp);
fprintf(stderr, "Failed to read file '%s'\n", *current_source_file);
free_global_variables();
exit(1);
}
fclose(fp);
initialize_register_list();
compile_file(program, identifier_name, arguments, 32, 32, output_file);
free(program);
current_source_file++;
}
fclose(output_file);
free_global_variables();
return 0;
}