/* * Copyright 2010 Christoph Bumiller * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "nv50_context.h" #include "nv50_pc.h" #define FLAGS_CC_SHIFT 7 #define FLAGS_ID_SHIFT 12 #define FLAGS_WR_ID_SHIFT 4 #define FLAGS_CC_MASK (0x1f << FLAGS_CC_SHIFT) #define FLAGS_ID_MASK (0x03 << FLAGS_ID_SHIFT) #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 */ 8, 8, 8, 4, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, /* 31 */ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, /* 47 */ 4, 8, 8, 8, 8, 8, 0, 0, 8 }; unsigned nv50_inst_min_size(struct nv_instruction *i) { int n; if (nv50_inst_min_size_tab[i->opcode] > 4) return 8; if (i->def[0] && i->def[0]->reg.file != NV_FILE_GPR) return 8; if (i->def[0]->join->reg.id > 63) return 8; for (n = 0; n < 3; ++n) { if (!i->src[n]) break; if (i->src[n]->value->reg.file != NV_FILE_GPR && i->src[n]->value->reg.file != NV_FILE_MEM_V) return 8; if (i->src[n]->value->reg.id > 63) return 8; } if (i->flags_def || i->flags_src || i->src[4]) return 8; if (i->is_join) return 8; if (i->src[2]) { if (i->saturate || i->src[2]->mod) return 8; if (i->src[0]->mod ^ i->src[1]->mod) return 8; if ((i->src[0]->mod | i->src[1]->mod) & NV_MOD_ABS) return 8; if (i->def[0]->join->reg.id < 0 || i->def[0]->join->reg.id != i->src[2]->value->join->reg.id) return 8; } return nv50_inst_min_size_tab[i->opcode]; } static INLINE ubyte STYPE(struct nv_instruction *nvi, int s) { return nvi->src[s]->typecast; } static INLINE ubyte DTYPE(struct nv_instruction *nvi, int d) { return nvi->def[d]->reg.type; } static INLINE struct nv_reg * SREG(struct nv_ref *ref) { return &ref->value->join->reg; } static INLINE struct nv_reg * DREG(struct nv_value *val) { return &val->join->reg; } static INLINE ubyte SFILE(struct nv_instruction *nvi, int s) { return nvi->src[s]->value->reg.file; } static INLINE ubyte DFILE(struct nv_instruction *nvi, int d) { return nvi->def[0]->reg.file; } static INLINE void SID(struct nv_pc *pc, struct nv_ref *ref, int pos) { pc->emit[pos / 32] |= SREG(ref)->id << (pos % 32); } static INLINE void DID(struct nv_pc *pc, struct nv_value *val, int pos) { pc->emit[pos / 32] |= DREG(val)->id << (pos % 32); } static INLINE uint32_t get_immd_u32(struct nv_ref *ref) { assert(ref->value->reg.file == NV_FILE_IMM); return ref->value->reg.imm.u32; } static INLINE void set_immd_u32(struct nv_pc *pc, uint32_t u32) { pc->emit[1] |= 3; pc->emit[0] |= (u32 & 0x3f) << 16; pc->emit[1] |= (u32 >> 6) << 2; } static INLINE void set_immd(struct nv_pc *pc, struct nv_ref *ref) { assert(ref->value->reg.file == NV_FILE_IMM); set_immd_u32(pc, get_immd_u32(ref)); } /* 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) { uint32_t i, val = get_immd_u32(ref); for (i = 0; i < pc->immd_count; ++i) if (pc->immd_buf[i] == val) break; if (i == pc->immd_count) { if (!(pc->immd_count % 8)) pc->immd_buf = REALLOC(pc->immd_buf, pc->immd_count * 4, (pc->immd_count + 8) * 4); pc->immd_buf[pc->immd_count++] = val; } SREG(ref)->id = i; } static INLINE void set_pred(struct nv_pc *pc, struct nv_instruction *i) { assert(!(pc->emit[1] & 0x00003f80)); pc->emit[1] |= i->cc << 7; if (i->flags_src) pc->emit[1] |= SREG(i->flags_src)->id << 12; } static INLINE void set_pred_wr(struct nv_pc *pc, struct nv_instruction *i) { assert(!(pc->emit[1] & 0x00000070)); if (i->flags_def) pc->emit[1] |= (DREG(i->flags_def)->id << 4) | 0x40; } static INLINE void set_a16_bits(struct nv_pc *pc, uint id) { ++id; /* $a0 is always 0 */ pc->emit[0] |= (id & 3) << 26; pc->emit[1] |= id & 4; } static INLINE void set_addr(struct nv_pc *pc, struct nv_instruction *i) { if (i->src[4]) set_a16_bits(pc, SREG(i->src[4])->id); } static void set_dst(struct nv_pc *pc, struct nv_value *value) { struct nv_reg *reg = &value->join->reg; if (reg->id < 0) { pc->emit[0] |= (127 << 2) | 1; /* set 'long'-bit to catch bugs */ pc->emit[1] |= 0x8; return; } if (reg->file == NV_FILE_OUT) pc->emit[1] |= 0x8; else if (reg->file == NV_FILE_ADDR) assert(0); pc->emit[0] |= reg->id << 2; } static void set_src_0(struct nv_pc *pc, struct nv_ref *ref) { struct nv_reg *reg = SREG(ref); if (reg->file == NV_FILE_MEM_S) pc->emit[1] |= 0x00200000; else if (reg->file == NV_FILE_MEM_P) pc->emit[0] |= 0x01800000; else if (reg->file != NV_FILE_GPR) NOUVEAU_ERR("invalid src0 register file: %d\n", reg->file); assert(reg->id < 128); pc->emit[0] |= reg->id << 9; } static void set_src_1(struct nv_pc *pc, struct nv_ref *ref) { struct nv_reg *reg = SREG(ref); if (reg->file >= NV_FILE_MEM_C(0) && reg->file <= NV_FILE_MEM_C(15)) { assert(!(pc->emit[1] & 0x01800000)); pc->emit[0] |= 0x00800000; pc->emit[1] |= (reg->file - NV_FILE_MEM_C(0)) << 22; } else if (reg->file != NV_FILE_GPR) NOUVEAU_ERR("invalid src1 register file: %d\n", reg->file); assert(reg->id < 128); pc->emit[0] |= reg->id << 16; } static void set_src_2(struct nv_pc *pc, struct nv_ref *ref) { struct nv_reg *reg = SREG(ref); if (reg->file >= NV_FILE_MEM_C(0) && reg->file <= NV_FILE_MEM_C(15)) { assert(!(pc->emit[1] & 0x01800000)); pc->emit[0] |= 0x01000000; pc->emit[1] |= (reg->file - NV_FILE_MEM_C(0)) << 22; } else if (reg->file != NV_FILE_GPR) NOUVEAU_ERR("invalid src2 register file: %d\n", reg->file); assert(reg->id < 128); pc->emit[1] |= reg->id << 14; } /* the default form: * - long instruction * - 1 to 3 sources in slots 0, 1, 2 * - address & flags */ static void emit_form_MAD(struct nv_pc *pc, struct nv_instruction *i) { pc->emit[0] |= 1; set_pred(pc, i); set_pred_wr(pc, i); if (i->def[0]) set_dst(pc, i->def[0]); else { pc->emit[0] |= 0x01fc; pc->emit[1] |= 0x0008; } if (i->src[0]) set_src_0(pc, i->src[0]); if (i->src[1]) set_src_1(pc, i->src[1]); if (i->src[2]) set_src_2(pc, i->src[2]); set_addr(pc, i); } /* like default form, but 2nd source in slot 2, no 3rd source */ static void emit_form_ADD(struct nv_pc *pc, struct nv_instruction *i) { pc->emit[0] |= 1; if (i->def[0]) set_dst(pc, i->def[0]); else { pc->emit[0] |= 0x01fc; pc->emit[1] |= 0x0008; } set_pred(pc, i); set_pred_wr(pc, i); if (i->src[0]) set_src_0(pc, i->src[0]); if (i->src[1]) set_src_2(pc, i->src[1]); set_addr(pc, i); } /* short mul */ static void emit_form_MUL(struct nv_pc *pc, struct nv_instruction *i) { assert(!i->is_long && !(pc->emit[0] & 1)); assert(i->def[0]); set_dst(pc, i->def[0]); if (i->src[0]) set_src_0(pc, i->src[0]); if (i->src[1]) set_src_1(pc, i->src[1]); } /* default immediate form * - 1 to 3 sources where last is immediate * - no address or predicate possible */ static void emit_form_IMM(struct nv_pc *pc, struct nv_instruction *i, ubyte mod_mask) { pc->emit[0] |= 1; assert(i->def[0]); assert(i->src[0]); set_dst(pc, i->def[0]); assert(!i->src[4] && !i->flags_src && !i->flags_def); if (i->src[2]) { set_immd(pc, i->src[2]); set_src_0(pc, i->src[1]); set_src_1(pc, i->src[0]); } else if (i->src[1]) { set_immd(pc, i->src[1]); set_src_0(pc, i->src[0]); } else set_immd(pc, i->src[0]); assert(!mod_mask); } static void set_ld_st_size(struct nv_pc *pc, int s, ubyte type) { switch (type) { case NV_TYPE_F64: pc->emit[1] |= 0x8000 << s; break; case NV_TYPE_F32: case NV_TYPE_S32: case NV_TYPE_U32: pc->emit[1] |= 0xc000 << s; break; case NV_TYPE_S16: pc->emit[1] |= 0x6000 << s; break; case NV_TYPE_U16: pc->emit[1] |= 0x4000 << s; break; case NV_TYPE_S8: pc->emit[1] |= 0x2000 << s; break; default: break; } } static void emit_ld(struct nv_pc *pc, struct nv_instruction *i) { ubyte sf = SFILE(i, 0); if (sf == NV_FILE_IMM) { sf = NV_FILE_MEM_C(0); nv_pc_alloc_immd(pc, i->src[0]); new_fixup(pc, NV50_FIXUP_DATA_RELOC, 0, SREG(i->src[0])->id, 0xffff, 9); } if (sf == NV_FILE_MEM_S || sf == NV_FILE_MEM_P) { pc->emit[0] = 0x10000001; pc->emit[1] = 0x04200000 | (0x3c << 12); if (sf == NV_FILE_MEM_P) pc->emit[0] |= 0x01800000; } else if (sf >= NV_FILE_MEM_C(0) && sf <= NV_FILE_MEM_C(15)) { pc->emit[0] = 0x10000001; pc->emit[1] = 0x24000000; pc->emit[1] |= (sf - NV_FILE_MEM_C(0)) << 22; } else if (sf >= NV_FILE_MEM_G(0) && sf <= NV_FILE_MEM_G(15)) { pc->emit[0] = 0xd0000001 | ((sf - NV_FILE_MEM_G(0)) << 16); pc->emit[1] = 0xa0000000; assert(i->src[4] && SREG(i->src[4])->file == NV_FILE_GPR); SID(pc, i->src[4], 9); } else 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, (sf == NV_FILE_MEM_L) ? 8 : 0, STYPE(i, 0)); set_dst(pc, i->def[0]); set_pred_wr(pc, i); set_pred(pc, i); if (sf < NV_FILE_MEM_G(0) || sf > NV_FILE_MEM_G(15)) { SID(pc, i->src[0], 9); set_addr(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 verify_mov(struct nv_instruction *i) { ubyte sf = SFILE(i, 0); ubyte df = DFILE(i, 0); if (df == NV_FILE_GPR) return 0; if (df != NV_FILE_OUT && df != NV_FILE_FLAGS && df != NV_FILE_ADDR) return 1; if (sf == NV_FILE_FLAGS) return 2; if (sf == NV_FILE_ADDR) return 3; if (sf == NV_FILE_IMM && df != NV_FILE_OUT) return 4; return 0; } static void emit_mov(struct nv_pc *pc, struct nv_instruction *i) { assert(!verify_mov(i)); if (SFILE(i, 0) >= NV_FILE_MEM_S) emit_ld(pc, i); else if (SFILE(i, 0) == NV_FILE_FLAGS) { pc->emit[0] = 0x00000001 | (DREG(i->def[0])->id << 2); pc->emit[1] = 0x20000780 | (SREG(i->src[0])->id << 12); } else if (SFILE(i, 0) == NV_FILE_ADDR) { pc->emit[0] = 0x00000001 | (DREG(i->def[0])->id << 2); pc->emit[1] = 0x40000780; set_a16_bits(pc, SREG(i->src[0])->id); } else if (DFILE(i, 0) == NV_FILE_FLAGS) { 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 (SFILE(i, 0) == NV_FILE_IMM) { if (i->opcode == NV_OP_LDA) { emit_ld(pc, i); } else { pc->emit[0] = 0x10008001; pc->emit[1] = 0x00000003; emit_form_IMM(pc, i, 0); } } else { pc->emit[0] = 0x10000000; pc->emit[0] |= DREG(i->def[0])->id << 2; pc->emit[0] |= SREG(i->src[0])->id << 9; if (!i->is_long) { pc->emit[0] |= 0x8000; } else { pc->emit[0] |= 0x00000001; pc->emit[1] = 0x0403c000; set_pred(pc, i); } } if (DFILE(i, 0) == NV_FILE_OUT) pc->emit[1] |= 0x8; } static void emit_interp(struct nv_pc *pc, struct nv_instruction *i) { pc->emit[0] = 0x80000000; assert(DFILE(i, 0) == NV_FILE_GPR); assert(SFILE(i, 0) == NV_FILE_MEM_V); DID(pc, i->def[0], 2); SID(pc, i->src[0], 16); if (i->flat) pc->emit[0] |= 1 << 8; else if (i->opcode == NV_OP_PINTERP) { pc->emit[0] |= 1 << 25; pc->emit[0] |= SREG(i->src[1])->id << 9; } if (i->centroid) pc->emit[0] |= 1 << 24; assert(i->is_long || !i->flags_src); if (i->is_long) { set_pred(pc, i); pc->emit[1] |= (pc->emit[0] & (3 << 24)) >> (24 - 16) | (pc->emit[0] & (1 << 8)) >> (18 - 8); pc->emit[0] |= 1; pc->emit[0] &= ~0x03000100; } } static void emit_minmax(struct nv_pc *pc, struct nv_instruction *i) { pc->emit[0] = 0x30000000; pc->emit[1] = (i->opcode == NV_OP_MIN) ? (2 << 28) : 0; switch (DTYPE(i, 0)) { case NV_TYPE_F32: pc->emit[0] |= 0x80000000; pc->emit[1] |= 0x80000000; break; case NV_TYPE_S32: pc->emit[1] |= 0x8c000000; break; case NV_TYPE_U32: pc->emit[1] |= 0x84000000; break; } emit_form_MAD(pc, i); if (i->src[0]->mod & NV_MOD_ABS) pc->emit[1] |= 0x00100000; if (i->src[1]->mod & NV_MOD_ABS) pc->emit[1] |= 0x00080000; } static void emit_add_f32(struct nv_pc *pc, struct nv_instruction *i) { 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[0] |= 0x8000; if (i->src[1]->mod & NV_MOD_NEG) pc->emit[0] |= 1 << 22; } else if (i->is_long) { emit_form_ADD(pc, i); 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); if (i->src[0]->mod & NV_MOD_NEG) pc->emit[0] |= 0x8000; if (i->src[1]->mod & NV_MOD_NEG) pc->emit[0] |= 1 << 22; } } static void emit_add_b32(struct nv_pc *pc, struct nv_instruction *i) { pc->emit[0] = 0x20008000; if (SFILE(i, 1) == NV_FILE_IMM) { emit_form_IMM(pc, i, 0); } else if (i->is_long) { pc->emit[0] = 0x20000000; pc->emit[1] = 0x04000000; emit_form_ADD(pc, i); } else { emit_form_MUL(pc, i); } if (i->src[0]->mod & NV_MOD_NEG) pc->emit[0] |= 1 << 28; if (i->src[1]->mod & NV_MOD_NEG) pc->emit[0] |= 1 << 22; } static void emit_add_a16(struct nv_pc *pc, struct nv_instruction *i) { 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 (s && i->src[0]) set_a16_bits(pc, SREG(i->src[0])->id); } static void emit_flow(struct nv_pc *pc, struct nv_instruction *i, ubyte flow_op) { pc->emit[0] = 0x00000003 | (flow_op << 28); pc->emit[1] = 0x00000000; set_pred(pc, i); if (i->target && (i->opcode != NV_OP_BREAK)) { 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 >> 2) & 0xffff) << 11; pc->emit[1] |= ((pos >> 18) & 0x003f) << 14; } } static INLINE void emit_add(struct nv_pc *pc, struct nv_instruction *i) { if (DFILE(i, 0) == NV_FILE_ADDR) emit_add_a16(pc, i); else { switch (DTYPE(i, 0)) { case NV_TYPE_F32: emit_add_f32(pc, i); break; case NV_TYPE_U32: case NV_TYPE_S32: emit_add_b32(pc, i); break; } } } static void emit_bitop2(struct nv_pc *pc, struct nv_instruction *i) { pc->emit[0] = 0xd0000000; if (SFILE(i, 1) == NV_FILE_IMM) { emit_form_IMM(pc, i, 0); if (i->opcode == NV_OP_OR) pc->emit[0] |= 0x0100; else if (i->opcode == NV_OP_XOR) pc->emit[0] |= 0x8000; } else { emit_form_MAD(pc, i); pc->emit[1] |= 0x04000000; if (i->opcode == NV_OP_OR) pc->emit[1] |= 0x4000; else if (i->opcode == NV_OP_XOR) pc->emit[1] |= 0x8000; } } 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; if (i->opcode == NV_OP_SHR) pc->emit[1] |= 1 << 29; if (SFILE(i, 1) == NV_FILE_IMM) { pc->emit[1] |= 1 << 20; pc->emit[0] |= (get_immd_u32(i->src[1]) & 0x7f) << 16; set_pred(pc, i); } else emit_form_MAD(pc, i); if (STYPE(i, 0) == NV_TYPE_S32) pc->emit[1] |= 1 << 27; } static void emit_flop(struct nv_pc *pc, struct nv_instruction *i) { struct nv_ref *src0 = i->src[0]; pc->emit[0] = 0x90000000; assert(STYPE(i, 0) == NV_TYPE_F32); assert(SFILE(i, 0) == NV_FILE_GPR); if (!i->is_long) { emit_form_MUL(pc, i); assert(i->opcode == NV_OP_RCP && !src0->mod); return; } pc->emit[1] = (i->opcode - NV_OP_RCP) << 29; emit_form_MAD(pc, i); if (src0->mod & NV_MOD_NEG) pc->emit[1] |= 0x04000000; if (src0->mod & NV_MOD_ABS) pc->emit[1] |= 0x00100000; } static void emit_mad_f32(struct nv_pc *pc, struct nv_instruction *i) { const boolean neg_mul = (i->src[0]->mod ^ i->src[1]->mod) & NV_MOD_NEG; const boolean neg_add = (i->src[2]->mod & NV_MOD_NEG); pc->emit[0] = 0xe0000000; if (!i->is_long) { emit_form_MUL(pc, i); assert(!neg_mul && !neg_add); return; } emit_form_MAD(pc, i); if (neg_mul) pc->emit[1] |= 0x04000000; if (neg_add) pc->emit[1] |= 0x08000000; if (i->saturate) pc->emit[1] |= 0x20000000; } static INLINE void emit_mad(struct nv_pc *pc, struct nv_instruction *i) { emit_mad_f32(pc, i); } static void emit_mul_f32(struct nv_pc *pc, struct nv_instruction *i) { boolean neg = (i->src[0]->mod ^ i->src[1]->mod) & NV_MOD_NEG; pc->emit[0] = 0xc0000000; if (SFILE(i, 1) == NV_FILE_IMM) { emit_form_IMM(pc, i, 0); if (neg) pc->emit[0] |= 0x8000; } else if (i->is_long) { emit_form_MAD(pc, i); if (neg) pc->emit[1] |= 0x08 << 24; } else { emit_form_MUL(pc, i); if (neg) pc->emit[0] |= 0x8000; } } static void emit_set(struct nv_pc *pc, struct nv_instruction *nvi) { assert(nvi->is_long); pc->emit[0] = 0x30000000; pc->emit[1] = 0x60000000; pc->emit[1] |= nvi->set_cond << 14; switch (STYPE(nvi, 0)) { case NV_TYPE_U32: pc->emit[1] |= 0x04000000; break; case NV_TYPE_S32: pc->emit[1] |= 0x0c000000; break; case NV_TYPE_F32: pc->emit[0] |= 0x80000000; break; default: assert(0); break; } emit_form_MAD(pc, nvi); } #define CVT_RN (0x00 << 16) #define CVT_FLOOR (0x02 << 16) #define CVT_CEIL (0x04 << 16) #define CVT_TRUNC (0x06 << 16) #define CVT_SAT (0x08 << 16) #define CVT_ABS (0x10 << 16) #define CVT_X32_X32 0x04004000 #define CVT_X32_S32 0x04014000 #define CVT_F32_F32 ((0xc0 << 24) | CVT_X32_X32) #define CVT_S32_F32 ((0x88 << 24) | CVT_X32_X32) #define CVT_U32_F32 ((0x80 << 24) | CVT_X32_X32) #define CVT_F32_S32 ((0x40 << 24) | CVT_X32_S32) #define CVT_F32_U32 ((0x40 << 24) | CVT_X32_X32) #define CVT_S32_S32 ((0x08 << 24) | CVT_X32_S32) #define CVT_S32_U32 ((0x08 << 24) | CVT_X32_X32) #define CVT_U32_S32 ((0x00 << 24) | CVT_X32_S32) #define CVT_U32_U32 ((0x00 << 24) | CVT_X32_X32) #define CVT_NEG 0x20000000 #define CVT_RI 0x08000000 static void emit_cvt(struct nv_pc *pc, struct nv_instruction *nvi) { ubyte dst_type = nvi->def[0] ? DTYPE(nvi, 0) : STYPE(nvi, 0); pc->emit[0] = 0xa0000000; switch (dst_type) { case NV_TYPE_F32: switch (STYPE(nvi, 0)) { case NV_TYPE_F32: pc->emit[1] = CVT_F32_F32; break; case NV_TYPE_S32: pc->emit[1] = CVT_F32_S32; break; case NV_TYPE_U32: pc->emit[1] = CVT_F32_U32; break; } break; case NV_TYPE_S32: switch (STYPE(nvi, 0)) { case NV_TYPE_F32: pc->emit[1] = CVT_S32_F32; break; case NV_TYPE_S32: pc->emit[1] = CVT_S32_S32; break; case NV_TYPE_U32: pc->emit[1] = CVT_S32_U32; break; } break; case NV_TYPE_U32: switch (STYPE(nvi, 0)) { case NV_TYPE_F32: pc->emit[1] = CVT_U32_F32; break; case NV_TYPE_S32: pc->emit[1] = CVT_U32_S32; break; case NV_TYPE_U32: pc->emit[1] = CVT_U32_U32; break; } break; } if (pc->emit[1] == CVT_F32_F32 && (nvi->opcode == NV_OP_CEIL || nvi->opcode == NV_OP_FLOOR || nvi->opcode == NV_OP_TRUNC)) pc->emit[1] |= CVT_RI; switch (nvi->opcode) { case NV_OP_CEIL: pc->emit[1] |= CVT_CEIL; break; case NV_OP_FLOOR: pc->emit[1] |= CVT_FLOOR; break; case NV_OP_TRUNC: pc->emit[1] |= CVT_TRUNC; break; case NV_OP_ABS: pc->emit[1] |= CVT_ABS; break; case NV_OP_SAT: pc->emit[1] |= CVT_SAT; break; case NV_OP_NEG: pc->emit[1] |= CVT_NEG; break; default: assert(nvi->opcode == NV_OP_CVT); break; } assert(nvi->opcode != NV_OP_ABS || !(nvi->src[0]->mod & NV_MOD_NEG)); if (nvi->src[0]->mod & NV_MOD_NEG) pc->emit[1] ^= CVT_NEG; if (nvi->src[0]->mod & NV_MOD_ABS) pc->emit[1] |= CVT_ABS; emit_form_MAD(pc, nvi); } static void emit_tex(struct nv_pc *pc, struct nv_instruction *i) { pc->emit[0] = 0xf0000001; pc->emit[1] = 0x00000000; DID(pc, i->def[0], 2); set_pred(pc, i); pc->emit[0] |= i->tex_t << 9; pc->emit[0] |= i->tex_s << 17; pc->emit[0] |= (i->tex_argc - 1) << 22; pc->emit[0] |= (i->tex_mask & 0x3) << 25; pc->emit[1] |= (i->tex_mask & 0xc) << 12; if (i->tex_live) pc->emit[1] |= 4; if (i->tex_cube) pc->emit[0] |= 0x08000000; if (i->opcode == NV_OP_TXB) pc->emit[1] |= 0x20000000; else if (i->opcode == NV_OP_TXL) pc->emit[1] |= 0x40000000; } static void emit_cvt2fixed(struct nv_pc *pc, struct nv_instruction *i) { ubyte mod = i->src[0]->mod; pc->emit[0] = 0xb0000000; pc->emit[1] = 0xc0000000; if (i->opcode == NV_OP_PREEX2) pc->emit[1] |= 0x4000; emit_form_MAD(pc, i); if (mod & NV_MOD_NEG) pc->emit[1] |= 0x04000000; if (mod & NV_MOD_ABS) pc->emit[1] |= 0x00100000; } static void emit_ddx(struct nv_pc *pc, struct nv_instruction *i) { assert(i->is_long && SFILE(i, 0) == NV_FILE_GPR); pc->emit[0] = (i->src[0]->mod & NV_MOD_NEG) ? 0xc0240001 : 0xc0140001; pc->emit[1] = (i->src[0]->mod & NV_MOD_NEG) ? 0x86400000 : 0x89800000; DID(pc, i->def[0], 2); SID(pc, i->src[0], 9); SID(pc, i->src[0], 32 + 14); set_pred(pc, i); set_pred_wr(pc, i); } static void emit_ddy(struct nv_pc *pc, struct nv_instruction *i) { assert(i->is_long && SFILE(i, 0) == NV_FILE_GPR); pc->emit[0] = (i->src[0]->mod & NV_MOD_NEG) ? 0xc0250001 : 0xc0150001; pc->emit[1] = (i->src[0]->mod & NV_MOD_NEG) ? 0x85800000 : 0x8a400000; DID(pc, i->def[0], 2); SID(pc, i->src[0], 9); SID(pc, i->src[0], 32 + 14); set_pred(pc, i); 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); */ switch (i->opcode) { case NV_OP_MOV: if (DFILE(i, 0) == NV_FILE_ADDR) emit_add_a16(pc, i); else emit_mov(pc, i); break; case NV_OP_LDA: emit_mov(pc, i); break; case NV_OP_STA: emit_st(pc, i); break; case NV_OP_LINTERP: case NV_OP_PINTERP: emit_interp(pc, i); break; case NV_OP_ADD: emit_add(pc, i); break; case NV_OP_AND: case NV_OP_OR: case NV_OP_XOR: emit_bitop2(pc, i); break; case NV_OP_CVT: case NV_OP_ABS: case NV_OP_NEG: case NV_OP_SAT: case NV_OP_CEIL: case NV_OP_FLOOR: case NV_OP_TRUNC: emit_cvt(pc, i); break; case NV_OP_DFDX: emit_ddx(pc, i); break; case NV_OP_DFDY: emit_ddy(pc, i); break; case NV_OP_RCP: case NV_OP_RSQ: case NV_OP_LG2: case NV_OP_SIN: case NV_OP_COS: case NV_OP_EX2: emit_flop(pc, i); break; case NV_OP_PRESIN: case NV_OP_PREEX2: emit_cvt2fixed(pc, i); break; case NV_OP_MAD: emit_mad(pc, i); break; case NV_OP_MAX: case NV_OP_MIN: emit_minmax(pc, i); break; case NV_OP_MUL: emit_mul_f32(pc, i); break; case NV_OP_SET: emit_set(pc, i); break; case NV_OP_SHL: case NV_OP_SHR: emit_shift(pc, i); break; case NV_OP_TEX: case NV_OP_TXB: 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_BRA: emit_flow(pc, i, 0x1); break; case NV_OP_CALL: emit_flow(pc, i, 0x2); break; case NV_OP_RET: emit_flow(pc, i, 0x3); break; case NV_OP_BREAKADDR: emit_flow(pc, i, 0x4); break; case NV_OP_BREAK: emit_flow(pc, i, 0x5); break; case NV_OP_JOINAT: emit_flow(pc, i, 0xa); break; case NV_OP_NOP: case NV_OP_JOIN: pc->emit[0] = 0xf0000001; pc->emit[1] = 0xe0000000; break; case NV_OP_PHI: case NV_OP_UNDEF: case NV_OP_SUB: NOUVEAU_ERR("operation \"%s\" should have been eliminated\n", nv_opcode_name(i->opcode)); break; default: NOUVEAU_ERR("unhandled NV_OP: %d\n", i->opcode); abort(); break; } if (i->is_join) { assert(i->is_long && !(pc->emit[1] & 1)); pc->emit[1] |= 2; } assert((pc->emit[0] & 1) == i->is_long); }