Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9eef4c8
Isolate AMD HIP/HSA in mlir_rocm_arch_runtime to fix multi-LLVM crash
umangyadav Apr 21, 2026
5983822
[EXTERNAL] Harden libmlir_rocm_runtime.so against cl::opt collisions
umangyadav Apr 23, 2026
0f49f2a
Collapse mlir_rocm_arch_runtime shim into in-process dlopen
umangyadav Apr 23, 2026
5d0f654
Restore dlmopen(LM_ID_NEWLM) as the HIP loader on glibc
umangyadav Apr 23, 2026
41dc976
Prevent tools from re-exporting LLVM/MLIR statics via --exclude-libs,ALL
umangyadav Apr 23, 2026
99dd270
[EXTERNAL] Drop CXX_VISIBILITY_PRESET=hidden on libmlir_rocm_runtime.so
umangyadav Apr 23, 2026
693fb07
Apply -Wl,-Bsymbolic-functions to every rocMLIR-produced shared library
umangyadav Apr 23, 2026
eaf12bf
[EXTERNAL] Delay-load libamdhip64 from libmlir_rocm_runtime.so via dl…
umangyadav Apr 23, 2026
843034a
[EXTERNAL] Coordinate HIP namespace between RocmSystemDetect and mlir…
umangyadav Apr 23, 2026
d3e4398
Delay-load HIP/HIPRTC in rocmlir-tuning-driver
umangyadav Apr 23, 2026
7dea208
[EXTERNAL] Add RocmDynamicLoader.h: shared HIP/HIPRTC/HSA delay-load …
umangyadav Apr 23, 2026
3007bc8
[EXTERNAL] Use RocmDynamicLoader in upstream HIP runtime wrappers
umangyadav Apr 23, 2026
e72d714
Use RocmDynamicLoader in rocMLIR-side HIP delay-loaders
umangyadav Apr 23, 2026
e61ae4a
[EXTERNAL] Refactor ROCm runtime loader as RocmRuntimeLoader.{h,cpp}
umangyadav Apr 23, 2026
d93d71c
Use renamed RocmRuntimeLoader from rocMLIR consumers
umangyadav Apr 23, 2026
9b5593f
Add regression tests for the ROCm runtime delay-load contract
umangyadav Apr 23, 2026
574668e
[EXTERNAL] Make RocmRuntimeLoader version-agnostic across ROCm releases
umangyadav Apr 23, 2026
176e410
Make ROCm-loader regression tests version-agnostic
umangyadav Apr 23, 2026
92fa5f0
[EXTERNAL] Fix stale RocmDynamicLoader.h references in comments
umangyadav Apr 24, 2026
18f4d68
[EXTERNAL] Document Owned policy as canonical-owner-only
umangyadav Apr 24, 2026
d7ab75d
[EXTERNAL] Use upstream LLVM Windows/POSIX idioms in ROCm runtime loader
umangyadav Apr 24, 2026
68bb69e
[EXTERNAL] Simplify RocmRuntimeLoader.cpp
umangyadav Apr 24, 2026
8195f85
Simplify ROCm-loader cleanliness lit scripts
umangyadav Apr 24, 2026
e18459d
Fix bugs introduced by the simplification round
umangyadav Apr 24, 2026
53c18f5
[EXTERNAL] LLVM coding-standards polish for ROCm runtime loader
umangyadav Apr 24, 2026
0acefc0
Polish ROCm-loader CMake comments and harden cleanliness scripts
umangyadav Apr 24, 2026
df17029
Harden ROCm-loader cleanliness scripts and lit substitution
umangyadav Apr 24, 2026
20e3b56
Fix native-arch parse and per-device cache bugs
umangyadav Apr 24, 2026
057b9d9
Fail fast on missing HIP/HIPRTC in rocmlir-tuning-driver
umangyadav Apr 24, 2026
5ce0a72
[EXTERNAL] Gate MLIRRocmExecutionEngineUtils on MLIR_ENABLE_ROCM_RUNNER
umangyadav Apr 24, 2026
af31833
Add tests for native-arch parser, multi-GPU cache, and HIP-missing path
umangyadav Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,33 @@ if (DEFINED ENV{ADDRESS_SANITIZER})
endif()
### End workaround

# Force every shared library we produce (rocMLIR's own and the embedded
# LLVM/MLIR `libLLVM*.so` / `libMLIR*.so`) to bind its internal function
# calls to its own definitions at link time. Intra-library calls then
# cannot be interposed by whichever libLLVM happens to be loaded first in
# the process.
#
# In practice this is defence-in-depth rather than a complete fix for the
# "two LLVMs, one process" problem. It removes PLT entries for all
# intra-library cl::* function calls, which reduces the attack surface,
# but it does NOT affect data-symbol interposition (vtables, RTTI, the
# process-global cl::SubCommand singleton pointer). Fully fixing the
# mlir-runner / xmir-runner JIT path against ROCm's `libLLVM.so.<MAJOR>`
# still needs either a version script on `libLLVMSupport.so.<MAJOR>git`
# or a repo-wide `CXX_VISIBILITY_PRESET=hidden` on the embedded LLVM;
# those are tracked separately. Applying -Bsymbolic-functions
# unconditionally is harmless (it never relaxes isolation, only
# tightens it) and forward-compatible with both of those follow-ups.
#
# The flag is a no-op on Windows (DLL imports already go through IAT)
# and unsupported on Apple's ld (skipped).
if (NOT WIN32 AND NOT APPLE)
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic-functions")
set(CMAKE_MODULE_LINKER_FLAGS
"${CMAKE_MODULE_LINKER_FLAGS} -Wl,-Bsymbolic-functions")
endif()

# Set up the build for the LLVM/MLIR git-submodule
include(cmake/llvm-project.cmake)

Expand Down
14 changes: 14 additions & 0 deletions cmake/llvm-project.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,20 @@ function(add_rocmlir_tool name)
set(EXCLUDE_FROM_ALL ON) # LLVM functions read this variable, set it paranoidly
endif()
add_mlir_tool(${name} ${exclude_from_all} ${ARGN})

# Prevent symbols from static LLVM/MLIR archives linked into this tool
# from being re-exported to the dynamic symbol table. That matters most
# in static / fat-lib builds (BUILD_FAT_LIBROCKCOMPILER or BUILD_SHARED_LIBS
# OFF) where the tool pulls cl::opt definitions straight from libLLVMSupport.a
# -- without --exclude-libs,ALL the tool would unconditionally re-export
# them and any later-dlopened libLLVM.so.* (pulled in by libamdhip64 /
# libamd_comgr / runner libraries) would unify against them and trip
# "Option '...' already exists!" at static-init time. The flag is also
# a harmless no-op in the shared-lib build. Apple's ld does not accept
# GNU-style --exclude-libs.
if (NOT WIN32 AND NOT APPLE)
target_link_options(${name} PRIVATE "LINKER:--exclude-libs,ALL")
endif()
endfunction()


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//===- RocmRuntimeLoader.h - Lazy ROCm library loading utilities -*- C++-*-===//
//
// Part of the LLVM Project, 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
//
//===----------------------------------------------------------------------===//
//
// Public API for delay-loading ROCm runtime shared libraries (libamdhip64,
// libhiprtc, libhsa-runtime64) from the MLIR ExecutionEngine and from
// downstream consumers.
//
// Consumers do NOT link the ROCm runtime at build time. Doing so would
// transitively pull `libamd_comgr` and ROCm's `libLLVM.so.<major>` into
// the host process, which collides at static-init time with MLIR's own
// embedded LLVM (duplicate `cl::opt` registration aborts the process
// from `_dl_init` with "Option '...' already exists" or with a
// SmallPtrSet "Bucket < End" assertion). Loading these libraries with
// `dlmopen(LM_ID_NEWLM, ...)` on glibc puts them in a private link-map
// namespace where their LLVM cannot interpose ours.
//
// ROCm version compatibility:
//
// This loader is intentionally version-agnostic. It is built once
// and works against any ROCm major version present at runtime --
// ROCm 4.x through any future ROCm release we have not yet seen.
// The selection algorithm prefers the unversioned SONAME (e.g.
// `libamdhip64.so` / `amdhip64.dll`), which is what every standard
// ROCm install ships and what `find_package(hip)` resolves; if that
// alias is absent (some runtime-only deployments), the loader falls
// back to enumerating versioned SONAMEs (`libamdhip64.so.<MAJOR>`
// for descending MAJOR). HIP, HIPRTC and HSA each maintain a stable
// C ABI within a major version, so any HIP MAJOR the user has
// installed is acceptable to MLIR. There is no compile-time floor
// or ceiling on the ROCm version this code supports.
//
// Design choices that govern this API:
//
// - Header is platform-agnostic: no `<windows.h>`, no `<dlfcn.h>`, no
// `_GNU_SOURCE` define. All platform-specific machinery lives in
// `RocmRuntimeLoader.cpp`. Downstream `add_mlir_library` users can
// include this header without inheriting Windows-macro pollution
// (`min`, `max`, `ERROR`, ...) or feature-test-macro surprises.
//
// - `LoadedLibrary` is an opaque struct rather than a `void *` typedef
// so a future change can carry extra state (search path used, debug
// info, ...) without breaking callers.
//
// - Cross-process coordination: `RocmSystemDetect` exports
// `mlirRocmSystemDetectGetHipHandle` (declared in
// `RocmSystemDetect.h`) so subsequent loaders share its HIP handle
// and the process keeps a single HSA session. KFD enforces one
// session per process; an independent second `dlmopen` would
// otherwise return `hipErrorNoDevice` from every call.
//
// - HIPRTC and HSA load into HIP's link-map namespace via the
// `relatedHandle` parameter so they share HIP's KFD session even
// when HIP itself was loaded into a non-default namespace.
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_EXECUTIONENGINE_ROCMRUNTIMELOADER_H
#define MLIR_EXECUTIONENGINE_ROCMRUNTIMELOADER_H

namespace mlir::rocm_loader {

/// Identifies which ROCm shared library to delay-load. The enumerator
/// order is internal and may change; do not rely on it.
enum class Library {
Hip,
Hiprtc,
Hsa,
};

/// Opaque handle returned by `loadRocmLibrary`. `handle == nullptr`
/// indicates load failure; callers must treat that as "runtime
/// unavailable" and degrade gracefully.
struct LoadedLibrary {
void *handle = nullptr;
};

/// How `loadRocmLibrary` should coordinate with other loaders that
/// might already have opened the requested library in this process.
enum class CoordinationPolicy {
/// Default. For `Library::Hip`, attempt to reuse the HIP handle
/// owned by `RocmSystemDetect` (looked up via `RTLD_DEFAULT`); for
/// every other library this is equivalent to `Owned`. This is the
/// policy downstream consumers should use.
Auto,

/// Skip the shared-handle lookup. The caller is the canonical
/// owner. Reserved for `RocmSystemDetect.cpp` to break recursion at
/// first load.
///
/// IMPORTANT: do not use `Owned` from elsewhere. KFD permits only
/// one HSA session per process; on glibc each `Owned` call performs
/// a fresh `dlmopen(LM_ID_NEWLM, ...)` and thus opens HIP into a
/// new namespace. A second `Owned` invocation in the same process
/// will succeed at the `dlmopen` level but every subsequent HIP
/// call (`hipGetDeviceCount` etc.) returns `hipErrorNoDevice`. Use
/// `Auto` from non-canonical callers so they receive the shared
/// handle that `RocmSystemDetect` already holds.
Owned,
};

/// Load `lib` into a private link-map namespace and return an opaque
/// handle. On glibc this uses `dlmopen(LM_ID_NEWLM, ...)`; on other
/// POSIX platforms `dlopen(RTLD_LAZY | RTLD_LOCAL)`; on Windows
/// `LoadLibraryW` with UTF-8 -> UTF-16 conversion of the SONAME.
///
/// When `relatedHandle` is non-null, the new library is opened in the
/// same link-map namespace as `relatedHandle` (glibc only; falls back
/// to the default namespace elsewhere). This is how HIPRTC and HSA
/// share HIP's KFD session.
///
/// Returns a `LoadedLibrary` whose `handle` is null on failure.
/// Failures are non-fatal: this function never aborts the process.
LoadedLibrary
loadRocmLibrary(Library lib, void *relatedHandle = nullptr,
CoordinationPolicy policy = CoordinationPolicy::Auto);

/// Resolve `name` in a previously-loaded library. Returns `nullptr` if
/// the library failed to load or if the symbol is absent. Callers
/// should treat `nullptr` as a soft error and disable the corresponding
/// feature.
void *resolveRocmSymbol(const LoadedLibrary &lib, const char *name);

} // namespace mlir::rocm_loader

#endif // MLIR_EXECUTIONENGINE_ROCMRUNTIMELOADER_H
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,37 @@ class RocmSystemDetect : public std::vector<SystemDevice> {

} // namespace mlir

extern "C" {

/// Returns the opaque HIP runtime handle owned by `RocmSystemDetect`,
/// or `nullptr` if HIP could not be loaded (or this binary did not
/// link `MLIRRocmExecutionEngineUtils`).
///
/// `RocmSystemDetect` is the canonical owner of the per-process HIP
/// handle. When it loads `libamdhip64`, it uses
/// `dlmopen(LM_ID_NEWLM, ...)` (glibc) to put HIP and its transitive
/// dependencies (libamd_comgr, ROCm's libLLVM) in a private link-map
/// namespace. KFD enforces one HSA session per process; if a second
/// loader (for example mlir-runner's `libmlir_rocm_runtime.so`) opens
/// HIP into a *different* namespace, that second instance receives
/// `hipErrorNoDevice` from every call. To avoid that, all subsequent
/// HIP loaders look up this symbol via `RTLD_DEFAULT` and reuse the
/// returned handle. The recommended way to do that is to call
/// `mlir::rocm_loader::loadRocmLibrary(Library::Hip)` (defined in
/// `mlir/ExecutionEngine/RocmRuntimeLoader.h`), which performs the
/// lookup transparently.
///
/// The function is `extern "C"` and uses an opaque `void *` so it can
/// be safely dlsym-ed from a TU that does not include this header
/// (notably from `RocmRuntimeLoader.cpp` itself, which avoids a
/// link-time dependency on `MLIRRocmExecutionEngineUtils`).
///
/// Visibility: the symbol is published with `LLVM_ALWAYS_EXPORT`
/// (`__declspec(dllexport)` on Windows, default visibility on POSIX)
/// so it lands in the host process's dynamic symbol table for
/// `RTLD_DEFAULT` lookup.
void *mlirRocmSystemDetectGetHipHandle();

} // extern "C"

#endif // MLIR_EXECUTIONENGINE_ROCMSYSTEMDETECT_H_
Loading