def gen_profiler(
    func_attrs: Dict[str, Any],
    workdir: str,
    rank: int,
    shape_eval_template: jinja2.Template,
    exec_template: jinja2.Template,
    tensor_decl_template: jinja2.Template,
    extra_header_template: jinja2.Template,
    get_func_signature: Any,
    extra_code: str = "",
    func_call_template: jinja2.Template = FUNC_CALL_TEMPLATE,
    indent: str = "  ",
) -> str:
    """Generates standalone executables for profiler.

    Parameters
    ----------
    func_attrs : Dict
        Operation attributes.
    workdir : str
        Directory to store the generated outputs.
    rank: int
        Rank of the input tensor. If using [M, N] in exec_key, the rank here
        must be 2 because if implies that the inputs are reshaped for profiling.
        For code gen, the real shapes are used.
    exec_template : jinja2.Template
        Execution block template.
    tensor_decl_template: jinja2.Template
        Tensor declaration template.
    extra_header_template : jinja2.Template
        Extra header template.
    indent : str, optional
        Indent for codegen, target dependent e.g. C++, python, etc., by default "  ".
    """
    op_type = func_attrs["op"]
    shape_eval = shape_eval_template.render(rank=rank) if shape_eval_template else ""
    eps = func_attrs.get("eps", "1e-5")

    op_instance = func_attrs["op_instance"]
    file_pairs = []
    for op_name, op in op_instance.items():
        config = emit_instance(op)
        config_name = extract_config_name(config)
        instances = INSTANCE_TEMPLATE.render(
            name="DeviceInstance", config_name=config_name, config=config
        )
        exe_path = exec_template.render(
            instance="DeviceInstance",
            dtype="void",
            reduce_dims=rank - 1,
            rank=rank,
            eps=eps,
        )

        op_func = FUNC_TEMPLATE.render(
            instances_decl=instances,
            func_signature=get_func_signature(func_attrs),
            shape_eval=shape_eval,
            exec_paths=exe_path,
            extra_headers=extra_header_template.render(),
            extra_code=extra_code,
        )
        structs_def = STRUCTS_DEF_TEMPLATE.render()
        args_parse = ARGS_PARSE_TEMPLATE.render(rank=rank)
        tensor_decl = tensor_decl_template.render(rank=rank)

        input_dim_names = [f"in_{i}" for i in range(rank)]
        func_call = func_call_template.render(
            func_name=func_attrs["name"],
            input="(void *) memory_pool->RequestHalfTensorByIdx(0)",
            gamma="(void *) memory_pool->RequestHalfTensorByIdx(2)",
            beta="(void *) memory_pool->RequestHalfTensorByIdx(3)",
            output="(void *) memory_pool->RequestHalfTensorByIdx(1)",
            input_dim_names=input_dim_names,
            indent=indent,
        )
        code = PROFILER_TEMPLATE.render(
            op_func=op_func,
            structs_def=structs_def,
            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.
    rank: int
        Rank of the input tensor. If using [M, N] in exec_key, the rank here
        must be 2 because if implies that the inputs are reshaped for profiling.
        For code gen, the real shapes are used.
    exec_template : jinja2.Template
        Execution block template.
    tensor_decl_template: jinja2.Template
        Tensor declaration template.
    extra_header_template : jinja2.Template
        Extra header template.
    indent : str, optional
        Indent for codegen, target dependent e.g. C++, python, etc., by default "  ".
    
python/aitemplate/backend/rocm/normalization/norm_common.py