def gen_profiler(
    func_attrs,
    workdir,
    dim_info_dict,
    args_parse,
    gemm_flag,
    extra_code="",
    ndims=2,
    extra_shape_template=EXTRA_SHAPE_TEMPLATE,
    problem_args_template=PROBLEM_ARGS_TEMPLATE,
    extra_header_template=EXTRA_HEADER_TEMPLATE,
    tensor_decl_template=TENSOR_DECL_TEMPLATE,
):
    """Generates standalone executables for profiler.

    Parameters
    ----------
    func_attrs : Dict
        Operation attributes.
    workdir : str
        Directory to store the generated outputs.
    dim_info_dict: Dict[str, DimInfo]
        Generated from gemm._extract_dims().
        Used to store mapping between dim_names to input / output tensor dims.
    args_parse: str
        Profiler input argument parser.
    gemm_flag : str
        Flag telling which backend should be generated. options are '','bias','bias_relu','bias_sigmoid','bias_add_relu'.
    extra_code : str
        Extra code for self-defined operators.
    ndims : int
        Number of dims for each parameter, 2 for gemm, 3 for bmm
    extra_shape_template: jinja2.Template
        Shape evaluation template.
    problem_args_template: jinja2.Template
        Problem args template for profiler.
    extra_header_template: jinja2.Template
        Extra header template as we have different headers for gemm and bmm.
    tensor_decl_template: jinja2.Template
        Tensor declaration template.
    """
    op_type = func_attrs["op"]
    op_instance = func_attrs["op_instance"]
    # shape function
    op_func_shape = gemm_common.gen_shape_eval_code(
        indent=2, dtype="ck::index_t", dim_info_dict=dim_info_dict, is_ptr=True
    )

    adims = ["&a_dim" + str(i) for i in range(ndims)]
    bdims = ["&b_dim" + str(i) for i in range(ndims)]
    cdims = ["&c_dim" + str(i) for i in range(ndims)]
    pdims = []
    if func_attrs.get("shape") is not None:
        pdims = ["p_dim" + str(i) for i in range(len(func_attrs["shape"]))]
    extra_shape_func = extra_shape_template.render(indent="  ")
    file_pairs = []
    has_d0_flag = has_d0(func_attrs)
    has_d1_flag = has_d1(func_attrs)

    for op_name, op in op_instance.items():
        config = emit_instance(op)
        config_name = extract_config_name(config)
        instance = INSTANCE_TEMPLATE.render(
            name="DeviceGemmInstance", config_name=config_name, config=config
        )
        problem_args = problem_args_template.render(
            indent="  ",
            gemm_flag=gemm_flag,
            has_d0=has_d0_flag,
            has_d1=has_d1_flag,
        )
        exec_program = EXEC_TEMPLATE.render(
            indent="  ",
            instance="DeviceGemmInstance",
            problem_args=problem_args,
            is_profiler=True,
        )
        extra_header = extra_header_template.render(
            gemm_flag=gemm_flag, has_d0=has_d0_flag
        )
        op_func = SRC_TEMPLATE.render(
            instances=instance,
            function_name="gemm",
            ndims=ndims,
            pdims=len(pdims),
            has_d0=has_d0_flag,
            has_d1=has_d1_flag,
            shape_func=op_func_shape,
            extra_shape=extra_shape_func,
            exec_paths=exec_program,
            extra_code=extra_code,
            gemm_flag=gemm_flag,
            extra_header=extra_header,
        )
        structs_def = STRUCTS_DEF_TEMPLATE.render()
        tensor_decl = tensor_decl_template.render(
            gemm_flag=gemm_flag, has_d0=has_d0_flag, has_d1=has_d1_flag
        )
        func_call = FUNC_CALL_TEMPLATE.render(
            func_name="gemm",
            in_ptr="(void *) memory_pool->RequestHalfTensorByIdx(0)",
            weight_ptr="(void *) memory_pool->RequestHalfTensorByIdx(1)",
            out_ptr="(void *) memory_pool->RequestHalfTensorByIdx(2)",
            bias_ptr="(void *) memory_pool->RequestHalfTensorByIdx(3)",
            d0_ptr=(
                "(void *) memory_pool->RequestHalfTensorByIdx(4)" if has_d0_flag else ""
            ),
            d1_ptr=(
                "(void *) memory_pool->RequestHalfTensorByIdx(5)" if has_d1_flag else ""
            ),
            adims=adims,
            bdims=bdims,
            cdims=cdims,
            pdims=pdims,
            gemm_flag=gemm_flag,
        )

        code = PROFILER_TEMPLATE.render(
            structs_def=structs_def,
            op_func=op_func,
            args_parse=args_parse,
            tensor_decl=tensor_decl,
            func_call=func_call,
            op_name=op_name,
        )
        prefix = os.path.join(workdir, "profiler", op_type)
        if not os.path.exists(prefix):
            os.makedirs(prefix)
        src_path = os.path.join(prefix, op_name + ".cpp")
        obj_path = os.path.join(prefix, op_name)
        if os.path.exists(obj_path):
            continue
        with open(src_path, "w") as fo:
            fo.write(code)
        file_pairs.append((src_path, obj_path))
    return file_pairs
Generates standalone executables for profiler.

    Parameters
    ----------
    func_attrs : Dict
        Operation attributes.
    workdir : str
        Directory to store the generated outputs.
    dim_info_dict: Dict[str, DimInfo]
        Generated from gemm._extract_dims().
        Used to store mapping between dim_names to input / output tensor dims.
    args_parse: str
        Profiler input argument parser.
    gemm_flag : str
        Flag telling which backend should be generated. options are '','bias','bias_relu','bias_sigmoid','bias_add_relu'.
    extra_code : str
        Extra code for self-defined operators.
    ndims : int
        Number of dims for each parameter, 2 for gemm, 3 for bmm
    extra_shape_template: jinja2.Template
        Shape evaluation template.
    problem_args_template: jinja2.Template
        Problem args template for profiler.
    extra_header_template: jinja2.Template
        Extra header template as we have different headers for gemm and bmm.
    tensor_decl_template: jinja2.Template
        Tensor declaration template.
    
python/aitemplate/backend/rocm/gemm/common.py