handle type cast in ir
This commit is contained in:
parent
3b22cf6078
commit
ddb6921f12
@ -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).
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
104
src/acir.c
104
src/acir.c
@ -1,4 +1,5 @@
|
||||
#include <stdlib.h>
|
||||
#include <vtype.h>
|
||||
#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: {
|
||||
|
@ -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).
|
||||
|
10
src/vtype.c
10
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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user