Skip to content

Commit 336fdc4

Browse files
authored
Merge pull request #3458 from andrew-platt/b/fftpack_oneapi
FFTPACK 5.1: suppress default-real promotion for Intel and Flang builds
2 parents 2634c25 + 55194fb commit 336fdc4

5 files changed

Lines changed: 192 additions & 5 deletions

File tree

modules/nwtc-library/CMakeLists.txt

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ set(NWTCLIBS_SOURCES
9999

100100
# NetLib sources
101101
src/NetLib/fftpack/fftpack5.1.f
102+
src/NetLib/fftpack/fftpack_kind.f
102103
src/NetLib/scalapack/dlasrt2.f
103104
src/NetLib/scalapack/slasrt2.f
104105
src/NetLib/fftpack/NWTC_FFTPACK.f90
@@ -126,11 +127,49 @@ get_filename_component(FCNAME ${CMAKE_Fortran_COMPILER} NAME)
126127

127128
# FFTPACK 5.1 uses legacy Fortran 77 type punning (COMPLEX<->REAL) and must
128129
# NOT be promoted to double precision (its callers pass explicit SiKi/R4Ki arrays).
129-
# -Wno-pedantic is required because -pedantic (added for Debug builds) promotes the
130-
# argument mismatches back to errors, overriding -fallow-argument-mismatch.
130+
#
131+
# fftpack5.1.f declares all of its arrays as bare REAL/COMPLEX, but NWTC_FFTPACK.f90
132+
# hands it explicitly kinded REAL(SiKi)/COMPLEX(SiKi) buffers and passes their element
133+
# counts as LENSAV/LENWRK. If DOUBLE_PRECISION promotes the default REAL to 8 bytes in
134+
# these files, FFTPACK writes twice as many bytes as those buffers hold, silently
135+
# corrupting the heap and stack (observed as SIGBUS/SIGSEGV far from the FFT call).
136+
# fftpack_kind.f reports the kind actually used so NWTC_FFTPACK can verify it at run
137+
# time; it must therefore be compiled with exactly the same flags as fftpack5.1.f.
138+
#
139+
# The Visual Studio build does the same thing via RealKIND="realKIND4" on both files
140+
# in vs-build/modules/NWTC-Library.vfproj.
141+
set(FFTPACK_SOURCES
142+
src/NetLib/fftpack/fftpack5.1.f
143+
src/NetLib/fftpack/fftpack_kind.f
144+
)
131145
if (${CMAKE_Fortran_COMPILER_ID} STREQUAL "GNU")
132-
set_source_files_properties(src/NetLib/fftpack/fftpack5.1.f PROPERTIES COMPILE_FLAGS
146+
# -Wno-pedantic is required because -pedantic (added for Debug builds) promotes the
147+
# argument mismatches back to errors, overriding -fallow-argument-mismatch.
148+
set_source_files_properties(${FFTPACK_SOURCES} PROPERTIES COMPILE_FLAGS
133149
"-fallow-argument-mismatch -Wno-pedantic -fno-default-real-8 -fno-default-double-8")
150+
elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "^Intel")
151+
# Intel's -real-size 64 promotes REAL->REAL(8) *and* COMPLEX->COMPLEX(8), so it must be
152+
# undone here. -double-size 64 restores the Intel default so the genuine DOUBLE
153+
# PRECISION accumulators in fftpack5.1.f (DSUM, TPI, ARGH, ...) stay 8 bytes, matching
154+
# gfortran's -fno-default-double-8. Intel needs no -fallow-argument-mismatch analogue:
155+
# these are F77 externals with no explicit interfaces, so it never diagnoses them.
156+
if("${CMAKE_Fortran_COMPILER_VERSION}" VERSION_GREATER "19")
157+
if (WIN32)
158+
set(FFTPACK_REAL4_FLAGS "/real-size:32 /double-size:64")
159+
else()
160+
set(FFTPACK_REAL4_FLAGS "-real-size 32 -double-size 64")
161+
endif()
162+
else() # the hyphenated spelling above is only supported from version 19 onwards
163+
if (WIN32)
164+
set(FFTPACK_REAL4_FLAGS "/real_size:32 /double_size:64")
165+
else()
166+
set(FFTPACK_REAL4_FLAGS "-real_size 32 -double_size 64")
167+
endif()
168+
endif()
169+
set_source_files_properties(${FFTPACK_SOURCES} PROPERTIES COMPILE_FLAGS "${FFTPACK_REAL4_FLAGS}")
170+
elseif (${CMAKE_Fortran_COMPILER_ID} STREQUAL "Flang")
171+
set_source_files_properties(${FFTPACK_SOURCES} PROPERTIES COMPILE_FLAGS
172+
"-fno-default-real-8")
134173
endif()
135174

136175
# Recursive use of routine in qk61/dqk61 will trigger errors in debug

modules/nwtc-library/src/NetLib/fftpack/NWTC_FFTPACK.f90

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
! Also updated to check that transform has been initialized for the
88
! correct type (to avoid having wSave too small)
99
! ADP: 07/28/2014: Added in the complex FFT routines from fftpack v. 4.1
10+
! ADP: 08/15/2026: upgraded from fftpack v4.1 to v5.1 and added interfaces for 2D fft
1011
!=======================================================================
1112
MODULE NWTC_FFTPACK
1213
!-----------------------------------------------------------------------
@@ -674,6 +675,38 @@ SUBROUTINE ExitSINT(FFT_Data, ErrStat)
674675

675676
END SUBROUTINE ExitSINT
676677
!------------------------------------------------------------------------
678+
SUBROUTINE CheckFFTPACKRealKind( ErrStat )
679+
680+
! This subroutine verifies that fftpack5.1.f was compiled with a default REAL
681+
! kind of SiKi. FFTPACK 5.1 declares its arrays as bare REAL/COMPLEX, but this
682+
! wrapper hands it explicitly kinded REAL(SiKi)/COMPLEX(SiKi) buffers and passes
683+
! their element counts as LENSAV/LENWRK. If the build promotes the default REAL
684+
! to 8 bytes in fftpack5.1.f (DOUBLE_PRECISION does this by default, via
685+
! -fdefault-real-8 for GNU or -real-size 64 for Intel), FFTPACK writes twice as
686+
! many bytes as those buffers hold and silently corrupts the heap and stack.
687+
! See the FFTPACK_SOURCES block in modules/nwtc-library/CMakeLists.txt.
688+
689+
IMPLICIT NONE
690+
691+
INTEGER, INTENT(OUT),OPTIONAL :: ErrStat ! returns non-zero if an error occurred
692+
693+
INTEGER, EXTERNAL :: FFTPACK_REALKIND ! from src/NetLib/fftpack/fftpack_kind.f
694+
695+
696+
IF ( PRESENT(ErrStat) ) ErrStat = ErrID_None
697+
698+
IF ( FFTPACK_REALKIND() /= SiKi ) THEN
699+
CALL ProgAbort ( 'FFTPACK 5.1 was compiled with a default REAL kind of '// &
700+
TRIM(Num2LStr(FFTPACK_REALKIND()))//', but NWTC_FFTPACK requires '// &
701+
TRIM(Num2LStr(SiKi))//'. The build must suppress default-real promotion '// &
702+
'for fftpack5.1.f and fftpack_kind.f.', PRESENT(ErrStat) )
703+
IF ( PRESENT(ErrStat) ) ErrStat = ErrID_Fatal
704+
ENDIF
705+
706+
707+
END SUBROUTINE CheckFFTPACKRealKind
708+
!------------------------------------------------------------------------
709+
677710
SUBROUTINE InitCOST( NumSteps, FFT_Data, NormalizeIn, ErrStat )
678711

679712
! This subroutine initializes the cosine transform working space
@@ -694,6 +727,14 @@ SUBROUTINE InitCOST( NumSteps, FFT_Data, NormalizeIn, ErrStat )
694727

695728
IF ( PRESENT(ErrStat) ) ErrStat = ErrID_None
696729

730+
! Verify FFTPACK's default REAL kind matches this wrapper's (SiKi)
731+
732+
CALL CheckFFTPACKRealKind( ErrStat )
733+
IF ( PRESENT(ErrStat) ) THEN
734+
IF ( ErrStat >= AbortErrLev ) RETURN
735+
ENDIF
736+
737+
697738
! Number of timesteps in the time series returned from the cosine transform
698739
! N should be odd:
699740

@@ -763,6 +804,14 @@ SUBROUTINE InitCFFT( NumSteps, FFT_Data, NormalizeIn, ErrStat )
763804

764805
IF ( PRESENT(ErrStat) ) ErrStat = ErrID_None
765806

807+
! Verify FFTPACK's default REAL kind matches this wrapper's (SiKi)
808+
809+
CALL CheckFFTPACKRealKind( ErrStat )
810+
IF ( PRESENT(ErrStat) ) THEN
811+
IF ( ErrStat >= AbortErrLev ) RETURN
812+
ENDIF
813+
814+
766815
! Number of timesteps in the time series returned from the backward FFT
767816
! N should be even:
768817

@@ -832,6 +881,14 @@ SUBROUTINE InitFFT( NumSteps, FFT_Data, NormalizeIn, ErrStat )
832881

833882
IF ( PRESENT(ErrStat) ) ErrStat = ErrID_None
834883

884+
! Verify FFTPACK's default REAL kind matches this wrapper's (SiKi)
885+
886+
CALL CheckFFTPACKRealKind( ErrStat )
887+
IF ( PRESENT(ErrStat) ) THEN
888+
IF ( ErrStat >= AbortErrLev ) RETURN
889+
ENDIF
890+
891+
835892
! Number of timesteps in the time series returned from the backward FFT
836893
! N should be even:
837894

@@ -902,6 +959,14 @@ SUBROUTINE InitSINT( NumSteps, FFT_Data, NormalizeIn, ErrStat )
902959

903960
IF ( PRESENT(ErrStat) ) ErrStat = ErrID_None
904961

962+
! Verify FFTPACK's default REAL kind matches this wrapper's (SiKi)
963+
964+
CALL CheckFFTPACKRealKind( ErrStat )
965+
IF ( PRESENT(ErrStat) ) THEN
966+
IF ( ErrStat >= AbortErrLev ) RETURN
967+
ENDIF
968+
969+
905970
! Number of timesteps in the time series returned from the sine transform
906971
! N should be odd:
907972

@@ -994,6 +1059,14 @@ SUBROUTINE InitFFT2D( L, M, FFT_Data, NormalizeIn, ErrStat )
9941059

9951060
IF ( PRESENT(ErrStat) ) ErrStat = ErrID_None
9961061

1062+
! Verify FFTPACK's default REAL kind matches this wrapper's (SiKi)
1063+
1064+
CALL CheckFFTPACKRealKind( ErrStat )
1065+
IF ( PRESENT(ErrStat) ) THEN
1066+
IF ( ErrStat >= AbortErrLev ) RETURN
1067+
ENDIF
1068+
1069+
9971070
FFT_Data%L = L
9981071
FFT_Data%M = M
9991072

@@ -1155,6 +1228,14 @@ SUBROUTINE InitCFFT2D( L, M, FFT_Data, NormalizeIn, ErrStat )
11551228

11561229
IF ( PRESENT(ErrStat) ) ErrStat = ErrID_None
11571230

1231+
! Verify FFTPACK's default REAL kind matches this wrapper's (SiKi)
1232+
1233+
CALL CheckFFTPACKRealKind( ErrStat )
1234+
IF ( PRESENT(ErrStat) ) THEN
1235+
IF ( ErrStat >= AbortErrLev ) RETURN
1236+
ENDIF
1237+
1238+
11581239
FFT_Data%L = L
11591240
FFT_Data%M = M
11601241

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
C=======================================================================
2+
C FFTPACK_REALKIND
3+
C
4+
C Returns the default REAL kind that this file -- and therefore
5+
C fftpack5.1.f -- was compiled with.
6+
C
7+
C This file MUST be compiled with exactly the same real-size flags as
8+
C fftpack5.1.f. See the FFTPACK_SOURCES block in
9+
C modules/nwtc-library/CMakeLists.txt and the RealKIND="realKIND4"
10+
C file configurations in vs-build/modules/NWTC-Library.vfproj.
11+
C
12+
C FFTPACK 5.1 declares its arrays as bare REAL/COMPLEX, while the
13+
C NWTC_FFTPACK wrapper passes it explicitly kinded REAL(SiKi) and
14+
C COMPLEX(SiKi) buffers together with their element counts (LENSAV,
15+
C LENWRK). A build that promotes the default REAL to 8 bytes in
16+
C fftpack5.1.f -- which DOUBLE_PRECISION does by default via
17+
C -fdefault-real-8 (GNU) or -real-size 64 (Intel) -- makes FFTPACK
18+
C write twice as many bytes as those buffers hold. Nothing diagnoses
19+
C that at compile time, and the resulting heap/stack corruption
20+
C surfaces as a SIGBUS or SIGSEGV far away from the FFT call.
21+
C
22+
C NWTC_FFTPACK calls this at initialization and aborts with a clear
23+
C message if the answer is not SiKi, so a build system that forgets
24+
C the flag fails loudly instead of corrupting memory.
25+
C=======================================================================
26+
INTEGER FUNCTION FFTPACK_REALKIND ()
27+
FFTPACK_REALKIND = KIND(1.0)
28+
RETURN
29+
END

modules/nwtc-library/tests/test_NWTC_FFTPACK.F90

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module test_NWTC_FFTPACK
1717
subroutine test_NWTC_FFTPACK_suite(testsuite)
1818
type(unittest_type), allocatable, intent(out) :: testsuite(:)
1919
testsuite = [ &
20+
new_unittest("FFTPACK_real_kind", test_fftpack_real_kind), &
2021
new_unittest("FFT_roundtrip", test_fft_roundtrip), &
2122
new_unittest("FFT_forward_known", test_fft_forward_known), &
2223
new_unittest("FFT_forward_sign", test_fft_forward_sign), &
@@ -32,6 +33,17 @@ subroutine test_NWTC_FFTPACK_suite(testsuite)
3233
]
3334
end subroutine
3435

36+
! FFTPACK must be compiled with 4-byte default REAL, even in a DOUBLE_PRECISION
37+
! build: the NWTC_FFTPACK wrapper hands it REAL(SiKi) wSave/wWork buffers sized in
38+
! 4-byte elements. A build that forgot to suppress default-real promotion for
39+
! fftpack5.1.f would have FFTPACK write 8-byte elements into them and corrupt memory.
40+
subroutine test_fftpack_real_kind(error)
41+
type(error_type), allocatable, intent(out) :: error
42+
integer, external :: FFTPACK_REALKIND
43+
44+
call check(error, FFTPACK_REALKIND(), SiKi)
45+
end subroutine
46+
3547
! Forward then backward (no normalization) gives x*N
3648
subroutine test_fft_roundtrip(error)
3749
type(error_type), allocatable, intent(out) :: error

vs-build/modules/NWTC-Library.vfproj

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,32 @@
122122
<File RelativePath="..\..\modules\nwtc-library\src\NetLib\slatec\dqk61.f"/>
123123
<File RelativePath="..\..\modules\nwtc-library\src\NetLib\slatec\fdump.f"/>
124124
<File RelativePath="..\..\modules\nwtc-library\src\NetLib\fftpack\fftpack5.1.f">
125+
<FileConfiguration Name="Double_Debug|x64">
126+
<Tool Name="VFFortranCompilerTool" WarnInterfaces="false" RealKIND="realKIND4"/>
127+
</FileConfiguration>
128+
<FileConfiguration Name="Double_Release|x64">
129+
<Tool Name="VFFortranCompilerTool" WarnInterfaces="false" RealKIND="realKIND4"/>
130+
</FileConfiguration>
131+
<FileConfiguration Name="Double_OpenMP_Release|x64">
132+
<Tool Name="VFFortranCompilerTool" WarnInterfaces="false" RealKIND="realKIND4"/>
133+
</FileConfiguration>
134+
<FileConfiguration Name="Matlab_Release|x64">
135+
<Tool Name="VFFortranCompilerTool" WarnInterfaces="false" RealKIND="realKIND4"/>
136+
</FileConfiguration>
137+
<FileConfiguration Name="Matlab_Debug|x64">
138+
<Tool Name="VFFortranCompilerTool" WarnInterfaces="false" RealKIND="realKIND4"/>
139+
</FileConfiguration>
140+
<FileConfiguration Name="Release|x64">
141+
<Tool Name="VFFortranCompilerTool" WarnInterfaces="false" RealKIND="realKIND4"/>
142+
</FileConfiguration>
143+
<FileConfiguration Name="Debug|x64">
144+
<Tool Name="VFFortranCompilerTool" WarnInterfaces="false" RealKIND="realKIND4"/>
145+
</FileConfiguration>
146+
<FileConfiguration Name="OpenMP_Release|x64">
147+
<Tool Name="VFFortranCompilerTool" WarnInterfaces="false" RealKIND="realKIND4"/>
148+
</FileConfiguration>
149+
</File>
150+
<File RelativePath="..\..\modules\nwtc-library\src\NetLib\fftpack\fftpack_kind.f">
125151
<FileConfiguration Name="Double_Debug|x64">
126152
<Tool Name="VFFortranCompilerTool" RealKIND="realKIND4"/>
127153
</FileConfiguration>
@@ -218,6 +244,8 @@
218244
<Filter Name="ranlux">
219245
<File RelativePath="..\..\modules\nwtc-library\src\ranlux\RANLUX.f90"/>
220246
</Filter>
247+
<File RelativePath="..\..\modules\nwtc-library\src\GridInterp.f90"/>
248+
<File RelativePath="..\..\modules\nwtc-library\src\GridInterp_Types.f90"/>
221249
<File RelativePath="..\..\modules\nwtc-library\src\JSON.f90"/>
222250
<File RelativePath="..\..\modules\nwtc-library\src\KdTree.f90"/>
223251
<File RelativePath="..\..\modules\nwtc-library\src\ModMesh.f90"/>
@@ -257,8 +285,6 @@
257285
</File>
258286
<File RelativePath="..\..\modules\nwtc-library\src\VTK.f90"/>
259287
<File RelativePath="..\..\modules\nwtc-library\src\YAML.f90"/>
260-
<File RelativePath="..\..\modules\nwtc-library\src\GridInterp.f90"/>
261-
<File RelativePath="..\..\modules\nwtc-library\src\GridInterp_Types.f90"/>
262288
</Filter>
263289
</Files>
264290
<Globals/>

0 commit comments

Comments
 (0)