add quad repersentation

Signed-off-by: szdytom <szdytom@163.com>
This commit is contained in:
方而静 2023-06-13 19:44:07 +08:00
parent 24ab2a70f7
commit 38fd3c8911
17 changed files with 474 additions and 120 deletions

View File

@ -6,16 +6,21 @@ enum {
IR_ADD, IR_SUB, IR_MUL, IR_DIV,
IR_EQ, IR_NE, IR_LT, IR_GT, IR_LE, IR_GE,
IR_NEG,
IR_LIT,
IR_LIT32, IR_LIT64,
};
struct IRblock {
}
struct IRinstruction {
int op;
union {
struct { IRinstruction *left, *right; };
int val;
struct { int left, right; };
int val_i32;
int val_i64;
};
};
#endif
#endif

View File

@ -19,28 +19,32 @@ enum {
extern const char *ast_opname[31];
#ifndef ACC_ASTnode_SHARED_FIELDS
// AST structure field shared by all types
// llist_node *n : for linklist
// llist_node *n : linklist header
// int op : node operation
#define ASTnode_SHARED_FIELDS \
#define ACC_ASTnode_SHARED_FIELDS \
struct llist_node n; \
int op;
#endif
// AST structure (common)
struct ASTnode {
ASTnode_SHARED_FIELDS
ACC_ASTnode_SHARED_FIELDS
};
// AST binary operation node
struct ASTbinnode {
ASTnode_SHARED_FIELDS
ACC_ASTnode_SHARED_FIELDS
struct ASTnode *left;
struct ASTnode *right;
};
// AST if statement node
struct ASTifnode {
ASTnode_SHARED_FIELDS
ACC_ASTnode_SHARED_FIELDS
struct ASTnode *left; // condition true branch
struct ASTnode *right; // condition false branch
struct ASTnode *cond;
@ -48,41 +52,51 @@ struct ASTifnode {
// AST unary operation node
struct ASTunnode {
ASTnode_SHARED_FIELDS
ACC_ASTnode_SHARED_FIELDS
struct ASTnode *left;
};
// AST block node
struct ASTblocknode {
ASTnode_SHARED_FIELDS
ACC_ASTnode_SHARED_FIELDS
struct linklist st; // statements linklist
};
// AST integer literal (32bit) node
struct ASTi32node {
ASTnode_SHARED_FIELDS
ACC_ASTnode_SHARED_FIELDS
int32_t val;
};
// AST integer literal (64bit) node
struct ASTi64node {
ASTnode_SHARED_FIELDS
ACC_ASTnode_SHARED_FIELDS
int64_t val;
};
// AST assign literal node
struct ASTassignnode {
ASTnode_SHARED_FIELDS
ACC_ASTnode_SHARED_FIELDS
struct ASTnode* left;
struct ASTnode* right;
};
// AST variable value node
struct ASTvarnode {
ASTnode_SHARED_FIELDS
ACC_ASTnode_SHARED_FIELDS
int id;
};
// A function with its AST root.
// TODO: parameters
struct Afunction {
struct llist_node n; // linklist header
char *name; // function name
struct ASTnode *rt; // AST root
};
struct Afunction* afunc_make();
struct ASTnode* ast_make_binary(int op, struct ASTnode *left, struct ASTnode *right);
struct ASTnode* ast_make_lit_i32(int32_t x);
struct ASTnode* ast_make_lit_i64(int64_t x);
@ -91,6 +105,11 @@ struct ASTnode* ast_make_block();
struct ASTnode* ast_make_var(int id);
struct ASTnode* ast_make_assign(int op, struct ASTnode *left, struct ASTnode *right);
struct ASTnode* ast_make_if(struct ASTnode *left, struct ASTnode *right, struct ASTnode *cond);
void ast_debug_print(FILE *Outfile, struct ASTnode *rt);
void afunc_debug_print(FILE *Outfile, struct Afunction *f);
void afunc_free(struct Afunction *f);
void ast_free(struct ASTnode *x);
#endif

View File

@ -7,6 +7,7 @@
void* malloc_or_fail(size_t s, const char *func_name);
noreturn void fail_target(const char *target_name);
noreturn void fail_malloc(const char *func_name);
noreturn void fail_quad_op(int op, const char *func_name);
noreturn void fail_ast_op(int op, const char *func_name);
noreturn void fail_ce_expect(int line, const char *expected, const char *got);
noreturn void fail_ce(int line, const char *reason);

View File

@ -1,6 +1,6 @@
#ifndef ACC_PARSE_H
#define ACC_PARSE_H
struct ASTnode* parse(const char *name);
struct Afunction* parse_source(const char *filename);
#endif

View File

@ -1,7 +0,0 @@
#ifndef ACC_DEBUG_PRINT_AST
#define ACC_DEBUG_PRINT_AST
void debug_ast_print(FILE *Outfile, struct ASTnode *rt);
#endif

96
include/quad.h Normal file
View File

@ -0,0 +1,96 @@
#ifndef ACC_QUAD_H
#define ACC_QUAD_H
#include <stdint.h>
#include <stdbool.h>
#include "ast.h"
#include "util/linklist.h"
// Operation code definations
enum {
// Loading immediates
Q_IMM_I32, // integer (32bits)
// Terminates
Q_RET, // return
Q_BR_ALWAYS, // conditional goto: always goto true branch.
// Guard
Q_NULL,
};
// Local(in function) variable.
struct Qvar {
struct llist_node n; // linklist header
int id; // variable id
};
// Quad instruction.
struct Quad {
struct llist_node n; // linklist header
int op; // operation code
struct Qvar *dest; // operation destination
union {
struct { struct Qvar *left, *right; }; // left/right operands for calculations
struct { struct Qblock *bt, *bf; }; // true branch & false branch for conditional goto
int32_t val_i32; // immediate: integer (32bits)
};
};
// Basic block consist of Quad instructions.
struct Qblock {
struct llist_node n; // linklist header
int id; // block id
struct linklist ins; // instruction Quads
bool is_complete; // whether the block is properly ended with a terminate
};
// Function containing Quad instructions.
// TODO: paramaters
struct Qfunction {
struct llist_node n; // linklist header
char *name; // function name
struct linklist bs; // basic blocks
struct linklist vars; // local variables
};
// Constructs a Quad with an operator, a destination and two operands.
struct Quad* quad_make(int op, struct Qvar *dest, struct Qvar *left, struct Qvar *right);
// Constructs a Quad with a loads an integer immediate (32bits).
struct Quad* quad_make_i32(struct Qvar *dest, int32_t v);
// Constructs a Quad with instruction Q_BR_XXX (conditional goto).
struct Quad* quad_make_br(int op, struct Qblock *bt, struct Qblock *bf);
// Returns whether the given opcode is a opcode from a terminate.
bool quad_is_terminate(int op);
// Constructs a Qblock.
struct Qblock* qblock_make();
// Allocates a new basic block in the give function.
struct Qblock* qfunc_new_block(struct Qfunction *f);
// Appends an instruction to a block.
void qblock_add_ins(struct Qblock *b, struct Quad *x);
// Constructs a new Qvar.
struct Qvar* qvar_make(int id);
// Allocates a new variable in the given function.
struct Qvar* qfunc_new_var(struct Qfunction *f);
// Generates Quad Repersentation from an AST
struct Qfunction* qfunc_cgenerate(struct Afunction *afunc);
// Frees a Qblock and all its components.
void qblock_free(struct Qblock *b);
// Frees a Qfunction and all its components.
void qfunc_free(struct Qfunction *f);
// Prints the contents of a Qfunction for debugging.
void qfunc_debug_print(struct Qfunction *self, FILE *Outfile);
#endif

View File

@ -3,6 +3,6 @@
#include "util/linklist.h"
struct linklist scan_tokens(const char *name);
struct linklist scan_tokens(const char *filename);
#endif

View File

@ -3,7 +3,9 @@
// Target types
enum {
TARGET_AST
TARGET_AST,
TARGET_QUAD,
TARGET_NULL,
};
int target_parse(const char *target_string);

View File

@ -3,7 +3,9 @@
#include <stdbool.h>
#define ACC_ARRAY_LENGTH(a) (sizeof((a))/sizeof(*(a)))
bool strequal(const char *s1, const char *s2);
char *strclone(const char *s);
char* strclone(const char *s);
#endif

21
main.c
View File

@ -5,7 +5,7 @@
#include "parse.h"
#include "ast.h"
#include "target.h"
#include "print_ast.h"
#include "quad.h"
// Print out a usage if started incorrectly
static void usage(char *prog) {
@ -14,8 +14,13 @@ static void usage(char *prog) {
exit(1);
}
static FILE *Outfile;
// Do clean up job
void unload(void) {
if (Outfile && Outfile != stdout) {
fclose(Outfile);
}
}
int main(int argc, char *argv[]) {
@ -24,19 +29,21 @@ int main(int argc, char *argv[]) {
usage(argv[0]);
}
FILE *Outfile;
if (argc >= 4) {
Outfile = fopen(argv[3], "w");
} else {
Outfile = fopen("out.txt", "w");
Outfile = stdout;
}
int target = target_parse(argv[1]);
struct ASTnode *rt = parse(argv[2]);
struct Afunction *afunc = parse_source(argv[2]);
if (target == TARGET_AST) {
debug_ast_print(Outfile, rt);
afunc_debug_print(Outfile, afunc);
} else if (target == TARGET_QUAD) {
struct Qfunction *qfunc = qfunc_cgenerate(afunc);
qfunc_debug_print(qfunc, Outfile);
qfunc_free(qfunc);
}
ast_free(rt);
fclose(Outfile);
afunc_free(afunc);
return (0);
}

View File

@ -16,7 +16,7 @@ const char *ast_opname[] = {
NULL
};
// Build and return a binary AST node
// Constructs a binary AST node
struct ASTnode* ast_make_binary(int op, struct ASTnode *left, struct ASTnode *right) {
struct ASTbinnode *x = malloc_or_fail(sizeof(struct ASTbinnode), __FUNCTION__);
@ -26,7 +26,7 @@ struct ASTnode* ast_make_binary(int op, struct ASTnode *left, struct ASTnode *ri
return ((struct ASTnode*)x);
}
// Make an AST integer literal (32bit) node
// Make an AST integer literal (32bits) node
struct ASTnode* ast_make_lit_i32(int32_t v) {
struct ASTi32node *x = malloc_or_fail(sizeof(struct ASTi32node), __FUNCTION__);
@ -35,7 +35,7 @@ struct ASTnode* ast_make_lit_i32(int32_t v) {
return ((struct ASTnode*)x);
}
// Make an AST integer literal (64bit) node
// Make an AST integer literal (64bits) node
struct ASTnode* ast_make_lit_i64(int64_t v) {
struct ASTi64node *x = malloc_or_fail(sizeof(struct ASTi64node), __FUNCTION__);
@ -92,7 +92,83 @@ struct ASTnode* ast_make_if(struct ASTnode *left, struct ASTnode *right, struct
return ((struct ASTnode*)x);
}
// free an AST's memory
static void ast_print_dfs(FILE* Outfile, struct ASTnode *x, int tabs) {
for (int i = 0; i < tabs; ++i) {
fprintf(Outfile, "\t");
}
if (x == NULL) {
fprintf(Outfile, "--->NULL.\n");
return;
}
switch(x->op) {
case A_RETURN: case A_PRINT: {
struct ASTunnode *t = (struct ASTunnode*)x;
fprintf(Outfile, "--->UNOP(%s)\n", ast_opname[x->op]);
ast_print_dfs(Outfile, t->left, tabs + 1);
} break;
case A_LIT_I32: {
struct ASTi32node *t = (struct ASTi32node*)x;
fprintf(Outfile, "--->INT32(%d)\n", t->val);
} break;
case A_LIT_I64: {
struct ASTi64node *t = (struct ASTi64node*)x;
fprintf(Outfile, "--->INT64(%lld)\n", t->val);
} break;
case A_BLOCK: {
struct ASTblocknode *t = (struct ASTblocknode*)x;
fprintf(Outfile, "--->BLOCK(%d statements)\n", t->st.length);
struct llist_node *p = t->st.head;
while (p) {
ast_print_dfs(Outfile, (struct ASTnode*)p, tabs + 1);
p = p->nxt;
}
} break;
default: {
fail_ast_op(x->op, __FUNCTION__);
} break;
}
}
// Prints the structure of a AST into Outfile.
void ast_debug_print(FILE *Outfile, struct ASTnode *rt) {
ast_print_dfs(Outfile, rt, 0);
}
// Prints the structure of a Afunction into Outfile.
void afunc_debug_print(FILE *Outfile, struct Afunction *f) {
fprintf(Outfile, "FUNCTION %s: \n", f->name);
ast_print_dfs(Outfile, f->rt, 0);
}
// Constructs a Afunction.
struct Afunction* afunc_make() {
struct Afunction *res = (struct Afunction*)malloc_or_fail(sizeof(struct Afunction), __FUNCTION__);
res->rt = NULL;
res->name = NULL;
return res;
}
// Frees a Afunction and all its components.
void afunc_free(struct Afunction *f) {
if (f->name) {
free(f->name);
}
if (f->rt) {
ast_free(f->rt);
}
free(f);
}
// Frees an AST's memory, including its childs.
void ast_free(struct ASTnode *x) {
if (x == NULL) {
return;
@ -125,10 +201,13 @@ void ast_free(struct ASTnode *x) {
ast_free((struct ASTnode*)p);
p = nxt;
}
llist_free(&t->st);
} break;
case A_LIT_I32: case A_LIT_I64: {
} break;
default: {
fail_ast_op(x->op, __FUNCTION__);
} break;
}
free(x);

View File

@ -1,6 +1,8 @@
#include <stdio.h>
#include <stdlib.h>
#include "scan.h"
#include "ast.h"
#include "quad.h"
void fail_target(const char *target_name) {
fprintf(stderr, "unknown target: %s.\n", target_name);
@ -20,9 +22,24 @@ void* malloc_or_fail(size_t s, const char *func_name) {
return (res);
}
void fail_quad_op(int op, const char *func_name) {
if (op < Q_NULL) {
fprintf(stderr, "%s: unsupported Quad operator %s.\n", func_name, ast_opname[op]);
exit(1);
} else {
fprintf(stderr, "%s: unknown Quad operator %d.\n", func_name, op);
exit(1);
}
}
void fail_ast_op(int op, const char *func_name) {
fprintf(stderr, "%s: unknown ast operator %d.\n", func_name, op);
exit(1);
if (op < A_SOUL) {
fprintf(stderr, "%s: unsupported AST operator %s.\n", func_name, ast_opname[op]);
exit(1);
} else {
fprintf(stderr, "%s: unknown AST operator %d.\n", func_name, op);
exit(1);
}
}
void fail_ce_expect(int line, const char *expected, const char *got) {

View File

@ -347,11 +347,13 @@ static struct ASTnode* statement(void) {
// Parse one top-level function
// Sets the func_name param.
static struct ASTnode* function(char **func_name) {
static struct Afunction* function() {
struct Afunction *res = afunc_make();
match(T_INT);
expect(T_ID);
*func_name = current()->val_s; // transfer ownership of the identifier string to caller
current()->val_s = NULL; // prevent it from being freed in token_free() called by next.
res->name = current()->val_s; // transfer ownership of the identifier string to caller
current()->val_s = NULL; // prevent it from being freed in token_free() called by next().
next();
match(T_LP);
@ -359,20 +361,19 @@ static struct ASTnode* function(char **func_name) {
next();
goto END_PARAM_LIST;
}
// TODO: param list
// TODO: parameter list
END_PARAM_LIST:
match(T_RP);
return (block());
res->rt = block();
return (res);
}
// Parse ans return the full ast
struct ASTnode* parse(const char *name) {
Tokens = scan_tokens(name);
char *func_name;
struct ASTnode* res = function(&func_name);
struct Afunction* parse_source(const char *filename) {
Tokens = scan_tokens(filename);
struct Afunction* res = function();
free(func_name);
while (Tokens.length > 0) {
token_free(llist_popfront(&Tokens));
}

View File

@ -1,65 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "ast.h"
#include "print_ast.h"
#include "symbol.h"
#include "fatals.h"
#include "util/array.h"
static int tabs;
static void print_tabs(FILE* Outfile) {
for (int i = 0; i < tabs; ++i) {
fprintf(Outfile, "\t");
}
}
static void ast_dfs(FILE* Outfile, struct ASTnode *x) {
print_tabs(Outfile);
if (x == NULL) {
fprintf(Outfile, "--->NULL.\n");
return;
}
switch(x->op) {
case A_RETURN: case A_PRINT: {
struct ASTunnode *t = (struct ASTunnode*)x;
fprintf(Outfile, "--->UNOP(%s)\n", ast_opname[x->op]);
tabs += 1;
ast_dfs(Outfile, t->left);
tabs -= 1;
} break;
case A_LIT_I32: {
struct ASTi32node *t = (struct ASTi32node*)x;
fprintf(Outfile, "--->INT32(%d)\n", t->val);
} break;
case A_LIT_I64: {
struct ASTi64node *t = (struct ASTi64node*)x;
fprintf(Outfile, "--->INT64(%lld)\n", t->val);
} break;
case A_BLOCK: {
struct ASTblocknode *t = (struct ASTblocknode*)x;
fprintf(Outfile, "--->BLOCK(%d statements)\n", t->st.length);
tabs += 1;
struct llist_node *p = t->st.head;
while (p) {
ast_dfs(Outfile, (struct ASTnode*)p);
p = p->nxt;
}
tabs -= 1;
} break;
default: {
fprintf(Outfile, "--->%s...\n", ast_opname[x->op]);
} break;
}
}
void debug_ast_print(FILE *Outfile, struct ASTnode *rt) {
tabs = 0;
ast_dfs(Outfile, rt);
}

193
src/quad.c Normal file
View File

@ -0,0 +1,193 @@
#include <stdio.h>
#include <stdlib.h>
#include "quad.h"
#include "fatals.h"
// Constructs a Quad with an operator, a destination and two operands.
struct Quad* quad_make(int op, struct Qvar *dest, struct Qvar *left, struct Qvar *right) {
struct Quad *x = malloc_or_fail(sizeof(struct Quad), __FUNCTION__);
x->op = op;
x->dest = dest;
x->left = left;
x->right = right;
return (x);
}
// Constructs a Quad with a loads an integer immediate (32bits).
struct Quad* quad_make_i32(struct Qvar *dest, int32_t v) {
struct Quad *x = malloc_or_fail(sizeof(struct Quad), __FUNCTION__);
x->op = Q_IMM_I32;
x->dest = dest;
x->val_i32 = v;
return (x);
}
// Constructs a Quad with instruction Q_BR_XXX (conditional goto).
struct Quad* quad_make_br(int op, struct Qblock *bt, struct Qblock *bf) {
struct Quad *x = malloc_or_fail(sizeof(struct Quad), __FUNCTION__);
x->op = op;
x->bt = bt;
x->bf = bf;
return (x);
}
// Returns whether the given opcode is a opcode from a terminate.
bool quad_is_terminate(int op) {
switch (op) {
case Q_RET:
case Q_BR_ALWAYS:
return (true);
default:
return (false);
}
}
// Constructs a Qblock.
struct Qblock* qblock_make(int id) {
struct Qblock *x = malloc_or_fail(sizeof(struct Qblock), __FUNCTION__);
llist_init(&x->ins);
x->id = id;
x->is_complete = false;
return (x);
}
// Allocates a new basic block in the give function.
struct Qblock* qfunc_new_block(struct Qfunction *f) {
struct Qblock *x = qblock_make(f->bs.length);
llist_pushback(&f->bs, x);
return (x);
}
// Appends an instruction to a block.
void qblock_add_ins(struct Qblock *b, struct Quad *x) {
if (b->is_complete) {
return;
}
llist_pushback(&b->ins, x);
}
// Constructs a new Qvar.
struct Qvar* qvar_make(int id) {
struct Qvar *res = malloc_or_fail(sizeof(struct Qvar), __FUNCTION__);
res->id = id;
return (res);
}
// Allocates a new variable in the given function.
struct Qvar* qfunc_new_var(struct Qfunction *f) {
struct Qvar *res = qvar_make(f->vars.length);
llist_pushback(&f->vars, res);
return (res);
}
static struct Qvar* qcg_dfs(struct ASTnode *x, struct Qfunction *f, struct Qblock *b) {
switch (x->op) {
case A_RETURN: {
struct ASTunnode *t = (void*)x;
struct Qvar *value = qcg_dfs(t->left, f, b);
qblock_add_ins(b, quad_make(Q_RET, NULL, value, NULL));
b->is_complete = true;
return (NULL);
}
case A_LIT_I32: {
struct ASTi32node *t = (void*)x;
struct Qvar *res = qfunc_new_var(f);
qblock_add_ins(b, quad_make_i32(res, t->val));
return (res);
}
case A_BLOCK: {
struct ASTblocknode *t = (void*)x;
struct llist_node *p = t->st.head;
while (p) {
qcg_dfs((struct ASTnode*)p, f, b);
p = p->nxt;
}
return (NULL);
}
default: {
fail_ast_op(x->op, __FUNCTION__);
}
}
}
// Generates Quad Repersentation from an AST
struct Qfunction* qfunc_cgenerate(struct Afunction *afunc) {
struct Qfunction *self = malloc_or_fail(sizeof(struct Qfunction), __FUNCTION__);
self->name = afunc->name; // transfer ownership of function name string
afunc->name = NULL; // prevents the pointer being freed when freeing the Afunction
llist_init(&self->bs);
llist_init(&self->vars);
struct Qblock *entry = qfunc_new_block(self);
qcg_dfs(afunc->rt, self, entry);
return (self);
}
// Frees a Qblock and all its components.
void qblock_free(struct Qblock *b) {
llist_free(&b->ins);
free(b);
}
// Frees a Qfunction and all its components.
void qfunc_free(struct Qfunction *f) {
if (f->name) {
free(f->name);
}
struct llist_node *p = f->bs.head, *nxt;
while (p) {
nxt = p->nxt;
qblock_free((struct Qblock*)p);
p = nxt;
}
llist_free(&f->vars);
free(f);
}
// Prints the given instruction for debugging.
static void quad_debug_print(struct Quad *self, FILE *Outfile) {
switch(self->op) {
case Q_IMM_I32: {
fprintf(Outfile, "\t$%d = i32 %d;\n", self->dest->id, self->val_i32);
} break;
case Q_RET: {
fprintf(Outfile, "\tret $%d.\n", self->left->id);
} break;
default: {
fail_quad_op(self->op, __FUNCTION__);
}
}
}
// Prints the contents of a Qblock for debugging.
static void qblock_debug_print(struct Qblock *self, FILE *Outfile) {
fprintf(Outfile, "L%d:\n", self->id);
struct llist_node *p = self->ins.head;
while (p) {
quad_debug_print((struct Quad*)p, Outfile);
p = p->nxt;
}
}
// Prints the contents of a Qfunction for debugging.
void qfunc_debug_print(struct Qfunction *self, FILE *Outfile) {
fprintf(Outfile, "%s:\n", self->name);
struct llist_node *p = self->bs.head;
while (p) {
qblock_debug_print((struct Qblock*)p, Outfile);
p = p->nxt;
}
}

View File

@ -1,19 +1,23 @@
#include <string.h>
#include "util/misc.h"
#include "fatals.h"
#include "target.h"
// Parse the target string
int target_parse(const char *target_string) {
static const char *target_map_k[] = {
"ast",
"quad",
NULL
};
static const int target_map_v[] = {
TARGET_AST,
TARGET_QUAD,
};
for (int i = 0; target_map_k[i]; ++i) {
if (strcmp(target_map_k[i], target_string) == 0) {
if (strequal(target_map_k[i], target_string)) {
return (target_map_v[i]);
}
}

View File

@ -2,13 +2,14 @@
#include <stdlib.h>
#include "util/misc.h"
// check if two string are the same
// This function does what you think it does :)
// Returns whether the given strings are the same.
bool strequal(const char *s1, const char *s2) {
return (strcmp(s1, s2) == 0);
}
// A impl of C23 strdup()
// Clones the given string
// A impl of C23 strdup().
// Clones the given string and returns a pointer.
char* strclone(const char *s) {
int n = strlen(s);
char *res = malloc(n + 1);
@ -16,4 +17,3 @@ char* strclone(const char *s) {
res[n] = '\0';
return res;
}