syn68k/runtime/native/i386/process.c

531 lines
13 KiB
C

#include "config.h"
#include "template.h"
#include "process.h"
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
static int create_asmdata (const template_t *t, int num_operands);
static void dump_m68k_cc (char *s, const char *i386_cc);
static boolean_t
is_operand_holder (const char *p, int *operand_nump, boolean_t *pc_relative_pp)
{
boolean_t retval;
if (p[0] != '%')
retval = FALSE;
else
{
boolean_t unused;
if (!pc_relative_pp)
pc_relative_pp = &unused;
*pc_relative_pp = p[1] == 'P';
if (*pc_relative_pp)
++p;
retval = isdigit (p[1]);
if (retval)
if (operand_nump)
*operand_nump = atoi (&p[1]);
}
return retval;
}
static void
operand_replace (const char *old_code, char *new_code, int operand_num,
const char *new_operand, int end_num)
{
const char *s;
char *d;
for (s = old_code, d = new_code; *s != '\0'; )
{
int num;
boolean_t pc_p;
if (!is_operand_holder (s, &num, &pc_p) || num != operand_num)
*d++ = *s++;
else
{
if (pc_p)
{
sprintf (d, "code_end_%d+", end_num);
d+= strlen (d);
++s; /* to skip over the P */
}
strcpy (d, new_operand);
d += strlen (d);
for (s++; isdigit (*s); s++)
;
}
}
*d = '\0';
}
int
process_template (FILE *fp, FILE *header_fp, const template_t *t,
const char *make, int swapop_p)
{
int i, num_operands;
char cc[128], cmd[1024], buf[2048];
static char last_code_generated[32767];
FILE *bits;
/* Count the operands. */
num_operands = count_operands (t->code);
/* Generate the C code for all the operand combinations. It's the
* same for the case where our operands are swapped, so just reuse
* the old information if swapop_p is true.
*/
if (!swapop_p)
{
long size;
if (create_asmdata (t, num_operands) == FAILURE)
return 0;
/* Build the program to analyze it. If the make failed, we fail too. */
system ("rm -f analyze analyze.o");
sprintf (cmd, "%s -s analyze > /dev/null", make);
if (system (cmd) != 0)
return FAILURE;
/* Get the output of the analyzing program. */
sprintf (cmd, "./analyze");
bits = popen (cmd, "r");
if (bits == NULL)
return FAILURE;
/* Copy everything from our input pipe to our buffer. */
size = fread (last_code_generated, 1, sizeof last_code_generated, bits);
assert (size >= 0 && size < sizeof last_code_generated);
last_code_generated[size] = '\0';
if (pclose (bits) != 0)
return FAILURE;
}
else
abort ();
/* Output the code. */
sprintf (buf,
"/* Temporary machine-generated file. Delete me. */\n"
"\n"
"#include \"syn68k_private.h\"\n"
"#include \"native.h\"\n"
"\n"
"\n"
"int\n"
"%s%s (COMMON_ARGS",
t->macro_name, swapop_p ? "_swapop" : "");
fputs (buf, fp);
/* Output the prototype for this function to the haeder. */
sprintf (buf, "extern int %s%s (COMMON_ARGS",
t->macro_name, swapop_p ? "_swapop" : "");
fputs (buf, header_fp);
if (swapop_p)
{
for (i = num_operands - 1; i >= 0; i--)
{
fprintf (fp, ", int32 %s", t->operand_name[i]);
fprintf (header_fp, ", int32 %s", t->operand_name[i]);
}
}
else /* !swapop_p */
{
for (i = 0; i < num_operands; i++)
{
assert (t->operand_name[i] != NULL);
fprintf (fp, ", int32 %s", t->operand_name[i]);
fprintf (header_fp, ", int32 %s", t->operand_name[i]);
}
}
fputs (");\n", header_fp);
fputs (")\n"
"{\n"
" host_code_t *code;\n",
fp);
dump_m68k_cc (cc, t->i386_cc_out);
if (cc[0] != '-')
{
if (!strcmp (cc, "CNVXZ"))
fprintf (fp, " SPILL_CC_BITS (c, codep, cc_spill_if_changed);\n");
else
{
int b;
fprintf (fp, " SPILL_CC_BITS (c, codep, cc_spill_if_changed "
"& (");
for (b = 0; cc[b] != '\0'; b++)
fprintf (fp, "%sM68K_CC%c", (b > 0) ? "| " : "", cc[b]);
fputs ("));\n", fp);
}
/* Note which CC bits we just made valid. */
if (strcmp (cc, "CNVXZ"))
{
int b;
fprintf (fp,
" {\n"
" uint8 newly_valid_cc = (cc_to_compute & (");
for (b = 0; cc[b] != '\0'; b++)
fprintf (fp, "%sM68K_CC%c", (b > 0) ? "| " : "", cc[b]);
fputs ("));\n", fp);
fprintf (fp,
"\n"
" c->cached_cc |= newly_valid_cc;\n"
" c->dirty_cc |= newly_valid_cc;\n"
" }\n");
}
else
{
fputs (" c->cached_cc |= cc_to_compute;\n"
" c->dirty_cc |= cc_to_compute;\n",
fp);
}
}
fputs (" code = *codep;\n", fp);
fputs (last_code_generated, fp);
fputs (" *codep = code;\n"
" return 0;\n"
"}\n",
fp);
/* Just so I can watch its progress more easily, flush stuff. */
fflush (fp);
fflush (header_fp);
return SUCCESS;
}
static void
dump_m68k_cc (char *d, const char *i386_cc)
{
cc_mask_t m68k_cc;
const char *s;
m68k_cc = M68K_CC_NONE;
for (s = i386_cc; *s != '\0'; s++)
switch (*s)
{
case 'c':
m68k_cc |= (M68K_CCC | M68K_CCX);
break;
case 'o':
m68k_cc |= M68K_CCV;
break;
case 's':
m68k_cc |= M68K_CCN;
break;
case 'z':
m68k_cc |= M68K_CCZ;
break;
case 'a':
case 'p':
case 'd':
case 'i':
break;
default:
fprintf (stderr, "Unknown i386 cc bit specifier \"%c\".\n", *s);
break;
}
if (m68k_cc == M68K_CC_NONE)
*d++ = '-';
else
{
if (m68k_cc & M68K_CCC)
*d++ = 'C';
if (m68k_cc & M68K_CCN)
*d++ = 'N';
if (m68k_cc & M68K_CCV)
*d++ = 'V';
if (m68k_cc & M68K_CCX)
*d++ = 'X';
if (m68k_cc & M68K_CCZ)
*d++ = 'Z';
}
*d = '\0';
}
int
count_operands (const char *s)
{
int num_operands;
for (num_operands = 0; ; num_operands++)
{
const char *p;
for (p = s; *p != '\0'; p++)
{
int num;
if (is_operand_holder (p, &num, NULL) && num == num_operands)
break;
}
/* If we failed to find the operand, we're done. */
if (*p == '\0')
break;
}
return num_operands;
}
#define MAX_VALUE_SET_ENTRIES 35
typedef struct
{
int num_values;
long value[MAX_VALUE_SET_ENTRIES];
} value_set_t;
/*
* The addition of BROKEN_SIZE_32 (see template.h) and
* immediate_values[3] is a hacky workaround for Mac OS X's ld which
* doesn't recognize 0x80000000 (-2147483648) as a legitimate 32-bit
* relocatable offset. This prevents calls and jumps from being
* properly analyzed.
*
* For now we sacrifice the analysis of that bit pattern in the few
* template entries that would normally have it. We currently do this
* unconditionally because nobody has yet written a configure macro to
* detect this problem and then only use "BROKEN_SIZE_32" when we have
* a broken ld.
*/
static int
create_asmdata (const template_t *t, int num_operands)
{
static const value_set_t immediate_values[] =
{ { 12, { 0, 1, 2, -1, -2, 127, -128, -127, 0x37, -100, 0x12, -97 } },
{ 23, { 0, 1, 2, 0xFF, 0xFE, 127, -129, -128, -127, 128, 0x37,
-100, 0x12, -97, 32767, -32768, -32767, -1, -2, 0x871,
-1234, 0x1234, -561 } },
{ 33, { 0, 1, 2, 0xFF, 0xFE, 128, -129, 127, -128, -127, 0x37, -100,
0x12, -97,
32767, -32768, -32767, -1, 0xFFFE, 0x871, 0xFA03,
0x1234, 0x8765, 0x7FFFFFFF, 0x80000000, 0x80000001,
0xFFFFFFFF, 0xFFFFFFFE, 0x871529, 0x392332, 0xFA034433,
0x12345678, 0x87654321 } },
{ 32, { 0, 1, 2, 0xFF, 0xFE, 128, -129, 127, -128, -127, 0x37, -100,
0x12, -97,
32767, -32768, -32767, -1, 0xFFFE, 0x871, 0xFA03,
0x1234, 0x8765, 0x7FFFFFFF, 0x80000001,
0xFFFFFFFF, 0xFFFFFFFE, 0x871529, 0x392332, 0xFA034433,
0x12345678, 0x87654321 } }
};
#if 0
/* Explanation of this warning:
* The i386 has slightly more compact opcode sequences in some situations
* when %al/%ax/%eax is involved in an operation. The register specifier
* byte is omitted and the register is implicit in the special case opcode.
* Unfortunately, this can cause:
* addw $0x1234,%ax ; Using %ax shaves off a byte
* addw $1,%bx ; small immediate shaves off one byte
* to both require the same number of compiled bytes. analyze.c isn't
* yet smart enough to make fine distinctions between two different
* bit patterns of the same size which don't accept the same operands into
* the same bit offsets.
*/
#warning "Intentionally overlooking compact special cases for %al/%ax/%eax"
#endif
static const value_set_t register_values[3] =
{
{ 8, { 0, 1, 2, 3, 4, 5, 6, 7 } },
{ 6, { 0, 1, 2, 3, /* skip %bp,%sp */ 6, 7 } },
{ 6, { 0, 1, 2, 3, /* skip %ebp,%esp */ 6, 7 } }
};
static const char *register_name[3][8] =
{
/* We don't consider %esp and %ebp here because they are "escape"
* registers in some circumstances to indicate different addressing
* modes, and we don't allocate anything into them anyway.
* It's best to not confuse our software by allowing escape sequences.
*/
{ "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh" },
{ "%ax", "%cx", "%dx", "%bx", NULL, NULL, "%si", "%di" },
{ "%eax", "%ecx", "%edx", "%ebx", NULL, NULL, "%esi", "%edi" }
};
int current, n, op;
int which[MAX_OPERANDS + 1];
const value_set_t *value[MAX_OPERANDS];
boolean_t done_p;
long v;
FILE *fp;
memset (which, 0, sizeof which);
memset (value, 0xff, sizeof value); /* help us detect uninitialized use */
fp = fopen ("asmdata.h", "w");
if (fp == NULL)
{
fprintf (stderr, "Unable to open asmdata.c for writing.\n");
return FAILURE;
}
fputs ("/* This file is machine-generated and ephemeral. DO NOT EDIT! */\n"
"\n"
"extern void asmdata (void); /* Avoid compiler warnings. */\n"
"void asmdata ()\n"
"{\n",
fp);
/* Determine which test value sets we will use for each operand. */
for (n = 0; n < num_operands; n++)
{
if (t->operand[n].type == REGISTER)
value[n] = &register_values[t->operand[n].size];
else
value[n] = &immediate_values[t->operand[n].size];
}
for (current = 0, done_p = FALSE; !done_p; current++)
{
char code[2][1024];
int new_code = 0;
#if defined(HAVE_SYMBOL_UNDERSCORE)
const char *symbol_prefix = "_";
#else
const char *symbol_prefix = "";
#endif
strcpy (&code[new_code][0], t->code);
for (op = 0; op < num_operands; op++)
{
char buf[100];
v = value[op]->value[which[op]];
if (t->operand[op].type == REGISTER)
strcpy (buf, register_name[t->operand[op].size][v]);
else
sprintf (buf, "%ld", v);
operand_replace (code[new_code], code[!new_code], op, buf, current);
new_code = !new_code;
}
/*
* Use local labels so we get smaller branches even on Mac OS X. The
* following note comes from the Mac OS X Assembler reference:
*
* Note: The Mac OS X assembler for Intel i386 processors always
* produces branch instructions that are long (32 bits) for non-local
* labels. This allows the link editor to do procedure ordering (see
* the description of the -sectorder option in the ld(1) man page).
*/
fprintf (fp,
" asm volatile (\"\\n\"\n"
" \"%scode_start_%d:\\n\\t\"\n"
" \"%s\\n\"\n"
" \"Lcode_end_%d:\\n\"\n"
" \"%scode_end_%d:\");\n",
symbol_prefix, current, code[new_code], current,
symbol_prefix, current);
/* Try the next combination of operands. */
for (op = num_operands - 1; op >= 0; op--)
{
if (++which[op] >= value[op]->num_values)
{
which[op] = 0;
if (op == 0)
done_p = TRUE;
}
else
break;
}
if (num_operands == 0)
done_p = TRUE;
}
fputs ("}\n", fp);
fprintf (fp,
"\n"
"#define NUM_SAMPLES %d\n"
"#define NUM_OPERANDS %d\n"
"#define TEMPLATE template[%d]\n"
"\n",
current, num_operands, t - &template[0]);
fputs ("static const long value[NUM_SAMPLES][NUM_OPERANDS + 1] =\n"
"{\n",
fp);
memset (which, 0, sizeof which);
for (n = 0; n < current; n++)
{
fprintf (fp, " {");
for (op = 0; op < num_operands; op++)
fprintf (fp, "%s 0x%lX", op == 0 ? "" : ",",
(unsigned long) value[op]->value[which[op]]);
fputs (" },\n", fp);
/* Try the next combination of operands. */
for (op = num_operands - 1; op >= 0; op--)
{
if (++which[op] >= value[op]->num_values)
which[op] = 0;
else
break;
}
}
fputs ("};\n", fp);
for (n = 0; n < current; n++)
{
fprintf (fp,
"extern uint8 code_start_%d;\n"
"extern uint8 code_end_%d;\n",
n, n);
}
fputs ("\n"
"typedef struct\n"
"{\n"
" const uint8 *start, *end;\n"
"} sample_t;\n"
"\n",
fp);
fputs ("static const sample_t sample[NUM_SAMPLES] =\n"
"{\n", fp);
memset (which, 0, sizeof which);
for (n = 0; n < current; n++)
{
fprintf (fp,
" { &code_start_%d, &code_end_%d }",
n, n);
/* Try the next combination of operands. */
for (op = num_operands - 1; op >= 0; op--)
{
if (++which[op] >= value[op]->num_values)
which[op] = 0;
else
break;
}
if (n < current - 1)
fputs (",\n", fp);
}
fputs ("};\n", fp);
fclose (fp);
return SUCCESS;
}