From: Mark Wooding Date: Wed, 25 Sep 2019 19:38:06 +0000 (+0100) Subject: crypto-test: Introduce first version of new .c file. X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=9b70524ff3b57a798a64289bf144c498e14b9d5f;p=secnet.git crypto-test: Introduce first version of new .c file. This is not complete and does not compile yet. It is not wired into the build system. That will come right at the end, because I have chosen not to generate a fake history for the build machinery, which in the original work-in-progress series underwent some churn and was then refactored at the end. (This commit was originally an `@@@' commit from Mark Wooding, "@@@ crypto-test". I have added this commit message and folded in a couple of subsequent commits which I felt should be included.) Signed-off-by: Ian Jackson --- diff --git a/crypto-test.c b/crypto-test.c new file mode 100644 index 0000000..4a53a07 --- /dev/null +++ b/crypto-test.c @@ -0,0 +1,420 @@ +/* + * crypto-test.c: common test vector processing + */ +/* + * This file is Free Software. It was originally written for secnet. + * + * Copyright 2017, 2019 Mark Wooding + * + * You may redistribute secnet as a whole and/or modify it under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3, or (at your option) any + * later version. + * + * You may redistribute this file and/or modify it under the terms of + * the GNU General Public License as published by the Free Software + * Foundation; either version 2, or (at your option) any later + * version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, see + * https://www.gnu.org/licenses/gpl.html. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "secnet.h" +#include "util.h" + +#include "crypto-test.h" + +/*----- Utilities ---------------------------------------------------------*/ + +static void *xmalloc(size_t sz) +{ + void *p; + + if (!sz) return 0; + p = malloc(sz); + if (!p) { + fprintf(stderr, "out of memory!\n"); + exit(2); + } + return p; +} + +static void *xrealloc(void *p, size_t sz) +{ + void *q; + + if (!sz) { free(p); return 0; } + else if (!p) return xmalloc(sz); + q = realloc(p, sz); + if (!q) { + fprintf(stderr, "out of memory!\n"); + exit(2); + } + return q; +} + +static int lno; + +void bail(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + fprintf(stderr, "unexpected error (line %d): ", lno); + vfprintf(stderr, msg, ap); + va_end(ap); + fputc('\n', stderr); + exit(2); +} + +struct linebuf { + char *p; + size_t sz; +}; +#define LINEBUF_INIT { 0, 0 }; + +static int read_line(struct linebuf *b, FILE *fp) +{ + size_t n = 0; + int ch; + + ch = getc(fp); if (ch == EOF) return EOF; + for (;;) { + if (n >= b->sz) { + b->sz = b->sz ? 2*b->sz : 64; + b->p = xrealloc(b->p, b->sz); + } + if (ch == EOF || ch == '\n') { b->p[n++] = 0; return 0; } + b->p[n++] = ch; + ch = getc(fp); + } +} + +void parse_hex(uint8_t *b, size_t sz, char *p) +{ + size_t n = strlen(p); + unsigned i; + char bb[3]; + + if (n%2) bail("bad hex (odd number of nibbles)"); + else if (n/2 != sz) bail("bad hex (want %zu bytes, found %zu)", sz, n/2); + while (sz) { + for (i = 0; i < 2; i++) { + if (!isxdigit((unsigned char)p[i])) + bail("bad hex digit `%c'", p[i]); + bb[i] = p[i]; + } + bb[2] = 0; + p += 2; + *b++ = strtoul(bb, 0, 16); sz--; + } +} + +void dump_hex(FILE *fp, const uint8_t *b, size_t sz) + { while (sz--) fprintf(fp, "%02x", *b++); fputc('\n', fp); } + +void trivial_regty_init(union regval *v) { ; } +void trivial_regty_release(union regval *v) { ; } + +/* Define some global variables we shouldn't need. + * + * Annoyingly, `secnet.h' declares static pointers and initializes them to + * point to some external variables. At `-O0', GCC doesn't optimize these + * away, so there's a link-time dependency on these variables. Define them + * here, so that `f25519.c' and `f448.c' can find them. + * + * (Later GCC has `-Og', which optimizes without making debugging a + * nightmare, but I'm not running that version here. Note that `serpent.c' + * doesn't have this problem because it defines its own word load and store + * operations to cope with its endian weirdness, whereas the field arithmetic + * uses `unaligned.h' which manages to include `secnet.h'.) + */ +uint64_t now_global; +struct timeval tv_now_global; + +/* Bletch. util.c is a mess of layers. */ +int consttime_memeq(const void *s1in, const void *s2in, size_t n) +{ + const uint8_t *s1=s1in, *s2=s2in; + register volatile uint8_t accumulator=0; + + while (n-- > 0) { + accumulator |= (*s1++ ^ *s2++); + } + accumulator |= accumulator >> 4; /* constant-time */ + accumulator |= accumulator >> 2; /* boolean canonicalisation */ + accumulator |= accumulator >> 1; + accumulator &= 1; + accumulator ^= 1; + return accumulator; +} + +/*----- Built-in types ----------------------------------------------------*/ + +/* Signed integer. */ + +static void parse_int(union regval *v, char *p) +{ + char *q; + + errno = 0; + v->i = strtol(p, &q, 0); + if (*q || errno) bail("bad integer `%s'", p); +} + +static void dump_int(FILE *fp, const union regval *v) + { fprintf(fp, "%ld\n", v->i); } + +static int eq_int(const union regval *v0, const union regval *v1) + { return (v0->i == v1->i); } + +const struct regty regty_int = { + trivial_regty_init, + parse_int, + dump_int, + eq_int, + trivial_regty_release +}; + +/* Unsigned integer. */ + +static void parse_uint(union regval *v, char *p) +{ + char *q; + + errno = 0; + v->u = strtoul(p, &q, 0); + if (*q || errno) bail("bad integer `%s'", p); +} + +static void dump_uint(FILE *fp, const union regval *v) + { fprintf(fp, "%lu\n", v->u); } + +static int eq_uint(const union regval *v0, const union regval *v1) + { return (v0->u == v1->u); } + +const struct regty regty_uint = { + trivial_regty_init, + parse_uint, + dump_uint, + eq_uint, + trivial_regty_release +}; + +/* Byte string, as hex. */ + +void allocate_bytes(union regval *v, size_t sz) + { v->bytes.p = xmalloc(sz); v->bytes.sz = sz; } + +static void init_bytes(union regval *v) { v->bytes.p = 0; v->bytes.sz = 0; } + +static void parse_bytes(union regval *v, char *p) +{ + size_t n = strlen(p); + + allocate_bytes(v, n/2); + parse_hex(v->bytes.p, v->bytes.sz, p); +} + +static void dump_bytes(FILE *fp, const union regval *v) + { dump_hex(fp, v->bytes.p, v->bytes.sz); } + +static int eq_bytes(const union regval *v0, const union regval *v1) +{ + return v0->bytes.sz == v1->bytes.sz && + !memcmp(v0->bytes.p, v1->bytes.p, v0->bytes.sz); +} + +static void release_bytes(union regval *v) { free(v->bytes.p); } + +const struct regty regty_bytes = { + init_bytes, + parse_bytes, + dump_bytes, + eq_bytes, + release_bytes +}; + +/*----- Core test machinery -----------------------------------------------*/ + +/* Say that a register is `reset' by releasing and then re-initializing it. + * While there is a current test, all of that test's registers are + * initialized. The input registers are reset at the end of `check', ready + * for the next test to load new values. The output registers are reset at + * the end of `check_test_output', so that a test runner can run a test + * multiple times against the same test input, but with different context + * data. + */ + +#define REG(rvec, i) \ + ((struct reg *)((unsigned char *)state->rvec + (i)*state->regsz)) + +void check_test_output(struct test_state *state, const struct test *test) +{ + const struct regdef *def; + struct reg *reg, *in, *out; + int ok = 1; + int match; + + for (def = test->regs; def->name; def++) { + if (def->i >= state->nrout) continue; + in = REG(in, def->i); out = REG(out, def->i); + if (!def->ty->eq(&in->v, &out->v)) ok = 0; + } + if (ok) + state->win++; + else { + printf("failed test `%s'\n", test->name); + for (def = test->regs; def->name; def++) { + in = REG(in, def->i); + if (def->i >= state->nrout) { + printf("\t input `%s' = ", def->name); + def->ty->dump(stdout, &in->v); + } else { + out = REG(out, def->i); + match = def->ty->eq(&in->v, &out->v); + printf("\t%s `%s' = ", + match ? " output" : "expected", def->name); + def->ty->dump(stdout, &in->v); + if (!match) { + printf("\tcomputed `%s' = ", def->name); + def->ty->dump(stdout, &out->v); + } + } + } + state->lose++; + } + + for (def = test->regs; def->name; def++) { + if (def->i >= state->nrout) continue; + reg = REG(out, def->i); + def->ty->release(®->v); def->ty->init(®->v); + } +} + +void run_test(struct test_state *state, const struct test *test) +{ + test->fn(state->out, state->in, 0); + check_test_output(state, test); +} + +static void check(struct test_state *state, const struct test *test) +{ + const struct regdef *def, *miss = 0; + struct reg *reg; + int any = 0; + + if (!test) return; + for (def = test->regs; def->name; def++) { + reg = REG(in, def->i); + if (reg->f®F_LIVE) any = 1; + else if (!miss && !(def->f®F_OPT)) miss = def; + } + if (!any) return; + if (miss) + bail("register `%s' not set in test `%s'", def->name, test->name); + + test->run(state, test); + + for (def = test->regs; def->name; def++) { + reg = REG(in, def->i); + reg->f = 0; def->ty->release(®->v); def->ty->init(®->v); + } +} + +int run_test_suite(unsigned nrout, unsigned nreg, size_t regsz, + const struct test *tests, FILE *fp) +{ + struct linebuf buf = LINEBUF_INIT; + struct test_state state[1]; + const struct test *test; + const struct regdef *def; + struct reg *reg; + char *p; + const char *q; + int total; + size_t n; + + for (test = tests; test->name; test++) + for (def = test->regs; def->name; def++) + assert(def->i < nreg); + + state->in = xmalloc(nreg*regsz); + state->out = xmalloc(nrout*regsz); + state->nrout = nrout; + state->nreg = nreg; + state->regsz = regsz; + state->win = state->lose = 0; + + test = 0; + lno = 0; + while (!read_line(&buf, fp)) { + lno++; + p = buf.p; n = strlen(buf.p); + + while (isspace((unsigned char)*p)) p++; + if (*p == '#') continue; + if (!*p) { check(state, test); continue; } + + q = p; + while (*p && !isspace((unsigned char)*p)) p++; + if (*p) *p++ = 0; + + if (!strcmp(q, "test")) { + if (!*p) bail("missing argument"); + check(state, test); + if (test) { + for (def = test->regs; def->name; def++) { + def->ty->release(®(in, def->i)->v); + if (def->i < state->nrout) + def->ty->release(®(out, def->i)->v); + } + } + for (test = tests; test->name; test++) + if (!strcmp(p, test->name)) goto found_test; + bail("unknown test `%s'", p); + found_test: + for (def = test->regs; def->name; def++) { + reg = REG(in, def->i); + reg->f = 0; def->ty->init(®->v); + if (def->i < state->nrout) { + reg = REG(out, def->i); + reg->f = 0; def->ty->init(®->v); + } + } + continue; + } + + if (!test) bail("no current test"); + for (def = test->regs; def->name; def++) + if (!strcmp(q, def->name)) goto found_reg; + bail("unknown register `%s' in test `%s'", q, test->name); + found_reg: + reg = REG(in, def->i); + if (reg->f®F_LIVE) bail("register `%s' already set", def->name); + def->ty->parse(®->v, p); reg->f |= REGF_LIVE; + } + check(state, test); + + total = state->win + state->lose; + if (!state->lose) + printf("PASSED all %d test%s\n", state->win, total == 1 ? "" : "s"); + else + printf("FAILED %d of %d test%s\n", state->lose, total, + total == 1 ? "" : "s"); + return state->lose ? 1 : 0; +} diff --git a/crypto-test.h b/crypto-test.h new file mode 100644 index 0000000..94bb009 --- /dev/null +++ b/crypto-test.h @@ -0,0 +1,137 @@ +/* + * crypto-test.h: common test vector processing + */ +/* + * This file is Free Software. It was originally written for secnet. + * + * Copyright 2017, 2019 Mark Wooding + * + * You may redistribute secnet as a whole and/or modify it under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3, or (at your option) any + * later version. + * + * You may redistribute this file and/or modify it under the terms of + * the GNU General Public License as published by the Free Software + * Foundation; either version 2, or (at your option) any later + * version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, see + * https://www.gnu.org/licenses/gpl.html. + */ + +#ifndef crypto_test_h +#define crypto_test_h + +/* Basic model. + * + * There is a collection of `registers', each of which can store a value. + * Some registers are designated as `input': their values are set while + * reading the test vector. Other registers are designated as `output': the + * test function is expected to calculate their values, which are then + * compared against final values supplied by the test vector. Any + * discrepancies are reported. + * + * While a test suite is running, there is a single vector of registers, and + * registers are identified by index, starting from zero. The number of + * registers, `nreg', and a threshold `nrout', are defined by the test suite: + * registers with index less than `nrout' are for output; other registers up + * to, but not including, `nreg' are for input. Finally, the test suite + * defines the size of a register. The common test machinery treats + * registers as entirely opaque, acting on them only through their defined + * types. + * + * Each kind of test defines a register mapping, which assigns types and + * (textual) names to some subset of the registers. A register can be marked + * optional; by default, the test vector parser will report an error if a + * defined register is not assigned a value. + * + * The register type is responsible for handling the register on behalf of + * the common code. (Test functions can have built-in knowledge of which + * registers have which types, and can manipulate registers directly, of + * course.) + * + * Finally, each test defines a test runner, which is responsible for + * invoking the test function and checking that the output registers are + * correct. There is a generic test runner, but some tests might benefit + * from special arrangements. + */ + +union regval { + long i; /* signed integer */ + unsigned long u; /* unsigned integer */ + struct { void *p; size_t sz; } bytes; /* buffer of bytes */ +#ifdef REG_MEMBERS + REG_MEMBERS /* your members here */ +#endif +}; + +struct reg { + unsigned f; /* flags */ +#define REGF_LIVE 1u /* input register has a value */ + union regval v; /* register value */ +}; + +struct regty { + void (*init)(union regval *v); /* set up raw memory */ + void (*parse)(union regval *v, char *p); /* parse text input as value */ + void (*dump)(FILE *fp, const union regval *v); /* dump value as text */ + int (*eq)(const union regval *v0, const union regval *v1); /* equal? */ + void (*release)(union regval *v); /* release any resources */ +}; + +struct regdef { + const char *name; /* register name (for input files) */ + unsigned i; /* register index */ + const struct regty *ty; /* register type descriptor */ + unsigned f; /* flags */ +#define REGF_OPT 1u /* (input) register is optional */ +}; +#define REGLIST_END { 0 } + +struct test_state { + struct reg *in, *out; /* vectors of registers */ + unsigned nrout; /* number of output registers */ + unsigned nreg; /* total number of registers */ + size_t regsz; /* size of an individual register */ + int win, lose; /* number of tests passed/failed */ +}; + +struct test { + const char *name; /* name of the test */ + void (*run)(struct test_state *state, const struct test *test); + /* test runner (`run_test') */ + const struct regdef *regs; /* register definitions */ + void (*fn)(struct reg *out, const struct reg *in, void *ctx); + /* test function */ +}; + +/* Utility functions. */ +extern NORETURN(bail(const char *msg, ...)) + FORMAT(printf, 1, 2); +extern void parse_hex(uint8_t *b, size_t sz, char *p); +extern void dump_hex(FILE *fp, const uint8_t *b, size_t sz); +extern void trivial_regty_init(union regval *v); +extern void trivial_regty_release(union regval *v); +extern void allocate_bytes(union regval *v, size_t sz); + +/* Built-in register types. */ +extern const struct regty + regty_int, + regty_uint, + regty_bytes; + +/* Running tests. */ +extern void check_test_output(struct test_state *state, + const struct test *test); +extern void run_test(struct test_state *state, const struct test *test); +extern int run_test_suite(unsigned nrout, unsigned nreg, size_t regsz, + const struct test *tests, FILE *fp); + +#endif diff --git a/ec-field-test.c b/ec-field-test.c new file mode 100644 index 0000000..5c29704 --- /dev/null +++ b/ec-field-test.c @@ -0,0 +1,267 @@ +/* + * ec-field-test.c: test harness for elliptic-curve field arithmetic + * + * (The implementations originally came with different test arrangements, + * with complicated external dependencies. This file replicates the original + * tests, but without the dependencies.) + */ +/* + * This file is Free Software. It was originally written for secnet. + * + * Copyright 2017 Mark Wooding + * + * You may redistribute secnet as a whole and/or modify it under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3, or (at your option) any + * later version. + * + * You may redistribute this file and/or modify it under the terms of + * the GNU General Public License as published by the Free Software + * Foundation; either version 2, or (at your option) any later + * version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, see + * https://www.gnu.org/licenses/gpl.html. + */ + +#include + +#include "secnet.h" + +#include "f25519.h" +#include "fgoldi.h" + +#define f25519_FESZ 32u +#define fgoldi_FESZ 56u + +#define GLUE(x, y) GLUE_(x, y) +#define GLUE_(x, y) x##y +#define FIELDOP(op) GLUE(FIELD, _##op) + +#define REG_MEMBERS \ + uint8_t fe[FIELDOP(FESZ)]; +#include "crypto-test.h" + +enum { + RZ, RZ0 = RZ, RZ1, RXX = RZ0, RYY = RZ1, NROUT, + RM = NROUT, RI = RM, RN = RM, + RU, RV, RW, RX, RY, RA, + RV0 = RU, RV31 = RV0 + 31, + NREG +}; + +static void parse_fe(union regval *v, char *p) +{ + size_t n = strlen(p); + size_t sz = sizeof(v->fe); + + if (!*p) + memset(v->fe, 0xff, sizeof(v->fe)); + else { + if (sz > n/2) sz = n/2; + parse_hex(v->fe, sz, p); memset(v->fe + sz, 0, sizeof(v->fe) - sz); + } +} + +static void dump_fe(FILE *fp, const union regval *v) + { dump_hex(fp, v->fe, sizeof(v->fe)); } + +static int eq_fe(const union regval *v0, const union regval *v1) + { return (memcmp(v0->fe, v1->fe, sizeof(v0->fe)) == 0); } + +static const struct regty regty_fe = { + trivial_regty_init, + parse_fe, + dump_fe, + eq_fe, + trivial_regty_release +}; + +#define BINOP(op) \ + static void test_##op(struct reg *out, \ + const struct reg *in, void *ctx) \ + { \ + FIELD x, y, z; \ + \ + FIELDOP(load)(&x, in[RX].v.fe); \ + FIELDOP(load)(&y, in[RY].v.fe); \ + FIELDOP(op)(&z, &x, &y); \ + FIELDOP(store)(out[RZ].v.fe, &z); \ + } + +#define UNOP(op) \ + static void test_##op(struct reg *out, \ + const struct reg *in, void *ctx) \ + { \ + FIELD x, z; \ + \ + FIELDOP(load)(&x, in[RX].v.fe); \ + FIELDOP(op)(&z, &x); \ + FIELDOP(store)(out[RZ].v.fe, &z); \ + } + +BINOP(add) +BINOP(sub) +BINOP(mul) +UNOP(neg) +UNOP(sqr) +UNOP(inv) + +static void test_condneg(struct reg *out, const struct reg *in, void *ctx) +{ + FIELD x, z; + + FIELDOP(load)(&x, in[RX].v.fe); + FIELDOP(condneg)(&z, &x, in[RM].v.u); + FIELDOP(store)(out[RZ].v.fe, &z); +} + +static void test_mulconst(struct reg *out, const struct reg *in, void *ctx) +{ + FIELD x, z; + + FIELDOP(load)(&x, in[RX].v.fe); + FIELDOP(mulconst)(&z, &x, in[RA].v.i); + FIELDOP(store)(out[RZ].v.fe, &z); +} + +static void test_condswap(struct reg *out, const struct reg *in, void *ctx) +{ + FIELD x, y; + + FIELDOP(load)(&x, in[RX].v.fe); + FIELDOP(load)(&y, in[RY].v.fe); + FIELDOP(condswap)(&x, &y, in[RM].v.u); + FIELDOP(store)(out[RXX].v.fe, &x); + FIELDOP(store)(out[RYY].v.fe, &y); +} + +static void test_pick2(struct reg *out, const struct reg *in, void *ctx) +{ + FIELD x, y, z; + + FIELDOP(load)(&x, in[RX].v.fe); + FIELDOP(load)(&y, in[RY].v.fe); + FIELDOP(pick2)(&z, &x, &y, in[RM].v.u); + FIELDOP(store)(out[RZ].v.fe, &z); +} + +static void test_pickn(struct reg *out, const struct reg *in, void *ctx) +{ + FIELD v[32], z; + unsigned i; + + for (i = 0; in[RV0 + i].f®F_LIVE; i++) + FIELDOP(load)(&v[i], in[RV0 + i].v.fe); + FIELDOP(pickn)(&z, v, i, in[RI].v.u); + FIELDOP(store)(out[RZ].v.fe, &z); +} + +static void test_quosqrt(struct reg *out, const struct reg *in, void *ctx) +{ + FIELD x, y, z; + + FIELDOP(load)(&x, in[RX].v.fe); + FIELDOP(load)(&y, in[RY].v.fe); + if (FIELDOP(quosqrt)(&z, &x, &y)) + memset(out[RZ0].v.fe, 0xff, sizeof(out[RZ].v.fe)); + else + FIELDOP(store)(out[RZ].v.fe, &z); +} + +static void run_quosqrt(struct test_state *state, const struct test *test) +{ + test->fn(state->out, state->in, 0); + + /* ..._quosqrt returns an arbitrary square root. The test vector + * contains both. We win if we match either. + */ + if (eq_fe(&state->in[RZ1].v, &state->out[RZ].v)) + state->out[RZ0].v = state->in[RZ0].v; + state->out[RZ1].v = state->in[RZ1].v; + check_test_output(state, test); +} + +static void test_sub_mulc_add_sub_mul(struct reg *out, + const struct reg *in, void *ctx) +{ + FIELD u, v, w, x, y, z; + + FIELDOP(load)(&u, in[RU].v.fe); + FIELDOP(load)(&v, in[RV].v.fe); + FIELDOP(load)(&w, in[RW].v.fe); + FIELDOP(load)(&x, in[RX].v.fe); + FIELDOP(load)(&y, in[RY].v.fe); + + FIELDOP(sub)(&z, &u, &v); + FIELDOP(mulconst)(&z, &z, in[RA].v.i); + FIELDOP(add)(&z, &z, &w); + FIELDOP(sub)(&x, &x, &y); + FIELDOP(mul)(&z, &z, &x); + FIELDOP(store)(out[RZ].v.fe, &z); +} + +#define REG_U { "u", RU, ®ty_fe, 0 } +#define REG_V { "v", RV, ®ty_fe, 0 } +#define REG_W { "w", RW, ®ty_fe, 0 } +#define REG_X { "x", RX, ®ty_fe, 0 } +#define REG_Y { "y", RY, ®ty_fe, 0 } +#define REG_A { "a", RA, ®ty_int, 0 } +#define REG_M { "m", RM, ®ty_uint, 0 } +#define REG_I { "i", RI, ®ty_uint, 0 } +#define REG_XX { "xx", RXX, ®ty_fe, 0 } +#define REG_YY { "yy", RYY, ®ty_fe, 0 } +#define REG_Z { "z", RZ, ®ty_fe, 0 } +#define REG_Z0 { "z0", RZ0, ®ty_fe, 0 } +#define REG_Z1 { "z1", RZ1, ®ty_fe, 0 } +#define REG_BIGY { "Y", RY, ®ty_fe, 0 } +#define REG_BIGZ { "Z", RZ, ®ty_fe, 0 } +#define REG_N { "n", RN, ®ty_uint, 0 } +#define REG_Vi(i) { "v[" # i "]", RV0 + i, ®ty_fe, REGF_OPT } +#define REG_VV \ + REG_Vi( 0), REG_Vi( 1), REG_Vi( 2), REG_Vi( 3), \ + REG_Vi( 4), REG_Vi( 5), REG_Vi( 6), REG_Vi( 7), \ + REG_Vi( 8), REG_Vi( 9), REG_Vi(10), REG_Vi(11), \ + REG_Vi(12), REG_Vi(13), REG_Vi(14), REG_Vi(15), \ + REG_Vi(16), REG_Vi(17), REG_Vi(18), REG_Vi(19), \ + REG_Vi(20), REG_Vi(21), REG_Vi(22), REG_Vi(23), \ + REG_Vi(24), REG_Vi(25), REG_Vi(26), REG_Vi(27), \ + REG_Vi(28), REG_Vi(29), REG_Vi(30), REG_Vi(31) +static const struct regdef + unop_regs[] = { REG_X, REG_Z, REGLIST_END }, + binop_regs[] = { REG_X, REG_Y, REG_Z, REGLIST_END }, + condneg_regs[] = { REG_X, REG_M, REG_Z, REGLIST_END }, + mulconst_regs[] = { REG_X, REG_A, REG_Z, REGLIST_END }, + pick2_regs[] = { REG_X, REG_Y, REG_M, REG_Z, REGLIST_END }, + pickn_regs[] = { REG_VV, REG_I, REG_Z, REGLIST_END }, + condswap_regs[] = { REG_X, REG_Y, REG_M, REG_XX, REG_YY, REGLIST_END }, + quosqrt_regs[] = { REG_X, REG_Y, REG_Z0, REG_Z1, REGLIST_END }, + sub_mulc_add_sub_mul_regs[] = + { REG_U, REG_V, REG_A, REG_W, REG_X, REG_Y, REG_Z, REGLIST_END }; + +static const struct test tests[] = { + { "add", run_test, binop_regs, test_add }, + { "sub", run_test, binop_regs, test_sub }, + { "neg", run_test, unop_regs, test_neg }, + { "condneg", run_test, condneg_regs, test_condneg }, + { "condswap", run_test, condswap_regs, test_condswap }, + { "mulconst", run_test, mulconst_regs, test_mulconst }, + { "mul", run_test, binop_regs, test_mul }, + { "sqr", run_test, unop_regs, test_sqr }, + { "inv", run_test, unop_regs, test_inv }, + { "pick2", run_test, pick2_regs, test_pick2 }, + { "pickn", run_test, pickn_regs, test_pickn }, + { "quosqrt", run_quosqrt, quosqrt_regs, test_quosqrt }, + { "sub-mulc-add-sub-mul", run_test, + sub_mulc_add_sub_mul_regs, test_sub_mulc_add_sub_mul }, + { 0 } +}; + +int main(void) + { return run_test_suite(NROUT, NREG, sizeof(struct reg), tests, stdin); }