@@ -64,12 +64,9 @@ endif()
6464# When not specified, FetchContent the IREE sources from GitHub at the tag
6565# specified by `IREE_GIT_TAG`.
6666#
67- # We choose not to take a source dependency on the IREE compiler, which is
68- # quite heavy and can be slow to build. It is left to the consumer of the EP to
69- # make sure iree compiler tooling scripts are available in their environment.
70- # This can be generally done by `pip install iree-base-compiler`.
71-
72- # We may in future decide to build the IREE compiler from source.
67+ # The EP dynamically loads libIREECompiler.so at runtime via dlopen (no
68+ # compile-time linking to the compiler). The loader source and C API headers
69+ # come from the IREE source tree (compiler/bindings/c/).
7370#
7471################################################################################
7572
@@ -81,7 +78,7 @@ set(IREE_GIT_TAG "v3.10.0")
8178set (IREE_SOURCE_DIR "" CACHE FILEPATH "Path to IREE source" )
8279
8380# Set IREE build flags.
84- set (IREE_VISIBILITY_HIDDEN OFF )
81+ set (IREE_VISIBILITY_HIDDEN ON )
8582set (IREE_BUILD_COMPILER OFF )
8683set (IREE_BUILD_TESTS OFF )
8784set (IREE_BUILD_SAMPLES OFF )
@@ -103,6 +100,9 @@ endif()
103100
104101if (IREE_SOURCE_DIR )
105102 message (STATUS "Using existing IREE sources: ${IREE_SOURCE_DIR} " )
103+ # Normalize so downstream code can use iree_SOURCE_DIR unconditionally
104+ # (FetchContent sets this automatically; local source needs it explicitly).
105+ set (iree_SOURCE_DIR "${IREE_SOURCE_DIR} " )
106106 add_subdirectory (${IREE_SOURCE_DIR} iree SYSTEM EXCLUDE_FROM_ALL )
107107else ()
108108 message (STATUS "Fetching IREE sources from tag ${IREE_GIT_TAG} " )
@@ -131,6 +131,65 @@ else()
131131 endif ()
132132endif ()
133133
134+ ################################################################################
135+ # Compiler Loader
136+ #
137+ # The EP uses the IREE compiler C API (embedding_api.h) via a dynamic loader
138+ # (loader.h / loader.cpp). At runtime, the EP loads libIREECompiler.so via
139+ # dlopen — there is no compile-time linking to the compiler. The loader source
140+ # and C API headers come from the IREE source tree.
141+ #
142+ # At runtime, the compiler library is resolved in this order:
143+ # 1. Session option: ep.iree.compiler_lib_path (explicit path)
144+ # 2. Env var: IREE_EP_COMPILER_LIB (full path to library file)
145+ # 3. Env var: IREE_EP_COMPILER_LIB_DIR (directory to search)
146+ # 4. Relative to EP .so: iree/compiler/_mlir_libs/ (pip co-installs)
147+ # 5. Fallback: dlopen("libIREECompiler.so") via standard search paths
148+ #
149+ ################################################################################
150+
151+ # Helper: check whether a Python module is importable in the active environment.
152+ # Sets ${result_var} to TRUE/FALSE in the caller's scope.
153+ function (check_python_import module result_var )
154+ set (Python3_FIND_VIRTUALENV FIRST)
155+ find_package (Python3 COMPONENTS Interpreter QUIET )
156+ unset (Python3_FIND_VIRTUALENV)
157+ if (NOT Python3_FOUND)
158+ message (STATUS "Python3 interpreter not found; skipping ${module} check" )
159+ set (${result_var} FALSE PARENT_SCOPE )
160+ return ()
161+ endif ()
162+ execute_process (
163+ COMMAND "${Python3_EXECUTABLE} " -c
164+ "import importlib.util; exit(0 if importlib.util.find_spec('${module} ') else 1)"
165+ RESULT_VARIABLE _import_result
166+ OUTPUT_QUIET ERROR_QUIET
167+ )
168+ if (_import_result EQUAL 0)
169+ set (${result_var} TRUE PARENT_SCOPE )
170+ else ()
171+ set (${result_var} FALSE PARENT_SCOPE )
172+ endif ()
173+ endfunction ()
174+
175+ # Check for iree-base-compiler. Not required at build time — the EP discovers
176+ # libIREECompiler.so at runtime. A warning is printed if not found.
177+ # Use iree.compiler._mlir_libs (not iree.compiler) because the IREE build tree
178+ # creates a namespace package that makes find_spec('iree.compiler') succeed
179+ # even without iree-base-compiler installed.
180+ check_python_import ("iree.compiler._mlir_libs" _iree_compiler_installed )
181+ if (_iree_compiler_installed)
182+ message (STATUS "iree-base-compiler is installed" )
183+ else ()
184+ message (WARNING
185+ "iree-base-compiler is not installed in the active Python environment.\n "
186+ "The EP will still build but will fail to compile models at runtime unless "
187+ "iree-base-compiler is installed or IREE_EP_COMPILER_LIB / "
188+ "IREE_EP_COMPILER_LIB_DIR env var is set." )
189+ endif ()
190+
191+ set (IREE_COMPILER_BINDINGS_DIR "${iree_SOURCE_DIR} /compiler/bindings/c" )
192+
134193# Build shared library (EP plugin)
135194add_library (onnxruntime_ep_iree SHARED
136195 src/plugin_entry.cc
@@ -145,20 +204,61 @@ add_library(onnxruntime_ep_iree SHARED
145204 src/temp_file.cc
146205)
147206
207+ # Build IREE compiler loader as a static library. The loader's trampoline
208+ # functions use IREE_EMBED_EXPORTED (visibility("default")) which we can't
209+ # override at compile time. Instead, --exclude-libs localizes all symbols
210+ # from this archive when linking into our shared library.
211+ add_library (iree_compiler_loader STATIC
212+ ${IREE_COMPILER_BINDINGS_DIR} /iree/compiler/loader/loader.cpp
213+ )
214+ set_target_properties (iree_compiler_loader PROPERTIES POSITION_INDEPENDENT_CODE ON )
215+ target_include_directories (iree_compiler_loader PRIVATE
216+ ${IREE_COMPILER_BINDINGS_DIR}
217+ )
218+ if (MSVC )
219+ target_compile_options (iree_compiler_loader PRIVATE /w )
220+ else ()
221+ target_compile_options (iree_compiler_loader PRIVATE -w )
222+ endif ()
223+
148224# Statically link IREE runtime and io/parameter libraries.
225+ # Link dl for dlopen/dlsym (compiler loader).
149226target_link_libraries (onnxruntime_ep_iree PRIVATE
150227 iree_runtime_unified
151228 iree_io_parameter_index
152229 iree_io_parameter_index_provider
153230 iree_io_formats_irpa_irpa
154231 iree_modules_io_parameters_parameters
232+ iree_compiler_loader
233+ ${CMAKE_DL_LIBS}
155234)
156235
236+ # Localize all symbols from the compiler loader static library so its
237+ # IREE_EMBED_EXPORTED trampolines don't leak into our public symbol table.
238+ if (UNIX AND NOT APPLE )
239+ # GNU ld: hide all symbols from the loader's static archive.
240+ target_link_options (onnxruntime_ep_iree PRIVATE
241+ "LINKER:--exclude-libs,libiree_compiler_loader.a"
242+ )
243+ elseif (APPLE )
244+ # macOS ld: generate an unexported symbols list from the loader's trampolines.
245+ # The ireeCompiler* symbols are generated by loader.cpp with
246+ # visibility("default") — we re-hide them here.
247+ set (_unexport_file "${CMAKE_CURRENT_BINARY_DIR} /iree_loader_unexported.txt" )
248+ file (WRITE "${_unexport_file} " "_ireeCompiler*\n " )
249+ target_link_options (onnxruntime_ep_iree PRIVATE
250+ "LINKER:-unexported_symbols_list,${_unexport_file} "
251+ )
252+ endif ()
253+ # On Windows, MSVC only exports symbols explicitly marked __declspec(dllexport),
254+ # so the loader trampolines (which use visibility("default"), a no-op on MSVC)
255+ # are not exported. No additional linker flags needed.
157256
158257# Include directories
159258target_include_directories (onnxruntime_ep_iree PRIVATE
160259 ${IREE_EP_ONNX_SRC_DIR} /include
161260 ${CMAKE_CURRENT_SOURCE_DIR} /src
261+ ${IREE_COMPILER_BINDINGS_DIR}
162262)
163263
164264# Compiler flags
@@ -177,23 +277,15 @@ else()
177277 )
178278endif ()
179279
180- # Export symbols for plugin loading
181- if (MSVC )
182- # Windows: Use .def file (ORT_EXPORT is defined as empty in ORT headers on Windows)
183- set_target_properties (onnxruntime_ep_iree PROPERTIES
184- LINK_FLAGS "/DEF:${CMAKE_CURRENT_SOURCE_DIR} /src/symbols.def"
185- )
186- elseif (UNIX AND NOT APPLE )
187- # Linux: Use version script
188- set_target_properties (onnxruntime_ep_iree PROPERTIES
189- LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR} /src/symbols.lds"
190- )
191- elseif (APPLE )
192- # macOS: Export all symbols
193- set_target_properties (onnxruntime_ep_iree PROPERTIES
194- LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR} /src/symbols.txt"
195- )
196- endif ()
280+ # Hide all symbols by default. Only plugin entry points (CreateEpFactories,
281+ # ReleaseEpFactory) are exported via explicit visibility("default") attributes.
282+ # VISIBILITY_INLINES_HIDDEN suppresses weak inline/template symbols (e.g.
283+ # std::format, libstdc++ internals) that would otherwise leak into the ABI.
284+ set_target_properties (onnxruntime_ep_iree PROPERTIES
285+ CXX_VISIBILITY_PRESET hidden
286+ C_VISIBILITY_PRESET hidden
287+ VISIBILITY_INLINES_HIDDEN ON
288+ )
197289
198290# Install
199291install (TARGETS onnxruntime_ep_iree
0 commit comments