replace quad by ssa ir

Signed-off-by: szdytom <szdytom@163.com>
This commit is contained in:
方而静 2023-06-14 21:15:34 +08:00
parent 003a5215a8
commit 376c7c3eeb
20 changed files with 558 additions and 653 deletions

4
.gitignore vendored
View File

@ -47,4 +47,8 @@ out.txt
# builder # builder
.xmake/ .xmake/
.cache/
build/ build/
# Language Server
compile_commands.json

View File

@ -1,26 +1,118 @@
#ifndef ACC_ACIR_H #ifndef ACC_ACIR_H
#define ACC_ACIR_H #define ACC_ACIR_H
// operations in the ACC IR(ACIR) #include <stdbool.h>
#include <stdint.h>
#include "ast.h"
#include "util/linklist.h"
// Operation code definations in the ACC IR(ACIR).
enum { enum {
IR_ADD, IR_SUB, IR_MUL, IR_DIV, // Loads
IR_EQ, IR_NE, IR_LT, IR_GT, IR_LE, IR_GE, IR_IMM_I32, // immediate integer (32bits)
IR_NEG,
IR_LIT32, IR_LIT64, // 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 { 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 { union {
struct { int left, right; }; struct { struct IRinstruction *left, *right; }; // left/right operands for calculations
int val_i32; struct { struct IRinstruction *cond; // jump condition
int val_i64; 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 #endif

View File

@ -1,6 +1,7 @@
#ifndef ACC_AST_H #ifndef ACC_AST_H
#define ACC_AST_H #define ACC_AST_H
#include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include "util/linklist.h" #include "util/linklist.h"
@ -114,5 +115,8 @@ void afunc_debug_print(FILE *Outfile, struct Afunction *f);
void afunc_free(struct Afunction *f); void afunc_free(struct Afunction *f);
void ast_free(struct ASTnode *x); void ast_free(struct ASTnode *x);
// Parse source into AST.
struct Afunction* Afunction_from_source(const char *filename);
#endif #endif

View File

@ -4,10 +4,9 @@
#include <stddef.h> #include <stddef.h>
#include <stdnoreturn.h> #include <stdnoreturn.h>
void* malloc_or_fail(size_t s, const char *func_name);
noreturn void fail_target(const char *target_name); noreturn void fail_target(const char *target_name);
noreturn void fail_malloc(const char *func_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_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_expect(int line, const char *expected, const char *got);
noreturn void fail_ce(int line, const char *reason); noreturn void fail_ce(int line, const char *reason);

View File

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

View File

@ -1,101 +0,0 @@
#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)
// 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

View File

@ -4,7 +4,7 @@
// Target types // Target types
enum { enum {
TARGET_AST, TARGET_AST,
TARGET_QUAD, TARGET_ACIR,
TARGET_NULL, TARGET_NULL,
}; };

View File

@ -5,6 +5,7 @@
#define ACC_ARRAY_LENGTH(a) (sizeof((a))/sizeof(*(a))) #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); bool strequal(const char *s1, const char *s2);
char* strclone(const char *s); char* strclone(const char *s);

13
main.c
View File

@ -2,10 +2,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "scan.h" #include "scan.h"
#include "parse.h"
#include "ast.h" #include "ast.h"
#include "target.h" #include "target.h"
#include "quad.h" #include "acir.h"
// Print out a usage if started incorrectly // Print out a usage if started incorrectly
static void usage(char *prog) { static void usage(char *prog) {
@ -36,13 +35,13 @@ int main(int argc, char *argv[]) {
} }
int target = target_parse(argv[1]); 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) { if (target == TARGET_AST) {
afunc_debug_print(Outfile, afunc); afunc_debug_print(Outfile, afunc);
} else if (target == TARGET_QUAD) { } else if (target == TARGET_ACIR) {
struct Qfunction *qfunc = qfunc_cgenerate(afunc); struct IRfunction *ir = IRfunction_from_ast(afunc);
qfunc_debug_print(qfunc, Outfile); IRfunction_print(ir, Outfile);
qfunc_free(qfunc); IRfunction_free(ir);
} }
afunc_free(afunc); afunc_free(afunc);
return (0); return (0);

264
src/acir.c Normal file
View File

@ -0,0 +1,264 @@
#include <stdlib.h>
#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;
}
}

View File

@ -2,6 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "ast.h" #include "ast.h"
#include "fatals.h" #include "fatals.h"
#include "util/misc.h"
#include "util/linklist.h" #include "util/linklist.h"
const char *ast_opname[] = { const char *ast_opname[] = {
@ -105,6 +106,7 @@ static void ast_print_dfs(FILE* Outfile, struct ASTnode *x, int tabs) {
} }
switch(x->op) { switch(x->op) {
case A_LNOT: case A_BNOT: case A_NEG:
case A_RETURN: case A_PRINT: { case A_RETURN: case A_PRINT: {
struct ASTunnode *t = (struct ASTunnode*)x; struct ASTunnode *t = (struct ASTunnode*)x;
fprintf(Outfile, "--->UNOP(%s)\n", ast_opname[x->op]); fprintf(Outfile, "--->UNOP(%s)\n", ast_opname[x->op]);
@ -179,7 +181,7 @@ void ast_free(struct ASTnode *x) {
switch (x->op) { switch (x->op) {
case A_IF: { case A_IF: {
ast_free(((struct ASTifnode*)x)->cond); ast_free(((struct ASTifnode*)x)->cond);
} [[fallthrough]]; } // fall through
case A_ASSIGN: case A_ASSIGN:
case A_ADD: case A_SUB: case A_MUL: case A_DIV: case A_ADD: case A_SUB: case A_MUL: case A_DIV:

View File

@ -2,7 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "scan.h" #include "scan.h"
#include "ast.h" #include "ast.h"
#include "quad.h" #include "acir.h"
void fail_target(const char *target_name) { void fail_target(const char *target_name) {
fprintf(stderr, "unknown target: %s.\n", target_name); fprintf(stderr, "unknown target: %s.\n", target_name);
@ -14,22 +14,13 @@ void fail_malloc(const char *func_name) {
exit(1); exit(1);
} }
void* malloc_or_fail(size_t s, const char *func_name) { void fail_ir_op(int op, const char *func_name) {
void *res = malloc(s); if (op < IR_NULL) {
if (res == NULL) { fprintf(stderr, "%s: unsupported IR operator %s.\n", func_name, ast_opname[op]);
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);
} else { } else {
fprintf(stderr, "%s: unknown Quad operator %d.\n", func_name, op); fprintf(stderr, "%s: unknown IR operator %d.\n", func_name, op);
exit(1);
} }
exit(1);
} }
void fail_ast_op(int op, const char *func_name) { void fail_ast_op(int op, const char *func_name) {

View File

@ -7,9 +7,13 @@
#include "ast.h" #include "ast.h"
#include "fatals.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. // Operators with larger precedence value will be evaluated first.
static int op_precedence(struct token *t) { static int op_precedence(struct token *t) {
switch (t->type) { switch (t->type) {
@ -86,76 +90,70 @@ static bool direction_rtl(int t) {
} }
// Next token // Next token
static void next(void) { static void next(struct Pcontext *ctx) {
if (Tokens.head) { if (ctx->cur) {
token_free(llist_popfront(&Tokens)); 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 // return current token from input stream
static struct token* current(void) { static struct token* current(struct Pcontext *ctx) {
return (preview(0)); 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 // match a token or report syntax error
static void match(int t) { static void match(struct Pcontext *ctx, int t) {
if (current()->type == t) { if (current(ctx)->type == t) {
next(); next(ctx);
} else { } 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. // check current token's type or report syntax error.
static void expect(int t) { static void expect(struct Pcontext *ctx, int t) {
if (current()->type != t) { if (current(ctx)->type != t) {
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]);
} }
} }
static struct ASTnode* statement(void); static struct ASTnode* statement(struct Pcontext *ctx);
static struct ASTnode* expression(void); static struct ASTnode* expression(struct Pcontext *ctx);
// Parse a primary factor and return an // Parse a primary factor and return an
// AST node representing it. // AST node representing it.
static struct ASTnode* primary(void) { static struct ASTnode* primary(struct Pcontext *ctx) {
struct ASTnode *res; struct ASTnode *res;
struct token *t = current(); struct token *t = current(ctx);
if (t->type == T_LP) { if (t->type == T_LP) {
// ( expr ) considered as primary // ( expr ) considered as primary
next(); next(ctx);
res = expression(); res = expression(ctx);
match(T_RP); match(ctx, T_RP);
} else if (t->type == T_I32_LIT) { } else if (t->type == T_I32_LIT) {
res = ast_make_lit_i32(t->val_i32); res = ast_make_lit_i32(t->val_i32);
next(); next(ctx);
} else if (t->type == T_I64_LIT) { } else if (t->type == T_I64_LIT) {
res = ast_make_lit_i64(current()->val_i64); res = ast_make_lit_i64(current(ctx)->val_i64);
next(); next(ctx);
} else if (t->type == T_ID) { } else if (t->type == T_ID) {
// TODO: identifier. // TODO: identifier.
fail_ce(t->line, "got an identifier"); fail_ce(t->line, "got an identifier");
/* /*
int id = findglob((char*)current()->val); int id = findglob((char*)current(ctx)->val);
if (id == -1) { 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); exit(1);
} }
next(); next(ctx);
return (ast_make_var(id)); return (ast_make_var(id));
*/ */
} else { } else {
@ -176,16 +174,16 @@ static bool is_prefix_op(int op) {
} }
// Parses a primary expression with prefixes, e.g. ~10 // Parses a primary expression with prefixes, e.g. ~10
static struct ASTnode* prefixed_primary(void) { static struct ASTnode* prefixed_primary(struct Pcontext *ctx) {
struct token *t = current(); struct token *t = current(ctx);
if (is_prefix_op(t->type)) { if (is_prefix_op(t->type)) {
next(); next(ctx);
struct ASTnode *child = prefixed_primary(); struct ASTnode *child = prefixed_primary(ctx);
return (ast_make_unary(unary_arithop(t), child)); return (ast_make_unary(unary_arithop(t), child));
} }
return (primary()); return (primary(ctx));
} }
// Returns whether the given token type can be a binary operator. // 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 // 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; struct ASTnode *left, *right;
left = prefixed_primary(); left = prefixed_primary(ctx);
struct token *op = current(); struct token *op = current(ctx);
if (!is_binop(op->type)) { if (!is_binop(op->type)) {
return (left); return (left);
} }
int tp = op_precedence(op); int tp = op_precedence(op);
while (tp > precedence) { while (tp > precedence) {
next(); next(ctx);
if (direction_rtl(op->type)) { if (direction_rtl(op->type)) {
right = binexpr(precedence); right = binexpr(ctx, precedence);
left = ast_make_assign(binary_arithop(op), left, right); left = ast_make_assign(binary_arithop(op), left, right);
} else { } else {
right = binexpr(tp); right = binexpr(ctx, tp);
left = ast_make_binary(binary_arithop(op), left, right); // join right into left left = ast_make_binary(binary_arithop(op), left, right); // join right into left
} }
op = current(); op = current(ctx);
if (!is_binop(op->type)) { if (!is_binop(op->type)) {
return (left); return (left);
} }
@ -235,70 +233,70 @@ static struct ASTnode* binexpr(int precedence) {
} }
// parse one block of code, e.g. { a; b; } // parse one block of code, e.g. { a; b; }
static struct ASTnode* block(void) { static struct ASTnode* block(struct Pcontext *ctx) {
match(T_LB); match(ctx, T_LB);
if (current()->type == T_RB) { if (current(ctx)->type == T_RB) {
next(); next(ctx);
return NULL; return (NULL);
} }
struct ASTblocknode* res = (struct ASTblocknode*)ast_make_block(); struct ASTblocknode* res = (struct ASTblocknode*)ast_make_block();
while (current()->type != T_RB) { while (current(ctx)->type != T_RB) {
struct ASTnode *x; struct ASTnode *x;
x = statement(); x = statement(ctx);
llist_pushback_notnull(&res->st, x); llist_pushback_notnull(&res->st, x);
if (current()->type == T_EOF) { if (current(ctx)->type == T_EOF) {
break; break;
} }
} }
match(T_RB); match(ctx, T_RB);
return ((struct ASTnode*)res); return ((struct ASTnode*)res);
} }
// parse an expression // parse an expression
static struct ASTnode* expression(void) { static struct ASTnode* expression(struct Pcontext *ctx) {
if (current()->type == T_SEMI) { if (current(ctx)->type == T_SEMI) {
return (NULL); return (NULL);
} }
return (binexpr(0)); return (binexpr(ctx, 0));
} }
// parse one print statement // parse one print statement
static struct ASTnode* print_statement(void) { static struct ASTnode* print_statement(struct Pcontext *ctx) {
match(T_PRINT); match(ctx, T_PRINT);
struct ASTnode *res = ast_make_unary(A_PRINT, expression()); struct ASTnode *res = ast_make_unary(A_PRINT, expression(ctx));
match(T_SEMI); match(ctx, T_SEMI);
return (res); return (res);
} }
/* /*
// parse variable declaration statement // parse variable declaration statement
static struct ASTnode* var_declaration(void) { static struct ASTnode* var_declaration(void) {
match(T_INT); match(ctx, T_INT);
expect(T_IDENT); expect(ctx, T_IDENT);
if (findglob((char*)current()->val) != -1) { if (findglob((char*)current(ctx)->val) != -1) {
fail_ce("variable declared twice."); fail_ce("variable declared twice.");
} }
addglob((char*)current()->val); addglob((char*)current(ctx)->val);
next(); next(ctx);
match(T_SEMI); match(ctx, T_SEMI);
return (NULL); return (NULL);
} }
*/ */
// parse an if statement // parse an if statement
static struct ASTnode* if_statement(void) { static struct ASTnode* if_statement(struct Pcontext *ctx) {
match(T_IF); // if match(ctx, T_IF); // if
match(T_LP); // ( match(ctx, T_LP); // (
struct ASTnode* cond = expression(); struct ASTnode* cond = expression(ctx);
match(T_RP); // ) match(ctx, T_RP); // )
struct ASTnode* then = statement(); struct ASTnode* then = statement(ctx);
struct ASTnode* else_then; struct ASTnode* else_then;
if (current()->type == T_ELSE) { if (current(ctx)->type == T_ELSE) {
next(); // else next(ctx); // else
else_then = statement(); else_then = statement(ctx);
} else { } else {
else_then = NULL; // empty block else_then = NULL; // empty block
} }
@ -306,39 +304,39 @@ static struct ASTnode* if_statement(void) {
} }
// parse an while statement // parse an while statement
static struct ASTnode* while_statement(void) { static struct ASTnode* while_statement(struct Pcontext *ctx) {
match(T_WHILE); match(ctx, T_WHILE);
match(T_LP); match(ctx, T_LP);
struct ASTnode* cond = expression(); struct ASTnode* cond = expression(ctx);
match(T_RP); match(ctx, T_RP);
struct ASTnode* body = statement(); struct ASTnode* body = statement(ctx);
return (ast_make_binary(A_WHILE, cond, body)); return (ast_make_binary(A_WHILE, cond, body));
} }
// parse a for statement (into a while loop) // parse a for statement (into a while loop)
static struct ASTnode* for_statement(void) { static struct ASTnode* for_statement(struct Pcontext *ctx) {
match(T_FOR); match(ctx, T_FOR);
match(T_LP); match(ctx, T_LP);
struct ASTnode *init = statement(); struct ASTnode *init = statement(ctx);
struct ASTnode *cond; struct ASTnode *cond;
if (current()->type != T_SEMI) { if (current(ctx)->type != T_SEMI) {
cond = expression(); cond = expression(ctx);
} else { } else {
cond = ast_make_lit_i32(1); cond = ast_make_lit_i32(1);
} }
next(); // skip the ; next(ctx); // skip the ;
struct ASTnode *inc; struct ASTnode *inc;
if (current()->type != T_RP) { if (current(ctx)->type != T_RP) {
inc = expression(); inc = expression(ctx);
} else { } else {
inc = NULL; inc = NULL;
} }
match(T_RP); match(ctx, T_RP);
struct ASTnode *body = statement(); struct ASTnode *body = statement(ctx);
struct ASTblocknode *container = (struct ASTblocknode*)ast_make_block(); struct ASTblocknode *container = (void*)ast_make_block();
struct ASTnode *wbody; struct ASTnode *wbody;
if (body == NULL && inc == NULL) { if (body == NULL && inc == NULL) {
@ -348,53 +346,53 @@ static struct ASTnode* for_statement(void) {
} else if (inc == NULL) { } else if (inc == NULL) {
wbody = body; wbody = body;
} else { } 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, body);
llist_pushback_notnull(&wt->st, inc); llist_pushback_notnull(&wt->st, inc);
wbody = (struct ASTnode*)wt; wbody = (void*)wt;
} }
llist_pushback_notnull(&container->st, init); llist_pushback_notnull(&container->st, init);
llist_pushback(&container->st, ast_make_binary(A_WHILE, cond, wbody)); llist_pushback(&container->st, ast_make_binary(A_WHILE, cond, wbody));
return ((struct ASTnode*)container); return ((void*)container);
} }
static struct ASTnode* return_statement(void) { static struct ASTnode* return_statement(struct Pcontext *ctx) {
match(T_RETURN); match(ctx, T_RETURN);
struct ASTnode *res = expression(); struct ASTnode *res = expression(ctx);
match(T_SEMI); match(ctx, T_SEMI);
return (ast_make_unary(A_RETURN, res)); return (ast_make_unary(A_RETURN, res));
} }
// parse one statement // parse one statement
static struct ASTnode* statement(void) { static struct ASTnode* statement(struct Pcontext *ctx) {
switch (current()->type) { switch (current(ctx)->type) {
case T_LB: case T_LB:
return (block()); return (block(ctx));
case T_SEMI: case T_SEMI:
return (NULL); return (NULL);
case T_PRINT: case T_PRINT:
return (print_statement()); return (print_statement(ctx));
// case T_INT: // case T_INT:
// return (var_declaration()); // return (var_declaration());
case T_IF: case T_IF:
return (if_statement()); return (if_statement(ctx));
case T_WHILE: case T_WHILE:
return (while_statement()); return (while_statement(ctx));
case T_FOR: case T_FOR:
return (for_statement()); return (for_statement(ctx));
case T_RETURN: case T_RETURN:
return (return_statement()); return (return_statement(ctx));
default: { default: {
struct ASTnode* res = expression(); struct ASTnode* res = expression(ctx);
match(T_SEMI); match(ctx, T_SEMI);
return (res); return (res);
} }
} }
@ -402,36 +400,46 @@ static struct ASTnode* statement(void) {
// Parse one top-level function // Parse one top-level function
// Sets the func_name param. // Sets the func_name param.
static struct Afunction* function(void) { static struct Afunction* function(struct Pcontext *ctx) {
struct Afunction *res = afunc_make(); struct Afunction *res = afunc_make();
match(T_INT); match(ctx, T_INT);
expect(T_ID); expect(ctx, T_ID);
res->name = current()->val_s; // transfer ownership of the identifier string to caller res->name = current(ctx)->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(). current(ctx)->val_s = NULL; // prevent it from being freed in token_free() called by next(ctx).
next(); next(ctx);
match(T_LP); match(ctx, T_LP);
if (current()->type == T_VOID) { if (current(ctx)->type == T_VOID) {
next(); next(ctx);
goto END_PARAM_LIST; goto END_PARAM_LIST;
} }
// TODO: parameter list // TODO: parameter list
END_PARAM_LIST: END_PARAM_LIST:
match(T_RP); match(ctx, T_RP);
res->rt = block(); res->rt = block(ctx);
return (res); return (res);
} }
// Parse ans return the full ast // Frees a Pcontext and all its components.
struct Afunction* parse_source(const char *filename) { static void Pcontext_free(struct Pcontext *ctx) {
Tokens = scan_tokens(filename); struct llist_node *p = ctx->tokens.head, *nxt;
struct Afunction* res = function(); while (p) {
nxt = p->nxt;
while (Tokens.length > 0) { token_free((void*)p);
token_free(llist_popfront(&Tokens)); 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); return (res);
} }

View File

@ -1,240 +0,0 @@
#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);
}
// 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;
}
}

View File

@ -71,7 +71,7 @@ static char* scan_indentifier(int *n) {
while (isdigit(c) || isalpha(c) || c == '_') { while (isdigit(c) || isalpha(c) || c == '_') {
if (len >= sz - 1) { if (len >= sz - 1) {
sz *= 2; sz *= 2;
char *res = realloc(res, sz * sizeof(char)); res = realloc(res, sz * sizeof(char));
if (res == NULL) { if (res == NULL) {
fail_malloc(__FUNCTION__); fail_malloc(__FUNCTION__);
} }

View File

@ -1,123 +0,0 @@
/*
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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);
}
*/

View File

@ -6,14 +6,14 @@
// Parse the target string // Parse the target string
int target_parse(const char *target_string) { int target_parse(const char *target_string) {
static const char *target_map_k[] = { static const char *target_map_k[] = {
"ast", "_ast",
"quad", "_acir",
NULL NULL
}; };
static const int target_map_v[] = { static const int target_map_v[] = {
TARGET_AST, TARGET_AST,
TARGET_QUAD, TARGET_ACIR,
}; };
for (int i = 0; target_map_k[i]; ++i) { for (int i = 0; target_map_k[i]; ++i) {

View File

@ -89,8 +89,8 @@ void llist_insert(struct linklist *l, int index, void *val) {
p->nxt = x; p->nxt = x;
} }
// Pop the first element of the link list // Pop the first element of the link list.
// Return the first element. // Returns the first element.
void* llist_popfront(struct linklist *l) { void* llist_popfront(struct linklist *l) {
if (l->head == NULL) { if (l->head == NULL) {
return (NULL); return (NULL);

View File

@ -1,7 +1,18 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "fatals.h"
#include "util/misc.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 :) // This function does what you think it does :)
// Returns whether the given strings are the same. // Returns whether the given strings are the same.
bool strequal(const char *s1, const char *s2) { bool strequal(const char *s1, const char *s2) {

View File

@ -15,7 +15,7 @@ target("build")
add_includedirs("include/") add_includedirs("include/")
if is_mode("release") then if is_mode("release") then
set_strip("all") set_strip("all")
set_optimize("O2") set_optimize("faster")
end end
if is_mode("debug") then if is_mode("debug") then