/* * 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 */ #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<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<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<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; itemps[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; }