This is an automated email from the ASF dual-hosted git repository. tqchen pushed a commit to branch static-libxml2 in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 05506afc7f22518b91343178a83f2b5169f3b976 Author: tqchen <[email protected]> AuthorDate: Mon Jun 1 15:25:08 2026 +0000 [BUILD] Static-libxml2 + LLVM static build cross-platform support Three changes that together let TVM build with statically linked LLVM (no libxml2 runtime dep) on both Linux and Windows: 1. USE_STATIC_LIBXML2_FROM_SOURCE option (default OFF) + new file cmake/utils/CompileStaticLibXml2.cmake. When enabled, FetchContent pulls libxml2 v2.15.0 from GNOME/libxml2 with all optional features OFF (programs, tests, threading, output, xpath, etc.), builds it static, and substitutes it for whatever libxml2 the LLVM static link line would have brought in. Gated to only activate when USE_LLVM contains --link-static (FATAL_ERROR otherwise). 2. -Wl,--no-relax workaround in CMakeLists.txt for the GNU ld bug ld/25754: when LLVM static archives are linked into the large libtvm_compiler.so, ld can incorrectly relax an addr32- prefixed indirect GOT call into a direct call with the wrong displacement, jumping into read-only data and crashing inside llvm::X86Subtarget. Gated to Linux + USE_LLVM ON. 3. .github/workflows/main.yml — flip the existing Windows job's CMAKE_ARGS to USE_LLVM=llvm-config --link-static plus USE_STATIC_LIBXML2_FROM_SOURCE=ON, and add static-libxml2 to the push trigger so pushing the branch to apache/tvm exercises CI without needing a PR. Verified on Linux: mamba LLVM 22.1.x static and system LLVM 15 static both build clean, libtvm_compiler.so has no libxml2 in ldd, tvm.build('llvm') smoke test passes. --- .github/workflows/main.yml | 3 +- CMakeLists.txt | 14 +++++++ cmake/config.cmake | 9 +++++ cmake/modules/LLVM.cmake | 19 ++++++++++ cmake/utils/CompileStaticLibXml2.cmake | 68 ++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a30bbc3421..960ff16aea 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,6 +25,7 @@ on: push: branches: - main + - static-libxml2 pull_request: branches: - main @@ -92,7 +93,7 @@ jobs: shell: cmd /C call {0} run: | pip install scikit-build-core - set CMAKE_ARGS=-DUSE_LLVM=ON -DBUILD_TESTING=OFF + set CMAKE_ARGS=-DUSE_LLVM="llvm-config --link-static" -DUSE_STATIC_LIBXML2_FROM_SOURCE=ON -DBUILD_TESTING=OFF pip install --no-deps . -v - name: Install test dependencies shell: cmd /C call {0} diff --git a/CMakeLists.txt b/CMakeLists.txt index a11a872970..a4b70d5b0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -670,6 +670,20 @@ if (HIDE_PRIVATE_SYMBOLS AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") target_link_libraries(tvm_runtime PRIVATE ${HIDE_SYMBOLS_LINKER_FLAGS}) endif() +# Work around a GNU ld (binutils) relaxation bug that miscompiles +# R_X86_64_GOTPCRELX relocations inside very large statically-linked archives. +# When the full LLVM static libraries are linked into libtvm_compiler.so, the +# library is large enough that ld can relax an indirect GOT call (LLVM built +# with -fno-plt emits these) into a direct call with an incorrect displacement. +# The call then targets read-only data instead of the intended function and +# crashes at runtime with a SIGSEGV inside llvm::X86Subtarget during code +# generation. Disabling linker relaxation keeps the GOT-indirect sequences and +# avoids the miscompilation; it is harmless when LLVM is linked dynamically. +# See binutils bug ld/25754. +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT ${USE_LLVM} MATCHES ${IS_FALSE_PATTERN}) + set_property(TARGET tvm_compiler APPEND PROPERTY LINK_OPTIONS "-Wl,--no-relax") +endif() + # Create the `cpptest` target if we can find GTest. If not, we create dummy # targets that give the user an informative error message. if(GTEST_FOUND) diff --git a/cmake/config.cmake b/cmake/config.cmake index dfbe0d2178..0c4097a12f 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -135,6 +135,15 @@ set(USE_LLVM OFF) # Possible values: ON/OFF set(USE_MLIR OFF) +# Whether to fetch and statically link libxml2 in-tree instead of +# depending on the system libxml2. Only valid when USE_LLVM is +# configured with `--link-static`. When ON, FetchContent downloads +# GNOME/libxml2 at v2.15.0 with all optional features OFF and links +# the in-tree static archive into libtvm.so, replacing any -lxml2 +# that llvm-config --link-static emits. +# Default OFF (preserves existing behavior). +set(USE_STATIC_LIBXML2_FROM_SOURCE OFF) + #--------------------------------------------- # Contrib libraries #--------------------------------------------- diff --git a/cmake/modules/LLVM.cmake b/cmake/modules/LLVM.cmake index f944b41304..e3c0e25145 100644 --- a/cmake/modules/LLVM.cmake +++ b/cmake/modules/LLVM.cmake @@ -25,6 +25,14 @@ add_definitions(-DNDEBUG=1) # TODO(@jroesch, @tkonolige): if we actually use targets we can do this. # target_compile_definitions(tvm_compiler PRIVATE NDEBUG=1) +# Precondition: USE_STATIC_LIBXML2_FROM_SOURCE=ON is only supported with a static LLVM +# link (i.e. USE_LLVM must contain "--link-static"). Catch the mismatch early. +if(USE_STATIC_LIBXML2_FROM_SOURCE AND NOT (USE_LLVM MATCHES "--link-static")) + message(FATAL_ERROR + "USE_STATIC_LIBXML2_FROM_SOURCE=ON requires USE_LLVM to use --link-static " + "(e.g. set(USE_LLVM \"llvm-config --link-static\"))") +endif() + # Test if ${USE_LLVM} is not an explicit boolean false # It may be a boolean or a string if(NOT ${USE_LLVM} MATCHES ${IS_FALSE_PATTERN}) @@ -48,6 +56,17 @@ if(NOT ${USE_LLVM} MATCHES ${IS_FALSE_PATTERN}) src/target/rocm/llvm/*.cc src/target/hexagon/llvm/*.cc ) + + # When USE_STATIC_LIBXML2_FROM_SOURCE=ON, we will instead link against + # in-tree compiled libxml2 and remove the xml2 deps from existing llvm-config. + if(USE_STATIC_LIBXML2_FROM_SOURCE) + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/utils/CompileStaticLibXml2.cmake) + _libxml2compile() + list(FILTER LLVM_LIBS EXCLUDE REGEX "[Xx][Mm][Ll]2") + list(APPEND LLVM_LIBS LibXml2) + message(STATUS "USE_STATIC_LIBXML2_FROM_SOURCE=ON: replaced system xml2 with in-tree LibXml2") + endif() + list(APPEND TVM_LINKER_LIBS ${LLVM_LIBS}) list(APPEND COMPILER_SRCS ${COMPILER_LLVM_SRCS}) if(NOT MSVC) diff --git a/cmake/utils/CompileStaticLibXml2.cmake b/cmake/utils/CompileStaticLibXml2.cmake new file mode 100644 index 0000000000..f038b0d21b --- /dev/null +++ b/cmake/utils/CompileStaticLibXml2.cmake @@ -0,0 +1,68 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +include(FetchContent) + +# Function: _libxml2compile — fetch libxml2 (no submodule) and produce a +# static LibXml2 target with all optional deps disabled. Used when +# USE_STATIC_LIBXML2_FROM_SOURCE=ON to avoid depending on a system libxml2. +function(_libxml2compile) + FetchContent_Declare( + libxml2 + GIT_REPOSITORY https://github.com/GNOME/libxml2.git + GIT_TAG v2.15.0 + ) + + # Static build, all optional features OFF. + # Anything not listed here keeps upstream default (typically cascade-OFF + # via libxml2's own cmake_dependent_option logic). See task file for the + # full cross-check. + set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "") + set(LIBXML2_WITH_PROGRAMS OFF CACHE INTERNAL "") + set(LIBXML2_WITH_TESTS OFF CACHE INTERNAL "") + set(LIBXML2_WITH_PYTHON OFF CACHE INTERNAL "") + set(LIBXML2_WITH_DOCS OFF CACHE INTERNAL "") + set(LIBXML2_WITH_ICU OFF CACHE INTERNAL "") + set(LIBXML2_WITH_ICONV OFF CACHE INTERNAL "") + set(LIBXML2_WITH_LEGACY OFF CACHE INTERNAL "") + set(LIBXML2_WITH_ZLIB OFF CACHE INTERNAL "") + set(LIBXML2_WITH_READLINE OFF CACHE INTERNAL "") + set(LIBXML2_WITH_MODULES OFF CACHE INTERNAL "") + set(LIBXML2_WITH_HTTP OFF CACHE INTERNAL "") + set(LIBXML2_WITH_HTML OFF CACHE INTERNAL "") + set(LIBXML2_WITH_CATALOG OFF CACHE INTERNAL "") + set(LIBXML2_WITH_DEBUG OFF CACHE INTERNAL "") + set(LIBXML2_WITH_XINCLUDE OFF CACHE INTERNAL "") + set(LIBXML2_WITH_XPATH OFF CACHE INTERNAL "") + set(LIBXML2_WITH_VALID OFF CACHE INTERNAL "") + set(LIBXML2_WITH_REGEXPS OFF CACHE INTERNAL "") + set(LIBXML2_WITH_PATTERN OFF CACHE INTERNAL "") + set(LIBXML2_WITH_SAX1 OFF CACHE INTERNAL "") + set(LIBXML2_WITH_OUTPUT OFF CACHE INTERNAL "") + set(LIBXML2_WITH_PUSH OFF CACHE INTERNAL "") + set(LIBXML2_WITH_ISO8859X OFF CACHE INTERNAL "") + set(LIBXML2_WITH_TLS OFF CACHE INTERNAL "") + set(LIBXML2_WITH_THREAD_ALLOC OFF CACHE INTERNAL "") + set(LIBXML2_WITH_THREADS OFF CACHE INTERNAL "") # TVM never calls into libxml2 + + FetchContent_MakeAvailable(libxml2) + + # Windows requires LIBXML_STATIC defined on consumers of the static lib. + if(WIN32) + target_compile_definitions(LibXml2 INTERFACE LIBXML_STATIC) + endif() +endfunction()
