/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ /* * Copyright (C) 2014 Rob Clark * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Rob Clark */ /* A simple emulator for shader program, for testing purposes. */ #include #include #include #include #include #include #include #include #include #include #include "instr-a3xx.h" #include "asm/ir-a3xx.h" static void read_file(const char *filename, void **ptr, size_t *size) { int fd, ret; struct stat st; *ptr = MAP_FAILED; fd = open(filename, O_RDONLY); if (fd == -1) errx(1, "couldn't open `%s'", filename); ret = fstat(fd, &st); if (ret) errx(1, "couldn't stat `%s'", filename); *size = st.st_size; *ptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (*ptr == MAP_FAILED) errx(1, "couldn't map `%s'", filename); close(fd); } static void randomize(float *buf, int num) { unsigned i; for (i = 0; i < num; i++) *(buf++) = drand48(); } static float inputs[64], consts[1024]; static void shader_run(struct ir3_shader *shader, struct ir3_shader_info *info, float *outputs, float *regs) { static uint32_t dwords[64 * 1024]; static float actual_consts[ARRAY_SIZE(consts)]; uint32_t regs_count, half_regs_count; unsigned i; /* shadow the consts used by all shaders, and overlay correct * immediate values, so that we can compare before/after shaders * if one uses immediates encoded in instructions or immediates * at a different const regs (ie. sysval const changes, etc) */ memcpy(actual_consts, consts, sizeof(consts)); for (i = 0; i < shader->consts_count; i++) { struct ir3_const *c = shader->consts[i]; actual_consts[c->cstart->num + 0] = uif(c->val[0]); actual_consts[c->cstart->num + 1] = uif(c->val[1]); actual_consts[c->cstart->num + 2] = uif(c->val[2]); actual_consts[c->cstart->num + 3] = uif(c->val[3]); } ir3_shader_assemble(shader, dwords, ARRAY_SIZE(dwords), info); assert(((info->max_const + 1) * 4) < ARRAY_SIZE(consts)); assert(((info->max_reg + 1) * 4) < MAX_REGS); assert(((info->max_half_reg + 1) * 4) < MAX_REGS); regs_count = (info->max_reg + 1) * 4; half_regs_count = (info->max_half_reg + 1) * 4; /* copy input values into registers: */ for (i = 0; i < shader->ins_count; i++) { struct ir3_in *in = shader->ins[i]; int idx = in->rstart->num; if (in->rstart->flags & IR3_REG_HALF) idx += regs_count; regs[idx] = inputs[i]; } ir3_emu_run(dwords, shader->instrs_count, actual_consts, (info->max_const + 1) * 4, regs, regs_count, half_regs_count); /* copy output values back from registers: */ for (i = 0; i < shader->outs_count; i++) { struct ir3_out *out = shader->outs[i]; int idx = out->rstart->num; if (out->rstart->flags & IR3_REG_HALF) idx += regs_count; outputs[i] = regs[idx]; } } static void dump_registers(struct ir3_shader_info *info, float *regs) { unsigned i; /* TODO dump half regs too.. */ for (i = 0; i <= (info->max_reg + 1) * 4; i += 4) { printf(" %02d: %08x %08x %08x %08x\n", (i / 4), fui(regs[i+0]), fui(regs[i+1]), fui(regs[i+2]), fui(regs[i+3])); } } static int shader_test(struct ir3_shader *ref, struct ir3_shader *test) { float outputs1[64], outputs2[64]; float regs1[2*MAX_REGS], regs2[2*MAX_REGS]; struct ir3_shader_info info1, info2; unsigned i; int ret = 0; randomize(inputs, ARRAY_SIZE(inputs)); randomize(consts, ARRAY_SIZE(consts)); /* give registers random initial values: */ randomize(regs1, ARRAY_SIZE(regs1)); randomize(regs2, ARRAY_SIZE(regs2)); shader_run(ref, &info1, outputs1, regs1); shader_run(test, &info2, outputs2, regs2); assert(ref->outs_count == test->outs_count); for (i = 0; i < ref->outs_count; i++) { if (fui(outputs1[i]) != fui(outputs2[i])) { printf("out%d: %f (%08x) vs %f (%08x)\n", i, outputs1[i], fui(outputs1[i]), outputs2[i], fui(outputs2[i])); ret = -1; } } if (ret) { /* on failure, dump regs: */ printf("Fail!\n"); printf("----------\n"); printf("reference registers:\n"); dump_registers(&info1, regs1); printf("test registers:\n"); dump_registers(&info2, regs2); printf("----------\n"); } else { printf("Ok!\n"); } return ret; } int main(int argc, char **argv) { char *origfile, *newfile; char *origasm, *newasm; struct ir3_shader *origir, *newir; size_t size; if (argc != 3) { printf("usage: %s orig.asm new.asm\n", argv[0]); return -1; } origfile = argv[1]; newfile = argv[2]; printf("************ Reading %s\n", origfile); read_file(origfile, (void **)&origasm, &size); origir = ir3_asm_parse(origasm); if (!origir) { printf("error parsing:\n%s\n", origasm); return -1; } printf("************ Reading %s\n", newfile); read_file(newfile, (void **)&newasm, &size); newir = ir3_asm_parse(newasm); if (!newir) { printf("error parsing:\n%s\n", newasm); return -1; } return shader_test(origir, newir); }