diff --git a/lab/symbol_table.c b/lab/symbol_table/symbol_table.c similarity index 100% rename from lab/symbol_table.c rename to lab/symbol_table/symbol_table.c diff --git a/lab/tac/.gitignore b/lab/tac/.gitignore new file mode 100644 index 0000000..863400c --- /dev/null +++ b/lab/tac/.gitignore @@ -0,0 +1 @@ +tac diff --git a/lab/tac/Makefile b/lab/tac/Makefile new file mode 100644 index 0000000..0483fb7 --- /dev/null +++ b/lab/tac/Makefile @@ -0,0 +1,8 @@ +CFLAGS = -std=c17 -Wall -Wextra + +all: tac + +clean: + $(RM) tac + +.PHONY: all clean diff --git a/lab/tac/tac.c b/lab/tac/tac.c new file mode 100644 index 0000000..0c114fc --- /dev/null +++ b/lab/tac/tac.c @@ -0,0 +1,439 @@ +#include +#include +#include +#include +#include + +// -------------------------------------------------------------------- TAC Definition + +enum tac_variable_type { + TAC_VARIABLE_TYPE_VOID = 0, + TAC_VARIABLE_TYPE_INT, + TAC_VARIABLE_TYPE_FLOAT, +}; + +const char *tac_variable_type_to_string(enum tac_variable_type type) +{ + switch (type) { + case TAC_VARIABLE_TYPE_VOID: + return "void"; + case TAC_VARIABLE_TYPE_INT: + return "int"; + case TAC_VARIABLE_TYPE_FLOAT: + return "float"; + } + + assert(false); + return "INVALID"; +} + +struct tac_variable { + enum tac_variable_type type; + uint64_t identifier; +}; + +void tac_variable_print(const struct tac_variable *variable) +{ + assert(variable); + if (variable->type == TAC_VARIABLE_TYPE_VOID) { + printf("unused"); + } else { + printf("v%ld", variable->identifier); + } +} + +enum tac_instruction_op { + TAC_INSTRUCTION_OP_CONST, + + TAC_INSTRUCTION_OP_ASSIGN, + + // Arithmetic operations + TAC_INSTRUCTION_OP_ADD, + // ... + + // Control flow + TAC_INSTRUCTION_OP_LABEL, + TAC_INSTRUCTION_OP_JUMP, + TAC_INSTRUCTION_OP_JUMP_IF, + + // Function calls + TAC_INSTRUCTION_OP_CALL, + TAC_INSTRUCTION_OP_RETURN, + TAC_INSTRUCTION_OP_PUSH, + TAC_INSTRUCTION_OP_POP, +}; + +const char *tac_instruction_op_to_string(enum tac_instruction_op op) +{ + switch (op) { + case TAC_INSTRUCTION_OP_CONST: + return "CONST"; + case TAC_INSTRUCTION_OP_ASSIGN: + return "ASSIGN"; + case TAC_INSTRUCTION_OP_ADD: + return "ADD"; + case TAC_INSTRUCTION_OP_LABEL: + return "LABEL"; + case TAC_INSTRUCTION_OP_JUMP: + return "JUMP"; + case TAC_INSTRUCTION_OP_JUMP_IF: + return "JUMP_IF"; + case TAC_INSTRUCTION_OP_CALL: + return "CALL"; + case TAC_INSTRUCTION_OP_RETURN: + return "RETURN"; + case TAC_INSTRUCTION_OP_PUSH: + return "PUSH"; + case TAC_INSTRUCTION_OP_POP: + return "POP"; + } + + assert(false); + return "INVALID"; +} + +struct tac_function { + const char name[256]; + + // Using a singly linked list here for convenience, although it is super + // inefficient. + struct tac_instruction *start; +}; + +struct tac_instruction { + enum tac_instruction_op op; + struct tac_variable arg1, arg2, result; + + union { + long int_constant; + double float_constant; + }; + + uint64_t label; + const char *function; + + struct tac_instruction *next; +}; + +// -------------------------------------------------------------------- TAC Generator + +struct tac_variable gen_variable(enum tac_variable_type type) +{ + static uint64_t variable_index = 1; + + return (struct tac_variable){ + .type = type, + .identifier = variable_index++, + }; +} + +struct tac_instruction *gen_label(void) +{ + static uint64_t label_index = 1; + + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_LABEL, + .label = label_index++, + }; + return result; +} + +struct tac_instruction *gen_const_int(long value) +{ + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_CONST, + .result = gen_variable(TAC_VARIABLE_TYPE_INT), + .int_constant = value, + }; + return result; +} + +struct tac_instruction *gen_const_float(double value) +{ + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_CONST, + .result = gen_variable(TAC_VARIABLE_TYPE_FLOAT), + .float_constant = value, + }; + return result; +} + +struct tac_instruction *gen_assign(struct tac_variable lhs, struct tac_variable rhs) +{ + assert(lhs.type == rhs.type); + + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_ASSIGN, + .arg1 = rhs, + .result = lhs, + }; + return result; +} + +struct tac_instruction *gen_add(struct tac_variable lhs, struct tac_variable rhs) +{ + assert(lhs.type == rhs.type); + + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_ADD, + .arg1 = lhs, + .arg2 = rhs, + .result = gen_variable(lhs.type), + }; + return result; +} + +struct tac_instruction *gen_jump(uint64_t label) +{ + assert(label != 0); + + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_JUMP, + .label = label, + }; + return result; +} + +struct tac_instruction *gen_jump_if(uint64_t label, struct tac_variable condition) +{ + assert(label != 0); + assert(condition.type == TAC_VARIABLE_TYPE_INT); + + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_JUMP_IF, + .arg1 = condition, + .label = label, + }; + return result; +} + +struct tac_instruction *gen_call(const char *function, enum tac_variable_type result_type) +{ + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_CALL, + .result = gen_variable(result_type), + .function = function, + }; + return result; +} + +struct tac_instruction *gen_return() +{ + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_RETURN, + }; + return result; +} + +struct tac_instruction *gen_return_variable(struct tac_variable arg1) +{ + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_RETURN, + .arg1 = arg1, + }; + return result; +} + +struct tac_instruction *gen_push(struct tac_variable variable) +{ + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_PUSH, + .arg1 = variable, + }; + return result; +} + +struct tac_instruction *gen_pop(enum tac_variable_type result_type) +{ + struct tac_instruction *result = malloc(sizeof(*result)); + assert(result); + + *result = (struct tac_instruction){ + .op = TAC_INSTRUCTION_OP_POP, + .result = gen_variable(result_type), + }; + return result; +} + +void append_instruction(struct tac_function *function, struct tac_instruction *instruction) +{ + assert(function); + assert(instruction); + + struct tac_instruction **end = &function->start; + while (*end) { + end = &(*end)->next; + } + + *end = instruction; +} + +void clear_instructions(struct tac_function *function) +{ + assert(function); + + struct tac_instruction *current = function->start; + while (current) { + struct tac_instruction *to_free = current; + current = current->next; + free(to_free); + } +} + +void print_instructions(struct tac_function *function) +{ + assert(function); + + printf("==== %s ====\n", function->name); + + for (struct tac_instruction *instruction = function->start; instruction; instruction = instruction->next) { + printf("%s\t", tac_instruction_op_to_string(instruction->op)); + if (instruction->result.type != TAC_VARIABLE_TYPE_VOID) { + tac_variable_print(&instruction->result); + printf("\t%s\t", tac_variable_type_to_string(instruction->result.type)); + } else { + printf("\t\t"); + } + + switch (instruction->op) { + case TAC_INSTRUCTION_OP_CONST: + if (instruction->result.type == TAC_VARIABLE_TYPE_INT) { + printf("%ld", instruction->int_constant); + } else { + printf("%f", instruction->float_constant); + } + break; + + case TAC_INSTRUCTION_OP_ADD: + tac_variable_print(&instruction->arg1); + printf("\t"); + tac_variable_print(&instruction->arg2); + break; + + case TAC_INSTRUCTION_OP_LABEL: + case TAC_INSTRUCTION_OP_JUMP: + printf("L_%ld", instruction->label); + break; + + case TAC_INSTRUCTION_OP_JUMP_IF: + printf("L_%ld\t", instruction->label); + tac_variable_print(&instruction->arg1); + break; + + case TAC_INSTRUCTION_OP_CALL: + printf("%s", instruction->function); + break; + + case TAC_INSTRUCTION_OP_RETURN: + if (instruction->arg1.type != TAC_VARIABLE_TYPE_VOID) { + tac_variable_print(&instruction->arg1); + } + break; + + case TAC_INSTRUCTION_OP_ASSIGN: + case TAC_INSTRUCTION_OP_PUSH: + tac_variable_print(&instruction->arg1); + break; + + case TAC_INSTRUCTION_OP_POP: + break; + + default: + assert(false); + break; + } + + printf("\n"); + } +} + +// -------------------------------------------------------------------- + +int main(void) +{ + struct tac_function my_function = { + .name = "My TAC Function", + }; + + struct tac_instruction *inst1 = gen_pop(TAC_VARIABLE_TYPE_INT); + append_instruction(&my_function, inst1); + + struct tac_instruction *c1 = gen_const_int(1); + append_instruction(&my_function, c1); + + struct tac_instruction *c2 = gen_const_int(2); + append_instruction(&my_function, c2); + + struct tac_instruction *inst2 = gen_add(c1->result, c2->result); + append_instruction(&my_function, inst2); + + struct tac_instruction *inst3 = gen_add(inst2->result, inst1->result); + append_instruction(&my_function, inst3); + + struct tac_instruction *l1 = gen_label(); + struct tac_instruction *inst4 = gen_jump_if(l1->label, inst3->result); + append_instruction(&my_function, inst4); + + struct tac_instruction *inst5 = gen_add(inst3->result, c1->result); + append_instruction(&my_function, inst5); + append_instruction(&my_function, gen_assign(inst3->result, inst5->result)); + + struct tac_instruction *l2 = gen_label(); + struct tac_instruction *inst7 = gen_jump(l2->label); + append_instruction(&my_function, inst7); + + append_instruction(&my_function, l1); + + struct tac_instruction *inst8 = gen_add(inst3->result, c2->result); + append_instruction(&my_function, inst8); + append_instruction(&my_function, gen_assign(inst3->result, inst8->result)); + + append_instruction(&my_function, l2); + + append_instruction(&my_function, gen_push(c2->result)); + append_instruction(&my_function, gen_push(c1->result)); + append_instruction(&my_function, gen_push(inst3->result)); + + struct tac_instruction *inst9 = gen_call("some_function", TAC_VARIABLE_TYPE_FLOAT); + append_instruction(&my_function, inst9); + + append_instruction(&my_function, gen_return_variable(inst9->result)); + + print_instructions(&my_function); + + clear_instructions(&my_function); + return EXIT_SUCCESS; +}