icfp12/ovm/ovm.c

266 lines
7.1 KiB
C

#include "ovm.h"
#include <glib/gstdio.h>
#undef CMPEPS
// #define CMPEPS
void ovm_free(ovm_t *ovm) {
if (!ovm) return;
g_slice_free1(ovm->limit * sizeof(instruction_t), ovm->instructions);
g_slice_free1(ovm->limit * sizeof(gdouble), ovm->values);
g_slice_free(ovm_t, ovm);
}
ovm_t* ovm_load(const gchar *filename) {
GError *err = NULL;
gchar *buf = NULL, *cur;
gsize len = 0, i;
instruction_t *oi;
gdouble *vi;
ovm_t *ovm = g_slice_new(ovm_t);
ovm->limit = 0x4000;
ovm->instructions = g_slice_alloc0(ovm->limit * sizeof(instruction_t));
ovm->values = g_slice_alloc0(ovm->limit * sizeof(gdouble));
if (!g_file_get_contents(filename, &buf, &len, &err)) {
fprintf (stderr, "Unable to read file %s: %s\n", filename, err->message);
g_error_free (err);
exit(1);
}
if (0 != len % 12) {
fprintf (stderr, "file size of %s not a multiple of frame size\n", filename);
exit(1);
}
len /= 12;
oi = ovm->instructions;
vi = ovm->values;
for (cur = buf, i = 0; i < len; i++) {
*vi++ = *(gdouble*)buf;
buf += sizeof(gdouble);
*oi++ = *(guint32*)buf;
buf += sizeof(guint32);
if (++i < len) {
*oi++ = *(guint32*)buf;
buf += sizeof(guint32);
*vi++ = *(gdouble*)buf;
buf += sizeof(gdouble);
} else {
break;
}
}
ovm->used = i;
return ovm;
}
void ovm_print_c(ovm_t *ovm, const gchar *filename) {
guint i, max_in=0, max_out=0;
instruction_t *oi = ovm->instructions;
gdouble *vi = ovm->values;
guint64 *uvi = (guint64*) ovm->values;
guchar *usedv = g_slice_alloc0(ovm->used*sizeof(guchar));
guchar *localv = g_slice_alloc0(ovm->used*sizeof(guchar));
GString *str = g_string_sized_new(0), *buf = g_string_sized_new(0);
int f = g_open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0644);
gboolean first;
if (-1 == f) {
fprintf(stderr, "Couldn't open file %s: %s\n", filename, g_strerror(errno));
exit(2);
}
g_string_truncate(buf, 0);
#define LOCAL(i) do { \
if (!usedv[i]) { \
usedv[i] = TRUE; \
localv[i] = TRUE; \
} \
} while (0)
#define USED(i) (usedv[i] = TRUE, i)
#define USED_IN(i) (max_in = max_in > i ? max_in : i, i)
#define USED_OUT(i) (max_out = max_out > i ? max_out : i, i)
for (i = 0; i < ovm->used; i++) {
g_string_printf(str, "\t/* Invalid command: 0x%X */\n", (guint) oi[i]);
switch (instr_dop(oi[i])) {
case OP_STYPE:
switch (instr_sop(oi[i])) {
case SOP_NOOP:
g_string_printf(str, "/* NOOP */\n");
g_string_truncate(str, 0);
break;
case SOP_CMPZ:
switch (instr_sop_cmp(oi[i])) {
#ifdef CMPEPS /* cmp with eps */
case CMP_LTZ:
g_string_printf(str, "\tovm_status = (v%u.d < -eps);\n", USED(instr_sop_r1(oi[i])));
break;
case CMP_LEZ:
g_string_printf(str, "\tovm_status = (v%u.d <= eps);\n", USED(instr_sop_r1(oi[i])));
break;
case CMP_EQZ:
g_string_printf(str, "\tovm_status = (fabs(v%u.d) <= eps);\n", USED(instr_sop_r1(oi[i])));
break;
case CMP_GEZ:
g_string_printf(str, "\tovm_status = (v%u.d >= -eps);\n", USED(instr_sop_r1(oi[i])));
break;
case CMP_GTZ:
g_string_printf(str, "\tovm_status = (v%u.d > eps);\n", USED(instr_sop_r1(oi[i])));
break;
#else
case CMP_LTZ:
g_string_printf(str, "\tovm_status = (v%u.d < 0);\n", USED(instr_sop_r1(oi[i])));
break;
case CMP_LEZ:
g_string_printf(str, "\tovm_status = (v%u.d <= 0);\n", USED(instr_sop_r1(oi[i])));
break;
case CMP_EQZ:
g_string_printf(str, "\tovm_status = (v%u.d == 0);\n", USED(instr_sop_r1(oi[i])));
break;
case CMP_GEZ:
g_string_printf(str, "\tovm_status = (v%u.d >= 0);\n", USED(instr_sop_r1(oi[i])));
break;
case CMP_GTZ:
g_string_printf(str, "\tovm_status = (v%u.d > 0);\n", USED(instr_sop_r1(oi[i])));
break;
#endif
}
break;
case SOP_SQRT:
g_string_printf(str, "\tv%u.d = sqrt(v%u.d);\n", i, USED(instr_sop_r1(oi[i])));
LOCAL(i);
break;
case SOP_COPY:
g_string_printf(str, "\tv%u.d = v%u.d;\n", i, USED(instr_sop_r1(oi[i])));
LOCAL(i);
break;
case SOP_IN:
if (0x3e80 == instr_sop_r1(oi[i])) {
g_string_printf(str, "\tv%u.d = scenario;\n", i);
} else {
g_string_printf(str, "\tv%u.d = in[%u];\n", i, USED_IN(instr_sop_r1(oi[i])));
}
LOCAL(i);
break;
}
break;
case OP_ADD:
g_string_printf(str, "\tv%u.d = v%u.d + v%u.d;\n", i, USED(instr_dop_r1(oi[i])), USED(instr_dop_r2(oi[i])));
LOCAL(i);
break;
case OP_SUB:
g_string_printf(str, "\tv%u.d = v%u.d - v%u.d;\n", i, USED(instr_dop_r1(oi[i])), USED(instr_dop_r2(oi[i])));
LOCAL(i);
break;
case OP_MULT:
g_string_printf(str, "\tv%u.d = v%u.d * v%u.d;\n", i, USED(instr_dop_r1(oi[i])), USED(instr_dop_r2(oi[i])));
LOCAL(i);
break;
case OP_DIV:
#ifdef CMPEPS
g_string_printf(str, "\tv%u.d = (fabs(v%u.d) < eps) ? 0.0 : v%u.d / v%u.d;\n", i, USED(instr_dop_r2(oi[i])), USED(instr_dop_r1(oi[i])), instr_dop_r2(oi[i]));
#else
g_string_printf(str, "\tv%u.d = (v%u.d == 0) ? 0.0 : v%u.d / v%u.d;\n", i, USED(instr_dop_r2(oi[i])), USED(instr_dop_r1(oi[i])), instr_dop_r2(oi[i]));
#endif
LOCAL(i);
break;
case OP_OUT:
g_string_printf(str, "\tout[%u] = v%u.d;\n", USED_OUT(instr_dop_r1(oi[i])), USED(instr_dop_r2(oi[i])));
break;
case OP_PHI:
g_string_printf(str, "\tv%u.d = ovm_status ? v%u.d : v%u.d;\n", i, USED(instr_dop_r1(oi[i])), USED(instr_dop_r2(oi[i])));
LOCAL(i);
break;
}
g_string_append_len(buf, GSTR_LEN(str));
}
#undef USED
g_string_append_len(buf, CONST_STR_LEN(
"}\n"
"\n"
));
write(f, CONST_STR_LEN(
"#include \"task.h\"\n"
"#include <math.h>\n"
"\n"
"typedef union { gdouble d; guint64 i; } double_int;\n"
"\n"
"static gboolean ovm_status;\n"
"\n"
));
#ifdef CMPEPS
write(f, CONST_STR_LEN(
"static const gdouble eps = 1e-300;\n"
"\n"
));
#endif
g_string_printf(str, "const guint ovm_outs = %u, ovm_ins = %u;\n", max_out+1, max_in+1);
write(f, GSTR_LEN(str));
write(f, CONST_STR_LEN(
"\n"
));
first = TRUE;
for (i = 0; i < ovm->used; i++) {
if (!usedv[i] || localv[i]) continue;
if (first)
g_string_printf(str, "static double_int v%u", i);
else
g_string_printf(str, ", v%u", i);
first = FALSE;
write(f, GSTR_LEN(str));
}
if (!first) write(f, CONST_STR_LEN(";\n\n"));
write(f, CONST_STR_LEN(
"void ovm_init() {\n"
"\tovm_status = FALSE;\n"
"\n"
));
for (i = 0; i < ovm->used; i++) {
if (!usedv[i] || localv[i]) continue;
/* g_string_printf(str, "\tv%u.d = %f;\n", i, vi[i]); */
g_string_printf(str, "\tv%u.i = G_GUINT64_CONSTANT(%" G_GUINT64_FORMAT "); /* %f */ \n", i, uvi[i], vi[i]);
write(f, GSTR_LEN(str));
}
write(f, CONST_STR_LEN(
"}\n"
"\n"
));
write(f, CONST_STR_LEN(
"void ovm_step(guint scenario, gdouble *__restrict__ in, gdouble *__restrict__ out) {\n"
));
first = TRUE;
for (i = 0; i < ovm->used; i++) {
if (localv[i]) {
if (first)
g_string_printf(str, "\tdouble_int v%u", i);
else
g_string_printf(str, ", v%u", i);
first = FALSE;
write(f, GSTR_LEN(str));
}
}
if (!first) write(f, CONST_STR_LEN(";\n"));
write(f, GSTR_LEN(buf));
close(f);
g_slice_free1(ovm->used*sizeof(guchar), usedv);
g_slice_free1(ovm->used*sizeof(guchar), localv);
g_string_free(str, TRUE);
g_string_free(buf, TRUE);
}