#include #include #include #include "../gen.h" #include "mips.h" struct grammer_mapping { char *name; int name_len; enum grammer_type type; }; #define MAPPING(name, enum) {name, sizeof(name), GMR_ ##enum} static const struct grammer_mapping grammer_mappings[__GMR_LEN] = { // registers MAPPING("rd", RD), MAPPING("rs", RS), MAPPING("rt", RT), // fp registers MAPPING("fs", FS), MAPPING("ft", FT), MAPPING("fd", FD), // numberic fileds MAPPING("immd", IMMD), MAPPING("cc", CC), MAPPING("code", CODE), MAPPING("pos", POS), MAPPING("size", SIZE), MAPPING("hb", HB), MAPPING("hint", HINT), // addresses MAPPING("hi", HI), MAPPING("lo", LO), MAPPING("target", TARGET), MAPPING("offset", OFFSET), MAPPING("offset(base)", OFFSET_BASE), MAPPING("index(base)", INDEX_BASE) }; /* Parses the input string and matches it to a grammer type. Returns * the number of characters consumed, or -1 on error. */ int gen_parse_grammer_type(const char *name, enum grammer_type *res) { for (int i = 0; i < __GMR_LEN; i++) { const struct grammer_mapping *mapping = &grammer_mappings[i]; if (strncasecmp(name, mapping->name, mapping->name_len) != 0) continue; if (res != NULL) *res = mapping->type; return mapping->name_len; } return -1; } /* Parses a register name, returing 0 on success, 1 on error*/ int gen_parse_register(enum mips32_register *reg, struct string *name) { int len = name->len; int c0 = len > 0 ? name->str[0] : '\0', c1 = len > 1 ? name->str[1] : '\0', c2 = len > 2 ? name->str[2] : '\0', c3 = len > 3 ? name->str[3] : '\0'; // $zero if (c0 == 'z') { if (c1 == 'e' && c2 == 'r' && c3 == 'o') { *reg = MIPS32_REG_ZERO; return M_SUCCESS; } } // $a0-a3 $at else if (c0 == 'a') { if (c1 == 't') { *reg = MIPS32_REG_AT; return M_SUCCESS; } if (c1 >= '0' && c1 <= '3') { *reg = MIPS32_REG_A0; *reg += c1 - '0'; return M_SUCCESS; } } // $v0-v1 else if (c0 == 'v') { if (c1 >= '0' && c1 <= '1') { *reg = MIPS32_REG_V0; *reg += c1 - '0'; return M_SUCCESS; } } // $t0-t9 else if (c0 == 't') { if (c1 >= '0' && c1 <= '7') { *reg = MIPS32_REG_T0; *reg += c1 - '0'; return M_SUCCESS; } // reg T8-T9 are not in order with T0-T7 if (c1 >= '8' && c1 <= '9') { *reg = MIPS32_REG_T8; *reg += c1 - '8'; return M_SUCCESS; } } // $s0-s7 $sp else if (c0 == 's') { if (c1 >= '0' && c1 <= '7') { *reg = MIPS32_REG_S0; *reg += c1 - '0'; return M_SUCCESS; } if (c1 == 'p') { *reg = MIPS32_REG_SP; return M_SUCCESS; } } // $k0-k1 else if (c0 == 'k') { if (c1 >= '0' && c1 <= '1') { *reg = MIPS32_REG_K0; *reg += c1 - '0'; return M_SUCCESS; } } // $gp else if (c0 == 'g') { if (c1 == 'p') { *reg = MIPS32_REG_GP; return M_SUCCESS; } } // $fp else if (c0 == 'f') { if (c1 == 'p') { *reg = MIPS32_REG_FP; return M_SUCCESS; } } // $rp else if (c0 == 'r') { if (c1 == 'a') { *reg = MIPS32_REG_RA; return M_SUCCESS; } } // $0-31 (non aliased register names) else if (c0 >= '0' && c0 <= '9') { int i = c0 - '0'; if (c1 >= '0' && c1 <= '9') { i *= 10; i += c1 - '0'; } if (i <= 31) { *reg = i; return M_SUCCESS; } } ERROR("unknown register $%.*s", name->len, name->str); return 1; } /* Parses a floating point register name, returing 0 on success, 1 on error*/ int gen_parse_fp_register(enum mips32_fp_register *reg, struct string *name) { int len = name->len; int c0 = len > 0 ? name->str[0] : '\0', c1 = len > 1 ? name->str[1] : '\0', c2 = len > 2 ? name->str[2] : '\0'; *reg = 0; if (c0 != 'f') goto error; if (c1 < '0' || c1 > '9') goto error; *reg += c1 - '0'; if (c1 == '\0') return M_SUCCESS; if (c2 < '0' || c2 > '9') goto error; *reg *= 10; *reg += c2 - '0'; return M_SUCCESS; error: ERROR("unknown fp register $%.*s", name->len, name->str); return 1; } /* convert string to number with int len max length */ static int antoi(char *str, int len) { char c; int num = 0; while (c = *str++, len--) { if (c < '0' || c > '9') break; num *= 10; num += c - '0'; } return num; } /* Parses the override expression (after the =) in the dsl for the grammer. * returns the number of characters consumed, or -1 on error. */ int gen_parse_grammer_override(struct gen_ins_state *state, struct gen_ins_override *over, char *value) { int value_len = 0; /* get length of value */ for (const char *ptr = value; *ptr != '\0' && *ptr != ','; ptr++, value_len++); // rd if (strncmp("rd", value, value_len)) over->reg = state->rd; // rs else if (strncmp("rs", value, value_len)) over->reg = state->rs; // rt else if (strncmp("rt", value, value_len)) over->reg = state->rt; // fd else if (strncmp("fd", value, value_len)) over->fpreg = state->fd; // fs else if (strncmp("fs", value, value_len)) over->fpreg = state->fs; // ft else if (strncmp("ft", value, value_len)) over->fpreg = state->ft; // immd else if (strncmp("immd", value, value_len)) over->immd = state->immd; // -immd else if (strncmp("-immd", value, value_len)) over->immd = -state->immd; // floating point register else if (value_len > 2 && value[0] == '$' && value[1] == 'f') { struct string temp = { .str = value + 1, .len = value_len - 1 }; if (gen_parse_fp_register(&over->fpreg, &temp)) return -1; } // register else if (value_len > 1 && value[0] == '$') { struct string temp = { .str = value + 1, .len = value_len - 1 }; if (gen_parse_register(&over->reg, &temp)) return -1; } // number else over->immd = antoi(value, value_len); return value_len; } static int read_next_state(struct expr_ins_arg *arg, struct gen_ins_state *state, enum grammer_type type) { switch (type) { case GMR_RD: // rd if (arg->type != EXPR_INS_ARG_REGISTER) { ERROR("expected a register"); return 1; } if (gen_parse_register(&state->rd, &arg->reg)) return 1; break; case GMR_RS: // rs if (arg->type != EXPR_INS_ARG_REGISTER) { ERROR("expected a register"); return 1; } if (gen_parse_register(&state->rs, &arg->reg)) return 1; break; case GMR_RT: // rt if (arg->type != EXPR_INS_ARG_REGISTER) { ERROR("expected a register"); return 1; } if (gen_parse_register(&state->rt, &arg->reg)) return 1; break; case GMR_FS: // fs if (arg->type != EXPR_INS_ARG_REGISTER) { ERROR("expected a register"); return 1; } if (gen_parse_fp_register(&state->fs, &arg->reg)) return 1; break; case GMR_FT: // ft if (arg->type != EXPR_INS_ARG_REGISTER) { ERROR("expected a register"); return 1; } if (gen_parse_fp_register(&state->ft, &arg->reg)) return 1; break; case GMR_FD: // fd if (arg->type != EXPR_INS_ARG_REGISTER) { ERROR("expected a register"); return 1; } if (gen_parse_fp_register(&state->fd, &arg->reg)) return 1; break; case GMR_IMMD: // immd if (arg->type != EXPR_INS_ARG_IMMEDIATE) { ERROR("expected an immediate"); return 1; } state->immd = arg->immd; break; case GMR_CC: // cc if (arg->type != EXPR_INS_ARG_IMMEDIATE) { ERROR("expected an immediate"); return 1; } state->cc = arg->immd; break; case GMR_CODE: // code if (arg->type != EXPR_INS_ARG_IMMEDIATE) { ERROR("expected an immediate"); return 1; } state->code = arg->immd; break; case GMR_POS: // pos if (arg->type != EXPR_INS_ARG_IMMEDIATE) { ERROR("expected an immediate"); return 1; } state->pos = arg->immd; break; case GMR_SIZE: // size if (arg->type != EXPR_INS_ARG_IMMEDIATE) { ERROR("expected an immediate"); return 1; } // TODO: check valid size state->size = arg->immd; break; case GMR_HB: // hb if (arg->type != EXPR_INS_ARG_IMMEDIATE) { ERROR("expected an immediate"); return 1; } state->hb = !!(arg->immd); break; case GMR_HINT: // hint if (arg->type != EXPR_INS_ARG_IMMEDIATE) { ERROR("expected an immediate"); return 1; } state->hint = arg-> immd; break; case GMR_OFFSET: // offset state->offset = 0; if (arg->type == EXPR_INS_ARG_IMMEDIATE) state->offset = arg->immd; else if (arg->type == EXPR_INS_ARG_LABEL) state->label = &arg->label; else { ERROR("invalid instruction"); return 1; } break; case GMR_OFFSET_BASE: // offset(base) if (arg->type != EXPR_INS_ARG_OFFSET) { ERROR("expected an offset($base)"); return 1; } state->offset = arg->offset.immd; if (gen_parse_register(&state->base, &arg->offset.reg)) return 1; break; case GMR_INDEX_BASE: // index(base) if (arg->type != EXPR_INS_ARG_OFFSET) { ERROR("expected an index($base)"); return 1; } state->index = arg->offset.immd; if (gen_parse_register(&state->base, &arg->offset.reg)) return 1; break; case GMR_TARGET: // target state->target = 0; if (arg->type == EXPR_INS_ARG_IMMEDIATE) state->target = arg->immd; else if (arg->type == EXPR_INS_ARG_LABEL) state->label = &arg->label; else { ERROR("invalid instruction"); return 1; } break; default: break; } return 0; } int gen_read_grammer_state(struct generator *gen, struct expr *const expr, struct gen_ins_state *state, struct mips32_grammer *grammer) { char *ptr = grammer->grammer; uint32_t argi = 0; // read values into state while (*ptr != '\0') { struct expr_ins_arg *arg; enum grammer_type type; size_t consumed; if (argi >= expr->instruction.args_len) { ERROR("not enough arguments passed"); gen_output_expr(gen, expr); return 1; } arg = &expr->instruction.args[argi++]; consumed = gen_parse_grammer_type(ptr, &type); if (consumed < 0) { BUG("invalid grammer: %s", grammer); return 1; } if (read_next_state(arg, state, type)) { gen_output_expr(gen, expr); return 1; } ptr += consumed; if (*ptr == ',') { ptr++; continue; } if (*ptr != '\0') { BUG("invalid grammer: %s", grammer); return 1; } } return 0; } static int write_next_state(struct gen_ins_state *state, struct gen_ins_override *over, union mips32_instruction *ins, enum reference_type *reftype, char *ptr) { enum grammer_type type; size_t consumed; bool hc = false; consumed = gen_parse_grammer_type(ptr, &type); if (consumed < 0) { BUG("invalid grammer: %s", ptr); return -1; } if (*(ptr + consumed) == '=') { hc = true; consumed += gen_parse_grammer_override(state, over, ptr); if (consumed < 0) { BUG("invalid grammer: %s", ptr); return -1; } } switch (type) { case GMR_RD: ins->rd = hc ? over->reg : state->rd; break; case GMR_RS: ins->rs = hc ? over->reg : state->rs; break; case GMR_RT: ins->rt = hc ? over->reg : state->rt; break; case GMR_FS: ins->fs = hc ? over->fpreg : state->fs; break; case GMR_FT: ins->ft = hc ? over->fpreg : state->ft; break; case GMR_FD: ins->fd = hc ? over->fpreg : state->fd; break; case GMR_IMMD: ins->immd = hc ? over->immd : state->immd; break; case GMR_CC: ins->cc = state->cc; break; case GMR_CODE: ins->code = state->code; break; case GMR_POS: ins->shamt = state->pos; break; case GMR_SIZE: ins->rd = state->size ? state->size - 1 : 0; break; case GMR_HB: ins->hb = hc ? over->immd : state->hb; break; case GMR_HINT: ins->rt = state->hint; break; case GMR_OFFSET: ins->offset = state->offset; *reftype = REF_MIPS_PC16; break; case GMR_OFFSET_BASE: ins->offset = state->offset; ins->rs = state->base; *reftype = REF_MIPS_16; break; case GMR_INDEX_BASE: ins->rt = state->index; ins->rs = state->base; break; case GMR_TARGET: ins->target = state->target; *reftype = REF_MIPS_26; break; case GMR_HI: ins->immd = state->target >> 16; *reftype = REF_MIPS_HI16; break; case GMR_LO: ins->immd = state->target & 0x0000FFFF; *reftype = REF_MIPS_LO16; break; default: break; } return consumed; } int gen_write_grammer_state(struct generator *gen, struct gen_ins_state *state, char *grammer) { enum reference_type reftype = REF_NONE; struct gen_ins_override over; char *ptr = grammer; union mips32_instruction ins; while (*ptr != '\0') { int consumed; consumed = write_next_state(state, &over, &ins, &reftype, ptr); if (consumed < 0) return 1; ptr += consumed; if (*ptr == ',') { ptr++; continue; } if (*ptr != '\0') { BUG("invalid grammer: %s", grammer); return 1; } } }