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 609ab46c8f38459b93e6a8fba60e596320a84f01 Author: tqchen <[email protected]> AuthorDate: Mon Jun 1 15:11:59 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 (no 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 — enable the static LLVM build with USE_STATIC_LIBXML2_FROM_SOURCE on Windows CI so this branch exercises the cross-platform story. 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 | 7 ++++ CMakeLists.txt | 14 +++++++ cmake/config.cmake | 9 +++++ cmake/modules/LLVM.cmake | 22 +++++++++++ cmake/utils/CompileStaticLibXml2.cmake | 68 ++++++++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a30bbc3421..5fbdce7978 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -88,6 +88,13 @@ jobs: shell: cmd /C call {0} run: | conda install -c conda-forge llvmdev cmake ninja zlib libxml2-devel + - name: Build TVM (static LLVM + static libxml2) + shell: cmd /C call {0} + run: | + mkdir build + cd build + cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DUSE_LLVM="llvm-config --link-static" -DUSE_STATIC_LIBXML2_FROM_SOURCE=ON -DUSE_CUDA=OFF -DUSE_LIBBACKTRACE=OFF -DBUILD_TESTING=OFF + ninja tvm tvm_runtime - name: Install TVM shell: cmd /C call {0} run: | 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..adeaa420bb 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,20 @@ 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, fetch libxml2 in-tree (no submodule) and + # replace any system libxml2 reference that llvm-config emitted with the + # in-tree static target. This keeps libtvm.so free of a runtime libxml2 dep. + if(USE_STATIC_LIBXML2_FROM_SOURCE) + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/utils/CompileStaticLibXml2.cmake) + _libxml2compile() + # Drop any -lxml2 / *libxml2* / *xml2* entries llvm-config injected. + list(FILTER LLVM_LIBS EXCLUDE REGEX "[Xx][Mm][Ll]2") + # Link the in-tree static target instead. + 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()
