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

'SYSTEM' Subroutine and 'SYSTEM' Function Differences?

Kevin_T_
Beginner
1,382 Views

Is there any documentation as to the differences between the 'system' function and the (apparently intrinsic?) 'system' subroutine?  All I can find is the 'system' function.  Basically I'm not sure if code should be doing this:

call system('echo Hello World')

  Or this:

use ifport, only: system
status = system('echo Hello World')

The reason I ask is because I'm encountering problems related to using the 'system' subroutine that don't appear to affect the 'system' function. I'm trying to figure out if that's because the 'system' subroutine is not officially supported or being used wrong.  (Apologies if this has been answered before, but 'system' is a horrible search term.)

0 Kudos
11 Replies
andrew_4619
Honored Contributor II
1,382 Views

Use EXECUTE_COMMAND_LINE as that is standard Fortran rather than a non-standard  extension

0 Kudos
Steve_Lionel
Honored Contributor III
1,382 Views

There is no such subroutine SYSTEM provided by Intel Fortran. That you can CALL it as a subroutine means only that the compiler can't see that it's a function. In this case it's harmless and the return value is thrown away, but, technically, doing so is an error on your part. What sort of differences are you seeing?

I do agree that EXECUTE_COMMAND_LINE is the better choice. What exactly would you like to do in the command?

0 Kudos
Kevin_T_
Beginner
1,382 Views

I'm seeing that in some circumstances the string passed to 'system' as a subroutine call can get mangled.  This program compiles as ('ifort -assume nounderscore systemConcat.f90'):

program systemConcat

  implicit none

  write(*,*) 'System calls with concatenation:'

  call system('echo '//'a')
  call system('echo '//'ab')
  call system('echo '//'abc')
  call system('echo '//'abcd')
  call system('echo '//'abcde')
  call system('echo '//'abcdef')
  call system('echo '//'abcdefg')
  call system('echo '//'abcdefgh')

  write(*,*) 'System calls without concatenation:'  

  call system('echo a')
  call system('echo ab')
  call system('echo abc')
  call system('echo abcd')
  call system('echo abcde')
  call system('echo abcdef')
  call system('echo abcdefg')
  call system('echo abcdefgh')

end program systemConcat

The output is then:

System calls with concatenation:
a
ab
abc�8@
abcd8@
abcde@
abcdef
abcdefg
abcdefgh
 System calls without concatenation:
a
ab
abc
abcd
abcde
abcdef
abcdefg
abcdefgh

Using either 'execute_command_line' or 'system' as a function via 'ifport' the output is the same either way.  Compiling with '-warn all' highlights the issue nicely (EDIT: see post below on this warning):

warning #8889: Explicit declaration of the EXTERNAL attribute is required.   [SYSTEM]

Not sure why the default is to let that slide, but that's on me; I should have checked more thoroughly to see if the compiler had found anything to complain about.  I'll change the offending code section to use 'execute_command_line'.  Thanks for the input!

0 Kudos
GVautier
New Contributor II
1,382 Views

A function send back the return value through registers. So if you call a function like a subroutine, the changes in registers value (not anticipated by the compiler) may interfere with code execution and produce this kind of errors.

0 Kudos
Steve_Lionel
Honored Contributor III
1,382 Views

I am unable to reproduce the results you show. I tried with and without optimization, both 32 and 64 bit, and every time I get:

 System calls with concatenation:
a
ab
abc
abcd
abcde
abcdef
abcdefg
abcdefgh
 System calls without concatenation:
a
ab
abc
abcd
abcde
abcdef
abcdefg
abcdefgh

/assume:nounderscore has no effect on Windows. Is this Linux or Mac? If so, then what is happening is that you are getting the libc version of "system" which wants a nul-terminated string. ifort puts a nul at the end of string constants but not if you concatenate them.

As for the warning - that is a side effect of a new feature in Fortran 2018, IMPLICIT NONE (EXTERNAL), which forces you to declare external procedures either with EXTERNAL or with an explicit interface. The /warn option was enhanced to add /warn:external. and if you just say /warn, the new option is implied which, like /warn:declarations implying IMPLICIT NONE, implies IMPLICIT NONE (EXTERNAL). That, however, is not the cause of the issue you describe.

Gilles is correct that if you call a function as a subroutine, that can cause a problem in some situations, but not this one. The uses that are problematic are:

  • On IA-32, calling a function that returns a real. In this case, the result is pushed on the floating point stack (a remnant of the x87 instruction set), and never popped, resulting in a FP stack overflow if done enough times. This is not an issue on x64.
  • Calling a function whose result requires the caller to pass a hidden first argument for the result by reference, such as a character or array. Here, you'll likely get an access violation/segfault, or the routine won't work because the wrong number of arguments have been passed.

Neither of these cases apply here.

 

0 Kudos
Kevin_T_
Beginner
1,382 Views

Good catch; compiling with ifort 15.0.7 there is no warning at all, but the issue persists (unfortunately I don't have a Windows installation available, but it happens on a few different Linux machines, with compilers ranging between 15 and 19.1.0.166).  I guess I'm back to wondering why the compiler lets me call a subroutine that doesn't officially exist without telling me that I'm dumb.  But regardless, even if 'system' worked perfectly I'd definitely prefer to be using standard routines and would want to move to 'execute_command_line' anyway.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,382 Views

>> there is no warning at all... compiler lets me call a subroutine that doesn't officially exist without telling me that I'm dumb

When your program contains:

    call someNameHereSuchAsSYSTEMOrMeaninglessName(arg1, arg2, arg3)

The compiler required to assume you supplied a known external procedure (subroutine) name that will be found, or not found, by the linker (and in this case the function name is found and being a function it supplies a return argument, and in this case, the compiler version is unaware of the return value). In the external procedure case, it is your responsibility to provide the correct arguments (assuming you did not specify, or USE a module, that specifies the interface).

In this case, the external procedure name matches an internal (non-standard) function name. Because you have "call ..." the compiler has been told to select an external procedure (subroutine) name. It is your responsibility to provide language compliant code...
... even in situations where an older compiler may have produced working code with non-compliant sources.

What you have is non-conformant code. In such cases you get undefined behavior.

Jim Dempsey

0 Kudos
GVautier
New Contributor II
1,382 Views

It's the purpose of interface checking to avoid these errors. That was happening frequently before that checking functionality with old compilers.

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,382 Views

Kevin T. wrote:

I guess I'm back to wondering why the compiler lets me call a subroutine that doesn't officially exist without telling me that I'm dumb.  

The compiler doesn't know. Fortran has a feature called "implicit interface" that assumes a subroutine or function exists that is compatible with how you called it, unless you do so in a manner that requires an explicit interface. You can read my old blog post for more on that.

Normally, you would get an error at link (ld) time if the routine really didn't exist. But here, a routine by the appropriate name DID exist, just not the one you wanted. You caused this to happen by using "-assume nounderscore". On Linux/Mac, the convention is that Fortran routines have an underscore at the end of their names, C routines don't. Because the convention is also to downcase names on those platforms, and one is also linking to the C library (glibc in your case), the C "system" routine is pulled in rather than the Fortran "system_", and this triggers the behavior you saw.

As I wrote above, when you used -warn you got -warn external, which causes the compiler to require a declaration that a name is an external procedure. You would have seen the same bad behavior, without the warning, if you had added "external system" and still used -assume nounderscore.

0 Kudos
Steve_Lionel
Honored Contributor III
1,382 Views

I will add my usual observation that compiler options that change calling and naming conventions are evil and should be avoided if at all possible. This includes -names, -iface and -assume nounderscore. Instead, use an explicit interface with either BIND(C, NAME=) if possible, or !DEC$ ATTRIBUTES if not.

0 Kudos
Kevin_T_
Beginner
1,382 Views

Steve Lionel (Ret.) (Blackbelt) wrote:

I will add my usual observation that compiler options that change calling and naming conventions are evil and should be avoided if at all possible.

I definitely don't disagree; unfortunately I'm in the position where I have Code A that needs to interface with Code B, and that's the way it's been decided that's going to happen. 

On the plus side, though, making it work has highlighted lots of non-standards conforming source and other problems that have flown under the radar until now.  For example, the 'system' calls above appear to work "just fine" when compiled without "-assume nounderscore".  It was only when I started digging into why things were breaking with "-assume nounderscore" that I started finding some silly things being done in areas of the code that have been "working fine" and left alone for years.

Thank you for the thoughts, everyone; I've learned a few new tricks, and that's always a good day.

0 Kudos
Reply