[Mesa-dev] [PATCH 1/4] nir: add new convergent, uniform-control, and cross-thread attributes
Matt Turner
mattst88 at gmail.com
Tue Aug 1 03:40:30 UTC 2017
On Mon, Jul 31, 2017 at 7:02 PM, Connor Abbott
<connora at valvesoftware.com> wrote:
> From: Connor Abbott <cwabbott0 at gmail.com>
>
> These are properties of the instruction that must be respected when
> moving it around, in addition to the usual SSA dominance guarantee.
> Previously, we only had special handling for fddx and fddy, in a very
> ad-hoc way. But with arb_shader_ballot and arb_shader_group_vote, we'll
> have to start handling a lot more instructions with similar constraints,
> so we want to add a more formal model of what optimizations can and
> cannot do.
>
> v2: don't add attribute for ALU instructions
> v3: special-case derivative ALU instructions
> v4: rename convergent to uniform-control, and add LLVM-style convergent
> attribute
> ---
> src/compiler/nir/nir.h | 126 +++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 126 insertions(+)
>
> diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h
> index 9313b7a..24934f0 100644
> --- a/src/compiler/nir/nir.h
> +++ b/src/compiler/nir/nir.h
> @@ -986,6 +986,39 @@ typedef enum {
> * intrinsic are due to the register reads/writes.
> */
> NIR_INTRINSIC_CAN_REORDER = (1 << 1),
> +
> + /**
> + * Indicates whether this intrinsic is "convergent". An operation is
> + * convergent if results from one thread depend on results from another
> + * thread, but in such a way that additional threads being enabled doesn't
> + * affect the result of the operation. Examples of convergent operations
> + * include screen-space derivatives, readInvocation() from
> + * ARB_shader_ballot, etc. Note that this is a more precise version of
> + * LLVM's "convergent" attribute, which simply stipulates that control
> + * dependencies cannot be added, since the set of active threads can only be
> + * reduced by adding control dependencies.
> + */
> + NIR_INTRINSIC_CONVERGENT = (1 << 2),
> +
> + /**
> + * Indicates whether this intrinsic is "cross-thread". An operation is
> + * cross-thread if results in one thread depend on the set of active threads
> + * when it is executed, as well as possibly the input value of the other
> + * threads, and therefore optimizations cannot change the execution mask
> + * when the operation is called. Examples of cross-thread operations include
> + * the "any" reduction which returns "true" in all threads if any thread
> + * inputs "true", ballotARB() from ARB_shader_ballot, etc. Note that any
> + * cross-thread operation must be convergent.
> + */
> + NIR_INTRINSIC_CROSS_THREAD = (1 << 3),
> +
> + /**
> + * Indicates that this intrinsic is guaranteed to always be called in
> + * uniform control flow, that is, control flow with the same execution mask
> + * as when the program started. If an operation is uniform-control, it must
> + * be convergent as well, since the optimizer must maintain the guarantee.
> + */
> + NIR_INTRINSIC_UNIFORM_CONTROL = (1 << 4),
> } nir_intrinsic_semantic_flag;
>
> /**
> @@ -1460,6 +1493,99 @@ NIR_DEFINE_CAST(nir_instr_as_parallel_copy, nir_instr,
> type, nir_instr_type_parallel_copy)
>
> /*
> + * Helpers to determine if an instruction is cross-thread, convergent, or
> + * uniform-control. See NIR_INTRINSIC_{CONVERGENT|CROSS_THREAD|UNIFORM_CONTROL}
> + * for the definitions.
> + */
> +static inline bool
> +nir_instr_is_uniform_control(const nir_instr *instr)
> +{
> + switch (instr->type) {
> + case nir_instr_type_alu:
> + switch (nir_instr_as_alu(instr)->op) {
> + case nir_op_fddx:
> + case nir_op_fddy:
> + case nir_op_fddx_fine:
> + case nir_op_fddy_fine:
> + case nir_op_fddx_coarse:
> + case nir_op_fddy_coarse:
> + /* Section 8.13.1 (Derivative Functions) of the GLSL 4.50 spec says:
> + *
> + * "Derivatives are undefined within non-uniform control flow."
> + *
> + * Thus, we can assume they are called in uniform control flow.
> + */
> + return true;
> +
> + default:
> + return false;
> + }
> +
> + case nir_instr_type_intrinsic: {
> + nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
> + return nir_intrinsic_infos[intrin->intrinsic].flags &
> + NIR_INTRINSIC_UNIFORM_CONTROL;
> + }
> +
> + case nir_instr_type_tex:
> + switch (nir_instr_as_tex(instr)->op) {
> + case nir_texop_tex:
> + case nir_texop_txb:
> + case nir_texop_lod:
> + /* These three take implicit derivatives, so they are
> + * uniform-control as well.
> + */
> + return true;
> +
> + default:
> + return false;
> + }
This block is indented too much.
> +
> + default:
> + return false;
> + }
> +}
> +
> +static inline bool
> +nir_instr_is_cross_thread(const nir_instr *instr)
> +{
> + switch (instr->type) {
> + case nir_instr_type_intrinsic: {
> + nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
> + return nir_intrinsic_infos[intrin->intrinsic].flags &
> + NIR_INTRINSIC_CROSS_THREAD;
Please align this with the first operand.
More information about the mesa-dev
mailing list