handle type cast in ir

This commit is contained in:
方而静 2023-06-26 08:13:31 +08:00
parent 3b22cf6078
commit ddb6921f12
6 changed files with 101 additions and 26 deletions

View File

@ -35,6 +35,7 @@ enum {
// Defination of IR type code, which simplier than VType. // Defination of IR type code, which simplier than VType.
enum { enum {
IRT_UNDEF, // undefined value
IRT_VOID, // void IRT_VOID, // void
IRT_I1, // bool IRT_I1, // bool
IRT_I32, // 32bits integer IRT_I32, // 32bits integer
@ -88,8 +89,6 @@ struct IRfunction {
char *name; // function name char *name; // function name
struct linklist bs; // basic blocks struct linklist bs; // basic blocks
int ins_count; // number of instructions, used for allocating instruction identifier. 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. // 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); struct IRinstruction* IRinstruction_new_i32(struct IRblock *owner, int32_t v);
// Contructs an IRinstruction with an void immediate only. // 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); struct IRinstruction* IRinstruction_new_void(struct IRblock *owner);
// Constructs a IRinstuction with instruction IR_JMP or IR_BR (which is conditional jump). // Constructs a IRinstuction with instruction IR_JMP or IR_BR (which is conditional jump).

View File

@ -95,6 +95,7 @@ struct Afunction {
struct llist_node n; // linklist header struct llist_node n; // linklist header
char *name; // function name char *name; // function name
struct ASTnode *rt; // AST root struct ASTnode *rt; // AST root
struct VType ret_type; // return type
}; };
struct Afunction* Afunction_new(); struct Afunction* Afunction_new();

View File

@ -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. // Returns whether the given value types are the same.
bool VType_eq(const struct VType *x, const struct VType *y); 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 #endif

View File

@ -1,4 +1,5 @@
#include <stdlib.h> #include <stdlib.h>
#include <vtype.h>
#include "util/misc.h" #include "util/misc.h"
#include "fatals.h" #include "fatals.h"
#include "acir.h" #include "acir.h"
@ -44,8 +45,16 @@ struct IRinstruction* IRinstruction_new_i32(struct IRblock *owner, int32_t v) {
return (self); 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. // 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) { struct IRinstruction* IRinstruction_new_void(struct IRblock *owner) {
IRinstruction_constructor_shared_code IRinstruction_constructor_shared_code
@ -149,6 +158,7 @@ int IRTypecode_integer_promote(int self) {
// Returns a string identifier for the given type. // Returns a string identifier for the given type.
const char *IRTypecode_stringify(int self) { const char *IRTypecode_stringify(int self) {
static const char *map[] = { static const char *map[] = {
"undef",
"void", "void",
"i1", "i1",
"i32", "i32",
@ -160,6 +170,15 @@ const char *IRTypecode_stringify(int self) {
return map[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. // Translate an AST unary arithmetic opcode to a IR opcode.
static int IRopcode_from_ast_unary(int op) { static int IRopcode_from_ast_unary(int op) {
switch (op) { switch (op) {
@ -186,58 +205,94 @@ const char* IRopcode_stringify(int self) {
NULL 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. // DFS on an AST and build IR.
static struct IRinstruction* IRcg_dfs(struct ASTnode *x, struct IRfunction *f, struct IRblock *b) { static struct IRinstruction* IRcg_dfs(struct ASTnode *x, struct cg_context *ctx) {
// nothing to do, return the null object. // nothing to do, return the undef object.
if (x == NULL) { if (x == NULL) {
return (f->null); return (ctx->undef);
} }
switch (x->op) { switch (x->op) {
case A_RETURN: { case A_RETURN: {
struct ASTunnode *t = (void*)x; struct ASTunnode *t = (void*)x;
struct IRinstruction *value = IRcg_dfs(t->left, f, b); struct IRinstruction *value = IRcg_dfs(t->left, ctx);
IRinstruction_new(b, IR_RET, IRT_VOID, value, NULL); value = IRinstruction_cast(value, &ctx->af->ret_type, ctx->undef);
b->is_complete = true; IRinstruction_new(ctx->b, IR_RET, IRT_VOID, value, NULL);
return (f->null); ctx->b->is_complete = true;
return (ctx->undef);
} }
case A_BLOCK: { case A_BLOCK: {
struct ASTblocknode *t = (void*)x; struct ASTblocknode *t = (void*)x;
struct llist_node *p = t->st.head; struct llist_node *p = t->st.head;
while (p) { while (p) {
IRcg_dfs((struct ASTnode*)p, f, b); IRcg_dfs((struct ASTnode*)p, ctx);
p = p->nxt; p = p->nxt;
} }
return (f->null); return (ctx->undef);
} }
case A_LIT_I32: { case A_LIT_I32: {
struct ASTi32node *t = (void*)x; 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: { case A_NEG: case A_BNOT: {
struct ASTunnode *t = (void*)x; 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); int type = IRTypecode_integer_promote(value->type);
if (type != 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: { case A_LNOT: {
struct ASTunnode *t = (void*)x; struct ASTunnode *t = (void*)x;
// A logical not operation is basicly equivlant to comparing the value to 0. // A logical not operation is basicly equivlant to comparing the value to 0.
struct IRinstruction *value = IRcg_dfs(t->left, f, b), struct IRinstruction *value = IRcg_dfs(t->left, ctx),
*zero = IRinstruction_new_i32(b, 0); *zero = IRinstruction_new_i32(ctx->b, 0);
return (IRinstruction_new(b, IR_CMP_EQ, IRT_I1, value, zero)); return (IRinstruction_new(ctx->b, IR_CMP_EQ, IRT_I1, value, zero));
} }
default: { default: {
@ -257,8 +312,15 @@ struct IRfunction* IRfunction_from_ast(struct Afunction *afunc) {
llist_init(&self->bs); llist_init(&self->bs);
struct IRblock *entry = IRblock_new(self); // construct the function entry block. 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); return (self);
} }
@ -303,7 +365,7 @@ void IRinstruction_print(struct IRinstruction *self, FILE *Outfile) {
case IR_IMM: { case IR_IMM: {
fprintf(Outfile, "\t$%d = %s", self->id, IRTypecode_stringify(self->type)); fprintf(Outfile, "\t$%d = %s", self->id, IRTypecode_stringify(self->type));
switch (self->type) { switch (self->type) {
case IRT_VOID: { case IRT_VOID: case IRT_UNDEF: {
} break; } break;
case IRT_I1: { case IRT_I1: {

View File

@ -11,7 +11,7 @@
struct Pcontext { struct Pcontext {
struct linklist tokens; // token list struct linklist tokens; // token list
struct llist_node *cur; // current token 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. // 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. // Sets the func_name param.
static struct Afunction* function(struct Pcontext *ctx) { static struct Afunction* function(struct Pcontext *ctx) {
struct Afunction *res = Afunction_new(); 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); expect(ctx, T_ID);
res->name = current(ctx)->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(ctx)->val_s = NULL; // prevent it from being freed in token_free() called by next(ctx). current(ctx)->val_s = NULL; // prevent it from being freed in token_free() called by next(ctx).

View File

@ -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) { bool VType_eq(const struct VType *x, const struct VType *y) {
return (x->bt == y->bt); 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);
}
}