Slatec modernised to Fortran 2018

Kia ora/Hello,

I’ve spent rather too long modernising SLATEC to free-form Fortran 2018 with proper module structure. It builds with CMake and fpm (woohoo!).

The original had about 8,300 GOTOs. It now has 27. The remaining ones are in MINPACK and they looked at me funny, so I left them for now. Everything else has been converted to state machines, DO/EXIT loops, or BLOCK constructs depending on what the original control flow was trying to do.

Arithmetic IFs are gone, INTENT attributes are in, and the whole thing is organised into modules (special_functions, linear, diff_integ, interpolation, and so on).

COMMON blocks in SLAP still need converting to module variables, and some EXTERNAL declarations want proper procedure interfaces. Contributions welcome if anyone fancies it but I’ll continue working on this too.

For numerical verification I tested against Abramowitz & Stegun and NIST DLMF reference values. I also ran a subset of routines on an emulated IBM System/360 via Hercules/TK4- using the original FORTRAN G (1966) and H (1969) compilers. SLATEC was finished in 1993, but it was written conservatively enough that compilers predating it by 27 years still compile the code cleanly. The results match (which is amazing!). That’s 60 years of backwards compatibility in action.

Credit goes to Mehdi Chinoune and Jacob Williams (to name a few). I built on top both of their wonderful work, this would’ve been so much harder to do if it wasn’t for them.

Repo: GitHub - Zaneham/SLATEC

IBM/360 toolchain: GitHub - Zaneham/Fortran360

Hope you all enjoy your holidays and have a happy new year.

22 Likes

Thanks for doing this. I see that there are many include statements in the modernized code, which I think should generally be avoided.

1 Like

Cheers! Thanks for the feedback. I’ll work on this later :slight_smile:

Just curious, what are your reasons for this?

I can’t speak for him but from my point of view, and what I’ve read, theres no encapsulation or namespace management and its harder for compilers to optimise.

Curious if you used any automated tools to help update the code?

I’ve been playing around with SLATEC for the last year or so. It has broken a number of the tools I’ve tried. Even something that could reliably convert labelled DO loops to block DO/END DO form would help a lot.

The most entertaining routines are DPLPMN, DPLPMU, SPLPMN and SPLPMU - which use ASSIGNed GOTO to implement internal procedures. They were obviously written using a preprocessor of some sort. A side effect of this is that all loops were implemented as IF-GOTO, rather than DO, because it was illegal in Fortran 66 to have a DO loop in the extended range of another DO loop… So it is unfortunate that the pre-preprocessed code was not included in the NETLIB files. Would have made understanding the code and converting it to modern form a lot easier.

For kicks, I recoded SPLPMN using F90 internal procedures. It is a little tricky because one also has to watch the scope of the variables in the internal procedures to make sure they are declared in the main procedure - if shared by multiple internal procedures.

1 Like

Yes, I did! I made a whole bunch of python scripts each targets a specific GOTO pattern. Looking at the preprocessor for DPLPMN/SPLPMN seems to be called “MARVEL” and im guessing it was probably a Sandia internal tool written by R.J Hanson and K.L Hiebert in 1981. I’ve got quite a bit of documentation on me but I still have some gaps.

3 Likes

Can you point out the routines with the 27 GO TOs. I’ve found a combination of BLOCK constructs and unindexed DOs can be used to refactor some fairly complex GO TO patterns. Sometimes you have to create some integer flags that can be used in IF tests etc. One thing you have to watch out for is that sometimes the end points for the loops and blocks need to be moved much lower in the source file to replicate the existing logic flow.

I remember getting into trouble replacing an instance of Assigned GOTO by “more modern” code. That is because the nominally integer type variable K used as the recipient of ASSIGN 3456 TO K becomes undefined when a statement number has been assigned to it. Before K can be used in any arithmetic expression, an integer value has to be assigned to it. The programmer has to remain aware of whether K is currently an integer or a statement number, or even a label for a Format string.
This is what we get for using the same variable name to represent (i) an integer value, and (ii) an address. Almost the same problem as with pointer variables in C.

1 Like

O.K. I found the code (I think) and the modifications are actually fairly trivial. Here is a synopsis using SNLS1.

  1. Create a block named block200 by inserting a BLOCK200 : BLOCK statement BEFORE line 651 in snls1.inc
  2. Replace 200 CONTINUE with END BLOCK BLOCK200
  3. Change all GOTO 200 to EXIT BLOCK200
  4. Change 100 CONTINUE to LOOP100 : DO
  5. Change IF (ratio>=p0001 ) GOTO 100 to IF (ratio < p0001) EXIT LOOP100
  6. Insert END DO LOOP100 after this IF statement (line 1003) and before the existing END DO at line 1004.

I would also replace all the IF ( Info = ) statements after the 200 CONTINUE with a SELECT CASE construct but thats just more readable to me. Maybe not to others.

Edit. Got the LOOP100 mod wrong. My suggested mod would have overlapping DO’s - not good. Here is what I think would work.

Change 5. above to IF (ratio >= p0001) CYCLE LOOP100
Instead of 6. above insert
EXIT LOOP100
END DO LOOP100
after the END IF at line 1005

1 Like

Nice! It is fairly trivial to convert fixed to free form. I just used a script with a few sed commands for the vast majority. Then fixed the few ‘significant blank’ issues by hand. But then the fun began…

I see you also converted the [DS]PLPM[NU] routines to use internal procedures.

I’ve never heard of a Fortran preprocessor called ‘marvel’, but yes it is mentioned in the source. Would be interesting to know more about it. Period preprocessors that did support internal procedures included flecs, sftran and a couple others. Since sftran was from JPL, I wonder if marvel was some sftran variant.

When I write programs like that, I add a fictitious declaration like

! host association :: a, b, i, j

This at least tells a human reader where those variables come from. Of course, it is just a comment, so the compiler can’t help with enforcement. However, I can imagine that an automatic code tool might add local declarations for those variables at some time in the future, ignoring the comment hint, and break the code anyway. The latest fortran standard has the import statement where the compiler can enforce the correct variable association, but the last I checked that is not yet supported by some of the popular fortran compilers.

What I did was add ‘implicit none’ to each of the internal procedures. Then it became apparent which ones needed to be declared higher up.

The sentence in the f66 standard is “Further, the extended range of a DO may not contain a DO of the same program unit that has an extended range.” I have read that sentence many times and I have never understood exactly what it means. Your interpretation is the most restrictive, and it does explain why many preprocessors (ratfor, sftran, etc.) converted DO loops into unstructured IF()GOTO loops. This was particularly frustrating in the 1980s when optimizing compilers could convert DO loops into efficient vector code, but could not do the same with the unstructured IF()GOTO loop equivalent.

The Python scripts could be helpful to other Fortran modernizers if you upload them.

I wrote a Fortran program to do the case conversion in 2000 time frame. It has a feature to do all the files in a directory. I did LAPACK once and it took about 10 seconds or so on a recent AMD processor to do the entire code base. It still works great for the majority of F77 files but does sometimes encounter some issues when people mix F90 features like in line comments with F77. I’m thinking about revisiting it (last mod was around 2008) and adding indentation options, moving COMMONS to modules etc. I also use sed scripts to fix things like adding IMPLICIT NONE statements in the correct order and position (I really wish the restrictions on this could be relaxed and let them preceed a USE statement) and changing indentation. What would also be nice is if someone could update the old M4 pre-processor to recognize Modern Fortran, at least until the proposed standard preprocessor becomes self-aware.

1 Like

I’ve put them in the Scripts folder within the repository. If you would like a separate repo I’m more than happy to do that.

1 Like

Just thought I’d post here but I finished the GOTO’s in the remaining folders. They were in rkfab.inc, dnls1.inc and snls1.inc. Happy new year by the way!

3 Likes

The original work Modern SLATEC Library

2 Likes

Thank you so so much for your work Mehdi!