diff --git a/.github/workflows/rocm-artifact-hashes.yml b/.github/workflows/rocm-artifact-hashes.yml new file mode 100644 index 000000000..f7ed92949 --- /dev/null +++ b/.github/workflows/rocm-artifact-hashes.yml @@ -0,0 +1,46 @@ +name: ROCm artifact hashes + +on: + workflow_dispatch: + +jobs: + hash-rocm-artifacts: + runs-on: ubuntu-latest + steps: + - uses: julia-actions/setup-julia@v2 + with: + version: "1.12" + - name: Download tarballs and compute hashes + shell: julia {0} + run: | + import Pkg + Pkg.add("ArtifactUtils") + using ArtifactUtils, Base.BinaryPlatforms + + for rocm_arch in [ + "gfx908", + "gfx90a", + "gfx94X-dcgpu", + "gfx950-dcgpu", + "gfx101X-dgpu", + "gfx103X-all", + "gfx110X-all", + "gfx1150", + "gfx1151", + "gfx1152", + "gfx120X-all", + ] + @info "Linux, $rocm_arch" + platform = Platform("x86_64", "linux", Dict("libc" => "glibc", "rocm_arch" => replace(rocm_arch, "-" => "_"))) + add_artifact!("Artifacts.toml", "ROCm", "https://repo.amd.com/rocm/tarball/therock-dist-linux-$rocm_arch-7.13.0.tar.gz"; lazy = true, platform) + + startswith(rocm_arch, "gfx9") && continue + @info "Windows, $rocm_arch" + platform = Platform("x86_64", "windows", Dict("rocm_arch" => replace(rocm_arch, "-" => "_"))) + add_artifact!("Artifacts.toml", "ROCm", "https://repo.amd.com/rocm/tarball/therock-dist-windows-$rocm_arch-7.13.0.tar.gz"; lazy = true, platform) + end + - name: Upload hash report + uses: actions/upload-artifact@v4 + with: + name: Artifacts.toml + path: Artifacts.toml diff --git a/.pkg/platform_augmentation.jl b/.pkg/platform_augmentation.jl new file mode 100644 index 000000000..1a4475cea --- /dev/null +++ b/.pkg/platform_augmentation.jl @@ -0,0 +1,285 @@ +using Base.BinaryPlatforms, Libdl + +function rocm_arch_string(target::Integer) + patch = lowercase(string(target % 100, base = 16)) + return "gfx$(div(target, 10000))$(div(target, 100) % 100)$(patch)" +end + +function rocm_arch_from_device_name(device_name::AbstractString) + device_lower = lowercase(device_name) + + if !occursin("radeon", device_lower) && !occursin("amd", device_lower) + return "" + end + + # STX Halo iGPUs (gfx1151 architecture) + # Radeon 8050S Graphics / Radeon 8060S Graphics + if occursin("8050s", device_lower) || + occursin("8060s", device_lower) || + occursin("device 1586", device_lower) + return "gfx1151" + end + + # STX Point iGPUs (gfx1150 architecture) + # Radeon 880M / 890M Graphics + if occursin("880m", device_lower) || + occursin("890m", device_lower) + return "gfx1150" + end + + # RDNA4 GPUs (gfx120X architecture) + # AMD Radeon AI PRO R9700, AMD Radeon RX 9070 XT, AMD Radeon RX 9070 GRE, + # AMD Radeon RX 9070, AMD Radeon RX 9060 XT + if occursin("r9700", device_lower) || + occursin("9060", device_lower) || + occursin("9070", device_lower) + return "gfx120X" + end + + # RDNA3 GPUs (gfx110X architecture) + # AMD Radeon PRO V710, AMD Radeon PRO W7900 Dual Slot, AMD Radeon PRO W7900, + # AMD Radeon PRO W7800 48GB, AMD Radeon PRO W7800, AMD Radeon PRO W7700, + # AMD Radeon RX 7900 XTX, AMD Radeon RX 7900 XT, AMD Radeon RX 7900 GRE, + # AMD Radeon RX 7800 XT, AMD Radeon RX 7700 XT + if occursin("7700", device_lower) || + occursin("7800", device_lower) || + occursin("7900", device_lower) || + occursin("v710", device_lower) + return "gfx110X" + end + + # RDNA2 GPUs (gfx103X architecture) + # AMD Radeon RX 6800 XT, AMD Radeon RX 6800, AMD Radeon RX 6700 XT, + # AMD Radeon RX 6700, AMD Radeon RX 6600 XT, AMD Radeon RX 6600, + # AMD Radeon RX 6500 XT, AMD Radeon RX 6500 + if occursin("6800", device_lower) || + occursin("6700", device_lower) || + occursin("6600", device_lower) || + occursin("6500", device_lower) + return "gfx103X" + end + + return "" +end + +function rocm_arch_linux() + topology_root = "/sys/class/kfd/kfd/topology/nodes/" + isdir(topology_root) || return String[] + + arch = String[] + for dir in readdir(topology_root; join = true) + props = joinpath(dir, "properties") + isfile(props) || continue + + for s in eachline(props) + m = match(r"^gfx_target_version (\d+)$", s) + m === nothing && continue + + target = parse(Int, m[1]) + target == 0 && continue + + push!(arch, rocm_arch_string(target)) + end + end + + unique!(arch) + sort!(arch; rev = true) + return arch +end + +function wmi_query_video_controllers() + ole32 = Libdl.dlopen("ole32.dll") + oleaut32 = Libdl.dlopen("oleaut32.dll") + + CoInitializeEx = Libdl.dlsym(ole32, :CoInitializeEx) + CoInitializeSecurity = Libdl.dlsym(ole32, :CoInitializeSecurity) + CoCreateInstance = Libdl.dlsym(ole32, :CoCreateInstance) + CoUninitialize = Libdl.dlsym(ole32, :CoUninitialize) + SysAllocString = Libdl.dlsym(oleaut32, :SysAllocString) + SysFreeString = Libdl.dlsym(oleaut32, :SysFreeString) + + # Helper: allocate a BSTR from a Julia string + function bstr(s::String) + ws = transcode(UInt16, s * "\0") + ccall(SysAllocString, Ptr{UInt16}, (Ptr{UInt16},), ws) + end + free_bstr(p) = ccall(SysFreeString, Cvoid, (Ptr{UInt16},), p) + + # Helper: read a BSTR pointer back to Julia String + function read_bstr(p::Ptr{UInt16}) + p == C_NULL && return "" + # BSTR length (in bytes) is stored 4 bytes before the pointer + nbytes = unsafe_load(Ptr{UInt32}(p - 4)) + nchars = nbytes ÷ 2 + buf = unsafe_wrap(Array, p, nchars; own=false) + transcode(String, buf) + end + + # CLSID_WbemLocator = {4590F811-1D3A-11D0-891F-00AA004B2E24} + # IID_IWbemLocator = {DC12A687-737F-11CF-884D-00AA004B2E24} + CLSID_WbemLocator = UInt8[ + 0x11, 0xF8, 0x90, 0x45, # Data1 (little-endian) + 0x3A, 0x1D, # Data2 (little-endian) + 0xD0, 0x11, # Data3 (little-endian) + 0x89, 0x1F, 0x00, 0xAA, 0x00, 0x4B, 0x2E, 0x24] + + IID_IWbemLocator = UInt8[ + 0x87, 0xA6, 0x12, 0xDC, + 0x7F, 0x73, + 0xCF, 0x11, + 0x88, 0x4D, 0x00, 0xAA, 0x00, 0x4B, 0x2E, 0x24] + + # Step 1: CoInitializeEx(0, COINIT_MULTITHREADED=0) + ccall(CoInitializeEx, Clong, (Ptr{Cvoid}, Culong), C_NULL, 0) + + # Step 2: CoInitializeSecurity(NULL, -1, NULL, NULL, + # RPC_C_AUTHN_LEVEL_DEFAULT=0, RPC_C_IMP_LEVEL_IMPERSONATE=3, + # NULL, EOAC_NONE=0, NULL) + ccall(CoInitializeSecurity, Clong, + (Ptr{Cvoid}, Clong, Ptr{Cvoid}, Ptr{Cvoid}, + Culong, Culong, Ptr{Cvoid}, Culong, Ptr{Cvoid}), + C_NULL, -1, C_NULL, C_NULL, 0, 3, C_NULL, 0, C_NULL) + + # Step 3: CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER=1, + # &IID_IWbemLocator, &locator) + locator = Ref{Ptr{Cvoid}}(C_NULL) + hr = ccall(CoCreateInstance, Clong, + (Ptr{UInt8}, Ptr{Cvoid}, Culong, Ptr{UInt8}, Ref{Ptr{Cvoid}}), + CLSID_WbemLocator, C_NULL, 1, IID_IWbemLocator, locator) + hr < 0 && error("CoCreateInstance failed: 0x$(string(hr % UInt32, base=16))") + + # vtable layout for IWbemLocator (inherits IUnknown): + # [0] QueryInterface [1] AddRef [2] Release [3] ConnectServer + vtbl_loc = unsafe_load(Ptr{Ptr{Ptr{Cvoid}}}(locator[])) + + resource = bstr("ROOT\\CIMV2") + services = Ref{Ptr{Cvoid}}(C_NULL) + # Step 4: locator->ConnectServer(locator, resource, NULL,NULL,NULL,0,NULL,NULL, &services) + hr = ccall(unsafe_load(vtbl_loc, 4), Clong, + (Ptr{Cvoid}, Ptr{UInt16}, Ptr{Cvoid}, Ptr{Cvoid}, + Ptr{Cvoid}, Clong, Ptr{Cvoid}, Ptr{Cvoid}, Ref{Ptr{Cvoid}}), + locator[], resource, C_NULL, C_NULL, C_NULL, 0, C_NULL, C_NULL, services) + hr < 0 && error("ConnectServer failed: 0x$(string(hr % UInt32, base=16))") + + # vtable layout for IWbemServices — ExecQuery is at index 20 (0-based) + vtbl_svc = unsafe_load(Ptr{Ptr{Ptr{Cvoid}}}(services[])) + + language = bstr("WQL") + query = bstr("SELECT Name FROM Win32_VideoController") + results = Ref{Ptr{Cvoid}}(C_NULL) + # Step 5: services->ExecQuery(services, language, query, + # WBEM_FLAG_BIDIRECTIONAL=0, NULL, &results) + hr = ccall(unsafe_load(vtbl_svc, 21), Clong, # 0-based index 20 -> 1-based 21 + (Ptr{Cvoid}, Ptr{UInt16}, Ptr{UInt16}, Clong, Ptr{Cvoid}, Ref{Ptr{Cvoid}}), + services[], language, query, 0, C_NULL, results) + hr < 0 && error("ExecQuery failed: 0x$(string(hr % UInt32, base=16))") + + # vtable layout for IEnumWbemClassObject: + # [0] QI [1] AddRef [2] Release [3] Reset [4] Next [5] NextAsync [6] Clone [7] Skip + vtbl_enum = unsafe_load(Ptr{Ptr{Ptr{Cvoid}}}(results[])) + + # vtable layout for IWbemClassObject::Get is at index 4 (0-based) + # [0] QI [1] AddRef [2] Release [3] GetQualifierSet [4] Get ... + + names = String[] + while true + result = Ref{Ptr{Cvoid}}(C_NULL) + returned = Ref{Culong}(0) + + # results->Next(results, WBEM_INFINITE=-1, 1, &result, &returned) + hr = ccall(unsafe_load(vtbl_enum, 5), Clong, # Next at 0-based 4 -> 1-based 5 + (Ptr{Cvoid}, Clong, Culong, Ref{Ptr{Cvoid}}, Ref{Culong}), + results[], -1, 1, result, returned) + (hr != 0 || returned[] == 0) && break # S_FALSE or no more objects + + vtbl_obj = unsafe_load(Ptr{Ptr{Ptr{Cvoid}}}(result[])) + + # VARIANT is 16 bytes: vt(2) + reserved(6) + value(8) + variant = zeros(UInt8, 16) + prop = bstr("Name") + + # result->Get(result, L"Name", 0, &variant, NULL, NULL) + hr = ccall(unsafe_load(vtbl_obj, 5), Clong, # Get at 0-based 4 -> 1-based 5 + (Ptr{Cvoid}, Ptr{UInt16}, Clong, Ptr{UInt8}, Ptr{Cvoid}, Ptr{Cvoid}), + result[], prop, 0, variant, C_NULL, C_NULL) + + if hr == 0 + vt = reinterpret(UInt16, variant[1:2])[1] + if vt == 8 # VT_BSTR + p = reinterpret(UInt64, variant[9:16])[1] + push!(names, read_bstr(Ptr{UInt16}(p))) + end + end + + free_bstr(prop) + ccall(unsafe_load(vtbl_obj, 3), Culong, (Ptr{Cvoid},), result[]) # Release + end + + # Cleanup — mirrors the C example exactly + ccall(unsafe_load(vtbl_enum, 3), Culong, (Ptr{Cvoid},), results[]) # results->Release + ccall(unsafe_load(vtbl_svc, 3), Culong, (Ptr{Cvoid},), services[]) # services->Release + ccall(unsafe_load(vtbl_loc, 3), Culong, (Ptr{Cvoid},), locator[]) # locator->Release + ccall(CoUninitialize, Cvoid, ()) + free_bstr(query); free_bstr(language); free_bstr(resource) + + return names +end + +function rocm_arch() + if Sys.islinux() + return rocm_arch_linux() + elseif Sys.iswindows() + arch = map(rocm_arch_from_device_name, wmi_query_video_controllers()) + filter!(!isempty, arch) + unique!(arch) + sort!(arch; rev = true) + return arch + end + + return String[] +end + +function rocm_arch_comparison_strategy(a::String, b::String, a_requested::Bool, b_requested::Bool) + a == "none" && return false + b == "none" && return false + + a_arches = split(a, ',') + b_arches = split(b, ',') + for a_arch in a_arches + for b_arch in b_arches + rocm_arch_matches(a_arch, b_arch) && return true + rocm_arch_matches(b_arch, a_arch) && return true + end + end + return false +end + +function rocm_arch_core(arch::AbstractString) + return match(r"gfx(.*)", first(split(arch, r"[_-]", limit = 2)))[1] +end + +function rocm_arch_matches(pattern::AbstractString, arch::AbstractString) + pattern = rocm_arch_core(pattern) + arch = rocm_arch_core(arch) + + length(pattern) == length(arch) || return false + for (pattern_char, arch_char) in zip(pattern, arch) + if lowercase(pattern_char) == 'x' + isxdigit(arch_char) || lowercase(arch_char) == 'x' || return false + elseif pattern_char != arch_char + return false + end + end + return true +end + +function augment_platform!(platform::Platform) + if !haskey(platform, "rocm_arch") + arch = rocm_arch() + platform["rocm_arch"] = isempty(arch) ? "none" : join(arch, ',') + end + + BinaryPlatforms.set_compare_strategy!(platform, "rocm_arch", rocm_arch_comparison_strategy) + + return platform +end diff --git a/.pkg/select_artifacts.jl b/.pkg/select_artifacts.jl new file mode 100644 index 000000000..1f150bc19 --- /dev/null +++ b/.pkg/select_artifacts.jl @@ -0,0 +1,134 @@ +push!(Base.LOAD_PATH, dirname(@__DIR__)) + +using TOML, Artifacts, Base.BinaryPlatforms +include("./platform_augmentation.jl") +artifacts_toml = joinpath(dirname(@__DIR__), "Artifacts.toml") + +# Update Base.parse to support riscv64, needed for Julia <1.12 +@static if !haskey(BinaryPlatforms.arch_mapping, "riscv64") + + BinaryPlatforms.arch_mapping["riscv64"] = "(rv64|riscv64)" + + function bbparse(::Type{Platform}, triplet::AbstractString; validate_strict::Bool = false) + arch_mapping = BinaryPlatforms.arch_mapping + os_mapping = BinaryPlatforms.os_mapping + libc_mapping = BinaryPlatforms.libc_mapping + call_abi_mapping = BinaryPlatforms.call_abi_mapping + libgfortran_version_mapping = BinaryPlatforms.libgfortran_version_mapping + cxxstring_abi_mapping = BinaryPlatforms.cxxstring_abi_mapping + libstdcxx_version_mapping = BinaryPlatforms.libstdcxx_version_mapping + + # Helper function to collapse dictionary of mappings down into a regex of + # named capture groups joined by "|" operators + c(mapping) = string("(",join(["(?<$k>$v)" for (k, v) in mapping], "|"), ")") + + # We're going to build a mondo regex here to parse everything: + triplet_regex = Regex(string( + "^", + # First, the core triplet; arch/os/libc/call_abi + c(arch_mapping), + c(os_mapping), + c(libc_mapping), + c(call_abi_mapping), + # Next, optional things, like libgfortran/libstdcxx/cxxstring abi + c(libgfortran_version_mapping), + c(cxxstring_abi_mapping), + c(libstdcxx_version_mapping), + # Finally, the catch-all for extended tags + "(?(?:-[^-]+\\+[^-]+)*)?", + "\$", + )) + + m = match(triplet_regex, triplet) + if m !== nothing + # Helper function to find the single named field within the giant regex + # that is not "nothing" for each mapping we give it. + get_field(m, mapping) = begin + for k in keys(mapping) + if m[k] !== nothing + # Convert our sentinel "nothing" values to actual "nothing" + if endswith(k, "_nothing") + return nothing + end + # Convert libgfortran/libstdcxx version numbers + if startswith(k, "libgfortran") + return VersionNumber(parse(Int,k[12:end])) + elseif startswith(k, "libstdcxx") + return VersionNumber(3, 4, parse(Int,m[k][11:end])) + else + return k + end + end + end + end + + # Extract the information we're interested in: + arch = get_field(m, arch_mapping) + os = get_field(m, os_mapping) + libc = get_field(m, libc_mapping) + call_abi = get_field(m, call_abi_mapping) + libgfortran_version = get_field(m, libgfortran_version_mapping) + libstdcxx_version = get_field(m, libstdcxx_version_mapping) + cxxstring_abi = get_field(m, cxxstring_abi_mapping) + function split_tags(tagstr) + tag_fields = filter(!isempty, split(tagstr, "-")) + if isempty(tag_fields) + return Pair{String,String}[] + end + return map(v -> Symbol(v[1]) => v[2], split.(tag_fields, "+")) + end + tags = split_tags(m["tags"]) + + # Special parsing of os version number, if any exists + function extract_os_version(os_name, pattern) + m_osvn = match(pattern, m[os_name]) + if m_osvn !== nothing + return VersionNumber(m_osvn.captures[1]) + end + return nothing + end + os_version = nothing + if os == "macos" + os_version = extract_os_version("macos", r".*darwin([\d.]+)"sa) + end + if os == "freebsd" + os_version = extract_os_version("freebsd", r".*freebsd([\d.]+)"sa) + end + if os == "openbsd" + os_version = extract_os_version("openbsd", r".*openbsd([\d.]+)"sa) + end + + return Platform( + arch, os; + validate_strict, + libc, + call_abi, + libgfortran_version, + cxxstring_abi, + libstdcxx_version, + os_version, + tags..., + ) + end + throw(ArgumentError("Platform `$(triplet)` is not an officially supported platform")) + end + +else + # riscv64 is supported, all is fine + + const bbparse = parse + +end + + +# Get "target triplet" from ARGS, if given (defaulting to the host triplet otherwise) +target_triplet = get(ARGS, 1, Base.BinaryPlatforms.host_triplet()) + +# Augment this platform object with any special tags we require +platform = augment_platform!(HostPlatform(bbparse(Platform, target_triplet))) + +# Select all downloadable artifacts that match that platform +artifacts = select_downloadable_artifacts(artifacts_toml; platform, include_lazy=true) + +# Output the result to `stdout` as a TOML dictionary +TOML.print(stdout, artifacts) diff --git a/Artifacts.toml b/Artifacts.toml new file mode 100644 index 000000000..4bf93751f --- /dev/null +++ b/Artifacts.toml @@ -0,0 +1,191 @@ +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "927a3fe95e9d836dbc762f6327159526dcaa5bc7" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx908" + + [[ROCm.download]] + sha256 = "5d84753a8d8895ff2f6137a2a922ee8f36ce9c2e01b60a99d3ee776a683bfc34" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx908-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "66ad1dc431ab06faff7baf8842e91f80548f6ea6" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx90a" + + [[ROCm.download]] + sha256 = "b2d3c49ef936b3b24b10a25bae3e60df7ccc9c5134095a080bbc721d5062b4c7" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx90a-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "148fb9883a4a104c807e2ec2448986e0f4dd40f4" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx94x_dcgpu" + + [[ROCm.download]] + sha256 = "db5543de096fb175ff2ece19dacc28b2a3201df48b38051cc505e508d84e35ab" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx94X-dcgpu-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "e32c6a1039d8704432105abb9e381892f6b66e33" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx950_dcgpu" + + [[ROCm.download]] + sha256 = "794e8292c843621f772df83bbcbb6a8d5926279803dbd273679a1b6e782e16c7" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx950-dcgpu-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "6e52a70a9284555a2eeb670077b58c484860816f" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx101x_dgpu" + + [[ROCm.download]] + sha256 = "0cb099ec837e1206d710467674a254b07119c72fec765a5316d3703161c7bfe1" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx101X-dgpu-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "2351804874d5724fa82a74007d46f9a15951a9be" +lazy = true +os = "windows" +rocm_arch = "gfx101x_dgpu" + + [[ROCm.download]] + sha256 = "0f13040ed76d00fafe0540d5e23bf7b06ee96c7f341edf13d637f176c24508cc" + url = "https://repo.amd.com/rocm/tarball/therock-dist-windows-gfx101X-dgpu-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "472b6d4f2186b8363e4621b00d6e7a3d16d4a5ea" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx103x_all" + + [[ROCm.download]] + sha256 = "7049d3d934699226ea58ce821f64f7042f41467281f4d099ca123fd912592f23" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx103X-all-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "5cc5dbbf08958113bec3e77b81ebf3254fc5e59b" +lazy = true +os = "windows" +rocm_arch = "gfx103x_all" + + [[ROCm.download]] + sha256 = "479ccc5318a092023338800cbac3fcc604afd6dd5a8582856705d558ae7f3035" + url = "https://repo.amd.com/rocm/tarball/therock-dist-windows-gfx103X-all-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "7c7e3170d31e5360030a4c8bfbab39cf7bdb230b" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx110x_all" + + [[ROCm.download]] + sha256 = "c28e6861763bd5282caf349908aef99116160d200fdbf56c3ce947033c1876c3" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx110X-all-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "b502d69d1b873444498d3c398987e664212efffa" +lazy = true +os = "windows" +rocm_arch = "gfx110x_all" + + [[ROCm.download]] + sha256 = "3a27e7aa079c1c4ccc502be3aa1d89f9d2094726e4a75b731b9d590b075abf16" + url = "https://repo.amd.com/rocm/tarball/therock-dist-windows-gfx110X-all-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "4684f33015a110d83fcfe9f52a5aa9f6c6ceb0a8" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx1150" + + [[ROCm.download]] + sha256 = "49ab239f372901fbd2975285471cf5550de55a7bcd81602044cff426904c2ade" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx1150-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "4221777696eb7833ca9fcfd4b56a7703a92f87c5" +lazy = true +os = "windows" +rocm_arch = "gfx1150" + + [[ROCm.download]] + sha256 = "7fae82e61375fdcd9c8eb83761e91d0603e26a5cf45ade62aa3aa92afd28495b" + url = "https://repo.amd.com/rocm/tarball/therock-dist-windows-gfx1150-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "4d8726c11c5fa4f406cb151a8c729cf593d99afe" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx1151" + + [[ROCm.download]] + sha256 = "6550da740ac8565ec2f1dc886f6ec6425af1df79588d773594163b0778832560" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx1151-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "e6012913f4e5b177bb61862b7d56b6037683aa47" +lazy = true +os = "windows" +rocm_arch = "gfx1151" + + [[ROCm.download]] + sha256 = "9171d4bb2fdf9f7228c9658f5de93652d9c6e95974fe14de13b66ce77e8e0999" + url = "https://repo.amd.com/rocm/tarball/therock-dist-windows-gfx1151-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "6060cf1f79c122f9ed5ccc27f43ddcac0203b7ce" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx1152" + + [[ROCm.download]] + sha256 = "125094beb4e780bf3494e3b36d334a93593207bdcce18c8afb8161c82b703e5d" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx1152-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "d557c97f913bf700e8f6125a417fb29ce313621e" +lazy = true +os = "windows" +rocm_arch = "gfx1152" + + [[ROCm.download]] + sha256 = "3aeff35172973b174c379072c51a39cfce20a1695f2679691ea0567e68ba836a" + url = "https://repo.amd.com/rocm/tarball/therock-dist-windows-gfx1152-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "8b8d69691b2469e8f890fe977fe9c01e33233b64" +lazy = true +libc = "glibc" +os = "linux" +rocm_arch = "gfx120x_all" + + [[ROCm.download]] + sha256 = "150c3ed6eb51cda451a96d588ca04a15af2c8724fd5e692b705cba30faf4efcf" + url = "https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx120X-all-7.13.0.tar.gz" +[[ROCm]] +arch = "x86_64" +git-tree-sha1 = "389c9194587a0fda638ac38ff71bb5e9c9e88f34" +lazy = true +os = "windows" +rocm_arch = "gfx120x_all" + + [[ROCm.download]] + sha256 = "4496f1e4667f162b1fc03dc94a9465d139f5a8dc1cc1dc472b43e594fb8db68d" + url = "https://repo.amd.com/rocm/tarball/therock-dist-windows-gfx120X-all-7.13.0.tar.gz" diff --git a/Project.toml b/Project.toml index b8010f5dd..3cd41e355 100644 --- a/Project.toml +++ b/Project.toml @@ -10,6 +10,7 @@ projects = ["test", "docs", "perf"] AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c" AcceleratedKernels = "6a4ca0a5-0e36-4168-a932-d9be78d558f1" Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" Atomix = "a9b6321e-bd34-4604-b9c9-b65b8de01458" BFloat16s = "ab4f0b2a-ad5b-11e8-123f-65d77653426b" CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" @@ -18,16 +19,15 @@ GPUArrays = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" GPUCompiler = "61eb1bfa-7361-4325-ad38-22787b887f55" GPUToolbox = "096a3bc2-3ced-46d0-87f4-dd12716f4bfc" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" -LLD_jll = "d55e3150-da41-5e91-b323-ecfd1eec6109" LLVM = "929cbde3-209d-540e-8aea-75f648917ca0" LLVM_jll = "86de99a1-58d6-5da7-8064-bd56ce2e322c" +LazyArtifacts = "4af54fe1-eca0-43a8-85a7-787d91b784e3" Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Preferences = "21216c6a-2e73-6563-6e65-726566657250" PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" -ROCmDeviceLibs_jll = "873c0968-716b-5aa7-bb8d-d1e2e2aeff2d" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Random123 = "74087812-796a-5b5d-8853-05524746bad3" RandomNumbers = "e6cf234a-135c-5ec9-84dd-332b85af5143" @@ -51,6 +51,7 @@ AMDGPUSparseMatricesCSRExt = "SparseMatricesCSR" AbstractFFTs = "1.0" AcceleratedKernels = "0.3.1, 0.4" Adapt = "4" +Artifacts = "1.10" Atomix = "1" BFloat16s = "0.6.0" CEnum = "0.4, 0.5" @@ -61,12 +62,12 @@ GPUArrays = "11.3.1" GPUCompiler = "1" GPUToolbox = "0.1.0, 0.2, 0.3, 1" KernelAbstractions = "0.9.2" -LLD_jll = "15, 16, 17, 18, 19, 20" LLVM = "9" LLVM_jll = "15, 16, 17, 18, 19, 20" +LazyArtifacts = "1.10" +Libdl = "1.10" Preferences = "1" PrettyTables = "3" -ROCmDeviceLibs_jll = "=5.6.1, =6.2.1, =7.0.2" Random123 = "1.6" RandomNumbers = "1.5" SparseMatricesCSR = "0.6.9" diff --git a/src/compiler/Compiler.jl b/src/compiler/Compiler.jl index 5928e7110..b09b9cf0f 100644 --- a/src/compiler/Compiler.jl +++ b/src/compiler/Compiler.jl @@ -1,7 +1,6 @@ module Compiler import Core: LLVMPtr -import LLD_jll using ..GPUCompiler using ..LLVM diff --git a/src/compiler/codegen.jl b/src/compiler/codegen.jl index f859d7a32..1de38df08 100644 --- a/src/compiler/codegen.jl +++ b/src/compiler/codegen.jl @@ -42,6 +42,63 @@ function GPUCompiler.link_libraries!(@nospecialize(job::HIPCompilerJob), mod::LL wavefrontsize64=job.config.params.wavefrontsize64) end + +GPUCompiler.@unlocked function GPUCompiler.mcgen( + @nospecialize(job::CompilerJob{<:GCNCompilerTarget}), mod::LLVM.Module, format=LLVM.API.LLVMAssemblyFile, +) + clang_path = AMDGPU.ROCmDiscovery.clang_path + if isempty(clang_path) + # Fallback to GPUCompiler default if no external clang is found + return invoke(GPUCompiler.mcgen, Tuple{CompilerJob, LLVM.Module, typeof(format)}, job, mod, format) + end + + dl = GPUCompiler.llvm_datalayout(job.config.target) + if dl !== nothing + # Union of Julia's GC address spaces (10:11:12:13) and + # AMDGPU device lib's buffer/resource address spaces (7:8:9) + LLVM.datalayout!(mod, LLVM.DataLayout(replace(string(dl), "-ni:10:11:12:13" => "-ni:7:8:9:10:11:12:13"))) + end + + target = job.config.target + filetype = if format == LLVM.API.LLVMAssemblyFile + "asm" + elseif format == LLVM.API.LLVMObjectFile + "obj" + else + error("Unsupported GCN output format $format") + end + + input = tempname(cleanup=false) * ".bc" + output = tempname(cleanup=false) * (filetype == "asm" ? ".s" : ".o") + write(input, mod) + + wavefrontsize64 = if hasproperty(job.config.params, :wavefrontsize64) + job.config.params.wavefrontsize64 + else + true # Default fallback + end + + devlib_paths = get_device_libs_paths(target; wavefrontsize64) + devlib_flags = String[] + for path in devlib_paths + push!(devlib_flags, "-Xclang", "-mlink-builtin-bitcode", "-Xclang", path) + end + + cmd = `$clang_path -x ir $input -mcpu=$(target.dev_isa) --target=$(GPUCompiler.llvm_triple(target)) -nogpulib $devlib_flags $(filetype == "asm" ? "-S" : "-c") -o $output` + + try + run(cmd) + catch + error("""Failed to compile to GCN with external clang. + If you think this is a bug, please file an issue and attach $(input).""") + end + + code = filetype == "asm" ? read(output, String) : String(read(output)) + rm(input) + rm(output) + return code +end + function GPUCompiler.finish_module!( @nospecialize(job::HIPCompilerJob), mod::LLVM.Module, entry::LLVM.Function, ) @@ -142,7 +199,7 @@ function compiler_config(dev::HIP.HIPDevice; target = GCNCompilerTarget(; dev_isa, features) params = HIPCompilerParams(wavefrontsize64, unsafe_fp_atomics) - CompilerConfig(target, params; kernel, name, always_inline=true) + CompilerConfig(target, params; kernel, name, always_inline=true, validate=false) end const hipfunction_lock = ReentrantLock() @@ -184,18 +241,13 @@ function hipfunction(f::F, tt::TT = Tuple{}; kwargs...) where {F <: Core.Functio end function create_executable(obj) - lld = if AMDGPU.lld_artifact - `$(LLD_jll.lld()) -flavor gnu` - else - @assert !isempty(AMDGPU.lld_path) "ld.lld was not found; cannot link kernel" - `$(AMDGPU.lld_path)` - end + @assert !isempty(AMDGPU.lld_path) "ld.lld was not found; cannot link kernel" path_o = tempname(;cleanup=false) * ".obj" path_exe = tempname(;cleanup=false) * ".exe" write(path_o, obj) - run(`$lld -shared -o $path_exe $path_o`) + run(`$(AMDGPU.lld_path) -shared -o $path_exe $path_o`) bin = read(path_exe) rm(path_o) diff --git a/src/compiler/device_libs.jl b/src/compiler/device_libs.jl index 4f01667d8..3baf4a059 100644 --- a/src/compiler/device_libs.jl +++ b/src/compiler/device_libs.jl @@ -23,33 +23,28 @@ end const DEVICE_LIBS::Dict{String, DevLib} = Dict{String, DevLib}() -function link_device_libs!( - target::GCNCompilerTarget, mod::LLVM.Module; +function get_device_libs_paths( + target::GCNCompilerTarget; wavefrontsize64::Bool, ) - isnothing(libdevice_libs) && return + paths = String[] + isnothing(libdevice_libs) && return paths # 1. Load other libraries. lib_names = ("hc", "hip", "irif", "ockl", "opencl", "ocml") for lib_name in lib_names - devlib = get!(DEVICE_LIBS, lib_name) do - DevLib(lib_name, locate_lib(lib_name)) - end - load_and_link!(devlib, mod) + path = locate_lib(lib_name) + !isnothing(path) && push!(paths, path) end # 2. Load OCLC library. - devlib = get!(DEVICE_LIBS, "oclc") do - isa_short = replace(target.dev_isa, "gfx"=>"") - DevLib("oclc", locate_lib("oclc_isa_version_$isa_short")) - end - load_and_link!(devlib, mod) + isa_short = replace(target.dev_isa, "gfx"=>"") + path = locate_lib("oclc_isa_version_$isa_short") + !isnothing(path) && push!(paths, path) # 3. Load OCLC ABI library. - devlib = get!(DEVICE_LIBS, "oclc_abi") do - DevLib("oclc_abi", locate_lib("oclc_abi_version_500")) - end - load_and_link!(devlib, mod) + path = locate_lib("oclc_abi_version_500") + !isnothing(path) && push!(paths, path) # 4. Load options libraries. options = ( @@ -62,8 +57,25 @@ function link_device_libs!( for (option, value) in options toggle = value ? "on" : "off" name = "oclc_$(option)_$(toggle)" + path = locate_lib(name) + !isnothing(path) && push!(paths, path) + end + return paths +end + +function link_device_libs!( + target::GCNCompilerTarget, mod::LLVM.Module; + wavefrontsize64::Bool, +) + isnothing(libdevice_libs) && return + if !isempty(AMDGPU.ROCmDiscovery.clang_path) + return + end + + for path in get_device_libs_paths(target; wavefrontsize64) + name = basename(path) devlib = get!(DEVICE_LIBS, name) do - DevLib(name, locate_lib(name)) + DevLib(name, path) end load_and_link!(devlib, mod) end diff --git a/src/discovery/discovery.jl b/src/discovery/discovery.jl index a7998b4dc..95ea13240 100644 --- a/src/discovery/discovery.jl +++ b/src/discovery/discovery.jl @@ -1,36 +1,16 @@ module ROCmDiscovery -export lld_artifact, lld_path, libhsaruntime, libdevice_libs, libhip +export lld_artifact, lld_path, clang_path, clang_artifact, libhsaruntime, libdevice_libs, libhip export librocblas, librocsparse, librocsolver export librocrand, librocfft, libMIOpen_path -using LLD_jll -using ROCmDeviceLibs_jll +using LazyArtifacts using Preferences using Libdl -include("utils.jl") - -function get_artifact_library(pkg::Symbol, libname::Symbol)::String - succ, res = safe_exec("import $pkg; println($pkg.$libname)") - (succ && ispath(res)) || return "" - return res -end - -function get_ld_lld(rocm_path::String)::Tuple{String, Bool} - lld_path = find_ld_lld(rocm_path) - isempty(lld_path) || return (lld_path, false) - LLD_jll.is_available() || return (lld_path, false) - return (LLD_jll.lld_path, true) -end +Base.include(@__MODULE__, joinpath(@__DIR__, "..", "..", ".pkg", "platform_augmentation.jl")) -function get_device_libs(from_artifact::Bool; rocm_path::String) - if from_artifact && ROCmDeviceLibs_jll.is_available() - ROCmDeviceLibs_jll.bitcode_path - else - find_device_libs(rocm_path) - end -end +include("utils.jl") function _hip_runtime_version() v_ref = Ref{Cint}() @@ -47,7 +27,7 @@ end global rel_libdir::String = Sys.islinux() ? "" : "bin" global libhsaruntime::String = "" global lld_path::String = "" -global lld_artifact::Bool = false +global clang_path::String = "" global libhip::String = "" global libdevice_libs::String = "" global librocblas::String = "" @@ -81,14 +61,11 @@ function __init__() "" # Linker. - lld_path, lld_artifact = get_ld_lld(rocm_path) - global lld_path = lld_path - global lld_artifact = lld_artifact + global lld_path = find_ld_lld(rocm_path) + global clang_path = find_clang(rocm_path) global libhip = find_rocm_library(Sys.islinux() ? "libamdhip64" : "amdhip64"; rocm_path) - # Always load artifact device libraries. - from_artifact = true - global libdevice_libs = get_device_libs(from_artifact; rocm_path) + global libdevice_libs = find_device_libs(rocm_path) # HIP-based libraries. global librocblas = find_rocm_library(lib_prefix * "rocblas"; rocm_path) diff --git a/src/discovery/utils.jl b/src/discovery/utils.jl index cd85d0bc3..f9e917107 100644 --- a/src/discovery/utils.jl +++ b/src/discovery/utils.jl @@ -1,3 +1,13 @@ +# Resolve an artifact-backed ROCm root, if one has been installed. +function artifact_rocm_path()::String + try + p = augment_platform!(HostPlatform()) + return @artifact_str("ROCm", p) + catch + return "" + end +end + # use amdhip as query for a valid rocm_path function check_rocm_path(path::String) libname = (Sys.islinux() ? "libamdhip64" : "amdhip64") * "." * dlext @@ -24,6 +34,9 @@ function find_roc_path()::String rocm_path != "" && return rocm_path end + artifact_dir = artifact_rocm_path() + isdir(artifact_dir) && return artifact_dir + if Sys.islinux() hipconfig = Sys.which("hipconfig") if !isnothing(hipconfig) @@ -103,19 +116,20 @@ end function find_rocm_library(libs::Vector; rocm_path::String, ext::String = dlext)::String for lib in libs - path = find_rocm_library(lib, rocm_path, ext) + path = find_rocm_library(lib; rocm_path, ext) isempty(path) || return path end return "" end function find_rocm_library(lib::String; rocm_path::String, ext::String = dlext)::String - libdir = joinpath(rocm_path, rel_libdir) - isdir(libdir) || return "" - for file in readdir(libdir; join=true) - fname = basename(file) - matched = startswith(fname, lib) && contains(fname, ext) - matched && return file + for libdir in [joinpath(rocm_path, rel_libdir), joinpath(rocm_path, "lib")] + isdir(libdir) || continue + for file in readdir(libdir; join=true) + fname = basename(file) + matched = startswith(fname, lib) && contains(fname, ext) + matched && return file + end end return "" end @@ -148,3 +162,20 @@ function find_ld_lld(rocm_path::String)::String end return "" end + +function find_clang(rocm_path::String)::String + clang_name = "clang" * (Sys.iswindows() ? ".exe" : "") + + dirs = (joinpath(rocm_path, "llvm", "bin"), joinpath(rocm_path, "bin")) + hipconfig = Sys.which("hipconfig") + if !isnothing(hipconfig) + clang_path = read(`$hipconfig --hipclangpath`, String) + dirs = (dirs..., clang_path) + end + for dir in dirs + exp_clang_path = joinpath(dir, clang_name) + ispath(exp_clang_path) || continue + return exp_clang_path + end + return "" +end diff --git a/test/Project.toml b/test/Project.toml index 8a24df04d..dca70e332 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -12,6 +12,7 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" LLVM = "929cbde3-209d-540e-8aea-75f648917ca0" +Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" ParallelTestRunner = "d3525ed8-44d0-4b2c-a655-542cee43accc" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" @@ -24,6 +25,3 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[sources] -AMDGPU = {path = ".."} diff --git a/test/runtests.jl b/test/runtests.jl index 884b7edbc..075af829e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -26,6 +26,25 @@ end InteractiveUtils.versioninfo() AMDGPU.versioninfo() +include(joinpath(@__DIR__, "..", ".pkg", "platform_augmentation.jl")) + +@testset "platform augmentation" begin + @test rocm_arch_string(120000) == "gfx1200" + @test rocm_arch_string(120001) == "gfx1201" + @test rocm_arch_string(90010) == "gfx90a" + @test rocm_arch_from_device_name("AMD Radeon 8050S Graphics") == "gfx1151" + @test rocm_arch_from_device_name("AMD Radeon 890M Graphics") == "gfx1150" + @test rocm_arch_from_device_name("AMD Radeon RX 9070 XT") == "gfx120X" + @test rocm_arch_from_device_name("NVIDIA GeForce RTX 4090") == "" + @test rocm_arch_comparison_strategy("gfx120X_all", "gfx1200", false, false) + @test rocm_arch_comparison_strategy("gfx120X_all", "gfx1201", false, false) + @test rocm_arch_comparison_strategy("gfx120X_all", "gfx120X", false, false) + @test rocm_arch_comparison_strategy("gfx90X_dcgpu", "gfx90a", false, false) + @test rocm_arch_comparison_strategy("gfx94X_dcgpu", "gfx942", false, false) + @test !rocm_arch_comparison_strategy("gfx120X_all", "gfx1100", false, false) + @test !rocm_arch_comparison_strategy("none", "gfx1200", false, false) +end + # Autodiscovered tests testsuite = find_tests(@__DIR__)