Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Program exception with IFX

Robert_van_Amerongen
New Contributor III
517 Views

Recently I posted an issue where I showed a different behaviour between the classic compiler and IFX (community.intel.com/t5/Intel-Fortran-Compiler/Bug-with-IFX/m-p/1444426#M164342). Here is another one. There is no ICE during compilation, but IFX gives an error  "severe (157)". I presume it is related to the intrinsic MERGE and/or the intrinsic PRESENT. See the attached reproducer.

 

Robert

 

0 Kudos
1 Solution
jimdempseyatthecove
Black Belt
417 Views

Ron,

  do_asis = MERGE(asis, .FALSE.,  PRESENT(asis)) 

The above can (and will on some implementations) cause a memory access error/fault due to the fact that an implementation is free to preload both the tsource and fsource values into registers prior to using the mask for selection as to which source is to be used as the result (using conditional move instruction, thus permitting the implementation to eliminate a branch).

IOW the exception isn't a bug in the generated code, it is a bug in the user's source code.

Jim Dempsey

 

View solution in original post

20 Replies
Ron_Green
Moderator
494 Views

This example runs for me without error.  What am I missing, perhaps you are using an argument I am not using?

This is on a Fedora Core 36 OS

 

 

ifx -what -V tfc.f90
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 2023.0.0 Build 20221201
Copyright (C) 1985-2022 Intel Corporation. All rights reserved.

 Intel(R) Fortran 23.0-1198
GNU ld version 2.37-37.fc36
$ ./a.out
 length          16
 result >>This is a string<<
 
 length          32
 result >>This is a string                <<
 
 length          16
 result >>This is a string<<

 

FortranFan
Honored Contributor II
486 Views

@Robert_van_Amerongen ,

Note the semantics of `MERGE` in the standard are such your code does not conform with your `MERGE` statement in that as you have authored it, a standard-conforming processor can end up referencing `asis` optional received argument when it is not present.  The onus is on you to avoid that which can essentially mean to not use `MERGE` and go with the simpler `if ( present(asis) ) then do_asis = asis else do_asis = .false. end if'

With Fortran 2023, an author can do it C-style if they so choose: `do_asis  = ( present(asis) ? .true. : .false. )`

Robert_van_Amerongen
New Contributor III
458 Views

Thanks for your kind help. I am afraid, however, that the mystery remains.

I use Windows 11, MVS version 17.4.3, compilers IFX 2023.0.0 and IFORT 2021.8.0. I use this construction near to always when working with optional intent(in) arguments. Therefore I expect a correct working program. IFORT has never disappointed me. That IFX now failes is a surprise and that Ron has a good working example is an even greater surprise.

 

It is not clear to me why FortranFan suggest not to use this MERGE function. If the construction is not standard conforming then I expect a compiler error. As far as I read the standard (document N2184 page 411)  or Metcalf MFE (2018 edition) (page 189) I do it correct. So ..

 

Robert

 

Arjen_Markus
Honored Contributor I
452 Views

The problem with MERGE that FortranFan points out is that there is no guarantee that the expressions for the true and false cases are not both evaluated. Fortran allows short-circuiting, but does not enforce it. It leaves it to the discretion of the compiler. It may be advantageous to evaluate both first (in parallel for instance) and then decide which one to pass on, rather first decide the truth of the condition and then only evaluate the expression that will be the result. There is simply no way for a human being to devine that.

andrew_4619
Honored Contributor II
437 Views

I agree with Arjen/FF. The correct behaviour for the compiler is to evaluate both terms and then select based on the logical.  This is not a correct use of merge. An if/else is required.

I also agree that this is slightly annoying as I too make extensive use of merge to make more succinct and easier to read code  but avoid the usage case in your example that has  "present" or  "associated".

jimdempseyatthecove
Black Belt
427 Views

FWIW the proper way (portable way) is to do something like:

  do_asis = .FALSE.
  if(PRESENT(asis)) do_asis = asis

Jim Dempsey

 

Ron_Green
Moderator
420 Views

I didn't test on Windows.  I need to try it on Windows.  It could be a bug in the Windows IFX. There are enough FRTL differences between Linux and Windows. 

jimdempseyatthecove
Black Belt
418 Views

Ron,

  do_asis = MERGE(asis, .FALSE.,  PRESENT(asis)) 

The above can (and will on some implementations) cause a memory access error/fault due to the fact that an implementation is free to preload both the tsource and fsource values into registers prior to using the mask for selection as to which source is to be used as the result (using conditional move instruction, thus permitting the implementation to eliminate a branch).

IOW the exception isn't a bug in the generated code, it is a bug in the user's source code.

Jim Dempsey

 

Ron_Green
Moderator
401 Views

@jimdempseyatthecove  thanks for saving me some work.  We'll consider this issue answered and closed.

Robert_van_Amerongen
New Contributor III
366 Views

Thank you all for your help. Arjen basically made clear what FortranFan was telling and why using MERGE in this context can go wrong.

 

This also bring another issue on stage: how to deal with optional arguments. Sometime ago there was an proposel to add a feature similar to how C handles it: add the default value in the argument list. Maybe an item for Fortran 202y?

 

Robert

 

jimdempseyatthecove
Black Belt
348 Views

RE: how to deal with optional arguments.

Maybe Steve L. can add additional insight to an issue I've experienced in the past with optional character arguments, where the length of the/these arguments is/are passed as hidden arguments after the supplied arguments .and. where on call one or more of these arguments are not passed.

What I experienced, which may have been a compiler bug, was that in passing lesser arguments that the character variable length was improperly positioned on the call stack (my assumption) and this resulted in a false positive test of PRESENT for one of the missing arguments that followed the missing argument. As I said, this is an assumption of mine.

My work around was to assure that the potential missing (character) arguments were always place last, and int right to left order of probability of non-presentness. And preferably code for only one such missing character argument.

I had reported this quite a while ago and haven't resurrected the code that experienced this error. I post this here such that you (or those) experiencing this error can rework you code to circumvent this error.

Jim Dempsey

FortranFan
Honored Contributor II
340 Views

@jimdempseyatthecove ,

You will recall it's only with mixed-language C-Fortran programming and the nonstandard approach via the !DIR$ attributes toward directive-enhanced compilation the issue arises with the hidden parameters with Fortran subprogram arguments of CHARACTER type. 

Fortran 2018 with enhanced interoperability offers better support toward both string parameter passing between C and Fortran processors as well as with optional arguments generally in Fortran procedures.  You will recall Intel Fortran has supported this fully since around the 2015 timeframe.  Here is a silly example from several years ago.

So now, about 8 years later, is it possible for you to refactor your codes to move away from the use of !DIR$ attributes and directive-enhanced compilation and instead use standard C-Fortran interoperability?  Or is it too big of a task and your existing codes have to remain with the nonstandard !DIR$ attributes and hidden string length arguments?

Steve_Lionel
Black Belt Retired Employee
338 Views

Optional CHARACTER arguments are possible, with or without using the C interoperability features, but an explicit interface is always required for the Fortran caller as this ensures that the omission is properly passed as a zero by value and the length fields are in their proper places. As @FortranFan suggests, use of BIND(C) for the procedure eliminates hidden arguments, and Fortran 2018 supports optional arguments in interoperable procedures.

Fortran 2023 will introduce conditional expressions and conditional arguments. Conditional expressions are like C has, with similar syntax. For the first time, the standard specifies that the "false" choice is NOT evaluated, so this is a great way to use PRESENT.

Conditional arguments look similar, but here you can choose which actual argument(s) to pass as if they had been specified directly, and can also conditionally omit an argument by having .NIL. be the selected argument.

JohnNichols
Valued Contributor II
330 Views

Is there a simple - say pdf - that actually shows how to do this stuff?  

FortranFan
Honored Contributor II
323 Views

You can start with Intel Fortran Developer Guide, clearly available online in HTML.  And I have not checked of late, but you can download in PDF format likely:

https://www.intel.com/content/www/us/en/develop/documentation/fortran-compiler-oneapi-dev-guide-and-...

You can then also review the sections on Fortran Learn at Fortran-lang.org:

https://fortran-lang.org/en/learn/

The above are all as "simple" as they can get.

andrew_4619
Honored Contributor II
311 Views

oooo nice! I just read https://wg5-fortran.org/N2151-N2200/N2194.pdf section 2.4 (US 22. Conditional expressions and arguments ). Whilst the syntax initially looks a bit alien like all new things one will quickly get used to reading it. This really will be a nice addition in eliminating  clunky verbose looking  constructs to avoid evaluation of conditionally invalid terms like none present optional args and un-associated/unallocated things.  Thanks for all the ongoing hard work you standards people out there!

jimdempseyatthecove
Black Belt
272 Views

FF, Andrew,

This is neet.  In looking at 2.2 US 14. Automatic allocation of lengths of character variables and the example

call get_command(command)

Does this imply that the argument to get_command must be allocatable?

.OR.

Does this imply that arguments to generic functions can be disambiguated with the attribute of allocatable?

 

Jim Dempsey

FortranFan
Honored Contributor II
252 Views

@jimdempseyatthecove wrote:

FF, Andrew,

This is neet.  In looking at 2.2 US 14. Automatic allocation of lengths of character variables and the example

call get_command(command)

Does this imply that the argument to get_command must be allocatable?

.OR.

Does this imply that arguments to generic functions can be disambiguated with the attribute of allocatable?

 

Jim Dempsey


Jim,

  1. Re: "Does this imply that the argument to get_command must be allocatable?" - no.  The first argument labeled COMMAND need not be allocatable, but it can be if the coder so chooses.  This aspect is the same as in the current standard (Fortran 2018), nothing has changed in this regard.
  2. Re: "Does this imply that arguments to generic functions can be disambiguated with the attribute of allocatable?" - no.  No change in this regard either.  The Fortran standard to this day continues to work under the premise since 1966 and FORTRAN 66 and ANSI document toward an early standard that a conforming processor can do some "magic" that is NOT allowed in programs written by users.  Thus FORTRAN then on to Fortran 2023 allows intrinsic procedures such as MAX with what C world calls variadic arguments but a practitioner cannot author a standard-conforming program that has any similar semantics.  Along the same lines, the standard has FINDLOC with certain characteristics of Generics but hell, if a user wanted to author anything like that.  It's the same with GET_COMMAND intrinsic and others like it - users are not offered any facilities to disambiguate ALLOCATABLE dummy arguments from nonallocatable.  Starting Fortran 2018 though,  you will recall you are allowed to distinguish between POINTER and ALLOCATABLE attribute.

Go figure!

Anyways, with GET_COMMAND, you will note previously a coder would have had to do a careful two-step jive almost if the actual argument for COMMAND needed to be ALLOCATABLE.  Call GET_COMMAND once to get the needed length, allocate the actual argument for COMMAND to the right length and call GET_COMMAND again to fetch the command, like so as shown in the standard document example:

PROGRAM hello                     
   CHARACTER(:), ALLOCATABLE :: cmd  
   INTEGER :: cmdlen                 
   CALL GET_COMMAND(LENGTH=cmdlen)   
   IF (cmdlen>0) THEN                
      ALLOCATE(CHARACTER(cmdlen) :: cmd)
      CALL GET_COMMAND(cmd)             
      PRINT *, ’Hello ’, cmd            
   END IF                            
END PROGRAM                       

Starting Fortran 2023, this can be simplified to

PROGRAM hello                   
   CHARACTER(:), ALLOCATABLE :: cmd
   CALL GET_COMMAND(cmd)           
   PRINT *, ’Hello ’, cmd         
END PROGRAM                    

 

jimdempseyatthecove
Black Belt
245 Views

I suspect that GET_COMMAND must be able to disambiguate as to if the argument is allocatable or not as it has to be able to determine if it is permissible to or required to allocate or to not allocate.

For example:

PROGRAM hello                   
   CHARACTER(len=:), ALLOCATABLE :: cmd
   cmd = 'xx' ! cmd now allocated to 2 characters
              ! iow it is no longer unallocated
              ! (presumably its descriptor points to address 0 when not allocated)
   CALL GET_COMMAND(cmd)           
   PRINT *, ’Hello ’, cmd         
END PROGRAM 

 In the above example, GET_COMMAND would need to if it is permissible to reallocate cmd or reuse the 2 characters provided.

IOW in the older standards, the implementation provided the address of a descriptor containing the address and len or size of the argument. While an address of 0 could imply an unallocated allocatable, an allocated allocatable would not be able to provide this information (unless the implementation could deallocate the argument at the CALL statement should the argument be allocatable). In this manner, the absence of an address would convey that the variable is allocatable.

I suspect that the interface to GET_COMMAND has attribute INTENT(OUT) for the returned cmd and thus the implementation could deallocate (when cmd argument is allocatable) at the CALL statement as opposed to within the GET_COMMAND procedure. This then would enable the called (assembly) procedure to determine the dummy argument was allocatable without an explicit attribute in the descriptor or declared in the interface.

Note, a Fortran written procedure could not make use of this hack/feature.

Jim Dempsey

Steve_Lionel
Black Belt Retired Employee
240 Views

There are many places in Fortran where a compiler can do things that user code cannot, especially for intrinsic procedures. Consider MAX and MIN, for example. In the case here, the compiler knows if the character argument is allocatable and can pass that information to the support routine.

Reply