spirv: Allow pointers to have a deref at the base

Previously, pointers fell into two categories: index/offset for UBOs,
SSBOs, etc. and var + access chain for logical pointers.  This commit
adds another logical pointer mode that's deref + access chain.

It's tempting to think that we can just replace variable-based pointers
with deref-based or at least replace the access chain with a deref
chain.  Unfortunately, there are a few sticky bits that prevent this:

 1) We can't return deref-based pointers from OpVariable because those
    opcodes may come outside of a function so there's no place to emit
    the deref instructions.

 2) We can't always use variable-based pointers because we may not
    always know the variable.  (We do now, but he upcoming function
    rework will take that option away.)

 3) We also can't replace the access chain struct with a deref.  Due to
    the re-ordering we do in order to handle loop continues, the derefs
    we would emit as part of OpAccessChain may not dominate their uses.
    We normally fix this up with nir_repair_ssa but that generates phi
    nodes which we don't want in the middle of our deref chains.

All in all, we have no real better option than to support partial access
chains while also re-emitting the deref instructions on the spot.

Acked-by: Rob Clark <robdclark@gmail.com>
Acked-by: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Acked-by: Dave Airlie <airlied@redhat.com>
Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
This commit is contained in:
Jason Ekstrand
2018-03-23 08:22:54 -07:00
parent fdd5ffee32
commit d5930c222c
2 changed files with 23 additions and 43 deletions

View File

@@ -433,10 +433,17 @@ struct vtn_pointer {
/** The referenced variable, if known /** The referenced variable, if known
* *
* This field may be NULL if the pointer uses a (block_index, offset) pair * This field may be NULL if the pointer uses a (block_index, offset) pair
* instead of an access chain. * instead of an access chain or if the access chain starts at a deref.
*/ */
struct vtn_variable *var; struct vtn_variable *var;
/** The deref at the base of the chain
*
* This field may be NULL if the pointer uses a (block_index, offset) pair
* instead of an access chain or if the access chain starts at a variable.
*/
nir_deref_instr *deref;
/** An access chain describing how to get from var to the referenced data /** An access chain describing how to get from var to the referenced data
* *
* This field may be NULL if the pointer references the entire variable or * This field may be NULL if the pointer references the entire variable or

View File

@@ -112,6 +112,7 @@ vtn_access_chain_pointer_dereference(struct vtn_builder *b,
ptr->mode = base->mode; ptr->mode = base->mode;
ptr->type = type; ptr->type = type;
ptr->var = base->var; ptr->var = base->var;
ptr->deref = base->deref;
ptr->chain = chain; ptr->chain = chain;
return ptr; return ptr;
@@ -377,46 +378,30 @@ nir_deref_instr *
vtn_pointer_to_deref(struct vtn_builder *b, struct vtn_pointer *ptr) vtn_pointer_to_deref(struct vtn_builder *b, struct vtn_pointer *ptr)
{ {
/* Do on-the-fly copy propagation for samplers. */ /* Do on-the-fly copy propagation for samplers. */
if (ptr->var->copy_prop_sampler) if (ptr->var && ptr->var->copy_prop_sampler)
return vtn_pointer_to_deref(b, ptr->var->copy_prop_sampler); return vtn_pointer_to_deref(b, ptr->var->copy_prop_sampler);
nir_deref_instr *deref_var = nir_deref_instr *tail;
nir_deref_instr_create(b->nb.shader, nir_deref_type_var); if (ptr->deref) {
nir_ssa_dest_init(&deref_var->instr, &deref_var->dest, 1, 32, NULL); tail = ptr->deref;
nir_builder_instr_insert(&b->nb, &deref_var->instr); } else {
assert(ptr->var && ptr->var->var);
tail = nir_build_deref_var(&b->nb, ptr->var->var);
}
assert(ptr->var->var);
deref_var->mode = ptr->var->var->data.mode;
deref_var->type = ptr->var->var->type;
deref_var->var = ptr->var->var;
/* Raw variable access */ /* Raw variable access */
if (!ptr->chain) if (!ptr->chain)
return deref_var; return tail;
struct vtn_access_chain *chain = ptr->chain; struct vtn_access_chain *chain = ptr->chain;
vtn_assert(chain); vtn_assert(chain);
struct vtn_type *deref_type = ptr->var->type;
nir_deref_instr *tail = deref_var;
for (unsigned i = 0; i < chain->length; i++) { for (unsigned i = 0; i < chain->length; i++) {
enum glsl_base_type base_type = glsl_get_base_type(deref_type->type); if (glsl_type_is_struct(tail->type)) {
switch (base_type) { vtn_assert(chain->link[i].mode == vtn_access_mode_literal);
case GLSL_TYPE_UINT: unsigned idx = chain->link[i].id;
case GLSL_TYPE_INT: tail = nir_build_deref_struct(&b->nb, tail, idx);
case GLSL_TYPE_UINT16: } else {
case GLSL_TYPE_INT16:
case GLSL_TYPE_UINT8:
case GLSL_TYPE_INT8:
case GLSL_TYPE_UINT64:
case GLSL_TYPE_INT64:
case GLSL_TYPE_FLOAT:
case GLSL_TYPE_FLOAT16:
case GLSL_TYPE_DOUBLE:
case GLSL_TYPE_BOOL:
case GLSL_TYPE_ARRAY: {
deref_type = deref_type->array_element;
nir_ssa_def *index; nir_ssa_def *index;
if (chain->link[i].mode == vtn_access_mode_literal) { if (chain->link[i].mode == vtn_access_mode_literal) {
index = nir_imm_int(&b->nb, chain->link[i].id); index = nir_imm_int(&b->nb, chain->link[i].id);
@@ -425,18 +410,6 @@ vtn_pointer_to_deref(struct vtn_builder *b, struct vtn_pointer *ptr)
index = vtn_ssa_value(b, chain->link[i].id)->def; index = vtn_ssa_value(b, chain->link[i].id)->def;
} }
tail = nir_build_deref_array(&b->nb, tail, index); tail = nir_build_deref_array(&b->nb, tail, index);
break;
}
case GLSL_TYPE_STRUCT: {
vtn_assert(chain->link[i].mode == vtn_access_mode_literal);
unsigned idx = chain->link[i].id;
deref_type = deref_type->members[idx];
tail = nir_build_deref_struct(&b->nb, tail, idx);
break;
}
default:
vtn_fail("Invalid type for deref");
} }
} }