transformations: Add ‘--amd-gpu’ transformation option.

* guix/transformations.scm (split-on-commas): New procedure, moved from…
(transform-package-toolchain): … here.
(package-amd-gpu-specialization, transform-package-amd-gpu-targets): New
procedures.
(%transformations, %options): Add ‘amd-gpu’.
* tests/transformations.scm ("options->transformations, amd-gpu")
("options->transformations, amd-gpu, not applicable")
("options->transformations, amd-gpu, missing clang-rocm input")
("options->transformations, amd-gpu, wrong GPU"): New tests.
* doc/guix.texi (Package Transformation Options): Document it.

Change-Id: I56bf0dffbf12bc08cf6318fe56952473b395c303
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
Merges: #5583
This commit is contained in:
Ludovic Courtès 2026-01-13 09:38:38 +01:00 committed by Andreas Enge
parent 86984d7c62
commit 4562bbb1d2
No known key found for this signature in database
GPG key ID: F7D5C9BF765C61E3
3 changed files with 173 additions and 6 deletions

View file

@ -22,7 +22,7 @@
@set SUBSTITUTE-URLS https://@value{SUBSTITUTE-SERVER-1} https://@value{SUBSTITUTE-SERVER-2}
@copying
Copyright @copyright{} 2012--2025 Ludovic Courtès@*
Copyright @copyright{} 2012--2026 Ludovic Courtès@*
Copyright @copyright{} 2013, 2014, 2016, 2024 Andreas Enge@*
Copyright @copyright{} 2013 Nikita Karetnikov@*
Copyright @copyright{} 2014, 2015, 2016 Alex Kost@*
@ -13664,6 +13664,37 @@ coarse-grain counterpart of @dfn{function multi-versioning} as
implemented by the GNU tool chain (@pxref{Function Multiversioning,,,
gcc, Using the GNU Compiler Collection (GCC)}).
@cindex GPUs, AMD compilation targets
@cindex AMD GPUs
@cindex ROCm/HIP GPU targets
@vindex amd-gpu-targets
@anchor{amd-gpu-transformation-option}
@item --amd-gpu=@var{targets}
Build relevant packages for the AMD GPUs specified by @var{targets}.
Only packages with an @code{amd-gpu-targets} property are affected.
@var{targets} must be a comma-separated list of
@uref{https://llvm.org/docs/AMDGPUUsage.html#amdgpu-processor-table, AMD
GPU target identifiers}. For example, the command below builds the GPU
code of @code{rocm-bandwidth-test} for AMD Instinct MI250
(@code{gfx90a}) and for AMD Instinct MI300 (@code{gfx942}):
@example
guix build --amd-gpu=gfx90a,gfx942 rocm-bandwidth-test
@end example
To know the identifier(s) of the GPU(s) available on your machine, run:
@example
guix shell rocminfo -- rocm_agent_enumerator
@end example
Under the hood, these GPU identifiers are
passed to Clang's @option{--offload-arch} option.
When @option{--amd-gpu} is omitted, packages are built for a default set
of GPUs, that we hope covers common needs. Building for more GPUs
increases build time significantly and leads to bigger package binaries.
@item --with-source=@var{source}
@itemx --with-source=@var{package}=@var{source}
@itemx --with-source=@var{package}@@@var{version}=@var{source}

View file

@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2016-2024 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2016-2024, 2026 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2021 Marius Bakke <marius@gnu.org>
;;; Copyright © 2023 Sarthak Shah <shahsarthakw@gmail.com>
;;; Copyright © 2023-2025 Efraim Flashner <efraim@flashner.co.il>
@ -422,15 +422,15 @@ TOOLCHAIN must be an input list."
(not (memq p set))))
#:deep? #t))))
(define split-on-commas
(cute string-tokenize <> (char-set-complement (char-set #\,))))
(define (transform-package-toolchain replacement-specs)
"Return a procedure that, when passed a package, changes its toolchain or
that of its dependencies according to REPLACEMENT-SPECS. REPLACEMENT-SPECS is
a list of strings like \"fftw=gcc-toolchain@10\" meaning that the package to
the left of the equal sign must be built with the toolchain to the right of
the equal sign."
(define split-on-commas
(cute string-tokenize <> (char-set-complement (char-set #\,))))
(define (specification->input spec)
(let ((package (specification->package spec)))
(list (package-name package) package)))
@ -704,6 +704,97 @@ for MICRO-ARCHITECTURE, a string suitable for GCC's '-march'."
(rewrite obj)
obj))))))
(define (package-for-amd-gpu-targets p targets)
"Return a variant of P compiled for TARGETS, a list of strings denoting AMD
GPU targets."
(package/inherit p
(properties
`((amd-gpu-targets . ,targets)
,@(alist-delete 'amd-gpu-targets (package-properties p))))))
(define (assert-valid-amd-gpu-targets package targets)
"Raise an error if the AMD GPU identifiers listed in TARGETS are not known
to the compiler of PACKAGE."
(define (compiler-amd-gpu-targets package)
(assoc-ref (package-properties package) 'compiler-amd-gpu-targets))
(match (any (match-lambda
((label (? package? package))
(and (compiler-amd-gpu-targets package)
package))
(_ #f))
(package-direct-inputs package))
(#f
(raise (make-compound-condition
(formatted-message
(G_ "~a: no ROCm compiler was found among the inputs")
(package-full-name package))
(condition (&error-location
(location (package-location package)))))))
(compiler
(let ((supported (compiler-amd-gpu-targets compiler)))
;; This is quadratic but on small lists.
(and=> (find (negate (cut member <> supported)) targets)
(lambda (unsupported)
(raise
(make-compound-condition
(formatted-message
(G_ "compiler ~a does not support AMD GPU target ~a")
(package-full-name compiler) unsupported)
(condition (&error-location
(location (package-location compiler))))
(condition
(&fix-hint
(hint (format #f (G_ "Compiler ~a supports the following
AMD GPU targets:
@quotation
~a
@end quotation")
(package-full-name compiler "@@")
(string-join supported ", ")))))))))))))
(define package-amd-gpu-specialization
(mlambda (targets)
"Return a procedure that maps the given package to its counterpart built
for the given ROCm TARGETS, a list of GPU architectures."
(define rewriting-property
(gensym " package-amd-gpu-specialization"))
(define (mark p)
;; Mark P as already processed.
(package/inherit p
(properties `((,rewriting-property . #t)
,@(package-properties p)))))
(package-mapping (lambda (p)
(cond ((assq rewriting-property (package-properties p))
p)
((assq 'amd-gpu-targets (package-properties p))
(assert-valid-amd-gpu-targets p targets)
(info (N_ "compiling ~a for AMD GPU~{ ~a~}~%"
"compiling ~a for these AMD GPUs:~{ ~a~}~%"
(length targets))
(package-full-name p) targets)
(mark (package-for-amd-gpu-targets p targets)))
(else
p)))
(lambda (p)
(assq rewriting-property (package-properties p)))
#:deep? #t)))
(define (transform-package-amd-gpu-targets targets)
"Return a procedure that, when applied to a package, returns that package
such that the package itself and all its dependencies are specialized for
TARGET-LIST, a list of strings denoting AMD GPUs."
(match targets
((targets _ ...)
(let* ((rewrite (package-amd-gpu-specialization targets)))
(lambda (obj)
(if (package? obj)
(rewrite obj)
obj))))))
(define (transform-package-with-debug-info specs)
"Return a procedure that, when passed a package, set its 'replacement' field
to the same package but with #:strip-binaries? #f in its 'arguments' field."
@ -999,6 +1090,7 @@ are replaced by the specified upstream version."
(with-git-url . ,transform-package-source-git-url)
(with-c-toolchain . ,transform-package-toolchain)
(tune . ,transform-package-tuning)
(amd-gpu . ,transform-package-amd-gpu-targets)
(with-debug-info . ,transform-package-with-debug-info)
(without-tests . ,transform-package-tests)
(with-configure-flag . ,transform-package-configure-flag)
@ -1085,6 +1177,11 @@ building for ~a instead of ~a, so tuning cannot be guessed~%")
result)
(alist-delete 'tune result))
rest)))
(option '("amd-gpu") #t #f
(lambda (opt name arg result . rest)
(apply values
(alist-cons 'amd-gpu (split-on-commas arg) result)
rest)))
(option '("with-debug-info") #t #f
(parser 'with-debug-info))
(option '("without-tests") #t #f

View file

@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2016-2017, 2019-2024 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2016-2017, 2019-2024, 2026 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2021 Marius Bakke <marius@gnu.org>
;;;
;;; This file is part of GNU Guix.
@ -638,6 +638,45 @@
(package->bag (t p))
#f)))
(test-equal "options->transformations, amd-gpu"
'("gfx90a" "gfx942")
(let ((p (dummy-package "amd-gpu-code"
(native-inputs
(list (@ (gnu packages llvm) clang-rocm)))
(properties '((amd-gpu-targets . ("whatever"))))))
(t (options->transformation '((amd-gpu . ("gfx90a" "gfx942"))))))
(assoc-ref (package-properties (t p)) 'amd-gpu-targets)))
(test-equal "options->transformations, amd-gpu, not applicable"
#f
(let ((p (dummy-package "not-amd-gpu-code"))
(t (options->transformation '((amd-gpu . ("gfx90a" "gfx942"))))))
(assoc-ref (package-properties (t p)) 'amd-gpu-targets)))
(test-assert "options->transformations, amd-gpu, missing clang-rocm input"
(let ((p (dummy-package "amd-gpu-code"
(properties '((amd-gpu-targets . ("whatever"))))))
(t (options->transformation '((amd-gpu . ("generic"))))))
;; Since 'clang-rocm' is not among the inputs, an error should be raised.
(guard (c ((formatted-message? c)
(string-contains (formatted-message-string c)
"no ROCm compiler")))
(t p)
#f)))
(test-assert "options->transformations, amd-gpu, wrong GPU"
(let ((p (dummy-package "amd-gpu-code"
(native-inputs
(list (@ (gnu packages llvm) clang-rocm)))
(properties '((amd-gpu-targets . ("whatever"))))))
(t (options->transformation '((amd-gpu . ("does-not-exist"))))))
;; Since this AMD GPU target is not known to 'clang-rocm', an error should
;; be raised.
(guard (c ((formatted-message? c)
(member "does-not-exist" (formatted-message-arguments c))))
(t p)
#f)))
(test-equal "options->transformation + package->manifest-entry"
'((transformations . ((without-tests . "foo"))))
(let* ((p (dummy-package "foo"))