def _perform_gather_with_batch_dims(x, indices, batch_dims, gather_func, func_args, name):
    """
    An utility function to compute gather and gather_nd with batch_dims
    """
    # (Step 1)
    # Reshape x, indices with shape
    # x: [batch_1, ..., batch_n, *remaining_x_shape]
    # indices: [batch_1, ..., batch_n, *remaing_indices_shape]
    # into shape
    # x_reshape: [prod(batch_1, ..., batch_n), *remaning_x_shape]
    # indices_reshape: [prod(batch_1, ..., batch_n), *remaning_indices_shape]
    msg = ("The implementation of gather/gather_nd for iOS15 and older is not efficient. Highly recommend "
           " set minimum_deployment_target=coremltools.target.iOS16 in the coremltools.convert() function."
    )
    logger.warning(msg)
    x_shape = mb.shape(x=x)
    indices_shape = mb.shape(x=indices)
    batch_shape = mb.gather(x=x_shape, indices=_np.array(range(batch_dims)), axis=0)
    batch_prod = mb.reduce_prod(x=batch_shape, axes=[0], keep_dims=True)
    x_remaining_shape = mb.gather(x=x_shape, indices=_np.array(range(batch_dims, x.rank)), axis=0)
    indices_remaining_shape = mb.gather(x=indices_shape, indices=_np.array(range(batch_dims, indices.rank)), axis=0)
    new_x_shape = mb.concat(values=[batch_prod, x_remaining_shape], axis=0)
    new_indices_shape = mb.concat(values=[batch_prod, indices_remaining_shape], axis=0)
    x_reshape = mb.reshape(x=x, shape=new_x_shape)
    indices_reshape = mb.reshape(x=indices, shape=new_indices_shape)

    # (Step 2)
    # We iterate through the batch dimension, and compute the gather individually for each batch
    # All results are stacked into a tensor with shape [prod(batch_1, ..., batch_n), *remaning_result_shape]
    res = []
    if batch_prod.val is None:
        raise ValueError("batch dimension must be known at compile time")
    for i in range(batch_prod.val[0]):
        temp_x = mb.gather(x=x_reshape, indices=[i], axis=0)
        temp_indices = mb.gather(x=indices_reshape, indices=[i], axis=0)
        temp_x = mb.squeeze(x=temp_x, axes=[0])
        temp_indices = mb.squeeze(x=temp_indices, axes=[0])
        func_args.update({"x": temp_x, "indices": temp_indices})
        temp = gather_func(**func_args)
        res.append(temp)
    res = mb.stack(values=res, axis=0)

    # (Step 3)
    # Finally, we reshape the result to shape [batch_1, ..., batch_n, *remaining_result_shape]
    res_shape = mb.shape(x=res)
    res_remaning_shape = mb.gather(x=res_shape, indices=_np.array(range(1, res_shape.shape[0])), axis=0)
    res_new_shape = mb.concat(values=[batch_shape, res_remaning_shape], axis=0)
    return mb.reshape(x=res, shape=res_new_shape, name=name)

    An utility function to compute gather and gather_nd with batch_dims
    
coremltools/converters/mil/frontend/tensorflow/ops.py