summaryrefslogtreecommitdiff
path: root/src/mesa/drivers/dri/nouveau/nouveau_shader_1.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mesa/drivers/dri/nouveau/nouveau_shader_1.c')
-rw-r--r--src/mesa/drivers/dri/nouveau/nouveau_shader_1.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/src/mesa/drivers/dri/nouveau/nouveau_shader_1.c b/src/mesa/drivers/dri/nouveau/nouveau_shader_1.c
new file mode 100644
index 0000000000..5de9017f58
--- /dev/null
+++ b/src/mesa/drivers/dri/nouveau/nouveau_shader_1.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2006 Ben Skeggs.
+ *
+ * All Rights Reserved.
+ *
+ * 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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:
+ * Ben Skeggs <darktama@iinet.net.au>
+ */
+
+#include "glheader.h"
+#include "macros.h"
+#include "enums.h"
+
+#include "nouveau_shader.h"
+
+#define PASS1_OK 0
+#define PASS1_KILL 1
+#define PASS1_FAIL 2
+
+struct pass1_rec {
+ unsigned int temp[NVS_MAX_TEMPS];
+ unsigned int result[NVS_MAX_ATTRIBS];
+ unsigned int address[NVS_MAX_ADDRESS];
+ unsigned int cc[2];
+};
+
+static void
+pass1_remove_fragment(nvsPtr nvs, nvsFragmentList *item)
+{
+ if (item->prev) item->prev->next = item->next;
+ if (item->next) item->next->prev = item->prev;
+ if (nvs->list_head == item) nvs->list_head = item->next;
+ if (nvs->list_tail == item) nvs->list_tail = item->prev;
+
+ nvs->inst_count--;
+}
+
+static int
+pass1_result_needed(struct pass1_rec *rec, nvsInstruction *inst)
+{
+ if (inst->cond_update && rec->cc[inst->cond_reg])
+ return 1;
+ /* Only write components that are read later */
+ if (inst->dest.file == NVS_FILE_TEMP)
+ return (inst->mask & rec->temp[inst->dest.index]);
+ if (inst->dest.file == NVS_FILE_ADDRESS)
+ return (inst->mask & rec->address[inst->dest.index]);
+ /* No point writing result components that are written later */
+ if (inst->dest.file == NVS_FILE_RESULT)
+ return (inst->mask & ~rec->result[inst->dest.index]);
+ assert(0);
+}
+
+static void
+pass1_track_result(struct pass1_rec *rec, nvsInstruction *inst)
+{
+ if (inst->cond_test)
+ rec->cc[inst->cond_reg] = 1;
+ if (inst->dest.file == NVS_FILE_TEMP) {
+ inst->mask &= rec->temp[inst->dest.index];
+ } else if (inst->dest.file == NVS_FILE_RESULT) {
+ inst->mask &= ~rec->result[inst->dest.index];
+ rec->result[inst->dest.index] |= inst->mask;
+ } else if (inst->dest.file == NVS_FILE_ADDRESS) {
+ inst->mask &= rec->address[inst->dest.index];
+ }
+}
+
+static void
+pass1_track_source(nouveauShader *nvs, nvsInstruction *inst, int pos,
+ unsigned int read)
+{
+ struct pass1_rec *rec = nvs->pass_rec;
+ nvsRegister *src = &inst->src[pos];
+ unsigned int really_read = 0;
+ int i,sc;
+
+ /* Account for swizzling */
+ for (i=0; i<4; i++)
+ if (read & (1<<i)) really_read |= (1 << src->swizzle[i]);
+
+ /* Track register reads */
+ if (src->file == NVS_FILE_TEMP) {
+ if (nvs->temps[src->index].last_use == -1)
+ nvs->temps[src->index].last_use = inst->header.position;
+ rec->temp [src->index] |= really_read;
+ } else if (src->indexed) {
+ rec->address[src->addr_reg] |= (1<<src->addr_comp);
+ }
+
+ /* Modify swizzle to only access read components */
+ /* Find a component that is used.. */
+ for (sc=0;sc<4;sc++)
+ if (really_read & (1<<sc))
+ break;
+ /* Now set all unused components to that value */
+ for (i=0;i<4;i++)
+ if (!(read & (1<<i))) src->swizzle[i] = sc;
+}
+
+static int
+pass1_check_instruction(nouveauShader *nvs, nvsInstruction *inst)
+{
+ struct pass1_rec *rec = nvs->pass_rec;
+ unsigned int read0, read1, read2;
+
+ if (inst->op != NVS_OP_KIL) {
+ if (!pass1_result_needed(rec, inst))
+ return PASS1_KILL;
+ }
+ pass1_track_result(rec, inst);
+
+ read0 = read1 = read2 = 0;
+
+ switch (inst->op) {
+ case NVS_OP_FLR:
+ case NVS_OP_FRC:
+ case NVS_OP_MOV:
+ case NVS_OP_SSG:
+ case NVS_OP_ARL:
+ read0 = inst->mask;
+ break;
+ case NVS_OP_ADD:
+ case NVS_OP_MAX:
+ case NVS_OP_MIN:
+ case NVS_OP_MUL:
+ case NVS_OP_SEQ:
+ case NVS_OP_SFL:
+ case NVS_OP_SGE:
+ case NVS_OP_SGT:
+ case NVS_OP_SLE:
+ case NVS_OP_SLT:
+ case NVS_OP_SNE:
+ case NVS_OP_STR:
+ case NVS_OP_SUB:
+ read0 = inst->mask;
+ read1 = inst->mask;
+ break;
+ case NVS_OP_CMP:
+ case NVS_OP_LRP:
+ case NVS_OP_MAD:
+ read0 = inst->mask;
+ read1 = inst->mask;
+ read2 = inst->mask;
+ break;
+ case NVS_OP_XPD:
+ if (inst->mask & SMASK_X) read0 |= SMASK_Y|SMASK_Z;
+ if (inst->mask & SMASK_Y) read0 |= SMASK_X|SMASK_Z;
+ if (inst->mask & SMASK_Z) read0 |= SMASK_X|SMASK_Y;
+ read1 = read0;
+ break;
+ case NVS_OP_COS:
+ case NVS_OP_EX2:
+ case NVS_OP_EXP:
+ case NVS_OP_LG2:
+ case NVS_OP_LOG:
+ case NVS_OP_RCC:
+ case NVS_OP_RCP:
+ case NVS_OP_RSQ:
+ case NVS_OP_SCS:
+ case NVS_OP_SIN:
+ read0 = SMASK_X;
+ break;
+ case NVS_OP_POW:
+ read0 = SMASK_X;
+ read1 = SMASK_X;
+ break;
+ case NVS_OP_DIV:
+ read0 = inst->mask;
+ read1 = SMASK_X;
+ break;
+ case NVS_OP_DP2:
+ read0 = SMASK_X|SMASK_Y;
+ read1 = SMASK_X|SMASK_Y;
+ break;
+ case NVS_OP_DP3:
+ case NVS_OP_RFL:
+ read0 = SMASK_X|SMASK_Y|SMASK_Z;
+ read1 = SMASK_X|SMASK_Y|SMASK_Z;
+ break;
+ case NVS_OP_DP4:
+ read0 = SMASK_ALL;
+ read1 = SMASK_ALL;
+ break;
+ case NVS_OP_DPH:
+ read0 = SMASK_X|SMASK_Y|SMASK_Z;
+ read1 = SMASK_ALL;
+ break;
+ case NVS_OP_DST:
+ if (inst->mask & SMASK_Y) read0 = read1 = SMASK_Y;
+ if (inst->mask & SMASK_Z) read0 |= SMASK_Z;
+ if (inst->mask & SMASK_W) read1 |= SMASK_W;
+ break;
+ case NVS_OP_NRM:
+ read0 = SMASK_X|SMASK_Y|SMASK_Z;
+ break;
+ case NVS_OP_PK2H:
+ case NVS_OP_PK2US:
+ read0 = SMASK_X|SMASK_Y;
+ break;
+ case NVS_OP_DDX:
+ case NVS_OP_DDY:
+ case NVS_OP_UP2H:
+ case NVS_OP_UP2US:
+ case NVS_OP_PK4B:
+ case NVS_OP_PK4UB:
+ case NVS_OP_UP4B:
+ case NVS_OP_UP4UB:
+ read0 = SMASK_ALL;
+ break;
+ case NVS_OP_X2D:
+ read1 = SMASK_X|SMASK_Y;
+ if (inst->mask & (SMASK_X|SMASK_Z)) {
+ read0 |= SMASK_X;
+ read2 |= SMASK_X|SMASK_Y;
+ }
+ if (inst->mask & (SMASK_Y|SMASK_W)) {
+ read0 |= SMASK_Y;
+ read2 |= SMASK_Z|SMASK_W;
+ }
+ break;
+ case NVS_OP_LIT:
+ read0 |= SMASK_X|SMASK_Y|SMASK_W;
+ break;
+ case NVS_OP_TEX:
+ case NVS_OP_TXP:
+ case NVS_OP_TXL:
+ case NVS_OP_TXB:
+ read0 = SMASK_ALL;
+ break;
+ case NVS_OP_TXD:
+ read0 = SMASK_ALL;
+ read1 = SMASK_ALL;
+ read2 = SMASK_ALL;
+ break;
+ case NVS_OP_KIL:
+ break;
+ default:
+ fprintf(stderr, "Unknown sop=%d", inst->op);
+ return PASS1_FAIL;
+ }
+
+ /* Any values that are written by this inst can't have been read further up */
+ if (inst->dest.file == NVS_FILE_TEMP)
+ rec->temp[inst->dest.index] &= ~inst->mask;
+
+ if (read0) pass1_track_source(nvs, inst, 0, read0);
+ if (read1) pass1_track_source(nvs, inst, 1, read1);
+ if (read2) pass1_track_source(nvs, inst, 2, read2);
+
+ return PASS1_OK;
+}
+
+/* Some basic dead code elimination
+ * - Remove unused instructions
+ * - Don't write unused register components
+ * - Modify swizzles to not reference unneeded components.
+ */
+GLboolean
+nouveau_shader_pass1(nvsPtr nvs)
+{
+ nvsFragmentList *list = nvs->list_tail;
+ int i;
+
+ for (i=0; i<NVS_MAX_TEMPS; i++)
+ nvs->temps[i].last_use = -1;
+
+ nvs->pass_rec = calloc(1, sizeof(struct pass1_rec));
+
+ while (list) {
+ assert(list->fragment->type == NVS_INSTRUCTION);
+
+ switch(pass1_check_instruction(nvs, (nvsInstruction *)list->fragment)) {
+ case PASS1_OK:
+ break;
+ case PASS1_KILL:
+ pass1_remove_fragment(nvs, list);
+ break;
+ case PASS1_FAIL:
+ default:
+ free(nvs->pass_rec);
+ nvs->pass_rec = NULL;
+ return GL_FALSE;
+ }
+
+ list = list->prev;
+ }
+
+ free(nvs->pass_rec);
+ nvs->pass_rec = NULL;
+
+ return GL_TRUE;
+}
+
+