diff --git a/include/acir.h b/include/acir.h index 63d396f..6d3eb63 100644 --- a/include/acir.h +++ b/include/acir.h @@ -35,6 +35,7 @@ enum { // Defination of IR type code, which simplier than VType. enum { + IRT_UNDEF, // undefined value IRT_VOID, // void IRT_I1, // bool IRT_I32, // 32bits integer @@ -88,8 +89,6 @@ struct IRfunction { char *name; // function name struct linklist bs; // basic blocks int ins_count; // number of instructions, used for allocating instruction identifier. - struct IRinstruction *null; // an instruction with a value void, considered to have instruction id 0. - // This is used for the null object patern. }; // Constructs an IRinstruction with an operator, and two operands. @@ -100,7 +99,6 @@ struct IRinstruction* IRinstruction_new(struct IRblock *owner, int op, int type, struct IRinstruction* IRinstruction_new_i32(struct IRblock *owner, int32_t v); // Contructs an IRinstruction with an void immediate only. -// Use IRfunction.null instead of contructing a new void immediate. struct IRinstruction* IRinstruction_new_void(struct IRblock *owner); // Constructs a IRinstuction with instruction IR_JMP or IR_BR (which is conditional jump). diff --git a/include/ast.h b/include/ast.h index e70e610..dce0e91 100644 --- a/include/ast.h +++ b/include/ast.h @@ -95,6 +95,7 @@ struct Afunction { struct llist_node n; // linklist header char *name; // function name struct ASTnode *rt; // AST root + struct VType ret_type; // return type }; struct Afunction* Afunction_new(); diff --git a/include/vtype.h b/include/vtype.h index 3eed394..34f1b6c 100644 --- a/include/vtype.h +++ b/include/vtype.h @@ -36,4 +36,7 @@ bool VType_ext_eq(const struct VType *x, const struct VType *y); // Returns whether the given value types are the same. bool VType_eq(const struct VType *x, const struct VType *y); +// Returns whether the given VType is a variant of integer(including bool) +bool VType_is_int(const struct VType *self); + #endif diff --git a/src/acir.c b/src/acir.c index e533ed2..c3bcdad 100644 --- a/src/acir.c +++ b/src/acir.c @@ -1,4 +1,5 @@ #include +#include #include "util/misc.h" #include "fatals.h" #include "acir.h" @@ -44,8 +45,16 @@ struct IRinstruction* IRinstruction_new_i32(struct IRblock *owner, int32_t v) { return (self); } +// Contructs an IRinstruction with an undef immediate only. +struct IRinstruction* IRinstruction_new_undef(struct IRblock *owner) { + IRinstruction_constructor_shared_code + + self->op = IR_IMM; + self->type = IRT_UNDEF; + return (self); +} + // Contructs an IRinstruction with an void immediate only. -// Use IRfunction.null instead of contructing a new void immediate. struct IRinstruction* IRinstruction_new_void(struct IRblock *owner) { IRinstruction_constructor_shared_code @@ -149,6 +158,7 @@ int IRTypecode_integer_promote(int self) { // Returns a string identifier for the given type. const char *IRTypecode_stringify(int self) { static const char *map[] = { + "undef", "void", "i1", "i32", @@ -160,6 +170,15 @@ const char *IRTypecode_stringify(int self) { return map[self]; } +bool IRTypecode_is_int(int self) { + switch (self) { + case IRT_I1: case IRT_I32: case IRT_I64: + return (true); + default: + return (false); + } +} + // Translate an AST unary arithmetic opcode to a IR opcode. static int IRopcode_from_ast_unary(int op) { switch (op) { @@ -186,58 +205,94 @@ const char* IRopcode_stringify(int self) { NULL }; - return map[self]; + return (map[self]); } +struct IRinstruction *IRinstruction_cast(struct IRinstruction *self, const struct VType *vt + , struct IRinstruction *undef) { + int tc = IRTypecode_from_VType(vt); + if (self->type == tc) { + return (self); + } + + if (self->type == IRT_UNDEF) { + return (self); + } + + if (self->type == IRT_VOID) { + return (undef); + } + + if (IRTypecode_is_int(self->type)) { + if (!VType_is_int(vt)) { + return (undef); + } + + struct IRinstruction *pmt = IRinstruction_new(self->owner, IR_SEXT, + IRTypecode_integer_promote(self->type), self, NULL); + struct IRinstruction *res = IRinstruction_new(self->owner, IR_TRUNC, tc, pmt, NULL); + return (res); + } + fail_todo(__FUNCTION__); +} + +struct cg_context { + struct IRblock *b; + struct IRfunction *irf; + struct Afunction *af; + struct IRinstruction *undef; +}; + // DFS on an AST and build IR. -static struct IRinstruction* IRcg_dfs(struct ASTnode *x, struct IRfunction *f, struct IRblock *b) { - // nothing to do, return the null object. +static struct IRinstruction* IRcg_dfs(struct ASTnode *x, struct cg_context *ctx) { + // nothing to do, return the undef object. if (x == NULL) { - return (f->null); + return (ctx->undef); } 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, IRT_VOID, value, NULL); - b->is_complete = true; - return (f->null); + struct IRinstruction *value = IRcg_dfs(t->left, ctx); + value = IRinstruction_cast(value, &ctx->af->ret_type, ctx->undef); + IRinstruction_new(ctx->b, IR_RET, IRT_VOID, value, NULL); + ctx->b->is_complete = true; + return (ctx->undef); } case A_BLOCK: { struct ASTblocknode *t = (void*)x; struct llist_node *p = t->st.head; while (p) { - IRcg_dfs((struct ASTnode*)p, f, b); + IRcg_dfs((struct ASTnode*)p, ctx); p = p->nxt; } - return (f->null); + return (ctx->undef); } case A_LIT_I32: { struct ASTi32node *t = (void*)x; - return (IRinstruction_new_i32(b, t->val)); + return (IRinstruction_new_i32(ctx->b, t->val)); } case A_NEG: case A_BNOT: { struct ASTunnode *t = (void*)x; - struct IRinstruction *value = IRcg_dfs(t->left, f, b); + struct IRinstruction *value = IRcg_dfs(t->left, ctx); int type = IRTypecode_integer_promote(value->type); if (type != value->type) { - value = IRinstruction_new(b, IR_SEXT, type, value, NULL); + value = IRinstruction_new(ctx->b, IR_SEXT, type, value, NULL); } - return (IRinstruction_new(b, IRopcode_from_ast_unary(x->op), type, value, NULL)); + return (IRinstruction_new(ctx->b, IRopcode_from_ast_unary(x->op), type, value, NULL)); } case A_LNOT: { struct ASTunnode *t = (void*)x; // A logical not operation is basicly equivlant to comparing the value to 0. - struct IRinstruction *value = IRcg_dfs(t->left, f, b), - *zero = IRinstruction_new_i32(b, 0); - return (IRinstruction_new(b, IR_CMP_EQ, IRT_I1, value, zero)); + struct IRinstruction *value = IRcg_dfs(t->left, ctx), + *zero = IRinstruction_new_i32(ctx->b, 0); + return (IRinstruction_new(ctx->b, IR_CMP_EQ, IRT_I1, value, zero)); } default: { @@ -257,8 +312,15 @@ struct IRfunction* IRfunction_from_ast(struct Afunction *afunc) { llist_init(&self->bs); struct IRblock *entry = IRblock_new(self); // construct the function entry block. - self->null = IRinstruction_new_void(entry); // initialize the null object. - IRcg_dfs(afunc->rt, self, entry); // generate code by doing a DFS in our AST. + + struct cg_context *ctx = try_malloc(sizeof(struct cg_context), __FUNCTION__); + ctx->undef = IRinstruction_new_undef(entry); // initialize the undef object. + ctx->af = afunc; + ctx->b = entry; + ctx->irf = self; + + IRcg_dfs(afunc->rt, ctx); // generate code by doing a DFS in our AST. + free(ctx); return (self); } @@ -303,7 +365,7 @@ void IRinstruction_print(struct IRinstruction *self, FILE *Outfile) { case IR_IMM: { fprintf(Outfile, "\t$%d = %s", self->id, IRTypecode_stringify(self->type)); switch (self->type) { - case IRT_VOID: { + case IRT_VOID: case IRT_UNDEF: { } break; case IRT_I1: { diff --git a/src/parse.c b/src/parse.c index ee0b6cb..b9ab95c 100644 --- a/src/parse.c +++ b/src/parse.c @@ -11,7 +11,7 @@ struct Pcontext { struct linklist tokens; // token list struct llist_node *cur; // current token - struct VType func_type; // current function return type + struct Afunction *func; // current function }; // Checks that we have a binary operator and return its precedence. @@ -422,8 +422,9 @@ static bool parse_type(struct VType *self, struct Pcontext *ctx, bool ce) { // Sets the func_name param. static struct Afunction* function(struct Pcontext *ctx) { struct Afunction *res = Afunction_new(); + ctx->func = res; - parse_type(&ctx->func_type, ctx, true); + parse_type(&res->ret_type, ctx, true); 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). diff --git a/src/vtype.c b/src/vtype.c index 75dac36..0e197c3 100644 --- a/src/vtype.c +++ b/src/vtype.c @@ -76,3 +76,13 @@ bool VType_ext_eq(const struct VType *x, const struct VType *y) { bool VType_eq(const struct VType *x, const struct VType *y) { return (x->bt == y->bt); } + +// Returns whether the given VType is a variant of integer(including bool) +bool VType_is_int(const struct VType *self) { + switch (self->bt) { + case VT_BOOL: case VT_I32: case VT_I64: + return (true); + default: + return (false); + } +} \ No newline at end of file