static std::unique_ptr optimize_intrinsic_call()

in src/sksl/ir/SkSLFunctionCall.cpp [635:1003]


static std::unique_ptr<Expression> optimize_intrinsic_call(const Context& context,
                                                           Position pos,
                                                           IntrinsicKind intrinsic,
                                                           const ExpressionArray& argArray,
                                                           const Type& returnType) {
    // Replace constant variables with their literal values.
    IntrinsicArguments arguments = {};
    SkASSERT(SkToSizeT(argArray.size()) <= arguments.size());
    for (int index = 0; index < argArray.size(); ++index) {
        arguments[index] = ConstantFolder::GetConstantValueForVariable(*argArray[index]);
    }

    auto Get = [&](int idx, int col) -> float {
        return *arguments[idx]->getConstantValue(col);
    };

    switch (intrinsic) {
        // 8.1 : Angle and Trigonometry Functions
        case k_radians_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_radians);
        case k_degrees_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_degrees);
        case k_sin_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_sin);
        case k_cos_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_cos);
        case k_tan_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_tan);
        case k_sinh_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_sinh);
        case k_cosh_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_cosh);
        case k_tanh_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_tanh);
        case k_asin_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_asin);
        case k_acos_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_acos);
        case k_atan_IntrinsicKind:
            if (argArray.size() == 1) {
                return evaluate_intrinsic<float>(context, arguments, returnType,
                                                 Intrinsics::evaluate_atan);
            } else {
                return evaluate_pairwise_intrinsic(context, arguments, returnType,
                                                   Intrinsics::evaluate_atan2);
            }
        case k_asinh_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_asinh);

        case k_acosh_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_acosh);
        case k_atanh_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_atanh);
        // 8.2 : Exponential Functions
        case k_pow_IntrinsicKind:
            return evaluate_pairwise_intrinsic(context, arguments, returnType,
                                               Intrinsics::evaluate_pow);
        case k_exp_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_exp);
        case k_log_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_log);
        case k_exp2_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_exp2);
        case k_log2_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_log2);
        case k_sqrt_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_sqrt);
        case k_inversesqrt_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_inversesqrt);
        // 8.3 : Common Functions
        case k_abs_IntrinsicKind:
            return evaluate_intrinsic_numeric(context, arguments, returnType,
                                              Intrinsics::evaluate_abs);
        case k_sign_IntrinsicKind:
            return Intrinsics::evaluate_sign(context, arguments);

        case k_floor_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_floor);
        case k_ceil_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_ceil);
        case k_fract_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_fract);
        case k_mod_IntrinsicKind:
            return evaluate_pairwise_intrinsic(context, arguments, returnType,
                                               Intrinsics::evaluate_mod);
        case k_min_IntrinsicKind:
            return evaluate_pairwise_intrinsic(context, arguments, returnType,
                                               Intrinsics::evaluate_min);
        case k_max_IntrinsicKind:
            return evaluate_pairwise_intrinsic(context, arguments, returnType,
                                               Intrinsics::evaluate_max);
        case k_clamp_IntrinsicKind:
            return evaluate_3_way_intrinsic(context, arguments, returnType,
                                            Intrinsics::evaluate_clamp);
        case k_fma_IntrinsicKind:
            return evaluate_3_way_intrinsic(context, arguments, returnType,
                                            Intrinsics::evaluate_fma);
        case k_saturate_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_saturate);
        case k_mix_IntrinsicKind:
            if (arguments[2]->type().componentType().isBoolean()) {
                const SkSL::Type& numericType = arguments[0]->type().componentType();

                if (numericType.isFloat()) {
                    type_check_expression<float>(*arguments[0]);
                    type_check_expression<float>(*arguments[1]);
                } else if (numericType.isInteger()) {
                    type_check_expression<SKSL_INT>(*arguments[0]);
                    type_check_expression<SKSL_INT>(*arguments[1]);
                } else if (numericType.isBoolean()) {
                    type_check_expression<bool>(*arguments[0]);
                    type_check_expression<bool>(*arguments[1]);
                } else {
                    SkDEBUGFAILF("unsupported type %s", numericType.description().c_str());
                    return nullptr;
                }
                return evaluate_n_way_intrinsic(context, arguments[0], arguments[1], arguments[2],
                                                returnType, Intrinsics::evaluate_mix);
            } else {
                return evaluate_3_way_intrinsic(context, arguments, returnType,
                                                Intrinsics::evaluate_mix);
            }
        case k_step_IntrinsicKind:
            return evaluate_pairwise_intrinsic(context, arguments, returnType,
                                               Intrinsics::evaluate_step);
        case k_smoothstep_IntrinsicKind:
            return evaluate_3_way_intrinsic(context, arguments, returnType,
                                            Intrinsics::evaluate_smoothstep);
        case k_trunc_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_trunc);
        case k_round_IntrinsicKind:      // GLSL `round` documents its rounding mode as unspecified
        case k_roundEven_IntrinsicKind:  // and is allowed to behave identically to `roundEven`.
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_round);
        case k_floatBitsToInt_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_floatBitsToInt);
        case k_floatBitsToUint_IntrinsicKind:
            return evaluate_intrinsic<float>(context, arguments, returnType,
                                             Intrinsics::evaluate_floatBitsToUint);
        case k_intBitsToFloat_IntrinsicKind:
            return evaluate_intrinsic<SKSL_INT>(context, arguments, returnType,
                                                Intrinsics::evaluate_intBitsToFloat);
        case k_uintBitsToFloat_IntrinsicKind:
            return evaluate_intrinsic<SKSL_INT>(context, arguments, returnType,
                                                Intrinsics::evaluate_uintBitsToFloat);
        // 8.4 : Floating-Point Pack and Unpack Functions
        case k_packUnorm2x16_IntrinsicKind: {
            auto Pack = [&](int n) -> unsigned int {
                float x = Get(0, n);
                return (int)std::round(Intrinsics::evaluate_clamp(x, 0.0, 1.0) * 65535.0);
            };
            const double packed = ((Pack(0) << 0)  & 0x0000FFFF) |
                                  ((Pack(1) << 16) & 0xFFFF0000);
            return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
                                                          *context.fTypes.fUInt, &packed);
        }
        case k_packSnorm2x16_IntrinsicKind: {
            auto Pack = [&](int n) -> unsigned int {
                float x = Get(0, n);
                return (int)std::round(Intrinsics::evaluate_clamp(x, -1.0, 1.0) * 32767.0);
            };
            const double packed = ((Pack(0) << 0)  & 0x0000FFFF) |
                                  ((Pack(1) << 16) & 0xFFFF0000);
            return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
                                                          *context.fTypes.fUInt, &packed);
        }
        case k_packHalf2x16_IntrinsicKind: {
            auto Pack = [&](int n) -> unsigned int {
                return SkFloatToHalf(Get(0, n));
            };
            const double packed = ((Pack(0) << 0)  & 0x0000FFFF) |
                                  ((Pack(1) << 16) & 0xFFFF0000);
            return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
                                                          *context.fTypes.fUInt, &packed);
        }
        case k_unpackUnorm2x16_IntrinsicKind: {
            SKSL_INT x = *arguments[0]->getConstantValue(0);
            uint16_t a = ((x >> 0)  & 0x0000FFFF);
            uint16_t b = ((x >> 16) & 0x0000FFFF);
            const double unpacked[2] = {double(a) / 65535.0,
                                        double(b) / 65535.0};
            return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
                                                          *context.fTypes.fFloat2, unpacked);
        }
        case k_unpackSnorm2x16_IntrinsicKind: {
            SKSL_INT x = *arguments[0]->getConstantValue(0);
            int16_t a = ((x >> 0)  & 0x0000FFFF);
            int16_t b = ((x >> 16) & 0x0000FFFF);
            const double unpacked[2] = {Intrinsics::evaluate_clamp(double(a) / 32767.0, -1.0, 1.0),
                                        Intrinsics::evaluate_clamp(double(b) / 32767.0, -1.0, 1.0)};
            return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
                                                          *context.fTypes.fFloat2, unpacked);
        }
        case k_unpackHalf2x16_IntrinsicKind: {
            SKSL_INT x = *arguments[0]->getConstantValue(0);
            uint16_t a = ((x >> 0)  & 0x0000FFFF);
            uint16_t b = ((x >> 16) & 0x0000FFFF);
            const double unpacked[2] = {SkHalfToFloat(a),
                                        SkHalfToFloat(b)};
            return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
                                                          *context.fTypes.fFloat2, unpacked);
        }
        // 8.5 : Geometric Functions
        case k_length_IntrinsicKind:
            return Intrinsics::evaluate_length(arguments);

        case k_distance_IntrinsicKind:
            return Intrinsics::evaluate_distance(arguments);

        case k_dot_IntrinsicKind:
            return Intrinsics::evaluate_dot(arguments);

        case k_cross_IntrinsicKind: {
            auto X = [&](int n) -> float { return Get(0, n); };
            auto Y = [&](int n) -> float { return Get(1, n); };
            SkASSERT(arguments[0]->type().columns() == 3);  // the vec2 form is not a real intrinsic

            double vec[3] = {X(1) * Y(2) - Y(1) * X(2),
                             X(2) * Y(0) - Y(2) * X(0),
                             X(0) * Y(1) - Y(0) * X(1)};
            return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
                                                          returnType, vec);
        }
        case k_normalize_IntrinsicKind:
            return Intrinsics::evaluate_normalize(context, arguments);

        case k_faceforward_IntrinsicKind:
            return Intrinsics::evaluate_faceforward(context, arguments);

        case k_reflect_IntrinsicKind:
            return Intrinsics::evaluate_reflect(context, arguments);

        case k_refract_IntrinsicKind:
            return Intrinsics::evaluate_refract(context, arguments);

        // 8.6 : Matrix Functions
        case k_matrixCompMult_IntrinsicKind:
            return evaluate_pairwise_intrinsic(context, arguments, returnType,
                                               Intrinsics::evaluate_matrixCompMult);
        case k_transpose_IntrinsicKind: {
            double mat[16];
            int index = 0;
            for (int c = 0; c < returnType.columns(); ++c) {
                for (int r = 0; r < returnType.rows(); ++r) {
                    mat[index++] = Get(0, (returnType.columns() * r) + c);
                }
            }
            return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
                                                          returnType, mat);
        }
        case k_outerProduct_IntrinsicKind: {
            double mat[16];
            int index = 0;
            for (int c = 0; c < returnType.columns(); ++c) {
                for (int r = 0; r < returnType.rows(); ++r) {
                    mat[index++] = Get(0, r) * Get(1, c);
                }
            }
            return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
                                                          returnType, mat);
        }
        case k_determinant_IntrinsicKind: {
            float mat[16];
            extract_matrix(arguments[0], mat);
            float determinant;
            switch (arguments[0]->type().slotCount()) {
                case 4:
                    determinant = SkInvert2x2Matrix(mat, /*outMatrix=*/nullptr);
                    break;
                case 9:
                    determinant = SkInvert3x3Matrix(mat, /*outMatrix=*/nullptr);
                    break;
                case 16:
                    determinant = SkInvert4x4Matrix(mat, /*outMatrix=*/nullptr);
                    break;
                default:
                    SkDEBUGFAILF("unsupported type %s", arguments[0]->type().description().c_str());
                    return nullptr;
            }
            return Literal::MakeFloat(arguments[0]->fPosition, determinant, &returnType);
        }
        case k_inverse_IntrinsicKind: {
            float mat[16] = {};
            extract_matrix(arguments[0], mat);
            switch (arguments[0]->type().slotCount()) {
                case 4:
                    if (SkInvert2x2Matrix(mat, mat) == 0.0f) {
                        return nullptr;
                    }
                    break;
                case 9:
                    if (SkInvert3x3Matrix(mat, mat) == 0.0f) {
                        return nullptr;
                    }
                    break;
                case 16:
                    if (SkInvert4x4Matrix(mat, mat) == 0.0f) {
                        return nullptr;
                    }
                    break;
                default:
                    SkDEBUGFAILF("unsupported type %s", arguments[0]->type().description().c_str());
                    return nullptr;
            }

            double dmat[16];
            std::copy(mat, mat + std::size(mat), dmat);
            return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
                                                         returnType, dmat);
        }
        // 8.7 : Vector Relational Functions
        case k_lessThan_IntrinsicKind:
            return optimize_comparison(context, arguments, Intrinsics::compare_lessThan);

        case k_lessThanEqual_IntrinsicKind:
            return optimize_comparison(context, arguments, Intrinsics::compare_lessThanEqual);

        case k_greaterThan_IntrinsicKind:
            return optimize_comparison(context, arguments, Intrinsics::compare_greaterThan);

        case k_greaterThanEqual_IntrinsicKind:
            return optimize_comparison(context, arguments, Intrinsics::compare_greaterThanEqual);

        case k_equal_IntrinsicKind:
            return optimize_comparison(context, arguments, Intrinsics::compare_equal);

        case k_notEqual_IntrinsicKind:
            return optimize_comparison(context, arguments, Intrinsics::compare_notEqual);

        case k_any_IntrinsicKind:
            return coalesce_vector<bool>(arguments, /*startingState=*/false, returnType,
                                         Intrinsics::coalesce_any,
                                         /*finalize=*/nullptr);
        case k_all_IntrinsicKind:
            return coalesce_vector<bool>(arguments, /*startingState=*/true, returnType,
                                         Intrinsics::coalesce_all,
                                         /*finalize=*/nullptr);
        case k_not_IntrinsicKind:
            return evaluate_intrinsic<bool>(context, arguments, returnType,
                                            Intrinsics::evaluate_not);
        default:
            return nullptr;
    }
}