[Mesa-dev] [PATCH v3] glsl: Use a separate div_to_mul_rcp lowering flag for integers.

Ian Romanick idr at freedesktop.org
Mon Aug 29 11:10:21 PDT 2011


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 08/29/2011 10:52 AM, Kenneth Graunke wrote:
> From: Bryan Cain <bryancain3 at gmail.com>
> 
> Using multiply and reciprocal for integer division involves potentially
> lossy floating point conversions.  This is okay for older GPUs that
> represent integers as floating point, but undesirable for GPUs with
> native integer division instructions.
> 
> TGSI, for example, has UDIV/IDIV instructions for integer division,
> so it makes sense to handle this directly.  Likewise for i965.
> 
> Signed-off-by: Bryan Cain <bryancain3 at gmail.com>
> Signed-off-by: Kenneth Graunke <kenneth at whitecape.org>

Reviewed-by: Ian Romanick <ian.d.romanick at intel.com>

> ---
> v3: Check for int/float explicitly, fixing a mistake where div_to_mul_rcp
>     would be run on integers when !lowering(INT_DIV_TO_MUL_RCP) and
>     lowering(DIV_TO_MUL_RCP).
> 
>  src/glsl/ir_optimization.h                 |   13 ++--
>  src/glsl/lower_instructions.cpp            |  125 ++++++++++++++++------------
>  src/mesa/drivers/dri/i965/brw_shader.cpp   |    1 +
>  src/mesa/program/ir_to_mesa.cpp            |    2 +-
>  src/mesa/state_tracker/st_glsl_to_tgsi.cpp |    2 +-
>  5 files changed, 80 insertions(+), 63 deletions(-)
> 
> diff --git a/src/glsl/ir_optimization.h b/src/glsl/ir_optimization.h
> index f7808bd..48448d4 100644
> --- a/src/glsl/ir_optimization.h
> +++ b/src/glsl/ir_optimization.h
> @@ -29,12 +29,13 @@
>   */
>  
>  /* Operations for lower_instructions() */
> -#define SUB_TO_ADD_NEG 0x01
> -#define DIV_TO_MUL_RCP 0x02
> -#define EXP_TO_EXP2    0x04
> -#define POW_TO_EXP2    0x08
> -#define LOG_TO_LOG2    0x10
> -#define MOD_TO_FRACT   0x20
> +#define SUB_TO_ADD_NEG     0x01
> +#define DIV_TO_MUL_RCP     0x02
> +#define EXP_TO_EXP2        0x04
> +#define POW_TO_EXP2        0x08
> +#define LOG_TO_LOG2        0x10
> +#define MOD_TO_FRACT       0x20
> +#define INT_DIV_TO_MUL_RCP 0x40
>  
>  bool do_common_optimization(exec_list *ir, bool linked, unsigned max_unroll_iterations);
>  
> diff --git a/src/glsl/lower_instructions.cpp b/src/glsl/lower_instructions.cpp
> index 23aa19b..d79eb0a 100644
> --- a/src/glsl/lower_instructions.cpp
> +++ b/src/glsl/lower_instructions.cpp
> @@ -32,6 +32,7 @@
>   * Currently supported transformations:
>   * - SUB_TO_ADD_NEG
>   * - DIV_TO_MUL_RCP
> + * - INT_DIV_TO_MUL_RCP
>   * - EXP_TO_EXP2
>   * - POW_TO_EXP2
>   * - LOG_TO_LOG2
> @@ -47,8 +48,8 @@
>   * want to recognize add(op0, neg(op1)) or the other way around to
>   * produce a subtract anyway.
>   *
> - * DIV_TO_MUL_RCP:
> - * ---------------
> + * DIV_TO_MUL_RCP and INT_DIV_TO_MUL_RCP:
> + * --------------------------------------
>   * Breaks an ir_unop_div expression down to op0 * (rcp(op1)).
>   *
>   * Many GPUs don't have a divide instruction (945 and 965 included),
> @@ -56,6 +57,10 @@
>   * reciprocal.  By breaking the operation down, constant reciprocals
>   * can get constant folded.
>   *
> + * DIV_TO_MUL_RCP only lowers floating point division; INT_DIV_TO_MUL_RCP
> + * handles the integer case, converting to and from floating point so that
> + * RCP is possible.
> + *
>   * EXP_TO_EXP2 and LOG_TO_LOG2:
>   * ----------------------------
>   * Many GPUs don't have a base e log or exponent instruction, but they
> @@ -95,6 +100,7 @@ private:
>  
>     void sub_to_add_neg(ir_expression *);
>     void div_to_mul_rcp(ir_expression *);
> +   void int_div_to_mul_rcp(ir_expression *);
>     void mod_to_fract(ir_expression *);
>     void exp_to_exp2(ir_expression *);
>     void pow_to_exp2(ir_expression *);
> @@ -127,60 +133,67 @@ lower_instructions_visitor::sub_to_add_neg(ir_expression *ir)
>  void
>  lower_instructions_visitor::div_to_mul_rcp(ir_expression *ir)
>  {
> -   if (!ir->operands[1]->type->is_integer()) {
> -      /* New expression for the 1.0 / op1 */
> -      ir_rvalue *expr;
> -      expr = new(ir) ir_expression(ir_unop_rcp,
> -				   ir->operands[1]->type,
> -				   ir->operands[1],
> -				   NULL);
> -
> -      /* op0 / op1 -> op0 * (1.0 / op1) */
> -      ir->operation = ir_binop_mul;
> -      ir->operands[1] = expr;
> +   assert(ir->operands[1]->type->is_float());
> +
> +   /* New expression for the 1.0 / op1 */
> +   ir_rvalue *expr;
> +   expr = new(ir) ir_expression(ir_unop_rcp,
> +				ir->operands[1]->type,
> +				ir->operands[1]);
> +
> +   /* op0 / op1 -> op0 * (1.0 / op1) */
> +   ir->operation = ir_binop_mul;
> +   ir->operands[1] = expr;
> +
> +   this->progress = true;
> +}
> +
> +void
> +lower_instructions_visitor::int_div_to_mul_rcp(ir_expression *ir)
> +{
> +   assert(ir->operands[1]->type->is_integer());
> +
> +   /* Be careful with integer division -- we need to do it as a
> +    * float and re-truncate, since rcp(n > 1) of an integer would
> +    * just be 0.
> +    */
> +   ir_rvalue *op0, *op1;
> +   const struct glsl_type *vec_type;
> +
> +   vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
> +				      ir->operands[1]->type->vector_elements,
> +				      ir->operands[1]->type->matrix_columns);
> +
> +   if (ir->operands[1]->type->base_type == GLSL_TYPE_INT)
> +      op1 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[1], NULL);
> +   else
> +      op1 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[1], NULL);
> +
> +   op1 = new(ir) ir_expression(ir_unop_rcp, op1->type, op1, NULL);
> +
> +   vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
> +				      ir->operands[0]->type->vector_elements,
> +				      ir->operands[0]->type->matrix_columns);
> +
> +   if (ir->operands[0]->type->base_type == GLSL_TYPE_INT)
> +      op0 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[0], NULL);
> +   else
> +      op0 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[0], NULL);
> +
> +   vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
> +				      ir->type->vector_elements,
> +				      ir->type->matrix_columns);
> +
> +   op0 = new(ir) ir_expression(ir_binop_mul, vec_type, op0, op1);
> +
> +   if (ir->operands[1]->type->base_type == GLSL_TYPE_INT) {
> +      ir->operation = ir_unop_f2i;
> +      ir->operands[0] = op0;
>     } else {
> -      /* Be careful with integer division -- we need to do it as a
> -       * float and re-truncate, since rcp(n > 1) of an integer would
> -       * just be 0.
> -       */
> -      ir_rvalue *op0, *op1;
> -      const struct glsl_type *vec_type;
> -
> -      vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
> -					 ir->operands[1]->type->vector_elements,
> -					 ir->operands[1]->type->matrix_columns);
> -
> -      if (ir->operands[1]->type->base_type == GLSL_TYPE_INT)
> -	 op1 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[1], NULL);
> -      else
> -	 op1 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[1], NULL);
> -
> -      op1 = new(ir) ir_expression(ir_unop_rcp, op1->type, op1, NULL);
> -
> -      vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
> -					 ir->operands[0]->type->vector_elements,
> -					 ir->operands[0]->type->matrix_columns);
> -
> -      if (ir->operands[0]->type->base_type == GLSL_TYPE_INT)
> -	 op0 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[0], NULL);
> -      else
> -	 op0 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[0], NULL);
> -
> -      vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
> -					 ir->type->vector_elements,
> -					 ir->type->matrix_columns);
> -
> -      op0 = new(ir) ir_expression(ir_binop_mul, vec_type, op0, op1);
> -
> -      if (ir->operands[1]->type->base_type == GLSL_TYPE_INT) {
> -	 ir->operation = ir_unop_f2i;
> -	 ir->operands[0] = op0;
> -      } else {
> -	 ir->operation = ir_unop_i2u;
> -	 ir->operands[0] = new(ir) ir_expression(ir_unop_f2i, op0);
> -      }
> -      ir->operands[1] = NULL;
> +      ir->operation = ir_unop_i2u;
> +      ir->operands[0] = new(ir) ir_expression(ir_unop_f2i, op0);
>     }
> +   ir->operands[1] = NULL;
>  
>     this->progress = true;
>  }
> @@ -265,7 +278,9 @@ lower_instructions_visitor::visit_leave(ir_expression *ir)
>        break;
>  
>     case ir_binop_div:
> -      if (lowering(DIV_TO_MUL_RCP))
> +      if (ir->operands[1]->type->is_integer() && lowering(INT_DIV_TO_MUL_RCP))
> +	 int_div_to_mul_rcp(ir);
> +      else if (ir->operands[1]->type->is_float() && lowering(DIV_TO_MUL_RCP))
>  	 div_to_mul_rcp(ir);
>        break;
>  
> diff --git a/src/mesa/drivers/dri/i965/brw_shader.cpp b/src/mesa/drivers/dri/i965/brw_shader.cpp
> index 3ff6bba..7e53097 100644
> --- a/src/mesa/drivers/dri/i965/brw_shader.cpp
> +++ b/src/mesa/drivers/dri/i965/brw_shader.cpp
> @@ -100,6 +100,7 @@ brw_link_shader(struct gl_context *ctx, struct gl_shader_program *prog)
>        lower_instructions(shader->ir,
>  			 MOD_TO_FRACT |
>  			 DIV_TO_MUL_RCP |
> +			 INT_DIV_TO_MUL_RCP |
>  			 SUB_TO_ADD_NEG |
>  			 EXP_TO_EXP2 |
>  			 LOG_TO_LOG2);
> diff --git a/src/mesa/program/ir_to_mesa.cpp b/src/mesa/program/ir_to_mesa.cpp
> index 6820e4c..dd154db 100644
> --- a/src/mesa/program/ir_to_mesa.cpp
> +++ b/src/mesa/program/ir_to_mesa.cpp
> @@ -3232,7 +3232,7 @@ _mesa_ir_link_shader(struct gl_context *ctx, struct gl_shader_program *prog)
>  	 /* Lowering */
>  	 do_mat_op_to_vec(ir);
>  	 lower_instructions(ir, (MOD_TO_FRACT | DIV_TO_MUL_RCP | EXP_TO_EXP2
> -				 | LOG_TO_LOG2
> +				 | LOG_TO_LOG2 | INT_DIV_TO_MUL_RCP
>  				 | ((options->EmitNoPow) ? POW_TO_EXP2 : 0)));
>  
>  	 progress = do_lower_jumps(ir, true, true, options->EmitNoMainReturn, options->EmitNoCont, options->EmitNoLoops) || progress;
> diff --git a/src/mesa/state_tracker/st_glsl_to_tgsi.cpp b/src/mesa/state_tracker/st_glsl_to_tgsi.cpp
> index 9cac309..ec42742 100644
> --- a/src/mesa/state_tracker/st_glsl_to_tgsi.cpp
> +++ b/src/mesa/state_tracker/st_glsl_to_tgsi.cpp
> @@ -5012,7 +5012,7 @@ st_link_shader(struct gl_context *ctx, struct gl_shader_program *prog)
>           /* Lowering */
>           do_mat_op_to_vec(ir);
>           lower_instructions(ir, (MOD_TO_FRACT | DIV_TO_MUL_RCP | EXP_TO_EXP2
> -        			 | LOG_TO_LOG2
> +				 | LOG_TO_LOG2 | INT_DIV_TO_MUL_RCP
>          			 | ((options->EmitNoPow) ? POW_TO_EXP2 : 0)));
>  
>           progress = do_lower_jumps(ir, true, true, options->EmitNoMainReturn, options->EmitNoCont, options->EmitNoLoops) || progress;

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/

iEYEARECAAYFAk5b1g0ACgkQX1gOwKyEAw/aYwCePGPJqQMS+jrV8ptoASkv4CaL
TVUAn1+jEglPy5hPofiyaU73JMEjG1c3
=qLnO
-----END PGP SIGNATURE-----


More information about the mesa-dev mailing list