nv50: import new compiler

This commit is contained in:
Christoph Bumiller
2010-07-23 21:21:25 +02:00
parent c65f4fd5ae
commit 633f5ac612
15 changed files with 6472 additions and 4674 deletions
+9 -2
View File
@@ -10,7 +10,6 @@ C_SOURCES = \
nv50_draw.c \
nv50_miptree.c \
nv50_query.c \
nv50_program.c \
nv50_resource.c \
nv50_screen.c \
nv50_state.c \
@@ -19,6 +18,14 @@ C_SOURCES = \
nv50_tex.c \
nv50_transfer.c \
nv50_vbo.c \
nv50_push.c
nv50_push.c \
nv50_program.c \
nv50_shader_state.c \
nv50_pc.c \
nv50_pc_print.c \
nv50_pc_emit.c \
nv50_tgsi_to_nc.c \
nv50_pc_optimize.c \
nv50_pc_regalloc.c
include ../../Makefile.template
+433
View File
@@ -0,0 +1,433 @@
#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:
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->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)
{
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_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 (s == 1 &&
value->reg.file >= NV_FILE_MEM_C(0) &&
value->reg.file <= NV_FILE_MEM_C(15))
return TRUE;
if (s == 2 && nvi->src[1]->value->reg.file == NV_FILE_GPR)
return TRUE;
return FALSE;
case NV_OP_MOV:
assert(s == 0);
return TRUE;
default:
return FALSE;
}
}
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;
}
}
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;
}
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]);
}
void
nv_print_program(struct nv_basic_block *b)
{
struct nv_instruction *i = b->phi;
b->priv = 0;
debug_printf("=== BB %i ", b->id);
if (b->out[0])
debug_printf("(--0> %i) ", b->out[0]->id);
if (b->out[1])
debug_printf("(--1> %i) ", b->out[1]->id);
debug_printf("===\n");
if (!i)
i = b->entry;
for (; i; i = i->next)
nv_print_instruction(i);
if (!b->out[0]) {
debug_printf("END\n\n");
return;
}
if (!b->out[1] && ++(b->out[0]->priv) != b->out[0]->num_in)
return;
if (b->out[0] != b)
nv_print_program(b->out[0]);
if (b->out[1] && b->out[1] != b)
nv_print_program(b->out[1]);
}
static INLINE void
nvcg_show_bincode(struct nv_pc *pc)
{
int i;
for (i = 0; i < pc->bin_size / 4; ++i)
debug_printf("0x%08x ", pc->emit[i]);
debug_printf("\n");
}
static int
nv50_emit_program(struct nv_pc *pc)
{
uint32_t *code = pc->emit;
int n;
debug_printf("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->emit[-1] & 3) == 3) {
pc->emit[0] = 0xf0000001;
pc->emit[1] = 0xe0000000;
pc->bin_size += 8;
}
pc->emit = code;
code[pc->bin_size / 4 - 1] |= 1;
nvcg_show_bincode(pc);
return 0;
}
int
nv50_generate_code(struct nv50_translation_info *ti)
{
struct nv_pc *pc;
int ret;
pc = CALLOC_STRUCT(nv_pc);
if (!pc)
return 1;
ret = nv50_tgsi_to_nc(pc, ti);
if (ret)
goto out;
/* optimization */
ret = nv_pc_exec_pass0(pc);
if (ret)
goto out;
/* register allocation */
ret = nv_pc_exec_pass1(pc);
if (ret)
goto out;
/* prepare for emission */
ret = nv_pc_exec_pass2(pc);
if (ret)
goto out;
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;
ti->p->max_gpr = (pc->max_reg[NV_FILE_GPR] + 1) >> 1;
ti->p->max_gpr++;
ti->p->fixups = pc->fixups;
ti->p->num_fixups = pc->num_fixups;
debug_printf("SHADER TRANSLATION - %s\n", ret ? "failure" : "success");
out:
nv_pc_free_refs(pc);
if (ret) {
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++;
}
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 < 4; ++j) {
if (!nvi->src[j])
break;
--(nvi->src[j]->value->refc);
nvi->src[j] = 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) {
assert(nvi->opcode != NV_OP_PHI || !nvi->next);
if (!nvi->next || (nvi->opcode == NV_OP_PHI))
b->entry = nvi->prev;
else
b->entry = nvi->next;
}
if (nvi == b->phi) {
assert(!nvi->prev);
if (nvi->opcode != NV_OP_PHI)
debug_printf("WARN: b->phi points to non-PHI instruction\n");
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)
{
if (parent->out[0]) {
assert(!parent->out[1]);
parent->out[1] = b;
} else
parent->out[0] = b;
b->in[b->num_in++] = parent;
}
+431
View File
@@ -0,0 +1,431 @@
/*************************************************************************/
/* Copyright (C) 2010 I */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/*************************************************************************/
#ifndef __NV50_COMPILER_H__
#define __NV50_COMPILER_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
/* gap */
#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_COUNT 56
#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)
#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_PC_MAX_INSTRUCTIONS 2048
#define NV_PC_MAX_VALUES (NV_PC_MAX_INSTRUCTIONS * 4)
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);
}
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 */
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;
struct nv_instruction *insn;
ubyte mod;
ubyte typecast;
ubyte flags; /* not used yet */
};
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[5];
struct nv_ref *flags_src;
struct nv_basic_block *bb;
struct nv_basic_block *target; /* target block of control flow insn */
ubyte cc;
ubyte set_cond : 4;
ubyte fixed : 1; /* don't optimize away */
ubyte is_terminator : 1;
ubyte is_join : 1;
ubyte is_long : 1; /* for emission */
/* */
ubyte saturate : 1;
ubyte centroid : 1;
ubyte flat : 1;
ubyte padding : 4;
ubyte tex_live : 1;
/* */
ubyte tex_t; /* TIC binding */
ubyte tex_s; /* TSC binding */
ubyte tex_argc : 3;
ubyte tex_cube : 1;
ubyte tex_mask : 4;
/* */
ubyte quadop;
};
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;
uint num_in;
int id;
struct nv_basic_block *last_visitor;
uint priv;
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];
};
#define NV_FIXUP_CFLOW_RELOC 0
#define NV_FIXUP_PARAM_RELOC 1
struct nv_fixup {
ubyte type;
ubyte shift;
uint32_t mask;
uint32_t data;
uint32_t offset;
};
static INLINE void
nv_fixup_apply(uint32_t *bin, struct nv_fixup *fixup, uint32_t data)
{
uint32_t val;
val = bin[fixup->offset / 4] & ~fixup->mask;
data = (fixup->shift < 0) ? (data >> fixup->shift) : (data << fixup->shift);
val |= (fixup->data + data) & fixup->mask;
bin[fixup->offset / 4] = val;
}
struct nv_pc {
struct nv50_translation_info *ti;
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;
int num_values;
int num_instructions;
int num_refs;
int num_blocks;
int max_reg[4];
uint32_t *immd_buf; /* populated on emit */
unsigned immd_count;
uint32_t *emit;
unsigned bin_size;
unsigned bin_pos;
struct nv_fixup *fixups;
int num_fixups;
};
void nvbb_insert_tail(struct nv_basic_block *, struct nv_instruction *);
static INLINE struct nv_instruction *
new_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;
nvbb_insert_tail(pc->current_block, 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 = type;
return value;
}
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.type;
++val->refc;
return ref;
}
static INLINE struct nv_basic_block *
new_basic_block(struct nv_pc *pc)
{
struct nv_basic_block *bb = CALLOC_STRUCT(nv_basic_block);
bb->in = CALLOC(sizeof(struct nv_basic_block *), 4);
bb->id = pc->num_blocks++;
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 {
assert(*d);
*d = NULL;
}
}
/* nv50_emit.c */
void nv50_emit_instruction(struct nv_pc *, 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_program(struct nv_basic_block *b);
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_load(struct nv_instruction *, int s, struct nv_value *);
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 *);
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
+717
View File
@@ -0,0 +1,717 @@
#include "nv50_pc.h"
#define DESCEND_ARBITRARY(j, f) \
do { \
b->pass_seq = ctx->pc->pass_seq; \
\
for (j = 0; j < 2; ++j) \
if (b->out[j] && b->out[j]->pass_seq < ctx->pc->pass_seq) \
f(ctx, b->out[j]); \
} while (0)
extern unsigned nv50_inst_min_size(struct nv_instruction *);
struct nv_pc_pass {
struct nv_pc *pc;
};
static INLINE boolean
values_equal(struct nv_value *a, struct nv_value *b)
{
/* XXX: sizes */
return (a->reg.file == b->reg.file && a->join->reg.id == b->join->reg.id);
}
static INLINE boolean
inst_commutation_check(struct nv_instruction *a,
struct nv_instruction *b)
{
int si, di;
for (di = 0; di < 4; ++di) {
if (!a->def[di])
break;
for (si = 0; si < 5; ++si) {
if (!b->src[si])
continue;
if (values_equal(a->def[di], b->src[si]->value))
return FALSE;
}
}
if (b->flags_src && b->flags_src->value == a->flags_def)
return FALSE;
return TRUE;
}
/* Check whether we can swap the order of the instructions,
* where a & b may be either the earlier or the later one.
*/
static boolean
inst_commutation_legal(struct nv_instruction *a,
struct nv_instruction *b)
{
return inst_commutation_check(a, b) && inst_commutation_check(b, a);
}
static INLINE boolean
inst_cullable(struct nv_instruction *nvi)
{
return (!(nvi->is_terminator ||
nvi->target ||
nvi->fixed ||
nv_nvi_refcount(nvi)));
}
static INLINE boolean
nvi_isnop(struct nv_instruction *nvi)
{
if (nvi->opcode == NV_OP_EXPORT)
return TRUE;
if (nvi->fixed ||
nvi->is_terminator ||
nvi->flags_src ||
nvi->flags_def)
return FALSE;
if (nvi->def[0]->join->reg.id < 0)
return TRUE;
if (nvi->opcode != NV_OP_MOV && nvi->opcode != NV_OP_SELECT)
return FALSE;
if (nvi->def[0]->reg.file != nvi->src[0]->value->reg.file)
return FALSE;
if (nvi->src[0]->value->join->reg.id < 0) {
debug_printf("nvi_isnop: orphaned value detected\n");
return TRUE;
}
if (nvi->opcode == NV_OP_SELECT)
if (!values_equal(nvi->def[0], nvi->src[1]->value))
return FALSE;
return values_equal(nvi->def[0], nvi->src[0]->value);
}
static void
nv_pc_pass_pre_emission(struct nv_pc *pc, struct nv_basic_block *b)
{
struct nv_instruction *nvi, *next;
int j;
uint size, n32 = 0;
b->priv = 0;
if (pc->num_blocks)
b->bin_pos = pc->bb_list[pc->num_blocks - 1]->bin_pos +
pc->bb_list[pc->num_blocks - 1]->bin_size;
pc->bb_list[pc->num_blocks++] = b;
/* visit node */
for (nvi = b->entry; nvi; nvi = next) {
next = nvi->next;
if (nvi_isnop(nvi))
nv_nvi_delete(nvi);
}
for (nvi = b->entry; nvi; nvi = next) {
next = nvi->next;
size = nv50_inst_min_size(nvi);
if (nvi->next && size < 8)
++n32;
else
if ((n32 & 1) && nvi->next &&
nv50_inst_min_size(nvi->next) == 4 &&
inst_commutation_legal(nvi, nvi->next)) {
++n32;
debug_printf("permuting: ");
nv_print_instruction(nvi);
nv_print_instruction(nvi->next);
nv_nvi_permute(nvi, nvi->next);
next = nvi;
} else {
nvi->is_long = 1;
b->bin_size += n32 & 1;
if (n32 & 1)
nvi->prev->is_long = 1;
n32 = 0;
}
b->bin_size += 1 + nvi->is_long;
}
if (!b->entry) {
debug_printf("block %p is now empty\n", b);
} else
if (!b->exit->is_long) {
assert(n32);
b->exit->is_long = 1;
b->bin_size += 1;
/* might have del'd a hole tail of instructions */
if (!b->exit->prev->is_long && !(n32 & 1)) {
b->bin_size += 1;
b->exit->prev->is_long = 1;
}
}
assert(!b->exit || b->exit->is_long);
pc->bin_size += b->bin_size *= 4;
/* descend CFG */
if (!b->out[0])
return;
if (!b->out[1] && ++(b->out[0]->priv) != b->out[0]->num_in)
return;
#if 0
/* delete ELSE branch */
if (b->entry &&
b->entry->opcode == NV_OP_BRA && b->entry->target == b->out[0]) {
nv_nvi_delete(b->entry);
b->bin_size -= 2;
pc->bin_size -= 8;
}
#endif
for (j = 0; j < 2; ++j)
if (b->out[j] && b->out[j] != b)
nv_pc_pass_pre_emission(pc, b->out[j]);
}
int
nv_pc_exec_pass2(struct nv_pc *pc)
{
debug_printf("preparing %u blocks for emission\n", pc->num_blocks);
pc->bb_list = CALLOC(pc->num_blocks, sizeof(struct nv_basic_block *));
pc->num_blocks = 0;
nv_pc_pass_pre_emission(pc, pc->root);
return 0;
}
static INLINE boolean
is_cmem_load(struct nv_instruction *nvi)
{
return (nvi->opcode == NV_OP_LDA &&
nvi->src[0]->value->reg.file >= NV_FILE_MEM_C(0) &&
nvi->src[0]->value->reg.file <= NV_FILE_MEM_C(15));
}
static INLINE boolean
is_smem_load(struct nv_instruction *nvi)
{
return (nvi->opcode == NV_OP_LDA &&
(nvi->src[0]->value->reg.file == NV_FILE_MEM_S ||
nvi->src[0]->value->reg.file <= NV_FILE_MEM_P));
}
static INLINE boolean
is_immd_move(struct nv_instruction *nvi)
{
return (nvi->opcode == NV_OP_MOV &&
nvi->src[0]->value->reg.file == NV_FILE_IMM);
}
static INLINE void
check_swap_src_0_1(struct nv_instruction *nvi)
{
static const ubyte cc_swapped[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
struct nv_ref *src0 = nvi->src[0], *src1 = nvi->src[1];
if (!nv_op_commutative(nvi->opcode))
return;
assert(src0 && src1);
if (is_cmem_load(src0->value->insn)) {
if (!is_cmem_load(src1->value->insn)) {
nvi->src[0] = src1;
nvi->src[1] = src0;
/* debug_printf("swapping cmem load to 1\n"); */
}
} else
if (is_smem_load(src1->value->insn)) {
if (!is_smem_load(src0->value->insn)) {
nvi->src[0] = src1;
nvi->src[1] = src0;
/* debug_printf("swapping smem load to 0\n"); */
}
}
if (nvi->opcode == NV_OP_SET && nvi->src[0] != src0)
nvi->set_cond = cc_swapped[nvi->set_cond];
}
struct nv_pass {
struct nv_pc *pc;
int n;
void *priv;
};
static int
nv_pass_fold_stores(struct nv_pass *ctx, struct nv_basic_block *b)
{
struct nv_instruction *nvi, *sti;
int j;
for (sti = b->entry; sti; sti = sti->next) {
if (!sti->def[0])
continue;
if (sti->def[0]->reg.file != NV_FILE_OUT)
continue;
if (sti->opcode != NV_OP_MOV && sti->opcode != NV_OP_STA)
continue;
nvi = sti->src[0]->value->insn;
if (!nvi || nvi->opcode == NV_OP_PHI)
continue;
assert(nvi->def[0] == sti->src[0]->value);
if (nvi->def[0]->refc > 1)
continue;
nvi->def[0] = sti->def[0];
nvi->fixed = 1;
sti->fixed = 0;
}
DESCEND_ARBITRARY(j, nv_pass_fold_stores);
return 0;
}
static int
nv_pass_fold_loads(struct nv_pass *ctx, struct nv_basic_block *b)
{
struct nv_instruction *nvi, *ld;
int j;
for (nvi = b->entry; nvi; nvi = nvi->next) {
check_swap_src_0_1(nvi);
for (j = 0; j < 3; ++j) {
if (!nvi->src[j])
break;
ld = nvi->src[j]->value->insn;
if (!ld)
continue;
if (is_immd_move(ld) && nv50_nvi_can_use_imm(nvi, j)) {
nv_reference(ctx->pc, &nvi->src[j], ld->src[0]->value);
debug_printf("folded immediate %i\n", ld->def[0]->n);
continue;
}
if (ld->opcode != NV_OP_LDA)
continue;
if (!nv50_nvi_can_load(nvi, j, ld->src[0]->value))
continue;
if (j == 0 && ld->src[4]) /* can't load shared mem */
continue;
/* fold it ! */ /* XXX: ref->insn */
nv_reference(ctx->pc, &nvi->src[j], ld->src[0]->value);
if (ld->src[4])
nv_reference(ctx->pc, &nvi->src[4], ld->src[4]->value);
}
}
DESCEND_ARBITRARY(j, nv_pass_fold_loads);
return 0;
}
static int
nv_pass_lower_mods(struct nv_pass *ctx, struct nv_basic_block *b)
{
int j;
struct nv_instruction *nvi, *mi, *next;
ubyte mod;
for (nvi = b->entry; nvi; nvi = next) {
next = nvi->next;
if (nvi->opcode == NV_OP_SUB) {
nvi->opcode = NV_OP_ADD;
nvi->src[1]->mod ^= NV_MOD_NEG;
}
/* should not put any modifiers on NEG and ABS */
assert(nvi->opcode != NV_MOD_NEG || !nvi->src[0]->mod);
assert(nvi->opcode != NV_MOD_ABS || !nvi->src[0]->mod);
for (j = 0; j < 4; ++j) {
if (!nvi->src[j])
break;
mi = nvi->src[j]->value->insn;
if (!mi)
continue;
if (mi->def[0]->refc > 1)
continue;
if (mi->opcode == NV_OP_NEG) mod = NV_MOD_NEG;
else
if (mi->opcode == NV_OP_ABS) mod = NV_MOD_ABS;
else
continue;
if (nvi->opcode == NV_OP_ABS)
mod &= ~(NV_MOD_NEG | NV_MOD_ABS);
else
if (nvi->opcode == NV_OP_NEG && mod == NV_MOD_NEG) {
nvi->opcode = NV_OP_MOV;
mod = 0;
}
if (!(nv50_supported_src_mods(nvi->opcode, j) & mod))
continue;
nv_reference(ctx->pc, &nvi->src[j], mi->src[0]->value);
nvi->src[j]->mod ^= mod;
}
if (nvi->opcode == NV_OP_SAT) {
mi = nvi->src[0]->value->insn;
if ((mi->opcode == NV_OP_MAD) && !mi->flags_def) {
mi->saturate = 1;
mi->def[0] = nvi->def[0];
nv_nvi_delete(nvi);
}
}
}
DESCEND_ARBITRARY(j, nv_pass_lower_mods);
return 0;
}
#define SRC_IS_MUL(s) ((s)->insn && (s)->insn->opcode == NV_OP_MUL)
static struct nv_value *
find_immediate(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;
}
return (src->reg.file == NV_FILE_IMM) ? src : NULL;
}
static void
constant_operand(struct nv_pc *pc,
struct nv_instruction *nvi, struct nv_value *val, int s)
{
int t = s ? 0 : 1;
ubyte type;
if (!nvi->def[0])
return;
type = nvi->def[0]->reg.type;
switch (nvi->opcode) {
case NV_OP_MUL:
if ((type == NV_TYPE_F32 && val->reg.imm.f32 == 1.0f) ||
(NV_TYPE_ISINT(type) && val->reg.imm.u32 == 1)) {
nvi->opcode = NV_OP_MOV;
nv_reference(pc, &nvi->src[s], NULL);
if (!s) {
nvi->src[0] = nvi->src[1];
nvi->src[1] = NULL;
}
} else
if ((type == NV_TYPE_F32 && val->reg.imm.f32 == 2.0f) ||
(NV_TYPE_ISINT(type) && val->reg.imm.u32 == 2)) {
nvi->opcode = NV_OP_ADD;
nv_reference(pc, &nvi->src[s], NULL);
if (!s) {
nvi->src[0] = nvi->src[1];
nvi->src[1] = NULL;
}
} else
if (type == NV_TYPE_F32 && val->reg.imm.f32 == -1.0f) {
nvi->opcode = NV_OP_NEG;
nv_reference(pc, &nvi->src[s], NULL);
nvi->src[0] = nvi->src[t];
nvi->src[1] = NULL;
} else
if (type == NV_TYPE_F32 && val->reg.imm.f32 == -2.0f) {
nvi->opcode = NV_OP_ADD;
assert(!nvi->src[s]->mod);
nv_reference(pc, &nvi->src[s], nvi->src[t]->value);
nvi->src[t]->mod ^= NV_MOD_NEG;
nvi->src[s]->mod |= NV_MOD_NEG;
} else
if (val->reg.imm.u32 == 0) {
nvi->opcode = NV_OP_MOV;
nv_reference(pc, &nvi->src[t], NULL);
if (s) {
nvi->src[0] = nvi->src[1];
nvi->src[1] = NULL;
}
}
break;
case NV_OP_ADD:
if (val->reg.imm.u32 == 0) {
nvi->opcode = NV_OP_MOV;
nv_reference(pc, &nvi->src[s], NULL);
nvi->src[0] = nvi->src[t];
nvi->src[1] = NULL;
}
break;
default:
break;
}
}
static int
nv_pass_lower_arith(struct nv_pass *ctx, struct nv_basic_block *b)
{
struct nv_instruction *nvi, *next;
int j;
for (nvi = b->entry; nvi; nvi = next) {
struct nv_value *src0, *src1, *src;
int mod;
next = nvi->next;
if ((src = find_immediate(nvi->src[0])) != NULL)
constant_operand(ctx->pc, nvi, src, 0);
else
if ((src = find_immediate(nvi->src[1])) != NULL)
constant_operand(ctx->pc, nvi, src, 1);
/* try to combine MUL, ADD into MAD */
if (nvi->opcode != NV_OP_ADD)
continue;
src0 = nvi->src[0]->value;
src1 = nvi->src[1]->value;
if (SRC_IS_MUL(src0) && src0->refc == 1)
src = src0;
else
if (SRC_IS_MUL(src1) && src1->refc == 1)
src = src1;
else
continue;
nvi->opcode = NV_OP_MAD;
mod = nvi->src[(src == src0) ? 0 : 1]->mod;
nv_reference(ctx->pc, &nvi->src[(src == src0) ? 0 : 1], NULL);
nvi->src[2] = nvi->src[(src == src0) ? 1 : 0];
assert(!(mod & ~NV_MOD_NEG));
nvi->src[0] = new_ref(ctx->pc, src->insn->src[0]->value);
nvi->src[1] = new_ref(ctx->pc, src->insn->src[1]->value);
nvi->src[0]->mod = src->insn->src[0]->mod ^ mod;
nvi->src[1]->mod = src->insn->src[1]->mod;
}
DESCEND_ARBITRARY(j, nv_pass_lower_arith);
return 0;
}
/*
set $r2 g f32 $r2 $r3
cvt abs rn f32 $r2 s32 $r2
cvt f32 $c0 # f32 $r2
e $c0 bra 0x80
*/
#if 0
static int
nv_pass_lower_cond(struct nv_pass *ctx, struct nv_basic_block *b)
{
/* XXX: easier in IR builder for now */
return 0;
}
#endif
/* TODO: reload elimination, redundant store elimination */
struct nv_pass_reldelim {
struct nv_pc *pc;
};
static int
nv_pass_reload_elim(struct nv_pass_reldelim *ctx, struct nv_basic_block *b)
{
int j;
struct nv_instruction *ld, *next;
for (ld = b->entry; ld; ld = next) {
next = ld->next;
if (ld->opcode == NV_OP_LINTERP || ld->opcode == NV_OP_PINTERP) {
} else
if (ld->opcode == NV_OP_LDA) {
} else
if (ld->opcode == NV_OP_MOV) {
}
}
DESCEND_ARBITRARY(j, nv_pass_reload_elim);
return 0;
}
static int
nv_pass_tex_mask(struct nv_pass *ctx, struct nv_basic_block *b)
{
int i, c, j;
for (i = 0; i < ctx->pc->num_instructions; ++i) {
struct nv_instruction *nvi = &ctx->pc->instructions[i];
struct nv_value *def[4];
if (!nv_is_vector_op(nvi->opcode))
continue;
nvi->tex_mask = 0;
for (c = 0; c < 4; ++c) {
if (nvi->def[c]->refc)
nvi->tex_mask |= 1 << c;
def[c] = nvi->def[c];
}
j = 0;
for (c = 0; c < 4; ++c)
if (nvi->tex_mask & (1 << c))
nvi->def[j++] = def[c];
for (c = 0; c < 4; ++c)
if (!(nvi->tex_mask & (1 << c)))
nvi->def[j++] = def[c];
assert(j == 4);
}
return 0;
}
struct nv_pass_dce {
struct nv_pc *pc;
uint removed;
};
static int
nv_pass_dce(struct nv_pass_dce *ctx, struct nv_basic_block *b)
{
int j;
struct nv_instruction *nvi, *next;
for (nvi = b->entry; nvi; nvi = next) {
next = nvi->next;
if (inst_cullable(nvi)) {
nv_nvi_delete(nvi);
++ctx->removed;
}
}
DESCEND_ARBITRARY(j, nv_pass_dce);
return 0;
}
static INLINE boolean
bb_simple_if_endif(struct nv_basic_block *bb)
{
return (bb->out[0] && bb->out[1] &&
bb->out[0]->out[0] == bb->out[1] &&
!bb->out[0]->out[1]);
}
static int
nv_pass_flatten(struct nv_pass *ctx, struct nv_basic_block *b)
{
int j;
if (bb_simple_if_endif(b)) {
++ctx->n;
debug_printf("nv_pass_flatten: total IF/ENDIF constructs: %i\n", ctx->n);
}
DESCEND_ARBITRARY(j, nv_pass_flatten);
return 0;
}
int
nv_pc_exec_pass0(struct nv_pc *pc)
{
struct nv_pass_reldelim *reldelim;
struct nv_pass pass;
struct nv_pass_dce dce;
int ret;
reldelim = CALLOC_STRUCT(nv_pass_reldelim);
reldelim->pc = pc;
ret = nv_pass_reload_elim(reldelim, pc->root);
FREE(reldelim);
if (ret)
return ret;
pass.pc = pc;
pc->pass_seq++;
ret = nv_pass_flatten(&pass, pc->root);
if (ret)
return ret;
/* Do this first, so we don't have to pay attention
* to whether sources are supported memory loads.
*/
pc->pass_seq++;
ret = nv_pass_lower_arith(&pass, pc->root);
if (ret)
return ret;
pc->pass_seq++;
ret = nv_pass_fold_loads(&pass, pc->root);
if (ret)
return ret;
pc->pass_seq++;
ret = nv_pass_fold_stores(&pass, pc->root);
if (ret)
return ret;
pc->pass_seq++;
ret = nv_pass_lower_mods(&pass, pc->root);
if (ret)
return ret;
dce.pc = pc;
do {
dce.removed = 0;
pc->pass_seq++;
ret = nv_pass_dce(&dce, pc->root);
if (ret)
return ret;
} while (dce.removed);
ret = nv_pass_tex_mask(&pass, pc->root);
if (ret)
return ret;
return ret;
}
+287
View File
@@ -0,0 +1,287 @@
#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",
"(undefined)",
"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",
"BAD_OP"
};
static const char *nv_cond_names[] =
{
"never", "lt" , "eq" , "le" , "gt" , "ne" , "ge" , "",
"never", "ltu", "equ", "leu", "gtu", "neu", "geu", ""
};
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, 15)];
}
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)
{
if (buf >= 0)
PRINT(" %s%c%i[", cyan, c, buf);
else
PRINT(" %s%c[", cyan, c);
if (a)
PRINT("%s$a%i%s+", mgta, nv_value_id(a), cyan);
PRINT("%s0x%x%s]", orng, offset, cyan);
}
static INLINE void
nv_print_cond(struct nv_instruction *nvi)
{
PRINT("%s%s%s$c%i ",
gree, nv_cond_name(nvi->cc),
mgta, 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))
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_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;
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
PRINT(" #");
for (j = 0; j < 4; ++j) {
if (!i->src[j])
continue;
if (i->src[j]->mod)
PRINT(" %s", nv_modifier_string(i->src[j]->mod));
nv_print_ref(i->src[j],
(j == nv50_indirect_opnd(i)) ?
i->src[4]->value : NULL);
}
if (!i->is_long)
PRINT(" %ss", norm);
PRINT("\n");
}
+973
View File
@@ -0,0 +1,973 @@
/*
* XXX: phi function live intervals start at first ordinary instruction,
* add_range should be taking care of that already ...
*
* XXX: TEX must choose TEX's def as representative
*
* XXX: Aieee! Must materialize MOVs if source is in other basic block!
* -- absolutely, or we cannot execute the MOV conditionally at all
* XXX: Aieee! Must include PHIs in LVA so we pull through liveness if
* PHI source is e.g. in dominator block.
* -- seems we lose liveness somehow, track that
*/
#include "nv50_context.h"
#include "nv50_pc.h"
#include "util/u_simple_list.h"
#define NUM_REGISTER_FILES 4
struct register_set {
struct nv_pc *pc;
uint32_t last[NUM_REGISTER_FILES];
uint32_t bits[NUM_REGISTER_FILES][8];
};
struct nv_pc_pass {
struct nv_pc *pc;
struct nv_instruction **insns;
int num_insns;
uint pass_seq;
};
static void
ranges_coalesce(struct nv_range *range)
{
while (range->next && range->end >= range->next->bgn) {
struct nv_range *rnn = range->next->next;
assert(range->bgn <= range->next->bgn);
range->end = MAX2(range->end, range->next->end);
FREE(range->next);
range->next = rnn;
}
}
static boolean
add_range_ex(struct nv_value *val, int bgn, int end, struct nv_range *new_range)
{
struct nv_range *range, **nextp = &val->livei;
for (range = val->livei; range; range = range->next) {
if (end < range->bgn)
break; /* insert before */
if (bgn > range->end) {
nextp = &range->next;
continue; /* insert after */
}
/* overlap */
if (bgn < range->bgn) {
range->bgn = bgn;
if (end > range->end)
range->end = end;
ranges_coalesce(range);
return TRUE;
}
if (end > range->end) {
range->end = end;
ranges_coalesce(range);
return TRUE;
}
assert(bgn >= range->bgn);
assert(end <= range->end);
return TRUE;
}
if (!new_range)
new_range = CALLOC_STRUCT(nv_range);
new_range->bgn = bgn;
new_range->end = end;
new_range->next = range;
*(nextp) = new_range;
return FALSE;
}
static void
add_range(struct nv_value *val, struct nv_basic_block *b, int end)
{
int bgn;
if (!val->insn) /* ignore non-def values */
return;
assert(b->entry->serial <= b->exit->serial);
assert(b->phi->serial <= end);
assert(b->exit->serial + 1 >= end);
bgn = val->insn->serial;
if (bgn < b->entry->serial || bgn > b->exit->serial)
bgn = b->entry->serial;
// debug_printf("add_range(value %i): [%i, %i)\n", val->n, bgn, end);
if (bgn > end) {
debug_printf("Aieee! BLOCK [%i, %i], RANGE [%i, %i)\n",
b->entry->serial, b->exit->serial, bgn, end);
}
assert(bgn <= end);
if (bgn < val->insn->serial)
debug_printf("WARNING: leaking value %i ?\n", val->n);
add_range_ex(val, bgn, end, NULL);
}
#ifdef NV50_RA_DEBUG_JOIN
static void
livei_print(struct nv_value *a)
{
struct nv_range *r = a->livei;
debug_printf("livei %i: ", a->n);
while (r) {
debug_printf("[%i, %i) ", r->bgn, r->end);
r = r->next;
}
debug_printf("\n");
}
#endif
static void
livei_unify(struct nv_value *dst, struct nv_value *src)
{
struct nv_range *range, *next;
for (range = src->livei; range; range = next) {
next = range->next;
if (add_range_ex(dst, range->bgn, range->end, range))
FREE(range);
}
src->livei = NULL;
}
static void
livei_release(struct nv_value *val)
{
struct nv_range *range, *next;
for (range = val->livei; range; range = next) {
next = range->next;
FREE(range);
}
}
static boolean
livei_have_overlap(struct nv_value *a, struct nv_value *b)
{
struct nv_range *r_a, *r_b;
for (r_a = a->livei; r_a; r_a = r_a->next) {
for (r_b = b->livei; r_b; r_b = r_b->next) {
if (r_b->bgn < r_a->end &&
r_b->end > r_a->bgn)
return TRUE;
}
}
return FALSE;
}
static int
livei_end(struct nv_value *a)
{
struct nv_range *r = a->livei;
assert(r);
while (r->next)
r = r->next;
return r->end;
}
static boolean
livei_contains(struct nv_value *a, int pos)
{
struct nv_range *r;
for (r = a->livei; r && r->bgn <= pos; r = r->next)
if (r->end > pos)
return TRUE;
return FALSE;
}
static boolean
reg_assign(struct register_set *set, struct nv_value **def, int n)
{
int i, id, s;
uint m;
int f = def[0]->reg.file;
s = n << (nv_type_order(def[0]->reg.type) - 1);
m = (1 << s) - 1;
id = set->last[f];
for (i = 0; i * 32 < set->last[f]; ++i) {
if (set->bits[f][i] == 0xffffffff)
continue;
for (id = 0; id < 32; id += s)
if (!(set->bits[f][i] & (m << id)))
break;
if (id < 32)
break;
}
if (i * 32 + id > set->last[f])
return FALSE;
set->bits[f][i] |= m << id;
id += i * 32;
set->pc->max_reg[f] = MAX2(set->pc->max_reg[f], id + s - 1);
id >>= nv_type_order(def[0]->reg.type) - 1;
for (i = 0; i < n; ++i)
if (def[i]->livei)
def[i]->reg.id = id++;
return TRUE;
}
static INLINE void
reg_occupy(struct register_set *set, struct nv_value *val)
{
int s, id = val->reg.id, f = val->reg.file;
uint m;
if (id < 0)
return;
s = nv_type_order(val->reg.type) - 1;
id <<= s;
m = (1 << (1 << s)) - 1;
set->bits[f][id / 32] |= m << (id % 32);
if (set->pc->max_reg[f] < id)
set->pc->max_reg[f] = id;
}
static INLINE void
reg_release(struct register_set *set, struct nv_value *val)
{
int s, id = val->reg.id, f = val->reg.file;
uint m;
if (id < 0)
return;
s = nv_type_order(val->reg.type) - 1;
id <<= s;
m = (1 << (1 << s)) - 1;
set->bits[f][id / 32] &= ~(m << (id % 32));
}
static INLINE boolean
join_allowed(struct nv_pc_pass *ctx, struct nv_value *a, struct nv_value *b)
{
int i;
struct nv_value *val;
if (a->reg.file != b->reg.file ||
nv_type_sizeof(a->reg.type) != nv_type_sizeof(b->reg.type))
return FALSE;
if (a->join->reg.id == b->join->reg.id)
return TRUE;
#if 1
/* either a or b or both have been assigned */
if (a->join->reg.id >= 0 && b->join->reg.id >= 0)
return FALSE;
else
if (b->join->reg.id >= 0) {
if (a->join->reg.id >= 0)
return FALSE;
val = a;
a = b;
b = val;
}
for (i = 0; i < ctx->pc->num_values; ++i) {
val = &ctx->pc->values[i];
if (val->join->reg.id != a->join->reg.id)
continue;
if (val->join != a->join && livei_have_overlap(val->join, b->join))
return FALSE;
}
return TRUE;
#endif
return FALSE;
}
static INLINE void
do_join_values(struct nv_pc_pass *ctx, struct nv_value *a, struct nv_value *b)
{
int j;
struct nv_value *bjoin = b->join;
if (b->join->reg.id >= 0)
a->join->reg.id = b->join->reg.id;
livei_unify(a->join, b->join);
#ifdef NV50_RA_DEBUG_JOIN
debug_printf("joining %i to %i\n", b->n, a->n);
#endif
/* make a->join the new representative */
for (j = 0; j < ctx->pc->num_values; ++j)
if (ctx->pc->values[j].join == bjoin)
ctx->pc->values[j].join = a->join;
assert(b->join == a->join);
}
static INLINE void
try_join_values(struct nv_pc_pass *ctx, struct nv_value *a, struct nv_value *b)
{
if (!join_allowed(ctx, a, b)) {
#ifdef NV50_RA_DEBUG_JOIN
debug_printf("cannot join %i to %i: not allowed\n", b->n, a->n);
#endif
return;
}
if (livei_have_overlap(a->join, b->join)) {
#ifdef NV50_RA_DEBUG_JOIN
debug_printf("cannot join %i to %i: livei overlap\n", b->n, a->n);
livei_print(a);
livei_print(b);
#endif
return;
}
do_join_values(ctx, a, b);
}
/* For each operand of each phi in b, generate a new value by inserting a MOV
* at the end of the block it is coming from and replace the operand with it.
* This eliminates liveness conflicts.
*/
static int
pass_generate_phi_movs(struct nv_pc_pass *ctx, struct nv_basic_block *b)
{
struct nv_instruction *i, *i2;
struct nv_basic_block *p, *pn;
struct nv_value *val;
int n, j;
b->pass_seq = ctx->pc->pass_seq;
for (n = 0; n < b->num_in; ++n) {
p = b->in[n];
assert(p);
if (b->num_in > 1 && p->out[0] && p->out[1]) { /* if without else */
pn = new_basic_block(ctx->pc);
if (p->out[0] == b)
p->out[0] = pn;
else
p->out[1] = pn;
if (p->exit->target == b) /* target to new else-block */
p->exit->target = pn;
for (j = 0; j < b->num_in; ++j) {
if (b->in[j] == p) {
b->in[j] = pn;
break;
}
}
pn->out[0] = b;
pn->in[0] = p;
pn->num_in = 1;
} else
pn = p;
ctx->pc->current_block = pn;
/* every block with PHIs will also have other operations */
for (i = b->phi; i && i->opcode == NV_OP_PHI; i = i->next) {
for (j = 0; j < 4; ++j) {
if (!i->src[j])
j = 3;
else
if (i->src[j]->value->insn->bb == p)
break;
}
if (j >= 4)
continue;
assert(i->src[j]);
val = i->src[j]->value;
/* XXX: should probably not insert this after terminator */
i2 = new_instruction(ctx->pc, NV_OP_MOV);
i2->def[0] = new_value(ctx->pc, val->reg.file, val->reg.type);
i2->src[0] = new_ref (ctx->pc, val);
i2->def[0]->insn = i2;
nv_reference(ctx->pc, &i->src[j], i2->def[0]);
}
if (pn != p && pn->exit) {
/* XXX: this branch should probably be eliminated */
ctx->pc->current_block = b->in[n ? 0 : 1];
i2 = new_instruction(ctx->pc, NV_OP_BRA);
i2->target = b;
i2->is_terminator = 1;
}
}
if (b->out[0] && b->out[0]->pass_seq < ctx->pc->pass_seq) {
pass_generate_phi_movs(ctx, b->out[0]);
}
if (b->out[1] && b->out[1]->pass_seq < ctx->pc->pass_seq) {
pass_generate_phi_movs(ctx, b->out[1]);
}
return 0;
}
static int
pass_join_values(struct nv_pc_pass *ctx, int iter)
{
int c, n;
for (n = 0; n < ctx->num_insns; ++n) {
struct nv_instruction *i = ctx->insns[n];
switch (i->opcode) {
case NV_OP_PHI:
if (!iter)
continue;
try_join_values(ctx, i->src[0]->value, i->src[1]->value);
try_join_values(ctx, i->def[0], i->src[0]->value);
break;
case NV_OP_MOV:
if (iter && i->src[0]->value->insn &&
!nv_is_vector_op(i->src[0]->value->join->insn->opcode))
try_join_values(ctx, i->def[0], i->src[0]->value);
break;
case NV_OP_SELECT:
if (!iter)
break;
assert(join_allowed(ctx, i->def[0], i->src[0]->value));
assert(join_allowed(ctx, i->def[0], i->src[1]->value));
do_join_values(ctx, i->def[0], i->src[0]->value);
do_join_values(ctx, i->def[0], i->src[1]->value);
break;
case NV_OP_TEX:
case NV_OP_TXB:
case NV_OP_TXL:
case NV_OP_TXQ:
if (iter)
break;
for (c = 0; c < 4; ++c) {
if (!i->src[c])
break;
do_join_values(ctx, i->def[c], i->src[c]->value);
}
break;
default:
break;
}
}
return 0;
}
static int
pass_order_instructions(struct nv_pc_pass *ctx, struct nv_basic_block *b)
{
struct nv_instruction *i;
b->priv = 0;
assert(!b->exit || !b->exit->next);
for (i = b->phi; i; i = i->next) {
i->serial = ctx->num_insns;
ctx->insns[ctx->num_insns++] = i;
}
b->pass_seq = ctx->pc->pass_seq;
if (!b->out[0])
return 0;
if (!b->out[1] && ++(b->out[0]->priv) != b->out[0]->num_in)
return 0;
if (b->out[0] != b)
pass_order_instructions(ctx, b->out[0]);
if (b->out[1] && b->out[1] != b)
pass_order_instructions(ctx, b->out[1]);
return 0;
}
static void
bb_live_set_print(struct nv_pc *pc, struct nv_basic_block *b)
{
#ifdef NV50_RA_DEBUG_LIVE_SETS
int j;
struct nv_value *val;
debug_printf("live_set of %p: ", b);
for (j = 0; j < pc->num_values; ++j) {
if (!(b->live_set[j / 32] & (1 << (j % 32))))
continue;
val = &pc->values[j];
if (!val->insn)
continue;
debug_printf("%i ", val->n);
}
debug_printf("\n");
#endif
}
static INLINE void
live_set_add(struct nv_basic_block *b, struct nv_value *val)
{
if (!val->insn) /* don't add non-def values */
return;
/* debug_printf("live[%p] <- %i\n", b, val->n); */
b->live_set[val->n / 32] |= 1 << (val->n % 32);
}
static INLINE void
live_set_rem(struct nv_basic_block *b, struct nv_value *val)
{
/* if (val->insn)
debug_printf("live[%p] -> %i\n", b, val->n); */
b->live_set[val->n / 32] &= ~(1 << (val->n % 32));
}
static INLINE boolean
live_set_test(struct nv_basic_block *b, struct nv_ref *ref)
{
int n = ref->value->n;
return b->live_set[n / 32] & (1 << (n % 32));
}
/* check if bf (future) can be reached from bp (past) */
static boolean
bb_reachable_by(struct nv_basic_block *bf, struct nv_basic_block *bp,
struct nv_basic_block *bt)
{
if (bf == bp)
return TRUE;
if (bp == bt)
return FALSE;
if (bp->out[0] && bp->out[0] != bp &&
bb_reachable_by(bf, bp->out[0], bt))
return TRUE;
if (bp->out[1] && bp->out[1] != bp &&
bb_reachable_by(bf, bp->out[1], bt))
return TRUE;
return FALSE;
}
/* The live set of a block contains those values that are live immediately
* before the beginning of the block.
*/
static int
pass_build_live_sets(struct nv_pc_pass *ctx, struct nv_basic_block *b)
{
struct nv_instruction *i;
int j, n, ret = 0;
/* slight hack for undecidedness: set phi = entry if it's undefined */
if (!b->phi)
b->phi = b->entry;
for (n = 0; n < 2; ++n) {
if (!b->out[n] || b->out[n] == b)
continue;
ret = pass_build_live_sets(ctx, b->out[n]);
if (ret)
return ret;
if (n == 0) {
for (j = 0; j < (ctx->pc->num_values + 31) / 32; ++j)
b->live_set[j] = b->out[n]->live_set[j];
} else {
for (j = 0; j < (ctx->pc->num_values + 31) / 32; ++j)
b->live_set[j] |= b->out[n]->live_set[j];
}
/* Kick values out of our live set that are created in incoming
* blocks of our successors that are not us.
*/
for (i = b->out[n]->phi; i && i->opcode == NV_OP_PHI; i = i->next) {
for (j = 0; j < 4; ++j) {
if (!i->src[j])
break;
assert(i->src[j]->value->insn);
if (bb_reachable_by(b, i->src[j]->value->insn->bb, b->out[n])) {
live_set_add(b, i->src[j]->value);
debug_printf("%p: live set + %i\n", b, i->src[j]->value->n);
} else {
live_set_rem(b, i->src[j]->value);
debug_printf("%p: live set - %i\n", b, i->src[j]->value->n);
}
}
}
}
if (b->pass_seq >= ctx->pc->pass_seq)
return 0;
b->pass_seq = ctx->pc->pass_seq;
debug_printf("%s: visiting block %p\n", __FUNCTION__, b);
if (!b->entry)
return 0;
bb_live_set_print(ctx->pc, b);
for (i = b->exit; i; i = i->prev) {
for (j = 0; j < 4; j++) {
if (!i->def[j])
break;
live_set_rem(b, i->def[j]);
}
for (j = 0; j < 4; j++) {
if (!i->src[j])
break;
live_set_add(b, i->src[j]->value);
}
if (i->src[4])
live_set_add(b, i->src[4]->value);
if (i->flags_def)
live_set_rem(b, i->flags_def);
if (i->flags_src)
live_set_add(b, i->flags_src->value);
}
bb_live_set_print(ctx->pc, b);
return 0;
}
static void collect_live_values(struct nv_basic_block *b, const int n)
{
int i;
if (b->out[0]) {
if (b->out[1]) { /* what to do about back-edges ? */
for (i = 0; i < n; ++i)
b->live_set[i] = b->out[0]->live_set[i] | b->out[1]->live_set[i];
} else {
memcpy(b->live_set, b->out[0]->live_set, n * sizeof(uint32_t));
}
} else
if (b->out[1]) {
memcpy(b->live_set, b->out[1]->live_set, n * sizeof(uint32_t));
} else {
memset(b->live_set, 0, n * sizeof(uint32_t));
}
}
/* NOTE: the live intervals of phi functions start the the first non-phi instruction */
static int
pass_build_intervals(struct nv_pc_pass *ctx, struct nv_basic_block *b)
{
struct nv_instruction *i, *i_stop;
int j, s;
const int n = (ctx->pc->num_values + 31) / 32;
debug_printf("building intervals for BB %i\n", b->id);
/* verify that first block does not have live-in values */
if (b->num_in == 0)
for (j = 0; j < n; ++j)
assert(b->live_set[j] == 0);
collect_live_values(b, n);
/* remove live-outs def'd in a parallel block, hopefully they're all phi'd */
for (j = 0; j < 2; ++j) {
if (!b->out[j] || !b->out[j]->phi)
continue;
for (i = b->out[j]->phi; i->opcode == NV_OP_PHI; i = i->next) {
live_set_rem(b, i->def[0]);
for (s = 0; s < 4; ++s) {
if (!i->src[s])
break;
assert(i->src[s]->value->insn);
if (bb_reachable_by(b, i->src[s]->value->insn->bb, b->out[j]))
live_set_add(b, i->src[s]->value);
else
live_set_rem(b, i->src[s]->value);
}
}
}
/* remaining live-outs are live until the end */
for (j = 0; j < ctx->pc->num_values; ++j) {
if (!(b->live_set[j / 32] & (1 << (j % 32))))
continue;
#ifdef NV50_RA_DEBUG_LIVEI
debug_printf("adding range for live value %i\n", j);
#endif
add_range(&ctx->pc->values[j], b, b->exit->serial + 1);
}
debug_printf("%s: looping through instructions now\n", __func__);
i_stop = b->entry ? b->entry->prev : NULL;
/* don't have to include phi functions here (will have 0 live range) */
for (i = b->exit; i != i_stop; i = i->prev) {
assert(i->serial >= b->phi->serial && i->serial <= b->exit->serial);
for (j = 0; j < 4; ++j) {
if (i->def[j])
live_set_rem(b, i->def[j]);
}
if (i->flags_def)
live_set_rem(b, i->flags_def);
for (j = 0; j < 5; ++j) {
if (i->src[j] && !live_set_test(b, i->src[j])) {
live_set_add(b, i->src[j]->value);
#ifdef NV50_RA_DEBUG_LIVEI
debug_printf("adding range for source that ends living: %i\n",
i->src[j]->value->n);
#endif
add_range(i->src[j]->value, b, i->serial);
}
}
if (i->flags_src && !live_set_test(b, i->flags_src)) {
live_set_add(b, i->flags_src->value);
#ifdef NV50_RA_DEBUG_LIVEI
debug_printf("adding range for source that ends living: %i\n",
i->flags_src->value->n);
#endif
add_range(i->flags_src->value, b, i->serial);
}
}
b->pass_seq = ctx->pc->pass_seq;
if (b->out[0] && b->out[0]->pass_seq < ctx->pc->pass_seq)
pass_build_intervals(ctx, b->out[0]);
if (b->out[1] && b->out[1]->pass_seq < ctx->pc->pass_seq)
pass_build_intervals(ctx, b->out[1]);
debug_printf("built intervals for block %p\n", b);
return 0;
}
static INLINE void
nv50_ctor_register_set(struct nv_pc *pc, struct register_set *set)
{
memset(set, 0, sizeof(*set));
set->last[NV_FILE_GPR] = 255;
set->last[NV_FILE_OUT] = 127;
set->last[NV_FILE_FLAGS] = 4;
set->last[NV_FILE_ADDR] = 4;
set->pc = pc;
}
static void
insert_ordered_tail(struct nv_value *list, struct nv_value *nval)
{
struct nv_value *elem = list->prev;
// debug_printf("inserting value %i\n", nval->n);
for (elem = list->prev;
elem != list && elem->livei->bgn > nval->livei->bgn;
elem = elem->prev);
/* now elem begins before or at the same time as val */
nval->prev = elem;
nval->next = elem->next;
elem->next->prev = nval;
elem->next = nval;
}
static int
pass_linear_scan(struct nv_pc_pass *ctx, int iter)
{
struct nv_instruction *i;
struct register_set f, free;
int k, n;
struct nv_value *cur, *val, *tmp[2];
struct nv_value active, inactive, handled, unhandled;
make_empty_list(&active);
make_empty_list(&inactive);
make_empty_list(&handled);
make_empty_list(&unhandled);
nv50_ctor_register_set(ctx->pc, &free);
/* joined values should have range = NULL and thus not be added;
* also, fixed memory values won't be added because they're not
* def'd, just used
*/
for (n = 0; n < ctx->num_insns; ++n) {
i = ctx->insns[n];
for (k = 0; k < 4; ++k) {
if (i->def[k] && i->def[k]->livei)
insert_ordered_tail(&unhandled, i->def[k]);
else
if (0 && i->def[k])
debug_printf("skipping def'd value %i: no livei\n", i->def[k]->n);
}
if (i->flags_def && i->flags_def->livei)
insert_ordered_tail(&unhandled, i->flags_def);
}
for (val = unhandled.next; val != unhandled.prev; val = val->next) {
assert(val->join == val);
assert(val->livei->bgn <= val->next->livei->bgn);
}
foreach_s(cur, tmp[0], &unhandled) {
remove_from_list(cur);
/* debug_printf("handling value %i\n", cur->n); */
foreach_s(val, tmp[1], &active) {
if (livei_end(val) <= cur->livei->bgn) {
reg_release(&free, val);
move_to_head(&handled, val);
} else
if (!livei_contains(val, cur->livei->bgn)) {
reg_release(&free, val);
move_to_head(&inactive, val);
}
}
foreach_s(val, tmp[1], &inactive) {
if (livei_end(val) <= cur->livei->bgn)
move_to_head(&handled, val);
else
if (livei_contains(val, cur->livei->bgn)) {
reg_occupy(&free, val);
move_to_head(&active, val);
}
}
f = free;
foreach(val, &inactive)
if (livei_have_overlap(val, cur))
reg_occupy(&f, val);
foreach(val, &unhandled)
if (val->reg.id >= 0 && livei_have_overlap(val, cur))
reg_occupy(&f, val);
if (cur->reg.id < 0) {
boolean mem = FALSE;
if (nv_is_vector_op(cur->insn->opcode))
mem = !reg_assign(&f, &cur->insn->def[0], 4);
else
if (iter)
mem = !reg_assign(&f, &cur, 1);
if (mem) {
NOUVEAU_ERR("out of registers\n");
abort();
}
}
insert_at_head(&active, cur);
reg_occupy(&free, cur);
}
return 0;
}
static int
pass_eliminate_moves(struct nv_pc_pass *ctx)
{
return 0;
}
int
nv_pc_exec_pass1(struct nv_pc *pc)
{
struct nv_pc_pass *ctx;
int i, ret;
debug_printf("REGISTER ALLOCATION - entering\n");
ctx = CALLOC_STRUCT(nv_pc_pass);
if (!ctx)
return -1;
ctx->pc = pc;
nv_print_program(ctx->pc->root);
ctx->insns = CALLOC(pc->num_instructions, sizeof(struct nv_instruction *));
pc->pass_seq++;
ret = pass_generate_phi_movs(ctx, pc->root);
assert(!ret);
nv_print_program(ctx->pc->root);
for (i = 0; i < pc->loop_nesting_bound; ++i) {
pc->pass_seq++;
ret = pass_build_live_sets(ctx, pc->root);
assert(!ret && "live sets");
if (ret) {
NOUVEAU_ERR("failed to build live sets (iteration %d)\n", i);
goto out;
}
}
pc->pass_seq++;
ret = pass_order_instructions(ctx, pc->root);
assert(!ret && "order instructions");
if (ret)
goto out;
pc->pass_seq++;
ret = pass_build_intervals(ctx, pc->root);
assert(!ret && "build intervals");
if (ret) {
NOUVEAU_ERR("failed to build live intervals\n");
goto out;
}
for (i = 0; i < 2; ++i) {
ret = pass_join_values(ctx, i);
if (ret)
goto out;
ret = pass_linear_scan(ctx, i);
if (ret)
goto out;
}
assert(!ret && "joining");
ret = pass_eliminate_moves(ctx);
for (i = 0; i < pc->num_values; ++i)
livei_release(&pc->values[i]);
debug_printf("REGISTER ALLOCATION - leaving\n");
nv_print_program(ctx->pc->root);
out:
FREE(ctx);
return ret;
}
File diff suppressed because it is too large Load Diff
+96 -55
View File
@@ -1,75 +1,116 @@
#ifndef __NV50_PROGRAM_H__
#define __NV50_PROGRAM_H__
/*
* Copyright 2010 Ben Skeggs
*
* 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_PROG_H__
#define __NV50_PROG_H__
#include "pipe/p_state.h"
#include "tgsi/tgsi_scan.h"
#include "nouveau/nouveau_class.h"
struct nv50_program_exec {
struct nv50_program_exec *next;
struct nv50_varying {
uint8_t id; /* tgsi index */
uint8_t hw; /* hw index, nv50 wants flat FP inputs last */
unsigned inst[2];
struct {
int index;
unsigned mask;
unsigned shift;
} param;
};
uint8_t mask : 4;
uint8_t linear : 1;
uint8_t pad : 3;
struct nv50_sreg4 {
uint8_t hw; /* hw index, nv50 wants flat FP inputs last */
uint8_t id; /* tgsi index */
uint8_t mask;
boolean linear;
ubyte sn, si; /* semantic name & index */
ubyte sn; /* semantic name */
ubyte si; /* semantic index */
};
struct nv50_program {
struct pipe_shader_state pipe;
struct tgsi_shader_info info;
boolean translated;
struct pipe_shader_state pipe;
unsigned type;
struct nv50_program_exec *exec_head;
struct nv50_program_exec *exec_tail;
unsigned exec_size;
struct nouveau_resource *data[1];
unsigned data_start[1];
ubyte type;
boolean translated;
struct nouveau_bo *bo;
struct nouveau_bo *bo;
struct nouveau_stateobj *so;
uint32_t *immd;
unsigned immd_nr;
unsigned param_nr;
uint32_t *code;
unsigned code_size;
unsigned code_start; /* offset inside bo */
uint32_t *immd;
unsigned immd_size;
unsigned parm_size; /* size limit of uniform buffer */
struct {
unsigned high_temp;
unsigned high_result;
ubyte max_gpr; /* REG_ALLOC_TEMP */
ubyte max_out; /* REG_ALLOC_RESULT or FP_RESULT_COUNT */
uint32_t attr[2];
uint32_t regs[4];
ubyte in_nr;
ubyte out_nr;
struct nv50_varying in[16];
struct nv50_varying out[16];
/* for VPs, io_nr doesn't count 'private' results (PSIZ etc.) */
unsigned in_nr, out_nr;
struct nv50_sreg4 in[PIPE_MAX_SHADER_INPUTS];
struct nv50_sreg4 out[PIPE_MAX_SHADER_OUTPUTS];
struct {
uint32_t attrs[3]; /* VP_ATTR_EN_0,1 and VP_GP_BUILTIN_ATTR_EN */
ubyte psiz;
ubyte bfc[2];
ubyte edgeflag;
ubyte clpd;
ubyte clpd_nr;
} vp;
/* FP colour inputs, VP/GP back colour outputs */
struct nv50_sreg4 two_side[2];
struct {
uint32_t flags[2]; /* 0x19a8, 196c */
uint32_t interp; /* 0x1988 */
uint32_t colors; /* 0x1904 */
} fp;
/* GP only */
unsigned vert_count;
uint8_t prim_type;
struct {
ubyte primid; /* primitive id output register */
uint8_t vert_count;
uint8_t prim_type; /* point, line strip or tri strip */
} gp;
/* VP & GP only */
uint8_t clpd, clpd_nr;
uint8_t psiz;
uint8_t edgeflag_in;
/* FP & GP only */
uint8_t prim_id;
} cfg;
void *fixups;
unsigned num_fixups;
};
#endif
#define NV50_INTERP_LINEAR (1 << 0)
#define NV50_INTERP_FLAT (1 << 1)
#define NV50_INTERP_CENTROID (1 << 2)
struct nv50_translation_info {
struct nv50_program *p;
unsigned inst_nr;
ubyte input_file;
ubyte output_file;
ubyte input_map[PIPE_MAX_SHADER_INPUTS][4];
ubyte output_map[PIPE_MAX_SHADER_OUTPUTS][4];
ubyte interp_mode[PIPE_MAX_SHADER_INPUTS];
int input_access[PIPE_MAX_SHADER_INPUTS][4];
int output_access[PIPE_MAX_SHADER_OUTPUTS][4];
boolean indirect_inputs;
boolean indirect_outputs;
struct tgsi_shader_info scan;
uint32_t *immd32;
unsigned immd32_nr;
ubyte edgeflag_out;
};
int nv50_generate_code(struct nv50_translation_info *ti);
boolean nv50_program_tx(struct nv50_program *p);
#endif /* __NV50_PROG_H__ */
+1 -1
View File
@@ -227,7 +227,7 @@ nv50_push_elements_instanced(struct pipe_context *pipe,
ctx.idxbuf = NULL;
ctx.vtx_size = 0;
ctx.edgeflag = 0.5f;
ctx.edgeflag_attr = nv50->vertprog->cfg.edgeflag_in;
ctx.edgeflag_attr = nv50->vertprog->vp.edgeflag;
/* map vertex buffers, determine vertex size */
for (i = 0; i < nv50->vtxelt->num_elements; i++) {
@@ -0,0 +1,619 @@
/*
* Copyright 2008 Ben Skeggs
* 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 "pipe/p_context.h"
#include "pipe/p_defines.h"
#include "pipe/p_state.h"
#include "util/u_inlines.h"
#include "nv50_context.h"
#include "nv50_transfer.h"
static void
nv50_transfer_constbuf(struct nv50_context *nv50,
struct pipe_resource *buf, unsigned size, unsigned cbi)
{
struct pipe_context *pipe = &nv50->pipe;
struct pipe_transfer *transfer;
struct nouveau_channel *chan = nv50->screen->base.channel;
struct nouveau_grobj *tesla = nv50->screen->tesla;
uint32_t *map;
unsigned count, start;
map = pipe_buffer_map(pipe, buf, PIPE_TRANSFER_READ, &transfer);
if (!map)
return;
count = MIN2(buf->width0, size);
start = 0;
while (count) {
unsigned nr = count;
nr = MIN2(nr, 2047);
/* FIXME: emit relocs for unsuiTed MM */
BEGIN_RING(chan, tesla, NV50TCL_CB_ADDR, 1);
OUT_RING (chan, (start << 8) | cbi);
BEGIN_RING_NI(chan, tesla, NV50TCL_CB_DATA(0), nr);
OUT_RINGp (chan, map, nr);
count -= nr;
start += nr;
map += nr;
}
pipe_buffer_unmap(pipe, buf, transfer);
}
static void
nv50_program_validate_data(struct nv50_context *nv50, struct nv50_program *p)
{
struct nouveau_channel *chan = nv50->screen->base.channel;
struct nouveau_grobj *tesla = nv50->screen->tesla;
unsigned cbi;
if (p->immd_size) {
uint32_t *data = p->immd;
unsigned count = p->immd_size / 4;
unsigned start = 0;
while (count) {
unsigned nr = count;
nr = MIN2(nr, 2047);
BEGIN_RING(chan, tesla, NV50TCL_CB_ADDR, 1);
OUT_RING (chan, (start << 8) | NV50_CB_PMISC);
BEGIN_RING_NI(chan, tesla, NV50TCL_CB_DATA(0), nr);
OUT_RINGp (chan, data, nr);
count -= nr;
start += nr;
data += nr;
}
}
if (p->parm_size == 0)
return;
switch (p->type) {
case PIPE_SHADER_VERTEX:
cbi = NV50_CB_PVP;
break;
case PIPE_SHADER_FRAGMENT:
cbi = NV50_CB_PFP;
break;
case PIPE_SHADER_GEOMETRY:
cbi = NV50_CB_PGP;
break;
default:
assert(0);
break;
}
nv50_transfer_constbuf(nv50, nv50->constbuf[p->type], p->parm_size, cbi);
}
static void
nv50_program_validate_code(struct nv50_context *nv50, struct nv50_program *p)
{
struct nouveau_channel *chan = nv50->screen->base.channel;
struct nouveau_grobj *tesla = nv50->screen->tesla;
struct nouveau_grobj *eng2d = nv50->screen->eng2d;
int ret;
unsigned offset;
unsigned size = p->code_size;
uint32_t *data = p->code;
assert(p->translated);
/* TODO: use a single bo (for each type) for shader code */
if (p->bo)
return;
ret = nouveau_bo_new(chan->device, NOUVEAU_BO_VRAM, 0x100, size, &p->bo);
assert(!ret);
offset = p->code_start = 0;
BEGIN_RING(chan, eng2d, NV50_2D_DST_FORMAT, 2);
OUT_RING (chan, NV50_2D_DST_FORMAT_R8_UNORM);
OUT_RING (chan, 1);
BEGIN_RING(chan, eng2d, NV50_2D_DST_PITCH, 1);
OUT_RING (chan, 0x40000);
BEGIN_RING(chan, eng2d, NV50_2D_DST_WIDTH, 2);
OUT_RING (chan, 0x10000);
OUT_RING (chan, 1);
while (size) {
unsigned nr = size / 4;
if (AVAIL_RING(chan) < 32)
FIRE_RING(chan);
nr = MIN2(nr, AVAIL_RING(chan) - 18);
nr = MIN2(nr, 1792);
if (nr < (size / 4))
nr &= ~0x3f;
assert(!(size & 3));
BEGIN_RING(chan, eng2d, NV50_2D_DST_ADDRESS_HIGH, 2);
OUT_RELOCh(chan, p->bo, offset, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
OUT_RELOCl(chan, p->bo, offset, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
BEGIN_RING(chan, eng2d, NV50_2D_SIFC_BITMAP_ENABLE, 2);
OUT_RING (chan, 0);
OUT_RING (chan, NV50_2D_SIFC_FORMAT_R8_UNORM);
BEGIN_RING(chan, eng2d, NV50_2D_SIFC_WIDTH, 10);
OUT_RING (chan, nr * 4);
OUT_RING (chan, 1);
OUT_RING (chan, 0);
OUT_RING (chan, 1);
OUT_RING (chan, 0);
OUT_RING (chan, 1);
OUT_RING (chan, 0);
OUT_RING (chan, 0);
OUT_RING (chan, 0);
OUT_RING (chan, 0);
BEGIN_RING_NI(chan, eng2d, NV50_2D_SIFC_DATA, nr);
OUT_RINGp (chan, data, nr);
data += nr;
offset += nr * 4;
size -= nr * 4;
}
BEGIN_RING(chan, tesla, NV50TCL_CODE_CB_FLUSH, 1);
OUT_RING (chan, 0);
}
static void
nv50_vp_update_stateobj(struct nv50_context *nv50, struct nv50_program *p)
{
struct nouveau_grobj *tesla = nv50->screen->tesla;
struct nouveau_stateobj *so = so_new(5, 7, 2);
nv50_program_validate_code(nv50, p);
so_method(so, tesla, NV50TCL_VP_ADDRESS_HIGH, 2);
so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
NOUVEAU_BO_HIGH, 0, 0);
so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
NOUVEAU_BO_LOW, 0, 0);
so_method(so, tesla, NV50TCL_VP_ATTR_EN_0, 2);
so_data (so, p->vp.attrs[0]);
so_data (so, p->vp.attrs[1]);
so_method(so, tesla, NV50TCL_VP_REG_ALLOC_RESULT, 1);
so_data (so, p->max_out);
so_method(so, tesla, NV50TCL_VP_REG_ALLOC_TEMP, 1);
so_data (so, p->max_gpr);
so_method(so, tesla, NV50TCL_VP_START_ID, 1);
so_data (so, p->code_start);
so_ref(so, &p->so);
so_ref(NULL, &so);
}
static void
nv50_fp_update_stateobj(struct nv50_context *nv50, struct nv50_program *p)
{
struct nouveau_grobj *tesla = nv50->screen->tesla;
struct nouveau_stateobj *so = so_new(6, 7, 2);
nv50_program_validate_code(nv50, p);
so_method(so, tesla, NV50TCL_FP_ADDRESS_HIGH, 2);
so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
NOUVEAU_BO_HIGH, 0, 0);
so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
NOUVEAU_BO_LOW, 0, 0);
so_method(so, tesla, NV50TCL_FP_REG_ALLOC_TEMP, 1);
so_data (so, p->max_gpr);
so_method(so, tesla, NV50TCL_FP_RESULT_COUNT, 1);
so_data (so, p->max_out);
so_method(so, tesla, NV50TCL_FP_CONTROL, 1);
so_data (so, p->fp.flags[0]);
so_method(so, tesla, NV50TCL_FP_CTRL_UNK196C, 1);
so_data (so, p->fp.flags[1]);
so_method(so, tesla, NV50TCL_FP_START_ID, 1);
so_data (so, p->code_start);
so_ref(so, &p->so);
so_ref(NULL, &so);
}
static void
nv50_gp_update_stateobj(struct nv50_context *nv50, struct nv50_program *p)
{
struct nouveau_grobj *tesla = nv50->screen->tesla;
struct nouveau_stateobj *so = so_new(6, 7, 2);
nv50_program_validate_code(nv50, p);
so_method(so, tesla, NV50TCL_GP_ADDRESS_HIGH, 2);
so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
NOUVEAU_BO_HIGH, 0, 0);
so_reloc (so, p->bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
NOUVEAU_BO_LOW, 0, 0);
so_method(so, tesla, NV50TCL_GP_REG_ALLOC_TEMP, 1);
so_data (so, p->max_gpr);
so_method(so, tesla, NV50TCL_GP_REG_ALLOC_RESULT, 1);
so_data (so, p->max_out);
so_method(so, tesla, NV50TCL_GP_OUTPUT_PRIMITIVE_TYPE, 1);
so_data (so, p->gp.prim_type);
so_method(so, tesla, NV50TCL_GP_VERTEX_OUTPUT_COUNT, 1);
so_data (so, p->gp.vert_count);
so_method(so, tesla, NV50TCL_GP_START_ID, 1);
so_data (so, p->code_start);
so_ref(so, &p->so);
so_ref(NULL, &so);
}
static boolean
nv50_program_validate(struct nv50_program *p)
{
p->translated = nv50_program_tx(p);
assert(p->translated);
return p->translated;
}
struct nouveau_stateobj *
nv50_vertprog_validate(struct nv50_context *nv50)
{
struct nv50_program *p = nv50->vertprog;
struct nouveau_stateobj *so = NULL;
if (!p->translated) {
if (nv50_program_validate(p))
nv50_vp_update_stateobj(nv50, p);
else
return NULL;
}
if (nv50->dirty & NV50_NEW_VERTPROG_CB)
nv50_program_validate_data(nv50, p);
if (!(nv50->dirty & NV50_NEW_VERTPROG))
return NULL;
nv50_program_validate_code(nv50, p);
so_ref(p->so, &so);
return so;
}
struct nouveau_stateobj *
nv50_fragprog_validate(struct nv50_context *nv50)
{
struct nv50_program *p = nv50->fragprog;
struct nouveau_stateobj *so = NULL;
if (!p->translated) {
if (nv50_program_validate(p))
nv50_fp_update_stateobj(nv50, p);
else
return NULL;
}
if (nv50->dirty & NV50_NEW_FRAGPROG_CB)
nv50_program_validate_data(nv50, p);
if (!(nv50->dirty & NV50_NEW_FRAGPROG))
return NULL;
nv50_program_validate_code(nv50, p);
so_ref(p->so, &so);
return so;
}
struct nouveau_stateobj *
nv50_geomprog_validate(struct nv50_context *nv50)
{
struct nv50_program *p = nv50->geomprog;
struct nouveau_stateobj *so = NULL;
if (!p->translated) {
if (nv50_program_validate(p))
nv50_gp_update_stateobj(nv50, p);
else
return NULL;
}
if (nv50->dirty & NV50_NEW_GEOMPROG_CB)
nv50_program_validate_data(nv50, p);
if (!(nv50->dirty & NV50_NEW_GEOMPROG))
return NULL;
nv50_program_validate_code(nv50, p);
so_ref(p->so, &so);
return so;
}
/* XXX: this might not work correctly in all cases yet: we assume that
* an FP generic input that is not written in the VP is gl_PointCoord.
*/
static uint32_t
nv50_pntc_replace(struct nv50_context *nv50, uint32_t pntc[8], unsigned m)
{
struct nv50_program *vp = nv50->vertprog;
struct nv50_program *fp = nv50->fragprog;
unsigned i, c;
memset(pntc, 0, 8 * sizeof(uint32_t));
if (nv50->geomprog)
vp = nv50->geomprog;
for (i = 0; i < fp->in_nr; i++) {
unsigned j, n = util_bitcount(fp->in[i].mask);
if (fp->in[i].sn != TGSI_SEMANTIC_GENERIC) {
m += n;
continue;
}
for (j = 0; j < vp->out_nr; ++j)
if (vp->out[j].sn == fp->in[i].sn && vp->out[j].si == fp->in[i].si)
break;
if (j < vp->out_nr) {
ubyte en = nv50->rasterizer->pipe.sprite_coord_enable;
if (!(en & (1 << vp->out[j].si))) {
m += n;
continue;
}
}
/* this is either PointCoord or replaced by sprite coords */
for (c = 0; c < 4; c++) {
if (!(fp->in[i].mask & (1 << c)))
continue;
pntc[m / 8] |= (c + 1) << ((m % 8) * 4);
++m;
}
}
if (nv50->rasterizer->pipe.sprite_coord_mode == PIPE_SPRITE_COORD_LOWER_LEFT)
return 0;
return (1 << 4);
}
static int
nv50_vec4_map(uint32_t *map32, int mid, uint32_t lin[4],
struct nv50_varying *in, struct nv50_varying *out)
{
int c;
uint8_t mv = out->mask, mf = in->mask, oid = out->hw;
uint8_t *map = (uint8_t *)map32;
for (c = 0; c < 4; ++c) {
if (mf & 1) {
if (in->linear)
lin[mid / 32] |= 1 << (mid % 32);
if (mv & 1)
map[mid] = oid;
else
if (c == 3)
map[mid] |= 1;
++mid;
}
oid += mv & 1;
mf >>= 1;
mv >>= 1;
}
return mid;
}
struct nouveau_stateobj *
nv50_fp_linkage_validate(struct nv50_context *nv50)
{
struct nouveau_grobj *tesla = nv50->screen->tesla;
struct nv50_program *vp;
struct nv50_program *fp = nv50->fragprog;
struct nouveau_stateobj *so;
struct nv50_varying dummy;
int i, n, c, m;
uint32_t map[16], lin[4], pntc[8];
uint32_t interp = fp->fp.interp;
uint32_t colors = fp->fp.colors;
uint32_t clip = 0x04;
uint32_t psiz = 0x000;
uint32_t primid = 0;
uint32_t sysval = 0;
if (nv50->geomprog) {
vp = nv50->geomprog;
memset(map, 0x80, sizeof(map));
} else {
vp = nv50->vertprog;
memset(map, 0x40, sizeof(map));
}
memset(lin, 0, sizeof(lin));
dummy.linear = 0;
dummy.mask = 0xf; /* map all components of HPOS */
m = nv50_vec4_map(map, 0, lin, &dummy, &vp->out[0]);
if (vp->vp.clpd < 0x40) {
for (c = 0; c < vp->vp.clpd_nr; ++c) {
map[m / 4] |= (vp->vp.clpd + c) << ((m % 4) * 8);
++m;
}
clip |= vp->vp.clpd_nr << 8;
}
colors |= m << 8; /* adjust BFC0 id */
/* if light_twoside is active, it seems FFC0_ID == BFC0_ID is bad */
if (nv50->rasterizer->pipe.light_twoside) {
for (i = 0; i < 2; ++i)
m = nv50_vec4_map(map, m, lin,
&fp->in[fp->vp.bfc[i]],
&vp->out[vp->vp.bfc[i]]);
}
colors += m - 4; /* adjust FFC0 id */
interp |= m << 8; /* set mid where 'normal' FP inputs start */
dummy.mask = 0x0;
for (i = 0; i < fp->in_nr; i++) {
for (n = 0; n < vp->out_nr; ++n)
if (vp->out[n].sn == fp->in[i].sn &&
vp->out[n].si == fp->in[i].si)
break;
m = nv50_vec4_map(map, m, lin,
&fp->in[i], (n < vp->out_nr) ? &vp->out[n] : &dummy);
}
/* PrimitiveID either is replaced by the system value, or
* written by the geometry shader into an output register
*/
if (fp->gp.primid < 0x40) {
map[m / 4] |= vp->gp.primid << ((m % 4) * 8);
primid = m++;
}
if (nv50->rasterizer->pipe.point_size_per_vertex) {
map[m / 4] |= vp->vp.psiz << ((m % 4) * 8);
psiz = (m++ << 4) | 1;
}
/* now fill the stateobj (at most 28 so_data) */
so = so_new(10, 54, 0);
n = (m + 3) / 4;
assert(m <= 64);
if (vp->type == PIPE_SHADER_GEOMETRY) {
so_method(so, tesla, NV50TCL_GP_RESULT_MAP_SIZE, 1);
so_data (so, m);
so_method(so, tesla, NV50TCL_GP_RESULT_MAP(0), n);
so_datap (so, map, n);
} else {
so_method(so, tesla, NV50TCL_VP_GP_BUILTIN_ATTR_EN, 1);
so_data (so, vp->vp.attrs[2]);
so_method(so, tesla, NV50TCL_MAP_SEMANTIC_4, 1);
so_data (so, primid);
so_method(so, tesla, NV50TCL_VP_RESULT_MAP_SIZE, 1);
so_data (so, m);
so_method(so, tesla, NV50TCL_VP_RESULT_MAP(0), n);
so_datap (so, map, n);
}
//colors = 0x01000404;
so_method(so, tesla, NV50TCL_MAP_SEMANTIC_0, 4);
so_data (so, colors);
so_data (so, clip);
so_data (so, sysval);
so_data (so, psiz);
so_method(so, tesla, NV50TCL_FP_INTERPOLANT_CTRL, 1);
so_data (so, interp);
so_method(so, tesla, NV50TCL_NOPERSPECTIVE_BITMAP(0), 4);
so_datap (so, lin, 4);
if (nv50->rasterizer->pipe.sprite_coord_enable) {
so_method(so, tesla, NV50TCL_POINT_SPRITE_CTRL, 1);
so_data (so,
nv50_pntc_replace(nv50, pntc, (interp >> 8) & 0xff));
so_method(so, tesla, NV50TCL_POINT_COORD_REPLACE_MAP(0), 8);
so_datap (so, pntc, 8);
}
so_method(so, tesla, NV50TCL_GP_ENABLE, 1);
so_data (so, (vp->type == PIPE_SHADER_GEOMETRY) ? 1 : 0);
return so;
}
static int
nv50_vp_gp_mapping(uint32_t *map32, int m,
struct nv50_program *vp, struct nv50_program *gp)
{
uint8_t *map = (uint8_t *)map32;
int i, j, c;
for (i = 0; i < gp->in_nr; ++i) {
uint8_t oid = 0, mv = 0, mg = gp->in[i].mask;
for (j = 0; j < vp->out_nr; ++j) {
if (vp->out[j].sn == gp->in[i].sn &&
vp->out[j].si == gp->in[i].si) {
mv = vp->out[j].mask;
oid = vp->out[j].hw;
break;
}
}
for (c = 0; c < 4; ++c, mv >>= 1, mg >>= 1) {
if (mg & mv & 1)
map[m++] = oid;
else
if (mg & 1)
map[m++] = (c == 3) ? 0x41 : 0x40;
oid += mv & 1;
}
}
return m;
}
struct nouveau_stateobj *
nv50_gp_linkage_validate(struct nv50_context *nv50)
{
struct nouveau_grobj *tesla = nv50->screen->tesla;
struct nouveau_stateobj *so;
struct nv50_program *vp = nv50->vertprog;
struct nv50_program *gp = nv50->geomprog;
uint32_t map[16];
int m = 0;
if (!gp)
return NULL;
memset(map, 0, sizeof(map));
m = nv50_vp_gp_mapping(map, m, vp, gp);
so = so_new(3, 24 - 3, 0);
so_method(so, tesla, NV50TCL_VP_GP_BUILTIN_ATTR_EN, 1);
so_data (so, vp->vp.attrs[2] | gp->vp.attrs[2]);
assert(m <= 32);
so_method(so, tesla, NV50TCL_VP_RESULT_MAP_SIZE, 1);
so_data (so, m);
m = (m + 3) / 4;
so_method(so, tesla, NV50TCL_VP_RESULT_MAP(0), m);
so_datap (so, map, m);
return so;
}
-3
View File
@@ -546,7 +546,6 @@ nv50_vp_state_create(struct pipe_context *pipe,
p->pipe.tokens = tgsi_dup_tokens(cso->tokens);
p->type = PIPE_SHADER_VERTEX;
tgsi_scan_shader(p->pipe.tokens, &p->info);
return (void *)p;
}
@@ -578,7 +577,6 @@ nv50_fp_state_create(struct pipe_context *pipe,
p->pipe.tokens = tgsi_dup_tokens(cso->tokens);
p->type = PIPE_SHADER_FRAGMENT;
tgsi_scan_shader(p->pipe.tokens, &p->info);
return (void *)p;
}
@@ -610,7 +608,6 @@ nv50_gp_state_create(struct pipe_context *pipe,
p->pipe.tokens = tgsi_dup_tokens(cso->tokens);
p->type = PIPE_SHADER_GEOMETRY;
tgsi_scan_shader(p->pipe.tokens, &p->info);
return (void *)p;
}
@@ -81,6 +81,9 @@ validate_fb(struct nv50_context *nv50)
case PIPE_FORMAT_R16G16B16A16_UNORM:
so_data(so, NV50TCL_RT_FORMAT_R16G16B16A16_UNORM);
break;
case PIPE_FORMAT_R16G16B16A16_FLOAT:
so_data(so, NV50TCL_RT_FORMAT_R16G16B16A16_FLOAT);
break;
case PIPE_FORMAT_R32G32B32A32_FLOAT:
so_data(so, NV50TCL_RT_FORMAT_R32G32B32A32_FLOAT);
break;
@@ -135,6 +138,12 @@ validate_fb(struct nv50_context *nv50)
case PIPE_FORMAT_Z32_FLOAT:
so_data(so, NV50TCL_ZETA_FORMAT_Z32_FLOAT);
break;
case PIPE_FORMAT_Z32_FLOAT_S8X24_USCALED:
so_data(so, NV50TCL_ZETA_FORMAT_Z32_FLOAT_X24S8_UNORM);
break;
case PIPE_FORMAT_Z16_UNORM:
so_data(so, NV50TCL_ZETA_FORMAT_Z16_UNORM);
break;
default:
NOUVEAU_ERR("AIIII unknown format %s\n",
util_format_name(fb->zsbuf->format));
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -519,7 +519,7 @@ nv50_vbo_static_attrib(struct nv50_context *nv50, unsigned attrib,
so_data (so, fui(v[1]));
break;
case 1:
if (attrib == nv50->vertprog->cfg.edgeflag_in) {
if (attrib == nv50->vertprog->vp.edgeflag) {
so_method(so, tesla, NV50TCL_EDGEFLAG_ENABLE, 1);
so_data (so, v[0] ? 1 : 0);
}
@@ -560,7 +560,7 @@ nv50_vbo_validate(struct nv50_context *nv50)
nv50->vbo_fifo = 0;
if (nv50->screen->force_push ||
nv50->vertprog->cfg.edgeflag_in < 16)
nv50->vertprog->vp.edgeflag < 16)
nv50->vbo_fifo = 0xffff;
for (i = 0; i < nv50->vtxbuf_nr; i++) {