#!/usr/bin/env perl # # Copyright (c) 2014-2015 Cisco Systems, Inc. All rights reserved. # Copyright (c) 2015-2021 Research Organization for Information Science # and Technology (RIST). All rights reserved. # Copyright (c) 2022 IBM Corporation. All rights reserved. # $COPYRIGHT$ # # Script to generate the overloaded MPI_SIZEOF interfaces and # subroutine bodies for both the mpi and mpi_f08 modules. # # This script won't really be necessary (i.e., be a whole lot simpler) # when Fortran compilers uniformly supprort TS 29113 -- i.e., they # support dimension(..). Using dimension(..), you can have just *one* # procedure for every type, and dimension(..) will resolve to both # scalars and all possible ranks. # # But for the meantime, we generate for all ranks so that we support # as many compilers as possible. :-\ (we don't check the compiler and # see if it supports dimension(..) and do a different generation based # on that, because we already have a zillion different options in the # Fortran support -- let's just do MPI_Sizeof this one way in the name # of simplicity...). # use strict; use Getopt::Long; my $header_arg; my $impl_arg; my $ierror_arg; my $maxrank_arg; my $generate_arg; my $mpi_arg; my $mpi_real2; my $mpi_iso_real16; my $mpi_real16; my $mpi_complex4; my $mpi_complex32; my $pmpi_arg; my $help_arg = 0; my $request_deprecate = 0; my $mpi_version = 0; &Getopt::Long::Configure("bundling"); my $ok = Getopt::Long::GetOptions("complex32=i" => \$mpi_complex32, "complex4=i" => \$mpi_complex4, "header=s" => \$header_arg, "impl=s" => \$impl_arg, "ierror=s" => \$ierror_arg, "maxrank=s" => \$maxrank_arg, "generate=i" => \$generate_arg, "mpi" => \$mpi_arg, "pmpi" => \$pmpi_arg, "real16=i" => \$mpi_real16, "real2=i" => \$mpi_real2, "iso_real16=i" => \$mpi_iso_real16, "request_deprecate=i" => \$request_deprecate, "mpi_version=i" => \$mpi_version, "help|h" => \$help_arg); die "Must specify header and/or impl filenames to output" if (!defined($header_arg) && !defined($impl_arg)); die "ierror handling must be optional or mandatory" if (defined($generate_arg) && $generate_arg && (lc($ierror_arg) ne "optional" && lc($ierror_arg) ne "mandatory")); die "max array rank must be >= 4 and <=15" if (defined($generate_arg) && $generate_arg && (!defined($maxrank_arg) || $maxrank_arg < 4 || $maxrank_arg > 15)); die "Must specify --pmpi and/or --mpi if --impl is specified" if (defined($generate_arg) && $generate_arg && (defined($impl_arg) && !defined($mpi_arg) && !defined($pmpi_arg))); die "Must specify real2, iso_real16, real16, complex4, and complex32" if (!defined($mpi_real2) || !defined($mpi_iso_real16) || !defined($mpi_real16) || !defined($mpi_complex4) || !defined($mpi_complex32)); ############################################################################# my $optional_ierror_param; my $optional_ierror_statement; if (lc($ierror_arg) eq "optional") { $optional_ierror_param = ", OPTIONAL"; $optional_ierror_statement = "IF (present(ierror)) "; } my $indent = " "; ############################################################################# my $subs; sub queue_sub { my ($f_type, $suffix, $import_type) = @_; # Leave off the MPI/PMI prefix; we'll add that when outputting my $sub_name = "Sizeof_$suffix"; # Make a hash for this subroutine my $subr; $subr->{name} = $sub_name; my $start = "${indent}SUBROUTINE ^PREFIX^$sub_name^RANK^(x, size, ierror)\n"; $start .= "${indent} USE, INTRINSIC :: iso_fortran_env, ONLY: " . uc($import_type) . "\n" if (defined($import_type)); # For long type names and large ranks, this first line gets very # long and only narrowly squeezed in before 72 columns. Use no # whitespace. $start .= $indent . uc($f_type) . "^DIMENSION^::x ${indent} INTEGER, INTENT(OUT) :: size ${indent} INTEGER$optional_ierror_param, INTENT(OUT) :: ierror"; $subr->{start} = $start; $subr->{middle} = "${indent} size = storage_size(x) / 8 ${indent} ${optional_ierror_statement}ierror = 0"; if (($mpi_version >= 4) && ($request_deprecate == 1)) { $subr->{end} = "!GCC\$ ATTRIBUTES DEPRECATED :: ^PREFIX^$sub_name^RANK^\n"; } else { $subr->{end} = ""; } $subr->{end} .= "${indent}END SUBROUTINE ^PREFIX^$sub_name^RANK^"; # Save it in the overall hash $subs->{$sub_name} = $subr; } sub generate { my ($prefix, $sub_name, $rank, $want_body) = @_; my $subr; # Deep copy %{$subr} = %{$subs->{$sub_name}}; # Make the initial version my $str = $subr->{start} . "\n"; $str .= "\n" . $subr->{middle} . "\n" if ($want_body); $str .= $subr->{end} . "\n"; # Substitute in the relevant parameters $str =~ s/\^PREFIX\^/$prefix/g; # If rank is 0, generate a scalar version. Otherwise, generate an # array version. if (0 == $rank) { $str =~ s/\^RANK\^/_scalar/g; $str =~ s/\^DIMENSION\^//; } else { $str =~ s/\^RANK\^/_r$rank/g; my $dim; my $d = $rank; while ($d > 1) { $dim .= "1,"; --$d; } $str =~ s/\^DIMENSION\^/, DIMENSION($dim*)/; } # All done return $str; } ############################################################################# # Main ############################################################################# for my $size (qw/8 16 32 64/) { queue_sub("integer(int${size})", "int${size}", "int${size}"); } for my $size (qw/16 32 64 128/) { if (!($size == 16 && $mpi_real2 == 0 && $mpi_iso_real16 == 0) && !($size == 128 && $mpi_real16 == 0)) { queue_sub("real(real${size})", "real${size}", "real${size}"); } if (!($size == 16 && $mpi_complex4 == 0 && $mpi_iso_real16 == 0) && !($size == 128 && $mpi_complex32 == 0)) { queue_sub("complex(real${size})", "complex${size}", "real${size}"); } } if ($mpi_real2 == 1 && $mpi_iso_real16 == 0) { queue_sub("real*2", "real16"); queue_sub("complex*4", "complex16"); } queue_sub("character", "character"); queue_sub("logical", "logical"); ####################################################### sub output_content { my ($prefix, $want_bodies) = @_; print OUT "${indent}INTERFACE ${prefix}Sizeof\n\n" if (!$want_bodies); # Print all the module procedure lines foreach my $sub_name (sort(keys(%{$subs}))) { my $rank = 0; while ($rank <= $maxrank_arg) { my $str = generate($prefix, $sub_name, $rank, $want_bodies); print OUT $str . "\n"; ++$rank; } } print OUT "${indent}END INTERFACE ${prefix}Sizeof\n\n" if (!$want_bodies); } # Output each file sub output_file { my ($filename, $want_bodies) = @_; unlink($filename); open(OUT, ">$filename") || die "Can't open $filename for writing"; print OUT "! -*- f90 -*- ! WARNING: This is a generated file! Edits will be lost! ! ! Copyright (c) 2014 Cisco Systems, Inc. All rights reserved. ! \$COPYRIGHT\$ ! ! This file was generated by gen-mpi-sizeof.pl for all the MPI_SIZEOF ! interface possibilities for intrinsic types. Once TS 29113 is ! supported in all compilers, we can simply have *one* procedure for ! each type and use dimension(..) to indicate scalars+all array ranks. ! But until more compilers support this, we simply generate a ! procedure for scalars and all possible ranks in an attempt to ! support lots of Fortran compilers.\n\n"; # Only output if the generate arg is 0. Otherwise, output an # empty .h file (that is still safe to include by mpif.h, but # won't include the MPI_SIZEOF interface block). if ($generate_arg) { output_content("MPI_", $want_bodies) if (!$want_bodies || ($want_bodies && $mpi_arg)); output_content("PMPI_", $want_bodies) if (!$want_bodies || ($want_bodies && $pmpi_arg)); } else { print OUT "! *** ATTENTION! ! ! Sad panda. ! ! This compiler does not support the Right Stuff to enable MPI_SIZEOF. ! Specifically: we need support for the INTERFACE keyword, ! ISO_FORTRAN_ENV, and the STORAGE_SIZE() intrinsic on all types. ! Apparently, this compiler does not support both of those things, so ! this file will be (effectively) blank (i.e., we didn't bother ! generating the necessary stuff for MPI_SIZEOF because the compiler ! doesn't support ! it). ! ! If you want support for MPI_SIZEOF, please use a different Fortran ! compiler to build Open MPI.\n\n"; if ($want_bodies) { my $name = $pmpi_arg ? "pompi_sad_panda" : "ompi_sad_panda"; print OUT "! ! Dummy subroutine, just so that there is *some* Fortran in this file ! (this is defensive programming: since the Fortran compiler doesn't ! support enough mojo, configure should set some AM_CONDITIONALs such ! that this file should not end up being compiled, but just in case ! that logic changes someday and this file *does* end up getting ! compiled, make sure that it's not entirely empty because some ! compilers are unhappy if there are no Fortran statements in this ! file). subroutine $name() implicit none print *, 'Open MPI is a sad panda because your Fortran compiler' print *, 'does not support enough Fortran mojo for MPI_SIZEOF' end subroutine $name\n\n"; } } close(OUT); } output_file($header_arg, 0) if (defined($header_arg)); output_file($impl_arg, 1) if (defined($impl_arg)); exit(0);