binutils-gdb/opcodes/e2k-stuff/e2k-opcode-generator.c

3493 lines
94 KiB
C

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int mcpu;
static int forward_incompat;
#define GENERATE_CSV
void
print_common_part_minimal (const char *type, const char *name,
const char *parse_args)
{
/* Note that elbrus-v1 "forward-compatible" instructions are generic ones and
shouldn't be placed under a special condition, while "forward-incompatible"
really should. */
#ifdef GENERATE_CSV
printf("%s%d,%s", forward_incompat ? "=" : ">=", mcpu, name);
#else
if (mcpu >= 2 || forward_incompat)
printf ("if (mcpu %s %d)\n", forward_incompat ? "==" : ">=", mcpu);
printf ("{\n\tstatic %s dummy =", type);
printf ("\n\t\t{\"%s\", %s", name, parse_args);
#endif
/* FIXME: zero it out so as to avoid FALSE "forward incompatible" instructions
in case `fetch_cop_channels ()' isn't invoked prior to this function. See
also my comment when setting `forward_incompat' to 1. */
forward_incompat = 0;
}
void
print_common_part (const char *type, const char *name, const char *parse_args)
{
print_common_part_minimal (type, name, parse_args);
printf (",NULL");
}
void
print_common_part_with_merge (const char *type, const char *name,
const char *parse_args, const char *merge)
{
print_common_part_minimal (type, name, parse_args);
printf (",%s", merge);
}
/* Number of operands (and thus elements in FORMAT array) are different
between different ALFs, but it's possible to print it here because we
receive it as a string. */
void
print_alf_part (const char *alopf, const char *mas, int cop, const int *chns,
const char *format)
{
int i;
#ifdef GENERATE_CSV
int is_mas = strcmp(mas, "NO_MAS");
printf (",%s%s,0x%x,", alopf, is_mas? "_MAS" : "", cop);
for (i = 0; i < 6; i++)
printf ("%d%s", chns[i], i < 5 ? "," : "");
printf (",%s", format);
#else
printf (", %s, %s, 0x%x, {", alopf, mas, cop);
for (i = 0; i < 6; i++)
printf ("%d%s", chns[i], i < 5 ? ", " : "");
printf ("}, %s", format);
#endif
}
void
print_alopf11_tail (const char *opce, const int *chns, const char *opc2,
int explicit_ales25_v4)
{
int i;
#ifdef GENERATE_CSV
printf (",%s,,,%s,%d", opce,opc2,explicit_ales25_v4);
#else
printf (", %s, ", opce);
printf ("{");
for (i = 0; i < 6; i++)
printf ("%s%s", chns[i] == 0 ? "0" : opc2, i < 5 ? ", " : "");
printf ("}");
printf (", %d", explicit_ales25_v4);
#endif
}
void
print_final_part ()
{
#ifdef GENERATE_CSV
printf("\n");
#else
printf ("};\n");
printf ("\tadd_to_insn_table ((e2k_opcode_templ *) &dummy);\n");
printf ("}\n");
#endif
}
typedef struct
{
int cop;
struct
{
const char *name;
int usage;
} opers[3];
int incompat;
int usage;
} e2k_oper;
typedef struct
{
int size;
e2k_oper *entries;
} e2k_oper_table;
#include "e2k-tables/tables.h"
/* These are opcodes used for operations producing flags (see Table B.1.4).
Note that in spite of the fact that most of them (except for INC{s,d} and
DEC{s,d}) have their counterparts in Tables B.1.{1,3.*} with exactly the
same values, those tables can't be used to encode these instructions because
of a wider set of allowed ALCes, whereas flag instructions can be encoded
only in ALC{0,3}. The failure to realize this from the very beginning was
the reason for Bug #101335. */
/* These flag instruction opcodes appeared starting from `elbrus-v1'. */
e2k_oper elbrus_v1_flag_opers[] =
{
{0x00, {{"ANDs", 0}, {"", 0}, {"", 0}}, 0},
{0x02, {{"ANDNs", 0}, {"", 0}, {"", 0}}, 0},
{0x04, {{"ORs", 0}, {"", 0}, {"", 0}}, 0},
{0x06, {{"ORNs", 0}, {"", 0}, {"", 0}}, 0},
{0x08, {{"XORs", 0}, {"", 0}, {"", 0}}, 0},
{0x0a, {{"XORNs", 0}, {"", 0}, {"", 0}}, 0},
{0x10, {{"ADDs", 0}, {"", 0}, {"", 0}}, 0},
{0x12, {{"SUBs", 0}, {"", 0}, {"", 0}}, 0},
{0x14, {{"SCLs", 0}, {"", 0}, {"", 0}}, 0},
{0x16, {{"SCRs", 0}, {"", 0}, {"", 0}}, 0},
{0x18, {{"SHLs", 0}, {"", 0}, {"", 0}}, 0},
{0x1a, {{"SHRs", 0}, {"", 0}, {"", 0}}, 0},
{0x1c, {{"SARs", 0}, {"", 0}, {"", 0}}, 0},
{0x22, {{"UMULX", 0}, {"", 0}, {"", 0}}, 0},
{0x23, {{"SMULX", 0}, {"", 0}, {"", 0}}, 0},
{0x30, {{"INCs", 0}, {"", 0}, {"", 0}}, 0},
{0x32, {{"DECs", 0}, {"", 0}, {"", 0}}, 0},
};
/* These ones were introduced into `elbrus-v3', after which the set of
instructions producing flags has remained intact up to `elbrus-v6'
inclusively. */
e2k_oper elbrus_v3_flag_opers[] =
{
{0x01, {{"ANDd", 0}, {"", 0}, {"", 0}}, 0},
{0x03, {{"ANDNd", 0}, {"", 0}, {"", 0}}, 0},
{0x05, {{"ORd", 0}, {"", 0}, {"", 0}}, 0},
{0x07, {{"ORNd", 0}, {"", 0}, {"", 0}}, 0},
{0x09, {{"XORd", 0}, {"", 0}, {"", 0}}, 0},
{0x0b, {{"XORNd", 0}, {"", 0}, {"", 0}}, 0},
{0x11, {{"ADDd", 0}, {"", 0}, {"", 0}}, 0},
{0x13, {{"SUBd", 0}, {"", 0}, {"", 0}}, 0},
{0x15, {{"SCLd", 0}, {"", 0}, {"", 0}}, 0},
{0x17, {{"SCRd", 0}, {"", 0}, {"", 0}}, 0},
{0x19, {{"SHLd", 0}, {"", 0}, {"", 0}}, 0},
{0x1b, {{"SHRd", 0}, {"", 0}, {"", 0}}, 0},
{0x1d, {{"SARd", 0}, {"", 0}, {"", 0}}, 0},
{0x31, {{"INCd", 0}, {"", 0}, {"", 0}}, 0},
{0x33, {{"DECd", 0}, {"", 0}, {"", 0}}, 0},
};
e2k_oper_table oper_tables[][5] =
{
{
{
sizeof (elbrus_v1_short_opers) / sizeof (e2k_oper),
elbrus_v1_short_opers
},
{
sizeof (elbrus_v1_long_opers) / sizeof (e2k_oper),
elbrus_v1_long_opers
},
{
0,
NULL
},
{
0,
NULL
},
{
sizeof (elbrus_v1_flag_opers) / sizeof (e2k_oper),
elbrus_v1_flag_opers
}
},
{
{
sizeof (elbrus_v2_short_opers) / sizeof (e2k_oper),
elbrus_v2_short_opers
},
{
sizeof (elbrus_v2_long_opers) / sizeof (e2k_oper),
elbrus_v2_long_opers
},
{
0,
NULL
},
{
0,
NULL
},
{
0,
NULL
}
},
{
{
sizeof (elbrus_v3_short_opers) / sizeof (e2k_oper),
elbrus_v3_short_opers
},
{
sizeof (elbrus_v3_long_opers) / sizeof (e2k_oper),
elbrus_v3_long_opers
},
{
sizeof (elbrus_v3_long_ext1_opers) / sizeof (e2k_oper),
elbrus_v3_long_ext1_opers
},
{
0,
NULL
},
{
sizeof (elbrus_v3_flag_opers) / sizeof (e2k_oper),
elbrus_v3_flag_opers,
}
},
{
{
sizeof (elbrus_v4_short_opers) / sizeof (e2k_oper),
elbrus_v4_short_opers
},
{
sizeof (elbrus_v4_long_opers) / sizeof (e2k_oper),
elbrus_v4_long_opers
},
{
sizeof (elbrus_v4_long_ext1_opers) / sizeof (e2k_oper),
elbrus_v4_long_ext1_opers
},
{
0,
NULL
},
{
0,
NULL
}
},
{
{
sizeof (elbrus_v5_short_opers) / sizeof (e2k_oper),
elbrus_v5_short_opers
},
{
sizeof (elbrus_v5_long_opers) / sizeof (e2k_oper),
elbrus_v5_long_opers
},
{
sizeof (elbrus_v5_long_ext1_opers) / sizeof (e2k_oper),
elbrus_v5_long_ext1_opers
},
{
0,
NULL
},
{
0,
NULL
}
},
{
{
sizeof (elbrus_v6_short_opers) / sizeof (e2k_oper),
elbrus_v6_short_opers
},
{
sizeof (elbrus_v6_long_opers) / sizeof (e2k_oper),
elbrus_v6_long_opers
},
{
sizeof (elbrus_v6_long_ext1_opers) / sizeof (e2k_oper),
elbrus_v6_long_ext1_opers
},
{
sizeof (elbrus_v6_long_ext2_opers) / sizeof (e2k_oper),
elbrus_v6_long_ext2_opers
},
{
0,
NULL
}
}
};
const e2k_oper_table *short_opers;
const e2k_oper_table *long_opers;
const e2k_oper_table *long_ext1_opers;
const e2k_oper_table *long_ext2_opers;
const e2k_oper_table *flag_opers;
const e2k_oper_table *elbrus_v1_short_opers_table = &oper_tables[0][0];
const e2k_oper_table *elbrus_v1_long_opers_table = &oper_tables[0][1];
const e2k_oper_table *elbrus_v1_flag_opers_table = &oper_tables[0][4];
typedef struct
{
const char *name;
const char *format;
} instr_format;
typedef struct
{
int cop;
int channels[6];
} instr_cop_chn;
int
fetch_cop_channels (const e2k_oper_table *table, const char *name,
instr_cop_chn *res)
{
int i, sz;
res->cop = -1;
for (i = 0; i < 6; i++)
res->channels[i] = 0;
for (i = 0; i < table->size; i++)
{
int j;
for (j = 0; j < 3; j++)
{
if (strcasecmp (table->entries[i].opers[j].name, name) == 0)
{
res->cop = table->entries[i].cop;
res->channels[j + 3] = res->channels[j] = 1;
table->entries[i].opers[j].usage += 1;
}
}
if (res->cop != -1)
{
/* As far as I understand, all sorts of SQRT are implemented in the
5-th channel only. */
if (strcasestr (name, "sqrt"))
{
int j;
/* Verify that we've obtained expected channels 2 and 5 from our
tables. */
for (j = 0; j < 6; j++)
{
if ((j != 2 && j != 5 && res->channels[j] == 1)
|| ((j == 2 || j == 5) && res->channels[j] == 0))
abort ();
}
res->channels[2] = 0;
}
/* FIXME: this is a rather rude solution. For this to work each
invocation of `print_common_part_minimal ()' should be preceded
with `fetch_cop_channels ()' which isn't always the case (consider
invocations of `output_alopf21_insn ()'). */
forward_incompat = table->entries[i].incompat;
return 1;
}
}
return 0;
}
void
gen_alopf1 ()
{
static const instr_format instrs_wo_mas[] =
{
{"adds", "ARGS_SSS"},
{"addd", "ARGS_DDD"},
{"subs", "ARGS_SSS"},
{"subd", "ARGS_DDD"},
{"udivx", "ARGS_DSS"},
{"umodx", "ARGS_DSS"},
{"sdivx", "ARGS_DSS"},
{"smodx", "ARGS_DSS"},
{"udivs", "ARGS_SSS"},
{"udivd", "ARGS_DDD"},
{"sdivs", "ARGS_SSS"},
{"sdivd", "ARGS_DDD"},
{"ands", "ARGS_SSS"},
{"andd", "ARGS_DDD"},
{"andns", "ARGS_SSS"},
{"andnd", "ARGS_DDD"},
{"ors", "ARGS_SSS"},
{"ord", "ARGS_DDD"},
{"orns", "ARGS_SSS"},
{"ornd", "ARGS_DDD"},
{"xors", "ARGS_SSS"},
{"xord", "ARGS_DDD"},
{"xorns", "ARGS_SSS"},
{"xornd", "ARGS_DDD"},
{"shls", "ARGS_SSS"},
{"shld", "ARGS_DDD"},
{"shrs", "ARGS_SSS"},
{"shrd", "ARGS_DDD"},
{"scls", "ARGS_SSS"},
{"scld", "ARGS_DDD"},
{"scrs", "ARGS_SSS"},
{"scrd", "ARGS_DDD"},
{"sars", "ARGS_SSS"},
{"sard", "ARGS_DDD"},
{"getfs", "ARGS_SSS"},
{"getfd", "ARGS_DDD"},
{"sxt", "ARGS_SSD"},
{"merges", "ARGS_SSS"},
{"merged", "ARGS_DDD"},
{"fadds", "ARGS_SSS"},
{"faddd", "ARGS_DDD"},
{"fsubs", "ARGS_SSS"},
{"fsubd", "ARGS_DDD"},
{"fmins", "ARGS_SSS"},
{"fmind", "ARGS_DDD"},
{"fmaxs", "ARGS_SSS"},
{"fmaxd", "ARGS_DDD"},
{"fmuls", "ARGS_SSS"},
{"fmuld", "ARGS_DDD"},
{"fxaddss", "ARGS_DSS"},
{"fxadddd", "ARGS_DDD"},
{"fxaddsx", "ARGS_DSD"},
{"fxadddx", "ARGS_DDD"},
{"fxaddxx", "ARGS_DDD"},
{"fxaddxd", "ARGS_DDD"},
{"fxaddxs", "ARGS_DDS"},
{"fxsubss", "ARGS_DSS"},
{"fxsubdd", "ARGS_DDD"},
{"fxsubsx", "ARGS_DSD"},
{"fxsubdx", "ARGS_DDD"},
{"fxsubxx", "ARGS_DDD"},
{"fxsubxd", "ARGS_DDD"},
{"fxsubxs", "ARGS_DDS"},
{"fxrsubss", "ARGS_DSS"},
{"fxrsubdd", "ARGS_DDD"},
{"fxrsubsx", "ARGS_DSD"},
{"fxrsubdx", "ARGS_DDD"},
{"fxmulss", "ARGS_DSS"},
{"fxmuldd", "ARGS_DDD"},
{"fxmulsx", "ARGS_DSD"},
{"fxmuldx", "ARGS_DDD"},
{"fxmulxx", "ARGS_DDD"},
{"fxmulxd", "ARGS_DDD"},
{"fxmulxs", "ARGS_DDS"},
{"fxdivss", "ARGS_DSS"},
{"fxdivdd", "ARGS_DDD"},
{"fxdivsx", "ARGS_DSD"},
{"fxdivdx", "ARGS_DDD"},
{"fxdivxx", "ARGS_DDD"},
{"fxdivxd", "ARGS_DDD"},
{"fxdivxs", "ARGS_DDS"},
{"fxdivtss", "ARGS_DSS"},
{"fxdivtdd", "ARGS_DDD"},
{"fxdivtsx", "ARGS_DSD"},
{"fxdivtdx", "ARGS_DDD"},
{"fxsqrtusx", "ARGS_DSD"},
{"fxsqrtudx", "ARGS_DDD"},
{"fxsqrtuxx", "ARGS_DDD"},
{"fxsqrttsx", "ARGS_DSD"},
{"fxsqrttdx", "ARGS_DDD"},
{"fxsqrttxx", "ARGS_DDD"},
{"movif", "ARGS_DSD"},
{"vfsi", "ARGS_SDD"},
{"vfbgv", "ARGS_SSS"},
{"mkfsw", "ARGS_SSD"},
};
static const instr_format instrs_with_mas[] =
{
{"ldcsb", "ARGS_SSD"},
{"lddsb", "ARGS_SSD"},
{"ldesb", "ARGS_SSD"},
{"ldfsb", "ARGS_SSD"},
{"ldgsb", "ARGS_SSD"},
{"ldssb", "ARGS_SSD"},
{"ldcsh", "ARGS_SSD"},
{"lddsh", "ARGS_SSD"},
{"ldesh", "ARGS_SSD"},
{"ldfsh", "ARGS_SSD"},
{"ldgsh", "ARGS_SSD"},
{"ldssh", "ARGS_SSD"},
{"ldcsw", "ARGS_SSD"},
{"lddsw", "ARGS_SSD"},
{"ldesw", "ARGS_SSD"},
{"ldfsw", "ARGS_SSD"},
{"ldgsw", "ARGS_SSD"},
{"ldssw", "ARGS_SSD"},
{"ldcsd", "ARGS_SSD"},
{"lddsd", "ARGS_SSD"},
{"ldesd", "ARGS_SSD"},
{"ldfsd", "ARGS_SSD"},
{"ldgsd", "ARGS_SSD"},
{"ldssd", "ARGS_SSD"},
{"ldb", "ARGS_DDD"},
{"ldh", "ARGS_DDD"},
{"ldw", "ARGS_DDD"},
{"ldd", "ARGS_DDD"}
};
static const struct {
const char *mas;
int size;
const instr_format *arr;
} alopf1_groups [] =
{
{
"NO_MAS",
sizeof (instrs_wo_mas) / sizeof (instrs_wo_mas[0]),
instrs_wo_mas
},
{
"MAS",
sizeof (instrs_with_mas) / sizeof (instrs_with_mas[0]),
instrs_with_mas
}
};
int i;
for (i = 0; i < 2; i++)
{
int j;
const char *mas = alopf1_groups[i].mas;
for (j = 0; j < alopf1_groups[i].size; j++)
{
const instr_format *crnt = &alopf1_groups[i].arr[j];
instr_cop_chn cop_chn;
const char *alopf = "ALOPF1";
/* Arguments of `MERGE{s,d}' instructions should be parsed
specially. */
if (strcmp (crnt->name, "merges") == 0
|| strcmp (crnt->name, "merged") == 0)
alopf = "MERGE";
/* Fetch info on ALS.cop and allowed channels from the short opers
table. */
if (! fetch_cop_channels (short_opers, crnt->name, &cop_chn))
continue;
if (strstr (crnt->name, "div") || strstr (crnt->name, "mod"))
{
/* According to Table B.1.1 ALS2 should be pretty valid . . . */
if (cop_chn.channels[2] == 0)
abort ();
cop_chn.channels[2] = 0;
}
print_common_part_with_merge ("e2k_alf1_opcode_templ", crnt->name,
"parse_alf_args", "merge_alopf_simple");
print_alf_part (alopf, mas, cop_chn.cop, cop_chn.channels,
crnt->format);
/* We don't have any additional info to print for ALOPF1, just
output
the closing brace. */
print_final_part ();
}
}
}
#define _generic(mnemo) (mcpu == 1 ? mnemo : "")
#define _e3s(mnemo) (mcpu == 2 ? mnemo : "")
void
gen_alopf2 ()
{
size_t i;
{
static const instr_format custom[] =
{
{"fxsqrtisx", "ARGS_SD"},
{"fxsqrtidx", "ARGS_DD"},
{"fxsqrtixx", "ARGS_DD"},
{"movfi", "ARGS_DS"},
{"bitrevs", "ARGS_SS"},
{"bitrevd", "ARGS_DD"},
{"lzcnts", "ARGS_SS"},
{"lzcntd", "ARGS_DD"},
{"popcnts", "ARGS_SS"},
{"popcntd", "ARGS_DD"},
{"modbgv", "ARGS_SS"},
};
for (i = 0; i < sizeof (custom) / sizeof (custom[0]); i++)
{
instr_cop_chn cop_chn;
if (! fetch_cop_channels (short_opers, custom[i].name, &cop_chn))
continue;
print_common_part_with_merge ("e2k_alf2_opcode_templ", custom[i].name,
"parse_alf_args", "merge_alopf_simple");
print_alf_part ("ALOPF2", "NO_MAS", cop_chn.cop, cop_chn.channels,
custom[i].format);
/* These ones have ALF2.opce == NONE == 0xc0, don't they? */
printf (",0xc0");
print_final_part ();
}
}
/* All flavours of MOVT. See table B.8.1. */
static const instr_format gen_movts[] =
{
{"movts", "ARGS_SS"},
{"movtd", "ARGS_DD"},
};
/* It makes sense to transpose this matrix, doesn't it? See
`custom_movts[j][i]' in the loop below. */
static const struct {
const char *name;
int opce;
} custom_movts[][3] =
{
/* opce = mv */
{
{"movts", 0xc0},
{"movtd", 0xc0}
},
/* opce = mvc */
{
{"movtcs", 0xc1},
{"movtcd", 0xc1}
},
/* opce = mvr */
{
{"movtrs", 0xc2},
{"movtrd", 0xc2}
},
/* opce = mvrc */
{
{"movtrcs", 0xc3},
{"movtrcd", 0xc3}
}
};
for (i = 0; i < 2; i++)
{
const instr_format *crnt = &gen_movts[i];
instr_cop_chn cop_chn;
int j;
if (!fetch_cop_channels (short_opers, crnt->name, &cop_chn))
continue;
for (j = 0; j < 4; j++)
{
print_common_part_with_merge ("e2k_alf2_opcode_templ",
custom_movts[j][i].name,
"parse_alf_args",
"merge_alopf_simple");
print_alf_part ("ALOPF2", "NO_MAS", cop_chn.cop, cop_chn.channels,
crnt->format);
/* It's time to initialize ALF2.opce now. */
printf (",0x%x", custom_movts[j][i].opce);
print_final_part ();
}
}
/* See Table B.3.1 */
{
static u_int8_t opce[] = {0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf};
static const char *gen_name [] = {"fstos", "fstod", "fdtos", "fdtod"};
instr_format custom[][4] = {
{
{_generic("fstois"), "ARGS_SS"},
{_generic("fstoid"), "ARGS_SD"},
{_generic("fdtois"), "ARGS_DS"},
{_generic("fdtoid"), "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{"", "ARGS_SD"},
{_generic("fxtois"), "ARGS_DS"}, /* ARGS_XS in fact */
{_generic("fxtoid"), "ARGS_DD"} /* ARGS_XD in fact */
},
{
{_generic("fstoistr"), "ARGS_SS"},
{_e3s("fstoidtr"), "ARGS_SD"},
{_generic("fdtoistr"), "ARGS_DS"},
{_e3s("fdtoidtr"), "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{"", "ARGS_SD"},
{_e3s("fxtoistr"), "ARGS_DS"},
{_e3s("fxtoidtr"), "ARGS_DD"}
},
{
{_generic("istofs"), "ARGS_SS"},
{_generic("istofd"), "ARGS_SD"},
{_generic("idtofs"), "ARGS_DS"},
{_generic("idtofd"), "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{_generic("istofx"), "ARGS_SD"},
{"", "ARGS_DS"},
{_generic("idtofx"), "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{_generic("fstofd"), "ARGS_SD"},
{_generic("fdtofs"), "ARGS_DS"},
{_generic("fxtofd"), "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{_generic("fstofx"), "ARGS_SD"},
{_generic("fxtofs"), "ARGS_DS"},
{_generic("fdtofx"), "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{"", "ARGS_SD"},
{_generic("pfdtois"), "ARGS_DS"},
{_generic("pfstois"), "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{"", "ARGS_SD"},
{"", "ARGS_DS"},
{"", "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{"", "ARGS_SD"},
{_generic("pfdtoistr"), "ARGS_DS"},
{_generic("pfstoistr"), "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{"", "ARGS_SD"},
{"", "ARGS_DS"},
{"", "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{"", "ARGS_SD"},
{"", "ARGS_DS"},
{_generic("pistofs"), "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{"", "ARGS_SD"},
{"", "ARGS_DS"},
{"", "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{_generic("pfstofd"), "ARGS_SD"},
{_generic("pfdtofs"), "ARGS_DS"},
{"", "ARGS_DD"}
},
{
{"", "ARGS_SS"},
{"", "ARGS_SD"},
{"", "ARGS_DS"},
{"", "ARGS_DD"}
}
};
for (i = 0; i < 16; i++)
{
int j;
for (j = 0; j < 4; j++)
{
instr_cop_chn cop_chn;
/* If there is no instruction, continue. */
if (custom[i][j].name[0] == '\0')
continue;
if (! fetch_cop_channels (elbrus_v1_short_opers_table,
gen_name[j], &cop_chn))
continue;
print_common_part_with_merge ("e2k_alf2_opcode_templ",
custom[i][j].name, "parse_alf_args",
"merge_alopf_simple");
print_alf_part ("ALOPF2", "NO_MAS", cop_chn.cop, cop_chn.channels,
custom[i][j].format);
/* It's time to initialize ALF2.opce now. */
printf (",0x%x", opce[i]);
print_final_part ();
}
}
}
{
/* Encode GETPL. */
instr_cop_chn cop_chn;
if (fetch_cop_channels (short_opers, "getpl", &cop_chn))
{
print_common_part_with_merge ("e2k_alf2_opcode_templ", "getpl",
"parse_alf_args", "merge_alopf_simple");
print_alf_part ("ALOPF2", "NO_MAS", cop_chn.cop, cop_chn.channels,
"ARGS_SD");
/* ALF2.opce == CUD == 0xf0 (see Table B.5.1) */
printf (",0xf0");
print_final_part ();
}
}
{
/* Encode GETSAP. */
instr_cop_chn cop_chn;
if (fetch_cop_channels (short_opers, "drtoap", &cop_chn))
{
/* Note that GETSAP (unlike other instructions having
`cop == DRTOAP'?) can be encoded in channels 0 and 1 only (in a
pair of these channels to be precise). */
cop_chn.channels[3] = cop_chn.channels[4] = 0;
print_common_part_with_merge ("e2k_alf2_opcode_templ", "getsap",
"parse_alf_args", "merge_alopf_simple");
print_alf_part ("ALOPF2", "NO_MAS", cop_chn.cop, cop_chn.channels,
"ARGS_SQ");
/* ALF2.opce == USD == 0xec (see Table B.5.1) */
printf (",0xec");
print_final_part ();
}
}
{
/* Encode CUDTOAP and GDTOAP. */
instr_cop_chn cop_chn;
if (fetch_cop_channels (short_opers, "drtoap", &cop_chn))
{
unsigned int alf2_opce[] = {0xf0, 0xf2};
const char *names[] = {"cudtoap", "gdtoap"};
for (i = 0; i < 2; i++)
{
print_common_part_with_merge
("e2k_alf2_opcode_templ", names[i],
"parse_alf_args", "merge_alopf_simple");
print_alf_part ("ALOPF2", "NO_MAS", cop_chn.cop,
cop_chn.channels, "ARGS_SQ");
/* ALF2.opce == GD == 0xf2 (see Table B.5.1) */
printf (",0x%x", alf2_opce[i]);
print_final_part ();
}
}
}
{
/* Encode e3s-specific MOVX{,a,c}. */
instr_cop_chn cop_chn;
if (fetch_cop_channels (short_opers, "movx", &cop_chn))
{
int opce[3] = {0xc0, 0xc1, 0xc2};
const char *ins_name[3] = {"movx", "movxa", "movxc"};
for (i = 0; i < 3; i++)
{
print_common_part_with_merge ("e2k_alf2_opcode_templ",
ins_name[i], "parse_alf_args",
"merge_alopf_simple");
print_alf_part ("ALOPF2", "NO_MAS", cop_chn.cop, cop_chn.channels,
"ARGS_DD");
printf (",0x%x", opce[i]);
print_final_part ();
}
}
}
}
void
gen_alopf3 ()
{
static const instr_format store_instrs[] =
{
{"stcsb", "ARGS_SSS"},
{"stdsb", "ARGS_SSS"},
{"stesb", "ARGS_SSS"},
{"stfsb", "ARGS_SSS"},
{"stgsb", "ARGS_SSS"},
{"stssb", "ARGS_SSS"},
{"stcsh", "ARGS_SSS"},
{"stdsh", "ARGS_SSS"},
{"stesh", "ARGS_SSS"},
{"stfsh", "ARGS_SSS"},
{"stgsh", "ARGS_SSS"},
{"stssh", "ARGS_SSS"},
{"stcsw", "ARGS_SSS"},
{"stdsw", "ARGS_SSS"},
{"stesw", "ARGS_SSS"},
{"stfsw", "ARGS_SSS"},
{"stgsw", "ARGS_SSS"},
{"stssw", "ARGS_SSS"},
{"stcsd", "ARGS_SSD"},
{"stdsd", "ARGS_SSD"},
{"stesd", "ARGS_SSD"},
{"stfsd", "ARGS_SSD"},
{"stgsd", "ARGS_SSD"},
{"stssd", "ARGS_SSD"},
{"stb", "ARGS_DDS"},
{"sth", "ARGS_DDS"},
{"stw", "ARGS_DDS"},
{"std", "ARGS_DDD"}
};
int i;
for (i = 0; i < sizeof (store_instrs) / sizeof (store_instrs[0]); i++)
{
const instr_format *crnt = &store_instrs[i];
instr_cop_chn cop_chn;
/* Fetching ALS.cop and legal channels from SHORT operations table. */
if (! fetch_cop_channels (short_opers, crnt->name, &cop_chn))
continue;
print_common_part ("e2k_alf3_opcode_templ", crnt->name,
"parse_alf_args");
print_alf_part ("ALOPF3", "MAS", cop_chn.cop, cop_chn.channels,
crnt->format);
/* No additional info to print. */
print_final_part ();
}
}
static void
gen_alopf7 ()
{
static const instr_format gen_ins[] =
{
{"cmpsb", "ARGS_SS"},
{"cmpdb", "ARGS_DD"},
{"cmpandsb", "ARGS_SS"},
{"cmpanddb", "ARGS_DD"},
{"fcmpsb", "ARGS_SS"},
{"fcmpdb", "ARGS_DD"},
{"fxcmpsb", "ARGS_SS"},
{"fxcmpdb", "ARGS_DD"},
{"fxcmpxb", "ARGS_DD"}
};
static const char *ins_name[][8] =
{
{"cmposb", "cmpbsb", "cmpesb", "cmpbesb", "cmpssb", "cmppsb", "cmplsb", "cmplesb"},
{"cmpodb", "cmpbdb", "cmpedb", "cmpbedb", "cmpsdb", "cmppdb", "cmpldb", "cmpledb"},
{"", "", "cmpandesb", "", "cmpandssb", "cmpandpsb", "", "cmpandlesb"},
{"", "", "cmpandedb", "", "cmpandsdb", "cmpandpdb", "", "cmpandledb"},
{"fcmpeqsb", "fcmpltsb", "fcmplesb", "fcmpuodsb", "fcmpneqsb", "fcmpnltsb", "fcmpnlesb", "fcmpodsb"},
{"fcmpeqdb", "fcmpltdb", "fcmpledb", "fcmpuoddb", "fcmpneqdb", "fcmpnltdb", "fcmpnledb", "fcmpoddb"},
{"fxcmpeqsb", "fxcmpltsb", "fxcmplesb", "fxcmpuodsb", "fxcmpneqsb", "fxcmpnltsb", "fxcmpnlesb", "fxcmpodsb"},
{"fxcmpeqdb", "fxcmpltdb", "fxcmpledb", "fxcmpuoddb", "fxcmpneqdb", "fxcmpnltdb", "fxcmpnledb", "fxcmpoddb"},
{"fxcmpeqxb", "fxcmpltxb", "fxcmplexb", "fxcmpuodxb", "fxcmpneqxb", "fxcmpnltxb", "fxcmpnlexb", "fxcmpodxb"}
};
int i;
for (i = 0; i < sizeof (gen_ins) / sizeof (gen_ins[0]); i++)
{
int j;
instr_cop_chn cop_chn;
const instr_format *crnt = &gen_ins[i];
/* Fetching ALS.cop and legal channels from SHORT operations table. */
if (! fetch_cop_channels (short_opers, crnt->name, &cop_chn))
continue;
for (j = 0; j < 8; j++)
{
/* Empty names correspond to non-existent instructions. */
if (ins_name[i][j][0] == '\0')
continue;
/* A specialized name should be output here rather than a "generic"
one. */
print_common_part ("e2k_alf7_opcode_templ", ins_name[i][j],
"parse_alf_args");
print_alf_part ("ALOPF7", "NO_MAS", cop_chn.cop, cop_chn.channels,
crnt->format);
/* It's time to initialize ALF7.dst2.cmpopce here which is equal to J
according to `Table B.2.1' in iset.single. */
printf (",,0x%x", j);
#ifndef GENERATE_CSV
/* Output an implicit number of NOPs implied by the comparison under
consideration. As far as I know, floating-point ones require 4,
while integer just 2. */
printf (", %d", ins_name[i][j][0] == 'f' ? 4 : 2);
#endif
print_final_part ();
}
}
}
static void
gen_alopf17 ()
{
static const instr_format gen_ins[] =
{
{"pcmpp", "ARGS_DD"},
{"qpcmpp", "ARGS_PP"}
};
static const char *ins_name[16]
= {"pcmpeqbop", "pcmpeqhop", "pcmpeqwop", "pcmpeqdop",
"pcmpgtbop", "pcmpgthop", "pcmpgtwop", "pcmpgtdop",
"pcmpeqbap", "pcmpeqhap", "pcmpeqwap", "pcmpeqdap",
"pcmpgtbap", "pcmpgthap", "pcmpgtwap", "pcmpgtdap"};
int i;
for (i = 0; i < 2; i++)
{
int j;
instr_cop_chn cop_chn;
const instr_format *crnt = &gen_ins[i];
/* Fetching ALS.cop and legal channels from SHORT operations table. */
if (! fetch_cop_channels (long_ext1_opers, crnt->name, &cop_chn))
continue;
for (j = 0; j < 16; j++)
{
/* A specialized name should be output here rather than a "generic"
one. */
char name[32];
sprintf (name, "%s%s", i == 0 ? "" : "q", ins_name[j]);
/* FIXME: eventually a dedicated e2k_alopf17_opcode_templ should
be used here. */
print_common_part ("e2k_alf7_opcode_templ", name, "parse_alf_args");
print_alf_part ("ALOPF17", "NO_MAS", cop_chn.cop, cop_chn.channels,
crnt->format);
/* It's time to initialize ALES.opce here which is equal to `0xc0 + J'
according to `Table B.2.5' in iset-v6.single. */
printf (",,,0x%x", 0xc0 + j);
#ifndef GENERATE_CSV
/* Output an implicit number of NOPs implied by the comparison.
According to the behaviour of LAS, these ones are likely to be
treated as integer ones. */
printf (", 2");
#endif
print_final_part ();
}
}
}
static void
gen_alopf8 ()
{
static const char *names[] = {"cctopo", "cctopb", "cctope", "cctopbe",
"cctops", "cctopp", "cctopl", "cctople"};
instr_cop_chn cop_chn;
/* This is going to succeed for generic. */
if ( fetch_cop_channels (short_opers, "cctob", &cop_chn))
{
size_t i;
for (i = 0; i < sizeof (names) / sizeof (names[0]); i++)
{
print_common_part ("e2k_alf8_opcode_templ", names[i],
"parse_alf_args");
print_alf_part ("ALOPF8", "NO_MAS", cop_chn.cop, cop_chn.channels,
"ARGS_S");
/* Encode ALF8.dst2.cmpopce which determines the tested relation. */
printf (",,0x%x", i);
print_final_part ();
}
}
}
static void
gen_alopf10 ()
{
int i;
static const char *ins_name[] = {"staab", "staah", "staaw",
"staad", "staaq", "staaqp",
"aaurw", "aaurws", "aaurwd", "aaurwq"};
static const char *real_name[] = {NULL, NULL, NULL,
NULL, NULL, NULL,
"staaw", "staaw", "staad", "staaq"};
static const char *fmt[] = {"ARGS_S", "ARGS_S", "ARGS_S",
"ARGS_D", "ARGS_Q", "ARGS_P",
"ARGS_S", "ARGS_S", "ARGS_D", "ARGS_Q"};
for (i = 0; i < sizeof (ins_name) / sizeof (ins_name[0]); i++)
{
const char *instr_fmt = i < 6 ? "ALOPF10" : "AAURW";
instr_cop_chn cop_chn;
if (! fetch_cop_channels (i != 5 ? long_opers : long_ext1_opers,
(real_name[i] != NULL
? real_name[i]
: ins_name[i]),
&cop_chn))
continue;
print_common_part ("e2k_alf10_opcode_templ", ins_name[i],
"parse_alf_args");
print_alf_part (instr_fmt, i < 6 ? "MAS" : "NO_MAS", cop_chn.cop,
cop_chn.channels, fmt[i]);
print_final_part ();
}
}
/* Note, that I've not seen `LDAA{B,H,W,D,Q}', being ALOPF19 as well, anywhere
within our sources including E2K Linux Kernel yet. Therefore they remain
unsupported meanwhile . . . */
static void
gen_alopf19 ()
{
int i;
static const char *ins_name[] = {"aaurr", "aaurrd", "aaurrq"};
static const char *real_name[] = {"ldaaw", "ldaad", "ldaaq"};
static const char *fmt[] = {"ARGS_S", "ARGS_D", "ARGS_Q"};
for (i = 0; i < 3; i++)
{
instr_cop_chn cop_chn;
if (! fetch_cop_channels (long_opers,
(real_name[i] != NULL
? real_name[i]
: ins_name[i]),
&cop_chn))
continue;
print_common_part ("e2k_alf9_opcode_templ", ins_name[i],
"parse_alf_args");
print_alf_part ("AAURR", "NO_MAS", cop_chn.cop, cop_chn.channels,
fmt[i]);
print_final_part ();
}
}
/* FIXME: can't these loads be unified with instructions in MAS[] in
gen_alopf11 () ??? */
static void
gen_alopf11_loads ()
{
static const instr_format ext_loads[] =
{
{"ldgdb", "ARGS_SSD"},
{"ldgdh", "ARGS_SSD"},
{"ldgdw", "ARGS_SSD"},
{"ldgdd", "ARGS_SSD"},
{"ldgdq", "ARGS_SSQ"},
{"ldcudb", "ARGS_SSD"},
{"ldcudh", "ARGS_SSD"},
{"ldcudw", "ARGS_SSD"},
{"ldcudd", "ARGS_SSD"},
{"ldcudq", "ARGS_SSQ"},
{"ldapb", "ARGS_QSD"},
{"ldaph", "ARGS_QSD"},
{"ldapw", "ARGS_QSD"},
{"ldapd", "ARGS_QSD"},
{"ldapq", "ARGS_QSQ"},
{"ldodwb", "ARGS_QSD"},
{"ldodwd", "ARGS_QSD"},
{"ldodwh", "ARGS_QSD"},
{"ldodwq", "ARGS_QSQ"},
{"ldodww", "ARGS_QSD"},
{"ldodpb", "ARGS_QSD"},
{"ldodpd", "ARGS_QSD"},
{"ldodph", "ARGS_QSD"},
{"ldodpq", "ARGS_QSQ"},
{"ldodpw", "ARGS_QSD"},
{"ldodrb", "ARGS_QSD"},
{"ldodrd", "ARGS_QSD"},
{"ldodrh", "ARGS_QSD"},
{"ldodrq", "ARGS_QSQ"},
{"ldodrw", "ARGS_QSD"},
{"ldcsq", "ARGS_DDQ"},
{"lddsq", "ARGS_DDQ"},
{"ldesq", "ARGS_SSQ"},
/* Beware of `r,ddq' format of arguments for the next two
instructions unlike `ddq' for the others. */
{"ldfsq", "ARGS_DDQ"},
{"ldgsq", "ARGS_DDQ"},
{"ldssq", "ARGS_DDQ"},
{"ldq", "ARGS_DDQ"},
};
static const instr_format ext1_loads[] =
{
{"ldqp", "ARGS_DDP"},
{"ldgdqp", "ARGS_SSP"},
{"ldcudqp", "ARGS_SSP"},
{"ldcsqp", "ARGS_DDP"},
{"lddsqp", "ARGS_DDP"},
{"ldesqp", "ARGS_SSP"},
/* Beware of `r,ddq' format of arguments for the next two
instructions unlike `ddq' for the others. */
{"ldfsqp", "ARGS_DDP"},
{"ldgsqp", "ARGS_DDP"},
{"ldssqp", "ARGS_DDP"},
{"ldapqp", "ARGS_QSP"},
{"ldrqp", "ARGS_DDP"},
};
static struct {
const instr_format *loads;
const int size;
} groups[] =
{
{ext_loads, sizeof (ext_loads) / sizeof (instr_format)},
{ext1_loads, sizeof (ext1_loads) / sizeof (instr_format)},
};
int i, j;
for (j = 0; j < 2; j++)
{
for (i = 0; i < groups[j].size; i++)
{
instr_cop_chn cop_chn;
/* Fetch info on ALS.cop and allowed channels from the LONG opers
table. */
if (! fetch_cop_channels (j == 0 ? long_opers : long_ext1_opers,
groups[j].loads[i].name, &cop_chn))
continue;
print_common_part_with_merge ("e2k_alopf11_opcode_templ",
groups[j].loads[i].name,
"parse_alf_args", "merge_alopf11");
print_alf_part ("ALOPF11", "MAS", cop_chn.cop, cop_chn.channels,
groups[j].loads[i].format);
print_alopf11_tail ("NONE", cop_chn.channels,
j == 0 ? "EXT" : "EXT1", 0);
print_final_part ();
}
}
}
struct extra_alopf11_lit8
{
u_int8_t max;
const char *warn;
};
static int
is_alopf11_lit8 (const char *name, struct extra_alopf11_lit8 *extra)
{
extra->max = 0;
extra->warn = NULL;
if (strcmp (name, "pextrh") == 0)
{
extra->max = 7;
extra->warn = ("use of 'pextrh' with count > 7 leads to an "
"undefined result");
}
else if (strcmp (name, "pinsh") == 0)
{
extra->max = 4;
extra->warn = ("use of 'pinsh' with count > 4 leads to an "
"undefined result");
}
else if (strcmp (name, "psllqh") == 0)
{
extra->max = 15;
extra->warn = ("use of 'psllqh' with count > 15 leads to a "
"zero result");
}
else if (strcmp (name, "psllql") == 0)
{
extra->max = 7;
extra->warn = ("use of 'psllql' with count > 7 leads to a "
"zero result");
}
else if (strcmp (name, "psrlqh") == 0)
{
extra->max = 7;
extra->warn = ("use of 'psrlqh' with count > 7 leads to a "
"zero result");
}
else if (strcmp (name, "psrlql") == 0)
{
extra->max = 15;
extra->warn = ("use of 'psrlql' with count > 15 leads to a "
"zero result");
}
else if (strcmp (name, "pshufw") == 0)
{
extra->max = 15;
extra->warn = "'pshufw' uses only four low-order bits of literal8";
}
if (extra->warn)
return 1;
return 0;
}
static void
print_alopf11_lit8 (const struct extra_alopf11_lit8 *extra)
{
printf (",%d,", extra->max);
if (extra->warn)
printf ("\"%s\"", extra->warn);
else
printf ("NULL");
}
static void
gen_alopf11 ()
{
static const instr_format masless[] =
{
{"muls", "ARGS_SSS"},
{"muld", "ARGS_DDD"},
{"umulx", "ARGS_SSD"},
{"smulx", "ARGS_SSD"},
{"fdivs", "ARGS_SSS"},
{"fdivd", "ARGS_DDD"},
{"fsqrttd", "ARGS_DDD"},
{"pfmuls", "ARGS_DDD"},
{"pfmuld", "ARGS_DDD"},
{"paddb", "ARGS_SSS"},
{"paddh", "ARGS_SSS"},
{"paddw", "ARGS_SSS"},
{"paddd", "ARGS_SSS"},
{"paddsb", "ARGS_SSS"},
{"paddsh", "ARGS_SSS"},
{"paddusb", "ARGS_SSS"},
{"paddush", "ARGS_SSS"},
{"psubb", "ARGS_SSS"},
{"psubh", "ARGS_SSS"},
{"psubw", "ARGS_SSS"},
{"psubd", "ARGS_SSS"},
{"psubsb", "ARGS_SSS"},
{"psubsh", "ARGS_SSS"},
{"psubusb", "ARGS_SSS"},
{"psubush", "ARGS_SSS"},
{"pmaxsh", "ARGS_SSS"},
{"pmaxub", "ARGS_SSS"},
{"pminsh", "ARGS_SSS"},
{"pminub", "ARGS_SSS"},
{"psadbw", "ARGS_SSS"},
{"pmulhuh", "ARGS_SSS"},
{"pmulhh", "ARGS_SSS"},
{"pmullh", "ARGS_SSS"},
{"pmaddh", "ARGS_SSS"},
{"pslld", "ARGS_SSS"},
{"psllw", "ARGS_SSS"},
{"psllh", "ARGS_SSS"},
{"psrld", "ARGS_SSS"},
{"psrlw", "ARGS_SSS"},
{"psrlh", "ARGS_SSS"},
{"psraw", "ARGS_SSS"},
{"psrah", "ARGS_SSS"},
{"pfadds", "ARGS_SSS"},
{"pfaddd", "ARGS_DDD"},
{"pfsubs", "ARGS_SSS"},
{"pfsubd", "ARGS_DDD"},
{"aptoap", "ARGS_QSQ"},
{"aptoapb", "ARGS_QSQ"},
/* I suppose that GETVA like CAST and TDTOMP isn't actually ALOPF1 as
it is said in iset.single. It seems to be ALOPF11 in fact. */
{"getva", "ARGS_QSD"},
{"pandd", "ARGS_DDD"},
{"pandnd", "ARGS_DDD"},
{"pord", "ARGS_DDD"},
{"pxord", "ARGS_DDD"},
/* I wonder if LDRD is MASless . . . */
{"ldrd", "ARGS_DDD"},
{"fstoifs", "ARGS_SSS"},
{"fdtoifd", "ARGS_DDD"},
{"umulhd", "ARGS_DDD"},
{"smulhd", "ARGS_DDD"},
/* FIXME: the way arguments of PUTTC are currently parsed is too generic
for this instruction: I can encode any general-purpose register as a
DST. However, iset.single says that only `%tc{,d}' should be used.
Interestingly enough, LAS lets one specify any register for DST as
well. */
{"puttc", "ARGS_DDD"},
{"pminuw", "ARGS_DDD"},
{"pminsw", "ARGS_DDD"},
{"pmaxuw", "ARGS_DDD"},
{"pmaxsw", "ARGS_DDD"},
{"fscales", "ARGS_SSS"},
{"fscaled", "ARGS_DSD"},
{"fxscalesx", "ARGS_DSD"},
{"mpsadbh", "ARGS_DDD"},
{"packuswh", "ARGS_DDD"},
{"pavgusb", "ARGS_DDD"},
{"pavgush", "ARGS_DDD"},
{"qpand", "ARGS_PPP"},
{"qpandn", "ARGS_PPP"},
{"qpor", "ARGS_PPP"},
{"qpxor", "ARGS_PPP"},
{"qpaddb", "ARGS_PPP"},
{"qpaddh", "ARGS_PPP"},
{"qpaddsb", "ARGS_PPP"},
{"qpaddsh", "ARGS_PPP"},
{"qpaddusb", "ARGS_PPP"},
{"qpaddush", "ARGS_PPP"},
{"qpaddw", "ARGS_PPP"},
{"qpaddd", "ARGS_PPP"},
{"qpsubb", "ARGS_PPP"},
{"qpsubh", "ARGS_PPP"},
{"qpsubsb", "ARGS_PPP"},
{"qpsubsh", "ARGS_PPP"},
{"qpsubusb", "ARGS_PPP"},
{"qpsubush", "ARGS_PPP"},
{"qpsubw", "ARGS_PPP"},
{"qpsubd", "ARGS_PPP"},
{"qpfadds", "ARGS_PPP"},
{"qpfaddd", "ARGS_PPP"},
{"qpfhadds", "ARGS_PPP"},
{"qpfhsubs", "ARGS_PPP"},
{"qpfaddsubs", "ARGS_PPP"},
{"qpfaddsubd", "ARGS_PPP"},
{"qpfstoifs", "ARGS_DPP"},
{"qpfdtoifd", "ARGS_DPP"},
{"qpfmins", "ARGS_PPP"},
{"qpfmind", "ARGS_PPP"},
{"qpfmaxs", "ARGS_PPP"},
{"qpfmaxd", "ARGS_PPP"},
{"qpfmuls", "ARGS_PPP"},
{"qpfmuld", "ARGS_PPP"},
{"qpfsubs", "ARGS_PPP"},
{"qpfsubd", "ARGS_PPP"},
{"qpmsk2sgnb", "ARGS_PSP"},
{"qppackdl", "ARGS_DDP"},
{"qpsllh", "ARGS_PDP"},
{"qpsllw", "ARGS_PDP"},
{"qpslld", "ARGS_PDP"},
{"qpsrlh", "ARGS_PDP"},
{"qpsrlw", "ARGS_PDP"},
{"qpsrld", "ARGS_PDP"},
{"qpsrah", "ARGS_PDP"},
{"qpsraw", "ARGS_PDP"},
{"pfdivs", "ARGS_SSS"},
{"pfdivd", "ARGS_DDD"},
{"pcmpeqd", "ARGS_DDD"},
{"pcmpgtd", "ARGS_DDD"},
{"pfhadds", "ARGS_DDD"},
{"pfhsubs", "ARGS_DDD"},
{"pfaddsubs", "ARGS_DDD"},
{"pfmins", "ARGS_DDD"},
{"pfmind", "ARGS_DDD"},
{"pfmaxs", "ARGS_DDD"},
{"pfmaxd", "ARGS_DDD"},
{"pfsqrttd", "ARGS_DDD"},
{"pminsb", "ARGS_DDD"},
{"pminuh", "ARGS_DDD"},
{"pmaxsb", "ARGS_DDD"},
{"pmaxuh", "ARGS_DDD"},
{"pfstoifs", "ARGS_DDD"},
{"pfdtoifd", "ARGS_DDD"},
{"phaddh", "ARGS_DDD"},
{"phaddw", "ARGS_DDD"},
{"phaddsh", "ARGS_DDD"},
{"phsubh", "ARGS_DDD"},
{"phsubw", "ARGS_DDD"},
{"phsubsh", "ARGS_DDD"},
{"psignb", "ARGS_DDD"},
{"psignh", "ARGS_DDD"},
{"psignw", "ARGS_DDD"},
{"pmaddubsh", "ARGS_DDD"},
{"pmulhrsh", "ARGS_DDD"},
{"phminposuh", "ARGS_DDD"},
{"pmulubhh", "ARGS_DDD"},
{"pextrh", "ARGS_DDS"},
{"pinsh", "ARGS_DDD"},
{"psllqh", "ARGS_DDD"},
{"psllql", "ARGS_DDD"},
{"psrlqh", "ARGS_DDD"},
{"psrlql", "ARGS_DDD"},
/* Starting from elbrus-v4 these operations are encoded in some ALCes as
ALOPF11. */
{"fadds", "ARGS_SSS"},
{"faddd", "ARGS_DDD"},
{"fsubs", "ARGS_SSS"},
{"fsubd", "ARGS_DDD"},
{"fmuls", "ARGS_SSS"},
{"fmuld", "ARGS_DDD"},
{"qpacksshb", "ARGS_PPP"},
{"qpacksswh", "ARGS_PPP"},
{"qpackushb", "ARGS_PPP"},
{"qpackuswh", "ARGS_PPP"},
{"qpavgusb", "ARGS_PPP"},
{"qpavgush", "ARGS_PPP"},
{"qpcmpeqb", "ARGS_PPP"},
{"qpcmpeqd", "ARGS_PPP"},
{"qpcmpeqh", "ARGS_PPP"},
{"qpcmpeqw", "ARGS_PPP"},
{"qpcmpgtb", "ARGS_PPP"},
{"qpcmpgtd", "ARGS_PPP"},
{"qpcmpgth", "ARGS_PPP"},
{"qpcmpgtw", "ARGS_PPP"},
{"qphaddh", "ARGS_PPP"},
{"qphaddsh", "ARGS_PPP"},
{"qphaddw", "ARGS_PPP"},
{"qphsubh", "ARGS_PPP"},
{"qphsubsh", "ARGS_PPP"},
{"qphsubw", "ARGS_PPP"},
{"qpmaxsb", "ARGS_PPP"},
{"qpmaxsh", "ARGS_PPP"},
{"qpmaxsw", "ARGS_PPP"},
{"qpmaxub", "ARGS_PPP"},
{"qpmaxuh", "ARGS_PPP"},
{"qpmaxuw", "ARGS_PPP"},
{"qpminsb", "ARGS_PPP"},
{"qpminsh", "ARGS_PPP"},
{"qpminsw", "ARGS_PPP"},
{"qpminub", "ARGS_PPP"},
{"qpminuh", "ARGS_PPP"},
{"qpminuw", "ARGS_PPP"},
{"qpmulhh", "ARGS_PPP"},
{"qpmulhrsh", "ARGS_PPP"},
{"qpmulhuh", "ARGS_PPP"},
{"qpmullh", "ARGS_PPP"},
{"qpmulubhh", "ARGS_DPP"},
{"qpsignb", "ARGS_PPP"},
{"qpsignh", "ARGS_PPP"},
{"qpsignw", "ARGS_PPP"},
{"qphminposuh", "ARGS_PPD"},
{"qpmaddh", "ARGS_PPP"},
{"qpmaddubsh", "ARGS_PPP"},
{"qpmpsadbh", "ARGS_PSP"},
{"qpsadbw", "ARGS_PPP"},
{"qpsrcd", "ARGS_PDP"},
{"qpsrcw", "ARGS_PDP"},
/* FIXME: according to `iset-v1.single' the next two instructions are
ALOPF1, however their opcodes can't be found among instructions having
a short encoding. Shouldn't you file a bug to these idiots at last? */
{"cast", "ARGS_QQQ"},
{"tdtomp", "ARGS_DDD"},
{"odtoap", "ARGS_QDQ"},
{"psrcd", "ARGS_DDD"},
{"psrcw", "ARGS_DDD"},
{"getfzs", "ARGS_SSS"},
{"getfzd", "ARGS_DDD"},
{"puttagqp", "ARGS_PSP"},
{"pmullw", "ARGS_DDD"},
{"qpmullw", "ARGS_PPP"},
{"pmrgp", "ARGS_DDD"},
{"qpmrgp", "ARGS_PPP"},
{"clmulh", "ARGS_DDD"},
{"clmull", "ARGS_DDD"},
};
/* FIXME: is MAS applicable to PUTTAGx instructions at all? LAS lets one
encode it with these instructions, but I doubt that this is correct. What
has made me believe that it's relevant to them? I can't find any evidence
of that within iset-vX.single . . . */
static const instr_format mas[] =
{
{"puttags", "ARGS_SSS"},
{"puttagd", "ARGS_DSD"}
};
static const struct {
const char *mas;
int size;
const instr_format *arr;
} groups [] =
{
{
"NO_MAS",
sizeof (masless) / sizeof (masless[0]),
masless
},
{
"MAS",
sizeof (mas) / sizeof (mas[0]),
mas
}
};
int i;
/* I guess that there is probably no point in generating templates for MAS
and MASless instructions inside one loop. To be revisited . . . */
for (i = 0; i < 2; i++)
{
int j;
const char *mas = groups[i].mas;
for (j = 0; j < groups[i].size; j++)
{
int k;
instr_cop_chn cop_chn;
const instr_format *crnt = &groups[i].arr[j];
struct extra_alopf11_lit8 extra;
int need_merge = strstr (crnt->name, "mrgp") ? 1 : 0;
int need_extra_ind = is_alopf11_lit8 (crnt->name, &extra);
/* See Bug #78923, Comment #2 for the original list of instructions
requiring explicitly specified default ALES{2,5} syllables on
elbrus-v4. */
int explicit_ales25_v4 = (strcmp (crnt->name, "pfadds") == 0
|| strcmp (crnt->name, "pfaddd") == 0
|| strcmp (crnt->name, "pfsubs") == 0
|| strcmp (crnt->name, "pfsubd") == 0
|| strcmp (crnt->name, "pfmuls") == 0
|| strcmp (crnt->name, "pfhadds") == 0
|| strcmp (crnt->name, "pfhsubs") == 0
|| strcmp (crnt->name, "pfaddsubs") == 0
);
for (k = 0; k < 3; k++)
{
const char *ext = (k == 0 ? "EXT" : (k == 1 ? "EXT1" : "EXT2"));
const e2k_oper_table *lopers
= (k == 0
? long_opers
: (k == 1 ? long_ext1_opers : long_ext2_opers));
if (lopers == NULL)
continue;
if (! fetch_cop_channels (lopers, crnt->name, &cop_chn))
continue;
if (strstr (crnt->name, "div"))
{
/* According to Table B.1.3 ALS2 should be pretty valid . . . */
if (cop_chn.channels[2] == 0)
abort ();
cop_chn.channels[2] = 0;
}
else if (strcmp (crnt->name, "puttc") == 0)
{
if (cop_chn.channels[3] == 0)
abort ();
cop_chn.channels[3] = 0;
}
print_common_part_with_merge
((need_extra_ind
? "e2k_alopf11_lit8_opcode_templ"
: "e2k_alopf11_opcode_templ"),
crnt->name, "parse_alf_args", "merge_alopf11");
print_alf_part (need_merge
?
"ALOPF11_MERGE"
: (need_extra_ind ? "ALOPF11_LIT8" : "ALOPF11"),
mas, cop_chn.cop, cop_chn.channels, crnt->format);
print_alopf11_tail ("NONE", cop_chn.channels, ext,
explicit_ales25_v4);
if (need_extra_ind)
print_alopf11_lit8 (&extra);
print_final_part ();
}
}
}
/* Floating-point comparisons producing bit masks (see Table B.2.3) */
{
static const instr_format generic[] = {
{"fcmps", "ARGS_SSS"},
{"fcmpd", "ARGS_DDD"},
{"pfcmps", "ARGS_DDD"},
{"pfcmpd", "ARGS_DDD"},
{"qpfcmps", "ARGS_PPP"},
{"qpfcmpd", "ARGS_PPP"},
};
static const char *suffixes[] = {"eq", "lt", "le", "uod", "neq",
"nlt", "nle", "od"};
static const char *opces[] = {"0xc0", "0xc1", "0xc2", "0xc3", "0xc4",
"0xc5", "0xc6", "0xc7"};
for (i = 0; i < 6; i++)
{
int j;
instr_cop_chn cop_chn;
char name[32];
size_t gen_len;
const e2k_oper_table *lopers = i < 4 ? long_opers : long_ext1_opers;
const char *ext = i < 4 ? "EXT" : "EXT1";
gen_len = strlen (generic[i].name);
strncpy (name, generic[i].name, gen_len - 1);
if (! fetch_cop_channels (lopers, generic[i].name, &cop_chn))
continue;
for (j = 0; j < 8; j++)
{
size_t sfx_len = strlen (suffixes[j]);
strncpy (&name[gen_len - 1], suffixes[j], sfx_len);
/* This should copy the trailing zero as well. */
strncpy (&name[gen_len + sfx_len - 1],
&generic[i].name[gen_len - 1], 2);
print_common_part_with_merge ("e2k_alopf11_opcode_templ", name,
"parse_alf_args", "merge_alopf11");
print_alf_part ("ALOPF11", "NO_MAS", cop_chn.cop, cop_chn.channels,
generic[i].format);
print_alopf11_tail (opces[j], cop_chn.channels, ext, 0);
print_final_part ();
}
}
}
/* Floating-point comparisons producing flags (see B.2.4 in iset.single) */
{
static const instr_format general[] = {
{"fcmpsf", "ARGS_SSS"},
{"fcmpdf", "ARGS_DDS"},
{"fxcmpsf", "ARGS_DSS"},
{"fxcmpdf", "ARGS_DDS"},
{"fxcmpxf", "ARGS_DDS"}
};
static const char *custom_opce[2] = {"0xc7", "0xc3"};
static const char *custom_name[][2] = {
{"fcmpodsf", "fcmpudsf"},
{"fcmpoddf", "fcmpuddf"},
{"fxcmpodsf", "fxcmpudsf"},
{"fxcmpoddf", "fxcmpuddf"},
{"fxcmpodxf", "fxcmpudxf"}
};
static const char *others[] = {
/* These are PCMPs */
"pcmpeqb", "pcmpeqh", "pcmpeqw", "pcmpgtb", "pcmpgth", "pcmpgtw",
"pmovmskb", "pmovmskps", "pmovmskpd", "packsshb", "packushb",
"pshufw",
"packsswh", "punpckhbh", "punpckhhw", "punpckhwd", "punpcklbh",
"punpcklhw", "punpcklwd"};
for (i = 0; i < 5; i++)
{
int j;
instr_cop_chn cop_chn;
if (! fetch_cop_channels (long_opers, general[i].name, &cop_chn))
continue;
for (j = 0; j < 2; j++)
{
print_common_part_with_merge ("e2k_alopf11_opcode_templ",
custom_name[i][j], "parse_alf_args",
"merge_alopf11");
print_alf_part ("ALOPF11", "NO_MAS", cop_chn.cop, cop_chn.channels,
general[i].format);
print_alopf11_tail (custom_opce[j], cop_chn.channels, "EXT", 0);
print_final_part ();
}
}
for (i = 0; i < 19; i++)
{
instr_cop_chn cop_chn;
struct extra_alopf11_lit8 extra;
int need_extra_ind = is_alopf11_lit8 (others[i], &extra);
if (! fetch_cop_channels (long_opers, others[i], &cop_chn))
continue;
print_common_part_with_merge
((need_extra_ind
? "e2k_alopf11_lit8_opcode_templ"
: "e2k_alopf11_opcode_templ"),
others[i], "parse_alf_args", "merge_alopf11");
print_alf_part (need_extra_ind ? "ALOPF11_LIT8" : "ALOPF11", "NO_MAS",
cop_chn.cop, cop_chn.channels, "ARGS_DDD");
print_alopf11_tail ("NONE", cop_chn.channels, "EXT", 0);
if (need_extra_ind)
print_alopf11_lit8 (&extra);
print_final_part ();
}
}
gen_alopf11_loads ();
}
static void
gen_alopf11_with_flags ()
{
/* REMINDER: opcodes are normally taken from Tables B.1.1 and B.1.3. What
they are duplicated for in Table B.1.4 (see for example, ANDs_f{b,h,w})
is a non-trivial question . . .
At the same time for currently unsupported {INC,DEC}s_f{b,h,w} there are
no appropriate opcodes in B.1.1 and B.1.3, they should be fetched from
B.1.4 instead.
Note that the aforementioned `{INC,DEC}s_f{b,h,w}' as well as
`S,{{C,H}{L,R},AR}s_f{b,h,w}' are ALOPF21 in fact . . . */
static const char *opc_gen[] = {"adds", "subs", "umulx", "smulx", "ands",
"andns", "ors", "orns", "xors", "xorns"};
static const char *opc_v3[] = {"addd", "subd", "andd", "andnd", "ord",
"ornd", "xord", "xornd"};
static const char *opc2[4] = {"FLB", "FLH", "FLW", "FLD"};
static const char sfx[4] = {'b', 'h', 'w', 'd'};
static struct
{
const char **opc;
size_t num_opc;
const char *args_fmt;
size_t sfx_lo;
size_t sfx_hi;
}
groups[] =
{
{opc_gen, sizeof (opc_gen) / sizeof (opc_gen[0]), "ARGS_SSS", 0, 2},
{opc_v3, sizeof (opc_v3) / sizeof (opc_v3[0]), "ARGS_DDS", 3, 3},
};
size_t i;
size_t gidx;
/* FIXME: for a better symmetry one may consider `gidx = {0,1}' for each
`mcpu'. This shouldn't change anything. */
if (mcpu != 1 && mcpu != 3)
return;
gidx = mcpu == 1 ? 0 : 1;
for (i = 0; i < groups[gidx].num_opc; i++)
{
instr_cop_chn cop_chn;
size_t j;
/* Search flag opcodes and channels for the currently processed `mcpu'. */
if (! fetch_cop_channels (flag_opers, groups[gidx].opc[i], &cop_chn))
continue;
for (j = groups[gidx].sfx_lo; j <= groups[gidx].sfx_hi; j++)
{
char name[32];
sprintf (name, "%s_f%c", groups[gidx].opc[i], sfx[j]);
print_common_part_with_merge ("e2k_alopf11_opcode_templ", name,
"parse_alf_args", "merge_alopf11");
print_alf_part ("ALOPF11", "NO_MAS", cop_chn.cop, cop_chn.channels,
groups[gidx].args_fmt);
print_alopf11_tail ("NONE", cop_chn.channels, opc2[j], 0);
print_final_part ();
}
}
}
/* I don't know what name would be appropriate for this function but
among others it generates FSQRT{s,id} . . . */
static void
gen_alopf12_fsqrts ()
{
static const instr_format fsqrts[] =
{
{"frcps", "ARGS_SS"},
{"fsqrts", "ARGS_SS"},
{"fsqrtid", "ARGS_DD"},
{"frsqrts", "ARGS_SS"},
{"pfsqrts", "ARGS_SS"},
{"gettd", "ARGS_DD"},
{"gettags", "ARGS_SS"},
{"gettagd", "ARGS_DS"},
{"gettc", "ARGS_SD"},
{"invtc", "ARGS_SD"},
/* FIXME: I guess that this one along with several preceding instructions
isn't "fsqrt". Keep it here for now since within this function
appropriate values for `ALS.opce == ALES.opce == NONE' and `ALES.opc2
== EXT' are set. Anyway, this function is to be revisited along with
many others . . .
FIXME: implement extra verification taking into account that the use of
DST other than `%tst' with "puttst" is prohibited. */
{"puttst", "ARGS_DD"},
};
int i;
for (i = 0; i < sizeof (fsqrts) / sizeof (fsqrts[0]); i++)
{
instr_cop_chn cop_chn;
/* Fetch info on ALS.cop and allowed channels from the LONG opers
table. */
if (! fetch_cop_channels (long_opers, fsqrts[i].name, &cop_chn))
continue;
if (strcmp (fsqrts[i].name, "frcps") == 0)
{
/* According to Table B.1.1 ALS2 should be pretty valid . . . */
if (cop_chn.channels[2] == 0)
abort ();
cop_chn.channels[2] = 0;
}
else if (strcmp (fsqrts[i].name, "gettc") == 0
|| strcmp (fsqrts[i].name, "invtc") == 0)
{
if (cop_chn.channels[3] == 0)
abort ();
cop_chn.channels[3] = 0;
}
else if (strcmp (fsqrts[i].name, "puttst") == 0)
{
if (cop_chn.channels[0] == 0)
abort ();
cop_chn.channels[0] = 0;
}
print_common_part ("e2k_alopf12_opcode_templ", fsqrts[i].name,
"parse_alf_args");
print_alf_part ("ALOPF12", "NO_MAS", cop_chn.cop, cop_chn.channels,
fsqrts[i].format);
/* It's time to initialize `ALS.opce = 0xc0 (NONE)',
`ALES.opce = 0xc0 (NONE)' and `ALES.opc2 = 0x1 (EXT)'. */
printf (",0xc0,,0xc0,0x1");
print_final_part ();
}
}
/* PSHUFH is an ALOPF12 instruction which is to be parsed and encoded
in a special way. */
static void
gen_pshufh ()
{
instr_cop_chn cop_chn;
if (! fetch_cop_channels (long_opers, "pshufh", &cop_chn))
return;
print_common_part ("e2k_alopf12_opcode_templ", "pshufh", "parse_alf_args");
print_alf_part ("ALOPF12_PSHUFH", "NO_MAS", cop_chn.cop, cop_chn.channels,
/* I cannot understand according to iset.single which format
arguments of PSHUFH have, but according to sample .s-files
`pshufh,1 %dr3, 0x1b, %dr3' they look like double. Anyway
arguments are treated specially . . . */
"ARGS_DD");
/* I believe that `ALS.opce = 0xc0 (NONE)' because nothing else is said about
it, `ALES.opce should be determined from a literal8 argument' and
`ALES.opc2 = 0x1 (EXT)'. */
printf (",0xc0,,0x0,0x1");
print_final_part ();
}
static void
gen_alopf12 ()
{
/* These instructions have ALS.opce == USD == 0xec (see B.5 in
iset.single). */
static const instr_format *crnt, usd_instrs [] =
{
{"getsod", "ARGS_QQ"},
{"getsp", "ARGS_SD"},
};
size_t i;
gen_alopf12_fsqrts ();
for (i = 0; i < sizeof (usd_instrs) / sizeof (usd_instrs[0]); i++)
{
instr_cop_chn cop_chn;
crnt = &usd_instrs[i];
/* Fetch info on ALS.cop and allowed channels from the LONG opers
table. */
if (! fetch_cop_channels (long_opers, crnt->name, &cop_chn))
continue;
print_common_part ("e2k_alopf12_opcode_templ", crnt->name,
"parse_alf_args");
print_alf_part ("ALOPF12", "NO_MAS", cop_chn.cop, cop_chn.channels,
crnt->format);
/* It's time to initialize `ALS.opce = 0xec', `ALES.opce = 0xc0 (NONE)'
and `ALES.opc2 = 0x1 (EXT)'. */
printf (",0xec,,0xc0,0x1");
print_final_part ();
}
{
static const instr_format insns[] =
{
{"qpsgn2mskb", "ARGS_PS"},
{"qpswitchw", "ARGS_PP"},
{"qpswitchd", "ARGS_PP"},
};
for (i = 0; i < sizeof (insns) / sizeof (insns[0]); i++)
{
instr_cop_chn cop_chn;
crnt = &insns[i];
/* Fetch info on ALS.cop and allowed channels from the EXT1 LONG
opers table. */
if (! fetch_cop_channels (long_ext1_opers, crnt->name, &cop_chn))
continue;
print_common_part ("e2k_alopf12_opcode_templ", crnt->name,
"parse_alf_args");
print_alf_part ("ALOPF12", "NO_MAS", cop_chn.cop, cop_chn.channels,
crnt->format);
/* Here I print ALS.opce (I guess that it's NONE, because I've been
unable to find another value in iset-v5.single), ALES.opce and
ALES.opc2. */
printf (",NONE,,NONE,EXT1");
print_final_part ();
}
}
{
/* Note that these ones may also accept the first operand of quad
format. */
static const instr_format insns[] =
{
{"ibranchd", "ARGS_DD"},
{"icalld", "ARGS_DD"},
};
const char *alopf[] = {"ALOPF12_IBRANCHD", "ALOPF12_ICALLD"};
for (i = 0; i < sizeof (insns) / sizeof (insns[0]); i++)
{
instr_cop_chn cop_chn;
crnt = &insns[i];
/* Fetch info on ALS.cop and allowed channels from the EXT LONG
opers table. */
if (! fetch_cop_channels (long_opers, crnt->name, &cop_chn))
continue;
/* IBRANCHD and ICALLD are said to be implemented in ALC0 only.
Fortunately, they don't require a pair of `ALC{0,3}' channels if
the first operand is of quad format. */
cop_chn.channels[3] = 0;
print_common_part ("e2k_alopf12_opcode_templ", crnt->name,
"parse_alf_args");
print_alf_part (alopf[i], "NO_MAS", cop_chn.cop, cop_chn.channels,
crnt->format);
/* ALS.opce == NONE for IBRANCHD, but will be replaced with WBS for
ICALLD. ALES.opce is likely to be NONE for both of them, while
ALES.opc2 is explicitly said to be EXT for both instructions. */
printf (",NONE,,NONE,EXT");
print_final_part ();
}
}
{
static struct
{
const char *sfx;
int ales_opce;
}
qpcext_info[] =
{
{"0x00", 0xc0}, {"0x7f", 0xc2}, {"0x80", 0xc4}, {"0xff", 0xc6}
};
instr_cop_chn cop_chn;
/* QPCEXT comes from EXT1 table, but should I make use of this fact
here? The same question applies to many other places where I
explicitly search a particular `EXTj' table. */
if (fetch_cop_channels (long_ext1_opers, "qpcext", &cop_chn))
{
for (i = 0; i < sizeof (qpcext_info) / sizeof (qpcext_info[0]); i++)
{
char name[16];
sprintf (name, "qpcext_%s", qpcext_info[i].sfx);
print_common_part ("e2k_alopf12_opcode_templ", name,
"parse_alf_args");
print_alf_part ("ALOPF12", "NO_MAS", cop_chn.cop, cop_chn.channels,
"ARGS_DP");
/* Here I print ALS.opce (I guess that it's NONE, because I've been
unable to find another value in iset-v5.single), ALES.opce and
ALES.opc2. */
printf (",NONE,,0x%x,EXT1", qpcext_info[i].ales_opce);
print_final_part ();
}
}
}
{
/* See Table B.3.2 */
static const struct {
u_int8_t opce;
const char *name;
const char *arg_fmt;
} qptoqp_transforms[] =
{{0xc8, "qpfstois", "ARGS_PP"},
{0xca, "qpfstoistr", "ARGS_PP"},
{0xcc, "qpistofs", "ARGS_PP"},
{0xd8, "qpfstoid", "ARGS_DP"},
{0xda, "qpfstoidtr", "ARGS_DP"},
{0xdc, "qpistofd", "ARGS_DP"},
{0xde, "qpfstofd", "ARGS_DP"},
{0xe8, "qpfdtois", "ARGS_PD"},
{0xea, "qpfdtoistr", "ARGS_PD"},
{0xec, "qpidtofs", "ARGS_PD"},
{0xee, "qpfdtofs", "ARGS_PD"},
{0xf8, "qpfdtoid", "ARGS_PP"},
{0xfa, "qpfdtoidtr", "ARGS_PP"},
{0xfc, "qpidtofd", "ARGS_PP"}
};
instr_cop_chn cop_chn;
/* This will succeed only for elbrus-v5. */
if (fetch_cop_channels (long_ext1_opers, "qptoqp", &cop_chn))
{
for (i = 0;
i < sizeof (qptoqp_transforms) / sizeof (qptoqp_transforms[0]);
i++)
{
print_common_part ("e2k_alopf12_opcode_templ",
qptoqp_transforms[i].name, "parse_alf_args");
print_alf_part ("ALOPF12", "NO_MAS", cop_chn.cop, cop_chn.channels,
qptoqp_transforms[i].arg_fmt);
/* ALS.opce, ALES.opce and ALES.opc2 */
printf (",0x%x,,NONE,EXT1", qptoqp_transforms[i].opce);
print_final_part ();
}
}
}
gen_pshufh ();
}
static void
gen_alopf13 ()
{
static const instr_format stores[] =
{
{"stcsq", "ARGS_SSQ"},
{"stdsq", "ARGS_SSQ"},
{"stesq", "ARGS_SSQ"},
{"stfsq", "ARGS_SSQ"},
{"stgsq", "ARGS_SSQ"},
{"stssq", "ARGS_SSQ"},
{"strd", "ARGS_DDD"},
{"stgdb", "ARGS_SSS"},
{"stgdh", "ARGS_SSS"},
{"stgdw", "ARGS_SSS"},
{"stgdd", "ARGS_SSD"},
{"stgdq", "ARGS_SSQ"},
{"stgdmqp", "ARGS_SSP"},
{"stgdqp", "ARGS_SSP"},
{"stapb", "ARGS_QSS"},
{"staph", "ARGS_QSS"},
{"stapw", "ARGS_QSS"},
{"stapd", "ARGS_QSD"},
{"stapq", "ARGS_QSQ"},
{"stapqp", "ARGS_QSP"},
{"stapmqp", "ARGS_QSP"},
{"stq", "ARGS_DDQ"},
{"stmqp", "ARGS_DDP"},
{"stqp", "ARGS_DDP"},
{"stodpb", "ARGS_QSS"},
{"stodpd", "ARGS_QSD"},
{"stodph", "ARGS_QSS"},
{"stodpq", "ARGS_QSQ"},
{"stodpw", "ARGS_QSS"},
{"stodrb", "ARGS_QSS"},
{"stodrd", "ARGS_QSD"},
{"stodrh", "ARGS_QSS"},
{"stodrq", "ARGS_QSQ"},
{"stodrw", "ARGS_QSS"},
{"stodwb", "ARGS_QSS"},
{"stodwd", "ARGS_QSD"},
{"stodwh", "ARGS_QSS"},
{"stodwq", "ARGS_QSQ"},
{"stodww", "ARGS_QSS"},
{"stcsmqp", "ARGS_DDP"},
{"stcsqp", "ARGS_DDP"},
{"stdsmqp", "ARGS_DDP"},
{"stdsqp", "ARGS_DDP"},
{"stesmqp", "ARGS_SSP"},
{"stesqp", "ARGS_SSP"},
/* What do they mean by specifying `r, ddq' (i.e. not just `ddq') as
format of arguments for FS and GS operations in `iset-v5.single'? */
{"stfsmqp", "ARGS_DDP"},
{"stfsqp", "ARGS_DDP"},
{"stgsmqp", "ARGS_DDP"},
{"stgsqp", "ARGS_DDP"},
{"stssmqp", "ARGS_DDP"},
{"stssqp", "ARGS_DDP"},
{"strqp", "ARGS_DDP"},
};
int j;
for (j = 0; j < 2; j++)
{
int i;
const e2k_oper_table *lopers = j == 0 ? long_opers : long_ext1_opers;
const char *ext = j == 0 ? "EXT" : "EXT1";
for (i = 0; i < sizeof (stores) / sizeof (stores[0]); i++)
{
instr_cop_chn cop_chn;
/* Fetch info on ALS.cop and allowed channels from the LONG opers
table. */
if (! fetch_cop_channels (lopers, stores[i].name, &cop_chn))
continue;
print_common_part ("e2k_alopf13_opcode_templ", stores[i].name,
"parse_alf_args");
print_alf_part ("ALOPF13", "MAS", cop_chn.cop, cop_chn.channels,
stores[i].format);
/* `ALEF2.opce = 0xc0 (NONE)' and `ALES.opc2 = 0x1 (EXT)'. */
printf (",,,NONE,%s", ext);
print_final_part ();
}
}
}
static void
gen_alopf15 ()
{
instr_format rr [] =
{
{"rws", "ARGS_S"},
{"rwd", "ARGS_D"}
};
int i;
for (i = 0; i < 2; i++)
{
/* Despite the contents of the long operations table these instructions
are implemented in the zeroth channel only. */
static const int available_channels[6] = {1, 0, 0, 0, 0, 0};
instr_cop_chn cop_chn;
instr_format *crnt = &rr[i];
if (! fetch_cop_channels (long_opers, crnt->name, &cop_chn))
continue;
print_common_part ("e2k_alopf15_opcode_templ", crnt->name,
"parse_alf_args");
print_alf_part ("ALOPF15", "NO_MAS", cop_chn.cop, available_channels,
crnt->format);
/* Currently we don't encode anything else. Code in `tc-e2k.c' sets
ALEF2.{opc2,opce} to their "default" values itself. */
print_final_part ();
}
}
static void
gen_alopf16 ()
{
instr_format rr [] =
{
{"rrs", "ARGS_S"},
{"rrd", "ARGS_D"}
};
int i;
for (i = 0; i < 2; i++)
{
/* Despite the contents of the long operations table these instructions
are implemented in the zeroth channel only. */
static const int available_channels[6] = {1, 0, 0, 0, 0, 0};
instr_cop_chn cop_chn;
instr_format *crnt = &rr[i];
if (! fetch_cop_channels (long_opers, crnt->name, &cop_chn))
continue;
print_common_part ("e2k_alopf16_opcode_templ", crnt->name,
"parse_alf_args");
print_alf_part ("ALOPF16", "NO_MAS", cop_chn.cop, available_channels,
crnt->format);
/* Currently we don't encode anything else. Code in `tc-e2k.c' sets
ALEF2.{opc2,opce} to their "default" values itself. */
print_final_part ();
}
}
static void
output_alopf21_insn (const char *name, int cop, const int *channels,
const char *arg_fmts, const char *opc2)
{
print_common_part ("e2k_alopf21_opcode_templ", name, "parse_alf_args");
print_alf_part ("ALOPF21", "NO_MAS", cop, channels, arg_fmts);
/* Now print ALEF1.opc2. */
printf (",,,,%s", opc2);
print_final_part ();
}
typedef struct
{
char name[64];
int channels[6];
const char *arg_fmts;
} combined_table_entry;
typedef struct
{
combined_table_entry entry[128];
} combined_table;
static void
add_combined_table_entry_by_idx (combined_table *dst, int idx,
const char *name, const int channels[],
const char *args_fmt)
{
combined_table_entry *entry = &dst->entry[idx];
strcpy (entry->name, name);
memcpy (entry->channels, channels, sizeof (entry->channels));
entry->arg_fmts = args_fmt;
}
static void
add_combined_table_entry (combined_table *dst, int scnd, int frst, int fmt,
const char *name, const int channels[],
const char *args_fmt)
{
int idx = 32 * scnd + 2 * frst + fmt;
add_combined_table_entry_by_idx (dst, idx, name, channels, args_fmt);
}
static void
add_empty_combined_table_entry_by_idx (combined_table *dst, int idx)
{
static int no_channels[6] = {0, 0, 0, 0, 0, 0};
/* Let an empty `name' be a sign of an empty entry. */
add_combined_table_entry_by_idx (dst, idx, "", no_channels, "");
}
static void
add_empty_combined_table_entry (combined_table *dst, int scnd, int frst,
int fmt)
{
int idx = 32 * scnd + 2 * frst + fmt;
add_empty_combined_table_entry_by_idx (dst, idx);
}
static void
fill_combined_table (int cpu, combined_table *dst, const char *prefix,
const char *frst_stage[], const char *scnd_stage[],
const int channels[], const char *sfmt, const char *dfmt,
int (* callback) (int, const char *, const char *, int))
{
int i;
for (i = 0; i < 4; i++)
{
int j;
for (j = 0; j < 16; j++)
{
int k;
for (k = 0; k < 2; k++)
{
if (scnd_stage[i][0] == '\0' || frst_stage[j][0] == '\0'
|| (callback != NULL
&& callback (cpu, scnd_stage[i], frst_stage[j], k) == 0))
add_empty_combined_table_entry (dst, i, j, k);
else
{
char name[64];
sprintf (name, "%s%s_%s%s", prefix, frst_stage[j],
scnd_stage[i], k == 0 ? "s" : "d");
add_combined_table_entry (dst, i, j, k, name, channels,
k == 0 ? sfmt : dfmt);
}
}
}
}
}
static void
fill_icmb (int cpu, combined_table *dst, const char *frst_stage[],
const char *scnd_stage[])
{
/* A subset of channels appropriate for ICMB{0,1,2,3} seems to be
identical across `elbrus-v{1,2,3,4,5}'. */
static const int channels[6] = {0, 1, 0, 0, 1, 0};
fill_combined_table (cpu, dst, "", frst_stage, scnd_stage, channels,
"ARGS_SSSS", "ARGS_DDDD", NULL);
}
static void
fill_icmb012 (int cpu, combined_table *dst, const char *scnd_stage[])
{
/* The corresponding Table B.1.5.2 seems to be identical across `iset-v{1,2,
3,4,5}.single'. If that changes, replace this array with a function filling
in one depending on `mcpu' value. */
static const char *frst_stage[] = {
"and", "andn", "or", "orn", "xor", "xorn", "", "merge", "add", "sub", "scl",
"scr", "shl", "shr", "sar", "getf"};
fill_icmb (cpu, dst, frst_stage, scnd_stage);
}
#define __elbrus_v1(s) cpu == 1 ? s : ""
#define __elbrus_v3(s) cpu >= 3 ? s : ""
#define __elbrus_v5(s) cpu >= 5 ? s : ""
static void
fill_icmb0 (int cpu, combined_table *dst)
{
static const char *scnd_stage[] = {"and", "andn", "or", "orn"};
fill_icmb012 (cpu, dst, scnd_stage);
}
static void
fill_icmb1 (int cpu, combined_table *dst)
{
const char *scnd_stage[] = {"xor", "xorn", "rsub",
__elbrus_v1 ("merge")};
fill_icmb012 (cpu, dst, scnd_stage);
}
static void
fill_icmb2 (int cpu, combined_table *dst)
{
const char *scnd_stage[] = {"add", "sub", cpu == 1 ? "scl" : "",
__elbrus_v1 ("scr")};
fill_icmb012 (cpu, dst, scnd_stage);
}
static void
fill_icmb3 (int cpu, combined_table *dst)
{
static const int insf_channels[] = {1, 1, 0, 1, 1, 0};
const char *frst_stage[] =
{
__elbrus_v1 ("and"),
__elbrus_v1 ("andn"),
__elbrus_v1 ("or"),
__elbrus_v1 ("orn"),
__elbrus_v1 ("xor"),
__elbrus_v1 ("xorn"),
"",
__elbrus_v1 ("merge"),
__elbrus_v1 ("add"),
__elbrus_v1 ("sub"),
__elbrus_v1 ("scl"),
__elbrus_v1 ("scr"),
__elbrus_v1 ("shl"),
__elbrus_v1 ("shr"),
__elbrus_v1 ("sar"),
__elbrus_v1 ("getf")
};
const char *scnd_stage[] =
{
__elbrus_v1 ("shl"),
__elbrus_v1 ("shr"),
__elbrus_v1 ("sar"),
__elbrus_v1 ("getf")
};
fill_icmb (cpu, dst, frst_stage, scnd_stage);
/* Interestingly enough, according to `iset-v1.single' I can use any
2nd_stage value I like to encode INSF, while `iset-v{X >= 2}.single'
enforce the usage of `2nd_stage == 3'. Use `3' to be compatible with all
istruction sets. */
add_combined_table_entry (dst, 3, 6, 0, "insfs", insf_channels, "ARGS_SSSS");
add_combined_table_entry (dst, 3, 6, 1, "insfd", insf_channels, "ARGS_DDDD");
}
static int
fcmb_callback (int cpu, const char *scnd, const char *frst, int fmt)
{
int i;
if (cpu != 1)
return 1;
/* For elbrus-v1 take into account that annoying constraint next to the
Tables B.1.7.4 and B.1.8.4 in `iset-v1.single'. */
for (i = 0; i < 2; i++)
{
const char *swp;
/* Note that the actual `frst' can never be `rsub'. However, I take into
account that `frst' and `scnd' are swapped below. */
if ((strcmp (frst, "add") == 0 || strcmp (frst, "sub") == 0 ||
strcmp (frst, "rsub") == 0)
&& strcmp (scnd, "mul") == 0)
return 1;
/* Swap `frst' and `scnd': this lets me avoid duplication of `strcmp's. */
swp = scnd;
scnd = frst;
frst = swp;
}
return 0;
}
static void
fill_fcmb (int cpu, combined_table *dst, const char *scnd_stage[])
{
static const char *frst_stage[] = {"add", "sub", "", "", "mul", "", "", "",
"", "", "", "", "", "", "", ""};
static const int channels_0134[6] = {1, 1, 0, 1, 1, 0};
static const int channels_all[6] = {1, 1, 1, 1, 1, 1};
fill_combined_table (cpu, dst, "f", frst_stage, scnd_stage,
cpu < 4 ? channels_0134 : channels_all,
"ARGS_SSSS", "ARGS_DDDD", &fcmb_callback);
}
static void
fill_fcmb0 (int cpu, combined_table *dst)
{
static const char *scnd_stage[] = {"add", "sub", "", ""};
fill_fcmb (cpu, dst, scnd_stage);
}
static void
fill_fcmb1 (int cpu, combined_table *dst)
{
const char *scnd_stage[] =
{
__elbrus_v1 ("mul"),
"rsub",
"",
""
};
fill_fcmb (cpu, dst, scnd_stage);
}
/* Constraints imposed on `PFCMBx' and `QPFCMBx' seem to be identical except
for the fact that no constraint is imposed on ADDSUB in the latter case. See
notes next to Tables B.1.8.5 and B.1.9.5. */
static int
common_pfcmb_qpfcmb (int cpu, const char *scnd, const char *frst, int fmt,
int is_qpfcmb)
{
if (cpu >= 3 && fmt == 1)
{
int i;
const char *subop[] = {scnd, frst};
for (i = 0; i < 2; i++)
{
static const char *dbl_prohibit[] = {"hadd", "hsub", "addsub"};
int j;
for (j = 0; j < (is_qpfcmb ? 2 : 3); j++)
{
if (strcmp (subop[i], dbl_prohibit[j]) == 0)
return 0;
}
}
}
return 1;
}
static int
pfcmb_callback (int cpu, const char *scnd, const char *frst, int fmt)
{
/* Note that I reuse `fcmb_callback' here because additional constraints
imposed on `PFCMBx' operations on elbrus-v1 seem to be identical to those
imposed on `FCMBx'. */
if (cpu == 1)
return fcmb_callback (cpu, scnd, frst, fmt);
return common_pfcmb_qpfcmb (cpu, scnd, frst, fmt, 0);
}
static void
fill_pfcmb (int cpu, combined_table *dst, const char *scnd_stage[])
{
const char *frst_stage[] =
{
"add",
"sub",
__elbrus_v3 ("hadd"),
__elbrus_v3 ("hsub"),
"mul",
"",
"",
__elbrus_v3 ("addsub"),
"", "", "", "", "", "", "", ""
};
static const int channels_0134[6] = {1, 1, 0, 1, 1, 0};
static const int channels_all[6] = {1, 1, 1, 1, 1, 1};
fill_combined_table (cpu, dst, "pf", frst_stage, scnd_stage,
cpu < 4 ? channels_0134 : channels_all,
"ARGS_DDDD", "ARGS_DDDD", &pfcmb_callback);
}
static void
fill_pfcmb0 (int cpu, combined_table *dst)
{
const char *scnd_stage[] =
{
"add",
"sub",
__elbrus_v3 ("hadd"),
__elbrus_v3 ("hsub")
};
fill_pfcmb (cpu, dst, scnd_stage);
}
static void
fill_pfcmb1 (int cpu, combined_table *dst)
{
const char *scnd_stage[] =
{
__elbrus_v1 ("mul"),
"rsub",
"",
__elbrus_v3 ("addsub")
};
fill_pfcmb (cpu, dst, scnd_stage);
/* These ones have been implemented since `elbrus-v2'. */
if (cpu >= 2)
{
static const int channels_0134[] = {1, 1, 0, 1, 1, 0};
add_combined_table_entry (dst, 2, 6, 1, "pshufb", channels_0134,
"ARGS_DDDD");
add_combined_table_entry (dst, 3, 6, 1, "pmerge", channels_0134,
"ARGS_DDDD");
}
}
static int
qpfcmb_callback (int cpu, const char *scnd, const char *frst, int fmt)
{
return common_pfcmb_qpfcmb (cpu, scnd, frst, fmt, 1);
}
static void
fill_qpfcmb (int cpu, combined_table *dst, const char *scnd_stage[])
{
const char *frst_stage[] =
{
__elbrus_v5 ("add"),
__elbrus_v5 ("sub"),
__elbrus_v5 ("hadd"),
__elbrus_v5 ("hsub"),
__elbrus_v5 ("mul"),
"",
"",
__elbrus_v5 ("addsub"),
"", "", "", "", "", "", "", ""
};
static const int channels_all[6] = {1, 1, 1, 1, 1, 1};
/* Note that I reuse `fcmb_callback' here because additional constraints
imposed on `PFCMBx' operations on elbrus-v1 seem to be identical to those
imposed on `FCMBx'. */
fill_combined_table (cpu, dst, "qpf", frst_stage, scnd_stage,
channels_all, "ARGS_PPPP", "ARGS_PPPP",
&qpfcmb_callback);
}
static void
fill_qpfcmb0 (int cpu, combined_table *dst)
{
const char *scnd_stage[] =
{
__elbrus_v5 ("add"),
__elbrus_v5 ("sub"),
__elbrus_v5 ("hadd"),
__elbrus_v5 ("hsub")
};
fill_qpfcmb (cpu, dst, scnd_stage);
}
static void
fill_qpfcmb1 (int cpu, combined_table *dst)
{
const char *scnd_stage[] =
{
"",
__elbrus_v5 ("rsub"),
"",
__elbrus_v5 ("addsub")
};
fill_qpfcmb (cpu, dst, scnd_stage);
/* Note that for CPU's prior to `elbrus-v5' the above code fills in an empty
table in fact. Non-standard instructions should be added only for
`elbrus-v5', of course. */
if (cpu >= 5)
{
static const int channels_0134[] = {1, 1, 0, 1, 1, 0};
add_combined_table_entry (dst, 0, 6, 1, "qppermb", channels_0134,
"ARGS_PPPP");
add_combined_table_entry (dst, 2, 6, 1, "qpshufb", channels_0134,
"ARGS_PPPP");
add_combined_table_entry (dst, 3, 6, 1, "qpmerge", channels_0134,
"ARGS_PPPP");
}
}
static void
fill_logical_combined_table (int cpu, combined_table *dst, const char *prefix,
int addend, int ident, const char *fmt)
{
int i;
static const int channels[] = {1, 1, 0, 1, 1, 0};
for (i = 0; i < 128; i++)
{
int idx = i + addend;
char sfx[16];
if (cpu < 5)
sfx[0] = '\0';
else if (ident == 0)
sprintf (sfx, "%d", idx);
else if (ident == 1)
sprintf (sfx, "0x%02x", idx);
else
{
const char *human_sfx;
switch (idx)
{
case 0x80:
human_sfx = "and";
break;
case 0x96:
human_sfx = "xor";
break;
case 0xd8:
human_sfx = "sel3";
break;
case 0xe8:
human_sfx = "mjr";
break;
case 0xfe:
human_sfx = "or";
break;
default:
human_sfx = "";
}
strcpy (sfx, human_sfx);
}
if (sfx[0] == '\0')
add_empty_combined_table_entry_by_idx (dst, i);
else
{
char name[64];
sprintf (name, "%slog_%s", prefix, sfx);
add_combined_table_entry_by_idx (dst, i, name, channels, fmt);
}
}
}
static void
fill_plog_lcmbd0_dec (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "p", 0, 0, "ARGS_DDDD");
}
static void
fill_plog_lcmbd0_hex (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "p", 0, 1, "ARGS_DDDD");
}
static void
fill_plog_lcmbd0_named (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "p", 0, 2, "ARGS_DDDD");
}
static void
fill_plog_lcmbd1_dec (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "p", 128, 0, "ARGS_DDDD");
}
static void
fill_plog_lcmbd1_hex (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "p", 128, 1, "ARGS_DDDD");
}
static void
fill_plog_lcmbd1_named (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "p", 128, 2, "ARGS_DDDD");
}
static void
fill_qplog_lcmbq0_dec (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "qp", 0, 0, "ARGS_DDDD");
}
static void
fill_qplog_lcmbq0_hex (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "qp", 0, 1, "ARGS_DDDD");
}
static void
fill_qplog_lcmbq0_named (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "qp", 0, 2, "ARGS_DDDD");
}
static void
fill_qplog_lcmbq1_dec (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "qp", 128, 0, "ARGS_DDDD");
}
static void
fill_qplog_lcmbq1_hex (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "qp", 128, 1, "ARGS_DDDD");
}
static void
fill_qplog_lcmbq1_named (int cpu, combined_table *dst)
{
fill_logical_combined_table (cpu, dst, "qp", 128, 2, "ARGS_DDDD");
}
/* FIXME: this currently duplicates the functionality of `print_common_part_
minimal ()'. When the latter is fixed in respect of `mcpu' conditionals
it should be used from here. */
static void
print_combined_table (const char *opc2, const combined_table *table,
int first, int last)
{
int i;
char cond[64];
if (first == 1 && last == 6)
sprintf(cond,">=1");
else if (first == last)
sprintf(cond,"=%d", first);
else if (last == 6)
sprintf(cond,">=%d",first);
else
sprintf(cond,">=%d <=%d", first, last);
for (i = 0; i < 128; i++)
{
const char *mrg;
const char *name = table->entry[i].name;
int merge = 0;
if (name[0] == '\0')
continue;
/* Be very careful so as not to mark `{q,}pmerge' and other similar
* "combined" instructions not really making use of MRGC as
* ALOPF21_MERGE. */
mrg = strstr (name, "merge");
if (mrg
&& ((mrg > name && mrg[-1] == '_'
&& (mrg[5] == 's' || mrg[5] == 'd'))
|| (mrg == name && mrg[5] == '_')))
merge = 1;
printf("%s,%s,merge_alopf_simple",cond,table->entry[i].name);
print_alf_part (merge ? "ALOPF21_MERGE" : "ALOPF21", "NO_MAS", i,
table->entry[i].channels, table->entry[i].arg_fmts);
/* Now print ALEF1.opc2. */
printf (",,,,%s", opc2);
print_final_part ();
}
}
static void
intersect (combined_table *a, const combined_table *b)
{
int i;
for (i = 0; i < 128; i++)
{
int j;
int demolish = 1;
if (a->entry[i].name[0] == '\0'
|| b->entry[i].name[0] == '\0'
|| strcmp (a->entry[i].name, b->entry[i].name))
{
a->entry[i].name[0] = '\0';
continue;
}
if (a->entry[i].arg_fmts != b->entry[i].arg_fmts)
abort ();
for (j = 0; j < 6; j++)
{
a->entry[i].channels[j] *= b->entry[i].channels[j];
if (a->entry[i].channels[j] != 0)
demolish = 0;
}
if (demolish)
a->entry[i].name[0] = '\0';
}
}
static void
subtract (combined_table *a, const combined_table *b)
{
int i;
for (i = 0; i < 128; i++)
{
int j;
int demolish = 1;
if (b->entry[i].name[0] == '\0')
continue;
if (a->entry[i].name[0] == '\0'
|| strcmp (a->entry[i].name, b->entry[i].name)
|| a->entry[i].arg_fmts != b->entry[i].arg_fmts)
abort ();
for (j = 0; j < 6; j++)
{
if (a->entry[i].channels[j] == 0 && b->entry[i].channels[j] == 1)
abort ();
a->entry[i].channels[j] -= b->entry[i].channels[j];
if (a->entry[i].channels[j] != 0)
demolish = 0;
}
if (demolish)
a->entry[i].name[0] = '\0';
}
}
static void
gen_alopf21_combined ()
{
static void (*fill[]) (int, combined_table *) = {
&fill_icmb0, &fill_icmb1, &fill_icmb2, &fill_icmb3, &fill_fcmb0,
&fill_fcmb1, &fill_pfcmb0, &fill_pfcmb1, &fill_qpfcmb0, &fill_qpfcmb1,
&fill_plog_lcmbd0_dec, &fill_plog_lcmbd0_hex,
&fill_plog_lcmbd1_dec, &fill_plog_lcmbd1_hex, &fill_plog_lcmbd1_named,
&fill_qplog_lcmbq0_dec, &fill_qplog_lcmbq0_hex,
&fill_qplog_lcmbq1_dec, &fill_qplog_lcmbq1_hex, &fill_qplog_lcmbq1_named
};
static const char *opc2[] = {"ICMB0", "ICMB1", "ICMB2", "ICMB3", "FCMB0",
"FCMB1", "PFCMB0", "PFCMB1", "QPFCMB0",
"QPFCMB1",
"LCMBD0", "LCMBD0", "LCMBD1", "LCMBD1", "LCMBD1",
"LCMBQ0", "LCMBQ0", "LCMBQ1", "LCMBQ1", "LCMBQ1"
};
int i;
for (i = 0; i < sizeof (fill) / sizeof (fill[0]); i++)
{
combined_table tables[6];
int j;
for (j = 0; j < 6; j++)
(*fill[i]) (j + 1, &tables[j]);
for (j = 0; j < 6; j++)
{
int k;
combined_table tmp = tables[j];
for (k = j + 1; k < 6; k++)
{
intersect (&tmp, &tables[k]);
subtract (&tables[j], &tmp);
/* `tables[j]' contains a subset of instructions supported by
`mcpu = j + 1' missing in `tables[k]', i.e. those ones
unsupported by `mcpu = k + 1'. Therefore, the last `mcpu'
which supports them is `k'. */
print_combined_table (opc2[i], &tables[j], j + 1, k);
subtract (&tables[k], &tmp);
tables[j] = tmp;
}
print_combined_table (opc2[i], &tables[j], j + 1, 6);
}
}
}
static void
gen_alopf21_with_flags ()
{
static const char *opc_gen[] = {"incs", "decs", "shls", "shrs", "scls",
"scrs", "sars"};
static const char *opc_v3[] = {"incd", "decd", "shld", "shrd", "scld",
"scrd", "sard"};
static const char *opc2[4] = {"FLB", "FLH", "FLW", "FLD"};
static const char sfx[4] = {'b', 'h', 'w', 'd'};
static struct
{
const char **opc;
size_t num_opc;
const char *args_fmt;
size_t sfx_lo;
size_t sfx_hi;
}
groups[] =
{
{opc_gen, sizeof (opc_gen) / sizeof (opc_gen[0]), "ARGS_SSS", 0, 2},
{opc_v3, sizeof (opc_v3) / sizeof (opc_v3[0]), "ARGS_DDS", 3, 3},
};
size_t i;
size_t gidx;
/* FIXME: for a better symmetry one may consider `gidx = {0,1}' for each
`mcpu'. This shouldn't change anything. */
if (mcpu != 1 && mcpu != 3)
return;
gidx = mcpu == 1 ? 0 : 1;
for (i = 0; i < groups[gidx].num_opc; i++)
{
instr_cop_chn cop_chn;
size_t j;
/* Search flag opcodes and channels for the currently processed `mcpu'. */
if (! fetch_cop_channels (flag_opers, groups[gidx].opc[i], &cop_chn))
continue;
for (j = groups[gidx].sfx_lo; j <= groups[gidx].sfx_hi; j++)
{
char name[32];
sprintf (name, "%s_f%c", groups[gidx].opc[i], sfx[j]);
output_alopf21_insn (name, cop_chn.cop, cop_chn.channels,
groups[gidx].args_fmt, opc2[j]);
}
}
}
static void
gen_alopf21_custom ()
{
size_t i;
static const instr_format instrs[] =
{
{"addcd", "ARGS_DDSD"},
{"addcd_c", "ARGS_DDSD"},
{"subcd", "ARGS_DDSD"},
{"subcd_c", "ARGS_DDSD"},
{"fmas", "ARGS_SSSS"},
{"fmss", "ARGS_SSSS"},
{"fnmas", "ARGS_SSSS"},
{"fnmss", "ARGS_SSSS"},
{"fmad", "ARGS_DDDD"},
{"fmsd", "ARGS_DDDD"},
{"fnmad", "ARGS_DDDD"},
{"fnmsd", "ARGS_DDDD"},
{"qpfmas", "ARGS_PPPP"},
{"qpfmss", "ARGS_PPPP"},
{"qpfnmas", "ARGS_PPPP"},
{"qpfnmss", "ARGS_PPPP"},
{"qpfmad", "ARGS_PPPP"},
{"qpfmsd", "ARGS_PPPP"},
{"qpfnmad", "ARGS_PPPP"},
{"qpfnmsd", "ARGS_PPPP"},
{"qpfmass", "ARGS_PPPP"},
{"qpfmsas", "ARGS_PPPP"},
{"qpfmasd", "ARGS_PPPP"},
{"qpfmsad", "ARGS_PPPP"},
};
for (i = 0; i < 2; i++)
{
int j;
const e2k_oper_table *tables[] = {long_opers, long_ext2_opers};
const char *exts[] = {"EXT", "EXT2"};
for (j = 0; j < sizeof (instrs) / sizeof (instrs[0]); j++)
{
instr_cop_chn cop_chn;
if (! fetch_cop_channels (tables[i], instrs[j].name, &cop_chn))
continue;
output_alopf21_insn (instrs[j].name, cop_chn.cop, cop_chn.channels,
instrs[j].format, exts[i]);
}
}
}
static void
gen_alopf22 ()
{
/* Encoding all flavours of MOVTq. */
static const struct {
const char *name;
int opce;
} custom_movtq[] =
{
/* opce = mv */
{"movtq", 0xc0},
/* opce = mvc */
{"movtcq", 0xc1},
/* opce = mvr */
{"movtrq", 0xc2},
/* opce = mvrc */
{"movtrcq", 0xc3}
};
int i;
instr_cop_chn cop_chn;
if (! fetch_cop_channels (long_opers, "movtq", &cop_chn))
return;
for (i = 0; i < 4; i++)
{
/* No `e2k_alopf22_opcode_templ' has been introduced yet because
for the supported ALOPF22 instructions (these are all flavours of
MOVTq) ALEF2.{opce,opc2} have their "default" values which are set
in `parse_alf2_args ()'. */
print_common_part ("e2k_alf2_opcode_templ", custom_movtq[i].name,
"parse_alf_args");
print_alf_part ("ALOPF22", "NO_MAS", cop_chn.cop, cop_chn.channels,
"ARGS_QQ");
/* It's time to initialize ALF2.opce now. */
printf (",0x%x", custom_movtq[i].opce);
print_final_part ();
}
}
void
print_usage (int used)
{
int i;
fprintf (stderr, "%s operations:\n", used ? "Used" : "Unused");
for (i = 0; i < 6; i++)
{
int j;
/* Don't care about `flag_opers' for now: their impact is
insignificant. This is why I use `j < 4', not `j < 5' here. */
for (j = 0; j < 4; j++)
{
int k;
const e2k_oper_table *table = &oper_tables[i][j];
for (k = 0; k < oper_tables[i][j].size; k++)
{
int l;
e2k_oper *entry = &table->entries[k];
for (l = 0; l < 3; l++)
{
const char *name = entry->opers[l].name;
int usage = entry->opers[l].usage;
if (name[0] != '\0')
{
if ((used && usage > 0) || (!used && usage == 0))
fprintf (stderr, "%s\n", name);
}
else if (usage > 0)
abort ();
}
}
}
}
fprintf (stderr, "\n\n\n\n\n");
}
int
main ()
{
int i;
printf("ver,name,merge,format,opc1,ch0,ch1,ch2,ch3,ch4,ch5,args,opce,opc_cmp,ales_opce,ales_opc2,explicit_ales25_v4,alopf11_lit8_count,alopf11_lit8_warn\n");
for (i = 0; i < 6; i++)
{
short_opers = &oper_tables[i][0];
long_opers = &oper_tables[i][1];
long_ext1_opers = &oper_tables[i][2];
long_ext2_opers = &oper_tables[i][3];
flag_opers = &oper_tables[i][4];
mcpu = i + 1;
gen_alopf1 ();
gen_alopf2 ();
gen_alopf3 ();
gen_alopf7 ();
gen_alopf17 ();
gen_alopf8 ();
gen_alopf10 ();
gen_alopf19 ();
gen_alopf11 ();
gen_alopf11_with_flags ();
gen_alopf12 ();
gen_alopf13 ();
gen_alopf15 ();
gen_alopf16 ();
gen_alopf21_with_flags ();
gen_alopf22 ();
gen_alopf21_custom ();
}
gen_alopf21_combined ();
#if 0
print_usage (1);
print_usage (0);
#endif
return 0;
}