nv50: import new compiler
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
+490
-4611
File diff suppressed because it is too large
Load Diff
@@ -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__ */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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++) {
|
||||
|
||||
Reference in New Issue
Block a user