glsl: Set the precisions of builtin function arguments and returns.
authorEmma Anholt <emma@anholt.net>
Wed, 1 Mar 2023 23:31:21 +0000 (15:31 -0800)
committerMarge Bot <emma+marge@anholt.net>
Tue, 21 Mar 2023 00:51:24 +0000 (00:51 +0000)
These have precision qualifiers defined in the spec, in which case we
should emit them them while generating builtin signatures and code.  We've
been special-casing them in GLSL lower_precision, but now we can just rely
on the precision qualifier of the builtin if non-NONE.

Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/21666>

src/compiler/glsl/builtin_functions.cpp
src/compiler/glsl/lower_precision.cpp

index e07ac61..4d1f67f 100644 (file)
@@ -1017,7 +1017,11 @@ private:
     * member available.
     */
    ir_variable *in_var(const glsl_type *type, const char *name);
+   ir_variable *in_mediump_var(const glsl_type *type, const char *name);
+   ir_variable *in_highp_var(const glsl_type *type, const char *name);
    ir_variable *out_var(const glsl_type *type, const char *name);
+   ir_variable *out_lowp_var(const glsl_type *type, const char *name);
+   ir_variable *out_highp_var(const glsl_type *type, const char *name);
    ir_constant *imm(float f, unsigned vector_elements=1);
    ir_constant *imm(bool b, unsigned vector_elements=1);
    ir_constant *imm(int i, unsigned vector_elements=1);
@@ -1079,6 +1083,10 @@ private:
                                ir_expression_operation opcode,
                                const glsl_type *return_type,
                                const glsl_type *param_type);
+   ir_function_signature *unop_precision(builtin_available_predicate avail,
+                                         ir_expression_operation opcode,
+                                         const glsl_type *return_type,
+                                         const glsl_type *param_type, uint32_t precision);
    ir_function_signature *binop(builtin_available_predicate avail,
                                 ir_expression_operation opcode,
                                 const glsl_type *return_type,
@@ -5651,11 +5659,43 @@ builtin_builder::in_var(const glsl_type *type, const char *name)
 }
 
 ir_variable *
+builtin_builder::in_highp_var(const glsl_type *type, const char *name)
+{
+   ir_variable *var = in_var(type, name);
+   var->data.precision = GLSL_PRECISION_HIGH;
+   return var;
+}
+
+ir_variable *
+builtin_builder::in_mediump_var(const glsl_type *type, const char *name)
+{
+   ir_variable *var = in_var(type, name);
+   var->data.precision = GLSL_PRECISION_MEDIUM;
+   return var;
+}
+
+ir_variable *
 builtin_builder::out_var(const glsl_type *type, const char *name)
 {
    return new(mem_ctx) ir_variable(type, name, ir_var_function_out);
 }
 
+ir_variable *
+builtin_builder::out_lowp_var(const glsl_type *type, const char *name)
+{
+   ir_variable *var = out_var(type, name);
+   var->data.precision = GLSL_PRECISION_LOW;
+   return var;
+}
+
+ir_variable *
+builtin_builder::out_highp_var(const glsl_type *type, const char *name)
+{
+   ir_variable *var = out_var(type, name);
+   var->data.precision = GLSL_PRECISION_HIGH;
+   return var;
+}
+
 ir_constant *
 builtin_builder::imm(bool b, unsigned vector_elements)
 {
@@ -6290,8 +6330,9 @@ builtin_builder::_uint64BitsToDouble(builtin_available_predicate avail, const gl
 ir_function_signature *
 builtin_builder::_packUnorm2x16(builtin_available_predicate avail)
 {
-   ir_variable *v = in_var(glsl_type::vec2_type, "v");
+   ir_variable *v = in_highp_var(glsl_type::vec2_type, "v");
    MAKE_SIG(glsl_type::uint_type, avail, 1, v);
+   sig->return_precision = GLSL_PRECISION_HIGH;
    body.emit(ret(expr(ir_unop_pack_unorm_2x16, v)));
    return sig;
 }
@@ -6301,6 +6342,7 @@ builtin_builder::_packSnorm2x16(builtin_available_predicate avail)
 {
    ir_variable *v = in_var(glsl_type::vec2_type, "v");
    MAKE_SIG(glsl_type::uint_type, avail, 1, v);
+   sig->return_precision = GLSL_PRECISION_HIGH;
    body.emit(ret(expr(ir_unop_pack_snorm_2x16, v)));
    return sig;
 }
@@ -6308,8 +6350,9 @@ builtin_builder::_packSnorm2x16(builtin_available_predicate avail)
 ir_function_signature *
 builtin_builder::_packUnorm4x8(builtin_available_predicate avail)
 {
-   ir_variable *v = in_var(glsl_type::vec4_type, "v");
+   ir_variable *v = in_mediump_var(glsl_type::vec4_type, "v");
    MAKE_SIG(glsl_type::uint_type, avail, 1, v);
+   sig->return_precision = GLSL_PRECISION_HIGH;
    body.emit(ret(expr(ir_unop_pack_unorm_4x8, v)));
    return sig;
 }
@@ -6317,8 +6360,9 @@ builtin_builder::_packUnorm4x8(builtin_available_predicate avail)
 ir_function_signature *
 builtin_builder::_packSnorm4x8(builtin_available_predicate avail)
 {
-   ir_variable *v = in_var(glsl_type::vec4_type, "v");
+   ir_variable *v = in_mediump_var(glsl_type::vec4_type, "v");
    MAKE_SIG(glsl_type::uint_type, avail, 1, v);
+   sig->return_precision = GLSL_PRECISION_HIGH;
    body.emit(ret(expr(ir_unop_pack_snorm_4x8, v)));
    return sig;
 }
@@ -6326,8 +6370,9 @@ builtin_builder::_packSnorm4x8(builtin_available_predicate avail)
 ir_function_signature *
 builtin_builder::_unpackUnorm2x16(builtin_available_predicate avail)
 {
-   ir_variable *p = in_var(glsl_type::uint_type, "p");
+   ir_variable *p = in_highp_var(glsl_type::uint_type, "p");
    MAKE_SIG(glsl_type::vec2_type, avail, 1, p);
+   sig->return_precision = GLSL_PRECISION_HIGH;
    body.emit(ret(expr(ir_unop_unpack_unorm_2x16, p)));
    return sig;
 }
@@ -6335,8 +6380,9 @@ builtin_builder::_unpackUnorm2x16(builtin_available_predicate avail)
 ir_function_signature *
 builtin_builder::_unpackSnorm2x16(builtin_available_predicate avail)
 {
-   ir_variable *p = in_var(glsl_type::uint_type, "p");
+   ir_variable *p = in_highp_var(glsl_type::uint_type, "p");
    MAKE_SIG(glsl_type::vec2_type, avail, 1, p);
+   sig->return_precision = GLSL_PRECISION_HIGH;
    body.emit(ret(expr(ir_unop_unpack_snorm_2x16, p)));
    return sig;
 }
@@ -6345,8 +6391,9 @@ builtin_builder::_unpackSnorm2x16(builtin_available_predicate avail)
 ir_function_signature *
 builtin_builder::_unpackUnorm4x8(builtin_available_predicate avail)
 {
-   ir_variable *p = in_var(glsl_type::uint_type, "p");
+   ir_variable *p = in_highp_var(glsl_type::uint_type, "p");
    MAKE_SIG(glsl_type::vec4_type, avail, 1, p);
+   sig->return_precision = GLSL_PRECISION_MEDIUM;
    body.emit(ret(expr(ir_unop_unpack_unorm_4x8, p)));
    return sig;
 }
@@ -6354,8 +6401,9 @@ builtin_builder::_unpackUnorm4x8(builtin_available_predicate avail)
 ir_function_signature *
 builtin_builder::_unpackSnorm4x8(builtin_available_predicate avail)
 {
-   ir_variable *p = in_var(glsl_type::uint_type, "p");
+   ir_variable *p = in_highp_var(glsl_type::uint_type, "p");
    MAKE_SIG(glsl_type::vec4_type, avail, 1, p);
+   sig->return_precision = GLSL_PRECISION_MEDIUM;
    body.emit(ret(expr(ir_unop_unpack_snorm_4x8, p)));
    return sig;
 }
@@ -6363,8 +6411,9 @@ builtin_builder::_unpackSnorm4x8(builtin_available_predicate avail)
 ir_function_signature *
 builtin_builder::_packHalf2x16(builtin_available_predicate avail)
 {
-   ir_variable *v = in_var(glsl_type::vec2_type, "v");
+   ir_variable *v = in_mediump_var(glsl_type::vec2_type, "v");
    MAKE_SIG(glsl_type::uint_type, avail, 1, v);
+   sig->return_precision = GLSL_PRECISION_HIGH;
    body.emit(ret(expr(ir_unop_pack_half_2x16, v)));
    return sig;
 }
@@ -6372,8 +6421,9 @@ builtin_builder::_packHalf2x16(builtin_available_predicate avail)
 ir_function_signature *
 builtin_builder::_unpackHalf2x16(builtin_available_predicate avail)
 {
-   ir_variable *p = in_var(glsl_type::uint_type, "p");
+   ir_variable *p = in_highp_var(glsl_type::uint_type, "p");
    MAKE_SIG(glsl_type::vec2_type, avail, 1, p);
+   sig->return_precision = GLSL_PRECISION_MEDIUM;
    body.emit(ret(expr(ir_unop_unpack_half_2x16, p)));
    return sig;
 }
@@ -7072,6 +7122,7 @@ builtin_builder::_textureSize(builtin_available_predicate avail,
    ir_variable *s = in_var(sampler_type, "sampler");
    /* The sampler always exists; add optional lod later. */
    MAKE_SIG(return_type, avail, 1, s);
+   sig->return_precision = GLSL_PRECISION_HIGH;
 
    ir_texture *tex = new(mem_ctx) ir_texture(ir_txs);
    tex->set_sampler(new(mem_ctx) ir_dereference_variable(s), return_type);
@@ -7621,27 +7672,44 @@ builtin_builder::_bitfieldInsert(const glsl_type *type)
    return sig;
 }
 
-UNOP(bitfieldReverse, ir_unop_bitfield_reverse, gpu_shader5_or_es31_or_integer_functions)
+ir_function_signature *
+builtin_builder::_bitfieldReverse(const glsl_type *type)
+{
+   ir_variable *x = in_highp_var(type, "x");
+   MAKE_SIG(type, gpu_shader5_or_es31_or_integer_functions, 1, x);
+   sig->return_precision = GLSL_PRECISION_HIGH;
+   body.emit(ret(expr(ir_unop_bitfield_reverse, x)));
+   return sig;
+}
 
 ir_function_signature *
 builtin_builder::_bitCount(const glsl_type *type)
 {
-   return unop(gpu_shader5_or_es31_or_integer_functions, ir_unop_bit_count,
-               glsl_type::ivec(type->vector_elements), type);
+   ir_variable *x = in_var(type, "x");
+   MAKE_SIG(glsl_type::ivec(type->vector_elements), gpu_shader5_or_es31_or_integer_functions, 1, x);
+   sig->return_precision = GLSL_PRECISION_LOW;
+   body.emit(ret(expr(ir_unop_bit_count, x)));
+   return sig;
 }
 
 ir_function_signature *
 builtin_builder::_findLSB(const glsl_type *type)
 {
-   return unop(gpu_shader5_or_es31_or_integer_functions, ir_unop_find_lsb,
-               glsl_type::ivec(type->vector_elements), type);
+   ir_variable *x = in_highp_var(type, "x");
+   MAKE_SIG(glsl_type::ivec(type->vector_elements), gpu_shader5_or_es31_or_integer_functions, 1, x);
+   sig->return_precision = GLSL_PRECISION_LOW;
+   body.emit(ret(expr(ir_unop_find_lsb, x)));
+   return sig;
 }
 
 ir_function_signature *
 builtin_builder::_findMSB(const glsl_type *type)
 {
-   return unop(gpu_shader5_or_es31_or_integer_functions, ir_unop_find_msb,
-               glsl_type::ivec(type->vector_elements), type);
+   ir_variable *x = in_highp_var(type, "x");
+   MAKE_SIG(glsl_type::ivec(type->vector_elements), gpu_shader5_or_es31_or_integer_functions, 1, x);
+   sig->return_precision = GLSL_PRECISION_LOW;
+   body.emit(ret(expr(ir_unop_find_msb, x)));
+   return sig;
 }
 
 ir_function_signature *
@@ -7682,14 +7750,18 @@ builtin_builder::_fma(builtin_available_predicate avail, const glsl_type *type)
 ir_function_signature *
 builtin_builder::_ldexp(const glsl_type *x_type, const glsl_type *exp_type)
 {
-   return binop(x_type->is_double() ? fp64 : gpu_shader5_or_es31_or_integer_functions,
-                ir_binop_ldexp, x_type, x_type, exp_type);
+   ir_variable *x = in_highp_var(x_type, "x");
+   ir_variable *y = in_highp_var(exp_type, "y");
+   MAKE_SIG(x_type, x_type->is_double() ? fp64 : gpu_shader5_or_es31_or_integer_functions, 2, x, y);
+   sig->return_precision = GLSL_PRECISION_HIGH;
+   body.emit(ret(expr(ir_binop_ldexp, x, y)));
+   return sig;
 }
 
 ir_function_signature *
 builtin_builder::_dfrexp(const glsl_type *x_type, const glsl_type *exp_type)
 {
-   ir_variable *x = in_var(x_type, "x");
+   ir_variable *x = in_highp_var(x_type, "x");
    ir_variable *exponent = out_var(exp_type, "exp");
    MAKE_SIG(x_type, fp64, 2, x, exponent);
 
@@ -7702,9 +7774,10 @@ builtin_builder::_dfrexp(const glsl_type *x_type, const glsl_type *exp_type)
 ir_function_signature *
 builtin_builder::_frexp(const glsl_type *x_type, const glsl_type *exp_type)
 {
-   ir_variable *x = in_var(x_type, "x");
-   ir_variable *exponent = out_var(exp_type, "exp");
+   ir_variable *x = in_highp_var(x_type, "x");
+   ir_variable *exponent = out_highp_var(exp_type, "exp");
    MAKE_SIG(x_type, gpu_shader5_or_es31_or_integer_functions, 2, x, exponent);
+   sig->return_precision = GLSL_PRECISION_HIGH;
 
    const unsigned vec_elem = x_type->vector_elements;
    const glsl_type *bvec = glsl_type::get_instance(GLSL_TYPE_BOOL, vec_elem, 1);
@@ -7750,10 +7823,11 @@ builtin_builder::_frexp(const glsl_type *x_type, const glsl_type *exp_type)
 ir_function_signature *
 builtin_builder::_uaddCarry(const glsl_type *type)
 {
-   ir_variable *x = in_var(type, "x");
-   ir_variable *y = in_var(type, "y");
-   ir_variable *carry = out_var(type, "carry");
+   ir_variable *x = in_highp_var(type, "x");
+   ir_variable *y = in_highp_var(type, "y");
+   ir_variable *carry = out_lowp_var(type, "carry");
    MAKE_SIG(type, gpu_shader5_or_es31_or_integer_functions, 3, x, y, carry);
+   sig->return_precision = GLSL_PRECISION_HIGH;
 
    body.emit(assign(carry, ir_builder::carry(x, y)));
    body.emit(ret(add(x, y)));
@@ -7771,10 +7845,11 @@ builtin_builder::_addSaturate(builtin_available_predicate avail,
 ir_function_signature *
 builtin_builder::_usubBorrow(const glsl_type *type)
 {
-   ir_variable *x = in_var(type, "x");
-   ir_variable *y = in_var(type, "y");
-   ir_variable *borrow = out_var(type, "borrow");
+   ir_variable *x = in_highp_var(type, "x");
+   ir_variable *y = in_highp_var(type, "y");
+   ir_variable *borrow = out_lowp_var(type, "borrow");
    MAKE_SIG(type, gpu_shader5_or_es31_or_integer_functions, 3, x, y, borrow);
+   sig->return_precision = GLSL_PRECISION_HIGH;
 
    body.emit(assign(borrow, ir_builder::borrow(x, y)));
    body.emit(ret(sub(x, y)));
@@ -7835,10 +7910,10 @@ builtin_builder::_mulExtended(const glsl_type *type)
       unpack_type = glsl_type::uvec2_type;
    }
 
-   ir_variable *x = in_var(type, "x");
-   ir_variable *y = in_var(type, "y");
-   ir_variable *msb = out_var(type, "msb");
-   ir_variable *lsb = out_var(type, "lsb");
+   ir_variable *x = in_highp_var(type, "x");
+   ir_variable *y = in_highp_var(type, "y");
+   ir_variable *msb = out_highp_var(type, "msb");
+   ir_variable *lsb = out_highp_var(type, "lsb");
    MAKE_SIG(glsl_type::void_type, gpu_shader5_or_es31_or_integer_functions, 4, x, y, msb, lsb);
 
    ir_variable *unpack_val = body.make_temp(unpack_type, "_unpack_val");
@@ -8303,6 +8378,10 @@ builtin_builder::_image(image_prototype_ctr prototype,
       } else {
          ir_variable *ret_val =
             body.make_temp(sig->return_type, "_ret_val");
+         /* all non-void image functions return highp, so make our temporary and return
+          * value in the signature highp.
+          */
+         ret_val->data.precision = GLSL_PRECISION_HIGH;
          body.emit(call(f, ret_val, sig->parameters));
          body.emit(ret(ret_val));
       }
@@ -8312,6 +8391,7 @@ builtin_builder::_image(image_prototype_ctr prototype,
    } else {
       sig->intrinsic_id = id;
    }
+   sig->return_precision = GLSL_PRECISION_HIGH;
 
    return sig;
 }
index 2b01b9a..1912270 100644 (file)
@@ -431,7 +431,7 @@ handle_call(ir_call *ir, const struct set *lowerable_rvalues)
       ir_rvalue *param = (ir_rvalue*)ir->actual_parameters.get_head();
       ir_variable *resource = param->variable_referenced();
 
-      assert(ir->callee->return_precision == GLSL_PRECISION_NONE);
+      assert(ir->callee->return_precision == GLSL_PRECISION_HIGH);
       assert(resource->type->without_array()->is_image());
 
       /* GLSL ES 3.20 requires that images have a precision modifier, but if
@@ -460,7 +460,7 @@ handle_call(ir_call *ir, const struct set *lowerable_rvalues)
    }
 
    /* Return the declared precision for user-defined functions. */
-   if (!ir->callee->is_builtin())
+   if (!ir->callee->is_builtin() || ir->callee->return_precision != GLSL_PRECISION_NONE)
       return ir->callee->return_precision;
 
    /* Handle special calls. */
@@ -476,10 +476,6 @@ handle_call(ir_call *ir, const struct set *lowerable_rvalues)
        * uses lower precision. The function parameters don't matter.
        */
       if (var && var->type->without_array()->is_sampler()) {
-         /* textureSize always returns highp. */
-         if (!strcmp(ir->callee_name(), "textureSize"))
-            return GLSL_PRECISION_HIGH;
-
          /* textureGatherOffsets always takes a highp array of constants. As
           * per the discussion https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/16547#note_1393704
           * trying to lower the precision results in segfault later on
@@ -493,40 +489,18 @@ handle_call(ir_call *ir, const struct set *lowerable_rvalues)
       }
    }
 
-   if (/* Parameters are always highp: */
+   if (ir->callee->return_precision != GLSL_PRECISION_NONE)
+      return ir->callee->return_precision;
+
+   if (/* Parameters are always implicitly promoted to highp: */
        !strcmp(ir->callee_name(), "floatBitsToInt") ||
        !strcmp(ir->callee_name(), "floatBitsToUint") ||
        !strcmp(ir->callee_name(), "intBitsToFloat") ||
        !strcmp(ir->callee_name(), "uintBitsToFloat") ||
-       !strcmp(ir->callee_name(), "bitfieldReverse") ||
-       !strcmp(ir->callee_name(), "frexp") ||
-       !strcmp(ir->callee_name(), "ldexp") ||
-       /* Parameters and outputs are always highp: */
-       /* TODO: The operations are highp, but carry and borrow outputs are lowp. */
-       !strcmp(ir->callee_name(), "uaddCarry") ||
-       !strcmp(ir->callee_name(), "usubBorrow") ||
-       !strcmp(ir->callee_name(), "imulExtended") ||
-       !strcmp(ir->callee_name(), "umulExtended") ||
-       !strcmp(ir->callee_name(), "unpackUnorm2x16") ||
-       !strcmp(ir->callee_name(), "unpackSnorm2x16") ||
-       /* Outputs are highp: */
-       !strcmp(ir->callee_name(), "packUnorm2x16") ||
-       !strcmp(ir->callee_name(), "packSnorm2x16") ||
-       /* Parameters are mediump and outputs are highp. The parameters should
-        * be optimized in NIR, not here, e.g:
-        * - packHalf2x16 can just be a bitcast from f16vec2 to uint32
-        * - Other opcodes don't have to convert parameters to highp if the hw
-        *   has f16 versions. Optimize in NIR accordingly.
-        */
-       !strcmp(ir->callee_name(), "packHalf2x16") ||
-       !strcmp(ir->callee_name(), "packUnorm4x8") ||
-       !strcmp(ir->callee_name(), "packSnorm4x8") ||
        /* Atomic functions are not lowered. */
        strstr(ir->callee_name(), "atomic") == ir->callee_name())
       return GLSL_PRECISION_HIGH;
 
-   assert(ir->callee->return_precision == GLSL_PRECISION_NONE);
-
    /* Number of parameters to check if they are lowerable. */
    unsigned check_parameters = ir->actual_parameters.length();
 
@@ -538,11 +512,6 @@ handle_call(ir_call *ir, const struct set *lowerable_rvalues)
       check_parameters = 1;
    } else if (!strcmp(ir->callee_name(), "bitfieldInsert")) {
       check_parameters = 2;
-   } if (function_always_returns_mediump_or_lowp(ir->callee_name())) {
-      /* These only lower the return value. Parameters keep their precision,
-       * which is preserved in map_builtin.
-       */
-      check_parameters = 0;
    }
 
    /* If the call is to a builtin, then the function won’t have a return
@@ -587,10 +556,12 @@ find_lowerable_rvalues_visitor::visit_leave(ir_call *ir)
       handle_precision(var->type, return_precision);
 
    if (lower_state == SHOULD_LOWER) {
-      /* There probably shouldn’t be any situations where multiple ir_call
-       * instructions write to the same temporary?
+      /* Function calls always write to a temporary return value in the caller,
+       * which has no other users.  That temp may start with the precision of
+       * the function's signature, but if we're inferring the precision of an
+       * unqualified builtin operation (particularly the imageLoad overrides!)
+       * then we need to update it.
        */
-      assert(var->data.precision == GLSL_PRECISION_NONE);
       var->data.precision = GLSL_PRECISION_MEDIUM;
    } else {
       var->data.precision = GLSL_PRECISION_HIGH;