#include #include #include #include "ir.h" static int cap_interned = 32; static int num_interned = 0; static char **interned_strings = NULL; const char* ir_str(const char *str) { if (interned_strings == NULL) { interned_strings = malloc(cap_interned * sizeof(char*)); } for (int i = 0; i < num_interned; i++) { if (strcmp(interned_strings[i], str) == 0) return interned_strings[i]; } if (num_interned == cap_interned) { cap_interned *= 2; interned_strings = realloc(interned_strings, cap_interned * sizeof(char*)); } interned_strings[num_interned++] = strdup(str); return interned_strings[num_interned - 1]; } struct ir* ir_make(void) { struct ir *ir = malloc(sizeof(struct ir)); ir->cap = 32; ir->len = 0; ir->inss = malloc(ir->cap * sizeof(struct irins*)); ir->globspace = 0; return ir; } void ir_delete(struct ir *ir) { for (int i = 0; i < ir->len; i++) { irins_delete(ir->inss[i]); } free(ir->inss); free(ir); } void ir_print(struct ir *ir, FILE *f) { fprintf(f, "IR [globspace=%d]\n", ir->globspace); for (int i = 0; i < ir->len; i++) { irins_print(ir->inss[i], f); } } const char* condcode_show(enum condcode condcode) { switch (condcode) { case CCZ: return "z"; case CCNZ: return "nz"; case CCL: return "l"; case CCG: return "g"; case CCLE: return "le"; case CCGE: return "ge"; default: assert(false); } } const char* ref_show(struct ref ref) { static const char *special[8] = {"A", "B", "C", "D", "X", "Y", "SP", "BP"}; static char *buffer = NULL; static int buffer_size = 0; if (buffer == NULL) { buffer_size = 40; // enough for all non-name refs buffer = malloc(buffer_size + 1); } const char *suffix = NULL; if (ref.type == REF_MEM) { if (ref.rel == REFREL_ZERO) suffix = ""; else if (ref.rel == REFREL_DATA) suffix = ":data"; else if (ref.rel == REFREL_HEAP) suffix = ":heap"; else assert(false); } switch (ref.type) { case REF_REG: if (ref.reg == REG_UNUSED) { strcpy(buffer, "<>"); } else if (ref.reg < 0) { strcpy(buffer, special[-(ref.reg - REG_A)]); } else { sprintf(buffer, "t%d", ref.reg); } break; case REF_MEM: if (ref.reg == REG_UNUSED) { sprintf(buffer, "[0x%x%s]", ref.offset, suffix); } else if (ref.reg < 0) { if (ref.offset < 0) { sprintf(buffer, "[%s - %d%s]", special[-(ref.reg - REG_A)], -ref.offset, suffix); } else { sprintf(buffer, "[%s + %d%s]", special[-(ref.reg - REG_A)], ref.offset, suffix); } } else { if (ref.offset < 0) { sprintf(buffer, "[t%d - %d%s]", ref.reg, -ref.offset, suffix); } else { sprintf(buffer, "[t%d + %d%s]", ref.reg, ref.offset, suffix); } } break; case REF_IMM: sprintf(buffer, "%d", ref.imm); break; case REF_VARNAME: { int namelen = strlen(ref.name); if (buffer_size < 1 + namelen) { buffer_size = 1 + namelen; buffer = realloc(buffer, 1 + namelen + 1); } buffer[0] = '@'; memcpy(buffer + 1, ref.name, namelen + 1); break; } default: assert(false); } return buffer; } static const char* ref_show_c(struct ref ref) { static char *cbuf[3] = {NULL, NULL, NULL}; static int cbufsize[3] = {-1, -1, -1}; static int cbufi = 0; const char *sbuf = ref_show(ref); int len = strlen(sbuf); if (cbufsize[cbufi] < len) { cbufsize[cbufi] = len; if (cbuf[cbufi]) cbuf[cbufi] = realloc(cbuf[cbufi], len + 1); else cbuf[cbufi] = malloc(len + 1); } memcpy(cbuf[cbufi], sbuf, len + 1); char *buf = cbuf[cbufi]; cbufi = (cbufi + 1) % 3; return buf; } void irins_print(struct irins *ins, FILE *f) { switch (ins->type) { case INS_ADD: fprintf(f, "\t%s <- %s + %s\n", ref_show_c(ins->r0), ref_show_c(ins->r1), ref_show_c(ins->r2)); break; case INS_SUB: fprintf(f, "\t%s <- %s - %s\n", ref_show_c(ins->r0), ref_show_c(ins->r1), ref_show_c(ins->r2)); break; case INS_MUL: fprintf(f, "\t%s <- %s * %s\n", ref_show_c(ins->r0), ref_show_c(ins->r1), ref_show_c(ins->r2)); break; case INS_DIV: fprintf(f, "\t%s <- %s / %s\n", ref_show_c(ins->r0), ref_show_c(ins->r1), ref_show_c(ins->r2)); break; case INS_MOD: fprintf(f, "\t%s <- %s %% %s\n", ref_show_c(ins->r0), ref_show_c(ins->r1), ref_show_c(ins->r2)); break; case INS_NEG: fprintf(f, "\t%s <- -%s\n", ref_show_c(ins->r0), ref_show_c(ins->r1)); break; case INS_NOT: fprintf(f, "\t%s <- ~%s\n", ref_show_c(ins->r0), ref_show_c(ins->r1)); break; case INS_TEST: fprintf(f, "\ttest %s, %s\n", ref_show_c(ins->r1), ref_show_c(ins->r2)); break; case INS_PUSH: fprintf(f, "\tpush %s\n", ref_show_c(ins->r1)); break; case INS_POP: fprintf(f, "\t%s <- pop\n", ref_show_c(ins->r0)); break; case INS_CMP: fprintf(f, "\tcmp %s, %s\n", ref_show_c(ins->r1), ref_show_c(ins->r2)); break; case INS_LBL: fprintf(f, "%s:\n", ins->name); break; case INS_JMP: fprintf(f, "\tjmp %s\n", ins->name); break; case INS_JCC: fprintf(f, "\tj%s %s\n", condcode_show(ins->condcode), ins->name); break; case INS_CALL: fprintf(f, "\tcall %s\n", ins->name); break; case INS_CALLV: fprintf(f, "\t%s <- call %s\n", ref_show_c(ins->r0), ins->name); break; case INS_RET: fprintf(f, "\tret\n"); break; case INS_RETV: fprintf(f, "\tret %s\n", ref_show_c(ins->r1)); break; case INS_MOV: fprintf(f, "\t%s <- %s\n", ref_show_c(ins->r0), ref_show_c(ins->r1)); break; case INS_BRK: fprintf(f, "\tbrk\n"); break; case INS_HWI: fprintf(f, "\thwi %s\n", ref_show_c(ins->r1)); break; default: assert(false); } } struct ref ir_reserve_global(struct ir *ir, int size) { int offset = ir->globspace; ir->globspace += size; return ref_mem(REG_UNUSED, offset, REFREL_DATA); } void ir_insert_before(struct ir *ir, int pos, struct irins *ins) { assert(pos >= 0 && pos <= ir->len); if (ir->len == ir->cap) { ir->cap *= 2; ir->inss = realloc(ir->inss, ir->cap * sizeof(struct irins*)); } memmove(ir->inss + pos + 1, ir->inss + pos, (ir->len - pos) * sizeof(struct irins*)); ir->inss[pos] = ins; ir->len++; } void ir_append(struct ir *ir, struct irins *ins) { ir_insert_before(ir, ir->len, ins); } struct irins* irins_make(enum instype type) { struct irins *ins = malloc(sizeof(struct irins)); ins->type = type; ins->name = NULL; return ins; } struct irins* irins_make_name(enum instype type, const char *name) { struct irins *ins = irins_make(type); ins->name = ir_str(name); return ins; } struct irins* irins_make_0(enum instype type, struct ref r0) { struct irins *ins = irins_make(type); ins->r0 = r0; return ins; } struct irins* irins_make_01(enum instype type, struct ref r0, struct ref r1) { struct irins *ins = irins_make(type); ins->r0 = r0; ins->r1 = r1; return ins; } struct irins* irins_make_012(enum instype type, struct ref r0, struct ref r1, struct ref r2) { struct irins *ins = irins_make_01(type, r0, r1); ins->r2 = r2; return ins; } struct irins* irins_make_1(enum instype type, struct ref r1) { struct irins *ins = irins_make(type); ins->r1 = r1; return ins; } struct irins* irins_make_12(enum instype type, struct ref r1, struct ref r2) { struct irins *ins = irins_make(type); ins->r1 = r1; ins->r2 = r2; return ins; } struct irins* irins_make_jcc(const char *name, enum condcode condcode) { struct irins *ins = irins_make(INS_JCC); ins->name = ir_str(name); ins->condcode = condcode; return ins; } void irins_delete(struct irins *ins) { free(ins); } void irins_which_refs(const struct irins *ins, bool have[3]) { have[0] = false; have[1] = false; have[2] = false; switch (ins->type) { case INS_ADD: have[0] = true; have[1] = true; have[2] = true; break; case INS_SUB: have[0] = true; have[1] = true; have[2] = true; break; case INS_MUL: have[0] = true; have[1] = true; have[2] = true; break; case INS_DIV: have[0] = true; have[1] = true; have[2] = true; break; case INS_MOD: have[0] = true; have[1] = true; have[2] = true; break; case INS_NEG: have[0] = true; have[1] = true; break; case INS_NOT: have[0] = true; have[1] = true; break; case INS_TEST: have[1] = true; have[2] = true; break; case INS_PUSH: have[1] = true; break; case INS_POP: have[0] = true; break; case INS_CMP: have[1] = true; have[2] = true; break; case INS_LBL: break; case INS_JMP: break; case INS_JCC: break; case INS_CALL: break; case INS_CALLV: have[0] = true; break; case INS_RET: break; case INS_RETV: have[1] = true; break; case INS_MOV: have[0] = true; have[1] = true; break; case INS_BRK: break; case INS_HWI: have[1] = true; break; default: assert(false); } } const char* gen_label_name(void) { static int next = 0; static char buf[16]; sprintf(buf, "__L%d", next++); return ir_str(buf); } struct ref ref_reg(int reg) { return (struct ref){REF_REG, reg, 0, REFREL_ZERO, 0, NULL}; } struct ref ref_mem(int reg, int offset, enum refrel rel) { return (struct ref){REF_MEM, reg, offset, rel, 0, NULL}; } struct ref ref_imm(int imm) { return (struct ref){REF_IMM, REG_UNUSED, 0, REFREL_ZERO, imm, NULL}; } struct ref ref_varname(const char *name) { return (struct ref){REF_VARNAME, REG_UNUSED, 0, REFREL_ZERO, 0, ir_str(name)}; } struct ref ref_next_register(void) { static int next = 0; return ref_reg(next++); } bool ref_equal(struct ref r1, struct ref r2) { if (r1.type != r2.type) return false; switch (r1.type) { case REF_REG: return r1.reg == r2.reg; case REF_MEM: return r1.reg == r2.reg && r1.offset == r2.offset && r1.rel == r2.rel; case REF_IMM: return r1.imm == r2.imm; case REF_VARNAME: return strcmp(r1.name, r2.name) == 0; default: assert(false); } }