Files
intel-hard-graphics/src/gallium/drivers/r300/compiler/nir_to_rc.c
T
2026-06-06 13:25:15 +03:00

1837 lines
57 KiB
C

/*
* Copyright © 2014-2015 Broadcom
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include "nir_to_rc.h"
#include "compiler/nir/nir.h"
#include "compiler/nir/nir_builder.h"
#include "compiler/nir/nir_deref.h"
#include "compiler/nir/nir_legacy.h"
#include "compiler/radeon_code.h"
#include "compiler/radeon_compiler.h"
#include "compiler/radeon_program.h"
#include "compiler/radeon_program_constants.h"
#include "pipe/p_screen.h"
#include "pipe/p_state.h"
#include "util/compiler.h"
#include "util/u_debug.h"
#include "util/u_dynarray.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "r300_nir.h"
#include "r300_screen.h"
struct ntr_immediate {
float values[4];
};
struct ntr_compile {
nir_shader *s;
struct r300_shader_semantics *semantics;
/* Options */
bool lower_fabs;
/* r300's RC backend only tracks a single ADDR[0].x; logical address
* indexing collapses to that one register. */
struct rc_dst_register addr_reg;
/* if condition set up at the end of a block, for ntr_emit_if(). */
struct rc_src_register if_cond;
/* Temps for our NIR SSA and register values. */
struct rc_dst_register *reg_temp;
struct rc_src_register *ssa_temp;
unsigned num_temps;
/* Map from NIR driver_location to RC input register. */
struct rc_src_register *input_index_map;
/* RC-side state for direct emission. */
struct radeon_compiler *compiler;
/* one struct ntr_immediate per NIR load_const */
struct util_dynarray immediates;
/* UBO size in vec4s (0 if no UBO) */
unsigned ubo_size;
/* offset for IMMEDIATE indices in the compiler's constants table */
unsigned immediate_offset;
/* FS output tracking. */
int fs_output_color_index[4];
int fs_output_depth_index;
int fs_output_index[FRAG_RESULT_MAX];
unsigned num_outputs;
};
struct ntr_alu_dst {
struct rc_dst_register reg;
rc_saturate_mode saturate;
};
static unsigned ntr_rc_register_index(struct ntr_compile *c, int index);
static struct rc_dst_register
ntr_temp(struct ntr_compile *c)
{
return (struct rc_dst_register) {
.File = RC_FILE_TEMPORARY,
.Index = ntr_rc_register_index(c, c->num_temps++),
.WriteMask = RC_MASK_XYZW,
};
}
static void ntr_emit_cf_list(struct ntr_compile *c, struct exec_list *list);
static unsigned
ntr_rc_register_index(struct ntr_compile *c, int index)
{
/* Negative offsets to relative addressing should have been lowered in NIR. */
assert(index >= 0);
if (index >= RC_REGISTER_MAX_INDEX)
rc_error(c->compiler, "r300: Register index too high.\n");
return index;
}
static struct rc_dst_register
ntr_dst_register(struct ntr_compile *c, rc_register_file file, int index)
{
return (struct rc_dst_register) {
.File = file,
.Index = ntr_rc_register_index(c, index),
.WriteMask = RC_MASK_XYZW,
};
}
static struct rc_dst_register
ntr_writemask(struct rc_dst_register dst, unsigned writemask)
{
dst.WriteMask &= writemask;
return dst;
}
static struct ntr_alu_dst
ntr_alu_dst(struct rc_dst_register reg, rc_saturate_mode saturate)
{
return (struct ntr_alu_dst) {
.reg = reg,
.saturate = saturate,
};
}
static struct ntr_alu_dst
ntr_alu_dst_saturate(struct ntr_alu_dst dst)
{
dst.saturate = RC_SATURATE_ZERO_ONE;
return dst;
}
static void
ntr_set_dst_index(struct ntr_compile *c, struct rc_dst_register *dst, int index)
{
dst->Index = ntr_rc_register_index(c, index);
}
static struct rc_src_register
ntr_src_register(struct ntr_compile *c, rc_register_file file, int index)
{
return (struct rc_src_register) {
.File = file,
.Index = ntr_rc_register_index(c, index),
.Swizzle = RC_SWIZZLE_XYZW,
.Negate = RC_MASK_NONE,
};
}
static void
ntr_set_src_index(struct ntr_compile *c, struct rc_src_register *src, int index)
{
src->Index = ntr_rc_register_index(c, index);
}
static struct rc_src_register
ntr_src_from_dst(struct rc_dst_register dst)
{
return (struct rc_src_register) {
.File = dst.File,
.Index = dst.Index,
.Swizzle = RC_SWIZZLE_XYZW,
.Negate = RC_MASK_NONE,
};
}
static struct rc_src_register
ntr_swizzle(struct rc_src_register src, int x, int y, int z, int w)
{
int swizzle[4] = {x, y, z, w};
unsigned old_swizzle = src.Swizzle;
unsigned old_negate = src.Negate;
src.Swizzle = 0;
src.Negate = RC_MASK_NONE;
for (unsigned i = 0; i < 4; i++) {
assert(swizzle[i] < 4);
SET_SWZ(src.Swizzle, i, GET_SWZ(old_swizzle, swizzle[i]));
if (old_negate & (1 << swizzle[i]))
src.Negate |= 1 << i;
}
return src;
}
static struct rc_src_register
ntr_scalar(struct rc_src_register src, int x)
{
return ntr_swizzle(src, x, x, x, x);
}
static struct rc_src_register
ntr_abs(struct rc_src_register src)
{
src.Abs = true;
src.Negate = RC_MASK_NONE;
return src;
}
static struct rc_src_register
ntr_negate(struct rc_src_register src)
{
src.Negate ^= RC_MASK_XYZW;
return src;
}
static struct rc_src_register
ntr_src_indirect(struct rc_src_register src)
{
src.RelAddr = true;
return src;
}
static void
ntr_rc_tex_sampler(struct ntr_compile *c, struct rc_sub_instruction *inst,
unsigned sampler_index, bool reladdr)
{
if (reladdr)
rc_error(c->compiler,
"r300: Relative addressing of sampler operands is unsupported.\n");
inst->TexSrcUnit = ntr_rc_register_index(c, sampler_index);
}
static struct rc_sub_instruction *
rc_sub_instruction(struct ntr_compile *c, rc_opcode opcode,
const struct rc_dst_register *dst,
rc_saturate_mode saturate,
const struct rc_src_register *src0,
const struct rc_src_register *src1,
const struct rc_src_register *src2)
{
const struct rc_opcode_info *opcode_info = rc_get_opcode_info(opcode);
assert(opcode_info->NumSrcRegs <= 3);
assert(opcode_info->HasDstReg == (dst != NULL));
assert(opcode_info->NumSrcRegs >= (src2 ? 3 : src1 ? 2 : src0 ? 1 : 0));
struct rc_instruction *rc_inst =
rc_insert_new_instruction(c->compiler, c->compiler->Program.Instructions.Prev);
struct rc_sub_instruction *inst = &rc_inst->U.I;
inst->Opcode = opcode;
if (dst) {
inst->DstReg = *dst;
inst->SaturateMode = saturate;
}
if (src0)
inst->SrcReg[0] = *src0;
if (src1)
inst->SrcReg[1] = *src1;
if (src2)
inst->SrcReg[2] = *src2;
return inst;
}
static struct rc_sub_instruction *
ntr_emit_alu_op(struct ntr_compile *c, rc_opcode opcode,
struct ntr_alu_dst dst,
const struct rc_src_register *src0,
const struct rc_src_register *src1,
const struct rc_src_register *src2)
{
return rc_sub_instruction(c, opcode, &dst.reg, dst.saturate,
src0, src1, src2);
}
static inline void
ntr_emit_alu_op1(struct ntr_compile *c, rc_opcode opcode,
struct ntr_alu_dst dst, struct rc_src_register src0)
{
ntr_emit_alu_op(c, opcode, dst, &src0, NULL, NULL);
}
static inline void
ntr_emit_alu_op2(struct ntr_compile *c, rc_opcode opcode,
struct ntr_alu_dst dst, struct rc_src_register src0,
struct rc_src_register src1)
{
ntr_emit_alu_op(c, opcode, dst, &src0, &src1, NULL);
}
static inline void
ntr_emit_alu_op3(struct ntr_compile *c, rc_opcode opcode,
struct ntr_alu_dst dst, struct rc_src_register src0,
struct rc_src_register src1, struct rc_src_register src2)
{
ntr_emit_alu_op(c, opcode, dst, &src0, &src1, &src2);
}
#define NTR_OP00(name, op) \
static inline void ntr_##name(struct ntr_compile *c) \
{ \
rc_sub_instruction(c, op, NULL, RC_SATURATE_NONE, NULL, NULL, NULL); \
}
#define NTR_OP01(name, op) \
static inline void ntr_##name(struct ntr_compile *c, struct rc_src_register src0)\
{ \
rc_sub_instruction(c, op, NULL, RC_SATURATE_NONE, &src0, NULL, NULL); \
}
#define NTR_OP11(name, op) \
static inline void ntr_##name(struct ntr_compile *c, struct rc_dst_register dst, \
struct rc_src_register src0) \
{ \
rc_sub_instruction(c, op, &dst, RC_SATURATE_NONE, &src0, NULL, NULL); \
}
NTR_OP00(KILL, RC_OPCODE_KILP)
NTR_OP00(BGNLOOP, RC_OPCODE_BGNLOOP)
NTR_OP00(BRK, RC_OPCODE_BRK)
NTR_OP00(CONT, RC_OPCODE_CONT)
NTR_OP00(ELSE, RC_OPCODE_ELSE)
NTR_OP00(ENDIF, RC_OPCODE_ENDIF)
NTR_OP00(ENDLOOP, RC_OPCODE_ENDLOOP)
NTR_OP01(KILL_IF, RC_OPCODE_KIL)
NTR_OP01(IF, RC_OPCODE_IF)
NTR_OP11(MOV, RC_OPCODE_MOV)
NTR_OP11(ARL, RC_OPCODE_ARL)
NTR_OP11(DDX, RC_OPCODE_DDX)
NTR_OP11(DDY, RC_OPCODE_DDY)
/**
* Interprets a nir_load_const used as a NIR src as a uint.
*
* For non-native-integers drivers, nir_load_const_instrs used by an integer ALU
* instruction (or in a phi-web used by an integer ALU instruction) were
* converted to floats and the ALU instruction swapped to the float equivalent.
* However, this means that integer load_consts used by intrinsics (which don't
* normally get that conversion) may have been reformatted to be floats. Given
* that all of our intrinsic nir_src_as_uint() calls are expected to be small,
* we can just look and see if they look like floats and convert them back to
* ints.
*/
static uint32_t
ntr_src_as_uint(struct ntr_compile *c, nir_src src)
{
uint32_t val = nir_src_as_uint(src);
if (val >= fui(1.0))
val = (uint32_t)uif(val);
return val;
}
static void
ntr_read_input_output(struct ntr_compile *c, gl_varying_slot location, unsigned base)
{
if (base >= c->semantics->num_total)
c->semantics->num_total = base + 1;
switch (location) {
case VARYING_SLOT_POS:
if (c->s->info.stage == MESA_SHADER_VERTEX)
c->semantics->pos = base;
else
c->semantics->wpos = base;
break;
case VARYING_SLOT_PSIZ:
c->semantics->psize = base;
break;
case VARYING_SLOT_COL0:
c->semantics->color[0] = base;
break;
case VARYING_SLOT_COL1:
c->semantics->color[1] = base;
break;
case VARYING_SLOT_BFC0:
c->semantics->bcolor[0] = base;
break;
case VARYING_SLOT_BFC1:
c->semantics->bcolor[1] = base;
break;
case VARYING_SLOT_FOGC:
c->semantics->fog = base;
break;
case VARYING_SLOT_FACE:
assert(c->s->info.stage == MESA_SHADER_FRAGMENT);
c->semantics->face = base;
break;
case VARYING_SLOT_EDGE:
assert(c->s->info.stage == MESA_SHADER_VERTEX);
fprintf(stderr, "r300 VP: cannot handle edgeflag output.\n");
break;
default:
if (location >= VARYING_SLOT_VAR0 && location <= VARYING_SLOT_VAR31) {
unsigned index = location - VARYING_SLOT_VAR0;
if (c->semantics->generic[index] == ATTR_UNUSED)
c->semantics->num_generic++;
c->semantics->generic[index] = base;
} else {
printf("Unhandled varying slot: %u\n", location);
UNREACHABLE("Unhandled varying slot");
}
break;
}
}
static unsigned
ntr_fs_output_index(struct ntr_compile *c, gl_frag_result location,
unsigned dual_source_blend_index)
{
int color_index = mesa_frag_result_get_color_index(location);
if (dual_source_blend_index) {
assert(location == FRAG_RESULT_COLOR || location == FRAG_RESULT_DATA0);
color_index += dual_source_blend_index;
}
int *output_index;
if (color_index >= 0 && color_index < ARRAY_SIZE(c->fs_output_color_index)) {
output_index = &c->fs_output_color_index[color_index];
} else if (location == FRAG_RESULT_DEPTH) {
output_index = &c->fs_output_depth_index;
} else {
assert(location < FRAG_RESULT_MAX);
output_index = &c->fs_output_index[location];
}
if (*output_index < 0)
*output_index = c->num_outputs++;
return *output_index;
}
static struct rc_dst_register
ntr_output_decl(struct ntr_compile *c, nir_intrinsic_instr *instr, uint32_t *frac)
{
nir_io_semantics semantics = nir_intrinsic_io_semantics(instr);
int base = nir_intrinsic_base(instr);
*frac = nir_intrinsic_component(instr);
struct rc_dst_register out;
if (c->s->info.stage == MESA_SHADER_FRAGMENT) {
switch (semantics.location) {
case FRAG_RESULT_DEPTH:
*frac = 2; /* z write is the to the .z channel in TGSI */
break;
case FRAG_RESULT_STENCIL:
*frac = 1;
break;
default:
break;
}
out = ntr_dst_register(c, RC_FILE_OUTPUT,
ntr_fs_output_index(c, semantics.location,
semantics.dual_source_blend_index));
} else {
ntr_read_input_output(c, semantics.location, base);
out = ntr_dst_register(c, RC_FILE_OUTPUT, base);
c->num_outputs = MAX2(c->num_outputs, base + semantics.num_slots);
}
unsigned write_mask;
if (nir_intrinsic_has_write_mask(instr))
write_mask = nir_intrinsic_write_mask(instr);
else
write_mask = ((1 << instr->num_components) - 1) << *frac;
write_mask = write_mask << *frac;
return ntr_writemask(out, write_mask);
}
static bool
ntr_try_store_in_tgsi_output_with_use(struct ntr_compile *c, struct rc_dst_register *dst, nir_src *src)
{
if (nir_src_is_if(src))
return false;
if (nir_src_use_instr(src)->type != nir_instr_type_intrinsic)
return false;
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(nir_src_use_instr(src));
if (intr->intrinsic != nir_intrinsic_store_output || !nir_src_is_const(intr->src[1])) {
return false;
}
uint32_t frac;
*dst = ntr_output_decl(c, intr, &frac);
ntr_set_dst_index(c, dst, dst->Index + ntr_src_as_uint(c, intr->src[1]));
return frac == 0;
}
/* If this reg is used only for storing an output, then in the simple
* cases we can write directly to the TGSI output instead of having
* store_output emit its own MOV.
*/
static bool
ntr_try_store_reg_in_tgsi_output(struct ntr_compile *c, struct rc_dst_register *dst,
nir_intrinsic_instr *reg_decl)
{
assert(reg_decl->intrinsic == nir_intrinsic_decl_reg);
/* Look for a single use for try_store_in_tgsi_output */
nir_src *use = NULL;
nir_foreach_reg_load (src, reg_decl) {
nir_intrinsic_instr *load = nir_instr_as_intrinsic(nir_src_use_instr(src));
nir_foreach_use_including_if (load_use, &load->def) {
/* We can only have one use */
if (use != NULL)
return false;
use = load_use;
}
}
if (use == NULL)
return false;
return ntr_try_store_in_tgsi_output_with_use(c, dst, use);
}
/* If this SSA def is used only for storing an output, then in the simple
* cases we can write directly to the TGSI output instead of having
* store_output emit its own MOV.
*/
static bool
ntr_try_store_ssa_in_tgsi_output(struct ntr_compile *c, struct rc_dst_register *dst, nir_def *def)
{
if (!list_is_singular(&def->uses))
return false;
nir_foreach_use_including_if (use, def) {
return ntr_try_store_in_tgsi_output_with_use(c, dst, use);
}
UNREACHABLE("We have one use");
}
static void
ntr_setup_inputs(struct ntr_compile *c)
{
if (c->s->info.stage != MESA_SHADER_FRAGMENT)
return;
unsigned num_inputs = 0;
nir_foreach_shader_in_variable (var, c->s) {
const struct glsl_type *type = var->type;
unsigned array_len = glsl_count_attribute_slots(type, false);
num_inputs = MAX2(num_inputs, var->data.driver_location + array_len);
}
c->input_index_map = ralloc_array(c, struct rc_src_register, num_inputs);
nir_foreach_shader_in_variable (var, c->s) {
const struct glsl_type *type = var->type;
unsigned array_len = glsl_count_attribute_slots(type, false);
struct rc_src_register decl;
decl = ntr_src_register(c, RC_FILE_INPUT, var->data.driver_location);
if (var->data.location == VARYING_SLOT_FACE) {
struct rc_dst_register temp = ntr_temp(c);
/* tgsi docs say that floating point FACE will be positive for
* frontface and negative for backface, but realistically
* GLSL-to-TGSI had been doing MOV_SAT to turn it into 0.0 vs 1.0.
* Copy that behavior, since some drivers (r300) have been doing a
* 0.0 vs 1.0 backface (and I don't think anybody has a non-1.0
* front face).
*/
rc_sub_instruction(c, RC_OPCODE_MOV, &temp, RC_SATURATE_ZERO_ONE,
&decl, NULL, NULL);
decl = ntr_src_from_dst(temp);
}
for (unsigned i = 0; i < array_len; i++) {
c->input_index_map[var->data.driver_location + i] = decl;
ntr_set_src_index(c, &c->input_index_map[var->data.driver_location + i],
decl.Index + i);
}
}
}
static int
ntr_sort_by_location(const nir_variable *a, const nir_variable *b)
{
return a->data.location - b->data.location;
}
/* Preallocate FS output registers in a deterministic order. */
static void
ntr_setup_outputs(struct ntr_compile *c)
{
if (c->s->info.stage != MESA_SHADER_FRAGMENT)
return;
nir_sort_variables_with_modes(c->s, ntr_sort_by_location, nir_var_shader_out);
nir_foreach_shader_out_variable (var, c->s) {
ntr_fs_output_index(c, var->data.location, var->data.index);
}
}
static rc_texture_target
rc_texture_target_from_sampler_dim(enum glsl_sampler_dim dim, bool is_array)
{
/* r300 has no array, multisample or buffer textures. */
assert(!is_array);
switch (dim) {
case GLSL_SAMPLER_DIM_1D:
return RC_TEXTURE_1D;
case GLSL_SAMPLER_DIM_2D:
case GLSL_SAMPLER_DIM_EXTERNAL:
return RC_TEXTURE_2D;
case GLSL_SAMPLER_DIM_3D:
return RC_TEXTURE_3D;
case GLSL_SAMPLER_DIM_CUBE:
return RC_TEXTURE_CUBE;
case GLSL_SAMPLER_DIM_RECT:
return RC_TEXTURE_RECT;
default:
UNREACHABLE("unknown sampler dim");
}
}
static void
ntr_setup_uniforms(struct ntr_compile *c)
{
/* lower_uniforms_to_ubo lowered non-sampler uniforms to UBOs. */
unsigned size = 0;
nir_foreach_variable_with_modes (var, c->s, nir_var_mem_ubo) {
int ubo = var->data.driver_location;
assert(ubo == 0 && size == 0);
size = glsl_get_explicit_size(var->interface_type, false);
}
c->ubo_size = size ? DIV_ROUND_UP(size, 16) : 0;
}
static void
ntr_setup_registers(struct ntr_compile *c)
{
assert(c->num_temps == 0);
/* After that, allocate non-array regs in our virtual space that we'll
* register-allocate before RC emit.
*/
nir_foreach_reg_decl_safe (nir_reg, nir_shader_get_entrypoint(c->s)) {
assert(nir_intrinsic_num_array_elems(nir_reg) == 0);
unsigned num_components = nir_intrinsic_num_components(nir_reg);
unsigned index = nir_reg->def.index;
struct rc_dst_register decl;
uint32_t write_mask = BITFIELD_MASK(num_components);
if (!ntr_try_store_reg_in_tgsi_output(c, &decl, nir_reg)) {
decl = ntr_writemask(ntr_temp(c), write_mask);
}
c->reg_temp[index] = decl;
}
}
static struct rc_src_register
ntr_get_load_const_src(struct ntr_compile *c, nir_load_const_instr *instr)
{
int num_components = instr->def.num_components;
/* Always emit a full vec4 immediate per load_const and let the
* RC backend's constant packing handle it later.
*/
float values[4] = {0};
assert(instr->def.bit_size == 32);
for (int i = 0; i < num_components; i++)
values[i] = uif(instr->value[i].u32);
unsigned index = util_dynarray_num_elements(&c->immediates, struct ntr_immediate);
struct ntr_immediate *slot =
util_dynarray_grow(&c->immediates, struct ntr_immediate, 1);
memcpy(slot->values, values, sizeof(values));
return ntr_src_register(c, RC_FILE_CONSTANT, c->immediate_offset + index);
}
static struct rc_src_register
ntr_reladdr(struct ntr_compile *c, struct rc_src_register addr)
{
ntr_ARL(c, c->addr_reg, addr);
return ntr_scalar(ntr_src_from_dst(c->addr_reg), 0);
}
/* Forward declare for recursion with indirects */
static struct rc_src_register ntr_get_src(struct ntr_compile *c, nir_src src);
static struct rc_src_register
ntr_get_chased_src(struct ntr_compile *c, nir_legacy_src *src)
{
if (src->is_ssa) {
if (nir_def_is_const(src->ssa))
return ntr_get_load_const_src(c, nir_def_as_load_const(src->ssa));
return c->ssa_temp[src->ssa->index];
} else {
struct rc_dst_register reg_temp = c->reg_temp[src->reg.handle->index];
ntr_set_dst_index(c, &reg_temp, reg_temp.Index + src->reg.base_offset);
if (src->reg.indirect) {
struct rc_src_register offset = ntr_get_src(c, nir_src_for_ssa(src->reg.indirect));
ntr_reladdr(c, offset);
return ntr_src_indirect(ntr_src_from_dst(reg_temp));
} else {
return ntr_src_from_dst(reg_temp);
}
}
}
static struct rc_src_register
ntr_get_src(struct ntr_compile *c, nir_src src)
{
nir_legacy_src chased = nir_legacy_chase_src(&src);
return ntr_get_chased_src(c, &chased);
}
static struct rc_src_register
ntr_get_alu_src(struct ntr_compile *c, nir_alu_instr *instr, int i)
{
/* The RC source modifier fields we use here are 32-bit float modifiers.
* Keep integer and f64 modifiers explicit in NIR.
*
* The lower_fabs requests that we not have native source modifiers
* for fabs, and instead emit MAX(a,-a) for nir_op_fabs.
*/
nir_legacy_alu_src src = nir_legacy_chase_alu_src(&instr->src[i], !c->lower_fabs);
struct rc_src_register usrc = ntr_get_chased_src(c, &src.src);
usrc = ntr_swizzle(usrc, src.swizzle[0], src.swizzle[1], src.swizzle[2], src.swizzle[3]);
if (src.fabs)
usrc = ntr_abs(usrc);
if (src.fneg)
usrc = ntr_negate(usrc);
return usrc;
}
/* Reswizzles a source so that the unset channels in the write mask still refer
* to one of the channels present in the write mask.
*/
static struct rc_src_register
ntr_swizzle_for_write_mask(struct rc_src_register src, uint32_t write_mask)
{
assert(write_mask);
int first_chan = ffs(write_mask) - 1;
return ntr_swizzle(src, (write_mask & RC_MASK_X) ? RC_SWIZZLE_X : first_chan,
(write_mask & RC_MASK_Y) ? RC_SWIZZLE_Y : first_chan,
(write_mask & RC_MASK_Z) ? RC_SWIZZLE_Z : first_chan,
(write_mask & RC_MASK_W) ? RC_SWIZZLE_W : first_chan);
}
static struct rc_dst_register
ntr_get_ssa_def_decl(struct ntr_compile *c, nir_def *ssa)
{
uint32_t writemask;
/* Fix writemask for nir_intrinsic_load_ubo_vec4 according to uses. */
if (nir_def_is_intrinsic(ssa) &&
nir_def_as_intrinsic(ssa)->intrinsic == nir_intrinsic_load_ubo_vec4)
writemask = nir_def_components_read(ssa);
else
writemask = BITSET_MASK(ssa->num_components);
struct rc_dst_register dst;
if (!ntr_try_store_ssa_in_tgsi_output(c, &dst, ssa))
dst = ntr_temp(c);
c->ssa_temp[ssa->index] = ntr_swizzle_for_write_mask(ntr_src_from_dst(dst), writemask);
return ntr_writemask(dst, writemask);
}
static struct rc_dst_register
ntr_get_chased_dest_decl(struct ntr_compile *c, nir_legacy_dest *dest)
{
if (dest->is_ssa)
return ntr_get_ssa_def_decl(c, dest->ssa);
else
return c->reg_temp[dest->reg.handle->index];
}
static struct rc_dst_register
ntr_get_chased_dest(struct ntr_compile *c, nir_legacy_dest *dest)
{
struct rc_dst_register dst = ntr_get_chased_dest_decl(c, dest);
if (!dest->is_ssa) {
ntr_set_dst_index(c, &dst, dst.Index + dest->reg.base_offset);
if (dest->reg.indirect) {
struct rc_src_register offset = ntr_get_src(c, nir_src_for_ssa(dest->reg.indirect));
ntr_reladdr(c, offset);
rc_error(c->compiler,
"r300: Relative addressing of destination operands is unsupported.\n");
}
}
return dst;
}
static struct rc_dst_register
ntr_get_dest(struct ntr_compile *c, nir_def *def)
{
nir_legacy_dest chased = nir_legacy_chase_dest(def);
return ntr_get_chased_dest(c, &chased);
}
static struct ntr_alu_dst
ntr_get_alu_dest(struct ntr_compile *c, nir_def *def)
{
nir_legacy_alu_dest chased = nir_legacy_chase_alu_dest(def);
struct rc_dst_register dst = ntr_get_chased_dest(c, &chased.dest);
rc_saturate_mode saturate =
chased.fsat ? RC_SATURATE_ZERO_ONE : RC_SATURATE_NONE;
/* Only registers get write masks */
if (!chased.dest.is_ssa)
dst = ntr_writemask(dst, chased.write_mask);
return ntr_alu_dst(dst, saturate);
}
/* For an SSA dest being populated by a constant src, replace the storage with
* a copy of the source reference.
*/
static void
ntr_store_def(struct ntr_compile *c, nir_def *def, struct rc_src_register src)
{
if (!src.RelAddr) {
switch (src.File) {
case RC_FILE_INPUT:
case RC_FILE_CONSTANT:
c->ssa_temp[def->index] = src;
return;
default:
break;
}
}
ntr_MOV(c, ntr_get_ssa_def_decl(c, def), src);
}
static void
ntr_store(struct ntr_compile *c, nir_def *def, struct rc_src_register src)
{
nir_legacy_dest chased = nir_legacy_chase_dest(def);
if (chased.is_ssa)
ntr_store_def(c, chased.ssa, src);
else {
struct rc_dst_register dst = ntr_get_chased_dest(c, &chased);
ntr_MOV(c, dst, src);
}
}
/* TODO: we should be probably scalarizing all the opcodes we need earlier in NIR. */
static void
ntr_emit_scalar(struct ntr_compile *c, rc_opcode op, struct ntr_alu_dst dst,
struct rc_src_register src0,
const struct rc_src_register *src1)
{
/* POW is the only 2-operand scalar op. */
bool has_src1 = op == RC_OPCODE_POW;
assert(has_src1 == (src1 != NULL));
for (unsigned i = 0; i < 4; i++) {
if (dst.reg.WriteMask & (1 << i)) {
struct ntr_alu_dst scalar_dst = dst;
struct rc_src_register scalar_src0 = ntr_scalar(src0, i);
struct rc_src_register scalar_src1;
scalar_dst.reg = ntr_writemask(scalar_dst.reg, 1 << i);
if (has_src1)
scalar_src1 = ntr_scalar(*src1, i);
ntr_emit_alu_op(c, op, scalar_dst, &scalar_src0,
has_src1 ? &scalar_src1 : NULL, NULL);
}
}
}
static void
ntr_emit_alu(struct ntr_compile *c, nir_alu_instr *instr)
{
struct rc_src_register src[4];
struct ntr_alu_dst dst;
unsigned i;
int num_srcs = nir_op_infos[instr->op].num_inputs;
/* Don't try to translate folded fsat since their source won't be valid */
if (instr->op == nir_op_fsat && nir_legacy_fsat_folds(instr))
return;
assert(num_srcs <= ARRAY_SIZE(src));
for (i = 0; i < num_srcs; i++)
src[i] = ntr_get_alu_src(c, instr, i);
dst = ntr_get_alu_dest(c, &instr->def);
static const rc_opcode op_map[] = {
[nir_op_mov] = RC_OPCODE_MOV,
[nir_op_fdot2_replicated] = RC_OPCODE_DP2,
[nir_op_fdot3_replicated] = RC_OPCODE_DP3,
[nir_op_fdot4_replicated] = RC_OPCODE_DP4,
[nir_op_ffract] = RC_OPCODE_FRC,
[nir_op_fround_even] = RC_OPCODE_ROUND,
[nir_op_slt] = RC_OPCODE_SLT,
[nir_op_sge] = RC_OPCODE_SGE,
[nir_op_seq] = RC_OPCODE_SEQ,
[nir_op_sne] = RC_OPCODE_SNE,
[nir_op_fadd] = RC_OPCODE_ADD,
[nir_op_fmul] = RC_OPCODE_MUL,
[nir_op_fmin] = RC_OPCODE_MIN,
[nir_op_fmax] = RC_OPCODE_MAX,
[nir_op_fmad] = RC_OPCODE_MAD,
};
if (instr->op < ARRAY_SIZE(op_map) && op_map[instr->op] > 0) {
/* The normal path for NIR to TGSI ALU op translation */
const struct rc_opcode_info *opcode_info = rc_get_opcode_info(op_map[instr->op]);
ntr_emit_alu_op(c, op_map[instr->op], dst,
opcode_info->NumSrcRegs > 0 ? &src[0] : NULL,
opcode_info->NumSrcRegs > 1 ? &src[1] : NULL,
opcode_info->NumSrcRegs > 2 ? &src[2] : NULL);
} else {
/* Special cases for NIR to TGSI ALU op translation. */
/* TODO: Use something like the ntr_store() path for the MOV calls so we
* don't emit extra MOVs for swizzles/srcmods of inputs/const/imm.
*/
switch (instr->op) {
case nir_op_fabs:
/* Try to eliminate */
if (!c->lower_fabs && nir_legacy_float_mod_folds(instr))
break;
if (c->lower_fabs)
ntr_emit_alu_op2(c, RC_OPCODE_MAX, dst, src[0], ntr_negate(src[0]));
else
ntr_emit_alu_op1(c, RC_OPCODE_MOV, dst, ntr_abs(src[0]));
break;
case nir_op_fsat:
ntr_emit_alu_op1(c, RC_OPCODE_MOV, ntr_alu_dst_saturate(dst), src[0]);
break;
case nir_op_fneg:
/* Try to eliminate */
if (nir_legacy_float_mod_folds(instr))
break;
ntr_emit_alu_op1(c, RC_OPCODE_MOV, dst, ntr_negate(src[0]));
break;
/* NOTE: TGSI 32-bit math ops have the old "one source channel
* replicated to all dst channels" behavior, while 64 is normal mapping
* of src channels to dst.
*/
case nir_op_frcp:
ntr_emit_scalar(c, RC_OPCODE_RCP, dst, src[0], NULL);
break;
case nir_op_frsq:
ntr_emit_scalar(c, RC_OPCODE_RSQ, dst, src[0], NULL);
break;
case nir_op_fexp2:
ntr_emit_scalar(c, RC_OPCODE_EX2, dst, src[0], NULL);
break;
case nir_op_flog2:
ntr_emit_scalar(c, RC_OPCODE_LG2, dst, src[0], NULL);
break;
case nir_op_fsin:
ntr_emit_scalar(c, RC_OPCODE_SIN, dst, src[0], NULL);
break;
case nir_op_fcos:
ntr_emit_scalar(c, RC_OPCODE_COS, dst, src[0], NULL);
break;
case nir_op_fsub:
ntr_emit_alu_op2(c, RC_OPCODE_ADD, dst, src[0], ntr_negate(src[1]));
break;
case nir_op_fpow:
ntr_emit_scalar(c, RC_OPCODE_POW, dst, src[0], &src[1]);
break;
case nir_op_fcsel:
/* Implement this as CMP(-abs(src0), src1, src2). */
ntr_emit_alu_op3(c, RC_OPCODE_CMP, dst,
ntr_negate(ntr_abs(src[0])), src[1], src[2]);
break;
case nir_op_fcsel_gt:
ntr_emit_alu_op3(c, RC_OPCODE_CMP, dst,
ntr_negate(src[0]), src[1], src[2]);
break;
case nir_op_fcsel_ge:
/* Implement this as if !(src0 < 0.0) was identical to src0 >= 0.0. */
ntr_emit_alu_op3(c, RC_OPCODE_CMP, dst, src[0], src[2], src[1]);
break;
default:
fprintf(stderr, "Unknown NIR opcode: %s\n", nir_op_infos[instr->op].name);
UNREACHABLE("Unknown NIR opcode");
}
}
}
static struct rc_src_register
ntr_src_offset(struct ntr_compile *c, struct rc_src_register nsrc, nir_src src)
{
if (nir_src_is_const(src)) {
ntr_set_src_index(c, &nsrc, nsrc.Index + ntr_src_as_uint(c, src));
return nsrc;
} else {
ntr_reladdr(c, ntr_get_src(c, src));
return ntr_src_indirect(nsrc);
}
}
static struct rc_dst_register
ntr_dst_offset(struct ntr_compile *c, struct rc_dst_register dst, nir_src src)
{
/* Indirect adressing should have been lowered earlier in NIR. */
assert(nir_src_is_const(src));
ntr_set_dst_index(c, &dst, dst.Index + ntr_src_as_uint(c, src));
return dst;
}
/* Some load operations in NIR will have a fractional offset that we need to
* swizzle down before storing to the result register.
*/
static struct rc_src_register
ntr_shift_by_frac(struct rc_src_register src, unsigned frac, unsigned num_components)
{
return ntr_swizzle(src, frac, frac + MIN2(num_components - 1, 1),
frac + MIN2(num_components - 1, 2), frac + MIN2(num_components - 1, 3));
}
static void
ntr_emit_load_ubo(struct ntr_compile *c, nir_intrinsic_instr *instr)
{
struct rc_src_register src = ntr_src_register(c, RC_FILE_CONSTANT, 0);
/* r300 only exposes a single UBO and any indirect UBO array indexing
* has been lowered before we get here. */
assert(nir_src_is_const(instr->src[0]));
assert(ntr_src_as_uint(c, instr->src[0]) == 0);
/* !pipe_caps.load_constbuf: Just emit it as a vec4 reference to the const
* file.
*/
ntr_set_src_index(c, &src, nir_intrinsic_base(instr));
if (nir_src_is_const(instr->src[1])) {
ntr_set_src_index(c, &src, src.Index + ntr_src_as_uint(c, instr->src[1]));
} else {
ntr_reladdr(c, ntr_get_src(c, instr->src[1]));
src = ntr_src_indirect(src);
}
int start_component = nir_intrinsic_component(instr);
src = ntr_shift_by_frac(src, start_component, instr->num_components);
ntr_store(c, &instr->def, src);
}
static void
ntr_emit_load_input(struct ntr_compile *c, nir_intrinsic_instr *instr)
{
uint32_t frac = nir_intrinsic_component(instr);
uint32_t num_components = instr->num_components;
unsigned base = nir_intrinsic_base(instr);
struct rc_src_register input;
nir_io_semantics semantics = nir_intrinsic_io_semantics(instr);
if (c->s->info.stage == MESA_SHADER_FRAGMENT)
ntr_read_input_output(c, semantics.location, base);
if (c->s->info.stage == MESA_SHADER_VERTEX) {
input = ntr_src_register(c, RC_FILE_INPUT, base);
} else {
input = c->input_index_map[base];
}
input = ntr_shift_by_frac(input, frac, num_components);
switch (instr->intrinsic) {
case nir_intrinsic_load_input:
input = ntr_src_offset(c, input, instr->src[0]);
ntr_store(c, &instr->def, input);
break;
case nir_intrinsic_load_interpolated_input: {
input = ntr_src_offset(c, input, instr->src[1]);
nir_intrinsic_instr *bary_instr = nir_def_as_intrinsic(instr->src[0].ssa);
switch (bary_instr->intrinsic) {
case nir_intrinsic_load_barycentric_pixel:
/* The barycentric load matches the interpolation on the input
* declaration, so we can use it directly.
*/
ntr_store(c, &instr->def, input);
break;
case nir_intrinsic_load_barycentric_centroid:
/* On r300 interpolation is fixed by the rasterizer state; the NIR
* lowering pairs centroid intrinsics with centroid-declared inputs,
* so we never need an explicit INTERP instruction. */
ntr_store(c, &instr->def, input);
break;
default:
UNREACHABLE("bad barycentric interp intrinsic\n");
}
break;
}
default:
UNREACHABLE("bad load input intrinsic\n");
}
}
static void
ntr_emit_store_output(struct ntr_compile *c, nir_intrinsic_instr *instr)
{
struct rc_src_register src = ntr_get_src(c, instr->src[0]);
if (src.File == RC_FILE_OUTPUT) {
/* If our src is the output file, that's an indication that we were able
* to emit the output stores in the generating instructions and we have
* nothing to do here.
*/
return;
}
uint32_t frac;
struct rc_dst_register out = ntr_output_decl(c, instr, &frac);
out = ntr_dst_offset(c, out, instr->src[1]);
uint8_t swizzle[4] = {0, 0, 0, 0};
for (int i = frac; i < 4; i++) {
if (out.WriteMask & (1 << i))
swizzle[i] = i - frac;
}
src = ntr_swizzle(src, swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
ntr_MOV(c, out, src);
}
static void
ntr_emit_intrinsic(struct ntr_compile *c, nir_intrinsic_instr *instr)
{
switch (instr->intrinsic) {
case nir_intrinsic_load_ubo:
case nir_intrinsic_load_ubo_vec4:
ntr_emit_load_ubo(c, instr);
break;
case nir_intrinsic_load_frag_coord:
case nir_intrinsic_load_point_coord:
case nir_intrinsic_load_front_face:
UNREACHABLE("system value should have been lowered to a varying");
break;
case nir_intrinsic_load_input:
case nir_intrinsic_load_interpolated_input:
ntr_emit_load_input(c, instr);
break;
case nir_intrinsic_store_output:
ntr_emit_store_output(c, instr);
break;
case nir_intrinsic_terminate:
ntr_KILL(c);
break;
case nir_intrinsic_terminate_if: {
struct rc_src_register cond = ntr_scalar(ntr_get_src(c, instr->src[0]), 0);
/* For !native_integers, the bool got lowered to 1.0 or 0.0. */
ntr_KILL_IF(c, ntr_negate(cond));
break;
}
/* In TGSI we don't actually generate the barycentric coords, and
* emit the corresponding INTERP_CENTROID instruction in
* ntr_emit_load_input. The barycentric loads themselves are
* therefore consumed there.
*/
case nir_intrinsic_load_barycentric_pixel:
case nir_intrinsic_load_barycentric_centroid:
break;
case nir_intrinsic_ddx:
case nir_intrinsic_ddx_coarse:
ntr_DDX(c, ntr_get_dest(c, &instr->def), ntr_get_src(c, instr->src[0]));
return;
case nir_intrinsic_ddy:
case nir_intrinsic_ddy_coarse:
ntr_DDY(c, ntr_get_dest(c, &instr->def), ntr_get_src(c, instr->src[0]));
return;
case nir_intrinsic_decl_reg:
case nir_intrinsic_load_reg:
case nir_intrinsic_load_reg_indirect:
case nir_intrinsic_store_reg:
case nir_intrinsic_store_reg_indirect:
/* fully consumed */
break;
default:
fprintf(stderr, "Unknown intrinsic: ");
nir_print_instr(&instr->instr, stderr);
fprintf(stderr, "\n");
break;
}
}
struct ntr_tex_operand_state {
struct rc_src_register srcs[3];
unsigned i;
};
static void
ntr_push_tex_arg(struct ntr_compile *c, nir_tex_instr *instr, nir_tex_src_type tex_src_type,
struct ntr_tex_operand_state *s)
{
int tex_src = nir_tex_instr_src_index(instr, tex_src_type);
if (tex_src < 0)
return;
nir_src *src = &instr->src[tex_src].src;
s->srcs[s->i++] = ntr_get_src(c, *src);
}
static void
ntr_emit_texture(struct ntr_compile *c, nir_tex_instr *instr)
{
struct rc_dst_register dst = ntr_get_dest(c, &instr->def);
assert(!instr->is_shadow);
rc_texture_target target =
rc_texture_target_from_sampler_dim(instr->sampler_dim, instr->is_array);
unsigned tex_opcode;
int tex_handle_src = nir_tex_instr_src_index(instr, nir_tex_src_texture_handle);
int sampler_handle_src = nir_tex_instr_src_index(instr, nir_tex_src_sampler_handle);
unsigned sampler_index = instr->sampler_index;
bool sampler_reladdr = false;
if (tex_handle_src >= 0 && sampler_handle_src >= 0) {
/* It seems we can't get separate tex/sampler on GL, just use one of the handles */
struct rc_src_register sampler = ntr_get_src(c, instr->src[tex_handle_src].src);
sampler_index = sampler.Index;
sampler_reladdr = sampler.RelAddr;
assert(nir_tex_instr_src_index(instr, nir_tex_src_sampler_offset) == -1);
} else {
assert(tex_handle_src == -1 && sampler_handle_src == -1);
int sampler_src = nir_tex_instr_src_index(instr, nir_tex_src_sampler_offset);
if (sampler_src >= 0) {
struct rc_src_register reladdr = ntr_get_src(c, instr->src[sampler_src].src);
ntr_reladdr(c, reladdr);
sampler_reladdr = true;
}
}
switch (instr->op) {
case nir_texop_tex:
if (nir_tex_instr_src_size(instr, nir_tex_instr_src_index(instr, nir_tex_src_backend1)) >
MAX2(instr->coord_components, 2) + instr->is_shadow)
tex_opcode = RC_OPCODE_TXP;
else
tex_opcode = RC_OPCODE_TEX;
break;
case nir_texop_txl:
tex_opcode = RC_OPCODE_TXL;
break;
case nir_texop_txb:
tex_opcode = RC_OPCODE_TXB;
break;
case nir_texop_txd:
tex_opcode = RC_OPCODE_TXD;
break;
default:
UNREACHABLE("unsupported tex op");
}
struct ntr_tex_operand_state s = {.i = 0};
ntr_push_tex_arg(c, instr, nir_tex_src_backend1, &s);
ntr_push_tex_arg(c, instr, nir_tex_src_backend2, &s);
/* The RC backend doesn't have TEX2/TXB2/TXL2-style opcodes; the
* shadow comparator that would need a second backend slot is
* already lowered before nir_to_rc by nir_lower_tex_shadow. */
assert(s.i <= 1);
if (instr->op == nir_texop_txd) {
/* Derivs appear in their own src args */
int ddx = nir_tex_instr_src_index(instr, nir_tex_src_ddx);
int ddy = nir_tex_instr_src_index(instr, nir_tex_src_ddy);
s.srcs[s.i++] = ntr_get_src(c, instr->src[ddx].src);
s.srcs[s.i++] = ntr_get_src(c, instr->src[ddy].src);
}
assert(s.i == rc_get_opcode_info(tex_opcode)->NumSrcRegs);
struct rc_sub_instruction *inst =
rc_sub_instruction(c, tex_opcode, &dst, RC_SATURATE_NONE, &s.srcs[0],
s.i > 1 ? &s.srcs[1] : NULL, s.i > 2 ? &s.srcs[2] : NULL);
inst->TexSrcTarget = target;
ntr_rc_tex_sampler(c, inst, sampler_index, sampler_reladdr);
inst->TexSwizzle = RC_SWIZZLE_XYZW;
}
static void
ntr_emit_jump(struct ntr_compile *c, nir_jump_instr *jump)
{
switch (jump->type) {
case nir_jump_break:
ntr_BRK(c);
break;
case nir_jump_continue:
ntr_CONT(c);
break;
default:
fprintf(stderr, "Unknown jump instruction: ");
nir_print_instr(&jump->instr, stderr);
fprintf(stderr, "\n");
abort();
}
}
static void
ntr_emit_ssa_undef(struct ntr_compile *c, nir_undef_instr *instr)
{
/* Nothing to do but make sure that we have some storage to deref. */
(void)ntr_get_ssa_def_decl(c, &instr->def);
}
static void
ntr_emit_instr(struct ntr_compile *c, nir_instr *instr)
{
switch (instr->type) {
case nir_instr_type_deref:
/* ignored, will be walked by nir_intrinsic_image_*_deref. */
break;
case nir_instr_type_alu:
ntr_emit_alu(c, nir_instr_as_alu(instr));
break;
case nir_instr_type_intrinsic:
ntr_emit_intrinsic(c, nir_instr_as_intrinsic(instr));
break;
case nir_instr_type_load_const:
/* Nothing to do here, as load consts are done directly from
* ntr_get_src() (since many constant NIR srcs will often get folded
* directly into a register file index instead of as a TGSI src).
*/
break;
case nir_instr_type_tex:
ntr_emit_texture(c, nir_instr_as_tex(instr));
break;
case nir_instr_type_jump:
ntr_emit_jump(c, nir_instr_as_jump(instr));
break;
case nir_instr_type_undef:
ntr_emit_ssa_undef(c, nir_instr_as_undef(instr));
break;
default:
fprintf(stderr, "Unknown NIR instr type: ");
nir_print_instr(instr, stderr);
fprintf(stderr, "\n");
abort();
}
}
static void
ntr_emit_if(struct ntr_compile *c, nir_if *if_stmt)
{
ntr_IF(c, c->if_cond);
ntr_emit_cf_list(c, &if_stmt->then_list);
if (!nir_cf_list_is_empty_block(&if_stmt->else_list)) {
ntr_ELSE(c);
ntr_emit_cf_list(c, &if_stmt->else_list);
}
ntr_ENDIF(c);
}
static void
ntr_emit_loop(struct ntr_compile *c, nir_loop *loop)
{
assert(!nir_loop_has_continue_construct(loop));
ntr_BGNLOOP(c);
ntr_emit_cf_list(c, &loop->body);
ntr_ENDLOOP(c);
}
static void
ntr_emit_block(struct ntr_compile *c, nir_block *block)
{
nir_foreach_instr (instr, block) {
ntr_emit_instr(c, instr);
}
/* Set up the if condition for ntr_emit_if(). */
nir_if *nif = nir_block_get_following_if(block);
if (nif)
c->if_cond = ntr_get_src(c, nif->condition);
}
static void
ntr_emit_cf_list(struct ntr_compile *c, struct exec_list *list)
{
foreach_list_typed (nir_cf_node, node, node, list) {
switch (node->type) {
case nir_cf_node_block:
ntr_emit_block(c, nir_cf_node_as_block(node));
break;
case nir_cf_node_if:
ntr_emit_if(c, nir_cf_node_as_if(node));
break;
case nir_cf_node_loop:
ntr_emit_loop(c, nir_cf_node_as_loop(node));
break;
default:
UNREACHABLE("unknown CF type");
}
}
}
static void
ntr_add_constants(struct ntr_compile *c)
{
/* Add UBO constants first (they show up before immediates in the
* compiler's constants table; the immediate offset compensates for
* this). */
for (unsigned i = 0; i < c->ubo_size; i++) {
struct rc_constant constant;
memset(&constant, 0, sizeof(constant));
constant.Type = RC_CONSTANT_EXTERNAL;
constant.UseMask = RC_MASK_XYZW;
constant.u.External = i;
rc_constants_add(&c->compiler->Program.Constants, &constant);
}
assert(c->compiler->Program.Constants.Count == c->immediate_offset);
util_dynarray_foreach (&c->immediates, struct ntr_immediate, imm) {
struct rc_constant constant;
constant.Type = RC_CONSTANT_IMMEDIATE;
constant.UseMask = RC_MASK_XYZW;
for (unsigned j = 0; j < 4; j++)
constant.u.Immediate[j] = imm->values[j];
rc_constants_add(&c->compiler->Program.Constants, &constant);
}
}
static void
ntr_emit_impl(struct ntr_compile *c, nir_function_impl *impl)
{
c->ssa_temp = rzalloc_array(c, struct rc_src_register, impl->ssa_alloc);
c->reg_temp = rzalloc_array(c, struct rc_dst_register, impl->ssa_alloc);
ntr_setup_registers(c);
ntr_setup_uniforms(c);
c->immediate_offset = c->compiler->Program.Constants.Count + c->ubo_size;
ntr_setup_inputs(c);
ntr_setup_outputs(c);
/* Emit RC instructions directly. */
ntr_emit_cf_list(c, &impl->body);
/* Add constants referenced by the emitted RC instructions. */
ntr_add_constants(c);
}
static int
type_size(const struct glsl_type *type, bool bindless)
{
return glsl_count_attribute_slots(type, false);
}
/* Allow vectorizing of ALU instructions.
*/
static uint8_t
ntr_should_vectorize_instr(const nir_instr *instr, const void *data)
{
if (instr->type != nir_instr_type_alu)
return 0;
return 4;
}
struct ntr_lower_tex_state {
nir_scalar channels[8];
unsigned i;
};
static void
nir_to_rc_lower_tex_instr_arg(nir_builder *b, nir_tex_instr *instr, nir_tex_src_type tex_src_type,
struct ntr_lower_tex_state *s)
{
int tex_src = nir_tex_instr_src_index(instr, tex_src_type);
if (tex_src < 0)
return;
nir_def *def = instr->src[tex_src].src.ssa;
for (int i = 0; i < def->num_components; i++) {
s->channels[s->i++] = nir_get_scalar(def, i);
}
nir_tex_instr_remove_src(instr, tex_src);
}
/**
* Merges together a vec4 of tex coordinate/compare/bias/lod into a backend tex
* src. This lets NIR handle the coalescing of the vec4 rather than trying to
* manage it on our own, and may lead to more vectorization.
*/
static bool
nir_to_rc_lower_tex_instr(nir_builder *b, nir_tex_instr *tex, void *data)
{
if (nir_tex_instr_src_index(tex, nir_tex_src_coord) < 0)
return false;
b->cursor = nir_before_instr(&tex->instr);
struct ntr_lower_tex_state s = {0};
nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_coord, &s);
/* We always have at least two slots for the coordinate, even on 1D. */
s.i = MAX2(s.i, 2);
nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_comparator, &s);
s.i = MAX2(s.i, 3);
nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_bias, &s);
/* XXX: LZ */
nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_lod, &s);
nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_projector, &s);
nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_ms_index, &s);
/* No need to pack undefs in unused channels of the tex instr */
while (!s.channels[s.i - 1].def)
s.i--;
/* Instead of putting undefs in the unused slots of the vecs, just put in
* another used channel. Otherwise, we'll get unnecessary moves into
* registers.
*/
assert(s.channels[0].def != NULL);
for (int i = 1; i < s.i; i++) {
if (!s.channels[i].def)
s.channels[i] = s.channels[0];
}
nir_tex_instr_add_src(tex, nir_tex_src_backend1, nir_vec_scalars(b, s.channels, MIN2(s.i, 4)));
if (s.i > 4)
nir_tex_instr_add_src(tex, nir_tex_src_backend2, nir_vec_scalars(b, &s.channels[4], s.i - 4));
return true;
}
static bool
nir_to_rc_lower_tex(nir_shader *s)
{
return nir_shader_tex_pass(s, nir_to_rc_lower_tex_instr,
nir_metadata_control_flow, NULL);
}
/* Lowers texture projectors if we can't do them as RC_OPCODE_TXP. */
static void
nir_to_rc_lower_txp(nir_shader *s)
{
nir_lower_tex_options lower_tex_options = {
.lower_txp = 0,
};
nir_foreach_block (block, nir_shader_get_entrypoint(s)) {
nir_foreach_instr (instr, block) {
if (instr->type != nir_instr_type_tex)
continue;
nir_tex_instr *tex = nir_instr_as_tex(instr);
if (nir_tex_instr_src_index(tex, nir_tex_src_projector) < 0)
continue;
bool has_compare = nir_tex_instr_src_index(tex, nir_tex_src_comparator) >= 0;
bool has_lod = nir_tex_instr_src_index(tex, nir_tex_src_lod) >= 0 ||
s->info.stage != MESA_SHADER_FRAGMENT;
bool has_offset = nir_tex_instr_src_index(tex, nir_tex_src_offset) >= 0;
/* We can do TXP for any tex (not txg) where we can fit all the
* coordinates and comparator and projector in one vec4 without any
* other modifiers to add on.
*
* nir_lower_tex() only handles the lowering on a sampler-dim basis, so
* if we get any funny projectors then we just blow them all away.
*/
if (tex->op != nir_texop_tex || has_lod || has_offset ||
(tex->coord_components >= 3 && has_compare))
lower_tex_options.lower_txp |= 1 << tex->sampler_dim;
}
}
/* nir_lower_tex must be run even if no options are set, because we need the
* LOD to be set for query_levels and for non-fragment shaders.
*/
NIR_PASS(_, s, nir_lower_tex, &lower_tex_options);
}
/* There are some issues with the tgsi_texcoord = false support in the state
* tracker, specifically we are still getting texcoords and pointcoord in some cases
* and ocassionally (with fixed function shaders) there are some inconsistencies
* like vs using generics and fs using texcoords. This function tries to fix it.
* See https://gitlab.freedesktop.org/mesa/mesa/-/issues/12749 for more details.
*/
void
ntr_fixup_varying_slots(nir_shader *s, nir_variable_mode mode)
{
if (s->info.name && !strcmp(s->info.name, "st/drawtex VS"))
return;
nir_foreach_variable_with_modes (var, s, mode) {
if (var->data.location >= VARYING_SLOT_VAR0 && var->data.location < VARYING_SLOT_PATCH0) {
var->data.location += 9;
} else if (var->data.location == VARYING_SLOT_PNTC) {
var->data.location = VARYING_SLOT_VAR8;
} else if ((var->data.location >= VARYING_SLOT_TEX0) &&
(var->data.location <= VARYING_SLOT_TEX7)) {
var->data.location += VARYING_SLOT_VAR0 - VARYING_SLOT_TEX0;
}
}
}
/**
* Translates the NIR shader to RC instructions on the given compiler.
*
* This requires some lowering of the NIR shader to prepare it for
* translation. We take ownership of the NIR shader passed; if you need to
* keep the NIR, then pass us a clone.
*/
void
nir_to_rc(struct nir_shader *s, struct pipe_screen *screen,
struct r300_fragment_program_external_state state,
union r300_shader_code rc, struct radeon_compiler *compiler)
{
struct ntr_compile *c;
bool is_r500 = r300_screen(screen)->caps.is_r500;
c = rzalloc(NULL, struct ntr_compile);
c->compiler = compiler;
c->lower_fabs = !is_r500 && s->info.stage == MESA_SHADER_VERTEX;
c->addr_reg = ntr_writemask(ntr_dst_register(c, RC_FILE_ADDRESS, 0), RC_MASK_X);
util_dynarray_init(&c->immediates, c);
for (unsigned i = 0; i < ARRAY_SIZE(c->fs_output_color_index); i++)
c->fs_output_color_index[i] = -1;
c->fs_output_depth_index = -1;
for (unsigned i = 0; i < ARRAY_SIZE(c->fs_output_index); i++)
c->fs_output_index[i] = -1;
if (s->info.stage == MESA_SHADER_FRAGMENT) {
c->semantics = &rc.f->inputs;
} else {
c->semantics = &rc.v->outputs;
}
if (s->info.stage == MESA_SHADER_FRAGMENT) {
static const nir_lower_sysvals_to_varyings_options sysval_options = {
.frag_coord = true,
.point_coord = true,
.front_face = true,
};
NIR_PASS(_, s, nir_lower_sysvals_to_varyings, &sysval_options);
}
ntr_fixup_varying_slots(s, s->info.stage == MESA_SHADER_FRAGMENT ? nir_var_shader_in : nir_var_shader_out);
/* The fragment backend doesn't support relative addressing of input
* sources, so lower array indexing on FS inputs before nir_lower_io turns
* derefs into load_input offsets.
*/
if (s->info.stage == MESA_SHADER_FRAGMENT) {
NIR_PASS(_, s, nir_lower_indirect_derefs_to_if_else_trees,
nir_var_shader_in, UINT32_MAX);
NIR_PASS(_, s, nir_remove_dead_variables, nir_var_shader_in, NULL);
}
NIR_PASS(_, s, nir_lower_io, nir_var_shader_in | nir_var_shader_out, type_size,
nir_lower_io_use_interpolated_input_intrinsics);
if (s->info.stage == MESA_SHADER_FRAGMENT) {
/* Shadow lowering. */
int num_texture_states = state.sampler_state_count;
if (num_texture_states > 0) {
nir_lower_tex_shadow_swizzle tex_swizzle[PIPE_MAX_SHADER_SAMPLER_VIEWS];
enum compare_func tex_compare_func[PIPE_MAX_SHADER_SAMPLER_VIEWS];
for (unsigned i = 0; i < num_texture_states; i++) {
tex_compare_func[i] = state.unit[i].texture_compare_func;
tex_swizzle[i].swizzle_r = GET_SWZ(state.unit[i].texture_swizzle, 0);
tex_swizzle[i].swizzle_g = GET_SWZ(state.unit[i].texture_swizzle, 1);
tex_swizzle[i].swizzle_b = GET_SWZ(state.unit[i].texture_swizzle, 2);
tex_swizzle[i].swizzle_a = GET_SWZ(state.unit[i].texture_swizzle, 3);
}
NIR_PASS(_, s, nir_lower_tex_shadow, num_texture_states, tex_compare_func,
tex_swizzle, true);
}
nir_to_rc_lower_txp(s);
NIR_PASS(_, s, nir_to_rc_lower_tex);
}
bool progress;
do {
progress = false;
NIR_PASS(progress, s, nir_opt_algebraic);
NIR_PASS(progress, s, nir_opt_constant_folding);
} while (progress);
do {
progress = false;
NIR_PASS(progress, s, nir_opt_algebraic_late);
if (progress) {
NIR_PASS(_, s, nir_opt_copy_prop);
NIR_PASS(_, s, nir_opt_dce);
NIR_PASS(_, s, nir_opt_cse);
}
} while (progress);
if (s->info.stage == MESA_SHADER_FRAGMENT) {
NIR_PASS(_, s, r300_nir_prepare_presubtract);
}
NIR_PASS(_, s, nir_lower_int_to_float);
NIR_PASS(_, s, nir_opt_copy_prop);
NIR_PASS(_, s, r300_nir_post_integer_lowering);
NIR_PASS(_, s, nir_lower_bool_to_float,
is_r500 || s->info.stage == MESA_SHADER_FRAGMENT);
/* bool_to_float generates MOVs for b2f32 that we want to clean up. */
NIR_PASS(_, s, nir_opt_copy_prop);
/* CSE cleanup after late ftrunc lowering. */
NIR_PASS(_, s, nir_opt_cse);
/* At this point we need to clean;
* a) fcsel_gt that come from the ftrunc lowering on R300,
* b) all flavours of fcsels that read three different temp sources on R500.
*/
if (s->info.stage == MESA_SHADER_VERTEX) {
if (is_r500)
NIR_PASS(_, s, r300_nir_lower_fcsel_r500);
else
NIR_PASS(_, s, r300_nir_lower_fcsel_r300);
NIR_PASS(_, s, r300_nir_lower_flrp);
} else {
NIR_PASS(_, s, r300_nir_lower_comparison_fs);
}
NIR_PASS(_, s, r300_nir_opt_algebraic_late);
NIR_PASS(_, s, nir_opt_dce);
NIR_PASS(_, s, nir_opt_shrink_vectors, false);
NIR_PASS(_, s, nir_opt_dce);
nir_move_options move_all = nir_move_const_undef | nir_move_load_ubo | nir_move_load_input | nir_move_load_frag_coord |
nir_move_comparisons | nir_move_copies | nir_move_load_ssbo;
NIR_PASS(_, s, nir_opt_move, move_all);
NIR_PASS(_, s, nir_move_vec_src_uses_to_dest, true);
/* Late vectorizing after nir_move_vec_src_uses_to_dest helps instructions but
* increases register usage. Testing shows this is beneficial only in VS.
*/
if (s->info.stage == MESA_SHADER_VERTEX)
NIR_PASS(_, s, nir_opt_vectorize, ntr_should_vectorize_instr, NULL);
NIR_PASS(_, s, nir_convert_from_ssa, true, false);
NIR_PASS(_, s, nir_lower_vec_to_regs, NULL, NULL);
/* locals_to_reg_intrinsics will leave dead derefs that are good to clean up.
*/
NIR_PASS(_, s, nir_lower_locals_to_regs, 32);
NIR_PASS(_, s, nir_opt_dce);
/* See comment in ntr_get_alu_src for supported modifiers */
NIR_PASS(_, s, nir_legacy_trivialize, !c->lower_fabs);
if (NIR_DEBUG(TGSI)) {
fprintf(stderr, "NIR before translation to TGSI:\n");
nir_print_shader(s, stderr);
}
c->s = s;
/* Emit the main function */
nir_function_impl *impl = nir_shader_get_entrypoint(c->s);
ntr_emit_impl(c, impl);
/* For FS, populate the FS-specific compiler outputs. */
if (s->info.stage == MESA_SHADER_FRAGMENT) {
struct r300_fragment_program_compiler *fc =
(struct r300_fragment_program_compiler *)compiler;
rc.f->uses_discard = s->info.fs.uses_discard;
fc->OutputDepth = c->fs_output_depth_index >= 0 ? c->fs_output_depth_index
: c->num_outputs;
for (unsigned i = 0; i < ARRAY_SIZE(c->fs_output_color_index); i++) {
fc->OutputColor[i] = c->fs_output_color_index[i] >= 0
? c->fs_output_color_index[i]
: c->num_outputs;
}
} else if (s->info.stage == MESA_SHADER_VERTEX) {
rc.v->num_inputs = s->num_inputs;
}
rc_calculate_inputs_outputs(compiler);
ralloc_free(c);
ralloc_free(s);
}