[Mpi-comments] MPI 3.0: Fortran 2008 interface - issue with the LOGICAL kind
Tobias Burnus
burnus at net-b.de
Tue Feb 26 06:19:07 CST 2013
Dear all,
Digest version: The Fortran 2008 interface should use
"LOGICAL(kind=C_BOOL)" instead of "LOGICAL".
MPI 3 [1] adds a new Fortran 2008 interface (or rather one using Fortran
2008 [2] plus TS 29113 [3]), which uses C binding ("bind(C)"). For
instance the following (from "6.7.2 Communicators"):
ABSTRACT INTERFACE
SUBROUTINE MPI_Comm_copy_attr_function(oldcomm, comm_keyval, extra_state,
attribute_val_in, attribute_val_out, flag, ierror) BIND(C)
TYPE(MPI_Comm) :: oldcomm
INTEGER :: comm_keyval, ierror
INTEGER(KIND=MPI_ADDRESS_KIND) :: extra_state, attribute_val_in,
attribute_val_out
LOGICAL :: flag
The code assumes that the default-kind INTEGER and LOGICAL are
interoperable. It is very likely that default-kind integers are
interoperable, given that C supports many integer types (signed char,
short, int, long, long long) and that Fortran's default-kind integer
typically matches "int".
However, for LOGICAL this is not necessarily the case. Fortran's
default-kind LOGICAL is always [5] as wide as the default-kind INTEGER
("LOGICAL(kind=4)"). However, C99's [4] _Bool is on nearly all systems
(one exception is 32bit Darwin) only one byte wide (sizeof(_Bool) == 1)
[6]. The Fortran standard only defines interoperability with this
logical kind:
Fortran 2008, "15.2.2 Named constants and derived types in the module":
"The value of C_BOOL shall be a valid value for a logical kind parameter
on the processor or shall be -1."
Fortran 2008, "Table 15.2: Interoperability between Fortran and C types":
"Fortran type / Named constant from the ISO C BINDING module (kind type
parameter if value is positive) / C type"
...
"LOGICAL / C_BOOL / _Bool"
Hence, a conforming compiler might only accept a single interoperable
LOGICAL kind, namely: C_BOOL. That's actually the case for the
PathScale/pathf95 compiler — and also GCC/gfortran (version >= 4.8) with
-std=f2003/f2008/f2008ts only accepts LOGICAL(C_BOOL) but not LOGICAL [7].
Note an additional potential issue: C99's _Bool only allows two values,
0 and 1; see C99's "6.3.1.2 Boolean type": "When any scalar value is
converted to _Bool, the result is 0 if the value compares equal to 0;
otherwise, the result is 1."
At least the GCC makes use of this restriction and sets TYPE_PRECISION
to 1 for C99/C++'s _Bool/bool and Fortran's LOGICAL (for all KIND=
values). As the hardware typically supports instructions of the form
"jump if zero" or "jump if not zero", also other values work in "if
(bool_var)". However, for negation (C/C++: "!", Fortran: ".not."), GCC
only flips one bit. Hence, for C99's _Bool, C++'s bool and Fortran's
LOGICAL (any kind), negating "-2" leads to "-1".
Example: If you have in one file:
"signed char bool_var = -2; sub(bool_var)"
and in the other file either:
void sub(_Bool bool_var)
or
subroutine sub(bool_var) bind(C)
logical(kind=C_Bool), value :: bool_var
both "bool_var" and "!bool_var"/".not.bool_var" evaluate to true/.true.
In that sense, Fortran's "LOGICAL :: v" is not interoperable with C's
"int v" unless the value is restricted to 0 and 1.
Note regarding C99: If you assign or cast to _Bool, the compiler ensures
that only the value 0 and 1 is used. Additionally, many operations in
C99 return an "int", for instance "&&" or "||" do (see C99, "6.5.13
Logical AND operator" and 6.5.14). Thus, in pure C code one does not
easily run into this issue. Nor in pure Fortran code as one there
typically doesn't mix LOGICAL with INTEGER. But when mixing C and
Fortran (or mixes code compiled iwth different compilers [8]), this can
lead to wrong code.
Hence, I strongly suggest to change in all BIND(C) procedures LOGICAL to
LOGICAL(C_Bool). For the three reasons above: (a) the standard only
defines interop with kind=C_Bool; (b) pathf95 [and gfortran with
-std=f2008ts] only support C_Bool kind logicals with Bind(C); (c) at
least with GCC, using default-kind LOGICAL might lead to wrong results
if the value is not 0 or 1.
Tobias
PS: The issue came up with Open MPI's bug report
https://svn.open-mpi.org/trac/ompi/ticket/3523
[1] MPI 3: http://www.mpi-forum.org/docs/mpi-3.0/mpi30-report.pdf;
erratum http://www.mpi-forum.org/docs/errata-30.pdf
[2] Fortran 2008 (FDIS):
ftp://ftp.nag.co.uk/sc22wg5/N1801-N1850/N1830.pdf ; corrigenda 1 & 2:
ftp://ftp.nag.co.uk/sc22wg5/N1901-N1950/N1903.pdf ,
ftp://ftp.nag.co.uk/sc22wg5/N1951-N2000/N1958.pdf
[3] TS 29113 (FDIS): ftp://ftp.nag.co.uk/sc22wg5/N1901-N1950/N1942.pdf
[4] C99+TC1,TC2,TC3:
http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
[5] That's required in Fortran 2008's "16.5.3.2 Storage sequence": "a
nonpointer scalar object that is default integer, default real, or
default logical occupies a single numeric storage unit,"
[6] That _Bool has is one byte wide is implied by C99's "The rank of
_Bool shall be less than the rank of all other standard integer types."
(6.3.1.1), but in my reading it does not formally demand it. With GCC,
one can force this width for 32bit Darwin via -mone-byte-bool.
[7] Currently, GCC still accepts LOGICAL / LOGICAL(4) with default
options (-std=gnu); however, that might change. See discussion at
http://gcc.gnu.org/ml/fortran/2013-01/msg00090.html and
http://gcc.gnu.org/PR55758
[8] In particular, Intel's Fortran compiler uses "-1" for .true.,
independent of logical kind and bind(C); for the reasons outlined above,
it thus interacts badly with GCC/gfortran.
More information about the mpi-comments
mailing list