blob: b5647bc510756bf06b4f9d5e1b6ae1e43f98d381 [file]
# Copyright 2026 The IREE Authors
#
# Licensed 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
# CMake side of the renamed IREE compiler ABI (IREE_EXPERIMENTAL_COMPILER_DYNAMIC_PLUGINS).
#
# Renaming every llvm/mlir C++ symbol lets libIREECompiler share a process with
# a foreign LLVM/MLIR. Plugins rename to match so they resolve against it.
# gen_rename_map.py decides the spelling, shared with Bazel so both builds land
# on the same ABI.
#
# Everything becomes a static archive first, because that is what llvm-objcopy
# takes. The linked library cannot be renamed instead: rewriting .dynsym breaks
# the ELF hash sections.
# Target names reach declared outputs, and some contain dots (`obj.MLIRCAPIIR`).
function(iree_renamed_link_sanitize_name VALUE OUT_VAR)
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" _result "${VALUE}")
set(${OUT_VAR} "${_result}" PARENT_SCOPE)
endfunction()
# Evaluates $<BOOL:...>, where a path is true. if() would call it false under
# CMP0054, inverting the very paths LLVM guards its link flags with.
function(iree_renamed_link_genex_bool VALUE OUT_VAR)
string(TOUPPER "${VALUE}" _upper)
if(_upper STREQUAL "" OR _upper STREQUAL "0" OR _upper STREQUAL "FALSE"
OR _upper STREQUAL "OFF" OR _upper STREQUAL "N" OR _upper STREQUAL "NO"
OR _upper STREQUAL "IGNORE" OR _upper STREQUAL "NOTFOUND"
OR _upper MATCHES "-NOTFOUND$")
set(${OUT_VAR} FALSE PARENT_SCOPE)
else()
set(${OUT_VAR} TRUE PARENT_SCOPE)
endif()
endfunction()
# Rewrites one static archive to the renamed ABI.
#
# Parameters:
# OUT_VAR: variable receiving the renamed archive path.
# NAME: base name for the outputs (must be unique in this directory).
# INPUT: archive path (generator expressions allowed).
# DEPENDS: extra dependencies of the rename actions.
function(iree_renamed_archive OUT_VAR)
cmake_parse_arguments(_RULE "" "NAME;INPUT" "DEPENDS" ${ARGN})
set(_dir "${CMAKE_CURRENT_BINARY_DIR}/renamed")
set(_map "${_dir}/${_RULE_NAME}.rename_map")
set(_out "${_dir}/lib${_RULE_NAME}.renamed.a")
set(_script "${IREE_ROOT_DIR}/build_tools/bazel/gen_rename_map.py")
add_custom_command(
OUTPUT "${_map}"
COMMAND
"${Python3_EXECUTABLE}" "${_script}"
--nm "$<TARGET_FILE:llvm-nm>"
--cxxfilt "$<TARGET_FILE:llvm-cxxfilt>"
--input "${_RULE_INPUT}"
--out "${_map}"
--symbol-prefix "${IREE_COMPILER_ABI_PREFIX}"
DEPENDS "${_RULE_INPUT}" "${_script}" llvm-nm llvm-cxxfilt ${_RULE_DEPENDS}
COMMENT "Computing llvm/mlir rename map for ${_RULE_NAME}"
)
add_custom_command(
OUTPUT "${_out}"
COMMAND
"$<TARGET_FILE:llvm-objcopy>" "--redefine-syms=${_map}"
"${_RULE_INPUT}" "${_out}"
DEPENDS "${_RULE_INPUT}" "${_map}" llvm-objcopy ${_RULE_DEPENDS}
COMMENT "Renaming llvm/mlir symbols in ${_RULE_NAME}"
)
set(${OUT_VAR} "${_out}" PARENT_SCOPE)
endfunction()
# As iree_renamed_archive, but starting from an object library's objects.
function(iree_renamed_archive_from_objects OUT_VAR)
cmake_parse_arguments(_RULE "" "NAME;TARGET" "" ${ARGN})
set(_dir "${CMAKE_CURRENT_BINARY_DIR}/renamed")
set(_archive "${_dir}/lib${_RULE_NAME}.objects.a")
add_custom_command(
OUTPUT "${_archive}"
COMMAND "$<TARGET_FILE:llvm-ar>" rcs "${_archive}"
"$<TARGET_OBJECTS:${_RULE_TARGET}>"
DEPENDS "$<TARGET_OBJECTS:${_RULE_TARGET}>" ${_RULE_TARGET} llvm-ar
COMMAND_EXPAND_LISTS
COMMENT "Archiving objects of ${_RULE_TARGET}"
)
iree_renamed_archive(_renamed
NAME "${_RULE_NAME}"
INPUT "${_archive}"
)
set(${OUT_VAR} "${_renamed}" PARENT_SCOPE)
endfunction()
# Splits the transitive link closure into archives to rename (STATIC_LIBS_VAR)
# and everything else, passed through untouched (OTHER_VAR). Imported targets
# and C archives are safe to pass through: they define no mangled C++ names.
function(iree_collect_static_link_closure STATIC_LIBS_VAR OTHER_VAR)
set(_worklist ${ARGN})
set(_visited "")
set(_static_libs "")
set(_other "")
while(_worklist)
list(POP_FRONT _worklist _item)
if(_item IN_LIST _visited)
continue()
endif()
list(APPEND _visited "${_item}")
# Directory-scope markers CMake embeds in LINK_LIBRARIES values.
if(_item MATCHES "^::@")
continue()
endif()
if(_item MATCHES "^\\$<LINK_ONLY:(.+)>$")
list(APPEND _worklist "${CMAKE_MATCH_1}")
continue()
endif()
# Decidable here: every closure target exists by the time this walk runs.
if(_item MATCHES "^\\$<TARGET_NAME_IF_EXISTS:([^>]+)>$")
if(TARGET "${CMAKE_MATCH_1}")
list(APPEND _worklist "${CMAKE_MATCH_1}")
endif()
continue()
endif()
if(_item MATCHES "^\\$<IF:\\$<TARGET_EXISTS:([^>]+)>,([^,]*),([^>]*)>$")
if(TARGET "${CMAKE_MATCH_1}")
set(_resolved "${CMAKE_MATCH_2}")
else()
set(_resolved "${CMAKE_MATCH_3}")
endif()
if(_resolved)
list(APPEND _worklist "${_resolved}")
endif()
continue()
endif()
# iree_cc_library reaches usage requirements through this indirection.
if(_item MATCHES "^\\$<TARGET_PROPERTY:([^,>]+),([A-Za-z_]+)>$")
if(TARGET "${CMAKE_MATCH_1}")
get_target_property(_prop_value "${CMAKE_MATCH_1}" "${CMAKE_MATCH_2}")
if(_prop_value)
list(APPEND _worklist ${_prop_value})
endif()
endif()
continue()
endif()
# The compiler dylib is no MLIR aggregate, so this exclusion list is empty
# and the guard always admits the library.
if(_item MATCHES "^\\$<\\$<NOT:\\$<IN_LIST:.*MLIR_AGGREGATE_EXCLUDE_LIBS.*>:([A-Za-z0-9_]+)>$")
list(APPEND _worklist "${CMAKE_MATCH_1}")
continue()
endif()
# LLVM guards platform link flags this way. The probe is a literal by now,
# so decide it rather than warn; an empty one contributes nothing.
if(_item MATCHES "^\\$<\\$<BOOL:(.*)>:(.*)>$")
set(_bool_cond "${CMAKE_MATCH_1}")
set(_bool_value "${CMAKE_MATCH_2}")
iree_renamed_link_genex_bool("${_bool_cond}" _bool_true)
if(_bool_true)
list(APPEND _worklist "${_bool_value}")
endif()
continue()
endif()
if(_item MATCHES "^\\$<")
# An unevaluated one would corrupt the build files. Dropping it yields an
# undefined symbol instead, which can be diagnosed.
message(WARNING
"iree_collect_static_link_closure: dropping unsupported generator "
"expression link item: ${_item}")
continue()
endif()
if(NOT TARGET "${_item}")
list(APPEND _other "${_item}")
continue()
endif()
get_target_property(_aliased "${_item}" ALIASED_TARGET)
if(_aliased)
list(APPEND _worklist "${_aliased}")
continue()
endif()
get_target_property(_imported "${_item}" IMPORTED)
get_target_property(_type "${_item}" TYPE)
if(_imported)
list(APPEND _other "${_item}")
elseif(_type STREQUAL "STATIC_LIBRARY")
list(APPEND _static_libs "${_item}")
elseif(_type STREQUAL "INTERFACE_LIBRARY" OR _type STREQUAL "OBJECT_LIBRARY")
# Recurse only. Object libraries are the walk's seeds; their objects are
# archived and whole-archive linked by the caller, so linking the
# library target itself would duplicate them unrenamed.
else()
# Shared libraries, executables: pass through.
list(APPEND _other "${_item}")
endif()
foreach(_prop LINK_LIBRARIES INTERFACE_LINK_LIBRARIES)
get_target_property(_deps "${_item}" ${_prop})
if(_deps)
list(APPEND _worklist ${_deps})
endif()
endforeach()
endwhile()
set(${STATIC_LIBS_VAR} "${_static_libs}" PARENT_SCOPE)
set(${OTHER_VAR} "${_other}" PARENT_SCOPE)
endfunction()
# Assembles the renamed libIREECompiler link.
#
# Deferred to the end of the top-level directory so the closure walk sees every
# target: the API directory is configured before most of the compiler tree, so
# aliases like iree::compiler::Codegen::Common do not exist yet.
#
# Reads IREE_RENAMED_SHARED_IMPL_OBJECT_LIBS, set by compiler/src/iree/compiler/API.
function(iree_renamed_shared_impl_finalize)
get_property(_object_libs GLOBAL PROPERTY IREE_RENAMED_SHARED_IMPL_OBJECT_LIBS)
set(_target iree_compiler_API_SharedImpl)
# Whole-archive: these are the exported surface, so nothing references them.
set(_whole_archives)
foreach(_object_lib ${_object_libs})
iree_renamed_link_sanitize_name("${_object_lib}" _base)
iree_renamed_archive_from_objects(_renamed
NAME "${_base}"
TARGET "${_object_lib}"
)
list(APPEND _whole_archives "${_renamed}")
endforeach()
# The closure is renamed too, or the references above find nothing. It keeps
# selective extraction, so the same objects land as in an unrenamed link.
iree_collect_static_link_closure(_closure_static_libs _closure_other
${_object_libs}
MLIRExportSMTLIB
MLIRTargetLLVMIRImport
)
set(_closure_archives)
foreach(_lib ${_closure_static_libs})
iree_renamed_link_sanitize_name("${_lib}" _base)
iree_renamed_archive(_renamed
NAME "closure_${_base}"
INPUT "$<TARGET_FILE:${_lib}>"
DEPENDS "${_lib}"
)
list(APPEND _closure_archives "${_renamed}")
endforeach()
if(APPLE)
set(_whole_archive_flags)
foreach(_archive ${_whole_archives})
list(APPEND _whole_archive_flags "-Wl,-force_load,${_archive}")
endforeach()
# ld64 ignores command-line order.
target_link_options(${_target} PRIVATE ${_whole_archive_flags})
target_link_libraries(${_target} PRIVATE
${_closure_archives}
${_closure_other}
)
else()
# Grouped so single-pass ELF linkers do not care about archive order.
target_link_libraries(${_target} PRIVATE
"-Wl,--whole-archive" ${_whole_archives} "-Wl,--no-whole-archive"
"-Wl,--start-group" ${_closure_archives} "-Wl,--end-group"
${_closure_other}
)
endif()
# Orders archive generation before the link.
add_custom_target(${_target}_renamed_archives
DEPENDS ${_whole_archives} ${_closure_archives}
)
add_dependencies(${_target} ${_target}_renamed_archives)
set_property(TARGET ${_target} APPEND PROPERTY
LINK_DEPENDS ${_whole_archives} ${_closure_archives}
)
endfunction()