diff --git a/.gitmodules b/.gitmodules index 1e0db2c57a28..adebdefafd96 100644 --- a/.gitmodules +++ b/.gitmodules @@ -44,3 +44,6 @@ [submodule "third_party/torch-mlir"] path = third_party/torch-mlir url = https://github.com/shark-infra/torch-mlir.git +[submodule "third_party/ROCm-Device-Libs"] + path = third_party/ROCm-Device-Libs + url = https://github.com/shark-infra/ROCm-Device-Libs diff --git a/CMakeLists.txt b/CMakeLists.txt index b5fafcf17e2a..f871771f44af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -812,6 +812,9 @@ else() # Add default external projects. iree_llvm_add_external_project(mlir-iree-dialects ${CMAKE_CURRENT_SOURCE_DIR}/llvm-external-projects/iree-dialects) iree_llvm_add_external_project(stablehlo ${CMAKE_CURRENT_SOURCE_DIR}/third_party/stablehlo) + if(IREE_TARGET_BACKEND_ROCM) + iree_llvm_add_external_project(ROCm-Device-Libs ${CMAKE_CURRENT_SOURCE_DIR}/third_party/ROCm-Device-Libs) + endif() # Ensure that LLVM-based dependencies needed for testing are included. add_dependencies(iree-test-deps FileCheck) diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index a80004a6aff9..4ac14b30f71c 100644 --- a/compiler/CMakeLists.txt +++ b/compiler/CMakeLists.txt @@ -4,6 +4,23 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# On Windows, DLLs go to the runtime directory and import +# libraries go to the lib directory. +# TODO: We should really be dumping binaries into bin/ not +# tools/. This must line up with binaries built this way because +# DLLs must be in the same directory as the binary. +# See: https://github.com/openxla/iree/issues/11297 +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/tools") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") +if(WIN32) + set(IREE_COMPILER_DYLIB_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") + set(IREE_COMPILER_DYLIB_INSTALL_PREFIX "bin/") +else() + set(IREE_COMPILER_DYLIB_DIR "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") + set(IREE_COMPILER_DYLIB_INSTALL_PREFIX "lib/") +endif() + # Always build the C bindings, since the API is available apart from # actually building the compiler. add_subdirectory(bindings/c) @@ -22,4 +39,11 @@ if(IREE_BUILD_COMPILER) configure_file(setup.py setup.py @ONLY) add_subdirectory(bindings/python) endif() + + # Post processing. + get_property(_iree_compiler_dylib_depends GLOBAL PROPERTY IREE_COMPILER_DYLIB_DEPENDS) + if(_iree_compiler_dylib_depends) + add_dependencies(iree_compiler_API_SharedImpl ${_iree_compiler_dylib_depends}) + endif() endif() + diff --git a/compiler/bindings/python/CMakeLists.txt b/compiler/bindings/python/CMakeLists.txt index ea89d0bedb68..314fe6fc34d7 100644 --- a/compiler/bindings/python/CMakeLists.txt +++ b/compiler/bindings/python/CMakeLists.txt @@ -214,42 +214,56 @@ if(TARGET lld) ) endif() -# Install shared libraries that the extension depends on. This uses -# CMake's defer feature to evaluate the install directive once everything -# has been evaluated (because there is no guarantee that this file evaluates -# before the API libraries are defined). While deferred calls are generally -# fragile, this install is purely a leaf feature (with no other calls -# depending on its sequencing), so this use is okay. -# We defer it to the compiler/ directory so that cmake_install.cmake targets -# are all self contained to the compiler, which is the most common spanning -# parent. -cmake_language(EVAL CODE " -cmake_language(DEFER DIRECTORY \"${IREE_SOURCE_DIR}/compiler\" - CALL install - TARGETS - iree_compiler_API_SharedImpl - DESTINATION \"${_PYTHON_INSTALL_PREFIX}/iree/compiler/_mlir_libs\" -) -") - -# On Windows, the IREECompiler.dll must be physically adjacent to the python -# extensions. This is only an issue for the build tree (the install tree -# places things appropriately). There is a lot of unavoidable fragility -# here. Most notably, the actual name of the DLL must be static and computed -# the same as where it is built. -if(WIN32) - set(_local_dll_copy - "${_PYTHON_BUILD_PREFIX}/iree/compiler/_mlir_libs/IREECompiler.dll") - add_custom_command( - OUTPUT "${_local_dll_copy}" - COMMAND "${CMAKE_COMMAND}" -E copy_if_different - "$" - "${_local_dll_copy}" - DEPENDS iree_compiler_API_SharedImpl +################################################################################ +# libIREECompiler.so dylib tree +# We copy the compiler shared library and its related files to both the build +# and install tree for consistency. Note that on Windows, this is load bearing +# since the DLL must be colocated in the same directory. On Unix, CMake's +# automatic RPATH handling in the build tree will cause binaries to resolve +# against the project level library, so this is just cosmetic, but it does +# have the side effect of making the Python build tree relocatable and making +# logic that expects to find the compiler libraries in a consistent place +# simple. Since a symlink is used on Unix and it is not the versioned dylib, +# the realpath will still be the build-tree wide shared library. This also means +# that any tools or files that must be colocated with the dylib will come from +# the project-wide location. +################################################################################ + +# Copy compiler dylib files into the python _mlir_libs tree so that the +# binaries can find them with a relative rpath. +get_property(_dylib_relpaths GLOBAL PROPERTY IREE_COMPILER_DYLIB_RELPATHS) +set(_dylib_copy_commands) +set(_dylib_target_files) +set(_dylib_src_files) +foreach (_dylib_relpath ${_dylib_relpaths}) + set(_dylib_srcpath "${IREE_COMPILER_DYLIB_DIR}/${_dylib_relpath}") + set(_dylib_outputpath "${_PYTHON_BUILD_PREFIX}/iree/compiler/_mlir_libs/${_dylib_relpath}") + cmake_path(GET _dylib_outputpath PARENT_PATH _parent_dir) + file(MAKE_DIRECTORY "${_parent_dir}") + list(APPEND _dylib_copy_commands + COMMAND ${CMAKE_COMMAND} -E create_symlink + "${_dylib_srcpath}" "${_dylib_outputpath}" ) - add_custom_target(IREEPythonCopyDLL ALL - DEPENDS "${_local_dll_copy}") -endif() + list(APPEND _dylib_target_files "${_dylib_outputpath}") + list(APPEND _dylib_src_files "${_dylib_srcpath}") + cmake_path(GET _dylib_relpath PARENT_PATH _dylib_install_destination) + install(FILES "${_dylib_srcpath}" + DESTINATION "${_PYTHON_INSTALL_PREFIX}/iree/compiler/_mlir_libs/${_dylib_install_destination}" + ) +endforeach() + +add_custom_command( + OUTPUT ${_dylib_target_files} + DEPENDS ${_dylib_src_files} + POST_BUILD + ${_dylib_copy_commands} +) + +add_custom_target(IREECompilerPythonDylibFiles + DEPENDS ${_dylib_target_files} +) + +add_dependencies(IREECompilerPythonModules IREECompilerPythonDylibFiles) ################################################################################ # Subdirectories diff --git a/compiler/src/iree/compiler/API/CMakeLists.txt b/compiler/src/iree/compiler/API/CMakeLists.txt index 6c4b976fb0ad..3ae0f6178ea5 100644 --- a/compiler/src/iree/compiler/API/CMakeLists.txt +++ b/compiler/src/iree/compiler/API/CMakeLists.txt @@ -139,12 +139,16 @@ foreach(_object_lib ${_EXPORT_OBJECT_LIBS}) # another level of fix upstream if you like pain. list(APPEND _EXPORT_OBJECT_DEPS "$>>>>") endforeach() -if(NOT GENERATOR_IS_MULTI_CONFIG) - # NOTE: multi-config generators (xcode, visual studio, ninja in multi-config - # mode, etc) don't support writing files with fixed names with contents that - # differ based on the available variants. - file(GENERATE OUTPUT export_objects_debug.txt CONTENT "OBJECTS:${_EXPORT_OBJECT_SRCS}\n\nDEPS:${_EXPORT_OBJECT_DEPS}") -endif() + +# UNCOMMENT TO DEBUG WHAT IS GOING ON. +# file(GENERATE OUTPUT export_objects_debug.txt CONTENT "OBJECTS:${_EXPORT_OBJECT_SRCS}\n\nDEPS:${_EXPORT_OBJECT_DEPS}") + +# Disable .so.0 style naming/linking. In order to be consistent across platforms +# and bindings, we will embed a major version in the library name when it is time. +# OS packagers can re-enable this or take other steps to get canonical shared +# library structures. +set(CMAKE_PLATFORM_NO_VERSIONED_SONAME ON) + iree_cc_library( SHARED NAME @@ -195,18 +199,18 @@ set_target_properties( OUTPUT_NAME "IREECompiler" VERSION "0" SOVERSION "0" - - LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" - # On Windows, DLLs go to the runtime directory and import - # libraries go to the lib directory. - # TODO: We should really be dumping binaries into bin/ not - # tools/. This must line up with binaries built this way because - # DLLs must be in the same directory as the binary. - # See: https://github.com/openxla/iree/issues/11297 - RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/tools" - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" ) +# Signal the main shared library/dll as part of the bundle of files that +# must be included with compiler dylibs. +# What we really want to be using here is the TARGET_LINKER_FILE_NAME +# generator expression. But since this is used in places where target +# dependent generator expressions are not legal, we guesstimate it with +# platform specific logic. +set(_target_linker_file_name "${CMAKE_SHARED_LIBRARY_PREFIX}IREECompiler${CMAKE_SHARED_LIBRARY_SUFFIX}") +set_property(GLOBAL APPEND PROPERTY + IREE_COMPILER_DYLIB_RELPATHS "${_target_linker_file_name}") + install( TARGETS iree_compiler_API_SharedImpl COMPONENT Compiler diff --git a/compiler/src/iree/compiler/Dialect/HAL/Target/ROCM/CMakeLists.txt b/compiler/src/iree/compiler/Dialect/HAL/Target/ROCM/CMakeLists.txt index 8c6323942607..23a39e760de3 100644 --- a/compiler/src/iree/compiler/Dialect/HAL/Target/ROCM/CMakeLists.txt +++ b/compiler/src/iree/compiler/Dialect/HAL/Target/ROCM/CMakeLists.txt @@ -48,3 +48,54 @@ iree_cc_library( ) ### BAZEL_TO_CMAKE_PRESERVES_ALL_CONTENT_BELOW_THIS_LINE ### + +# Query for list of device libs to build. +get_property(AMD_DEVICE_LIBS GLOBAL PROPERTY AMD_DEVICE_LIBS) + +set(_platform_lib_reldir "iree_platform_libs/rocm") +file(MAKE_DIRECTORY "${IREE_COMPILER_DYLIB_DIR}/${_platform_lib_reldir}") + +# Transform device lib targets to the generated bc file of each. +set(_all_device_bc_deps) +set(_all_device_bc_copy_commands) +set(_all_device_bc_files) +foreach (_device_lib_target ${AMD_DEVICE_LIBS}) + get_target_property(_device_basename ${_device_lib_target} ARCHIVE_OUTPUT_NAME) + get_target_property(_device_output_path ${_device_lib_target} OUTPUT_NAME) + set(_device_bc_relpath "${_platform_lib_reldir}/${_device_basename}.bc") + + # Note this bc file as being part of the bundle that must be included with + # the compiler dylib. + set_property(GLOBAL APPEND PROPERTY IREE_COMPILER_DYLIB_RELPATHS "${_device_bc_relpath}") + + list(APPEND _all_device_bc_deps "${_device_bc_path}") + list(APPEND _all_device_bc_files "${IREE_COMPILER_DYLIB_DIR}/${_device_bc_relpath}") + + # Copy to lib/ tree. + list(APPEND _all_device_bc_copy_commands + COMMAND ${CMAKE_COMMAND} -E copy + "${_device_output_path}" + "${IREE_COMPILER_DYLIB_DIR}/${_device_bc_relpath}" + ) +endforeach() + +# Generate a custom target with all file level dependencies and commands to +# copy to our build tree locations. +# Our GenDeviceLibs target depends on all of the defined device lib targets. +add_custom_command( + OUTPUT ${_all_device_bc_files} + DEPENDS ${_all_device_bc_deps} + POST_BUILD + ${_all_device_bc_copy_commands} +) +add_custom_target(iree_compiler_Dialect_HAL_Target_ROCM_GenDeviceLibs + DEPENDS ${_all_device_bc_files} +) + +# Ensure that the device libs are built when the compiler dylib is built. +set_property(GLOBAL APPEND PROPERTY IREE_COMPILER_DYLIB_DEPENDS + iree_compiler_Dialect_HAL_Target_ROCM_GenDeviceLibs) + +# Install. +install(FILES ${_all_device_bc_files} + DESTINATION "${IREE_COMPILER_DYLIB_INSTALL_PREFIX}/${_platform_lib_reldir}") diff --git a/compiler/src/iree/compiler/Dialect/HAL/Target/ROCM/ROCMTarget.cpp b/compiler/src/iree/compiler/Dialect/HAL/Target/ROCM/ROCMTarget.cpp index a85dc1bdd38a..be04a4257412 100644 --- a/compiler/src/iree/compiler/Dialect/HAL/Target/ROCM/ROCMTarget.cpp +++ b/compiler/src/iree/compiler/Dialect/HAL/Target/ROCM/ROCMTarget.cpp @@ -12,11 +12,14 @@ #include "iree/compiler/Codegen/LLVMGPU/Passes.h" #include "iree/compiler/Dialect/HAL/Target/TargetRegistry.h" #include "iree/compiler/Utils/FlatbufferUtils.h" +#include "iree/compiler/Utils/ToolUtils.h" #include "iree/schemas/rocm_executable_def_builder.h" #include "llvm/IR/Constants.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Target/TargetMachine.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" @@ -37,10 +40,9 @@ static llvm::cl::opt llvm::cl::desc("Whether to try Linking to AMD Bitcodes"), llvm::cl::init(false)); -static llvm::cl::opt - clROCMBitcodeDir("iree-rocm-bc-dir", - llvm::cl::desc("Directory of ROCM Bitcode"), - llvm::cl::init("/opt/rocm/amdgcn/bitcode")); +static llvm::cl::opt clROCMBitcodeDir( + "iree-rocm-bc-dir", llvm::cl::desc("Directory of ROCM Bitcode"), + llvm::cl::init(mlir::iree_compiler::findPlatformLibDirectory("rocm"))); namespace mlir { namespace iree_compiler { @@ -205,9 +207,15 @@ class ROCMTargetBackend final : public TargetBackend { iree_hal_rocm_ExecutableDef_start_as_root(builder); // Link module to Device Library + std::string rocmBitcodeDir = clROCMBitcodeDir; if (clROCMLinkBC) { - linkROCDLIfNecessary(llvmModule.get(), clROCMTargetChip, - clROCMBitcodeDir); + if (clROCMBitcodeDir.empty()) { + return variantOp.emitError() + << "cannot find ROCM bitcode files. Check your installation " + "consistency and in the worst case, set --iree-rocm-bc-dir= " + "to an explicit location on your system."; + } + linkROCDLIfNecessary(llvmModule.get(), clROCMTargetChip, rocmBitcodeDir); } // Serialize hsaco kernel into the binary that we will embed in the diff --git a/compiler/src/iree/compiler/Utils/ToolUtils.cpp b/compiler/src/iree/compiler/Utils/ToolUtils.cpp index 0d8141ec7a44..d717b776ce11 100644 --- a/compiler/src/iree/compiler/Utils/ToolUtils.cpp +++ b/compiler/src/iree/compiler/Utils/ToolUtils.cpp @@ -262,5 +262,22 @@ std::string findTool(std::string toolName) { return findTool(toolNames); } +std::string findPlatformLibDirectory(StringRef platformName) { + std::string dylibPath = getCurrentDylibPath(); + if (dylibPath.empty()) + return {}; + + SmallString<256> path(dylibPath); + llvm::sys::path::remove_filename(path); + llvm::sys::path::append(path, "iree_platform_libs", platformName); + if (!llvm::sys::fs::is_directory(path)) + return {}; + llvm::sys::fs::make_absolute(path); + (void)llvm::sys::path::remove_dots(path, /*remove_dot_dot=*/true); + + std::string pathStr(path); + return pathStr; +} + } // namespace iree_compiler } // namespace mlir diff --git a/compiler/src/iree/compiler/Utils/ToolUtils.h b/compiler/src/iree/compiler/Utils/ToolUtils.h index e6239428d1df..edf40fb99fb1 100644 --- a/compiler/src/iree/compiler/Utils/ToolUtils.h +++ b/compiler/src/iree/compiler/Utils/ToolUtils.h @@ -40,6 +40,20 @@ std::string findToolInEnvironment(SmallVector toolNames); std::string findTool(SmallVector toolNames); std::string findTool(std::string toolName); +// Finds a bundled directory containing platform libraries for the given +// platform name, returning an empty string if not found. We store bundled +// platform libraries in a directory like: +// iree_platform_libs/{platformName} +// adjacent to the shared library hosting the compiler (i.e. this entry +// point). On a Posix system, this will typically be in a "lib" dir and +// on Windows, it will be adjacent to executables (i.e. a "bin" or "tools" +// dir). Note that if installed to a system library directory on a Posix +// system, this would be under something like: +// /usr/lib/iree_platform_libs/{platformName} +// This is not atypical to how other dependencies are located in a qualified +// lib directory. +std::string findPlatformLibDirectory(StringRef platformName); + } // namespace iree_compiler } // namespace mlir diff --git a/third_party/ROCm-Device-Libs b/third_party/ROCm-Device-Libs new file mode 160000 index 000000000000..3adc16d48e2a --- /dev/null +++ b/third_party/ROCm-Device-Libs @@ -0,0 +1 @@ +Subproject commit 3adc16d48e2ac4536e7ea62c7c147c1c91691b20