//  [python]
def _impl_v10(cls, inputs, attr, params, expected_out_dtypes=None):
        if expected_out_dtypes is None:
            # The default QLinearMatMul converter is expected to have one of
            # these output dtypes.
            expected_out_dtypes = ["int8", "uint8"]

        # Some of the ops used below take scalar-like inputs, and may require either
        # of the following:
        #
        # - the input is Const node (not merely an expression that *could* be reduced
        #   to a single Const at graph-compilation time)
        #
        # - the input has a specific dtype
        #
        # This function attempts to present 'x' in a form that meets both of those
        # requirements.
        def try_resolve_to_const(x, dtype_override=None):
            x2 = try_resolve_var_to_const(x, params)
            num_elem = n.prod(infer_shape(x))
            if num_elem == 1:
                x2 = ensure_scalar_shape(x2)
            x_dtype = infer_type(x).checked_type.dtype
            if (dtype_override is not None) and (dtype_override != x_dtype):
                x2 = _op.cast(x2, dtype_override)
            x3 = fold_constant(x2)
            return x3

        # Unpack the inputs and obtain some type info...
        a, a_scale, a_zp, b, b_scale, b_zp, y_scale, y_zp = inputs

        a_type = infer_type(a).checked_type  # 'T1' in ONNX doc for this op
        a_scale_type = infer_type(a_scale).checked_type
        a_zp_type = infer_type(a_zp).checked_type

        b_type = infer_type(b).checked_type  # 'T2' in ONNX doc for this op
        b_scale_type = infer_type(b_scale).checked_type
        b_zp_type = infer_type(b_zp).checked_type

        y_scale_type = infer_type(y_scale).checked_type
        y_zp_type = infer_type(y_zp).checked_type  # 'T3' in ONNX doc for this op

        # Verify type assumptions, based on the ONNX doc for this op...
        assert a_type.dtype in ["int8", "uint8"]
        assert a_scale_type.dtype == "float32"
        assert a_zp_type.dtype == a_type.dtype

        assert b_type.dtype in ["int8", "uint8"]
        assert b_scale_type.dtype == "float32"
        assert b_zp_type.dtype == b_type.dtype

        assert y_scale_type.dtype == "float32"
        assert y_zp_type.dtype in expected_out_dtypes

        # _qnn.op.dense requires the zero-point values to have dtype int32.
        a_scale_scalar = try_resolve_to_const(a_scale)
        a_zp_scalar = try_resolve_to_const(a_zp, "int32")

        b_scale_scalar = try_resolve_to_const(b_scale)
        b_zp_scalar = try_resolve_to_const(b_zp, "int32")

        y_scale_scalar = try_resolve_to_const(y_scale)
        y_zp_scalar = try_resolve_to_const(y_zp, "int32")

        # TODO: Confirm that we're using 'num_hidden_units' correctly / as intended with
        # the '_qnn.op.dense' instance below.
        num_hidden_units = infer_shape(b)[-1]

        # - Specify the matmul result dtype as int32, so that hopefully the matmul will use
        #   a 32-bit accumulator as seems to be required by the ONNX op's documentation.
        #
        # TL;DR:
        # The ONNX documentation for this op is clear about acceptable overflow
        # behavior during the matmul operation:
        #   - The scalar multiplication ops MAY NOT overflow.
        #   - The scalar addition ops, which sum the results of the scalar multiplication,
        #     MAY overflow, but if they do so, it must behave as one would expect during
        #     32-bit integer-addition overflow.
        # As of this writing, Relay's qnn.op.dense operator doesn't expose a way for us to
        # express these constraints.
        #
        # TODO: Extend TVM / Relay / TIR / etc. to allow this kind of constraint to be
        # expressed in a Relay graph. And then update this importer and various TVM
        # backends accordingly.
        matmul_result_dtype = "int32"
        # TODO(vvchernov): possibly it is better to use unsigned type for result
        # if input types are unsigned:
        # if a_type.dtype == "uint8" and b_type.dtype == "uint8":
        #     matmul_result_dtype = "uint32"

        matmul_result = qmatmul(
            a,
            b,
            a_zp_scalar,
            b_zp_scalar,
            a_scale_scalar,
            b_scale_scalar,
            num_hidden_units,
            matmul_result_dtype,
        )

        # This information might only be found in the C++ code-comments for the
        # dense.matmul op, but the quantized tensor returned by _qnn.op.dense
        # has scale==(a_scale_scalar * b_scale_scalar), and zero_point==0.
        #
        # 'matmul_result_zp_scalar' has type 'int32' to satisfy input requirements
        # of the [de/re]quantize ops below.
        matmul_result_scale_scalar = fold_constant(_op.multiply(a_scale_scalar, b_scale_scalar))
        matmul_result_zp_scalar = _op.const(0, dtype="int32")

        if "int32" in expected_out_dtypes:
            # This is the adaptation of the QLinearMatMul converter for MatMulInteger,
            # in the MatMulInteger case we skip the unnecessary requantization step.
            return matmul_result

        # requantize requires y_scale to be constant,
        # if y_scale is not constant, doing dequantize -> quantize
        if isinstance(y_scale_scalar, _expr.Constant):
            y = _qnn.op.requantize(
                matmul_result,
                matmul_result_scale_scalar,
                matmul_result_zp_scalar,
                y_scale_scalar,
                y_zp_scalar,
                axis=-1,
                rounding="TONEAREST",
                out_dtype=y_zp_type.dtype,
            )
        else:
            matmul_result_deq = _qnn.op.dequantize(
                matmul_result, matmul_result_scale_scalar, matmul_result_zp_scalar, axis=0
            )

            y = _qnn.op.quantize(
                matmul_result_deq, y_scale_scalar, y_zp_scalar, axis=0, out_dtype=y_zp_type.dtype
            )

        return y