summaryrefslogtreecommitdiffstats
path: root/src/gallium/drivers/nv50/nv50_pc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/drivers/nv50/nv50_pc.c')
-rw-r--r--src/gallium/drivers/nv50/nv50_pc.c814
1 files changed, 0 insertions, 814 deletions
diff --git a/src/gallium/drivers/nv50/nv50_pc.c b/src/gallium/drivers/nv50/nv50_pc.c
deleted file mode 100644
index 9137f871f5a..00000000000
--- a/src/gallium/drivers/nv50/nv50_pc.c
+++ /dev/null
@@ -1,814 +0,0 @@
-/*
- * Copyright 2010 Christoph Bumiller
- *
- * 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 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 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.
- */
-
-#include "nv50_pc.h"
-#include "nv50_program.h"
-
-#include <stdio.h>
-
-/* returns TRUE if operands 0 and 1 can be swapped */
-boolean
-nv_op_commutative(uint opcode)
-{
- switch (opcode) {
- case NV_OP_ADD:
- case NV_OP_MUL:
- case NV_OP_MAD:
- case NV_OP_AND:
- case NV_OP_OR:
- case NV_OP_XOR:
- case NV_OP_MIN:
- case NV_OP_MAX:
- case NV_OP_SAD:
- return TRUE;
- default:
- return FALSE;
- }
-}
-
-/* return operand to which the address register applies */
-int
-nv50_indirect_opnd(struct nv_instruction *i)
-{
- if (!i->src[4])
- return -1;
-
- switch (i->opcode) {
- case NV_OP_MOV:
- case NV_OP_LDA:
- case NV_OP_STA:
- return 0;
- default:
- return 1;
- }
-}
-
-boolean
-nv50_nvi_can_use_imm(struct nv_instruction *nvi, int s)
-{
- if (nvi->flags_src || nvi->flags_def)
- return FALSE;
-
- switch (nvi->opcode) {
- case NV_OP_ADD:
- case NV_OP_MUL:
- case NV_OP_AND:
- case NV_OP_OR:
- case NV_OP_XOR:
- case NV_OP_SHL:
- case NV_OP_SHR:
- return (s == 1) && (nvi->src[0]->value->reg.file == NV_FILE_GPR) &&
- (nvi->def[0]->reg.file == NV_FILE_GPR);
- case NV_OP_MOV:
- assert(s == 0);
- return (nvi->def[0]->reg.file == NV_FILE_GPR);
- default:
- return FALSE;
- }
-}
-
-boolean
-nv50_nvi_can_load(struct nv_instruction *nvi, int s, struct nv_value *value)
-{
- int i;
-
- for (i = 0; i < 3 && nvi->src[i]; ++i)
- if (nvi->src[i]->value->reg.file == NV_FILE_IMM)
- return FALSE;
-
- switch (nvi->opcode) {
- case NV_OP_ABS:
- case NV_OP_ADD:
- case NV_OP_CEIL:
- case NV_OP_FLOOR:
- case NV_OP_TRUNC:
- case NV_OP_CVT:
- case NV_OP_ROUND:
- case NV_OP_NEG:
- case NV_OP_MAD:
- case NV_OP_MUL:
- case NV_OP_SAT:
- case NV_OP_SUB:
- case NV_OP_MAX:
- case NV_OP_MIN:
- if (s == 0 && (value->reg.file == NV_FILE_MEM_S ||
- value->reg.file == NV_FILE_MEM_P))
- return TRUE;
- if (value->reg.file < NV_FILE_MEM_C(0) ||
- value->reg.file > NV_FILE_MEM_C(15))
- return FALSE;
- return (s == 1) ||
- ((s == 2) && (nvi->src[1]->value->reg.file == NV_FILE_GPR));
- case NV_OP_MOV:
- assert(s == 0);
- return /* TRUE */ FALSE; /* don't turn MOVs into loads */
- default:
- return FALSE;
- }
-}
-
-/* Return whether this instruction can be executed conditionally. */
-boolean
-nv50_nvi_can_predicate(struct nv_instruction *nvi)
-{
- int i;
-
- if (nvi->flags_src)
- return FALSE;
- for (i = 0; i < 4 && nvi->src[i]; ++i)
- if (nvi->src[i]->value->reg.file == NV_FILE_IMM)
- return FALSE;
- return TRUE;
-}
-
-ubyte
-nv50_supported_src_mods(uint opcode, int s)
-{
- switch (opcode) {
- case NV_OP_ABS:
- return NV_MOD_NEG | NV_MOD_ABS; /* obviously */
- case NV_OP_ADD:
- case NV_OP_MUL:
- case NV_OP_MAD:
- return NV_MOD_NEG;
- case NV_OP_DFDX:
- case NV_OP_DFDY:
- assert(s == 0);
- return NV_MOD_NEG;
- case NV_OP_MAX:
- case NV_OP_MIN:
- return NV_MOD_ABS;
- case NV_OP_CVT:
- case NV_OP_LG2:
- case NV_OP_NEG:
- case NV_OP_PREEX2:
- case NV_OP_PRESIN:
- case NV_OP_RCP:
- case NV_OP_RSQ:
- return NV_MOD_ABS | NV_MOD_NEG;
- default:
- return 0;
- }
-}
-
-/* We may want an opcode table. */
-boolean
-nv50_op_can_write_flags(uint opcode)
-{
- if (nv_is_vector_op(opcode))
- return FALSE;
- switch (opcode) { /* obvious ones like KIL, CALL, etc. not included */
- case NV_OP_PHI:
- case NV_OP_MOV:
- case NV_OP_SELECT:
- case NV_OP_LINTERP:
- case NV_OP_PINTERP:
- case NV_OP_LDA:
- return FALSE;
- default:
- break;
- }
- if (opcode >= NV_OP_RCP && opcode <= NV_OP_PREEX2)
- return FALSE;
- return TRUE;
-}
-
-int
-nv_nvi_refcount(struct nv_instruction *nvi)
-{
- int i, rc;
-
- rc = nvi->flags_def ? nvi->flags_def->refc : 0;
-
- for (i = 0; i < 4; ++i) {
- if (!nvi->def[i])
- return rc;
- rc += nvi->def[i]->refc;
- }
- return rc;
-}
-
-int
-nvcg_replace_value(struct nv_pc *pc, struct nv_value *old_val,
- struct nv_value *new_val)
-{
- int i, n;
-
- if (old_val == new_val)
- return old_val->refc;
-
- for (i = 0, n = 0; i < pc->num_refs; ++i) {
- if (pc->refs[i]->value == old_val) {
- ++n;
- nv_reference(pc, &pc->refs[i], new_val);
- }
- }
- return n;
-}
-
-struct nv_value *
-nvcg_find_constant(struct nv_ref *ref)
-{
- struct nv_value *src;
-
- if (!ref)
- return NULL;
-
- src = ref->value;
- while (src->insn && src->insn->opcode == NV_OP_MOV) {
- assert(!src->insn->src[0]->mod);
- src = src->insn->src[0]->value;
- }
- if ((src->reg.file == NV_FILE_IMM) ||
- (src->insn && src->insn->opcode == NV_OP_LDA &&
- src->insn->src[0]->value->reg.file >= NV_FILE_MEM_C(0) &&
- src->insn->src[0]->value->reg.file <= NV_FILE_MEM_C(15)))
- return src;
- return NULL;
-}
-
-struct nv_value *
-nvcg_find_immediate(struct nv_ref *ref)
-{
- struct nv_value *src = nvcg_find_constant(ref);
-
- return (src && src->reg.file == NV_FILE_IMM) ? src : NULL;
-}
-
-static void
-nv_pc_free_refs(struct nv_pc *pc)
-{
- int i;
- for (i = 0; i < pc->num_refs; i += 64)
- FREE(pc->refs[i]);
- FREE(pc->refs);
-}
-
-static const char *
-edge_name(ubyte type)
-{
- switch (type) {
- case CFG_EDGE_FORWARD: return "forward";
- case CFG_EDGE_BACK: return "back";
- case CFG_EDGE_LOOP_ENTER: return "loop";
- case CFG_EDGE_LOOP_LEAVE: return "break";
- case CFG_EDGE_FAKE: return "fake";
- default:
- return "?";
- }
-}
-
-void
-nv_pc_pass_in_order(struct nv_basic_block *root, nv_pc_pass_func f, void *priv)
-{
- struct nv_basic_block *bb[64], *bbb[16], *b;
- int j, p, pp;
-
- bb[0] = root;
- p = 1;
- pp = 0;
-
- while (p > 0) {
- b = bb[--p];
- b->priv = 0;
-
- for (j = 1; j >= 0; --j) {
- if (!b->out[j])
- continue;
-
- switch (b->out_kind[j]) {
- case CFG_EDGE_BACK:
- continue;
- case CFG_EDGE_FORWARD:
- case CFG_EDGE_FAKE:
- if (++b->out[j]->priv == b->out[j]->num_in)
- bb[p++] = b->out[j];
- break;
- case CFG_EDGE_LOOP_ENTER:
- bb[p++] = b->out[j];
- break;
- case CFG_EDGE_LOOP_LEAVE:
- if (!b->out[j]->priv) {
- bbb[pp++] = b->out[j];
- b->out[j]->priv = 1;
- }
- break;
- default:
- assert(0);
- break;
- }
- }
-
- f(priv, b);
-
- if (!p) {
- p = pp;
- for (; pp > 0; --pp)
- bb[pp - 1] = bbb[pp - 1];
- }
- }
-}
-
-static void
-nv_do_print_function(void *priv, struct nv_basic_block *b)
-{
- struct nv_instruction *i;
-
- debug_printf("=== BB %i ", b->id);
- if (b->out[0])
- debug_printf("[%s -> %i] ", edge_name(b->out_kind[0]), b->out[0]->id);
- if (b->out[1])
- debug_printf("[%s -> %i] ", edge_name(b->out_kind[1]), b->out[1]->id);
- debug_printf("===\n");
-
- i = b->phi;
- if (!i)
- i = b->entry;
- for (; i; i = i->next)
- nv_print_instruction(i);
-}
-
-void
-nv_print_function(struct nv_basic_block *root)
-{
- if (root->subroutine)
- debug_printf("SUBROUTINE %i\n", root->subroutine);
- else
- debug_printf("MAIN\n");
-
- nv_pc_pass_in_order(root, nv_do_print_function, root);
-}
-
-void
-nv_print_program(struct nv_pc *pc)
-{
- int i;
- for (i = 0; i < pc->num_subroutines + 1; ++i)
- if (pc->root[i])
- nv_print_function(pc->root[i]);
-}
-
-#if NV50_DEBUG & NV50_DEBUG_PROG_CFLOW
-static void
-nv_do_print_cfgraph(struct nv_pc *pc, FILE *f, struct nv_basic_block *b)
-{
- int i;
-
- b->pass_seq = pc->pass_seq;
-
- fprintf(f, "\t%i [shape=box]\n", b->id);
-
- for (i = 0; i < 2; ++i) {
- if (!b->out[i])
- continue;
- switch (b->out_kind[i]) {
- case CFG_EDGE_FORWARD:
- fprintf(f, "\t%i -> %i;\n", b->id, b->out[i]->id);
- break;
- case CFG_EDGE_LOOP_ENTER:
- fprintf(f, "\t%i -> %i [color=green];\n", b->id, b->out[i]->id);
- break;
- case CFG_EDGE_LOOP_LEAVE:
- fprintf(f, "\t%i -> %i [color=red];\n", b->id, b->out[i]->id);
- break;
- case CFG_EDGE_BACK:
- fprintf(f, "\t%i -> %i;\n", b->id, b->out[i]->id);
- continue;
- case CFG_EDGE_FAKE:
- fprintf(f, "\t%i -> %i [style=dotted];\n", b->id, b->out[i]->id);
- break;
- default:
- assert(0);
- break;
- }
- if (b->out[i]->pass_seq < pc->pass_seq)
- nv_do_print_cfgraph(pc, f, b->out[i]);
- }
-}
-
-/* Print the control flow graph of subroutine @subr (0 == MAIN) to a file. */
-static void
-nv_print_cfgraph(struct nv_pc *pc, const char *filepath, int subr)
-{
- FILE *f;
-
- f = fopen(filepath, "a");
- if (!f)
- return;
-
- fprintf(f, "digraph G {\n");
-
- ++pc->pass_seq;
-
- nv_do_print_cfgraph(pc, f, pc->root[subr]);
-
- fprintf(f, "}\n");
-
- fclose(f);
-}
-#endif /* NV50_DEBUG_PROG_CFLOW */
-
-static INLINE void
-nvcg_show_bincode(struct nv_pc *pc)
-{
- unsigned i;
-
- for (i = 0; i < pc->bin_size / 4; ++i) {
- debug_printf("0x%08x ", pc->emit[i]);
- if ((i % 16) == 15)
- debug_printf("\n");
- }
- debug_printf("\n");
-}
-
-static int
-nv50_emit_program(struct nv_pc *pc)
-{
- uint32_t *code = pc->emit;
- int n;
-
- NV50_DBGMSG(SHADER, "emitting program: size = %u\n", pc->bin_size);
-
- for (n = 0; n < pc->num_blocks; ++n) {
- struct nv_instruction *i;
- struct nv_basic_block *b = pc->bb_list[n];
-
- for (i = b->entry; i; i = i->next) {
- nv50_emit_instruction(pc, i);
-
- pc->bin_pos += 1 + (pc->emit[0] & 1);
- pc->emit += 1 + (pc->emit[0] & 1);
- }
- }
- assert(pc->emit == &code[pc->bin_size / 4]);
-
- /* XXX: we can do better than this ... */
- if (!pc->bin_size ||
- !(pc->emit[-2] & 1) || (pc->emit[-2] & 2) || (pc->emit[-1] & 3)) {
- pc->emit[0] = 0xf0000001;
- pc->emit[1] = 0xe0000000;
- pc->bin_size += 8;
- }
-
- pc->emit = code;
- code[pc->bin_size / 4 - 1] |= 1;
-
-#if NV50_DEBUG & NV50_DEBUG_SHADER
- nvcg_show_bincode(pc);
-#endif
-
- return 0;
-}
-
-int
-nv50_generate_code(struct nv50_translation_info *ti)
-{
- struct nv_pc *pc;
- int ret;
- int i;
-
- pc = CALLOC_STRUCT(nv_pc);
- if (!pc)
- return 1;
-
- pc->root = CALLOC(ti->subr_nr + 1, sizeof(pc->root[0]));
- if (!pc->root) {
- FREE(pc);
- return 1;
- }
- pc->num_subroutines = ti->subr_nr;
-
- ret = nv50_tgsi_to_nc(pc, ti);
- if (ret)
- goto out;
-#if NV50_DEBUG & NV50_DEBUG_PROG_IR
- nv_print_program(pc);
-#endif
-
- pc->opt_reload_elim = ti->store_to_memory ? FALSE : TRUE;
-
- /* optimization */
- ret = nv_pc_exec_pass0(pc);
- if (ret)
- goto out;
-#if NV50_DEBUG & NV50_DEBUG_PROG_IR
- nv_print_program(pc);
-#endif
-
- /* register allocation */
- ret = nv_pc_exec_pass1(pc);
- if (ret)
- goto out;
-#if NV50_DEBUG & NV50_DEBUG_PROG_CFLOW
- nv_print_program(pc);
- nv_print_cfgraph(pc, "nv50_shader_cfgraph.dot", 0);
-#endif
-
- /* prepare for emission */
- ret = nv_pc_exec_pass2(pc);
- if (ret)
- goto out;
- assert(!(pc->bin_size % 8));
-
- pc->emit = CALLOC(pc->bin_size / 4 + 2, 4);
- if (!pc->emit) {
- ret = 3;
- goto out;
- }
- ret = nv50_emit_program(pc);
- if (ret)
- goto out;
-
- ti->p->code_size = pc->bin_size;
- ti->p->code = pc->emit;
-
- ti->p->immd_size = pc->immd_count * 4;
- ti->p->immd = pc->immd_buf;
-
- /* highest 16 bit reg to num of 32 bit regs, limit to >= 4 */
- ti->p->max_gpr = MAX2(4, (pc->max_reg[NV_FILE_GPR] >> 1) + 1);
-
- ti->p->fixups = pc->fixups;
- ti->p->num_fixups = pc->num_fixups;
-
- ti->p->uses_lmem = ti->store_to_memory;
-
- NV50_DBGMSG(SHADER, "SHADER TRANSLATION - %s\n", ret ? "failed" : "success");
-
-out:
- nv_pc_free_refs(pc);
-
- for (i = 0; i < pc->num_blocks; ++i)
- FREE(pc->bb_list[i]);
- if (pc->root)
- FREE(pc->root);
- if (ret) { /* on success, these will be referenced by nv50_program */
- if (pc->emit)
- FREE(pc->emit);
- if (pc->immd_buf)
- FREE(pc->immd_buf);
- if (pc->fixups)
- FREE(pc->fixups);
- }
- FREE(pc);
- return ret;
-}
-
-static void
-nvbb_insert_phi(struct nv_basic_block *b, struct nv_instruction *i)
-{
- if (!b->phi) {
- i->prev = NULL;
- b->phi = i;
- i->next = b->entry;
- if (b->entry) {
- assert(!b->entry->prev && b->exit);
- b->entry->prev = i;
- } else {
- b->entry = i;
- b->exit = i;
- }
- } else {
- assert(b->entry);
- if (b->entry->opcode == NV_OP_PHI) { /* insert after entry */
- assert(b->entry == b->exit);
- b->entry->next = i;
- i->prev = b->entry;
- b->entry = i;
- b->exit = i;
- } else { /* insert before entry */
- assert(b->entry->prev && b->exit);
- i->next = b->entry;
- i->prev = b->entry->prev;
- b->entry->prev = i;
- i->prev->next = i;
- }
- }
-}
-
-void
-nvbb_insert_tail(struct nv_basic_block *b, struct nv_instruction *i)
-{
- if (i->opcode == NV_OP_PHI) {
- nvbb_insert_phi(b, i);
- } else {
- i->prev = b->exit;
- if (b->exit)
- b->exit->next = i;
- b->exit = i;
- if (!b->entry)
- b->entry = i;
- else
- if (i->prev && i->prev->opcode == NV_OP_PHI)
- b->entry = i;
- }
-
- i->bb = b;
- b->num_instructions++;
-
- if (i->prev && i->prev->is_terminator)
- nv_nvi_permute(i->prev, i);
-}
-
-void
-nvi_insert_after(struct nv_instruction *at, struct nv_instruction *ni)
-{
- if (!at->next) {
- nvbb_insert_tail(at->bb, ni);
- return;
- }
- ni->next = at->next;
- ni->prev = at;
- ni->next->prev = ni;
- ni->prev->next = ni;
-}
-
-void
-nv_nvi_delete(struct nv_instruction *nvi)
-{
- struct nv_basic_block *b = nvi->bb;
- int j;
-
- /* debug_printf("REM: "); nv_print_instruction(nvi); */
-
- for (j = 0; j < 5; ++j)
- nv_reference(NULL, &nvi->src[j], NULL);
- nv_reference(NULL, &nvi->flags_src, NULL);
-
- if (nvi->next)
- nvi->next->prev = nvi->prev;
- else {
- assert(nvi == b->exit);
- b->exit = nvi->prev;
- }
-
- if (nvi->prev)
- nvi->prev->next = nvi->next;
-
- if (nvi == b->entry) {
- /* PHIs don't get hooked to b->entry */
- b->entry = nvi->next;
- assert(!nvi->prev || nvi->prev->opcode == NV_OP_PHI);
- }
-
- if (nvi == b->phi) {
- if (nvi->opcode != NV_OP_PHI)
- NV50_DBGMSG(PROG_IR, "NOTE: b->phi points to non-PHI instruction\n");
-
- assert(!nvi->prev);
- if (!nvi->next || nvi->next->opcode != NV_OP_PHI)
- b->phi = NULL;
- else
- b->phi = nvi->next;
- }
-}
-
-void
-nv_nvi_permute(struct nv_instruction *i1, struct nv_instruction *i2)
-{
- struct nv_basic_block *b = i1->bb;
-
- assert(i1->opcode != NV_OP_PHI &&
- i2->opcode != NV_OP_PHI);
- assert(i1->next == i2);
-
- if (b->exit == i2)
- b->exit = i1;
-
- if (b->entry == i1)
- b->entry = i2;
-
- i2->prev = i1->prev;
- i1->next = i2->next;
- i2->next = i1;
- i1->prev = i2;
-
- if (i2->prev)
- i2->prev->next = i2;
- if (i1->next)
- i1->next->prev = i1;
-}
-
-void
-nvbb_attach_block(struct nv_basic_block *parent,
- struct nv_basic_block *b, ubyte edge_kind)
-{
- assert(b->num_in < 8);
-
- if (parent->out[0]) {
- assert(!parent->out[1]);
- parent->out[1] = b;
- parent->out_kind[1] = edge_kind;
- } else {
- parent->out[0] = b;
- parent->out_kind[0] = edge_kind;
- }
-
- b->in[b->num_in] = parent;
- b->in_kind[b->num_in++] = edge_kind;
-}
-
-/* NOTE: all BRKs are treated as conditional, so there are 2 outgoing BBs */
-
-boolean
-nvbb_dominated_by(struct nv_basic_block *b, struct nv_basic_block *d)
-{
- int j;
-
- if (b == d)
- return TRUE;
-
- for (j = 0; j < b->num_in; ++j)
- if ((b->in_kind[j] != CFG_EDGE_BACK) && !nvbb_dominated_by(b->in[j], d))
- return FALSE;
-
- return j ? TRUE : FALSE;
-}
-
-/* check if @bf (future) can be reached from @bp (past), stop at @bt */
-boolean
-nvbb_reachable_by(struct nv_basic_block *bf, struct nv_basic_block *bp,
- struct nv_basic_block *bt)
-{
- struct nv_basic_block *q[NV_PC_MAX_BASIC_BLOCKS], *b;
- int i, p, n;
-
- p = 0;
- n = 1;
- q[0] = bp;
-
- while (p < n) {
- b = q[p++];
-
- if (b == bf)
- break;
- if (b == bt)
- continue;
- assert(n <= (1024 - 2));
-
- for (i = 0; i < 2; ++i) {
- if (b->out[i] && !IS_WALL_EDGE(b->out_kind[i]) && !b->out[i]->priv) {
- q[n] = b->out[i];
- q[n++]->priv = 1;
- }
- }
- }
- for (--n; n >= 0; --n)
- q[n]->priv = 0;
-
- return (b == bf);
-}
-
-static struct nv_basic_block *
-nvbb_find_dom_frontier(struct nv_basic_block *b, struct nv_basic_block *df)
-{
- struct nv_basic_block *out;
- int i;
-
- if (!nvbb_dominated_by(df, b)) {
- for (i = 0; i < df->num_in; ++i) {
- if (df->in_kind[i] == CFG_EDGE_BACK)
- continue;
- if (nvbb_dominated_by(df->in[i], b))
- return df;
- }
- }
- for (i = 0; i < 2 && df->out[i]; ++i) {
- if (df->out_kind[i] == CFG_EDGE_BACK)
- continue;
- if ((out = nvbb_find_dom_frontier(b, df->out[i])))
- return out;
- }
- return NULL;
-}
-
-struct nv_basic_block *
-nvbb_dom_frontier(struct nv_basic_block *b)
-{
- struct nv_basic_block *df;
- int i;
-
- for (i = 0; i < 2 && b->out[i]; ++i)
- if ((df = nvbb_find_dom_frontier(b, b->out[i])))
- return df;
- return NULL;
-}