Migrating to the Fortran bindings shipped in ROCm#
Who this is for#
You write Fortran against hipFORT (use hipfort_rocblas, hipMalloc,
rocblas_dgemm, and so on) and want to know what to change, and when.
In one line: the bindings move into ROCm itself (rocm-systems and
rocm-libraries), you rename your use statements and link a per-library
Fortran archive, and nothing else in your code changes.
The layout of the new bindings (one self-contained module per library, ROCm only) is previewed in hipFORT pull request #540.
Are there breaking changes?#
Yes, but they are limited and mechanical: your code will not compile or link unchanged. Two edits are required, and the CUDA backend is dropped.
Source. The module names change:
use hipfort_rocblasbecomesuse rocblas.Build. The link target changes:
find_package(hipfort)andhipfort::rocblasbecomefind_package(rocblas)androc::rocblas_fortran.NVIDIA users. The CUDA (
nvptx) backend is not carried into the new bindings.
Everything at the call level (routine names, arguments, calling styles) is unchanged, so the two edits are find-and-replace, not a rewrite. The details are in What you change.
Do I need to do anything now?#
Not right now. Today’s hipFORT keeps working, unchanged, until it is removed at ROCm 11.0; keep building it yourself as you do today. Any time before 11.0, make the two changes in What you change. Old and new share the same interfaces underneath, so you migrate on your own schedule.
That said, sooner is better than later. The edit itself is mechanical, so the value of doing it early is not the edit, it is the room it leaves you afterwards: if anything behaves differently (a routine that was generated incorrectly, an overload that resolves differently, a build-system wrinkle), you want to hit it with releases to spare rather than while 11.0 is closing. Early reports also get fixed for everyone, so the sooner your code runs against the new bindings, the better they are when the rest of the ecosystem moves.
Timeline#
ROCm |
What it means for you |
|---|---|
≤ 10.1 |
Nothing changes. Keep using hipFORT as today. |
10.2 |
The new bindings ship inside ROCm (packaged, modernized). You can migrate, and this is the release to aim for rather than the last one before 11.0. |
11.0 |
The old hipFORT is removed. You must be on the new bindings by this release. |
What you change#
Two things: the modules you use, and the Fortran library you link. Your
actual calls (routine names, arguments, calling style) do not change.
Rename your use statements#
The hipfort_ prefix is dropped, and each library collapses to a single
module named after the library.
Old ( |
New (one module, no prefix) |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The HIP memory helpers you might use directly (hipfort_hipmalloc,
hipfort_hipmemcpy, hipfort_hiphostregister) fold into use hip as
well.
The shared helper modules go away. Delete these use lines; their symbols
now come from the library module you already use:
Old |
Where the symbols are now |
|---|---|
|
In each library module ( |
|
In each library module ( |
|
In |
Profiling (hipfort_roctx) is a special case, so it is not in the tables
above: the new roctx binding is deferred rather than shipped in the initial
packaged set, because ROCTx itself is moving into rocprofiler-sdk upstream and
its home is not yet settled. If you annotate Fortran with hipfort_roctx
today, old hipFORT keeps working through the 10.x series; past that, until a
packaged roctx binding lands, call the C ROCTx API directly through
iso_c_binding (it is a handful of bind(C) interfaces).
Only hipfort_cuda_errors (the CUDA-backend error enum) has no new
equivalent: it is tied to the dropped CUDA backend and retires with hipFORT at
11.0.
A rename looks like this; the calls in between are untouched:
! before
use hipfort
use hipfort_check
use hipfort_rocblas
! after
use hip ! runtime + hipCheck
use rocblas
This is a replacement, not an addition: delete the old use hipfort_rocblas
line.
Warning
Do not keep use hipfort_rocblas and use rocblas in the same
scope: both export the same public names (rocblas_dgemm,
rocblas_handle, the enums, and so on), so the compiler cannot tell which
one you mean and the file will not compile.
Different files in the same program can still migrate independently, because the clash is only within a single scope (see the FAQ).
Doing the rename automatically#
The rename is regular enough to script, so you do not have to walk a large
codebase by hand. On a Git tree, this rewrites every use line in place:
libs='rocblas|hipblas|rocsparse|hipsparse|hipfftw|rocfft|hipfft|rocsolver|hipsolver|rocrand|hiprand'
sed -i -E \
-e "/^[[:space:]]*use[[:space:]]+hipfort_($libs)_(enums|types)\b/Id" \
-e "/^[[:space:]]*use[[:space:]]+hipfort_(check|handles|auxiliary|types|enums)\b/Id" \
-e "s/^([[:space:]]*use[[:space:]]+)hipfort_($libs)\b/\1\2/I" \
-e "s/^([[:space:]]*use[[:space:]]+)hipfort_(hipmalloc|hipmemcpy|hiphostregister)\b/\1hip/I" \
-e "s/^([[:space:]]*use[[:space:]]+)hipfort\b/\1hip/I" \
$(git ls-files '*.f90' '*.F90')
The first two rules delete the modules that fold away, and the last three
rename what is left. They cannot step on each other, because _ is a word
character: hipfort_rocblas_enums is deleted rather than renamed to
rocblas_enums, and the last rule never fires on a hipfort_... name. The
I flag covers USE HIPFORT_ROCBLAS as well, and only: clauses
survive the rename (use hipfort_rocblas, only: rocblas_dgemm becomes
use rocblas, only: rocblas_dgemm).
Then read the diff, because a few things are deliberately left to you:
A file might end up with two
use hiplines (fromuse hipfortanduse hipfort_hipmalloc, for example). That is valid Fortran and compiles fine; collapse them if you prefer.A file that used
hipfort_check,hipfort_handles, orhipfort_auxiliarywithout ever usinghipfortitself loses those symbols, because the delete rules take the whole line. Adduse hipthere.use hipfort_roctxanduse hipfort_cuda_errorsare left untouched on purpose; they have no packaged equivalent yet (see above).Fixed-form sources, continuation lines, and
uselines behind preprocessor guards are not covered.grep -rni 'use[[:space:]]*hipfort' .finds whatever the rules missed.
A supported migration tool is worth shipping if codebases turn out to need more than this; the recipe above is the whole of what it would do, so open an issue if you would rather have the tool.
Link the per-library Fortran archive#
Instead of one libhipfort-*.a for everything, each library ships its own
Fortran archive and CMake target. The target sits in the C library’s own
namespace (roc:: or hip::) with a _fortran suffix, so
find_package(<lib>) gives you both the C library and its Fortran binding:
# before
find_package(hipfort REQUIRED)
target_link_libraries(app PRIVATE hipfort::rocblas)
# after
find_package(rocblas REQUIRED)
target_link_libraries(app PRIVATE roc::rocblas roc::rocblas_fortran)
Library |
|
Link (CMake target) |
|---|---|---|
HIP runtime |
|
|
rocBLAS |
|
|
hipBLAS |
|
|
rocSPARSE |
|
|
hipSPARSE |
|
|
rocFFT |
|
|
hipFFT |
|
|
hipFFTW |
|
Pending (packaged with hipFFT; target being finalized) |
rocSOLVER |
|
|
hipSOLVER |
|
|
rocRAND |
|
|
hipRAND |
|
|
The raw archive is lib<lib>_fortran.a (for example librocblas_fortran.a)
if you link without CMake.
Build options#
If you consume ROCm’s shipped bindings, you pass none of these. hipFORT’s
HIPFORT_* options were options for building hipFORT, and under the
packaged track you no longer build it: the bindings arrive precompiled, with
the array overloads on and assumed-rank off. The options below matter only
when you rebuild a binding yourself, which is the non-amdflang case in
Building one library’s binding from source.
The switch that replaces “do I build hipFORT at all” is
BUILD_FORTRAN_BINDINGS, which every rocm-systems and rocm-libraries
project exposes. It is ON by default but guarded: the bindings are built
when a Fortran compiler is present, and silently skipped when one is not, so a
C-only site never has to acquire a Fortran compiler. Pass
-DBUILD_FORTRAN_BINDINGS=OFF to opt out even when you have one.
Old hipFORT option |
New equivalent |
|---|---|
(build hipFORT, or do not) |
|
|
Both fold into one tri-state |
|
Gone. The per-compiler layout is unconditional (see Where the files install) |
|
Gone, with the CUDA backend |
|
Gone. Use the plain |
|
|
The interface tiers themselves do not change: the raw type(c_ptr) surface
is Fortran 2003 and is always present, the ergonomic array overloads are
Fortran 2008 and stay on, and the assumed-rank variants are Fortran 2018 and
stay opt-in. What changes is that one option now selects between them instead
of two. That follows the language: assumed-rank replaces the per-rank
variants rather than adding to them, because an assumed-rank dummy is not
distinguishable by rank from the per-rank specifics, so the two cannot legally
coexist in one generic. Two booleans could express that illegal combination;
three values cannot. For a side-by-side comparison of the call sites, see
Fortran interface variants.
The shipped bindings are built at the default, assumed-shape, which is what
hipFORT gives you today on any Fortran 2008 compiler. You only need the option
in a from-source rebuild, either to opt into assumed-rank or to drop to
none on a compiler whose Fortran 2008 support you do not trust.
Building the bindings’ own test suite is a separate switch,
BUILD_FORTRAN_TESTS, OFF by default and guarded the same way: asking
for the tests when the bindings were not built is a skip, not an error. If you
build rocSPARSE, note that its existing BUILD_FORTRAN_CLIENTS becomes a
deprecated alias of it, so its default flips from ON to OFF.
To check from CMake whether a binding is actually available, use the per-package
flag <pkg>_FORTRAN_FOUND (for example rocblas_FORTRAN_FOUND), which the
Fortran config package defines. Inside a ROCm build tree there are two more
specific variables: ROCM_LIBS_HAVE_FORTRAN reports only that a Fortran
compiler exists, while <LIB>_HAVE_FORTRAN_BINDINGS (for example
ROCSPARSE_HAVE_FORTRAN_BINDINGS) is true only when that library’s bindings
were really built. The two are deliberately distinct, because the compiler can
be present while a given library has BUILD_FORTRAN_BINDINGS=OFF.
Compiler support#
Which compiler?#
The bindings support amdflang (ROCm’s bundled LLVM Flang, the recommended
default), gfortran (7.5.0 or newer), and other iso_c_binding-capable
compilers such as Cray Fortran.
One catch is worth knowing: there is no portable Fortran ABI, so a .mod
(and its compiled .a) is tied to one compiler, and even one compiler
version; two compilers cannot share a .mod. ROCm therefore ships the
bindings precompiled for amdflang:
Building with amdflang? Link the shipped bindings as they are.
Building with another compiler (gfortran, Cray, and so on)? The shipped
.modwill not match, so build the bindings from source with your own compiler. This is automatic: when you build ROCm from source with a Fortran compiler present, the bindings build by default and you get a.modthat matches your compiler (pass-DBUILD_FORTRAN_BINDINGS=OFFto skip them).
Note
Mixing amdflang versions. Strictly, a .mod is tied to the compiler
version too, so an amdflang other than the one that built the shipped
bindings would normally refuse to read them. amdflang relaxes that:
starting with ROCm 10.1, the opt-in -fmodule-mismatch-check=warn lets it
read a .mod written by a different amdflang version, reporting a
warning instead of an error. The default is unchanged, and the flag only
covers version skew inside amdflang: nothing lets gfortran read an
amdflang .mod, or the reverse. When you can rebuild the binding with
the compiler you are actually using, still do, because the warning is
telling you the two artifacts were not built together.
The C libraries you link (libamdhip64.so, librocblas.so, and so on) are
the stable, compiler-agnostic ABI; only the thin Fortran layer is
compiler-specific.
Where the files install#
Because the artifacts are compiler-specific, they install under a per-compiler
subdirectory: the .mod files in include/fortran/<compiler>/ (for
example /opt/rocm/include/fortran/amdflang/) and the .a files in
lib/fortran/<compiler>/. A CMake find_package picks your compiler’s
subdirectory automatically; if you link with raw flags, point -I and -L
at it.
Linking without CMake, the command is the same for every compiler; only the
<compiler> subdirectory changes. For a program using rocBLAS:
# amdflang (links ROCm's precompiled bindings)
amdflang app.f90 \
-I/opt/rocm/include/fortran/amdflang \
-L/opt/rocm/lib/fortran/amdflang -lrocblas_fortran \
-L/opt/rocm/lib -lrocblas -lamdhip64 -o app
# gfortran (bindings built from source with gfortran)
gfortran app.f90 \
-I/opt/rocm/include/fortran/gfortran \
-L/opt/rocm/lib/fortran/gfortran -lrocblas_fortran \
-L/opt/rocm/lib -lrocblas -lamdhip64 -o app
Link the Fortran archive (-lrocblas_fortran) before the C library
(-lrocblas) and the HIP runtime (-lamdhip64), and give each library you
use its own -l<lib>_fortran.
Building one library’s binding from source#
If you are on a non-amdflang compiler, you build the binding yourself, but
you do not have to rebuild the C library. The generated .F90 is
self-contained Fortran, so compiling it needs only a Fortran compiler: it does
not rebuild rocBLAS, and it needs neither the C headers nor the .so at
build time (the vendor symbols resolve when you link your application).
Each library’s fortran/ directory is a self-contained CMake project. Point
it at an installed ROCm and build only the binding:
# gfortran (amdflang: same commands, only -DCMAKE_Fortran_COMPILER changes)
cmake -S projects/rocblas/fortran -B build/rocblas-fortran \
-DCMAKE_Fortran_COMPILER=gfortran \
-DCMAKE_PREFIX_PATH=/opt/rocm \
-DCMAKE_INSTALL_PREFIX=/opt/rocm
cmake --build build/rocblas-fortran
cmake --install build/rocblas-fortran
Add -DFORTRAN_ARRAY_INTERFACES=assumed-rank here if you want the Fortran
2018 variants; see Build options for the rest.
CMAKE_PREFIX_PATH lets the build find the installed C library (its version,
and any dependency binding); CMAKE_INSTALL_PREFIX puts the .mod and
lib<lib>_fortran.a under your compiler’s subdirectory. Building the C
library with -DBUILD_FORTRAN_BINDINGS=ON does the same thing under the hood
(it just invokes this fortran/ build through add_subdirectory), but it
rebuilds the C library too, which is much heavier.
A binding that uses another (rocSOLVER uses rocBLAS) needs the
dependency’s .mod, compiled with the same compiler, so build in dependency
order: rocBLAS’s binding first, then rocSOLVER’s, both with the same compiler.
rocSOLVER’s build finds rocBLAS’s binding through find_package(rocblas).
Building the C library alone first and the Fortran binding standalone later is
fine, and find_package(rocblas) then exposes both roc::rocblas and
roc::rocblas_fortran. This works because the Fortran target ships in its
own config package, not in the C library’s export set:
rocblas-config.cmake exports roc::rocblas and ends with
include(rocblas-fortran-config.cmake OPTIONAL), so a Fortran binding
installed later is picked up at find_package time, and ignored when absent.
The one requirement for that later-install case is that the installed C config
already carries the OPTIONAL include hook; a C library from a ROCm
predating this design will not auto-detect a Fortran binding added afterwards.
What does not change#
Your calls.
hipMalloc,rocblas_dgemm,hipblasDgemm, the enum and type names, andhipCheckkeep their names and signatures. Only the module youusechanges.Calling styles. Raw
type(c_ptr)still works (including for OpenMP target data); the array, typed-handle, and string overloads still resolve under the same name; you never touchiso_c_binding.The vendor libraries. You still link the same
.so(libamdhip64.so,librocblas.so, and so on). Only the Fortran.aand.modchange.
Special cases#
If a library ships its own Fortran module (for example
rocblas_module.f90 or rocsparse.f90), it is superseded by the generated
binding. The generated rocblas module becomes the single source of truth;
use it instead. If you already use one of these today (you write
use rocblas against rocblas_module.f90), the module name does not
change, so your use line stays; what changes is that the module now comes
from the generated binding and you link roc::rocblas_fortran. Check any
call sites where the hand-written interface and the generated one differ, such
as keyword arguments or overloads.
If you build for NVIDIA (CUDA), the nvptx backend is dropped from the
new bindings. It survives only in old hipFORT until 11.0.
FAQ#
Do I have to change my code now?
No. Old hipFORT stays available until ROCm 11.0, so you can migrate any time before then, but sooner is better than later: the edit is mechanical, and doing it early leaves room to hit and report anything it turns up, in your code or in the bindings, instead of racing the removal.
Will I be warned before old hipFORT is removed?
Old hipFORT keeps compiling and working through the 10.x series; whether it emits a build-time deprecation notice during that window is still being decided. Either way nothing is removed before ROCm 11.0, so a build that works today keeps working until then.
Can I install both the old and new bindings at once?
Yes. Nothing collides, so you can migrate one file at a time:
Axis |
Old hipFORT |
New bindings |
Clash? |
|---|---|---|---|
Module names |
|
|
No (prefixed versus bare) |
|
|
|
No (different subpaths) |
Archives |
|
|
No (different filenames) |
Symbols in the |
Mangled |
Mangled |
No (mangled per module name) |
CMake package |
|
|
No |
Two rules: build both with the same compiler as your application (a .mod is
compiler-specific); and never use both bindings for the same library in one
scope, because they export the same public names (rocblas_dgemm,
rocblas_handle, the enums, and so on) and the reference becomes ambiguous.
Note that this is a source-level clash only: at the object level the two
archives coexist fine, because their symbols are mangled per module name
(__hipfort_rocblas_MOD_... versus __rocblas_MOD_...), which is why
migrating one file at a time works.
My code is old fixed-form Fortran. Can I use these bindings?
Yes, in all but one case. Fixed form is not the same thing as an old standard:
a .f file with DO ... CONTINUE loops, implicit typing, and a 72-column
layout compiles fine with a current compiler, and it can use hip like any
other source. The requirement is only that the compiler handles Fortran
2003’s iso_c_binding, which every compiler in service does, not that your
code looks modern. The one thing that cannot work is a translation unit
compiled as pre-Fortran 90: below that, the language has no modules, so there
is no use statement to write and nothing to bind to. That is not a change
either, because hipFORT never had a callable path for such code. If you are
genuinely stuck there,
open an issue rather than
hand-writing glue: the answer in that case is a small C shim with F77 linkage
(plus an INCLUDE file for the constants), and it is something that can be
generated for the entry points you call.
Will my calls break?
Your calls will not: routine names, arguments, and calling styles stay the
same. But your code will not compile or link until you make the two edits
(rename the use line, change the link target); that is the breaking part,
and it is the whole of the migration.
Where do I get the new bindings?
They ship precompiled with ROCm (10.2 and later), with no separate install. To
build them in a source tree yourself, build with a Fortran compiler present:
the bindings are on by default (skipped automatically if no Fortran compiler is
found, and -DBUILD_FORTRAN_BINDINGS=OFF opts out).
Why is this changing?
Today’s hipFORT is written partially by hand in a separate repository, so it lags behind ROCm and sometimes misses functions or adds them late. The new bindings are generated straight from the ROCm headers and ship with ROCm, so they stay complete and current, and you no longer install or version-match a separate package.