644 lines
12 KiB
C
644 lines
12 KiB
C
|
#include <strings.h>
|
||
|
#include <string.h>
|
||
|
#include <merror.h>
|
||
|
|
||
|
#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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|