Lab: Add basic TAC example
This commit is contained in:
parent
9d2386b339
commit
7be668134d
1
lab/tac/.gitignore
vendored
Normal file
1
lab/tac/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
tac
|
8
lab/tac/Makefile
Normal file
8
lab/tac/Makefile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
CFLAGS = -std=c17 -Wall -Wextra
|
||||||
|
|
||||||
|
all: tac
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) tac
|
||||||
|
|
||||||
|
.PHONY: all clean
|
439
lab/tac/tac.c
Normal file
439
lab/tac/tac.c
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------- 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user