[llvm] [MLGO] Model selection for models lowered through EmitC (PR #212650)
Bhavesh M via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 14 10:55:06 PDT 2026
https://github.com/beamandala updated https://github.com/llvm/llvm-project/pull/212650
>From 45813381501ab784e22db6aea1f606def0c6543d Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Mon, 27 Jul 2026 12:34:38 -0700
Subject: [PATCH 01/12] Model selection
---
llvm/CMakeLists.txt | 19 ++++++
llvm/cmake/modules/MLGOCompile.cmake | 99 ++++++++++++++++++++++++++++
llvm/lib/Analysis/CMakeLists.txt | 13 +++-
llvm/lib/CodeGen/CMakeLists.txt | 13 +++-
llvm/test/CMakeLists.txt | 1 +
llvm/test/lit.cfg.py | 3 +
llvm/test/lit.site.cfg.py.in | 1 +
7 files changed, 147 insertions(+), 2 deletions(-)
create mode 100644 llvm/cmake/modules/MLGOCompile.cmake
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index a9d9f1d11ab44..8490c70617a7c 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -1226,6 +1226,21 @@ set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LLVM_LIBRARY_DIR} )
# For up-to-date instructions for installing the TFLite dependency, refer to
# the bot setup script: https://github.com/google/ml-compiler-opt/blob/main/buildbot/buildbot_init.sh
set(LLVM_HAVE_TFLITE "" CACHE BOOL "Use tflite")
+
+set(LLVM_MLGO_MODELS "" CACHE STRING "List of AOT MLGO models")
+set(LLVM_MLGO_MLIR_OPT "mlir-opt" CACHE STRING "Path or system binary name for mlir-opt")
+set(LLVM_MLGO_MLIR_TRANSLATE "mlir-translate" CACHE STRING "Path or system binary name for mlir-translate")
+
+if (NOT LLVM_MLGO_MODELS STREQUAL "")
+ set(LLVM_HAVE_EMITC_COMPILE "ON" CACHE BOOL "MLGO AOT models compiled with EmitC are available" FORCE)
+else()
+ set(LLVM_HAVE_EMITC_COMPILE "OFF" CACHE BOOL "MLGO AOT models compiled with EmitC are available" FORCE)
+endif()
+
+if (LLVM_HAVE_EMITC_COMPILE AND LLVM_HAVE_TFLITE)
+ message(FATAL_ERROR "Only one of LLVM_HAVE_TFLITE and LLVM_HAVE_EMITC_COMPILE can be enabled.")
+endif()
+
if (LLVM_HAVE_TFLITE)
find_package(tensorflow-lite REQUIRED)
endif()
@@ -1253,6 +1268,10 @@ if (NOT TENSORFLOW_AOT_PATH STREQUAL "")
install(TARGETS tf_xla_runtime EXPORT LLVMDevelopmentExports
ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT tf_xla_runtime)
set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS tf_xla_runtime)
+
+ if (LLVM_HAVE_TF_AOT AND LLVM_HAVE_EMITC_COMPILE)
+ message(FATAL_ERROR "Only one of LLVM_HAVE_TF_AOT and LLVM_HAVE_EMITC_COMPILE can be enabled.")
+ endif()
# Once we add more modules, we should handle this more automatically.
if (DEFINED LLVM_OVERRIDE_MODEL_HEADER_INLINERSIZEMODEL)
set(LLVM_INLINER_MODEL_PATH "none")
diff --git a/llvm/cmake/modules/MLGOCompile.cmake b/llvm/cmake/modules/MLGOCompile.cmake
new file mode 100644
index 0000000000000..c401f87bf23f7
--- /dev/null
+++ b/llvm/cmake/modules/MLGOCompile.cmake
@@ -0,0 +1,99 @@
+# Compile MLGO models expressed as MLIR to C++ headers via the EmitC pipeline.
+#
+# Each entry of ${models} has the form
+# "cli-flag,path/to/model.mlir,type". Entries whose type matches
+# ${target_type} are lowered with ${mlir_opt} and translated with
+# ${mlir_translate} to a C++ header defining Model<N>, placed in
+# ${LLVM_INCLUDE_DIR}/${include_subdir}. In the same directory, generate
+# ${def_file}, with one MLGO_MODEL(ClassName, "cli-flag") entry per model, and
+# ${umbrella_header}, including all the generated model headers.
+# Append the target driving generation to MLDeps in the caller's scope, and
+# define LLVM_HAVE_EMITC_COMPILE_<TARGET_TYPE>.
+function(mlgo_compile_models models mlir_opt mlir_translate target_type
+ include_subdir def_file umbrella_header)
+ if ("${mlir_opt}" MATCHES "/")
+ get_filename_component(mlir_opt "${mlir_opt}" ABSOLUTE BASE_DIR "${CMAKE_BINARY_DIR}")
+ endif()
+ if ("${mlir_translate}" MATCHES "/")
+ get_filename_component(mlir_translate "${mlir_translate}" ABSOLUTE BASE_DIR "${CMAKE_BINARY_DIR}")
+ endif()
+
+ set(DEF_CONTENT "/* Auto-generated by CMake */\n")
+ set(DEF_CONTENT "${DEF_CONTENT}#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: 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 "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")
+
+ # 1. Run MLIR pipeline to compile MODEL_PATH to EmitC MLIR
+ add_custom_command(OUTPUT ${EMITC_MLIR}
+ COMMAND ${mlir_opt} "--pass-pipeline=builtin.module(func.func(tosa-to-linalg-named,tosa-to-linalg,tosa-to-arith,tosa-to-tensor),symbol-privatize,mlgo-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,wrap-emitc-func-in-class{class-name-format=${CLASS_NAME}},math-expand-ops{ops=rsqrt},arith-expand,convert-math-to-emitc,convert-arith-to-emitc)"
+ ${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_${CLASS_NAME} DEPENDS ${HEADER_FILE})
+ list(APPEND MLGO_GEN_TARGETS mlgo_model_gen_${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}\nusing ${CLASS_NAME}_ns::${CLASS_NAME};\n")
+ endforeach()
+
+ string(APPEND DEF_CONTENT "\n#undef MLGO_MODEL\n")
+ string(APPEND HEADERS_CONTENT "}\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(MLDeps ${MLDeps} mlgo_models_gen_${target_type} PARENT_SCOPE)
+ string(TOUPPER ${target_type} target_type_allcaps)
+ add_compile_definitions(LLVM_HAVE_EMITC_COMPILE_${target_type_allcaps})
+ endif()
+endfunction()
diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt
index aecea1bb92a1f..ed7fc6e7e26c0 100644
--- a/llvm/lib/Analysis/CMakeLists.txt
+++ b/llvm/lib/Analysis/CMakeLists.txt
@@ -1,4 +1,15 @@
-if (DEFINED LLVM_HAVE_TF_AOT OR LLVM_HAVE_TFLITE)
+if (LLVM_HAVE_EMITC_COMPILE)
+ include(MLGOCompile)
+ mlgo_compile_models(
+ "${LLVM_MLGO_MODELS}"
+ "${LLVM_MLGO_MLIR_OPT}"
+ "${LLVM_MLGO_MLIR_TRANSLATE}"
+ inliner
+ llvm/Analysis
+ MLGOModels.def
+ InlinerSizeModelMulti.h
+ )
+elseif (DEFINED LLVM_HAVE_TF_AOT OR LLVM_HAVE_TFLITE)
include(TensorFlowCompile)
set(LLVM_INLINER_MODEL_PATH_DEFAULT "models/inliner-Oz")
diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt
index c572128b023c1..1c913c9c2f8ff 100644
--- a/llvm/lib/CodeGen/CMakeLists.txt
+++ b/llvm/lib/CodeGen/CMakeLists.txt
@@ -1,4 +1,15 @@
-if (DEFINED LLVM_HAVE_TF_AOT OR LLVM_HAVE_TFLITE)
+if (LLVM_HAVE_EMITC_COMPILE)
+ include(MLGOCompile)
+ mlgo_compile_models(
+ "${LLVM_MLGO_MODELS}"
+ "${LLVM_MLGO_MLIR_OPT}"
+ "${LLVM_MLGO_MLIR_TRANSLATE}"
+ regalloc
+ llvm/CodeGen
+ RegAllocEvictModels.def
+ RegAllocEvictModelMulti.h
+ )
+elseif (DEFINED LLVM_HAVE_TF_AOT OR LLVM_HAVE_TFLITE)
include(TensorFlowCompile)
set(LLVM_RAEVICT_MODEL_PATH_DEFAULT "models/regalloc-eviction")
diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt
index 2a00a429e2626..90d3cd494565a 100644
--- a/llvm/test/CMakeLists.txt
+++ b/llvm/test/CMakeLists.txt
@@ -19,6 +19,7 @@ llvm_canonicalize_cmake_booleans(
LLVM_EXAMPLEIRTRANSFORMS_LINK_INTO_TOOLS
LLVM_HAVE_TF_AOT
LLVM_HAVE_TFLITE
+ LLVM_HAVE_EMITC_COMPILE
LLVM_ENABLE_PROFCHECK
LLVM_INLINER_MODEL_AUTOGENERATED
LLVM_RAEVICT_MODEL_AUTOGENERATED
diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py
index 03a57a59fa04a..782e653448399 100644
--- a/llvm/test/lit.cfg.py
+++ b/llvm/test/lit.cfg.py
@@ -584,6 +584,9 @@ def enable_ptxas(ptxas_executable):
if config.have_tf_aot:
config.available_features.add("have_tf_aot")
+if getattr(config, "have_emitc_compile", False):
+ config.available_features.add("have_emitc_compile")
+
if getattr(config, "have_opencsd", False):
config.available_features.add("opencsd")
diff --git a/llvm/test/lit.site.cfg.py.in b/llvm/test/lit.site.cfg.py.in
index 130d9011df619..24279c94c2ae8 100644
--- a/llvm/test/lit.site.cfg.py.in
+++ b/llvm/test/lit.site.cfg.py.in
@@ -57,6 +57,7 @@ config.has_plugins = @LLVM_ENABLE_PLUGINS@
config.linked_bye_extension = @LLVM_BYE_LINK_INTO_TOOLS@
config.linked_exampleirtransforms_extension = @LLVM_EXAMPLEIRTRANSFORMS_LINK_INTO_TOOLS@
config.have_tf_aot = @LLVM_HAVE_TF_AOT@
+config.have_emitc_compile = @LLVM_HAVE_EMITC_COMPILE@
config.have_tflite = @LLVM_HAVE_TFLITE@
config.enable_profcheck = @LLVM_ENABLE_PROFCHECK@
config.llvm_inliner_model_autogenerated = @LLVM_INLINER_MODEL_AUTOGENERATED@
>From 5ec8a824edcddc6566fceaf84b01cc11b9f6dc22 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 28 Jul 2026 16:11:29 -0700
Subject: [PATCH 02/12] EmitCModelRunner
---
llvm/cmake/modules/MLGOCompile.cmake | 26 ++++++-
llvm/include/llvm/Analysis/EmitCModelRunner.h | 76 +++++++++++++++++++
llvm/lib/Analysis/MLInlineAdvisor.cpp | 58 ++++++++++++--
llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp | 62 +++++++++++++--
4 files changed, 210 insertions(+), 12 deletions(-)
create mode 100644 llvm/include/llvm/Analysis/EmitCModelRunner.h
diff --git a/llvm/cmake/modules/MLGOCompile.cmake b/llvm/cmake/modules/MLGOCompile.cmake
index c401f87bf23f7..b4e340087bd62 100644
--- a/llvm/cmake/modules/MLGOCompile.cmake
+++ b/llvm/cmake/modules/MLGOCompile.cmake
@@ -47,9 +47,33 @@ function(mlgo_compile_models models mlir_opt mlir_translate target_type
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
+ set(EMITC_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"
+ "wrap-emitc-func-in-class{class-name-format=${CLASS_NAME}}"
+ "mlgo-add-reflection-map{included-field-attrs=tf_saved_model.index_path}"
+ "math-expand-ops{ops=rsqrt}"
+ "arith-expand"
+ "convert-math-to-emitc"
+ "convert-arith-to-emitc"
+ )
+ string(JOIN "," PASS_PIPELINE ${EMITC_PASSES})
+ set(PASS_PIPELINE "builtin.module(${PASS_PIPELINE})")
+
# 1. Run MLIR pipeline to compile MODEL_PATH to EmitC MLIR
add_custom_command(OUTPUT ${EMITC_MLIR}
- COMMAND ${mlir_opt} "--pass-pipeline=builtin.module(func.func(tosa-to-linalg-named,tosa-to-linalg,tosa-to-arith,tosa-to-tensor),symbol-privatize,mlgo-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,wrap-emitc-func-in-class{class-name-format=${CLASS_NAME}},math-expand-ops{ops=rsqrt},arith-expand,convert-math-to-emitc,convert-arith-to-emitc)"
+ COMMAND ${mlir_opt} "--pass-pipeline=${PASS_PIPELINE}"
${MODEL_PATH} -o ${EMITC_MLIR}
DEPENDS ${MODEL_PATH}
VERBATIM
diff --git a/llvm/include/llvm/Analysis/EmitCModelRunner.h b/llvm/include/llvm/Analysis/EmitCModelRunner.h
new file mode 100644
index 0000000000000..6c36f930aeff0
--- /dev/null
+++ b/llvm/include/llvm/Analysis/EmitCModelRunner.h
@@ -0,0 +1,76 @@
+//===- EmitCModelRunner.h - Fast, precompiled model runner ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a model runner wrapping an EmitC compiled ML model.
+// Only inference is supported.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ANALYSIS_EMITCMODELRUNNER_H
+#define LLVM_ANALYSIS_EMITCMODELRUNNER_H
+
+#include "llvm/Analysis/MLModelRunner.h"
+#include "llvm/Analysis/TensorSpec.h"
+
+#include <memory>
+#include <type_traits>
+
+namespace llvm {
+
+template <class TGen>
+class EmitCModelRunner final : public MLModelRunner {
+public:
+ template <class FType>
+ EmitCModelRunner(LLVMContext &Ctx, const FType &InputSpec,
+ std::unique_ptr<TGen> Model = std::make_unique<TGen>())
+ : MLModelRunner(Ctx, MLModelRunner::Kind::Release, InputSpec.size()),
+ CompiledModel(std::move(Model)) {
+ assert(CompiledModel && "The CompiledModel should be valid");
+ for (size_t I = 0; I < InputSpec.size(); ++I)
+ populateTensor(I, InputSpec[I]);
+ }
+
+ ~EmitCModelRunner() override = default;
+
+ static bool classof(const MLModelRunner *R) {
+ return R->getKind() == MLModelRunner::Kind::Release;
+ }
+
+protected:
+ void *evaluateUntyped() override { return evaluateImpl(); }
+
+private:
+ void populateTensor(size_t Pos, const TensorSpec &Spec) {
+ void *Buffer = nullptr;
+ auto It = CompiledModel->reflectionMap.find(Spec.name());
+ if (It != CompiledModel->reflectionMap.end())
+ Buffer = static_cast<void *>(It->second);
+ setUpBufferForTensor(Pos, Spec, Buffer);
+ }
+
+ using ResultType = decltype(std::declval<TGen>()());
+
+ template <typename R = ResultType>
+ std::enable_if_t<!std::is_void_v<R>, void *> evaluateImpl() {
+ Result = (*CompiledModel)();
+ return &Result;
+ }
+
+ template <typename R = ResultType>
+ std::enable_if_t<std::is_void_v<R>, void *> evaluateImpl() {
+ (*CompiledModel)();
+ return nullptr;
+ }
+
+ std::conditional_t<std::is_void_v<ResultType>, char, ResultType> Result{};
+ std::unique_ptr<TGen> CompiledModel;
+};
+
+} // namespace llvm
+
+#endif // LLVM_ANALYSIS_EMITCMODELRUNNER_H
diff --git a/llvm/lib/Analysis/MLInlineAdvisor.cpp b/llvm/lib/Analysis/MLInlineAdvisor.cpp
index 9a5ae2ae26799..7245a65141782 100644
--- a/llvm/lib/Analysis/MLInlineAdvisor.cpp
+++ b/llvm/lib/Analysis/MLInlineAdvisor.cpp
@@ -72,20 +72,66 @@ using CompiledModelType = llvm::InlinerSizeModel;
using CompiledModelType = NoopSavedModelImpl;
#endif
+#if defined(LLVM_HAVE_EMITC_COMPILE_INLINER)
+#include "llvm/Analysis/EmitCModelRunner.h"
+#include "llvm/Analysis/InlinerSizeModelMulti.h"
+
+enum class MLGOModelChoice {
+ Default,
+#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) CLASS_NAME,
+#include "llvm/Analysis/MLGOModels.def"
+};
+
+static llvm::cl::opt<MLGOModelChoice> SelectedMLGOModel(
+ "mlgo-model",
+ llvm::cl::desc("Select the MLGO model to execute:"),
+ llvm::cl::init(MLGOModelChoice::Default),
+ llvm::cl::values(
+ clEnumValN(MLGOModelChoice::Default, "default", "Use standard heuristic")
+#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
+ , clEnumValN(MLGOModelChoice::CLASS_NAME, CLI_FLAG, "Use the " CLI_FLAG " MLGO model")
+#include "llvm/Analysis/MLGOModels.def"
+ )
+);
+
+static std::unique_ptr<MLModelRunner> createMLGOModelRunner(LLVMContext &Ctx, const std::vector<TensorSpec> &InputFeatures) {
+ switch (SelectedMLGOModel) {
+ case MLGOModelChoice::Default:
+ return nullptr;
+#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
+ case MLGOModelChoice::CLASS_NAME: \
+ return std::make_unique<EmitCModelRunner<CLASS_NAME>>(Ctx, InputFeatures);
+#include "llvm/Analysis/MLGOModels.def"
+ }
+ llvm_unreachable("Unknown MLGO model type!");
+}
+#endif
+
std::unique_ptr<InlineAdvisor>
llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
std::function<bool(CallBase &)> GetDefaultAdvice) {
if (!llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() &&
- InteractiveChannelBaseName.empty())
+ InteractiveChannelBaseName.empty()
+#if defined(LLVM_HAVE_EMITC_COMPILE_INLINER)
+ && SelectedMLGOModel == MLGOModelChoice::Default
+#endif
+ )
return nullptr;
auto RunnerFactory = [&](const std::vector<TensorSpec> &InputFeatures)
-> std::unique_ptr<MLModelRunner> {
std::unique_ptr<MLModelRunner> AOTRunner;
- if (InteractiveChannelBaseName.empty())
- AOTRunner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
- M.getContext(), InputFeatures, DecisionName,
- EmbeddedModelRunnerOptions().setModelSelector(ModelSelector));
- else {
+ if (InteractiveChannelBaseName.empty()) {
+#if defined(LLVM_HAVE_EMITC_COMPILE_INLINER)
+ if (SelectedMLGOModel != MLGOModelChoice::Default) {
+ AOTRunner = createMLGOModelRunner(M.getContext(), InputFeatures);
+ } else
+#endif
+ {
+ AOTRunner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
+ M.getContext(), InputFeatures, DecisionName,
+ EmbeddedModelRunnerOptions().setModelSelector(ModelSelector));
+ }
+ } else {
AOTRunner = std::make_unique<InteractiveModelRunner>(
M.getContext(), InputFeatures, InlineDecisionSpec,
InteractiveChannelBaseName + ".out",
diff --git a/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp b/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
index 54f1be77971d5..b4a55dbc79d7b 100644
--- a/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
+++ b/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
@@ -55,6 +55,41 @@ using CompiledModelType = RegAllocEvictModel;
using CompiledModelType = NoopSavedModelImpl;
#endif
+#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
+#include "llvm/Analysis/EmitCModelRunner.h"
+#include "llvm/CodeGen/RegAllocEvictModelMulti.h"
+
+enum class MLGORegAllocModelChoice {
+ Default,
+#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) CLASS_NAME,
+#include "llvm/CodeGen/RegAllocEvictModels.def"
+};
+
+static llvm::cl::opt<MLGORegAllocModelChoice> SelectedMLGORegAllocModel(
+ "regalloc-mlgo-model",
+ llvm::cl::desc("Select the MLGO model to execute for register allocation:"),
+ llvm::cl::init(MLGORegAllocModelChoice::Default),
+ llvm::cl::values(
+ clEnumValN(MLGORegAllocModelChoice::Default, "default", "Use standard heuristic")
+#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
+ , clEnumValN(MLGORegAllocModelChoice::CLASS_NAME, CLI_FLAG, "Use the " CLI_FLAG " MLGO model")
+#include "llvm/CodeGen/RegAllocEvictModels.def"
+ )
+);
+
+static std::unique_ptr<MLModelRunner> createMLGORegAllocModelRunner(LLVMContext &Ctx, const std::vector<TensorSpec> &InputFeatures) {
+ switch (SelectedMLGORegAllocModel) {
+ case MLGORegAllocModelChoice::Default:
+ return nullptr;
+#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
+ case MLGORegAllocModelChoice::CLASS_NAME: \
+ return std::make_unique<EmitCModelRunner<CLASS_NAME>>(Ctx, InputFeatures);
+#include "llvm/CodeGen/RegAllocEvictModels.def"
+ }
+ llvm_unreachable("Unknown MLGO model type!");
+}
+#endif
+
static cl::opt<std::string> InteractiveChannelBaseName(
"regalloc-evict-interactive-channel-base", cl::Hidden,
cl::desc(
@@ -368,14 +403,22 @@ class ReleaseModeEvictionAdvisorProvider final
getAdvisor(const MachineFunction &MF, const RAGreedy &RA,
MachineBlockFrequencyInfo *MBFI, MachineLoopInfo *Loops) override {
if (!Runner) {
- if (InteractiveChannelBaseName.empty())
- Runner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
- MF.getFunction().getContext(), InputFeatures, DecisionName);
- else
+ if (InteractiveChannelBaseName.empty()) {
+#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
+ if (SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default) {
+ Runner = createMLGORegAllocModelRunner(MF.getFunction().getContext(), InputFeatures);
+ } else
+#endif
+ {
+ Runner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
+ MF.getFunction().getContext(), InputFeatures, DecisionName);
+ }
+ } else {
Runner = std::make_unique<InteractiveModelRunner>(
MF.getFunction().getContext(), InputFeatures, DecisionSpec,
InteractiveChannelBaseName + ".out",
InteractiveChannelBaseName + ".in");
+ }
}
assert(MBFI && Loops &&
"Invalid provider state: must have analysis available");
@@ -1018,7 +1061,13 @@ bool RegAllocScoring::runOnMachineFunction(MachineFunction &MF) {
RegAllocEvictionAdvisorProvider *
llvm::createReleaseModeAdvisorProvider(LLVMContext &Ctx) {
- return new ReleaseModeEvictionAdvisorProvider(Ctx);
+ return llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
+ !InteractiveChannelBaseName.empty()
+#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
+ || SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default
+#endif
+ ? new ReleaseModeEvictionAdvisorProvider(Ctx)
+ : nullptr;
}
RegAllocEvictionAdvisorProvider *
@@ -1033,6 +1082,9 @@ RegAllocEvictionAdvisorAnalysisLegacy *
llvm::createReleaseModeAdvisorAnalysisLegacy() {
return llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
!InteractiveChannelBaseName.empty()
+#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
+ || SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default
+#endif
? new ReleaseModeEvictionAdvisorAnalysisLegacy()
: nullptr;
}
>From 62783b720ab01ad48213c9ac44eb6dc7982b10c1 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 28 Jul 2026 16:15:05 -0700
Subject: [PATCH 03/12] Formatting
---
llvm/include/llvm/Analysis/EmitCModelRunner.h | 3 +-
llvm/lib/Analysis/MLInlineAdvisor.cpp | 31 ++++++++--------
llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp | 35 +++++++++++--------
3 files changed, 37 insertions(+), 32 deletions(-)
diff --git a/llvm/include/llvm/Analysis/EmitCModelRunner.h b/llvm/include/llvm/Analysis/EmitCModelRunner.h
index 6c36f930aeff0..f60ca034d232d 100644
--- a/llvm/include/llvm/Analysis/EmitCModelRunner.h
+++ b/llvm/include/llvm/Analysis/EmitCModelRunner.h
@@ -22,8 +22,7 @@
namespace llvm {
-template <class TGen>
-class EmitCModelRunner final : public MLModelRunner {
+template <class TGen> class EmitCModelRunner final : public MLModelRunner {
public:
template <class FType>
EmitCModelRunner(LLVMContext &Ctx, const FType &InputSpec,
diff --git a/llvm/lib/Analysis/MLInlineAdvisor.cpp b/llvm/lib/Analysis/MLInlineAdvisor.cpp
index 7245a65141782..e04805ff135af 100644
--- a/llvm/lib/Analysis/MLInlineAdvisor.cpp
+++ b/llvm/lib/Analysis/MLInlineAdvisor.cpp
@@ -83,24 +83,25 @@ enum class MLGOModelChoice {
};
static llvm::cl::opt<MLGOModelChoice> SelectedMLGOModel(
- "mlgo-model",
- llvm::cl::desc("Select the MLGO model to execute:"),
+ "mlgo-model", llvm::cl::desc("Select the MLGO model to execute:"),
llvm::cl::init(MLGOModelChoice::Default),
- llvm::cl::values(
- clEnumValN(MLGOModelChoice::Default, "default", "Use standard heuristic")
-#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
- , clEnumValN(MLGOModelChoice::CLASS_NAME, CLI_FLAG, "Use the " CLI_FLAG " MLGO model")
+ llvm::cl::values(clEnumValN(MLGOModelChoice::Default, "default",
+ "Use standard heuristic")
+#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
+ , clEnumValN(MLGOModelChoice::CLASS_NAME, CLI_FLAG, \
+ "Use the " CLI_FLAG " MLGO model")
#include "llvm/Analysis/MLGOModels.def"
- )
-);
+ ));
-static std::unique_ptr<MLModelRunner> createMLGOModelRunner(LLVMContext &Ctx, const std::vector<TensorSpec> &InputFeatures) {
+static std::unique_ptr<MLModelRunner>
+createMLGOModelRunner(LLVMContext &Ctx,
+ const std::vector<TensorSpec> &InputFeatures) {
switch (SelectedMLGOModel) {
- case MLGOModelChoice::Default:
- return nullptr;
-#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
- case MLGOModelChoice::CLASS_NAME: \
- return std::make_unique<EmitCModelRunner<CLASS_NAME>>(Ctx, InputFeatures);
+ case MLGOModelChoice::Default:
+ return nullptr;
+#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
+ case MLGOModelChoice::CLASS_NAME: \
+ return std::make_unique<EmitCModelRunner<CLASS_NAME>>(Ctx, InputFeatures);
#include "llvm/Analysis/MLGOModels.def"
}
llvm_unreachable("Unknown MLGO model type!");
@@ -115,7 +116,7 @@ llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
#if defined(LLVM_HAVE_EMITC_COMPILE_INLINER)
&& SelectedMLGOModel == MLGOModelChoice::Default
#endif
- )
+ )
return nullptr;
auto RunnerFactory = [&](const std::vector<TensorSpec> &InputFeatures)
-> std::unique_ptr<MLModelRunner> {
diff --git a/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp b/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
index b4a55dbc79d7b..9366b81f37924 100644
--- a/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
+++ b/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
@@ -69,21 +69,23 @@ static llvm::cl::opt<MLGORegAllocModelChoice> SelectedMLGORegAllocModel(
"regalloc-mlgo-model",
llvm::cl::desc("Select the MLGO model to execute for register allocation:"),
llvm::cl::init(MLGORegAllocModelChoice::Default),
- llvm::cl::values(
- clEnumValN(MLGORegAllocModelChoice::Default, "default", "Use standard heuristic")
-#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
- , clEnumValN(MLGORegAllocModelChoice::CLASS_NAME, CLI_FLAG, "Use the " CLI_FLAG " MLGO model")
+ llvm::cl::values(clEnumValN(MLGORegAllocModelChoice::Default, "default",
+ "Use standard heuristic")
+#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
+ , clEnumValN(MLGORegAllocModelChoice::CLASS_NAME, CLI_FLAG, \
+ "Use the " CLI_FLAG " MLGO model")
#include "llvm/CodeGen/RegAllocEvictModels.def"
- )
-);
+ ));
-static std::unique_ptr<MLModelRunner> createMLGORegAllocModelRunner(LLVMContext &Ctx, const std::vector<TensorSpec> &InputFeatures) {
+static std::unique_ptr<MLModelRunner>
+createMLGORegAllocModelRunner(LLVMContext &Ctx,
+ const std::vector<TensorSpec> &InputFeatures) {
switch (SelectedMLGORegAllocModel) {
- case MLGORegAllocModelChoice::Default:
- return nullptr;
-#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
- case MLGORegAllocModelChoice::CLASS_NAME: \
- return std::make_unique<EmitCModelRunner<CLASS_NAME>>(Ctx, InputFeatures);
+ case MLGORegAllocModelChoice::Default:
+ return nullptr;
+#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
+ case MLGORegAllocModelChoice::CLASS_NAME: \
+ return std::make_unique<EmitCModelRunner<CLASS_NAME>>(Ctx, InputFeatures);
#include "llvm/CodeGen/RegAllocEvictModels.def"
}
llvm_unreachable("Unknown MLGO model type!");
@@ -406,7 +408,8 @@ class ReleaseModeEvictionAdvisorProvider final
if (InteractiveChannelBaseName.empty()) {
#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
if (SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default) {
- Runner = createMLGORegAllocModelRunner(MF.getFunction().getContext(), InputFeatures);
+ Runner = createMLGORegAllocModelRunner(MF.getFunction().getContext(),
+ InputFeatures);
} else
#endif
{
@@ -1064,7 +1067,8 @@ llvm::createReleaseModeAdvisorProvider(LLVMContext &Ctx) {
return llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
!InteractiveChannelBaseName.empty()
#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
- || SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default
+ ||
+ SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default
#endif
? new ReleaseModeEvictionAdvisorProvider(Ctx)
: nullptr;
@@ -1083,7 +1087,8 @@ llvm::createReleaseModeAdvisorAnalysisLegacy() {
return llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
!InteractiveChannelBaseName.empty()
#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
- || SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default
+ ||
+ SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default
#endif
? new ReleaseModeEvictionAdvisorAnalysisLegacy()
: nullptr;
>From 31ea6283a0d6c4f14f19d504ec26472c17c6ba4f Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 28 Jul 2026 18:43:32 -0700
Subject: [PATCH 04/12] Address review comments
---
llvm/include/llvm/Analysis/EmitCModelRunner.h | 29 +++++++-----------
llvm/lib/Analysis/CMakeLists.txt | 2 +-
llvm/lib/Analysis/MLInlineAdvisor.cpp | 30 +++++++++----------
3 files changed, 26 insertions(+), 35 deletions(-)
diff --git a/llvm/include/llvm/Analysis/EmitCModelRunner.h b/llvm/include/llvm/Analysis/EmitCModelRunner.h
index f60ca034d232d..2517f070d22e3 100644
--- a/llvm/include/llvm/Analysis/EmitCModelRunner.h
+++ b/llvm/include/llvm/Analysis/EmitCModelRunner.h
@@ -17,7 +17,6 @@
#include "llvm/Analysis/MLModelRunner.h"
#include "llvm/Analysis/TensorSpec.h"
-#include <memory>
#include <type_traits>
namespace llvm {
@@ -25,11 +24,8 @@ namespace llvm {
template <class TGen> class EmitCModelRunner final : public MLModelRunner {
public:
template <class FType>
- EmitCModelRunner(LLVMContext &Ctx, const FType &InputSpec,
- std::unique_ptr<TGen> Model = std::make_unique<TGen>())
- : MLModelRunner(Ctx, MLModelRunner::Kind::Release, InputSpec.size()),
- CompiledModel(std::move(Model)) {
- assert(CompiledModel && "The CompiledModel should be valid");
+ EmitCModelRunner(LLVMContext &Ctx, const FType &InputSpec)
+ : MLModelRunner(Ctx, MLModelRunner::Kind::Release, InputSpec.size()) {
for (size_t I = 0; I < InputSpec.size(); ++I)
populateTensor(I, InputSpec[I]);
}
@@ -46,28 +42,23 @@ template <class TGen> class EmitCModelRunner final : public MLModelRunner {
private:
void populateTensor(size_t Pos, const TensorSpec &Spec) {
void *Buffer = nullptr;
- auto It = CompiledModel->reflectionMap.find(Spec.name());
- if (It != CompiledModel->reflectionMap.end())
+ auto It = CompiledModel.reflectionMap.find(Spec.name());
+ if (It != CompiledModel.reflectionMap.end())
Buffer = static_cast<void *>(It->second);
setUpBufferForTensor(Pos, Spec, Buffer);
}
using ResultType = decltype(std::declval<TGen>()());
+ static_assert(!std::is_void_v<ResultType>,
+ "EmitCModelRunner models must return a non-void result.");
- template <typename R = ResultType>
- std::enable_if_t<!std::is_void_v<R>, void *> evaluateImpl() {
- Result = (*CompiledModel)();
+ void *evaluateImpl() {
+ Result = CompiledModel();
return &Result;
}
- template <typename R = ResultType>
- std::enable_if_t<std::is_void_v<R>, void *> evaluateImpl() {
- (*CompiledModel)();
- return nullptr;
- }
-
- std::conditional_t<std::is_void_v<ResultType>, char, ResultType> Result{};
- std::unique_ptr<TGen> CompiledModel;
+ ResultType Result{};
+ TGen CompiledModel{};
};
} // namespace llvm
diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt
index ed7fc6e7e26c0..408d27474007b 100644
--- a/llvm/lib/Analysis/CMakeLists.txt
+++ b/llvm/lib/Analysis/CMakeLists.txt
@@ -6,7 +6,7 @@ if (LLVM_HAVE_EMITC_COMPILE)
"${LLVM_MLGO_MLIR_TRANSLATE}"
inliner
llvm/Analysis
- MLGOModels.def
+ InlinerModels.def
InlinerSizeModelMulti.h
)
elseif (DEFINED LLVM_HAVE_TF_AOT OR LLVM_HAVE_TFLITE)
diff --git a/llvm/lib/Analysis/MLInlineAdvisor.cpp b/llvm/lib/Analysis/MLInlineAdvisor.cpp
index e04805ff135af..379d62b7ea25e 100644
--- a/llvm/lib/Analysis/MLInlineAdvisor.cpp
+++ b/llvm/lib/Analysis/MLInlineAdvisor.cpp
@@ -76,33 +76,33 @@ using CompiledModelType = NoopSavedModelImpl;
#include "llvm/Analysis/EmitCModelRunner.h"
#include "llvm/Analysis/InlinerSizeModelMulti.h"
-enum class MLGOModelChoice {
+enum class EmitCModelChoice {
Default,
#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) CLASS_NAME,
-#include "llvm/Analysis/MLGOModels.def"
+#include "llvm/Analysis/InlinerModels.def"
};
-static llvm::cl::opt<MLGOModelChoice> SelectedMLGOModel(
+static llvm::cl::opt<EmitCModelChoice> SelectedMLGOModel(
"mlgo-model", llvm::cl::desc("Select the MLGO model to execute:"),
- llvm::cl::init(MLGOModelChoice::Default),
- llvm::cl::values(clEnumValN(MLGOModelChoice::Default, "default",
+ llvm::cl::init(EmitCModelChoice::Default),
+ llvm::cl::values(clEnumValN(EmitCModelChoice::Default, "default",
"Use standard heuristic")
#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
- , clEnumValN(MLGOModelChoice::CLASS_NAME, CLI_FLAG, \
+ , clEnumValN(EmitCModelChoice::CLASS_NAME, CLI_FLAG, \
"Use the " CLI_FLAG " MLGO model")
-#include "llvm/Analysis/MLGOModels.def"
+#include "llvm/Analysis/InlinerModels.def"
));
static std::unique_ptr<MLModelRunner>
-createMLGOModelRunner(LLVMContext &Ctx,
- const std::vector<TensorSpec> &InputFeatures) {
+createEmitCModelRunner(LLVMContext &Ctx,
+ const std::vector<TensorSpec> &InputFeatures) {
switch (SelectedMLGOModel) {
- case MLGOModelChoice::Default:
+ case EmitCModelChoice::Default:
return nullptr;
#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
- case MLGOModelChoice::CLASS_NAME: \
+ case EmitCModelChoice::CLASS_NAME: \
return std::make_unique<EmitCModelRunner<CLASS_NAME>>(Ctx, InputFeatures);
-#include "llvm/Analysis/MLGOModels.def"
+#include "llvm/Analysis/InlinerModels.def"
}
llvm_unreachable("Unknown MLGO model type!");
}
@@ -114,7 +114,7 @@ llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
if (!llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() &&
InteractiveChannelBaseName.empty()
#if defined(LLVM_HAVE_EMITC_COMPILE_INLINER)
- && SelectedMLGOModel == MLGOModelChoice::Default
+ && SelectedMLGOModel == EmitCModelChoice::Default
#endif
)
return nullptr;
@@ -123,8 +123,8 @@ llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
std::unique_ptr<MLModelRunner> AOTRunner;
if (InteractiveChannelBaseName.empty()) {
#if defined(LLVM_HAVE_EMITC_COMPILE_INLINER)
- if (SelectedMLGOModel != MLGOModelChoice::Default) {
- AOTRunner = createMLGOModelRunner(M.getContext(), InputFeatures);
+ if (SelectedMLGOModel != EmitCModelChoice::Default) {
+ AOTRunner = createEmitCModelRunner(M.getContext(), InputFeatures);
} else
#endif
{
>From af18839f8a56863b09adc65a31b63567826347d3 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Wed, 29 Jul 2026 14:00:12 -0700
Subject: [PATCH 05/12] Address review comments (1)
---
llvm/CMakeLists.txt | 2 +-
.../{MLGOCompile.cmake => MLGOLower.cmake} | 49 +++++++++++--------
llvm/include/llvm/Analysis/EmitCModelRunner.h | 4 +-
llvm/lib/Analysis/CMakeLists.txt | 7 ++-
llvm/lib/Analysis/MLInlineAdvisor.cpp | 8 +--
llvm/lib/CodeGen/CMakeLists.txt | 7 ++-
llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp | 10 ++--
7 files changed, 47 insertions(+), 40 deletions(-)
rename llvm/cmake/modules/{MLGOCompile.cmake => MLGOLower.cmake} (73%)
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index 8490c70617a7c..3a548cbdbd1cc 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -1227,7 +1227,7 @@ set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LLVM_LIBRARY_DIR} )
# the bot setup script: https://github.com/google/ml-compiler-opt/blob/main/buildbot/buildbot_init.sh
set(LLVM_HAVE_TFLITE "" CACHE BOOL "Use tflite")
-set(LLVM_MLGO_MODELS "" CACHE STRING "List of AOT MLGO models")
+set(LLVM_MLGO_MODELS "" CACHE STRING "List of MLGO models")
set(LLVM_MLGO_MLIR_OPT "mlir-opt" CACHE STRING "Path or system binary name for mlir-opt")
set(LLVM_MLGO_MLIR_TRANSLATE "mlir-translate" CACHE STRING "Path or system binary name for mlir-translate")
diff --git a/llvm/cmake/modules/MLGOCompile.cmake b/llvm/cmake/modules/MLGOLower.cmake
similarity index 73%
rename from llvm/cmake/modules/MLGOCompile.cmake
rename to llvm/cmake/modules/MLGOLower.cmake
index b4e340087bd62..47ffc972265b8 100644
--- a/llvm/cmake/modules/MLGOCompile.cmake
+++ b/llvm/cmake/modules/MLGOLower.cmake
@@ -1,25 +1,34 @@
-# Compile MLGO models expressed as MLIR to C++ headers via the EmitC pipeline.
+# Lower MLGO models to C++ headers via the MLIR-based pipeline.
#
-# Each entry of ${models} has the form
-# "cli-flag,path/to/model.mlir,type". Entries whose type matches
-# ${target_type} are lowered with ${mlir_opt} and translated with
-# ${mlir_translate} to a C++ header defining Model<N>, placed in
-# ${LLVM_INCLUDE_DIR}/${include_subdir}. In the same directory, generate
-# ${def_file}, with one MLGO_MODEL(ClassName, "cli-flag") entry per model, and
-# ${umbrella_header}, including all the generated model headers.
-# Append the target driving generation to MLDeps in the caller's scope, and
-# define LLVM_HAVE_EMITC_COMPILE_<TARGET_TYPE>.
-function(mlgo_compile_models models mlir_opt mlir_translate target_type
- include_subdir def_file umbrella_header)
- if ("${mlir_opt}" MATCHES "/")
- get_filename_component(mlir_opt "${mlir_opt}" ABSOLUTE BASE_DIR "${CMAKE_BINARY_DIR}")
+# 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 MLDeps 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)
+ 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 ("${mlir_translate}" MATCHES "/")
- get_filename_component(mlir_translate "${mlir_translate}" ABSOLUTE BASE_DIR "${CMAKE_BINARY_DIR}")
+
+ if(NOT IS_ABSOLUTE "${mlir_translate}")
+ cmake_path(ABSOLUTE_PATH mlir_translate BASE_DIRECTORY "${CMAKE_BINARY_DIR}")
endif()
- set(DEF_CONTENT "/* Auto-generated by CMake */\n")
- set(DEF_CONTENT "${DEF_CONTENT}#ifndef MLGO_MODEL\n#define MLGO_MODEL(CLASS_NAME, CLI_FLAG)\n#endif\n\n")
+ 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 "")
@@ -71,7 +80,7 @@ function(mlgo_compile_models models mlir_opt mlir_translate target_type
string(JOIN "," PASS_PIPELINE ${EMITC_PASSES})
set(PASS_PIPELINE "builtin.module(${PASS_PIPELINE})")
- # 1. Run MLIR pipeline to compile MODEL_PATH to EmitC MLIR
+ # 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}
@@ -118,6 +127,6 @@ function(mlgo_compile_models models mlir_opt mlir_translate target_type
add_custom_target(mlgo_models_gen_${target_type} DEPENDS ${MLGO_GEN_TARGETS})
set(MLDeps ${MLDeps} mlgo_models_gen_${target_type} PARENT_SCOPE)
string(TOUPPER ${target_type} target_type_allcaps)
- add_compile_definitions(LLVM_HAVE_EMITC_COMPILE_${target_type_allcaps})
+ add_compile_definitions(LLVM_HAVE_MLIR_LOWERING_${target_type_allcaps})
endif()
endfunction()
diff --git a/llvm/include/llvm/Analysis/EmitCModelRunner.h b/llvm/include/llvm/Analysis/EmitCModelRunner.h
index 2517f070d22e3..cb14553aaa0e8 100644
--- a/llvm/include/llvm/Analysis/EmitCModelRunner.h
+++ b/llvm/include/llvm/Analysis/EmitCModelRunner.h
@@ -26,8 +26,8 @@ template <class TGen> class EmitCModelRunner final : public MLModelRunner {
template <class FType>
EmitCModelRunner(LLVMContext &Ctx, const FType &InputSpec)
: MLModelRunner(Ctx, MLModelRunner::Kind::Release, InputSpec.size()) {
- for (size_t I = 0; I < InputSpec.size(); ++I)
- populateTensor(I, InputSpec[I]);
+ for (auto [I, Spec] : llvm::enumerate(InputSpec))
+ populateTensor(I, Spec);
}
~EmitCModelRunner() override = default;
diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt
index 408d27474007b..99c189562727f 100644
--- a/llvm/lib/Analysis/CMakeLists.txt
+++ b/llvm/lib/Analysis/CMakeLists.txt
@@ -1,13 +1,12 @@
if (LLVM_HAVE_EMITC_COMPILE)
- include(MLGOCompile)
- mlgo_compile_models(
+ include(MLGOLower)
+ mlgo_lower_models(
"${LLVM_MLGO_MODELS}"
"${LLVM_MLGO_MLIR_OPT}"
"${LLVM_MLGO_MLIR_TRANSLATE}"
inliner
llvm/Analysis
- InlinerModels.def
- InlinerSizeModelMulti.h
+ InlinerModels
)
elseif (DEFINED LLVM_HAVE_TF_AOT OR LLVM_HAVE_TFLITE)
include(TensorFlowCompile)
diff --git a/llvm/lib/Analysis/MLInlineAdvisor.cpp b/llvm/lib/Analysis/MLInlineAdvisor.cpp
index 379d62b7ea25e..a4e3a167a8cb6 100644
--- a/llvm/lib/Analysis/MLInlineAdvisor.cpp
+++ b/llvm/lib/Analysis/MLInlineAdvisor.cpp
@@ -72,9 +72,9 @@ using CompiledModelType = llvm::InlinerSizeModel;
using CompiledModelType = NoopSavedModelImpl;
#endif
-#if defined(LLVM_HAVE_EMITC_COMPILE_INLINER)
+#if defined(LLVM_HAVE_MLIR_LOWERING_INLINER)
#include "llvm/Analysis/EmitCModelRunner.h"
-#include "llvm/Analysis/InlinerSizeModelMulti.h"
+#include "llvm/Analysis/InlinerModels.h"
enum class EmitCModelChoice {
Default,
@@ -113,7 +113,7 @@ llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
std::function<bool(CallBase &)> GetDefaultAdvice) {
if (!llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() &&
InteractiveChannelBaseName.empty()
-#if defined(LLVM_HAVE_EMITC_COMPILE_INLINER)
+#if defined(LLVM_HAVE_MLIR_LOWERING_INLINER)
&& SelectedMLGOModel == EmitCModelChoice::Default
#endif
)
@@ -122,7 +122,7 @@ llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
-> std::unique_ptr<MLModelRunner> {
std::unique_ptr<MLModelRunner> AOTRunner;
if (InteractiveChannelBaseName.empty()) {
-#if defined(LLVM_HAVE_EMITC_COMPILE_INLINER)
+#if defined(LLVM_HAVE_MLIR_LOWERING_INLINER)
if (SelectedMLGOModel != EmitCModelChoice::Default) {
AOTRunner = createEmitCModelRunner(M.getContext(), InputFeatures);
} else
diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt
index 1c913c9c2f8ff..4f88a4306e609 100644
--- a/llvm/lib/CodeGen/CMakeLists.txt
+++ b/llvm/lib/CodeGen/CMakeLists.txt
@@ -1,13 +1,12 @@
if (LLVM_HAVE_EMITC_COMPILE)
- include(MLGOCompile)
- mlgo_compile_models(
+ include(MLGOLower)
+ mlgo_lower_models(
"${LLVM_MLGO_MODELS}"
"${LLVM_MLGO_MLIR_OPT}"
"${LLVM_MLGO_MLIR_TRANSLATE}"
regalloc
llvm/CodeGen
- RegAllocEvictModels.def
- RegAllocEvictModelMulti.h
+ RegAllocEvictModels
)
elseif (DEFINED LLVM_HAVE_TF_AOT OR LLVM_HAVE_TFLITE)
include(TensorFlowCompile)
diff --git a/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp b/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
index 9366b81f37924..be02b96e8d921 100644
--- a/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
+++ b/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
@@ -55,9 +55,9 @@ using CompiledModelType = RegAllocEvictModel;
using CompiledModelType = NoopSavedModelImpl;
#endif
-#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
+#if defined(LLVM_HAVE_MLIR_LOWERING_REGALLOC)
#include "llvm/Analysis/EmitCModelRunner.h"
-#include "llvm/CodeGen/RegAllocEvictModelMulti.h"
+#include "llvm/CodeGen/RegAllocEvictModels.h"
enum class MLGORegAllocModelChoice {
Default,
@@ -406,7 +406,7 @@ class ReleaseModeEvictionAdvisorProvider final
MachineBlockFrequencyInfo *MBFI, MachineLoopInfo *Loops) override {
if (!Runner) {
if (InteractiveChannelBaseName.empty()) {
-#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
+#if defined(LLVM_HAVE_MLIR_LOWERING_REGALLOC)
if (SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default) {
Runner = createMLGORegAllocModelRunner(MF.getFunction().getContext(),
InputFeatures);
@@ -1066,7 +1066,7 @@ RegAllocEvictionAdvisorProvider *
llvm::createReleaseModeAdvisorProvider(LLVMContext &Ctx) {
return llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
!InteractiveChannelBaseName.empty()
-#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
+#if defined(LLVM_HAVE_MLIR_LOWERING_REGALLOC)
||
SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default
#endif
@@ -1086,7 +1086,7 @@ RegAllocEvictionAdvisorAnalysisLegacy *
llvm::createReleaseModeAdvisorAnalysisLegacy() {
return llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
!InteractiveChannelBaseName.empty()
-#if defined(LLVM_HAVE_EMITC_COMPILE_REGALLOC)
+#if defined(LLVM_HAVE_MLIR_LOWERING_REGALLOC)
||
SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default
#endif
>From d98a6e9af3ce6a6a7e97331bb9df9b5a4ab847a1 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Wed, 29 Jul 2026 16:22:55 -0700
Subject: [PATCH 06/12] Address review comments (2)
---
llvm/CMakeLists.txt | 5 +--
llvm/cmake/modules/MLGOLower.cmake | 8 ++--
llvm/include/llvm/Analysis/EmitCModelRunner.h | 14 +++----
llvm/lib/Analysis/MLInlineAdvisor.cpp | 38 ++++++++++---------
4 files changed, 35 insertions(+), 30 deletions(-)
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index 3a548cbdbd1cc..660befb697a52 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -1231,10 +1231,9 @@ set(LLVM_MLGO_MODELS "" CACHE STRING "List of MLGO models")
set(LLVM_MLGO_MLIR_OPT "mlir-opt" CACHE STRING "Path or system binary name for mlir-opt")
set(LLVM_MLGO_MLIR_TRANSLATE "mlir-translate" CACHE STRING "Path or system binary name for mlir-translate")
+set(LLVM_HAVE_EMITC_COMPILE OFF CACHE BOOL "MLGO models to be compiled with EmitC are available")
if (NOT LLVM_MLGO_MODELS STREQUAL "")
- set(LLVM_HAVE_EMITC_COMPILE "ON" CACHE BOOL "MLGO AOT models compiled with EmitC are available" FORCE)
-else()
- set(LLVM_HAVE_EMITC_COMPILE "OFF" CACHE BOOL "MLGO AOT models compiled with EmitC are available" FORCE)
+ set(LLVM_HAVE_EMITC_COMPILE ON CACHE BOOL "MLGO models to be compiled with EmitC are available" FORCE)
endif()
if (LLVM_HAVE_EMITC_COMPILE AND LLVM_HAVE_TFLITE)
diff --git a/llvm/cmake/modules/MLGOLower.cmake b/llvm/cmake/modules/MLGOLower.cmake
index 47ffc972265b8..82e32ea5c3457 100644
--- a/llvm/cmake/modules/MLGOLower.cmake
+++ b/llvm/cmake/modules/MLGOLower.cmake
@@ -35,7 +35,8 @@ function(mlgo_lower_models models mlir_opt mlir_translate target_type
set(MODEL_INDEX 1)
foreach(MODEL_INFO IN LISTS models)
- # Parse comma-separated fields: cli-flag,path/to/model.mlir,type
+ # 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)
@@ -57,6 +58,7 @@ function(mlgo_lower_models models mlir_opt mlir_translate target_type
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.
set(EMITC_PASSES
"func.func(tosa-to-linalg-named,tosa-to-linalg,tosa-to-arith,tosa-to-tensor)"
"symbol-privatize"
@@ -107,11 +109,11 @@ function(mlgo_lower_models models mlir_opt mlir_translate target_type
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}\nusing ${CLASS_NAME}_ns::${CLASS_NAME};\n")
+ string(APPEND HEADERS_CONTENT "namespace ${CLASS_NAME}_ns {\n#include \"${include_subdir}/${CLASS_NAME}.h\"\n} // namespace ${CLASS_NAME}_ns\nusing ${CLASS_NAME}_ns::${CLASS_NAME};\n")
endforeach()
string(APPEND DEF_CONTENT "\n#undef MLGO_MODEL\n")
- string(APPEND HEADERS_CONTENT "}\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
diff --git a/llvm/include/llvm/Analysis/EmitCModelRunner.h b/llvm/include/llvm/Analysis/EmitCModelRunner.h
index cb14553aaa0e8..21edb09450de6 100644
--- a/llvm/include/llvm/Analysis/EmitCModelRunner.h
+++ b/llvm/include/llvm/Analysis/EmitCModelRunner.h
@@ -1,14 +1,14 @@
-//===- EmitCModelRunner.h - Fast, precompiled model runner ---------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
-// This file implements a model runner wrapping an EmitC compiled ML model.
-// Only inference is supported.
-//
+///
+/// \file
+/// This file implements a model runner wrapping an EmitC compiled ML model.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_ANALYSIS_EMITCMODELRUNNER_H
@@ -57,8 +57,8 @@ template <class TGen> class EmitCModelRunner final : public MLModelRunner {
return &Result;
}
- ResultType Result{};
- TGen CompiledModel{};
+ ResultType Result = {};
+ TGen CompiledModel = {};
};
} // namespace llvm
diff --git a/llvm/lib/Analysis/MLInlineAdvisor.cpp b/llvm/lib/Analysis/MLInlineAdvisor.cpp
index a4e3a167a8cb6..8ff00b2bb6646 100644
--- a/llvm/lib/Analysis/MLInlineAdvisor.cpp
+++ b/llvm/lib/Analysis/MLInlineAdvisor.cpp
@@ -73,6 +73,7 @@ using CompiledModelType = NoopSavedModelImpl;
#endif
#if defined(LLVM_HAVE_MLIR_LOWERING_INLINER)
+constexpr bool HaveMLIRLoweringInliner = true;
#include "llvm/Analysis/EmitCModelRunner.h"
#include "llvm/Analysis/InlinerModels.h"
@@ -106,39 +107,42 @@ createEmitCModelRunner(LLVMContext &Ctx,
}
llvm_unreachable("Unknown MLGO model type!");
}
+#else
+constexpr bool HaveMLIRLoweringInliner = false;
+enum class EmitCModelChoice { Default };
+static const EmitCModelChoice SelectedMLGOModel = EmitCModelChoice::Default;
+inline std::unique_ptr<MLModelRunner>
+createEmitCModelRunner(LLVMContext &, const std::vector<TensorSpec> &) {
+ return nullptr;
+}
#endif
std::unique_ptr<InlineAdvisor>
llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
std::function<bool(CallBase &)> GetDefaultAdvice) {
if (!llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() &&
- InteractiveChannelBaseName.empty()
-#if defined(LLVM_HAVE_MLIR_LOWERING_INLINER)
- && SelectedMLGOModel == EmitCModelChoice::Default
-#endif
- )
+ InteractiveChannelBaseName.empty() &&
+ SelectedMLGOModel == EmitCModelChoice::Default)
return nullptr;
auto RunnerFactory = [&](const std::vector<TensorSpec> &InputFeatures)
-> std::unique_ptr<MLModelRunner> {
- std::unique_ptr<MLModelRunner> AOTRunner;
+ std::unique_ptr<MLModelRunner> ModelRunner;
if (InteractiveChannelBaseName.empty()) {
-#if defined(LLVM_HAVE_MLIR_LOWERING_INLINER)
- if (SelectedMLGOModel != EmitCModelChoice::Default) {
- AOTRunner = createEmitCModelRunner(M.getContext(), InputFeatures);
- } else
-#endif
- {
- AOTRunner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
- M.getContext(), InputFeatures, DecisionName,
- EmbeddedModelRunnerOptions().setModelSelector(ModelSelector));
+ if constexpr (HaveMLIRLoweringInliner) {
+ ModelRunner = createEmitCModelRunner(M.getContext(), InputFeatures);
+ } else {
+ ModelRunner =
+ std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
+ M.getContext(), InputFeatures, DecisionName,
+ EmbeddedModelRunnerOptions().setModelSelector(ModelSelector));
}
} else {
- AOTRunner = std::make_unique<InteractiveModelRunner>(
+ ModelRunner = std::make_unique<InteractiveModelRunner>(
M.getContext(), InputFeatures, InlineDecisionSpec,
InteractiveChannelBaseName + ".out",
InteractiveChannelBaseName + ".in");
}
- return AOTRunner;
+ return ModelRunner;
};
return std::make_unique<MLInlineAdvisor>(M, MAM, RunnerFactory,
GetDefaultAdvice);
>From 44bcd60483da937a16ad081d4e5c317cde468794 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Fri, 31 Jul 2026 12:29:21 -0700
Subject: [PATCH 07/12] Create helper functions for shared logic in inline and
regalloc advisors
---
llvm/include/llvm/Analysis/Utils/MLGOUtils.h | 82 ++++++++++++++++++++
llvm/lib/Analysis/MLInlineAdvisor.cpp | 31 +++-----
llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp | 51 +++++-------
3 files changed, 111 insertions(+), 53 deletions(-)
create mode 100644 llvm/include/llvm/Analysis/Utils/MLGOUtils.h
diff --git a/llvm/include/llvm/Analysis/Utils/MLGOUtils.h b/llvm/include/llvm/Analysis/Utils/MLGOUtils.h
new file mode 100644
index 0000000000000..6bd69d26ae8a5
--- /dev/null
+++ b/llvm/include/llvm/Analysis/Utils/MLGOUtils.h
@@ -0,0 +1,82 @@
+//===- MLGOUtils.h - Utilities for MLGO Release Mode ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file provides helper functions for creating MLModelRunners and checking
+/// model validity in release mode.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ANALYSIS_UTILS_MLGOUTILS_H
+#define LLVM_ANALYSIS_UTILS_MLGOUTILS_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Analysis/InteractiveModelRunner.h"
+#include "llvm/Analysis/MLModelRunner.h"
+#include "llvm/Analysis/ReleaseModeModelRunner.h"
+#include "llvm/Analysis/TensorSpec.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Support/CommandLine.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace llvm {
+
+/// Helper to check if a release-mode ML advisor has a valid model to execute.
+/// Overload for cl::opt<EnumType>.
+template <class CompiledModelType, class EnumType, bool ExternalStorage,
+ class ParserClass>
+bool isReleaseModelValid(
+ StringRef InteractiveChannelBaseName,
+ const cl::opt<EnumType, ExternalStorage, ParserClass> &SelectedModel,
+ EnumType DefaultModelVal = EnumType::Default) {
+ return isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
+ !InteractiveChannelBaseName.empty() ||
+ SelectedModel != DefaultModelVal;
+}
+
+/// Helper to check if a release-mode ML advisor has a valid model to execute.
+/// Overload for plain EnumType.
+template <class CompiledModelType, class EnumType>
+bool isReleaseModelValid(StringRef InteractiveChannelBaseName,
+ EnumType SelectedModel,
+ EnumType DefaultModelVal = EnumType::Default) {
+ return isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
+ !InteractiveChannelBaseName.empty() ||
+ SelectedModel != DefaultModelVal;
+}
+
+/// Helper to construct the appropriate MLModelRunner in release mode:
+/// 1. InteractiveModelRunner if an interactive channel is specified.
+/// 2. EmitCModelRunner if MLIR lowering is enabled.
+/// 3. ReleaseModeModelRunner<CompiledModelType> otherwise.
+template <class CompiledModelType, bool HaveMLIRLowering, class CreateEmitCFunc>
+std::unique_ptr<MLModelRunner> createReleaseModeModelRunner(
+ LLVMContext &Ctx, const std::vector<TensorSpec> &InputFeatures,
+ StringRef DecisionName, const std::string &InteractiveChannelBaseName,
+ const TensorSpec &InteractiveDecisionSpec,
+ CreateEmitCFunc &&CreateEmitCModelRunner,
+ const EmbeddedModelRunnerOptions &Options = {}) {
+ if (!InteractiveChannelBaseName.empty()) {
+ return std::make_unique<InteractiveModelRunner>(
+ Ctx, InputFeatures, InteractiveDecisionSpec,
+ InteractiveChannelBaseName + ".out",
+ InteractiveChannelBaseName + ".in");
+ }
+ if constexpr (HaveMLIRLowering) {
+ return CreateEmitCModelRunner(Ctx, InputFeatures);
+ } else {
+ return std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
+ Ctx, InputFeatures, DecisionName, Options);
+ }
+}
+
+} // namespace llvm
+
+#endif // LLVM_ANALYSIS_UTILS_MLGOUTILS_H
diff --git a/llvm/lib/Analysis/MLInlineAdvisor.cpp b/llvm/lib/Analysis/MLInlineAdvisor.cpp
index 8ff00b2bb6646..fd0e4b152bcc2 100644
--- a/llvm/lib/Analysis/MLInlineAdvisor.cpp
+++ b/llvm/lib/Analysis/MLInlineAdvisor.cpp
@@ -19,7 +19,6 @@
#include "llvm/Analysis/FunctionPropertiesAnalysis.h"
#include "llvm/Analysis/InlineCost.h"
#include "llvm/Analysis/InlineModelFeatureMaps.h"
-#include "llvm/Analysis/InteractiveModelRunner.h"
#include "llvm/Analysis/LazyCallGraph.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/MLModelRunner.h"
@@ -28,6 +27,7 @@
#include "llvm/Analysis/ReleaseModeModelRunner.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/TensorSpec.h"
+#include "llvm/Analysis/Utils/MLGOUtils.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Module.h"
@@ -111,7 +111,7 @@ createEmitCModelRunner(LLVMContext &Ctx,
constexpr bool HaveMLIRLoweringInliner = false;
enum class EmitCModelChoice { Default };
static const EmitCModelChoice SelectedMLGOModel = EmitCModelChoice::Default;
-inline std::unique_ptr<MLModelRunner>
+static inline std::unique_ptr<MLModelRunner>
createEmitCModelRunner(LLVMContext &, const std::vector<TensorSpec> &) {
return nullptr;
}
@@ -120,29 +120,16 @@ createEmitCModelRunner(LLVMContext &, const std::vector<TensorSpec> &) {
std::unique_ptr<InlineAdvisor>
llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
std::function<bool(CallBase &)> GetDefaultAdvice) {
- if (!llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() &&
- InteractiveChannelBaseName.empty() &&
- SelectedMLGOModel == EmitCModelChoice::Default)
+ if (!isReleaseModelValid<CompiledModelType>(InteractiveChannelBaseName,
+ SelectedMLGOModel))
return nullptr;
auto RunnerFactory = [&](const std::vector<TensorSpec> &InputFeatures)
-> std::unique_ptr<MLModelRunner> {
- std::unique_ptr<MLModelRunner> ModelRunner;
- if (InteractiveChannelBaseName.empty()) {
- if constexpr (HaveMLIRLoweringInliner) {
- ModelRunner = createEmitCModelRunner(M.getContext(), InputFeatures);
- } else {
- ModelRunner =
- std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
- M.getContext(), InputFeatures, DecisionName,
- EmbeddedModelRunnerOptions().setModelSelector(ModelSelector));
- }
- } else {
- ModelRunner = std::make_unique<InteractiveModelRunner>(
- M.getContext(), InputFeatures, InlineDecisionSpec,
- InteractiveChannelBaseName + ".out",
- InteractiveChannelBaseName + ".in");
- }
- return ModelRunner;
+ return createReleaseModeModelRunner<CompiledModelType,
+ HaveMLIRLoweringInliner>(
+ M.getContext(), InputFeatures, DecisionName, InteractiveChannelBaseName,
+ InlineDecisionSpec, createEmitCModelRunner,
+ EmbeddedModelRunnerOptions().setModelSelector(ModelSelector));
};
return std::make_unique<MLInlineAdvisor>(M, MAM, RunnerFactory,
GetDefaultAdvice);
diff --git a/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp b/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
index be02b96e8d921..0ed413a7707ad 100644
--- a/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
+++ b/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp
@@ -12,7 +12,6 @@
#include "AllocationOrder.h"
#include "RegAllocGreedy.h"
-#include "llvm/Analysis/InteractiveModelRunner.h"
#include "llvm/Analysis/MLModelRunner.h"
#include "llvm/Analysis/TensorSpec.h"
#include "llvm/CodeGen/RegAllocEvictionAdvisor.h"
@@ -23,6 +22,7 @@
#endif
#include "MLRegAllocEvictAdvisor.h"
#include "llvm/Analysis/ReleaseModeModelRunner.h"
+#include "llvm/Analysis/Utils/MLGOUtils.h"
#include "llvm/CodeGen/CalcSpillWeights.h"
#include "llvm/CodeGen/LiveRegMatrix.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
@@ -35,7 +35,6 @@
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
-#include "llvm/PassRegistry.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
@@ -56,6 +55,7 @@ using CompiledModelType = NoopSavedModelImpl;
#endif
#if defined(LLVM_HAVE_MLIR_LOWERING_REGALLOC)
+constexpr bool HaveMLIRLoweringRegAlloc = true;
#include "llvm/Analysis/EmitCModelRunner.h"
#include "llvm/CodeGen/RegAllocEvictModels.h"
@@ -90,6 +90,15 @@ createMLGORegAllocModelRunner(LLVMContext &Ctx,
}
llvm_unreachable("Unknown MLGO model type!");
}
+#else
+constexpr bool HaveMLIRLoweringRegAlloc = false;
+enum class MLGORegAllocModelChoice { Default };
+static const MLGORegAllocModelChoice SelectedMLGORegAllocModel =
+ MLGORegAllocModelChoice::Default;
+static inline std::unique_ptr<MLModelRunner>
+createMLGORegAllocModelRunner(LLVMContext &, const std::vector<TensorSpec> &) {
+ return nullptr;
+}
#endif
static cl::opt<std::string> InteractiveChannelBaseName(
@@ -405,23 +414,11 @@ class ReleaseModeEvictionAdvisorProvider final
getAdvisor(const MachineFunction &MF, const RAGreedy &RA,
MachineBlockFrequencyInfo *MBFI, MachineLoopInfo *Loops) override {
if (!Runner) {
- if (InteractiveChannelBaseName.empty()) {
-#if defined(LLVM_HAVE_MLIR_LOWERING_REGALLOC)
- if (SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default) {
- Runner = createMLGORegAllocModelRunner(MF.getFunction().getContext(),
- InputFeatures);
- } else
-#endif
- {
- Runner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
- MF.getFunction().getContext(), InputFeatures, DecisionName);
- }
- } else {
- Runner = std::make_unique<InteractiveModelRunner>(
- MF.getFunction().getContext(), InputFeatures, DecisionSpec,
- InteractiveChannelBaseName + ".out",
- InteractiveChannelBaseName + ".in");
- }
+ Runner = createReleaseModeModelRunner<CompiledModelType,
+ HaveMLIRLoweringRegAlloc>(
+ MF.getFunction().getContext(), InputFeatures, DecisionName,
+ InteractiveChannelBaseName, DecisionSpec,
+ createMLGORegAllocModelRunner);
}
assert(MBFI && Loops &&
"Invalid provider state: must have analysis available");
@@ -1064,12 +1061,8 @@ bool RegAllocScoring::runOnMachineFunction(MachineFunction &MF) {
RegAllocEvictionAdvisorProvider *
llvm::createReleaseModeAdvisorProvider(LLVMContext &Ctx) {
- return llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
- !InteractiveChannelBaseName.empty()
-#if defined(LLVM_HAVE_MLIR_LOWERING_REGALLOC)
- ||
- SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default
-#endif
+ return isReleaseModelValid<CompiledModelType>(InteractiveChannelBaseName,
+ SelectedMLGORegAllocModel)
? new ReleaseModeEvictionAdvisorProvider(Ctx)
: nullptr;
}
@@ -1084,12 +1077,8 @@ llvm::createDevelopmentModeAdvisorProvider(LLVMContext &Ctx) {
RegAllocEvictionAdvisorAnalysisLegacy *
llvm::createReleaseModeAdvisorAnalysisLegacy() {
- return llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
- !InteractiveChannelBaseName.empty()
-#if defined(LLVM_HAVE_MLIR_LOWERING_REGALLOC)
- ||
- SelectedMLGORegAllocModel != MLGORegAllocModelChoice::Default
-#endif
+ return isReleaseModelValid<CompiledModelType>(InteractiveChannelBaseName,
+ SelectedMLGORegAllocModel)
? new ReleaseModeEvictionAdvisorAnalysisLegacy()
: nullptr;
}
>From a0cba09b670a617d4071525be52473d91a49ec08 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Mon, 3 Aug 2026 12:19:47 -0700
Subject: [PATCH 08/12] rename LLVM_HAVE_EMITC_COMPILE and default it to off
and prevent the user from setting the variable
---
llvm/CMakeLists.txt | 12 ++++++------
llvm/lib/Analysis/CMakeLists.txt | 2 +-
llvm/lib/CodeGen/CMakeLists.txt | 2 +-
llvm/test/CMakeLists.txt | 2 +-
llvm/test/lit.cfg.py | 4 ++--
llvm/test/lit.site.cfg.py.in | 2 +-
6 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index 660befb697a52..ea749e23dd9b6 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -1231,13 +1231,13 @@ set(LLVM_MLGO_MODELS "" CACHE STRING "List of MLGO models")
set(LLVM_MLGO_MLIR_OPT "mlir-opt" CACHE STRING "Path or system binary name for mlir-opt")
set(LLVM_MLGO_MLIR_TRANSLATE "mlir-translate" CACHE STRING "Path or system binary name for mlir-translate")
-set(LLVM_HAVE_EMITC_COMPILE OFF CACHE BOOL "MLGO models to be compiled with EmitC are available")
+set(LLVM_HAVE_EMITC_LOWERING OFF)
if (NOT LLVM_MLGO_MODELS STREQUAL "")
- set(LLVM_HAVE_EMITC_COMPILE ON CACHE BOOL "MLGO models to be compiled with EmitC are available" FORCE)
+ set(LLVM_HAVE_EMITC_LOWERING ON)
endif()
-if (LLVM_HAVE_EMITC_COMPILE AND LLVM_HAVE_TFLITE)
- message(FATAL_ERROR "Only one of LLVM_HAVE_TFLITE and LLVM_HAVE_EMITC_COMPILE can be enabled.")
+if (LLVM_HAVE_EMITC_LOWERING AND LLVM_HAVE_TFLITE)
+ message(FATAL_ERROR "Only one of LLVM_HAVE_TFLITE and LLVM_HAVE_EMITC_LOWERING can be enabled.")
endif()
if (LLVM_HAVE_TFLITE)
@@ -1268,8 +1268,8 @@ if (NOT TENSORFLOW_AOT_PATH STREQUAL "")
ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT tf_xla_runtime)
set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS tf_xla_runtime)
- if (LLVM_HAVE_TF_AOT AND LLVM_HAVE_EMITC_COMPILE)
- message(FATAL_ERROR "Only one of LLVM_HAVE_TF_AOT and LLVM_HAVE_EMITC_COMPILE can be enabled.")
+ if (LLVM_HAVE_TF_AOT AND LLVM_HAVE_EMITC_LOWERING)
+ message(FATAL_ERROR "Only one of LLVM_HAVE_TF_AOT and LLVM_HAVE_EMITC_LOWERING can be enabled.")
endif()
# Once we add more modules, we should handle this more automatically.
if (DEFINED LLVM_OVERRIDE_MODEL_HEADER_INLINERSIZEMODEL)
diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt
index 99c189562727f..5aac720edc6b7 100644
--- a/llvm/lib/Analysis/CMakeLists.txt
+++ b/llvm/lib/Analysis/CMakeLists.txt
@@ -1,4 +1,4 @@
-if (LLVM_HAVE_EMITC_COMPILE)
+if (LLVM_HAVE_EMITC_LOWERING)
include(MLGOLower)
mlgo_lower_models(
"${LLVM_MLGO_MODELS}"
diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt
index 4f88a4306e609..b3b6ed71874f8 100644
--- a/llvm/lib/CodeGen/CMakeLists.txt
+++ b/llvm/lib/CodeGen/CMakeLists.txt
@@ -1,4 +1,4 @@
-if (LLVM_HAVE_EMITC_COMPILE)
+if (LLVM_HAVE_EMITC_LOWERING)
include(MLGOLower)
mlgo_lower_models(
"${LLVM_MLGO_MODELS}"
diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt
index 90d3cd494565a..a859812b71a45 100644
--- a/llvm/test/CMakeLists.txt
+++ b/llvm/test/CMakeLists.txt
@@ -19,7 +19,7 @@ llvm_canonicalize_cmake_booleans(
LLVM_EXAMPLEIRTRANSFORMS_LINK_INTO_TOOLS
LLVM_HAVE_TF_AOT
LLVM_HAVE_TFLITE
- LLVM_HAVE_EMITC_COMPILE
+ LLVM_HAVE_EMITC_LOWERING
LLVM_ENABLE_PROFCHECK
LLVM_INLINER_MODEL_AUTOGENERATED
LLVM_RAEVICT_MODEL_AUTOGENERATED
diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py
index 782e653448399..de07c3aa71e06 100644
--- a/llvm/test/lit.cfg.py
+++ b/llvm/test/lit.cfg.py
@@ -584,8 +584,8 @@ def enable_ptxas(ptxas_executable):
if config.have_tf_aot:
config.available_features.add("have_tf_aot")
-if getattr(config, "have_emitc_compile", False):
- config.available_features.add("have_emitc_compile")
+if getattr(config, "have_emitc_lowering", False):
+ config.available_features.add("have_emitc_lowering")
if getattr(config, "have_opencsd", False):
config.available_features.add("opencsd")
diff --git a/llvm/test/lit.site.cfg.py.in b/llvm/test/lit.site.cfg.py.in
index 24279c94c2ae8..2c8bd9687d4d8 100644
--- a/llvm/test/lit.site.cfg.py.in
+++ b/llvm/test/lit.site.cfg.py.in
@@ -57,7 +57,7 @@ config.has_plugins = @LLVM_ENABLE_PLUGINS@
config.linked_bye_extension = @LLVM_BYE_LINK_INTO_TOOLS@
config.linked_exampleirtransforms_extension = @LLVM_EXAMPLEIRTRANSFORMS_LINK_INTO_TOOLS@
config.have_tf_aot = @LLVM_HAVE_TF_AOT@
-config.have_emitc_compile = @LLVM_HAVE_EMITC_COMPILE@
+config.have_emitc_lowering = @LLVM_HAVE_EMITC_LOWERING@
config.have_tflite = @LLVM_HAVE_TFLITE@
config.enable_profcheck = @LLVM_ENABLE_PROFCHECK@
config.llvm_inliner_model_autogenerated = @LLVM_INLINER_MODEL_AUTOGENERATED@
>From 1b7fd8ea862d91778664dad02b4b74695b80d042 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Thu, 6 Aug 2026 12:36:11 -0700
Subject: [PATCH 09/12] Update CMAKE variable naming to not expose
implementation detail
---
llvm/CMakeLists.txt | 12 ++++++------
llvm/cmake/modules/MLGOLower.cmake | 4 ++--
llvm/lib/Analysis/CMakeLists.txt | 2 +-
llvm/lib/CodeGen/CMakeLists.txt | 2 +-
llvm/test/CMakeLists.txt | 2 +-
llvm/test/lit.cfg.py | 4 ++--
llvm/test/lit.site.cfg.py.in | 2 +-
7 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index ea749e23dd9b6..b0a40dbdfd423 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -1231,13 +1231,13 @@ set(LLVM_MLGO_MODELS "" CACHE STRING "List of MLGO models")
set(LLVM_MLGO_MLIR_OPT "mlir-opt" CACHE STRING "Path or system binary name for mlir-opt")
set(LLVM_MLGO_MLIR_TRANSLATE "mlir-translate" CACHE STRING "Path or system binary name for mlir-translate")
-set(LLVM_HAVE_EMITC_LOWERING OFF)
+set(LLVM_HAVE_MLIR_LOWERING OFF)
if (NOT LLVM_MLGO_MODELS STREQUAL "")
- set(LLVM_HAVE_EMITC_LOWERING ON)
+ set(LLVM_HAVE_MLIR_LOWERING ON)
endif()
-if (LLVM_HAVE_EMITC_LOWERING AND LLVM_HAVE_TFLITE)
- message(FATAL_ERROR "Only one of LLVM_HAVE_TFLITE and LLVM_HAVE_EMITC_LOWERING can be enabled.")
+if (LLVM_HAVE_MLIR_LOWERING AND LLVM_HAVE_TFLITE)
+ message(FATAL_ERROR "Only one of LLVM_HAVE_TFLITE and LLVM_HAVE_MLIR_LOWERING can be enabled.")
endif()
if (LLVM_HAVE_TFLITE)
@@ -1268,8 +1268,8 @@ if (NOT TENSORFLOW_AOT_PATH STREQUAL "")
ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT tf_xla_runtime)
set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS tf_xla_runtime)
- if (LLVM_HAVE_TF_AOT AND LLVM_HAVE_EMITC_LOWERING)
- message(FATAL_ERROR "Only one of LLVM_HAVE_TF_AOT and LLVM_HAVE_EMITC_LOWERING can be enabled.")
+ if (LLVM_HAVE_TF_AOT AND LLVM_HAVE_MLIR_LOWERING)
+ message(FATAL_ERROR "Only one of LLVM_HAVE_TF_AOT and LLVM_HAVE_MLIR_LOWERING can be enabled.")
endif()
# Once we add more modules, we should handle this more automatically.
if (DEFINED LLVM_OVERRIDE_MODEL_HEADER_INLINERSIZEMODEL)
diff --git a/llvm/cmake/modules/MLGOLower.cmake b/llvm/cmake/modules/MLGOLower.cmake
index 82e32ea5c3457..28c18eb4defb3 100644
--- a/llvm/cmake/modules/MLGOLower.cmake
+++ b/llvm/cmake/modules/MLGOLower.cmake
@@ -59,7 +59,7 @@ function(mlgo_lower_models models mlir_opt mlir_translate target_type
# Pass pipeline to lower MLIR models to EmitC dialect
# TODO: Simplify with builtin pipeline for translation.
- set(EMITC_PASSES
+ 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"
@@ -79,7 +79,7 @@ function(mlgo_lower_models models mlir_opt mlir_translate target_type
"convert-math-to-emitc"
"convert-arith-to-emitc"
)
- string(JOIN "," PASS_PIPELINE ${EMITC_PASSES})
+ string(JOIN "," PASS_PIPELINE ${MLIR_PASSES})
set(PASS_PIPELINE "builtin.module(${PASS_PIPELINE})")
# 1. Run MLIR pipeline to lower MODEL_PATH to EmitC MLIR
diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt
index 5aac720edc6b7..e248f3916b476 100644
--- a/llvm/lib/Analysis/CMakeLists.txt
+++ b/llvm/lib/Analysis/CMakeLists.txt
@@ -1,4 +1,4 @@
-if (LLVM_HAVE_EMITC_LOWERING)
+if (LLVM_HAVE_MLIR_LOWERING)
include(MLGOLower)
mlgo_lower_models(
"${LLVM_MLGO_MODELS}"
diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt
index b3b6ed71874f8..1b675d9b1322c 100644
--- a/llvm/lib/CodeGen/CMakeLists.txt
+++ b/llvm/lib/CodeGen/CMakeLists.txt
@@ -1,4 +1,4 @@
-if (LLVM_HAVE_EMITC_LOWERING)
+if (LLVM_HAVE_MLIR_LOWERING)
include(MLGOLower)
mlgo_lower_models(
"${LLVM_MLGO_MODELS}"
diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt
index a859812b71a45..a2269597fe5e6 100644
--- a/llvm/test/CMakeLists.txt
+++ b/llvm/test/CMakeLists.txt
@@ -19,7 +19,7 @@ llvm_canonicalize_cmake_booleans(
LLVM_EXAMPLEIRTRANSFORMS_LINK_INTO_TOOLS
LLVM_HAVE_TF_AOT
LLVM_HAVE_TFLITE
- LLVM_HAVE_EMITC_LOWERING
+ LLVM_HAVE_MLIR_LOWERING
LLVM_ENABLE_PROFCHECK
LLVM_INLINER_MODEL_AUTOGENERATED
LLVM_RAEVICT_MODEL_AUTOGENERATED
diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py
index de07c3aa71e06..8b8f31bf5a68f 100644
--- a/llvm/test/lit.cfg.py
+++ b/llvm/test/lit.cfg.py
@@ -584,8 +584,8 @@ def enable_ptxas(ptxas_executable):
if config.have_tf_aot:
config.available_features.add("have_tf_aot")
-if getattr(config, "have_emitc_lowering", False):
- config.available_features.add("have_emitc_lowering")
+if getattr(config, "have_mlir_lowering", False):
+ config.available_features.add("have_mlir_lowering")
if getattr(config, "have_opencsd", False):
config.available_features.add("opencsd")
diff --git a/llvm/test/lit.site.cfg.py.in b/llvm/test/lit.site.cfg.py.in
index 2c8bd9687d4d8..eb9934f85ad42 100644
--- a/llvm/test/lit.site.cfg.py.in
+++ b/llvm/test/lit.site.cfg.py.in
@@ -57,7 +57,7 @@ config.has_plugins = @LLVM_ENABLE_PLUGINS@
config.linked_bye_extension = @LLVM_BYE_LINK_INTO_TOOLS@
config.linked_exampleirtransforms_extension = @LLVM_EXAMPLEIRTRANSFORMS_LINK_INTO_TOOLS@
config.have_tf_aot = @LLVM_HAVE_TF_AOT@
-config.have_emitc_lowering = @LLVM_HAVE_EMITC_LOWERING@
+config.have_mlir_lowering = @LLVM_HAVE_MLIR_LOWERING@
config.have_tflite = @LLVM_HAVE_TFLITE@
config.enable_profcheck = @LLVM_ENABLE_PROFCHECK@
config.llvm_inliner_model_autogenerated = @LLVM_INLINER_MODEL_AUTOGENERATED@
>From 2e5db442cbdbcaa83fc919622b294edd6ff5bfca Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Thu, 6 Aug 2026 13:32:46 -0700
Subject: [PATCH 10/12] EmitCModelRunner tests
---
llvm/unittests/Analysis/MLModelRunnerTest.cpp | 57 +++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/llvm/unittests/Analysis/MLModelRunnerTest.cpp b/llvm/unittests/Analysis/MLModelRunnerTest.cpp
index 62f90699c8718..5179d673cfa97 100644
--- a/llvm/unittests/Analysis/MLModelRunnerTest.cpp
+++ b/llvm/unittests/Analysis/MLModelRunnerTest.cpp
@@ -8,6 +8,7 @@
#include "llvm/Analysis/MLModelRunner.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Analysis/EmitCModelRunner.h"
#include "llvm/Analysis/InteractiveModelRunner.h"
#include "llvm/Analysis/NoInferenceModelRunner.h"
#include "llvm/Analysis/ReleaseModeModelRunner.h"
@@ -122,6 +123,18 @@ class ComposedAOTModel final {
void Run() { getModel()->Run(); }
};
+class MockEmitCModel final {
+ int64_t A = 0;
+ int64_t B = 0;
+
+public:
+ std::map<std::string, void *> reflectionMap;
+
+ MockEmitCModel() : reflectionMap{{"a", &A}, {"b", &B}} {}
+
+ int64_t operator()() { return A - B; }
+};
+
static EmbeddedModelRunnerOptions makeOptions() {
EmbeddedModelRunnerOptions Opts;
Opts.setFeedPrefix("prefix_");
@@ -262,6 +275,50 @@ TEST(ReleaseModelRunner, ModelSelector) {
// expect the model implementation to fail at a point.
}
+TEST(EmitCModelRunner, NormalUse) {
+ LLVMContext Ctx;
+ std::vector<TensorSpec> Inputs{TensorSpec::createSpec<int64_t>("a", {1}),
+ TensorSpec::createSpec<int64_t>("b", {1})};
+ auto Evaluator =
+ std::make_unique<EmitCModelRunner<MockEmitCModel>>(Ctx, Inputs);
+ EXPECT_TRUE(EmitCModelRunner<MockEmitCModel>::classof(Evaluator.get()));
+ EXPECT_EQ(Evaluator->getKind(), MLModelRunner::Kind::Release);
+ *Evaluator->getTensor<int64_t>(0) = 10;
+ *Evaluator->getTensor<int64_t>(1) = 3;
+ EXPECT_EQ(Evaluator->evaluate<int64_t>(), 7);
+ EXPECT_EQ(*Evaluator->getTensor<int64_t>(0), 10);
+ EXPECT_EQ(*Evaluator->getTensor<int64_t>(1), 3);
+}
+
+TEST(EmitCModelRunner, ExtraFeatures) {
+ LLVMContext Ctx;
+ std::vector<TensorSpec> Inputs{TensorSpec::createSpec<int64_t>("a", {1}),
+ TensorSpec::createSpec<int64_t>("b", {1}),
+ TensorSpec::createSpec<int64_t>("c", {1})};
+ auto Evaluator =
+ std::make_unique<EmitCModelRunner<MockEmitCModel>>(Ctx, Inputs);
+ *Evaluator->getTensor<int64_t>(0) = 10;
+ *Evaluator->getTensor<int64_t>(1) = 3;
+ *Evaluator->getTensor<int64_t>(2) = -5;
+ EXPECT_EQ(Evaluator->evaluate<int64_t>(), 7);
+ EXPECT_EQ(*Evaluator->getTensor<int64_t>(0), 10);
+ EXPECT_EQ(*Evaluator->getTensor<int64_t>(1), 3);
+ EXPECT_EQ(*Evaluator->getTensor<int64_t>(2), -5);
+}
+
+TEST(EmitCModelRunner, ExtraFeaturesOutOfOrder) {
+ LLVMContext Ctx;
+ std::vector<TensorSpec> Inputs{TensorSpec::createSpec<int64_t>("b", {1}),
+ TensorSpec::createSpec<int64_t>("a", {1})};
+ auto Evaluator =
+ std::make_unique<EmitCModelRunner<MockEmitCModel>>(Ctx, Inputs);
+ *Evaluator->getTensor<int64_t>(0) = 3; // b
+ *Evaluator->getTensor<int64_t>(1) = 10; // a
+ EXPECT_EQ(Evaluator->evaluate<int64_t>(), 7); // a - b = 10 - 3 = 7
+ EXPECT_EQ(*Evaluator->getTensor<int64_t>(0), 3);
+ EXPECT_EQ(*Evaluator->getTensor<int64_t>(1), 10);
+}
+
#if defined(LLVM_ON_UNIX)
TEST(InteractiveModelRunner, Evaluation) {
LLVMContext Ctx;
>From a7e0e7a3eacb59cb5162578ab4b093cbc6a73243 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Thu, 13 Aug 2026 14:08:31 -0700
Subject: [PATCH 11/12] Update tests
---
llvm/cmake/modules/MLGOLower.cmake | 6 +-
.../MLRegAlloc/default-eviction-advisor.ll | 7 +-
llvm/test/CodeGen/MLRegAlloc/rel-codepath.ll | 6 +-
.../Transforms/Inline/ML/bounds-checks.ll | 6 +-
.../Inline/ML/coro-split-func-levels.ll | 4 +-
llvm/test/Transforms/Inline/ML/dead-callee.ll | 4 +-
.../ML/enable-inline-advisor-printing-ml.ll | 6 +-
llvm/test/Transforms/Inline/ML/fpi-update.ll | 4 +-
.../Inline/ML/ml-test-release-mode.ll | 4 +-
llvm/test/Transforms/Inline/ML/recursive.ll | 4 +-
.../Inline/ML/scc-dead-accounting.ll | 4 +-
.../Transforms/Inline/ML/skip-unreachable.ll | 4 +-
.../ML/state-accounting-skip-non-cold.ll | 6 +-
.../Inline/inlining-advisor-default.ll | 9 +-
llvm/unittests/Analysis/CMakeLists.txt | 1 +
llvm/unittests/Analysis/MLGOUtilsTest.cpp | 179 ++++++++++++++++++
16 files changed, 220 insertions(+), 34 deletions(-)
create mode 100644 llvm/unittests/Analysis/MLGOUtilsTest.cpp
diff --git a/llvm/cmake/modules/MLGOLower.cmake b/llvm/cmake/modules/MLGOLower.cmake
index 28c18eb4defb3..54118bea5df90 100644
--- a/llvm/cmake/modules/MLGOLower.cmake
+++ b/llvm/cmake/modules/MLGOLower.cmake
@@ -47,7 +47,7 @@ function(mlgo_lower_models models mlir_opt mlir_translate target_type
continue()
endif()
- set(CLASS_NAME "Model${MODEL_INDEX}")
+ set(CLASS_NAME "${target_type}_Model${MODEL_INDEX}")
math(EXPR MODEL_INDEX "${MODEL_INDEX} + 1")
if (NOT IS_ABSOLUTE "${MODEL_PATH}")
@@ -102,8 +102,8 @@ function(mlgo_lower_models models mlir_opt mlir_translate target_type
set_source_files_properties(${HEADER_FILE} PROPERTIES GENERATED 1)
# Custom target to force generation of this header
- add_custom_target(mlgo_model_gen_${CLASS_NAME} DEPENDS ${HEADER_FILE})
- list(APPEND MLGO_GEN_TARGETS mlgo_model_gen_${CLASS_NAME})
+ 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")
diff --git a/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll b/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll
index 881a80c41361c..f9509e57e1ff1 100644
--- a/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll
+++ b/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll
@@ -1,11 +1,13 @@
-; Check that, in the absence of dependencies, we emit an error message when
-; trying to use ML-driven advisor.
+; Check that, in the absence of dependencies or a selected model, we emit an
+; error message when trying to use ML-driven advisor.
; REQUIRES: !have_tf_aot
; REQUIRES: !have_tflite
; REQUIRES: default_triple
; RUN: not llc -O2 -regalloc-enable-advisor=development < %s 2>&1 | FileCheck %s
; RUN: not llc -O2 -regalloc-enable-advisor=release < %s 2>&1 | FileCheck %s
; RUN: llc -O2 -regalloc-enable-advisor=default < %s 2>&1 | FileCheck %s --check-prefix=DEFAULT
+; RUN: %if have_mlir_lowering %{ not llc -O2 -regalloc-enable-advisor=release -regalloc-mlgo-model=default < %s 2>&1 | FileCheck %s %}
+; RUN: %if have_mlir_lowering %{ not llc -O2 -regalloc-enable-advisor=release -regalloc-mlgo-model=invalid_model < %s 2>&1 | FileCheck %s --check-prefix=INVALID %}
; regalloc-enable-advisor is not enabled for NVPTX
; UNSUPPORTED: target=nvptx{{.*}}
@@ -18,3 +20,4 @@ define void @f2(i64 %lhs, i64 %rhs, ptr %addr) {
; CHECK: Requested regalloc eviction advisor analysis could not be created. Using default
; DEFAULT-NOT: Requested regalloc eviction advisor analysis could not be created. Using default
+; INVALID: {{.*}}llc{{.*}}: for the --regalloc-mlgo-model option: Cannot find option named 'invalid_model'!
diff --git a/llvm/test/CodeGen/MLRegAlloc/rel-codepath.ll b/llvm/test/CodeGen/MLRegAlloc/rel-codepath.ll
index 03866d0add9bc..d426ce6f8f3e3 100644
--- a/llvm/test/CodeGen/MLRegAlloc/rel-codepath.ll
+++ b/llvm/test/CodeGen/MLRegAlloc/rel-codepath.ll
@@ -1,6 +1,6 @@
-; REQUIRES: have_tf_aot
+; REQUIRES: have_tf_aot || have_mlir_lowering
; REQUIRES: x86_64-linux
-; REQUIRES: llvm_raevict_model_autogenerated
+; REQUIRES: llvm_raevict_model_autogenerated || have_mlir_lowering
;
; Check the code path for release mode is correctly taken. It is shared with
; development mode, and we separately test the internals of that (logged
@@ -10,7 +10,7 @@
; RUN: llc -mtriple=x86_64-linux-unknown -regalloc=greedy -regalloc-enable-advisor=default \
; RUN: %S/Inputs/input.ll -o %t.default
-; RUN: llc -mtriple=x86_64-linux-unknown -regalloc=greedy -regalloc-enable-advisor=release \
+; RUN: llc -mtriple=x86_64-linux-unknown -regalloc=greedy %if have_mlir_lowering %{ -regalloc-enable-advisor=release -regalloc-mlgo-model=regalloc %} %else %{ -regalloc-enable-advisor=release %} \
; RUN: %S/Inputs/input.ll -o %t.release
; RUN: not diff %t.release %t.default
diff --git a/llvm/test/Transforms/Inline/ML/bounds-checks.ll b/llvm/test/Transforms/Inline/ML/bounds-checks.ll
index 7270fa3a93b1e..f1e2911a2004c 100644
--- a/llvm/test/Transforms/Inline/ML/bounds-checks.ll
+++ b/llvm/test/Transforms/Inline/ML/bounds-checks.ll
@@ -2,9 +2,9 @@
; In all cases, the end result is the same: mandatory inlinings must happen.
; However, when we discover we 'trip' over the artificially-low size increase
; factor, we don't inline anymore.
-; REQUIRES: llvm_inliner_model_autogenerated
-; RUN: opt -passes=scc-oz-module-inliner -enable-ml-inliner=release -ml-advisor-size-increase-threshold=10.0 -S < %s 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=NOBOUNDS
-; RUN: opt -passes=scc-oz-module-inliner -enable-ml-inliner=release -ml-advisor-size-increase-threshold=1.0 -S < %s 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=BOUNDS
+; REQUIRES: llvm_inliner_model_autogenerated || have_mlir_lowering
+; RUN: opt -passes=scc-oz-module-inliner %if have_mlir_lowering %{ -enable-ml-inliner=release -mlgo-model=inliner %} %else %{ -enable-ml-inliner=release %} -ml-advisor-size-increase-threshold=10.0 -S < %s 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=NOBOUNDS
+; RUN: opt -passes=scc-oz-module-inliner %if have_mlir_lowering %{ -enable-ml-inliner=release -mlgo-model=inliner %} %else %{ -enable-ml-inliner=release %} -ml-advisor-size-increase-threshold=1.0 -S < %s 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=BOUNDS
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-grtev4-linux-gnu"
diff --git a/llvm/test/Transforms/Inline/ML/coro-split-func-levels.ll b/llvm/test/Transforms/Inline/ML/coro-split-func-levels.ll
index 79e1ebeec17b9..b3660cb7c5646 100644
--- a/llvm/test/Transforms/Inline/ML/coro-split-func-levels.ll
+++ b/llvm/test/Transforms/Inline/ML/coro-split-func-levels.ll
@@ -1,6 +1,6 @@
-; REQUIRES: llvm_inliner_model_autogenerated
+; REQUIRES: llvm_inliner_model_autogenerated || have_mlir_lowering
; RUN: opt -S -passes='coro-early,scc-oz-module-inliner,print<inline-advisor>' \
-; RUN: -enable-ml-inliner=release -keep-inline-advisor-for-printing < %s
+; RUN: %if have_mlir_lowering %{ -enable-ml-inliner=release -mlgo-model=inliner %} %else %{ -enable-ml-inliner=release %} -keep-inline-advisor-for-printing < %s
define void @_Z5get_sv() presplitcoroutine {
%1 = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
diff --git a/llvm/test/Transforms/Inline/ML/dead-callee.ll b/llvm/test/Transforms/Inline/ML/dead-callee.ll
index a88655777e6f9..34d44eda1a625 100644
--- a/llvm/test/Transforms/Inline/ML/dead-callee.ll
+++ b/llvm/test/Transforms/Inline/ML/dead-callee.ll
@@ -1,5 +1,5 @@
-; REQUIRES: llvm_inliner_model_autogenerated
-; RUN: opt -passes=inliner-ml-advisor-release -S < %s | FileCheck %s
+; REQUIRES: llvm_inliner_model_autogenerated || have_mlir_lowering
+; RUN: opt %if have_mlir_lowering %{ -passes=inliner-ml-advisor-release -mlgo-model=inliner %} %else %{ -passes=inliner-ml-advisor-release %} -S < %s | FileCheck %s
; Check that our accounting works when a function in a non-trivial SCC is dead.
diff --git a/llvm/test/Transforms/Inline/ML/enable-inline-advisor-printing-ml.ll b/llvm/test/Transforms/Inline/ML/enable-inline-advisor-printing-ml.ll
index 376548b3f06fe..f72fb741cc037 100644
--- a/llvm/test/Transforms/Inline/ML/enable-inline-advisor-printing-ml.ll
+++ b/llvm/test/Transforms/Inline/ML/enable-inline-advisor-printing-ml.ll
@@ -1,10 +1,10 @@
-; REQUIRES: llvm_inliner_model_autogenerated
+; REQUIRES: llvm_inliner_model_autogenerated || have_mlir_lowering
-; RUN: opt -enable-ml-inliner=release -passes=scc-oz-module-inliner \
+; RUN: opt %if have_mlir_lowering %{ -enable-ml-inliner=release -mlgo-model=inliner %} %else %{ -enable-ml-inliner=release %} -passes=scc-oz-module-inliner \
; RUN: -keep-inline-advisor-for-printing -mandatory-inlining-first=1 \
; RUN: -enable-scc-inline-advisor-printing -S < %s 2>&1 | FileCheck %s
-; RUN: opt -enable-ml-inliner=release -passes=scc-oz-module-inliner \
+; RUN: opt %if have_mlir_lowering %{ -enable-ml-inliner=release -mlgo-model=inliner %} %else %{ -enable-ml-inliner=release %} -passes=scc-oz-module-inliner \
; RUN: -keep-inline-advisor-for-printing -mandatory-inlining-first=0 \
; RUN: -enable-scc-inline-advisor-printing -S < %s 2>&1 \
; RUN: | FileCheck %s --check-prefix=TWO
diff --git a/llvm/test/Transforms/Inline/ML/fpi-update.ll b/llvm/test/Transforms/Inline/ML/fpi-update.ll
index 9208ad70b9a35..2a3fa12616629 100644
--- a/llvm/test/Transforms/Inline/ML/fpi-update.ll
+++ b/llvm/test/Transforms/Inline/ML/fpi-update.ll
@@ -3,9 +3,9 @@ target triple = "x86_64-grtev4-linux-gnu"
; TODO: we could instantiate the MLInliner with a non-model generated evaluator
; and drop the requirement
-; REQUIRES: llvm_inliner_model_autogenerated
+; REQUIRES: llvm_inliner_model_autogenerated || have_mlir_lowering
-; RUN: opt -enable-ml-inliner=release -passes='scc-oz-module-inliner,print<inline-advisor>' \
+; RUN: opt %if have_mlir_lowering %{ -enable-ml-inliner=release -mlgo-model=inliner %} %else %{ -enable-ml-inliner=release %} -passes='scc-oz-module-inliner,print<inline-advisor>' \
; RUN: -keep-inline-advisor-for-printing -max-devirt-iterations=0 \
; RUN: -mandatory-inlining-first=0 -S < %s 2>&1 | FileCheck %s
diff --git a/llvm/test/Transforms/Inline/ML/ml-test-release-mode.ll b/llvm/test/Transforms/Inline/ML/ml-test-release-mode.ll
index 9cbbfdd124e1f..711652a197fbf 100644
--- a/llvm/test/Transforms/Inline/ML/ml-test-release-mode.ll
+++ b/llvm/test/Transforms/Inline/ML/ml-test-release-mode.ll
@@ -5,6 +5,6 @@
; This test uses Inputs/test-module.ll, as it will share it with a similar test
; for the 'development' mode.
;
-; REQUIRES: llvm_inliner_model_autogenerated
-; RUN: opt -passes=scc-oz-module-inliner -enable-ml-inliner=release -S < %S/Inputs/test-module.ll 2>&1 | FileCheck %S/Inputs/test-module.ll --check-prefix=CHECK
+; REQUIRES: llvm_inliner_model_autogenerated || have_mlir_lowering
+; RUN: opt -passes=scc-oz-module-inliner %if have_mlir_lowering %{ -enable-ml-inliner=release -mlgo-model=inliner %} %else %{ -enable-ml-inliner=release %} -S < %S/Inputs/test-module.ll 2>&1 | FileCheck %S/Inputs/test-module.ll --check-prefix=CHECK
; RUN: opt -passes=scc-oz-module-inliner -enable-ml-inliner=default -S < %S/Inputs/test-module.ll 2>&1 | FileCheck %S/Inputs/test-module.ll --check-prefix=DEFAULT
diff --git a/llvm/test/Transforms/Inline/ML/recursive.ll b/llvm/test/Transforms/Inline/ML/recursive.ll
index 2d9240a12a713..db5b01d90bfbd 100644
--- a/llvm/test/Transforms/Inline/ML/recursive.ll
+++ b/llvm/test/Transforms/Inline/ML/recursive.ll
@@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
-; REQUIRES: llvm_inliner_model_autogenerated
-; RUN: opt -S %s -o - -passes='inliner-ml-advisor-release' -ml-inliner-skip-policy=if-caller-not-cold | FileCheck %s
+; REQUIRES: llvm_inliner_model_autogenerated || have_mlir_lowering
+; RUN: opt -S %s -o - %if have_mlir_lowering %{ -passes='inliner-ml-advisor-release' -mlgo-model=inliner %} %else %{ -passes='inliner-ml-advisor-release' %} -ml-inliner-skip-policy=if-caller-not-cold | FileCheck %s
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "aarch64-unknown-linux-android29"
diff --git a/llvm/test/Transforms/Inline/ML/scc-dead-accounting.ll b/llvm/test/Transforms/Inline/ML/scc-dead-accounting.ll
index 41c0e4c313811..897475cd6dbae 100644
--- a/llvm/test/Transforms/Inline/ML/scc-dead-accounting.ll
+++ b/llvm/test/Transforms/Inline/ML/scc-dead-accounting.ll
@@ -9,9 +9,9 @@
; In this example if loop-unroll is ran after a mandatory inlining CGSCC pass,
; edges would increase but wouldn't be tracked
-; REQUIRES: llvm_inliner_model_autogenerated
+; REQUIRES: llvm_inliner_model_autogenerated || have_mlir_lowering
-; RUN: opt -enable-ml-inliner=release -passes=inliner-ml-advisor-release \
+; RUN: opt %if have_mlir_lowering %{ -enable-ml-inliner=release -mlgo-model=inliner %} %else %{ -enable-ml-inliner=release %} -passes=inliner-ml-advisor-release \
; RUN: -keep-inline-advisor-for-printing \
; RUN: -enable-scc-inline-advisor-printing -S < %s 2>&1 | FileCheck %s
diff --git a/llvm/test/Transforms/Inline/ML/skip-unreachable.ll b/llvm/test/Transforms/Inline/ML/skip-unreachable.ll
index 47a75c3ad898b..2f548edd65dd1 100644
--- a/llvm/test/Transforms/Inline/ML/skip-unreachable.ll
+++ b/llvm/test/Transforms/Inline/ML/skip-unreachable.ll
@@ -1,7 +1,7 @@
; Test skipping inlining when the callsite is unreachable (for both mandatory
; and non-mandatory cases)
-; REQUIRES: llvm_inliner_model_autogenerated
-; RUN: opt -passes=inliner-ml-advisor-release -S < %s | FileCheck %s --check-prefix=CHECK
+; REQUIRES: llvm_inliner_model_autogenerated || have_mlir_lowering
+; RUN: opt %if have_mlir_lowering %{ -passes=inliner-ml-advisor-release -mlgo-model=inliner %} %else %{ -passes=inliner-ml-advisor-release %} -S < %s | FileCheck %s --check-prefix=CHECK
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-grtev4-linux-gnu"
diff --git a/llvm/test/Transforms/Inline/ML/state-accounting-skip-non-cold.ll b/llvm/test/Transforms/Inline/ML/state-accounting-skip-non-cold.ll
index 0887f5e29187d..e75523cf90b58 100644
--- a/llvm/test/Transforms/Inline/ML/state-accounting-skip-non-cold.ll
+++ b/llvm/test/Transforms/Inline/ML/state-accounting-skip-non-cold.ll
@@ -1,7 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; REQUIRES: llvm_inliner_model_autogenerated && asserts
-; RUN: opt -passes='default<O3>' -enable-ml-inliner=release -ml-inliner-skip-policy=if-caller-not-cold -S %s -o - | FileCheck %s
-; RUN: opt -passes='default<O3>' -ml-inliner-stop-immediately -enable-ml-inliner=release -ml-inliner-skip-policy=if-caller-not-cold -S %s -o - | FileCheck %s
+; REQUIRES: (llvm_inliner_model_autogenerated || have_mlir_lowering) && asserts
+; RUN: opt -passes='default<O3>' %if have_mlir_lowering %{ -enable-ml-inliner=release -mlgo-model=inliner %} %else %{ -enable-ml-inliner=release %} -ml-inliner-skip-policy=if-caller-not-cold -S %s -o - | FileCheck %s
+; RUN: opt -passes='default<O3>' -ml-inliner-stop-immediately %if have_mlir_lowering %{ -enable-ml-inliner=release -mlgo-model=inliner %} %else %{ -enable-ml-inliner=release %} -ml-inliner-skip-policy=if-caller-not-cold -S %s -o - | FileCheck %s
declare ptr @f()
diff --git a/llvm/test/Transforms/Inline/inlining-advisor-default.ll b/llvm/test/Transforms/Inline/inlining-advisor-default.ll
index 502a16280192d..87ddc286092d4 100644
--- a/llvm/test/Transforms/Inline/inlining-advisor-default.ll
+++ b/llvm/test/Transforms/Inline/inlining-advisor-default.ll
@@ -1,10 +1,13 @@
-; Check that, in the absence of dependencies, we emit an error message when
-; trying to use ML-driven inlining.
+; Check that, in the absence of dependencies or a selected model, we emit an
+; error message when trying to use ML-driven inlining.
; REQUIRES: !have_tf_aot
; REQUIRES: !have_tflite
; RUN: not opt -passes=scc-oz-module-inliner -enable-ml-inliner=development -S < %s 2>&1 | FileCheck %s
; RUN: not opt -passes=scc-oz-module-inliner -enable-ml-inliner=release -S < %s 2>&1 | FileCheck %s
+; RUN: %if have_mlir_lowering %{ not opt -passes=scc-oz-module-inliner -enable-ml-inliner=release -mlgo-model=default -S < %s 2>&1 | FileCheck %s %}
+; RUN: %if have_mlir_lowering %{ not opt -passes=scc-oz-module-inliner -enable-ml-inliner=release -mlgo-model=invalid_model -S < %s 2>&1 | FileCheck %s --check-prefix=INVALID %}
declare i64 @f1()
-; CHECK: Could not setup Inlining Advisor for the requested mode and/or options
\ No newline at end of file
+; CHECK: Could not setup Inlining Advisor for the requested mode and/or options
+; INVALID: {{.*}}opt{{.*}}: for the --mlgo-model option: Cannot find option named 'invalid_model'!
\ No newline at end of file
diff --git a/llvm/unittests/Analysis/CMakeLists.txt b/llvm/unittests/Analysis/CMakeLists.txt
index b1f8cb918d590..9ca57024a6134 100644
--- a/llvm/unittests/Analysis/CMakeLists.txt
+++ b/llvm/unittests/Analysis/CMakeLists.txt
@@ -43,6 +43,7 @@ set(ANALYSIS_TEST_SOURCES
MemoryBuiltinsTest.cpp
MemoryProfileInfoTest.cpp
MemorySSATest.cpp
+ MLGOUtilsTest.cpp
MLModelRunnerTest.cpp
PhiValuesTest.cpp
PluginInlineAdvisorAnalysisTest.cpp
diff --git a/llvm/unittests/Analysis/MLGOUtilsTest.cpp b/llvm/unittests/Analysis/MLGOUtilsTest.cpp
new file mode 100644
index 0000000000000..34e2e1a71b3c9
--- /dev/null
+++ b/llvm/unittests/Analysis/MLGOUtilsTest.cpp
@@ -0,0 +1,179 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Analysis/Utils/MLGOUtils.h"
+#include "llvm/Analysis/EmitCModelRunner.h"
+#include "llvm/Analysis/ReleaseModeModelRunner.h"
+#include "llvm/Analysis/TensorSpec.h"
+#include "llvm/IR/LLVMContext.h"
+#include "gtest/gtest.h"
+#include <map>
+#include <string>
+
+using namespace llvm;
+
+namespace {
+
+class MockAOTModel final {
+ int64_t A = 0;
+ int64_t B = 0;
+ int64_t R = 0;
+
+public:
+ MockAOTModel() = default;
+ int LookupArgIndex(const std::string &Name) {
+ if (Name == "feed_a")
+ return 0;
+ if (Name == "feed_b")
+ return 1;
+ return -1;
+ }
+ int LookupResultIndex(const std::string &) { return 0; }
+ void Run() { R = A + B; }
+ void *result_data(int RIndex) { return (RIndex == 0) ? &R : nullptr; }
+ void *arg_data(int Index) {
+ switch (Index) {
+ case 0:
+ return &A;
+ case 1:
+ return &B;
+ default:
+ return nullptr;
+ }
+ }
+};
+
+class MockEmitCModel1 final {
+ int64_t A = 0;
+ int64_t B = 0;
+
+public:
+ std::map<std::string, void *> reflectionMap;
+
+ MockEmitCModel1() : reflectionMap{{"a", &A}, {"b", &B}} {}
+
+ int64_t operator()() { return A - B; }
+};
+
+class MockEmitCModel2 final {
+ int64_t A = 0;
+ int64_t B = 0;
+
+public:
+ std::map<std::string, void *> reflectionMap;
+
+ MockEmitCModel2() : reflectionMap{{"a", &A}, {"b", &B}} {}
+
+ int64_t operator()() { return A + B; }
+};
+
+enum class TestModelChoice { Default, Model1, Model2 };
+
+TEST(MLGOUtilsTest, IsReleaseModelValid) {
+ // With NoopSavedModelImpl (no embedded AOT model):
+ // 1. Default model choice without interactive channel -> invalid
+ EXPECT_FALSE(
+ (isReleaseModelValid<NoopSavedModelImpl>("", TestModelChoice::Default)));
+ // 2. Selected model choice -> valid
+ EXPECT_TRUE(
+ (isReleaseModelValid<NoopSavedModelImpl>("", TestModelChoice::Model1)));
+ EXPECT_TRUE(
+ (isReleaseModelValid<NoopSavedModelImpl>("", TestModelChoice::Model2)));
+ // 3. Interactive channel specified -> valid regardless of model choice
+ EXPECT_TRUE((isReleaseModelValid<NoopSavedModelImpl>(
+ "channel", TestModelChoice::Default)));
+
+ // With a custom default model value:
+ EXPECT_FALSE((isReleaseModelValid<NoopSavedModelImpl>(
+ "", TestModelChoice::Model1,
+ /*DefaultModelVal=*/TestModelChoice::Model1)));
+
+ // With a valid embedded AOT model (MockAOTModel):
+ EXPECT_TRUE(
+ (isReleaseModelValid<MockAOTModel>("", TestModelChoice::Default)));
+
+ // Overload with cl::opt<EnumType>:
+ cl::opt<TestModelChoice> OptChoice("test-mlgo-utils-choice",
+ cl::init(TestModelChoice::Default));
+ EXPECT_FALSE((isReleaseModelValid<NoopSavedModelImpl>("", OptChoice)));
+ OptChoice = TestModelChoice::Model1;
+ EXPECT_TRUE((isReleaseModelValid<NoopSavedModelImpl>("", OptChoice)));
+ OptChoice = TestModelChoice::Default;
+ EXPECT_TRUE((isReleaseModelValid<NoopSavedModelImpl>("channel", OptChoice)));
+ EXPECT_TRUE((isReleaseModelValid<MockAOTModel>("", OptChoice)));
+}
+
+TEST(MLGOUtilsTest, CreateReleaseModeModelRunnerModelSelection) {
+ LLVMContext Ctx;
+ std::vector<TensorSpec> Inputs{TensorSpec::createSpec<int64_t>("a", {1}),
+ TensorSpec::createSpec<int64_t>("b", {1})};
+ TensorSpec OutputSpec = TensorSpec::createSpec<int64_t>("result", {1});
+
+ auto CreateRunnerForChoice =
+ [&](TestModelChoice Choice) -> std::unique_ptr<MLModelRunner> {
+ auto Factory = [&](LLVMContext &C, const std::vector<TensorSpec> &Specs)
+ -> std::unique_ptr<MLModelRunner> {
+ switch (Choice) {
+ case TestModelChoice::Default:
+ return nullptr;
+ case TestModelChoice::Model1:
+ return std::make_unique<EmitCModelRunner<MockEmitCModel1>>(C, Specs);
+ case TestModelChoice::Model2:
+ return std::make_unique<EmitCModelRunner<MockEmitCModel2>>(C, Specs);
+ }
+ llvm_unreachable("unknown model choice");
+ };
+ return createReleaseModeModelRunner<NoopSavedModelImpl,
+ /*HaveMLIRLowering=*/true>(
+ Ctx, Inputs, "decision", "", OutputSpec, Factory);
+ };
+
+ // Default choice produces no runner
+ EXPECT_EQ(CreateRunnerForChoice(TestModelChoice::Default), nullptr);
+
+ // Model1 choice produces Model1 (A - B)
+ auto Runner1 = CreateRunnerForChoice(TestModelChoice::Model1);
+ ASSERT_NE(Runner1, nullptr);
+ EXPECT_TRUE(EmitCModelRunner<MockEmitCModel1>::classof(Runner1.get()));
+ *Runner1->getTensor<int64_t>(0) = 10;
+ *Runner1->getTensor<int64_t>(1) = 3;
+ EXPECT_EQ(Runner1->evaluate<int64_t>(), 7);
+
+ // Model2 choice produces Model2 (A + B)
+ auto Runner2 = CreateRunnerForChoice(TestModelChoice::Model2);
+ ASSERT_NE(Runner2, nullptr);
+ EXPECT_TRUE(EmitCModelRunner<MockEmitCModel2>::classof(Runner2.get()));
+ *Runner2->getTensor<int64_t>(0) = 10;
+ *Runner2->getTensor<int64_t>(1) = 3;
+ EXPECT_EQ(Runner2->evaluate<int64_t>(), 13);
+}
+
+TEST(MLGOUtilsTest, CreateReleaseModeModelRunnerAOTFallback) {
+ LLVMContext Ctx;
+ std::vector<TensorSpec> Inputs{TensorSpec::createSpec<int64_t>("a", {1}),
+ TensorSpec::createSpec<int64_t>("b", {1})};
+ TensorSpec OutputSpec = TensorSpec::createSpec<int64_t>("result", {1});
+
+ auto DummyEmitCFactory =
+ [](LLVMContext &,
+ const std::vector<TensorSpec> &) -> std::unique_ptr<MLModelRunner> {
+ llvm_unreachable(
+ "EmitC factory should not be called when HaveMLIRLowering=false");
+ };
+
+ auto Runner =
+ createReleaseModeModelRunner<MockAOTModel, /*HaveMLIRLowering=*/false>(
+ Ctx, Inputs, "result", "", OutputSpec, DummyEmitCFactory);
+ ASSERT_NE(Runner, nullptr);
+ EXPECT_TRUE(ReleaseModeModelRunner<MockAOTModel>::classof(Runner.get()));
+ *Runner->getTensor<int64_t>(0) = 10;
+ *Runner->getTensor<int64_t>(1) = 3;
+ EXPECT_EQ(Runner->evaluate<int64_t>(), 13);
+}
+
+} // namespace
>From 2da25762e24ab0daf0f33cb07b43ac93661761c2 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Thu, 13 Aug 2026 15:05:22 -0700
Subject: [PATCH 12/12] Add todo about removing hardcoded pass pipeline
---
llvm/cmake/modules/MLGOLower.cmake | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/cmake/modules/MLGOLower.cmake b/llvm/cmake/modules/MLGOLower.cmake
index 54118bea5df90..a8ba142000b7c 100644
--- a/llvm/cmake/modules/MLGOLower.cmake
+++ b/llvm/cmake/modules/MLGOLower.cmake
@@ -59,6 +59,8 @@ function(mlgo_lower_models models mlir_opt mlir_translate target_type
# 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.
set(MLIR_PASSES
"func.func(tosa-to-linalg-named,tosa-to-linalg,tosa-to-arith,tosa-to-tensor)"
"symbol-privatize"
More information about the llvm-commits
mailing list