X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fcompiler%2Fnir%2Fnir_gather_info.c;h=b6e6b0afd1f2da62c23445645302d6db8fef8778;hb=fd73ed1bd7e4d5a6af05d29908d6c4e2cca868b5;hp=380140ad5ce3f8ad80fa85b471056d4f7acbaf99;hpb=e1af20f18a86f52a9640faf2d4ff8a71b0a4fa9b;p=mesa.git diff --git a/src/compiler/nir/nir_gather_info.c b/src/compiler/nir/nir_gather_info.c index 380140ad5ce..b6e6b0afd1f 100644 --- a/src/compiler/nir/nir_gather_info.c +++ b/src/compiler/nir/nir_gather_info.c @@ -22,21 +22,231 @@ */ #include "nir.h" +#include "main/menums.h" static void -gather_intrinsic_info(nir_intrinsic_instr *instr, nir_shader *shader) +set_io_mask(nir_shader *shader, nir_variable *var, int offset, int len, + bool is_output_read) +{ + for (int i = 0; i < len; i++) { + assert(var->data.location != -1); + + int idx = var->data.location + offset + i; + bool is_patch_generic = var->data.patch && + idx != VARYING_SLOT_TESS_LEVEL_INNER && + idx != VARYING_SLOT_TESS_LEVEL_OUTER && + idx != VARYING_SLOT_BOUNDING_BOX0 && + idx != VARYING_SLOT_BOUNDING_BOX1; + uint64_t bitfield; + + if (is_patch_generic) { + assert(idx >= VARYING_SLOT_PATCH0 && idx < VARYING_SLOT_TESS_MAX); + bitfield = BITFIELD64_BIT(idx - VARYING_SLOT_PATCH0); + } + else { + assert(idx < VARYING_SLOT_MAX); + bitfield = BITFIELD64_BIT(idx); + } + + if (var->data.mode == nir_var_shader_in) { + if (is_patch_generic) + shader->info.patch_inputs_read |= bitfield; + else + shader->info.inputs_read |= bitfield; + + if (shader->info.stage == MESA_SHADER_FRAGMENT) { + shader->info.fs.uses_sample_qualifier |= var->data.sample; + } + } else { + assert(var->data.mode == nir_var_shader_out); + if (is_output_read) { + if (is_patch_generic) { + shader->info.patch_outputs_read |= bitfield; + } else { + shader->info.outputs_read |= bitfield; + } + } else { + if (is_patch_generic) { + shader->info.patch_outputs_written |= bitfield; + } else if (!var->data.read_only) { + shader->info.outputs_written |= bitfield; + } + } + + + if (var->data.fb_fetch_output) + shader->info.outputs_read |= bitfield; + } + } +} + +/** + * Mark an entire variable as used. Caller must ensure that the variable + * represents a shader input or output. + */ +static void +mark_whole_variable(nir_shader *shader, nir_variable *var, bool is_output_read) +{ + const struct glsl_type *type = var->type; + + if (nir_is_per_vertex_io(var, shader->info.stage)) { + assert(glsl_type_is_array(type)); + type = glsl_get_array_element(type); + } + + const unsigned slots = + var->data.compact ? DIV_ROUND_UP(glsl_get_length(type), 4) + : glsl_count_attribute_slots(type, false); + + set_io_mask(shader, var, 0, slots, is_output_read); +} + +static unsigned +get_io_offset(nir_deref_instr *deref, bool is_vertex_input) +{ + unsigned offset = 0; + + for (nir_deref_instr *d = deref; d; d = nir_deref_instr_parent(d)) { + if (d->deref_type == nir_deref_type_array) { + if (!nir_src_is_const(d->arr.index)) + return -1; + + offset += glsl_count_attribute_slots(d->type, is_vertex_input) * + nir_src_as_uint(d->arr.index); + } + /* TODO: we can get the offset for structs here see nir_lower_io() */ + } + + return offset; +} + +/** + * Try to mark a portion of the given varying as used. Caller must ensure + * that the variable represents a shader input or output. + * + * If the index can't be interpreted as a constant, or some other problem + * occurs, then nothing will be marked and false will be returned. + */ +static bool +try_mask_partial_io(nir_shader *shader, nir_variable *var, + nir_deref_instr *deref, bool is_output_read) +{ + const struct glsl_type *type = var->type; + + if (nir_is_per_vertex_io(var, shader->info.stage)) { + assert(glsl_type_is_array(type)); + type = glsl_get_array_element(type); + } + + /* The code below only handles: + * + * - Indexing into matrices + * - Indexing into arrays of (arrays, matrices, vectors, or scalars) + * + * For now, we just give up if we see varying structs and arrays of structs + * here marking the entire variable as used. + */ + if (!(glsl_type_is_matrix(type) || + (glsl_type_is_array(type) && !var->data.compact && + (glsl_type_is_numeric(glsl_without_array(type)) || + glsl_type_is_boolean(glsl_without_array(type)))))) { + + /* If we don't know how to handle this case, give up and let the + * caller mark the whole variable as used. + */ + return false; + } + + unsigned offset = get_io_offset(deref, false); + if (offset == -1) + return false; + + unsigned num_elems; + unsigned elem_width = 1; + unsigned mat_cols = 1; + if (glsl_type_is_array(type)) { + num_elems = glsl_get_aoa_size(type); + if (glsl_type_is_matrix(glsl_without_array(type))) + mat_cols = glsl_get_matrix_columns(glsl_without_array(type)); + } else { + num_elems = glsl_get_matrix_columns(type); + } + + /* double element width for double types that takes two slots */ + if (glsl_type_is_dual_slot(glsl_without_array(type))) + elem_width *= 2; + + if (offset >= num_elems * elem_width * mat_cols) { + /* Constant index outside the bounds of the matrix/array. This could + * arise as a result of constant folding of a legal GLSL program. + * + * Even though the spec says that indexing outside the bounds of a + * matrix/array results in undefined behaviour, we don't want to pass + * out-of-range values to set_io_mask() (since this could result in + * slots that don't exist being marked as used), so just let the caller + * mark the whole variable as used. + */ + return false; + } + + set_io_mask(shader, var, offset, elem_width, is_output_read); + return true; +} + +static void +gather_intrinsic_info(nir_intrinsic_instr *instr, nir_shader *shader, + void *dead_ctx) { switch (instr->intrinsic) { + case nir_intrinsic_demote: + case nir_intrinsic_demote_if: case nir_intrinsic_discard: case nir_intrinsic_discard_if: - assert(shader->stage == MESA_SHADER_FRAGMENT); - shader->info->fs.uses_discard = true; + assert(shader->info.stage == MESA_SHADER_FRAGMENT); + shader->info.fs.uses_discard = true; + break; + + case nir_intrinsic_interp_deref_at_centroid: + case nir_intrinsic_interp_deref_at_sample: + case nir_intrinsic_interp_deref_at_offset: + case nir_intrinsic_load_deref: + case nir_intrinsic_store_deref:{ + nir_deref_instr *deref = nir_src_as_deref(instr->src[0]); + if (deref->mode == nir_var_shader_in || + deref->mode == nir_var_shader_out) { + nir_variable *var = nir_deref_instr_get_variable(deref); + bool is_output_read = false; + if (var->data.mode == nir_var_shader_out && + instr->intrinsic == nir_intrinsic_load_deref) + is_output_read = true; + + if (!try_mask_partial_io(shader, var, deref, is_output_read)) + mark_whole_variable(shader, var, is_output_read); + + /* We need to track which input_reads bits correspond to a + * dvec3/dvec4 input attribute */ + if (shader->info.stage == MESA_SHADER_VERTEX && + var->data.mode == nir_var_shader_in && + glsl_type_is_dual_slot(glsl_without_array(var->type))) { + for (unsigned i = 0; i < glsl_count_attribute_slots(var->type, false); i++) { + int idx = var->data.location + i; + shader->info.vs.double_inputs |= BITFIELD64_BIT(idx); + } + } + } break; + } + case nir_intrinsic_load_draw_id: + case nir_intrinsic_load_frag_coord: + case nir_intrinsic_load_point_coord: case nir_intrinsic_load_front_face: case nir_intrinsic_load_vertex_id: case nir_intrinsic_load_vertex_id_zero_base: case nir_intrinsic_load_base_vertex: + case nir_intrinsic_load_first_vertex: + case nir_intrinsic_load_is_indexed_draw: + case nir_intrinsic_load_base_instance: case nir_intrinsic_load_instance_id: case nir_intrinsic_load_sample_id: case nir_intrinsic_load_sample_pos: @@ -47,14 +257,32 @@ gather_intrinsic_info(nir_intrinsic_instr *instr, nir_shader *shader) case nir_intrinsic_load_local_invocation_index: case nir_intrinsic_load_work_group_id: case nir_intrinsic_load_num_work_groups: - shader->info->system_values_read |= - (1 << nir_system_value_from_intrinsic(instr->intrinsic)); + case nir_intrinsic_load_tess_coord: + case nir_intrinsic_load_tess_level_outer: + case nir_intrinsic_load_tess_level_inner: + case nir_intrinsic_load_patch_vertices_in: + shader->info.system_values_read |= + (1ull << nir_system_value_from_intrinsic(instr->intrinsic)); + break; + + case nir_intrinsic_quad_broadcast: + case nir_intrinsic_quad_swap_horizontal: + case nir_intrinsic_quad_swap_vertical: + case nir_intrinsic_quad_swap_diagonal: + if (shader->info.stage == MESA_SHADER_FRAGMENT) + shader->info.fs.needs_helper_invocations = true; break; case nir_intrinsic_end_primitive: case nir_intrinsic_end_primitive_with_counter: - assert(shader->stage == MESA_SHADER_GEOMETRY); - shader->info->gs.uses_end_primitive = 1; + assert(shader->info.stage == MESA_SHADER_GEOMETRY); + shader->info.gs.uses_end_primitive = 1; + /* fall through */ + + case nir_intrinsic_emit_vertex: + if (nir_intrinsic_stream_id(instr) > 0) + shader->info.gs.uses_streams = true; + break; default: @@ -65,17 +293,55 @@ gather_intrinsic_info(nir_intrinsic_instr *instr, nir_shader *shader) static void gather_tex_info(nir_tex_instr *instr, nir_shader *shader) { - if (instr->op == nir_texop_tg4) - shader->info->uses_texture_gather = true; + if (shader->info.stage == MESA_SHADER_FRAGMENT && + nir_tex_instr_has_implicit_derivative(instr)) + shader->info.fs.needs_helper_invocations = true; + + switch (instr->op) { + case nir_texop_tg4: + shader->info.uses_texture_gather = true; + break; + default: + break; + } } static void -gather_info_block(nir_block *block, nir_shader *shader) +gather_alu_info(nir_alu_instr *instr, nir_shader *shader) +{ + switch (instr->op) { + case nir_op_fddx: + case nir_op_fddy: + shader->info.uses_fddx_fddy = true; + /* Fall through */ + case nir_op_fddx_fine: + case nir_op_fddy_fine: + case nir_op_fddx_coarse: + case nir_op_fddy_coarse: + if (shader->info.stage == MESA_SHADER_FRAGMENT) + shader->info.fs.needs_helper_invocations = true; + break; + default: + break; + } + + shader->info.uses_64bit |= instr->dest.dest.ssa.bit_size == 64; + unsigned num_srcs = nir_op_infos[instr->op].num_inputs; + for (unsigned i = 0; i < num_srcs; i++) { + shader->info.uses_64bit |= nir_src_bit_size(instr->src[i].src) == 64; + } +} + +static void +gather_info_block(nir_block *block, nir_shader *shader, void *dead_ctx) { nir_foreach_instr(instr, block) { switch (instr->type) { + case nir_instr_type_alu: + gather_alu_info(nir_instr_as_alu(instr), shader); + break; case nir_instr_type_intrinsic: - gather_intrinsic_info(nir_instr_as_intrinsic(instr), shader); + gather_intrinsic_info(nir_instr_as_intrinsic(instr), shader, dead_ctx); break; case nir_instr_type_tex: gather_tex_info(nir_instr_as_tex(instr), shader); @@ -89,80 +355,33 @@ gather_info_block(nir_block *block, nir_shader *shader) } } -/** - * Returns the bits in the inputs_read, outputs_written, or - * system_values_read bitfield corresponding to this variable. - */ -static inline uint64_t -get_io_mask(nir_variable *var, gl_shader_stage stage) -{ - assert(var->data.mode == nir_var_shader_in || - var->data.mode == nir_var_shader_out || - var->data.mode == nir_var_system_value); - assert(var->data.location >= 0); - - const struct glsl_type *var_type = var->type; - if (stage == MESA_SHADER_GEOMETRY && var->data.mode == nir_var_shader_in) { - /* Most geometry shader inputs are per-vertex arrays */ - if (var->data.location >= VARYING_SLOT_VAR0) - assert(glsl_type_is_array(var_type)); - - if (glsl_type_is_array(var_type)) - var_type = glsl_get_array_element(var_type); - } - - bool is_vertex_input = (var->data.mode == nir_var_shader_in && - stage == MESA_SHADER_VERTEX); - unsigned slots = glsl_count_attribute_slots(var_type, is_vertex_input); - return ((1ull << slots) - 1) << var->data.location; -} - void nir_shader_gather_info(nir_shader *shader, nir_function_impl *entrypoint) { - /* This pass does not yet support tessellation shaders */ - assert(shader->stage == MESA_SHADER_VERTEX || - shader->stage == MESA_SHADER_GEOMETRY || - shader->stage == MESA_SHADER_FRAGMENT || - shader->stage == MESA_SHADER_COMPUTE); - - bool uses_sample_qualifier = false; - shader->info->inputs_read = 0; - foreach_list_typed(nir_variable, var, node, &shader->inputs) { - shader->info->inputs_read |= get_io_mask(var, shader->stage); - uses_sample_qualifier |= var->data.sample; - } - - if (shader->stage == MESA_SHADER_FRAGMENT) - shader->info->fs.uses_sample_qualifier = uses_sample_qualifier; - - /* TODO: Some day we may need to add stream support to NIR */ - shader->info->outputs_written = 0; - foreach_list_typed(nir_variable, var, node, &shader->outputs) - shader->info->outputs_written |= get_io_mask(var, shader->stage); - - shader->info->system_values_read = 0; - foreach_list_typed(nir_variable, var, node, &shader->system_values) - shader->info->system_values_read |= get_io_mask(var, shader->stage); - - shader->info->num_textures = 0; - shader->info->num_images = 0; + shader->info.num_textures = 0; + shader->info.num_images = 0; nir_foreach_variable(var, &shader->uniforms) { - const struct glsl_type *type = var->type; - unsigned count = 1; - if (glsl_type_is_array(type)) { - count = glsl_get_length(type); - type = glsl_get_array_element(type); - } + shader->info.num_textures += glsl_type_get_sampler_count(var->type); + shader->info.num_images += glsl_type_get_image_count(var->type); + } - if (glsl_type_is_image(type)) { - shader->info->num_images += count; - } else if (glsl_type_is_sampler(type)) { - shader->info->num_textures += count; - } + shader->info.inputs_read = 0; + shader->info.outputs_written = 0; + shader->info.outputs_read = 0; + shader->info.patch_outputs_read = 0; + shader->info.patch_inputs_read = 0; + shader->info.patch_outputs_written = 0; + shader->info.system_values_read = 0; + if (shader->info.stage == MESA_SHADER_VERTEX) { + shader->info.vs.double_inputs = 0; + } + if (shader->info.stage == MESA_SHADER_FRAGMENT) { + shader->info.fs.uses_sample_qualifier = false; } + void *dead_ctx = ralloc_context(NULL); nir_foreach_block(block, entrypoint) { - gather_info_block(block, shader); + gather_info_block(block, shader, dead_ctx); } + ralloc_free(dead_ctx); }