blob: 63408314a9d75903c34b4da3e95648d68c4c2ffc [file] [edit]
# Lower MLGO models to C++ headers via the MLIR-based pipeline.
#
# Each entry in ${models} has the form "cli-flag,path/to/model.mlir,type".
# For entries whose type matches ${target_type}, this function:
# 1. Lowers model MLIR with ${mlir_opt} and translates it with ${mlir_translate}
# to a C++ header defining Model<N> in ${LLVM_INCLUDE_DIR}/${include_subdir}.
# 2. Generates ${generated_file_basename}.def in ${LLVM_INCLUDE_DIR}/${include_subdir}
# with one MLGO_MODEL(ClassName, "cli-flag") entry per model.
# 3. Generates ${generated_file_basename}.h in ${LLVM_INCLUDE_DIR}/${include_subdir}
# including all generated model headers.
# 4. Appends the target driving generation to ${out_var} in the caller's scope.
# 5. Defines LLVM_HAVE_MLIR_LOWERING_<TARGET_TYPE>.
function(mlgo_lower_models models mlir_opt mlir_translate target_type
include_subdir generated_file_basename out_var)
set(def_file "${generated_file_basename}.def")
set(umbrella_header "${generated_file_basename}.h")
if(NOT IS_ABSOLUTE "${mlir_opt}")
cmake_path(ABSOLUTE_PATH mlir_opt BASE_DIRECTORY "${CMAKE_BINARY_DIR}")
endif()
if(NOT IS_ABSOLUTE "${mlir_translate}")
cmake_path(ABSOLUTE_PATH mlir_translate BASE_DIRECTORY "${CMAKE_BINARY_DIR}")
endif()
string(APPEND def_content
"/* Auto-generated by CMake */\n"
"#ifndef MLGO_MODEL\n"
"# define MLGO_MODEL(CLASS_NAME, CLI_FLAG)\n"
"#endif\n\n"
)
set(headers_content "/* Auto-generated by CMake */\nnamespace llvm {\n")
set(mlgo_gen_targets "")
set(model_index 1)
foreach(model_info IN LISTS models)
# Parse comma-separated fields (from the LLVM_MLGO_MODELS flag)
# cli-flag,path/to/model.mlir,type
string(REPLACE "," ";" model_fields "${model_info}")
list(GET model_fields 0 cli_flag)
list(GET model_fields 1 model_path)
list(GET model_fields 2 model_type)
# Only process models matching target_type
if (NOT model_type STREQUAL target_type)
continue()
endif()
set(class_name "${target_type}_Model${model_index}")
math(EXPR model_index "${model_index} + 1")
if (NOT IS_ABSOLUTE "${model_path}")
get_filename_component(model_path "${model_path}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
endif()
set(emitc_mlir "${CMAKE_CURRENT_BINARY_DIR}/${class_name}_emitc.mlir")
set(header_file "${LLVM_INCLUDE_DIR}/${include_subdir}/${class_name}.h")
# Pass pipeline to lower MLIR models to EmitC dialect
# TODO: Simplify with builtin pipeline for translation.
# TODO: It isn't ideal to hardcode this pass pipeline here. It would be better to use the transform dialect.
# TODO: This pipeline is tested independently in the following files. Make sure that tests and this CMake logic share the definition.
# mlir/test/Integration/Dialect/EmitC/inline-oz-test-model-tosa.mlir
# mlir/test/Integration/Dialect/EmitC/regalloc-eviction-test-model-tosa.mlir
set(mlir_passes
"func.func(tosa-to-linalg-named,tosa-to-linalg,tosa-to-arith,tosa-to-tensor)"
"symbol-privatize"
"scalarize-single-element-tensor-return"
"one-shot-bufferize{bufferize-function-boundaries=true function-boundary-type-conversion=identity-layout-map buffer-alignment=0}"
"buffer-results-to-out-params{hoist-static-allocs=true}"
"func.func(promote-buffers-to-stack)"
"buffer-deallocation-pipeline"
"func.func(convert-linalg-to-loops)"
"expand-strided-metadata"
"canonicalize"
"memref-elide-reinterpret-cast"
"convert-to-emitc"
"math-expand-ops{ops=rsqrt}"
"arith-expand{include-min-max-f=true include-min-max-i=true}"
"convert-math-to-emitc"
"convert-arith-to-emitc"
"wrap-emitc-func-in-class{class-name-format=${class_name}}"
"mlgo-add-reflection-map{included-field-attrs=tf_saved_model.index_path}"
)
string(JOIN "," pass_pipeline ${mlir_passes})
set(pass_pipeline "builtin.module(${pass_pipeline})")
# 1. Run MLIR pipeline to lower model_path to EmitC MLIR
add_custom_command(OUTPUT ${emitc_mlir}
COMMAND ${mlir_opt} "--pass-pipeline=${pass_pipeline}"
${model_path} -o ${emitc_mlir}
DEPENDS ${model_path}
VERBATIM
)
# 2. Translate EmitC MLIR to C++ header
add_custom_command(OUTPUT ${header_file}
COMMAND ${mlir_translate} -mlir-to-cpp ${emitc_mlir} -o ${header_file} --mlir-print-stacktrace-on-diagnostic
DEPENDS ${emitc_mlir}
VERBATIM
)
# Set properties so CMake knows these are generated during the build
set_source_files_properties(${emitc_mlir} PROPERTIES GENERATED 1)
set_source_files_properties(${header_file} PROPERTIES GENERATED 1)
# Custom target to force generation of this header
add_custom_target(mlgo_model_gen_${model_type}_${class_name} DEPENDS ${header_file})
list(APPEND mlgo_gen_targets mlgo_model_gen_${model_type}_${class_name})
# Append the model metadata to the .def file
string(APPEND def_content "MLGO_MODEL(${class_name}, \"${cli_flag}\")\n")
# Append to the master header content
string(APPEND headers_content
"namespace ${class_name}_ns {\n"
"#include \"${include_subdir}/${class_name}.h\"\n"
"} // namespace ${class_name}_ns\n"
"using ${class_name}_ns::${class_name};\n"
)
endforeach()
string(APPEND def_content "\n#undef MLGO_MODEL\n")
string(APPEND headers_content "} // namespace llvm\n")
# Stage the generated files, then only update the copies under
# LLVM_INCLUDE_DIR when their content changed, so that reconfiguring does not
# trigger rebuilds of their consumers.
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${def_file}.tmp" "${def_content}")
configure_file("${CMAKE_CURRENT_BINARY_DIR}/${def_file}.tmp"
"${LLVM_INCLUDE_DIR}/${include_subdir}/${def_file}" COPYONLY)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${umbrella_header}.tmp" "${headers_content}")
configure_file("${CMAKE_CURRENT_BINARY_DIR}/${umbrella_header}.tmp"
"${LLVM_INCLUDE_DIR}/${include_subdir}/${umbrella_header}" COPYONLY)
if (mlgo_gen_targets)
add_custom_target(mlgo_models_gen_${target_type} DEPENDS ${mlgo_gen_targets})
set(${out_var} ${${out_var}} mlgo_models_gen_${target_type} PARENT_SCOPE)
string(TOUPPER ${target_type} target_type_allcaps)
add_compile_definitions(LLVM_HAVE_MLIR_LOWERING_${target_type_allcaps})
endif()
endfunction()