From 376c7c3eeb71a4553fe8fef62be9f651072f017e Mon Sep 17 00:00:00 2001 From: szdytom Date: Wed, 14 Jun 2023 21:15:34 +0800 Subject: [PATCH] replace quad by ssa ir Signed-off-by: szdytom --- .gitignore | 6 +- include/acir.h | 116 ++++++++++++++++-- include/ast.h | 4 + include/fatals.h | 3 +- include/parse.h | 6 - include/quad.h | 101 ---------------- include/target.h | 2 +- include/util/misc.h | 1 + main.c | 13 +- src/acir.c | 264 +++++++++++++++++++++++++++++++++++++++++ src/ast.c | 4 +- src/fatals.c | 21 +--- src/parse.c | 282 +++++++++++++++++++++++--------------------- src/quad.c | 240 ------------------------------------- src/scan.c | 2 +- src/symbol.c | 123 ------------------- src/target.c | 6 +- src/util/linklist.c | 4 +- src/util/misc.c | 11 ++ xmake.lua | 2 +- 20 files changed, 558 insertions(+), 653 deletions(-) delete mode 100644 include/parse.h delete mode 100644 include/quad.h create mode 100644 src/acir.c delete mode 100644 src/quad.c delete mode 100644 src/symbol.c diff --git a/.gitignore b/.gitignore index 416f3c6..f7432d4 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,8 @@ out.txt # builder .xmake/ -build/ \ No newline at end of file +.cache/ +build/ + +# Language Server +compile_commands.json diff --git a/include/acir.h b/include/acir.h index 61772c2..1105ec7 100644 --- a/include/acir.h +++ b/include/acir.h @@ -1,26 +1,118 @@ #ifndef ACC_ACIR_H #define ACC_ACIR_H -// operations in the ACC IR(ACIR) +#include +#include +#include "ast.h" +#include "util/linklist.h" + +// Operation code definations in the ACC IR(ACIR). enum { - IR_ADD, IR_SUB, IR_MUL, IR_DIV, - IR_EQ, IR_NE, IR_LT, IR_GT, IR_LE, IR_GE, - IR_NEG, - IR_LIT32, IR_LIT64, + // Loads + IR_IMM_I32, // immediate integer (32bits) + + // SSA + IR_PHI, // phi node in SSA + + // Arithmetic operations + IR_NEG, // negation + IR_NOT, // bitwise not + IR_CMP_EQ, // compare whether equal + + // Terminates + IR_RET, // return + IR_JMP, // jump: always goto true branch + IR_BR, // conditional jump + + // Guard + IR_NULL, }; -struct IRblock { - -} +// Argument of phi function. +struct IRphi_arg { + struct llist_node n; // linklist header + struct IRblock *source; // predecessor basic block + struct IRinstruction *value; // corresponding value when comming from the block +}; +// IR instruction. +// The result is represented as itself. struct IRinstruction { - int op; + struct llist_node n; // linklist header + int op; // operation code + int id; // value identifier + struct IRblock *owner; // the basic block containing this instruction union { - struct { int left, right; }; - int val_i32; - int val_i64; + struct { struct IRinstruction *left, *right; }; // left/right operands for calculations + struct { struct IRinstruction *cond; // jump condition + struct IRblock *bt, *bf; }; // true branch & false branch for conditional jump + struct linklist phi; // Phi instruction argument list + int32_t val_i32; // immediate: integer (32bits) }; }; +// IR basic block +struct IRblock { + struct llist_node n; // linklist header + int id; // block identifier, the function entry block will have value 0 + struct linklist ins; // contained instructions + bool is_complete; // whether the block is properly ended with a terminate + struct linklist pre; // predecessor list of this basic block + struct IRfunction *owner; // the function containing this function +}; + +// Function containing IR instructions. +// TODO: paramaters +struct IRfunction { + struct llist_node n; // linklist header + char *name; // function name + struct linklist bs; // basic blocks + int ins_count; // number of instructions, used for allocating instruction identifier. +}; + +// Constructs an IRinstruction with an operator, and two operands. +struct IRinstruction* IRinstruction_new(struct IRblock *owner, int op, + struct IRinstruction *left, struct IRinstruction *right); + +// Constructs a IRinstruction with an integer immediate (32bits). +struct IRinstruction* IRinstruction_new_i32(struct IRblock *owner, int32_t v); + +// Constructs a IRinstuction with instruction Q_JMP or Q_BR (which is conditional jump). +struct IRinstruction* IRinstruction_new_jmp(struct IRblock *owner, int op, struct IRinstruction *cond, + struct IRblock *bt, struct IRblock *bf); + +// Returns whether an IR opcode is a terminate. +// Terminate must and may only appear exactly once at ther end of each basic block. +bool IRis_terminate(int op); + +// Returns whether an IR opcode is a jump opcode. +bool IRis_jmp(int op); + +// Constructs a IRblock. +struct IRblock* IRblock_new(struct IRfunction *owner); + +// Allocating instruction identifiers in IRfunction +int IRfunction_alloc_ins(struct IRfunction *self); + +// Generates Quad Repersentation from an AST +struct IRfunction* IRfunction_from_ast(struct Afunction *afunc); + +// Frees a IRinstruction and all its components. +void IRinstruction_free(struct IRinstruction *self); + +// Frees a IRblock and all its components. +void IRblock_free(struct IRblock *self); + +// Frees a IRfunction and all its components. +void IRfunction_free(struct IRfunction *self); + +// Outputs the instruction. +void IRinstruction_print(struct IRinstruction *self, FILE *Outfile); + +// Outputs the containing instructions of the IRblock. +void IRblock_print(struct IRblock *self, FILE *Outfile); + +// Outputs the containing instructions of the IRfunction. +void IRfunction_print(struct IRfunction *self, FILE *Outfile); #endif diff --git a/include/ast.h b/include/ast.h index fa89932..151be9a 100644 --- a/include/ast.h +++ b/include/ast.h @@ -1,6 +1,7 @@ #ifndef ACC_AST_H #define ACC_AST_H +#include #include #include "util/linklist.h" @@ -114,5 +115,8 @@ void afunc_debug_print(FILE *Outfile, struct Afunction *f); void afunc_free(struct Afunction *f); void ast_free(struct ASTnode *x); +// Parse source into AST. +struct Afunction* Afunction_from_source(const char *filename); + #endif diff --git a/include/fatals.h b/include/fatals.h index 6021950..5e05f91 100644 --- a/include/fatals.h +++ b/include/fatals.h @@ -4,10 +4,9 @@ #include #include -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_ir_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); diff --git a/include/parse.h b/include/parse.h deleted file mode 100644 index b83ad25..0000000 --- a/include/parse.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef ACC_PARSE_H -#define ACC_PARSE_H - -struct Afunction* parse_source(const char *filename); - -#endif diff --git a/include/quad.h b/include/quad.h deleted file mode 100644 index 5e0e836..0000000 --- a/include/quad.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef ACC_QUAD_H -#define ACC_QUAD_H - -#include -#include -#include "ast.h" -#include "util/linklist.h" - -// Operation code definations -enum { - // Loading immediates - Q_IMM_I32, // integer (32bits) - - // Arithmetic operations - Q_NEG, // negation - Q_NOT, // bitwise not - Q_CMP_EQ, // compare whether equal - - // 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 diff --git a/include/target.h b/include/target.h index 4fc2761..bca3716 100644 --- a/include/target.h +++ b/include/target.h @@ -4,7 +4,7 @@ // Target types enum { TARGET_AST, - TARGET_QUAD, + TARGET_ACIR, TARGET_NULL, }; diff --git a/include/util/misc.h b/include/util/misc.h index 6987f4e..dd632a7 100644 --- a/include/util/misc.h +++ b/include/util/misc.h @@ -5,6 +5,7 @@ #define ACC_ARRAY_LENGTH(a) (sizeof((a))/sizeof(*(a))) +void* malloc_or_fail(size_t s, const char *func_name); bool strequal(const char *s1, const char *s2); char* strclone(const char *s); diff --git a/main.c b/main.c index fcee80f..5002a5f 100644 --- a/main.c +++ b/main.c @@ -2,10 +2,9 @@ #include #include #include "scan.h" -#include "parse.h" #include "ast.h" #include "target.h" -#include "quad.h" +#include "acir.h" // Print out a usage if started incorrectly static void usage(char *prog) { @@ -36,13 +35,13 @@ int main(int argc, char *argv[]) { } int target = target_parse(argv[1]); - struct Afunction *afunc = parse_source(argv[2]); + struct Afunction *afunc = Afunction_from_source(argv[2]); if (target == TARGET_AST) { afunc_debug_print(Outfile, afunc); - } else if (target == TARGET_QUAD) { - struct Qfunction *qfunc = qfunc_cgenerate(afunc); - qfunc_debug_print(qfunc, Outfile); - qfunc_free(qfunc); + } else if (target == TARGET_ACIR) { + struct IRfunction *ir = IRfunction_from_ast(afunc); + IRfunction_print(ir, Outfile); + IRfunction_free(ir); } afunc_free(afunc); return (0); diff --git a/src/acir.c b/src/acir.c new file mode 100644 index 0000000..8d7a14d --- /dev/null +++ b/src/acir.c @@ -0,0 +1,264 @@ +#include +#include "util/misc.h" +#include "fatals.h" +#include "acir.h" + +#define IRinstruction_constructor_shared_code \ + struct IRinstruction *self = malloc_or_fail(sizeof(struct IRinstruction), __FUNCTION__); \ + self->id = IRfunction_alloc_ins(owner->owner); \ + self->owner = owner; \ + IRblock_add_ins(owner, self); \ + +// Adds one instruction to list. +// Internal function only: IRinstruction_new_xxx() automaticly calls this function. +static void IRblock_add_ins(struct IRblock *self, struct IRinstruction *x) { + if (self->is_complete) { + return; + } + llist_pushback(&self->ins, x); +} + +// Constructs an IRinstruction with an operator, and two operands. +struct IRinstruction* IRinstruction_new(struct IRblock *owner, int op, + struct IRinstruction *left, struct IRinstruction *right) { + IRinstruction_constructor_shared_code + + self->op = op; + self->left = left; + self->right = right; + + if (IRis_terminate(self->op)) { + owner->is_complete = true; + } + return (self); +} + +// Constructs a IRinstruction with an integer immediate (32bits). +struct IRinstruction* IRinstruction_new_i32(struct IRblock *owner, int32_t v) { + IRinstruction_constructor_shared_code + + self->op = IR_IMM_I32; + self->val_i32 = v; + return (self); +} + +// Constructs a IRinstuction with instruction Q_JMP or Q_BR (which is conditional jump). +struct IRinstruction* IRinstruction_new_jmp(struct IRblock *owner, int op, struct IRinstruction *cond, + struct IRblock *bt, struct IRblock *bf) { + IRinstruction_constructor_shared_code + + self->op = op; + self->cond = cond; + self->bt = bt; + self->bf = bf; + owner->is_complete = true; + + if (bt) { + llist_pushback(&bt->pre, owner); + } + if (bf) { + llist_pushback(&bf->pre, owner); + } + return (self); +} + +// Returns whether an IR opcode is a terminate. +// Terminate must and may only appear exactly once at ther end of each basic block. +bool IRis_terminate(int op) { + switch (op) { + case IR_RET: case IR_BR: case IR_JMP: + return (true); + + default: + return (false); + } +} + +// Returns whether an IR opcode is a jump opcode. +bool IRis_jmp(int op) { + switch (op) { + case IR_JMP: case IR_BR: + return (true); + + default: + return (false); + } +} + +// Constructs a IRblock. +struct IRblock* IRblock_new(struct IRfunction *owner) { + struct IRblock *self = malloc_or_fail(sizeof(struct IRblock), __FUNCTION__); + + self->id = owner->bs.length; + self->owner = owner; + self->is_complete = false; + llist_init(&self->pre); + llist_init(&self->ins); + llist_pushback(&owner->bs, self); + return (self); +} + +// Allocating instruction identifiers in IRfunction +int IRfunction_alloc_ins(struct IRfunction *self) { + return self->ins_count++; +} + +// Translate an AST unary arithmetic opcode to a IR opcode. +static int translate_ast_unary_op(int op) { + switch (op) { + case A_NEG: return (IR_NEG); + case A_BNOT: return (IR_NOT); + default: fail_ir_op(op, __FUNCTION__); + } +} + +static struct IRinstruction* IRcg_dfs(struct ASTnode *x, struct IRfunction *f, struct IRblock *b) { + if (x == NULL) { + return (NULL); + } + + switch (x->op) { + case A_RETURN: { + struct ASTunnode *t = (void*)x; + struct IRinstruction *value = IRcg_dfs(t->left, f, b); + IRinstruction_new(b, IR_RET, value, NULL); + b->is_complete = true; + return (NULL); + } + + case A_BLOCK: { + struct ASTblocknode *t = (void*)x; + struct llist_node *p = t->st.head; + while (p) { + IRcg_dfs((struct ASTnode*)p, f, b); + p = p->nxt; + } + return (NULL); + } + + case A_LIT_I32: { + struct ASTi32node *t = (void*)x; + return (IRinstruction_new_i32(b, t->val)); + } + + case A_NEG: case A_BNOT: { + struct ASTunnode *t = (void*)x; + struct IRinstruction *value = IRcg_dfs(t->left, f, b); + return (IRinstruction_new(b, translate_ast_unary_op(x->op), value, NULL)); + } + + case A_LNOT: { + struct ASTunnode *t = (void*)x; + struct IRinstruction *value = IRcg_dfs(t->left, f, b), + *zero = IRinstruction_new_i32(b, 0); + return (IRinstruction_new(b, IR_CMP_EQ, value, zero)); + } + + default: { + fail_ast_op(x->op, __FUNCTION__); + } + } +} + +// Generates Quad Repersentation from an AST +struct IRfunction* IRfunction_from_ast(struct Afunction *afunc) { + struct IRfunction *self = malloc_or_fail(sizeof(struct IRfunction), __FUNCTION__); + + self->name = afunc->name; // transfer ownership of function name string + afunc->name = NULL; // prevents the pointer being freed when freeing the Afunction + + self->ins_count = 0; + llist_init(&self->bs); + + struct IRblock *entry = IRblock_new(self); + IRcg_dfs(afunc->rt, self, entry); + return (self); +} + +// Frees a IRinstruction and all its components. +void IRinstruction_free(struct IRinstruction *self) { + if (self->op == IR_PHI) { + llist_free(&self->phi); + } + free(self); +} + +// Frees a IRblock and all its components. +void IRblock_free(struct IRblock *self) { + struct llist_node *p = self->ins.head, *nxt; + while (p) { + nxt = p->nxt; + IRinstruction_free((void*)p); + p = nxt; + } + free(self); +} + +// Frees a IRfunction and all its components. +void IRfunction_free(struct IRfunction *self) { + if (self->name) { + free(self->name); + } + + struct llist_node *p = self->bs.head, *nxt; + while (p) { + nxt = p->nxt; + IRblock_free((void*)p); + p = nxt; + } + + free(self); +} + +// Outputs the instruction. +void IRinstruction_print(struct IRinstruction *self, FILE *Outfile) { + switch(self->op) { + case IR_IMM_I32: { + fprintf(Outfile, "\t$%d = i32 %d;\n", self->id, self->val_i32); + } break; + + case IR_RET: { + if (self->left) { + fprintf(Outfile, "\tret $%d.\n", self->left->id); + } else { + fputs("\tret.", Outfile); + } + } break; + + case IR_NEG: { + fprintf(Outfile, "\t$%d = neg $%d;\n", self->id, self->left->id); + } break; + + case IR_NOT: { + fprintf(Outfile, "\t$%d = not $%d;\n", self->id, self->left->id); + } break; + + case IR_CMP_EQ: { + fprintf(Outfile, "\t$%d = eq $%d, $%d;\n", self->id, self->left->id, self->right->id); + } break; + + default: { + fail_ir_op(self->op, __FUNCTION__); + } break; + } +} + +// Outputs the containing instructions of the IRblock. +void IRblock_print(struct IRblock *self, FILE *Outfile) { + fprintf(Outfile, "L%d:\n", self->id); + struct llist_node *p = self->ins.head; + while (p) { + IRinstruction_print((void*)p, Outfile); + p = p->nxt; + } +} + +// Outputs the containing instructions of the IRfunction. +void IRfunction_print(struct IRfunction *self, FILE *Outfile) { + fprintf(Outfile, "%s:\n", self->name); + struct llist_node *p = self->bs.head; + while (p) { + IRblock_print((void*)p, Outfile); + p = p->nxt; + } +} diff --git a/src/ast.c b/src/ast.c index b98097a..5729c1b 100644 --- a/src/ast.c +++ b/src/ast.c @@ -2,6 +2,7 @@ #include #include "ast.h" #include "fatals.h" +#include "util/misc.h" #include "util/linklist.h" const char *ast_opname[] = { @@ -105,6 +106,7 @@ static void ast_print_dfs(FILE* Outfile, struct ASTnode *x, int tabs) { } switch(x->op) { + case A_LNOT: case A_BNOT: case A_NEG: case A_RETURN: case A_PRINT: { struct ASTunnode *t = (struct ASTunnode*)x; fprintf(Outfile, "--->UNOP(%s)\n", ast_opname[x->op]); @@ -179,7 +181,7 @@ void ast_free(struct ASTnode *x) { switch (x->op) { case A_IF: { ast_free(((struct ASTifnode*)x)->cond); - } [[fallthrough]]; + } // fall through case A_ASSIGN: case A_ADD: case A_SUB: case A_MUL: case A_DIV: diff --git a/src/fatals.c b/src/fatals.c index d9e1177..e096a4b 100644 --- a/src/fatals.c +++ b/src/fatals.c @@ -2,7 +2,7 @@ #include #include "scan.h" #include "ast.h" -#include "quad.h" +#include "acir.h" void fail_target(const char *target_name) { fprintf(stderr, "unknown target: %s.\n", target_name); @@ -14,22 +14,13 @@ void fail_malloc(const char *func_name) { exit(1); } -void* malloc_or_fail(size_t s, const char *func_name) { - void *res = malloc(s); - if (res == NULL) { - fail_malloc(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); +void fail_ir_op(int op, const char *func_name) { + if (op < IR_NULL) { + fprintf(stderr, "%s: unsupported IR operator %s.\n", func_name, ast_opname[op]); } else { - fprintf(stderr, "%s: unknown Quad operator %d.\n", func_name, op); - exit(1); + fprintf(stderr, "%s: unknown IR operator %d.\n", func_name, op); } + exit(1); } void fail_ast_op(int op, const char *func_name) { diff --git a/src/parse.c b/src/parse.c index e42cd09..6e4a792 100644 --- a/src/parse.c +++ b/src/parse.c @@ -7,9 +7,13 @@ #include "ast.h" #include "fatals.h" -static struct linklist Tokens; // current token for parsing +// Parsing Context +struct Pcontext { + struct linklist tokens; // token list + struct llist_node *cur; // current token +}; -// Check that we have a binary operator and return its precedence. +// Checks that we have a binary operator and return its precedence. // Operators with larger precedence value will be evaluated first. static int op_precedence(struct token *t) { switch (t->type) { @@ -86,76 +90,70 @@ static bool direction_rtl(int t) { } // Next token -static void next(void) { - if (Tokens.head) { - token_free(llist_popfront(&Tokens)); +static void next(struct Pcontext *ctx) { + if (ctx->cur) { + ctx->cur = ctx->cur->nxt; } } -// preview next kth token from input stream -static struct token* preview(int k) { - if (Tokens.length <= k) { - static struct token token_eof = { - .type = T_EOF - }; - return (&token_eof); - } - - struct token* res = llist_get(&Tokens, k); - return (res); -} - // return current token from input stream -static struct token* current(void) { - return (preview(0)); +static struct token* current(struct Pcontext *ctx) { + static struct token token_eof = { + .type = T_EOF + }; + + if (ctx->cur) { + return ((void*)ctx->cur); + } + return (&token_eof); } // match a token or report syntax error -static void match(int t) { - if (current()->type == t) { - next(); +static void match(struct Pcontext *ctx, int t) { + if (current(ctx)->type == t) { + next(ctx); } else { - fail_ce_expect(current()->line, token_typename[t], token_typename[current()->type]); + fail_ce_expect(current(ctx)->line, token_typename[t], token_typename[current(ctx)->type]); } } // check current token's type or report syntax error. -static void expect(int t) { - if (current()->type != t) { - fail_ce_expect(current()->line, token_typename[t], token_typename[current()->type]); +static void expect(struct Pcontext *ctx, int t) { + if (current(ctx)->type != t) { + fail_ce_expect(current(ctx)->line, token_typename[t], token_typename[current(ctx)->type]); } } -static struct ASTnode* statement(void); -static struct ASTnode* expression(void); +static struct ASTnode* statement(struct Pcontext *ctx); +static struct ASTnode* expression(struct Pcontext *ctx); // Parse a primary factor and return an // AST node representing it. -static struct ASTnode* primary(void) { +static struct ASTnode* primary(struct Pcontext *ctx) { struct ASTnode *res; - struct token *t = current(); + struct token *t = current(ctx); if (t->type == T_LP) { // ( expr ) considered as primary - next(); - res = expression(); - match(T_RP); + next(ctx); + res = expression(ctx); + match(ctx, T_RP); } else if (t->type == T_I32_LIT) { res = ast_make_lit_i32(t->val_i32); - next(); + next(ctx); } else if (t->type == T_I64_LIT) { - res = ast_make_lit_i64(current()->val_i64); - next(); + res = ast_make_lit_i64(current(ctx)->val_i64); + next(ctx); } else if (t->type == T_ID) { // TODO: identifier. fail_ce(t->line, "got an identifier"); /* - int id = findglob((char*)current()->val); + int id = findglob((char*)current(ctx)->val); if (id == -1) { - fprintf(stderr, "syntax error on line %d: unknown indentifier %s.\n", Line, (char*)current()->val); + fprintf(stderr, "syntax error on line %d: unknown indentifier %s.\n", Line, (char*)current(ctx)->val); exit(1); } - next(); + next(ctx); return (ast_make_var(id)); */ } else { @@ -176,16 +174,16 @@ static bool is_prefix_op(int op) { } // Parses a primary expression with prefixes, e.g. ~10 -static struct ASTnode* prefixed_primary(void) { - struct token *t = current(); +static struct ASTnode* prefixed_primary(struct Pcontext *ctx) { + struct token *t = current(ctx); if (is_prefix_op(t->type)) { - next(); - struct ASTnode *child = prefixed_primary(); + next(ctx); + struct ASTnode *child = prefixed_primary(ctx); return (ast_make_unary(unary_arithop(t), child)); } - return (primary()); + return (primary(ctx)); } // Returns whether the given token type can be a binary operator. @@ -204,28 +202,28 @@ static bool is_binop(int t) { } // Return an AST tree whose root is a binary operator -static struct ASTnode* binexpr(int precedence) { +static struct ASTnode* binexpr(struct Pcontext *ctx, int precedence) { struct ASTnode *left, *right; - left = prefixed_primary(); - struct token *op = current(); + left = prefixed_primary(ctx); + struct token *op = current(ctx); if (!is_binop(op->type)) { return (left); } int tp = op_precedence(op); while (tp > precedence) { - next(); + next(ctx); if (direction_rtl(op->type)) { - right = binexpr(precedence); + right = binexpr(ctx, precedence); left = ast_make_assign(binary_arithop(op), left, right); } else { - right = binexpr(tp); + right = binexpr(ctx, tp); left = ast_make_binary(binary_arithop(op), left, right); // join right into left } - op = current(); + op = current(ctx); if (!is_binop(op->type)) { return (left); } @@ -235,70 +233,70 @@ static struct ASTnode* binexpr(int precedence) { } // parse one block of code, e.g. { a; b; } -static struct ASTnode* block(void) { - match(T_LB); - if (current()->type == T_RB) { - next(); - return NULL; +static struct ASTnode* block(struct Pcontext *ctx) { + match(ctx, T_LB); + if (current(ctx)->type == T_RB) { + next(ctx); + return (NULL); } struct ASTblocknode* res = (struct ASTblocknode*)ast_make_block(); - while (current()->type != T_RB) { + while (current(ctx)->type != T_RB) { struct ASTnode *x; - x = statement(); + x = statement(ctx); llist_pushback_notnull(&res->st, x); - if (current()->type == T_EOF) { + if (current(ctx)->type == T_EOF) { break; } } - match(T_RB); + match(ctx, T_RB); return ((struct ASTnode*)res); } // parse an expression -static struct ASTnode* expression(void) { - if (current()->type == T_SEMI) { +static struct ASTnode* expression(struct Pcontext *ctx) { + if (current(ctx)->type == T_SEMI) { return (NULL); } - return (binexpr(0)); + return (binexpr(ctx, 0)); } // parse one print statement -static struct ASTnode* print_statement(void) { - match(T_PRINT); - struct ASTnode *res = ast_make_unary(A_PRINT, expression()); - match(T_SEMI); +static struct ASTnode* print_statement(struct Pcontext *ctx) { + match(ctx, T_PRINT); + struct ASTnode *res = ast_make_unary(A_PRINT, expression(ctx)); + match(ctx, T_SEMI); return (res); } /* // parse variable declaration statement static struct ASTnode* var_declaration(void) { - match(T_INT); - expect(T_IDENT); - if (findglob((char*)current()->val) != -1) { + match(ctx, T_INT); + expect(ctx, T_IDENT); + if (findglob((char*)current(ctx)->val) != -1) { fail_ce("variable declared twice."); } - addglob((char*)current()->val); - next(); - match(T_SEMI); + addglob((char*)current(ctx)->val); + next(ctx); + match(ctx, T_SEMI); return (NULL); } */ // parse an if statement -static struct ASTnode* if_statement(void) { - match(T_IF); // if - match(T_LP); // ( - struct ASTnode* cond = expression(); - match(T_RP); // ) - struct ASTnode* then = statement(); +static struct ASTnode* if_statement(struct Pcontext *ctx) { + match(ctx, T_IF); // if + match(ctx, T_LP); // ( + struct ASTnode* cond = expression(ctx); + match(ctx, T_RP); // ) + struct ASTnode* then = statement(ctx); struct ASTnode* else_then; - if (current()->type == T_ELSE) { - next(); // else - else_then = statement(); + if (current(ctx)->type == T_ELSE) { + next(ctx); // else + else_then = statement(ctx); } else { else_then = NULL; // empty block } @@ -306,39 +304,39 @@ static struct ASTnode* if_statement(void) { } // parse an while statement -static struct ASTnode* while_statement(void) { - match(T_WHILE); - match(T_LP); - struct ASTnode* cond = expression(); - match(T_RP); - struct ASTnode* body = statement(); +static struct ASTnode* while_statement(struct Pcontext *ctx) { + match(ctx, T_WHILE); + match(ctx, T_LP); + struct ASTnode* cond = expression(ctx); + match(ctx, T_RP); + struct ASTnode* body = statement(ctx); return (ast_make_binary(A_WHILE, cond, body)); } // parse a for statement (into a while loop) -static struct ASTnode* for_statement(void) { - match(T_FOR); - match(T_LP); - struct ASTnode *init = statement(); +static struct ASTnode* for_statement(struct Pcontext *ctx) { + match(ctx, T_FOR); + match(ctx, T_LP); + struct ASTnode *init = statement(ctx); struct ASTnode *cond; - if (current()->type != T_SEMI) { - cond = expression(); + if (current(ctx)->type != T_SEMI) { + cond = expression(ctx); } else { cond = ast_make_lit_i32(1); } - next(); // skip the ; + next(ctx); // skip the ; struct ASTnode *inc; - if (current()->type != T_RP) { - inc = expression(); + if (current(ctx)->type != T_RP) { + inc = expression(ctx); } else { inc = NULL; } - match(T_RP); - struct ASTnode *body = statement(); - struct ASTblocknode *container = (struct ASTblocknode*)ast_make_block(); + match(ctx, T_RP); + struct ASTnode *body = statement(ctx); + struct ASTblocknode *container = (void*)ast_make_block(); struct ASTnode *wbody; if (body == NULL && inc == NULL) { @@ -348,53 +346,53 @@ static struct ASTnode* for_statement(void) { } else if (inc == NULL) { wbody = body; } else { - struct ASTblocknode* wt = (struct ASTblocknode*)ast_make_block(); + struct ASTblocknode* wt = (void*)ast_make_block(); llist_pushback_notnull(&wt->st, body); llist_pushback_notnull(&wt->st, inc); - wbody = (struct ASTnode*)wt; + wbody = (void*)wt; } llist_pushback_notnull(&container->st, init); llist_pushback(&container->st, ast_make_binary(A_WHILE, cond, wbody)); - return ((struct ASTnode*)container); + return ((void*)container); } -static struct ASTnode* return_statement(void) { - match(T_RETURN); - struct ASTnode *res = expression(); - match(T_SEMI); +static struct ASTnode* return_statement(struct Pcontext *ctx) { + match(ctx, T_RETURN); + struct ASTnode *res = expression(ctx); + match(ctx, T_SEMI); return (ast_make_unary(A_RETURN, res)); } // parse one statement -static struct ASTnode* statement(void) { - switch (current()->type) { +static struct ASTnode* statement(struct Pcontext *ctx) { + switch (current(ctx)->type) { case T_LB: - return (block()); + return (block(ctx)); case T_SEMI: return (NULL); case T_PRINT: - return (print_statement()); + return (print_statement(ctx)); // case T_INT: // return (var_declaration()); case T_IF: - return (if_statement()); + return (if_statement(ctx)); case T_WHILE: - return (while_statement()); + return (while_statement(ctx)); case T_FOR: - return (for_statement()); + return (for_statement(ctx)); case T_RETURN: - return (return_statement()); + return (return_statement(ctx)); default: { - struct ASTnode* res = expression(); - match(T_SEMI); + struct ASTnode* res = expression(ctx); + match(ctx, T_SEMI); return (res); } } @@ -402,36 +400,46 @@ static struct ASTnode* statement(void) { // Parse one top-level function // Sets the func_name param. -static struct Afunction* function(void) { +static struct Afunction* function(struct Pcontext *ctx) { struct Afunction *res = afunc_make(); - match(T_INT); - expect(T_ID); - 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(ctx, T_INT); + expect(ctx, T_ID); + res->name = current(ctx)->val_s; // transfer ownership of the identifier string to caller + current(ctx)->val_s = NULL; // prevent it from being freed in token_free() called by next(ctx). + next(ctx); - match(T_LP); - if (current()->type == T_VOID) { - next(); + match(ctx, T_LP); + if (current(ctx)->type == T_VOID) { + next(ctx); goto END_PARAM_LIST; } // TODO: parameter list END_PARAM_LIST: - match(T_RP); - res->rt = block(); + match(ctx, T_RP); + res->rt = block(ctx); return (res); } -// Parse ans return the full ast -struct Afunction* parse_source(const char *filename) { - Tokens = scan_tokens(filename); - struct Afunction* res = function(); - - while (Tokens.length > 0) { - token_free(llist_popfront(&Tokens)); +// Frees a Pcontext and all its components. +static void Pcontext_free(struct Pcontext *ctx) { + struct llist_node *p = ctx->tokens.head, *nxt; + while (p) { + nxt = p->nxt; + token_free((void*)p); + p = nxt; } - llist_free(&Tokens); +} + +// Parse source into AST. +struct Afunction* Afunction_from_source(const char *filename) { + struct Pcontext ctx = { + .tokens = scan_tokens(filename), + }; + ctx.cur = ctx.tokens.head; + + struct Afunction* res = function(&ctx); + Pcontext_free(&ctx); return (res); } diff --git a/src/quad.c b/src/quad.c deleted file mode 100644 index 13a6008..0000000 --- a/src/quad.c +++ /dev/null @@ -1,240 +0,0 @@ -#include -#include -#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); -} - -// Translate an AST unary arithmetic opcode to a Quad opcode. -static int unary_arithop(int op) { - switch (op) { - case A_NEG: return (Q_NEG); - case A_BNOT: return (Q_NOT); - default: fail_quad_op(op, __FUNCTION__); - } -} - -static struct Qvar* qcg_dfs(struct ASTnode *x, struct Qfunction *f, struct Qblock *b) { - if (x == NULL) { - return (NULL); - } - - 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_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); - } - - case A_LIT_I32: { - struct Qvar *res = qfunc_new_var(f); - struct ASTi32node *t = (void*)x; - qblock_add_ins(b, quad_make_i32(res, t->val)); - return (res); - } - - case A_NEG: case A_BNOT: { - struct Qvar *res = qfunc_new_var(f); - struct ASTunnode *t = (void*)x; - struct Qvar *value = qcg_dfs(t->left, f, b); - qblock_add_ins(b, quad_make(unary_arithop(x->op), res, value, NULL)); - return (res); - } - - case A_LNOT: { - struct Qvar *res = qfunc_new_var(f); - struct ASTunnode *t = (void*)x; - struct Qvar *value = qcg_dfs(t->left, f, b); - struct Qvar *zero = qfunc_new_var(f); - qblock_add_ins(b, quad_make_i32(zero, 0)); - qblock_add_ins(b, quad_make(Q_CMP_EQ, res, value, zero)); - return (res); - } - - 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: { - if (self->left) { - fprintf(Outfile, "\tret $%d.\n", self->left->id); - } else { - fputs("\tret.", Outfile); - } - } break; - - case Q_NEG: { - fprintf(Outfile, "\t$%d = neg $%d;\n", self->dest->id, self->left->id); - } break; - - case Q_NOT: { - fprintf(Outfile, "\t$%d = not $%d;\n", self->dest->id, self->left->id); - } break; - - case Q_CMP_EQ: { - fprintf(Outfile, "\t$%d = eq $%d, $%d;\n", self->dest->id, self->left->id, self->right->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; - } -} diff --git a/src/scan.c b/src/scan.c index cca632d..e6ced2c 100644 --- a/src/scan.c +++ b/src/scan.c @@ -71,7 +71,7 @@ static char* scan_indentifier(int *n) { while (isdigit(c) || isalpha(c) || c == '_') { if (len >= sz - 1) { sz *= 2; - char *res = realloc(res, sz * sizeof(char)); + res = realloc(res, sz * sizeof(char)); if (res == NULL) { fail_malloc(__FUNCTION__); } diff --git a/src/symbol.c b/src/symbol.c deleted file mode 100644 index 2a8e572..0000000 --- a/src/symbol.c +++ /dev/null @@ -1,123 +0,0 @@ -/* -#include -#include -#include -#include "util/misc.h" -#include "util/array.h" - -#define INDENT_CHARS 63 // 10 digits + 26 * 2(cap) alpha + 1 _ - -static int indent_char_id(char c) { - if ('0' <= c && c <= '9') { - return (c - '0'); - } - if ('a' <= c && c <= 'z') { - return (c - 'a' + 10); - } - if ('A' <= c && c <= 'Z') { - return (c - 'A' + 36); - } - if (c == '_') { - return (62); - } - fprintf(stderr, "%c is not a acceptable char in indentifier.\n", c); - exit(1); -} - -struct trie_node { - struct trie_node *c[INDENT_CHARS]; // childs - int sz; // size - int val; // endpoint value, -1 for not ended -}; - -static struct trie_node *root; -struct array Gsym; // symbol id to name map - -static void trie_free(struct trie_node *p) { - for (int i = 0; i < INDENT_CHARS; ++i) { - if (p->c[i]) { - trie_free(p->c[i]); - } - } - free(p); -} - -static struct trie_node* trie_createnode(void) { - struct trie_node *x = malloc(sizeof(struct trie_node)); - if (x == NULL) { - fprintf(stderr, "%s: failed to malloc trie node in symbol table.\n", __FUNCTION__); - exit(1); - } - - memset(x, 0, sizeof(struct trie_node)); - x->val = -1; - return (x); -} - -static void trie_set(char *str, int val) { - struct trie_node *p = root; - - int n = strlen(str); - for (int i = 0; i < n; ++i) { - int x = indent_char_id(str[i]); - if (!p->c[x]) { - p->c[x] = trie_createnode(); - } - p->sz += 1; - p = p->c[x]; - } - p->val = val; -} - -static int trie_get(char *str) { - struct trie_node *p = root; - - int n = strlen(str); - for (int i = 0; i < n; ++i) { - int x = indent_char_id(str[i]); - if (!p->c[x]) { // not found - return (-1); - } - p = p->c[x]; - } - return (p->val); -} - -static int IsSymbolListLoaded = 0; - -// init global symbol table -void symbol_init(void) { - IsSymbolListLoaded = 1; - array_init(&Gsym); - root = trie_createnode(); -} - -// unload global symbol table -void symbol_unload(void) { - if (!IsSymbolListLoaded) { - return; - } - - for (int i = 0; i < Gsym.length; ++i) { - free(array_get(&Gsym, i)); - } - array_free(&Gsym); - trie_free(root); -} - -// Determine if the symbol s is in the global symbol table. -// Return its slot position or -1 if not found. -int findglob(char *s) { - return (trie_get(s)); -} - -// Add a global symbol to the symbol table. -// Return the slot number in the symbol table. -int addglob(char *s) { - char *ss = strclone(s); - array_pushback(&Gsym, ss); - int res = Gsym.length - 1; - trie_set(ss, res); - return (res); -} -*/ diff --git a/src/target.c b/src/target.c index 3e13b59..65790f9 100644 --- a/src/target.c +++ b/src/target.c @@ -6,14 +6,14 @@ // Parse the target string int target_parse(const char *target_string) { static const char *target_map_k[] = { - "ast", - "quad", + "_ast", + "_acir", NULL }; static const int target_map_v[] = { TARGET_AST, - TARGET_QUAD, + TARGET_ACIR, }; for (int i = 0; target_map_k[i]; ++i) { diff --git a/src/util/linklist.c b/src/util/linklist.c index 87ec4a1..15f5ce8 100644 --- a/src/util/linklist.c +++ b/src/util/linklist.c @@ -89,8 +89,8 @@ void llist_insert(struct linklist *l, int index, void *val) { p->nxt = x; } -// Pop the first element of the link list -// Return the first element. +// Pop the first element of the link list. +// Returns the first element. void* llist_popfront(struct linklist *l) { if (l->head == NULL) { return (NULL); diff --git a/src/util/misc.c b/src/util/misc.c index 991211a..d168687 100644 --- a/src/util/misc.c +++ b/src/util/misc.c @@ -1,7 +1,18 @@ #include #include +#include "fatals.h" #include "util/misc.h" +// This function tries to malloc some memory. +// Will call fail_malloc() in fatals.h when failing. +void* malloc_or_fail(size_t s, const char *func_name) { + void *res = malloc(s); + if (res == NULL) { + fail_malloc(func_name); + } + return (res); +} + // This function does what you think it does :) // Returns whether the given strings are the same. bool strequal(const char *s1, const char *s2) { diff --git a/xmake.lua b/xmake.lua index 048aff1..33ad612 100644 --- a/xmake.lua +++ b/xmake.lua @@ -15,7 +15,7 @@ target("build") add_includedirs("include/") if is_mode("release") then set_strip("all") - set_optimize("O2") + set_optimize("faster") end if is_mode("debug") then