#include #include #include #include #include "finalize.h" #include "gen.h" enum { ZERO = 0, FP = 1, SP = 2, RET_ADDR = 3, FUNC_ADDR = 4, }; // output needs to be set before using the rest of the interface static FILE *out; void genSetOutput(FILE *out_) { out = out_; } // acquire / release register static bool usedReg[256]; static void check(void) { GenReg reg = 0; do { if (usedReg[reg]) { fprintf(stderr, "warning: register %" PRIu8 " not released\n", reg); } ++reg; } while (reg != 255); } GenReg genGetReg(void) { static bool first = true; if (first) { first = false; finalizeRegister(check); } GenReg reg = 5; while (usedReg[++reg]) { } if (reg < 6) { fprintf(stderr, "genGetReg: out of registers\n"); finalizeExit(1); } usedReg[reg] = true; return reg; } void genUngetReg(GenReg reg) { assert(usedReg[reg]); usedReg[reg] = false; } // internal print stuff static void printInstr(const char *instr) { assert(out); fprintf(out, "\t%s ", instr); } static void printUInt_(uint64_t val, const char *prefix, const char *suffix) { assert(out); if (prefix) { fprintf(out, "%s", prefix); } fprintf(out, "0x%" PRIx64, val); if (suffix) { fprintf(out, "%s", suffix); } } static void printUInt(uint64_t val) { printUInt_(val, 0, 0); } static void printReg(GenReg reg) { assert(out); fprintf(out, "%%%" PRIu8, reg); } static void printMem(GenReg reg) { assert(out); fprintf(out, "(%%%" PRIu8 ")", reg); } static void printDisplMem(int64_t displ, GenReg reg) { assert(out); fprintf(out, "%" PRId64 "(%%%" PRIu8 ")", displ, reg); } static void printLabel(const char *label, const char *prefix, const char *suffix) { assert(out); if (prefix) { fprintf(out, "%s", prefix); } fprintf(out, "%s", label); if (suffix) { fprintf(out, "%s", suffix); } } static void printLabelDef(const char *label) { assert(out); fprintf(out, "%s:\n", label); } static void printComma(void) { assert(out); fprintf(out, ", "); } static void printNl(void) { assert(out); fprintf(out, "\n"); } // header / footer void genHeader(void) { printInstr("ldzwq"); printUInt(0); printComma(); printReg(SP); printNl(); } void genFooter(void) { printInstr("halt"); printReg(0); printNl(); } // set active segment void genText(void) { printInstr(".text"); printNl(); } void genData(void) { printInstr(".data"); printNl(); } void genBSS(void) { printInstr(".bss"); printNl(); } // generate data void genLabeledUInt(const char *label, uint64_t val) { printInstr(".align"); printUInt(8); printNl(); printLabelDef(label); printInstr(".quad"); printUInt(val); printNl(); } // load literal into register void genLoadUInt(uint64_t val, GenReg reg) { if (val <= 0xFFFFu) { printInstr("ldzwq"); printUInt(val); printComma(); printReg(reg); printNl(); } else if (val <= 0xFFFFFFFFu) { printInstr("ldzwq"); printUInt_(val, "@w1(", ")"); printComma(); printReg(reg); printNl(); printInstr("shldwq"); printUInt_(val, "@w0(", ")"); printComma(); printReg(reg); printNl(); } else { printInstr("ldzwq"); printUInt_(val, "@w3(", ")"); printComma(); printReg(reg); printNl(); printInstr("shldwq"); printUInt_(val, "@w2(", ")"); printComma(); printReg(reg); printNl(); printInstr("shldwq"); printUInt_(val, "@w1(", ")"); printComma(); printReg(reg); printNl(); printInstr("shldwq"); printUInt_(val, "@w0(", ")"); printComma(); printReg(reg); printNl(); } } void genLoadLabel(const char *label, GenReg reg) { printInstr("ldzwq"); printLabel(label, "@w3(", ")"); printComma(); printReg(reg); printNl(); printInstr("shldwq"); printLabel(label, "@w2(", ")"); printComma(); printReg(reg); printNl(); printInstr("shldwq"); printLabel(label, "@w1(", ")"); printComma(); printReg(reg); printNl(); printInstr("shldwq"); printLabel(label, "@w0(", ")"); printComma(); printReg(reg); printNl(); } // load / fetch quad word (8 bytes) void genFetch(GenReg addr, GenReg dest) { printInstr("movq"); printMem(addr); printComma(); printReg(dest); printNl(); } void genFetchDispl(int64_t displ, GenReg addr, GenReg dest) { if (displ >= -128 && displ < 128) { printInstr("movq"); printDisplMem(displ, addr); printComma(); printReg(dest); printNl(); } else { // acquire new register and use it to store displ + addr GenReg displAddr = genGetReg(); genLoadUInt(displ, displAddr); // ok, we use two's complenent for signed genOp3r(GEN_ADD_R, displAddr, addr, displAddr); genFetch(displAddr, dest); genUngetReg(displAddr); } } void genStore(GenReg src, GenReg addr) { printInstr("movq"); printReg(src); printComma(); printMem(addr); printNl(); } void genStoreDispl(GenReg src, int64_t displ, GenReg addr) { if (displ >= -128 && displ < 128) { printInstr("movq"); printReg(src); printComma(); printDisplMem(displ, addr); printNl(); } else { // acquire new register and use it to store displ + addr GenReg displAddr = genGetReg(); genLoadUInt(displ, displAddr); // ok, we use two's complenent for signed genOp3r(GEN_ADD_R, displAddr, addr, displAddr); genStore(src, displAddr); genUngetReg(displAddr); } } // 2 address instructions void genOp2r(enum GenOp op, GenReg reg0, GenReg reg1) { assert(op >= GEN_OP2R_BEGIN && op < GEN_OP2R_END); if (op == GEN_UNARYMINUS_R) { printInstr("subq"); printReg(reg0); printComma(); printReg(ZERO); printComma(); printReg(reg1); printNl(); return; } else { assert(0); } } // 3 address instructions void genOp3r(enum GenOp op, GenReg reg0, GenReg reg1, GenReg reg2) { assert(op >= GEN_OP3R_BEGIN && op < GEN_OP3R_END); GenReg reg2_ = reg2; if (op == GEN_ADD_R) { printInstr("addq"); } else if (op == GEN_SUB_R) { printInstr("subq"); } else if (op == GEN_IMUL_R) { printInstr("imulq"); } else if (op == GEN_DIV_R || op == GEN_MOD_R) { printInstr("divq"); reg2 = 4; } else { assert(0); } printReg(reg0); printComma(); printReg(reg1); printComma(); printReg(reg2); printNl(); if (op == GEN_DIV_R || op == GEN_MOD_R) { printInstr("movq"); printReg(op == GEN_DIV_R ? 4 : 5); printComma(); printReg(reg2_); printNl(); } } void genOp3i(enum GenOp op, uint64_t val, GenReg reg1, GenReg reg2) { assert(op >= GEN_OP3I_BEGIN && op < GEN_OP3I_END); if (val > 255) { GenReg tmp = genGetReg(); genLoadUInt(val, tmp); genOp3r(op - GEN_OP3I_BEGIN + GEN_OP3R_BEGIN, tmp, reg1, reg2); genUngetReg(tmp); return; } GenReg reg2_ = reg2; if (op == GEN_ADD_I) { printInstr("addq"); } else if (op == GEN_SUB_I) { printInstr("subq"); } else if (op == GEN_IMUL_I) { printInstr("imulq"); } else if (op == GEN_DIV_I || op == GEN_MOD_I) { printInstr("divq"); reg2 = 4; } else { assert(0); } printUInt(val); printComma(); printReg(reg1); printComma(); printReg(reg2); printNl(); if (op == GEN_DIV_I || op == GEN_MOD_I) { printInstr("movq"); printReg(op == GEN_DIV_I ? 4 : 5); printComma(); printReg(reg2_); printNl(); } } // IO hack void genOutHack(GenReg src) { printInstr("subq"); printUInt(8 * (3 + 1)); printComma(); printReg(SP); printComma(); printReg(SP); printNl(); // store first function parameter genStoreDispl(src, 24, SP); // call function genLoadLabel("print_uint64", FUNC_ADDR); printInstr("call"); printReg(FUNC_ADDR); printComma(); printReg(RET_ADDR); printNl(); printInstr("addq"); printUInt(8 * (3 + 1)); printComma(); printReg(SP); printComma(); printReg(SP); printNl(); // print an additional newline printInstr("putc"); printUInt('\n'); printNl(); } void genInHack(GenReg dest) { printInstr("subq"); printUInt(8 * (3 + 0)); printComma(); printReg(SP); printComma(); printReg(SP); printNl(); genLoadLabel("get_uint64", FUNC_ADDR); printInstr("call"); printReg(FUNC_ADDR); printComma(); printReg(RET_ADDR); printNl(); // fetch return value genFetchDispl(16, SP, dest); printInstr("addq"); printUInt(8 * (3 + 0)); printComma(); printReg(SP); printComma(); printReg(SP); printNl(); }