#include "nv50_context.h"
#include "nv50_pc.h"
-// Definitions
-
#define FLAGS_CC_SHIFT 7
#define FLAGS_ID_SHIFT 12
#define FLAGS_WR_ID_SHIFT 4
#define FLAGS_WR_EN (1 << 6)
#define FLAGS_WR_ID_MASK (0x3 << FLAGS_WR_ID_SHIFT)
+#define NV50_FIXUP_CODE_RELOC 0
+#define NV50_FIXUP_DATA_RELOC 1
+
+struct nv50_fixup {
+ uint8_t type;
+ int8_t shift;
+ uint32_t mask;
+ uint32_t data;
+ uint32_t offset;
+};
+
+void
+nv50_relocate_program(struct nv50_program *p,
+ uint32_t code_base,
+ uint32_t data_base)
+{
+ struct nv50_fixup *f = (struct nv50_fixup *)p->fixups;
+ unsigned i;
+
+ for (i = 0; i < p->num_fixups; ++i) {
+ uint32_t data;
+
+ switch (f[i].type) {
+ case NV50_FIXUP_CODE_RELOC: data = code_base + f[i].data; break;
+ case NV50_FIXUP_DATA_RELOC: data = data_base + f[i].data; break;
+ default:
+ data = f[i].data;
+ break;
+ }
+ data = (f[i].shift < 0) ? (data >> -f[i].shift) : (data << f[i].shift);
+
+ p->code[f[i].offset / 4] &= ~f[i].mask;
+ p->code[f[i].offset / 4] |= data & f[i].mask;
+ }
+}
+
+static void
+new_fixup(struct nv_pc *pc, uint8_t ty, int w, uint32_t data, uint32_t m, int s)
+{
+ struct nv50_fixup *f;
+
+ const unsigned size = sizeof(struct nv50_fixup);
+ const unsigned n = pc->num_fixups;
+
+ if (!(n % 8))
+ pc->fixups = REALLOC(pc->fixups, n * size, (n + 8) * size);
+
+ f = (struct nv50_fixup *)pc->fixups;
+
+ f[n].offset = (pc->bin_pos + w) * 4;
+ f[n].type = ty;
+ f[n].data = data;
+ f[n].mask = m;
+ f[n].shift = s;
+
+ ++pc->num_fixups;
+}
+
const ubyte nv50_inst_min_size_tab[NV_OP_COUNT] =
{
0, 0, 0, 8, 8, 4, 4, 4, 8, 4, 4, 8, 8, 8, 8, 8, /* 15 */
4, 8, 8, 8, 8, 8, 0, 0, 8
};
-/* XXX: silence, you ! */
-unsigned
-nv50_inst_min_size(struct nv_instruction *i);
-
unsigned
nv50_inst_min_size(struct nv_instruction *i)
{
set_immd_u32(pc, get_immd_u32(ref));
}
-static void
-new_fixup(struct nv_pc *pc, unsigned type, uint32_t data, uint32_t m, int s)
-{
- const unsigned size = sizeof(struct nv_fixup);
- const unsigned n = pc->num_fixups;
- return;
-
- if (!(n % 8))
- pc->fixups = REALLOC(pc->fixups, n * size, (n + 8) * size);
-
- pc->fixups[n].offset = pc->bin_pos + (s / 32);
- pc->fixups[n].type = type;
- pc->fixups[n].data = data;
- pc->fixups[n].mask = m << (s % 32);
- pc->fixups[n].shift = s % 32;
-
- ++pc->num_fixups;
-
- assert(((data << (s % 32)) & pc->fixups[n].mask) == (data << (s % 32)));
-}
-
+/* Allocate data in immediate buffer, if we want to load the immediate
+ * for a constant buffer instead of inlining it into the code.
+ */
static void
nv_pc_alloc_immd(struct nv_pc *pc, struct nv_ref *ref)
{
struct nv_reg *reg = &value->join->reg;
if (reg->id < 0) {
- debug_printf("WARNING: unused dst, hope we can bucket it !\n");
- pc->emit[0] |= 127 << 2;
+ pc->emit[0] |= (127 << 2) | 1; /* set 'long'-bit to catch bugs */
pc->emit[1] |= 0x8;
return;
}
pc->emit[1] |= 0x8;
else
if (reg->file == NV_FILE_ADDR)
- assert(0);
+ assert(0);
pc->emit[0] |= reg->id << 2;
}
}
static void
-set_ld_st_size(struct nv_pc *pc, ubyte type)
+set_ld_st_size(struct nv_pc *pc, int s, ubyte type)
{
switch (type) {
case NV_TYPE_F64:
- pc->emit[1] |= 0x8000;
+ pc->emit[1] |= 0x8000 << s;
break;
case NV_TYPE_F32:
case NV_TYPE_S32:
case NV_TYPE_U32:
- pc->emit[1] |= 0xc000;
+ pc->emit[1] |= 0xc000 << s;
break;
case NV_TYPE_S16:
- pc->emit[1] |= 0x6000;
+ pc->emit[1] |= 0x6000 << s;
break;
case NV_TYPE_U16:
- pc->emit[1] |= 0x4000;
+ pc->emit[1] |= 0x4000 << s;
break;
case NV_TYPE_S8:
- pc->emit[1] |= 0x2000;
+ pc->emit[1] |= 0x2000 << s;
break;
default:
break;
sf = NV_FILE_MEM_C(0);
nv_pc_alloc_immd(pc, i->src[0]);
- new_fixup(pc, NV_FIXUP_PARAM_RELOC, SREG(i->src[0])->id, 0xffff, 9);
+ new_fixup(pc, NV50_FIXUP_DATA_RELOC, 0, SREG(i->src[0])->id, 0xffff, 9);
}
if (sf == NV_FILE_MEM_S ||
if (sf == NV_FILE_MEM_L) {
pc->emit[0] = 0xd0000001;
pc->emit[1] = 0x40000000;
+
+ set_addr(pc, i);
} else {
NOUVEAU_ERR("invalid ld source file\n");
abort();
}
- set_ld_st_size(pc, STYPE(i, 0));
+ set_ld_st_size(pc, (sf == NV_FILE_MEM_L) ? 8 : 0, STYPE(i, 0));
set_dst(pc, i->def[0]);
set_pred_wr(pc, i);
static void
emit_st(struct nv_pc *pc, struct nv_instruction *i)
{
+ assert(SFILE(i, 1) == NV_FILE_GPR);
+ assert(SFILE(i, 0) == NV_FILE_MEM_L);
+ pc->emit[0] = 0xd0000001;
+ pc->emit[1] = 0x60000000;
+
+ SID(pc, i->src[1], 2);
+ SID(pc, i->src[0], 9);
+
+ set_ld_st_size(pc, 8, STYPE(i, 1));
+
+ set_addr(pc, i);
+ set_pred(pc, i);
}
static int
set_a16_bits(pc, SREG(i->src[0])->id);
} else
if (DFILE(i, 0) == NV_FILE_FLAGS) {
- pc->emit[0] = 0x000001fd;
- pc->emit[1] = 0xa0000788 | (1 << 6);
+ pc->emit[0] = 0x00000001;
+ pc->emit[1] = 0xa0000000 | (1 << 6);
+ set_pred(pc, i);
pc->emit[0] |= SREG(i->src[0])->id << 9;
pc->emit[1] |= DREG(i->def[0])->id << 4;
} else
if (i->centroid)
pc->emit[0] |= 1 << 24;
+ assert(i->is_long || !i->flags_src);
+
if (i->is_long) {
- pc->emit[1] |= 0x0780 |
+ set_pred(pc, i);
+
+ pc->emit[1] |=
(pc->emit[0] & (3 << 24)) >> (24 - 16) |
(pc->emit[0] & (1 << 8)) >> (18 - 8);
{
pc->emit[0] = 0xb0000000;
+ assert(!((i->src[0]->mod | i->src[1]->mod) & NV_MOD_ABS));
+
if (SFILE(i, 1) == NV_FILE_IMM) {
emit_form_IMM(pc, i, 0);
if (i->src[0]->mod & NV_MOD_NEG) pc->emit[1] |= 1 << 26;
if (i->src[1]->mod & NV_MOD_NEG) pc->emit[1] |= 1 << 27;
+
+ if (i->saturate)
+ pc->emit[1] |= 0x20000000;
} else {
emit_form_MUL(pc, i);
static void
emit_add_a16(struct nv_pc *pc, struct nv_instruction *i)
{
- pc->emit[0] = 0xd0000001 | (get_immd_u32(i->src[0]) << 9);
+ int s = (i->opcode == NV_OP_MOV) ? 0 : 1;
+
+ pc->emit[0] = 0xd0000001 | ((uint16_t)get_immd_u32(i->src[s]) << 9);
pc->emit[1] = 0x20000000;
pc->emit[0] |= (DREG(i->def[0])->id + 1) << 2;
set_pred(pc, i);
if (i->src[1])
- set_a16_bits(pc, SREG(i->src[1])->id);
+ set_a16_bits(pc, SREG(i->src[1])->id + 1);
}
static void
set_pred(pc, i);
if (i->target && (i->opcode != NV_OP_BREAK)) {
- new_fixup(pc, NV_FIXUP_CFLOW_RELOC, i->target->bin_pos, 0x7ff800, 11);
- pc->emit[0] |= (i->target->bin_pos / 4) << 11;
+ uint32_t pos = i->target->bin_pos;
+
+ new_fixup(pc, NV50_FIXUP_CODE_RELOC, 0, pos, 0xffff << 11, 9);
+ new_fixup(pc, NV50_FIXUP_CODE_RELOC, 1, pos, 0x3f << 14, -4);
+
+ pc->emit[0] |= (pos / 4) << 11;
}
}
{
pc->emit[0] = 0xd0000000;
- if (SFILE(i, 0) == NV_FILE_IMM) {
+ if (SFILE(i, 1) == NV_FILE_IMM) {
emit_form_IMM(pc, i, 0);
if (i->opcode == NV_OP_OR)
}
}
+static void
+emit_arl(struct nv_pc *pc, struct nv_instruction *i)
+{
+ assert(SFILE(i, 0) == NV_FILE_GPR);
+ assert(SFILE(i, 1) == NV_FILE_IMM);
+
+ assert(!i->flags_def);
+
+ pc->emit[0] = 0x00000001;
+ pc->emit[1] = 0xc0000000;
+
+ pc->emit[0] |= (i->def[0]->reg.id + 1) << 2;
+ set_pred(pc, i);
+ set_src_0(pc, i->src[0]);
+ pc->emit[0] |= (get_immd_u32(i->src[1]) & 0x3f) << 16;
+}
+
static void
emit_shift(struct nv_pc *pc, struct nv_instruction *i)
{
+ if (DFILE(i, 0) == NV_FILE_ADDR) {
+ emit_arl(pc, i);
+ return;
+ }
+
pc->emit[0] = 0x30000001;
pc->emit[1] = 0xc4000000;
pc->emit[0] = 0x90000000;
- assert(SREG(src0)->type == NV_TYPE_F32);
- assert(SREG(src0)->file == NV_FILE_GPR);
+ assert(STYPE(i, 0) == NV_TYPE_F32);
+ assert(SFILE(i, 0) == NV_FILE_GPR);
if (!i->is_long) {
emit_form_MUL(pc, i);
pc->emit[0] |= i->tex_t << 9;
pc->emit[0] |= i->tex_s << 17;
- pc->emit[0] |= i->tex_argc << 22;
+ pc->emit[0] |= (i->tex_argc - 1) << 22;
pc->emit[0] |= (i->tex_mask & 0x3) << 25;
pc->emit[1] |= (i->tex_mask & 0xc) << 12;
else
if (i->opcode == NV_OP_TXL)
pc->emit[1] |= 0x40000000;
- else
- pc->emit[0] -= 1 << 22;
}
static void
set_pred_wr(pc, i);
}
+static void
+emit_quadop(struct nv_pc *pc, struct nv_instruction *i)
+{
+ pc->emit[0] = 0xc0000000;
+ pc->emit[1] = 0x80000000;
+
+ emit_form_ADD(pc, i);
+
+ pc->emit[0] |= i->lanes << 16;
+
+ pc->emit[0] |= (i->quadop & 0x03) << 20;
+ pc->emit[1] |= (i->quadop & 0xfc) << 20;
+}
+
void
nv50_emit_instruction(struct nv_pc *pc, struct nv_instruction *i)
{
- // nv_print_instruction(i);
+ /* nv_print_instruction(i); */
switch (i->opcode) {
case NV_OP_MOV:
case NV_OP_TXL:
emit_tex(pc, i);
break;
+ case NV_OP_QUADOP:
+ emit_quadop(pc, i);
+ break;
case NV_OP_KIL:
emit_flow(pc, i, 0x0);
break;
case NV_OP_UNDEF:
case NV_OP_SUB:
NOUVEAU_ERR("operation \"%s\" should have been eliminated\n",
- nv_opcode_name(i->opcode));
+ nv_opcode_name(i->opcode));
break;
default:
NOUVEAU_ERR("unhandled NV_OP: %d\n", i->opcode);