def gen_function_call(
    func_attrs,
    backend_spec,
    indent="  ",
):
    """Generates function call.

    Parameters
    ----------
    func_attrs : Dict[str, Any]
        Stores the operation attributes.
    backend_spec : BackendSpec
        CUDA / RocM type definitions
    indent : str, optional
        Indent for template, by default "  ".

    Returns
    -------
    str
        Rendered function call.
    """
    inputs = func_attrs["inputs"]
    input_accessors = func_attrs["input_accessors"]
    assert len(inputs) == len(input_accessors), (
        "expected inputs and input_accessors to have the same length, but got: "
        f'{len(inputs)}, {len(input_accessors)}, op: {func_attrs["name"]}'
    )
    original_inputs = func_attrs["original_inputs"]
    concatenate.check_rank(original_inputs, func_attrs["concat_dim"])
    orig_x, _ = concatenate.get_first_non_empty_input_if_any(original_inputs)
    y = func_attrs["outputs"][0]
    concat_dim = func_attrs["concat_dim"]

    input_names = [i._attrs["name"] for i in inputs]
    real_input_shape_defs = []
    real_input_shape_names = []
    # It's not uncommon that multiple shape definitions share the same
    # dimension values. In such a case, we could keep a single definition.
    # In some rare cases, this little trick can dramatically reduce the
    # number of lines generated for the relevant concatenate op and thus
    # may improve the compilation time. Currently, we only enable this
    # optimization for cases where we care about compilation time, as
    # for most cases, the unoptimized version can generate more readable
    # code while having little impact to the compilation time.
    seen_input_shape_dims = {}
    input_shape_name_subs = {}
    optimize_for_compilation_time = Target.current()._kwargs.get(
        "optimize_for_compilation_time", False
    )

    def _make_dims_key(dims):
        dim_vals = [
            str(d.value()) if isinstance(d, IntImm) else d._attrs["name"] for d in dims
        ]
        return ",".join(dim_vals)

    non_empty_input, non_empty_idx = concatenate.get_first_non_empty_input_if_any(
        inputs
    )
    non_empty_input_accessor = input_accessors[non_empty_idx]
    for idx, (i, input_accessor) in enumerate(zip(inputs, input_accessors)):
        input_shape_name = f'{i._attrs["name"]}_shape_{idx}'
        orig_input_shape = input_accessor.original_shapes
        if is_empty_rank1_tensor(orig_input_shape):
            orig_dim = orig_input_shape[0]
            orig_input_shape = deepcopy(non_empty_input_accessor.original_shapes)
            orig_input_shape[concat_dim] = orig_dim
        dims = ", ".join([dim._attrs["name"] for dim in orig_input_shape])
        dims_key = _make_dims_key(orig_input_shape)
        seen_shape_name = seen_input_shape_dims.get(dims_key, None)
        if not optimize_for_compilation_time or seen_shape_name is None:
            one_shape_def = INPUT_SHAPE_DEF_TEMPLATE.render(
                indent="      ",
                input_shape_name=input_shape_name,
                input_dims=dims,
                index_type=backend_spec.index_type,
            )
            real_input_shape_defs.append(one_shape_def)
            real_input_shape_names.append(input_shape_name)
            seen_input_shape_dims[dims_key] = input_shape_name
        else:
            real_input_shape_names.append(seen_shape_name)
            input_shape_name_subs[input_shape_name] = seen_shape_name

    y_shape = y._attrs["shape"]
    y_dim_refs = ", ".join(["&" + dim._attrs["name"] for dim in y_shape])

    input_masks = func_attrs["input_masks"]
    input_indices = [idx for idx, m in enumerate(input_masks) if m is True]
    assert len(inputs) == len(input_indices)
    concat_dim_sizes = []
    for idx, mask in enumerate(input_masks):
        if is_empty_rank1_tensor(original_inputs[idx]._attrs["shape"]):
            d = "0"
        elif mask:
            d = "-1"
        else:
            d = str(original_inputs[idx]._attrs["shape"][concat_dim].value())
        concat_dim_sizes.append(d)

    # update dim size for real inputs
    for input_accessor, input_index in zip(input_accessors, input_indices):
        if is_empty_rank1_tensor(input_accessor.original_shapes):
            dim = input_accessor.original_shapes[0]._attrs["name"]
        else:
            dim = input_accessor.original_shapes[concat_dim]._attrs["name"]
        concat_dim_sizes[input_index] = dim

    input_mask_values = ["true" if mask is True else "false" for mask in input_masks]

    # all input shape defs and names, including those that are masked out
    all_input_shape_defs = []
    all_input_shape_names = []
    seen_input_shape_dims = {}
    # first, create shape defs for inputs that have been masked off
    for (
        mask,
        orig_input,
    ) in zip(input_masks, original_inputs):
        if mask is False:
            orig_input_shape_name = f'orig_{orig_input._attrs["name"]}_shape'
            if orig_input_shape_name not in all_input_shape_names:
                dims = ", ".join(
                    [str(dim._attrs["values"][0]) for dim in orig_input._attrs["shape"]]
                )
                dims_key = _make_dims_key(orig_input._attrs["shape"])
                seen_shape_name = seen_input_shape_dims.get(dims_key, None)
                if not optimize_for_compilation_time or seen_shape_name is None:
                    one_shape_def = INPUT_SHAPE_DEF_TEMPLATE.render(
                        indent="      ",
                        input_shape_name=orig_input_shape_name,
                        input_dims=dims,
                        index_type=backend_spec.index_type,
                    )
                    all_input_shape_defs.append(one_shape_def)
                    seen_input_shape_dims[dims_key] = orig_input_shape_name
                    all_input_shape_names.append(orig_input_shape_name)
                else:
                    all_input_shape_names.append(seen_shape_name)
            else:
                all_input_shape_names.append(orig_input_shape_name)
        else:
            all_input_shape_names.append("")
    # update all_input_shapes with real input shapes
    for idx, (input_tensor, input_index) in enumerate(zip(inputs, input_indices)):
        input_shape_name = f'{input_tensor._attrs["name"]}_shape_{idx}'
        sub_name = input_shape_name_subs.get(input_shape_name, None)
        if sub_name is not None:
            input_shape_name = sub_name
        all_input_shape_names[input_index] = input_shape_name

    if optimize_for_compilation_time:
        inputs_initialization = _make_initialization_loops("inputs", input_names)
        real_input_shapes_initialization = _make_initialization_loops(
            "real_input_shapes", real_input_shape_names
        )
        all_input_shapes_initialization = _make_initialization_loops(
            "all_input_shapes", all_input_shape_names
        )
        concat_dim_sizes_initialization = _make_initialization_loops(
            "concat_dim_sizes", concat_dim_sizes
        )
        input_masks_initialization = _make_initialization_loops(
            "input_masks", input_mask_values
        )

        # A special version that aims for reducing compilation time for some uncommon
        # cases, e.g. where we have more than 100 concatenate ops, each of which taking
        # thousands of real inputs.
        return FUNC_CALL_TEMPLATE_OPT.render(
            indent=indent,
            num_inputs=len(input_names),
            inputs_initialization=inputs_initialization,
            real_input_shape_defs="".join(real_input_shape_defs),
            num_real_input_shapes=len(real_input_shape_names),
            real_input_shapes_initialization=real_input_shapes_initialization,
            all_input_shape_defs="".join(all_input_shape_defs),
            num_all_input_shapes=len(all_input_shape_names),
            all_input_shapes_initialization=all_input_shapes_initialization,
            num_input_masks=len(input_mask_values),
            input_masks_initialization=input_masks_initialization,
            num_concat_dim_sizes=len(concat_dim_sizes),
            concat_dim_sizes_initialization=concat_dim_sizes_initialization,
            output_dim_refs=y_dim_refs,
            func_name=func_attrs["name"],
            output=y._attrs["name"],
            output_ptr=y._attrs["name"],
            concat_dim=concat_dim,
            rank=len(orig_x._attrs["shape"]),
            num_real_inputs=len(inputs),
            num_all_inputs=len(original_inputs),
            index_type=backend_spec.index_type,
        )
    else:
        return FUNC_CALL_TEMPLATE.render(
            indent=indent,
            inputs=",\n      ".join(input_names),
            real_input_shape_defs="".join(real_input_shape_defs),
            real_input_shapes=", ".join(real_input_shape_names),
            all_input_shape_defs="".join(all_input_shape_defs),
            all_input_shapes=", ".join(all_input_shape_names),
            input_masks=", ".join(input_mask_values),
            concat_dim_sizes=", ".join(concat_dim_sizes),
            output_dim_refs=y_dim_refs,
            func_name=func_attrs["name"],
            output=y._attrs["name"],
            output_ptr=y._attrs["name"],
            concat_dim=concat_dim,
            rank=len(orig_x._attrs["shape"]),
            num_real_inputs=len(inputs),
            num_all_inputs=len(original_inputs),
            index_type=backend_spec.index_type,
        )
Generates function call.

Parameters
----------
func_attrs : Dict[str, Any]
Stores the operation attributes.
backend_spec : BackendSpec
CUDA / RocM type definitions
indent : str, optional
Indent for template, by default "  ".

Returns
-------
str
Rendered function call.
python/aitemplate/backend/common/concatenate_common.py