nv50: delete old shader compiler files

This commit is contained in:
Christoph Bumiller 2012-03-18 23:28:07 +01:00
parent 0bbf1659df
commit df982399cc
7 changed files with 0 additions and 7252 deletions

View File

@ -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;
}

View File

@ -1,502 +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.
*/
#ifndef __NV50_COMPILER_H__
#define __NV50_COMPILER_H__
#include "nv50_debug.h"
#include "pipe/p_defines.h"
#include "util/u_inlines.h"
#include "util/u_memory.h"
#define NV_OP_PHI 0
#define NV_OP_EXTRACT 1
#define NV_OP_COMBINE 2
#define NV_OP_LDA 3
#define NV_OP_STA 4
#define NV_OP_MOV 5
#define NV_OP_ADD 6
#define NV_OP_SUB 7
#define NV_OP_NEG 8
#define NV_OP_MUL 9
#define NV_OP_MAD 10
#define NV_OP_CVT 11
#define NV_OP_SAT 12
#define NV_OP_NOT 13
#define NV_OP_AND 14
#define NV_OP_OR 15
#define NV_OP_XOR 16
#define NV_OP_SHL 17
#define NV_OP_SHR 18
#define NV_OP_RCP 19
#define NV_OP_UNDEF 20
#define NV_OP_RSQ 21
#define NV_OP_LG2 22
#define NV_OP_SIN 23
#define NV_OP_COS 24
#define NV_OP_EX2 25
#define NV_OP_PRESIN 26
#define NV_OP_PREEX2 27
#define NV_OP_MIN 28
#define NV_OP_MAX 29
#define NV_OP_SET 30
#define NV_OP_SAD 31
#define NV_OP_KIL 32
#define NV_OP_BRA 33
#define NV_OP_CALL 34
#define NV_OP_RET 35
#define NV_OP_BREAK 36
#define NV_OP_BREAKADDR 37
#define NV_OP_JOINAT 38
#define NV_OP_TEX 39
#define NV_OP_TXB 40
#define NV_OP_TXL 41
#define NV_OP_TXF 42
#define NV_OP_TXQ 43
#define NV_OP_DFDX 44
#define NV_OP_DFDY 45
#define NV_OP_QUADOP 46
#define NV_OP_LINTERP 47
#define NV_OP_PINTERP 48
#define NV_OP_ABS 49
#define NV_OP_CEIL 50
#define NV_OP_FLOOR 51
#define NV_OP_TRUNC 52
#define NV_OP_NOP 53
#define NV_OP_SELECT 54
#define NV_OP_EXPORT 55
#define NV_OP_JOIN 56
#define NV_OP_ROUND 57
#define NV_OP_COUNT 58
#define NV_FILE_GPR 0
#define NV_FILE_OUT 1
#define NV_FILE_ADDR 2
#define NV_FILE_FLAGS 3
#define NV_FILE_IMM 16
#define NV_FILE_MEM_S 32
#define NV_FILE_MEM_P 33
#define NV_FILE_MEM_V 34
#define NV_FILE_MEM_L 48
#define NV_FILE_MEM_G(i) (64 + i)
#define NV_FILE_MEM_C(i) (80 + i)
#define NV_MOD_NEG 1
#define NV_MOD_ABS 2
#define NV_MOD_NOT 4
#define NV_MOD_SAT 8
#define NV_TYPE_U8 0x00
#define NV_TYPE_S8 0x01
#define NV_TYPE_U16 0x02
#define NV_TYPE_S16 0x03
#define NV_TYPE_U32 0x04
#define NV_TYPE_S32 0x05
#define NV_TYPE_P32 0x07
#define NV_TYPE_F32 0x09
#define NV_TYPE_F64 0x0b
#define NV_TYPE_VEC(x, n) (NV_TYPE_##x | (n << 4))
#define NV_TYPE_LO 0x00
#define NV_TYPE_HI 0x80
#define NV_TYPE_ANY 0xff
#define NV_TYPE_ISINT(t) ((t) <= 5)
#define NV_TYPE_ISFLT(t) ((t) & 0x08)
/* $cX registers contain 4 bits: OCSZ (Z is bit 0) */
#define NV_CC_FL 0x0
#define NV_CC_LT 0x1
#define NV_CC_EQ 0x2
#define NV_CC_LE 0x3
#define NV_CC_GT 0x4
#define NV_CC_NE 0x5
#define NV_CC_GE 0x6
#define NV_CC_U 0x8
#define NV_CC_TR 0xf
#define NV_CC_O 0x10
#define NV_CC_C 0x11
#define NV_CC_A 0x12
#define NV_CC_S 0x13
#define NV_PC_MAX_INSTRUCTIONS 2048
#define NV_PC_MAX_VALUES (NV_PC_MAX_INSTRUCTIONS * 4)
#define NV_PC_MAX_BASIC_BLOCKS 1024
static INLINE boolean
nv_is_vector_op(uint opcode)
{
return (opcode >= NV_OP_TEX) && (opcode <= NV_OP_TXQ);
}
static INLINE uint
nv_type_order(ubyte type)
{
switch (type & 0xf) {
case NV_TYPE_U8:
case NV_TYPE_S8:
return 0;
case NV_TYPE_U16:
case NV_TYPE_S16:
return 1;
case NV_TYPE_U32:
case NV_TYPE_F32:
case NV_TYPE_S32:
case NV_TYPE_P32:
return 2;
case NV_TYPE_F64:
return 3;
}
assert(0);
return 0;
}
static INLINE uint
nv_type_sizeof(ubyte type)
{
if (type & 0xf0)
return (1 << nv_type_order(type)) * (type >> 4);
return 1 << nv_type_order(type);
}
static INLINE uint
nv_type_sizeof_base(ubyte type)
{
return 1 << nv_type_order(type);
}
struct nv_reg {
int id;
ubyte file;
ubyte type; /* type of generating instruction's result */
ubyte as_type; /* default type for new references to this value */
union {
float f32;
double f64;
int32_t s32;
uint32_t u32;
} imm;
};
struct nv_range {
struct nv_range *next;
int bgn;
int end;
};
struct nv_value {
struct nv_reg reg;
struct nv_instruction *insn;
struct nv_value *join;
int n;
struct nv_range *livei;
int refc;
struct nv_value *next;
struct nv_value *prev;
};
struct nv_ref {
struct nv_value *value;
ubyte mod;
ubyte typecast;
ubyte flags; /* not used yet */
};
#define NV_REF_FLAG_REGALLOC_PRIV (1 << 0)
struct nv_basic_block;
struct nv_instruction {
struct nv_instruction *next;
struct nv_instruction *prev;
uint opcode;
int serial;
struct nv_value *def[4];
struct nv_value *flags_def;
struct nv_ref *src[6];
struct nv_ref *flags_src;
struct nv_basic_block *bb;
struct nv_basic_block *target; /* target block of control flow insn */
ubyte cc;
unsigned set_cond : 4;
unsigned fixed : 1; /* don't optimize away */
unsigned is_terminator : 1;
unsigned is_join : 1;
unsigned is_long : 1; /* for emission */
/* */
unsigned saturate : 1;
unsigned centroid : 1;
unsigned flat : 1;
unsigned lanes : 4;
unsigned tex_live : 1;
/* */
ubyte tex_t; /* TIC binding */
ubyte tex_s; /* TSC binding */
unsigned tex_argc : 3;
unsigned tex_cube : 1;
unsigned tex_mask : 4;
/* */
ubyte quadop;
};
static INLINE int
nvi_vector_size(struct nv_instruction *nvi)
{
int i;
assert(nvi);
for (i = 0; i < 4 && nvi->def[i]; ++i);
return i;
}
#define CFG_EDGE_FORWARD 0
#define CFG_EDGE_BACK 1
#define CFG_EDGE_LOOP_ENTER 2
#define CFG_EDGE_LOOP_LEAVE 4
#define CFG_EDGE_FAKE 8
/* 'WALL' edge means where reachability check doesn't follow */
/* 'LOOP' edge means just having to do with loops */
#define IS_LOOP_EDGE(k) ((k) & 7)
#define IS_WALL_EDGE(k) ((k) & 9)
struct nv_basic_block {
struct nv_instruction *entry; /* first non-phi instruction */
struct nv_instruction *exit;
struct nv_instruction *phi; /* very first instruction */
int num_instructions;
struct nv_basic_block *out[2]; /* no indirect branches -> 2 */
struct nv_basic_block *in[8]; /* hope that suffices */
uint num_in;
ubyte out_kind[2];
ubyte in_kind[8];
int id;
int subroutine;
uint priv; /* reset to 0 after you're done */
uint pass_seq;
uint32_t bin_pos; /* position, size in emitted code */
uint32_t bin_size;
uint32_t live_set[NV_PC_MAX_VALUES / 32];
};
struct nv50_translation_info;
struct nv_pc {
struct nv_basic_block **root;
struct nv_basic_block *current_block;
struct nv_basic_block *parent_block;
int loop_nesting_bound;
uint pass_seq;
struct nv_value values[NV_PC_MAX_VALUES];
struct nv_instruction instructions[NV_PC_MAX_INSTRUCTIONS];
struct nv_ref **refs;
struct nv_basic_block *bb_list[NV_PC_MAX_BASIC_BLOCKS];
int num_values;
int num_instructions;
int num_refs;
int num_blocks;
int num_subroutines;
int max_reg[4];
uint32_t *immd_buf; /* populated on emit */
unsigned immd_count;
uint32_t *emit;
unsigned bin_size;
unsigned bin_pos;
void *fixups;
unsigned num_fixups;
/* optimization enables */
boolean opt_reload_elim;
};
void nvbb_insert_tail(struct nv_basic_block *, struct nv_instruction *);
void nvi_insert_after(struct nv_instruction *, struct nv_instruction *);
static INLINE struct nv_instruction *
nv_alloc_instruction(struct nv_pc *pc, uint opcode)
{
struct nv_instruction *insn;
insn = &pc->instructions[pc->num_instructions++];
assert(pc->num_instructions < NV_PC_MAX_INSTRUCTIONS);
insn->cc = NV_CC_TR;
insn->opcode = opcode;
return insn;
}
static INLINE struct nv_instruction *
new_instruction(struct nv_pc *pc, uint opcode)
{
struct nv_instruction *insn = nv_alloc_instruction(pc, opcode);
nvbb_insert_tail(pc->current_block, insn);
return insn;
}
static INLINE struct nv_instruction *
new_instruction_at(struct nv_pc *pc, struct nv_instruction *at, uint opcode)
{
struct nv_instruction *insn = nv_alloc_instruction(pc, opcode);
nvi_insert_after(at, insn);
return insn;
}
static INLINE struct nv_value *
new_value(struct nv_pc *pc, ubyte file, ubyte type)
{
struct nv_value *value = &pc->values[pc->num_values];
assert(pc->num_values < NV_PC_MAX_VALUES - 1);
value->n = pc->num_values++;
value->join = value;
value->reg.id = -1;
value->reg.file = file;
value->reg.type = value->reg.as_type = type;
return value;
}
static INLINE struct nv_value *
new_value_like(struct nv_pc *pc, struct nv_value *like)
{
struct nv_value *val = new_value(pc, like->reg.file, like->reg.type);
val->reg.as_type = like->reg.as_type;
return val;
}
static INLINE struct nv_ref *
new_ref(struct nv_pc *pc, struct nv_value *val)
{
int i;
struct nv_ref *ref;
if ((pc->num_refs % 64) == 0) {
const unsigned old_size = pc->num_refs * sizeof(struct nv_ref *);
const unsigned new_size = (pc->num_refs + 64) * sizeof(struct nv_ref *);
pc->refs = REALLOC(pc->refs, old_size, new_size);
ref = CALLOC(64, sizeof(struct nv_ref));
for (i = 0; i < 64; ++i)
pc->refs[pc->num_refs + i] = &ref[i];
}
ref = pc->refs[pc->num_refs++];
ref->value = val;
ref->typecast = val->reg.as_type;
++val->refc;
return ref;
}
static INLINE struct nv_basic_block *
new_basic_block(struct nv_pc *pc)
{
struct nv_basic_block *bb;
if (pc->num_blocks >= NV_PC_MAX_BASIC_BLOCKS)
return NULL;
bb = CALLOC_STRUCT(nv_basic_block);
bb->id = pc->num_blocks;
pc->bb_list[pc->num_blocks++] = bb;
return bb;
}
static INLINE void
nv_reference(struct nv_pc *pc, struct nv_ref **d, struct nv_value *s)
{
if (*d)
--(*d)->value->refc;
if (s) {
if (!*d)
*d = new_ref(pc, s);
else {
(*d)->value = s;
++(s->refc);
}
} else {
*d = NULL;
}
}
/* nv50_emit.c */
void nv50_emit_instruction(struct nv_pc *, struct nv_instruction *);
unsigned nv50_inst_min_size(struct nv_instruction *);
/* nv50_print.c */
const char *nv_opcode_name(uint opcode);
void nv_print_instruction(struct nv_instruction *);
/* nv50_pc.c */
void nv_print_function(struct nv_basic_block *root);
void nv_print_program(struct nv_pc *);
boolean nv_op_commutative(uint opcode);
int nv50_indirect_opnd(struct nv_instruction *);
boolean nv50_nvi_can_use_imm(struct nv_instruction *, int s);
boolean nv50_nvi_can_predicate(struct nv_instruction *);
boolean nv50_nvi_can_load(struct nv_instruction *, int s, struct nv_value *);
boolean nv50_op_can_write_flags(uint opcode);
ubyte nv50_supported_src_mods(uint opcode, int s);
int nv_nvi_refcount(struct nv_instruction *);
void nv_nvi_delete(struct nv_instruction *);
void nv_nvi_permute(struct nv_instruction *, struct nv_instruction *);
void nvbb_attach_block(struct nv_basic_block *parent,
struct nv_basic_block *, ubyte edge_kind);
boolean nvbb_dominated_by(struct nv_basic_block *, struct nv_basic_block *);
boolean nvbb_reachable_by(struct nv_basic_block *, struct nv_basic_block *,
struct nv_basic_block *);
struct nv_basic_block *nvbb_dom_frontier(struct nv_basic_block *);
int nvcg_replace_value(struct nv_pc *pc, struct nv_value *old_val,
struct nv_value *new_val);
struct nv_value *nvcg_find_immediate(struct nv_ref *);
struct nv_value *nvcg_find_constant(struct nv_ref *);
typedef void (*nv_pc_pass_func)(void *priv, struct nv_basic_block *b);
void nv_pc_pass_in_order(struct nv_basic_block *, nv_pc_pass_func, void *);
int nv_pc_exec_pass0(struct nv_pc *pc);
int nv_pc_exec_pass1(struct nv_pc *pc);
int nv_pc_exec_pass2(struct nv_pc *pc);
int nv50_tgsi_to_nc(struct nv_pc *, struct nv50_translation_info *);
#endif // NV50_COMPILER_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,321 +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_context.h"
#include "nv50_pc.h"
#define NVXX_DEBUG 0
#define PRINT(args...) debug_printf(args)
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif
static const char *norm = "\x1b[00m";
static const char *gree = "\x1b[32m";
static const char *blue = "\x1b[34m";
static const char *cyan = "\x1b[36m";
static const char *orng = "\x1b[33m";
static const char *mgta = "\x1b[35m";
static const char *nv_opcode_names[NV_OP_COUNT + 1] = {
"phi",
"extract",
"combine",
"lda",
"sta",
"mov",
"add",
"sub",
"neg",
"mul",
"mad",
"cvt",
"sat",
"not",
"and",
"or",
"xor",
"shl",
"shr",
"rcp",
"undef",
"rsqrt",
"lg2",
"sin",
"cos",
"ex2",
"presin",
"preex2",
"min",
"max",
"set",
"sad",
"kil",
"bra",
"call",
"ret",
"break",
"breakaddr",
"joinat",
"tex",
"texbias",
"texlod",
"texfetch",
"texsize",
"dfdx",
"dfdy",
"quadop",
"linterp",
"pinterp",
"abs",
"ceil",
"floor",
"trunc",
"nop",
"select",
"export",
"join",
"round",
"BAD_OP"
};
static const char *nv_cond_names[] =
{
"never", "lt" , "eq" , "le" , "gt" , "ne" , "ge" , "",
"never", "ltu", "equ", "leu", "gtu", "neu", "geu", "",
"o", "c", "a", "s"
};
static const char *nv_modifier_strings[] =
{
"",
"neg",
"abs",
"neg abs",
"not",
"not neg"
"not abs",
"not neg abs",
"sat",
"BAD_MOD"
};
const char *
nv_opcode_name(uint opcode)
{
return nv_opcode_names[MIN2(opcode, ARRAY_SIZE(nv_opcode_names) - 1)];
}
static INLINE const char *
nv_type_name(ubyte type)
{
switch (type) {
case NV_TYPE_U16: return "u16";
case NV_TYPE_S16: return "s16";
case NV_TYPE_F32: return "f32";
case NV_TYPE_U32: return "u32";
case NV_TYPE_S32: return "s32";
case NV_TYPE_P32: return "p32";
case NV_TYPE_F64: return "f64";
default:
return "BAD_TYPE";
}
}
static INLINE const char *
nv_cond_name(ubyte cc)
{
return nv_cond_names[MIN2(cc, 19)];
}
static INLINE const char *
nv_modifier_string(ubyte mod)
{
return nv_modifier_strings[MIN2(mod, 9)];
}
static INLINE int
nv_value_id(struct nv_value *value)
{
if (value->join->reg.id >= 0)
return value->join->reg.id;
return value->n;
}
static INLINE boolean
nv_value_allocated(struct nv_value *value)
{
return (value->reg.id >= 0) ? TRUE : FALSE;
}
static INLINE void
nv_print_address(const char c, int buf, struct nv_value *a, int offset)
{
const char ac = (a && nv_value_allocated(a)) ? '$' : '%';
if (buf >= 0)
PRINT(" %s%c%i[", cyan, c, buf);
else
PRINT(" %s%c[", cyan, c);
if (a)
PRINT("%s%ca%i%s+", mgta, ac, nv_value_id(a), cyan);
PRINT("%s0x%x%s]", orng, offset, cyan);
}
static INLINE void
nv_print_cond(struct nv_instruction *nvi)
{
char pfx = nv_value_allocated(nvi->flags_src->value->join) ? '$' : '%';
PRINT("%s%s %s%cc%i ",
gree, nv_cond_name(nvi->cc),
mgta, pfx, nv_value_id(nvi->flags_src->value));
}
static INLINE void
nv_print_value(struct nv_value *value, struct nv_value *ind, ubyte type)
{
char reg_pfx = '$';
if (type == NV_TYPE_ANY)
type = value->reg.type;
if (value->reg.file != NV_FILE_FLAGS)
PRINT(" %s%s", gree, nv_type_name(type));
if (!nv_value_allocated(value->join))
reg_pfx = '%';
switch (value->reg.file) {
case NV_FILE_GPR:
PRINT(" %s%cr%i", blue, reg_pfx, nv_value_id(value));
break;
case NV_FILE_OUT:
PRINT(" %s%co%i", mgta, reg_pfx, nv_value_id(value));
break;
case NV_FILE_ADDR:
PRINT(" %s%ca%i", mgta, reg_pfx, nv_value_id(value));
break;
case NV_FILE_FLAGS:
PRINT(" %s%cc%i", mgta, reg_pfx, nv_value_id(value));
break;
case NV_FILE_MEM_L:
nv_print_address('l', -1, ind, nv_value_id(value));
break;
case NV_FILE_MEM_S:
nv_print_address('s', -1, ind, 4 * nv_value_id(value));
break;
case NV_FILE_MEM_P:
nv_print_address('p', -1, ind, 4 * nv_value_id(value));
break;
case NV_FILE_MEM_V:
nv_print_address('v', -1, ind, 4 * nv_value_id(value));
break;
case NV_FILE_IMM:
switch (type) {
case NV_TYPE_U16:
case NV_TYPE_S16:
PRINT(" %s0x%04x", orng, value->reg.imm.u32);
break;
case NV_TYPE_F32:
PRINT(" %s%f", orng, value->reg.imm.f32);
break;
case NV_TYPE_F64:
PRINT(" %s%f", orng, value->reg.imm.f64);
break;
case NV_TYPE_U32:
case NV_TYPE_S32:
case NV_TYPE_P32:
PRINT(" %s0x%08x", orng, value->reg.imm.u32);
break;
}
break;
default:
if (value->reg.file >= NV_FILE_MEM_G(0) &&
value->reg.file <= NV_FILE_MEM_G(15))
nv_print_address('g', value->reg.file - NV_FILE_MEM_G(0), ind,
nv_value_id(value) * 4);
else
if (value->reg.file >= NV_FILE_MEM_C(0) &&
value->reg.file <= NV_FILE_MEM_C(15))
nv_print_address('c', value->reg.file - NV_FILE_MEM_C(0), ind,
nv_value_id(value) * 4);
else
NOUVEAU_ERR(" BAD_FILE[%i]", nv_value_id(value));
break;
}
}
static INLINE void
nv_print_ref(struct nv_ref *ref, struct nv_value *ind)
{
nv_print_value(ref->value, ind, ref->typecast);
}
void
nv_print_instruction(struct nv_instruction *i)
{
int j;
PRINT("%i: ", i->serial);
if (i->flags_src)
nv_print_cond(i);
PRINT("%s", gree);
if (i->opcode == NV_OP_SET)
PRINT("set %s", nv_cond_name(i->set_cond));
else
if (i->saturate)
PRINT("sat %s", nv_opcode_name(i->opcode));
else
PRINT("%s", nv_opcode_name(i->opcode));
if (i->flags_def)
nv_print_value(i->flags_def, NULL, NV_TYPE_ANY);
/* Only STORE & STA can write to MEM, and they do not def
* anything, so the address is thus part of the source.
*/
if (i->def[0])
nv_print_value(i->def[0], NULL, NV_TYPE_ANY);
else
if (i->target)
PRINT(" %s(BB:%i)", orng, i->target->id);
else
PRINT(" #");
for (j = 0; j < 4; ++j) {
if (!i->src[j])
continue;
if (i->src[j]->mod)
PRINT(" %s%s", gree, nv_modifier_string(i->src[j]->mod));
nv_print_ref(i->src[j],
(j == nv50_indirect_opnd(i)) ?
i->src[4]->value : NULL);
}
PRINT(" %s%c\n", norm, i->is_long ? 'l' : 's');
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff