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

Has anyone else had a duplicate symbol link error using IFX and OpenMP?

Valued Contributor I

I have a (somewhat cutdown) VS2019 solution using oneAPI 2022.1 that contains a DLL with two static library dependencies that produces a couple of duplicate symbol errors when linking:

1>------ Build started: Project: iw2d_sim1D (IFX), Configuration: Debug x64 ------
1>lld-link: error: duplicate symbol: .omp.mold_ctor
1>>>> defined at Debug.x64/iw2d_1D_process.obj
1>>>> defined at iw2d_simlib.lib(iw2d_type_model.obj)
1>lld-link: error: duplicate symbol: .omp.dtor
1>>>> defined at Debug.x64/iw2d_1D_process.obj
1>>>> defined at iw2d_simlib.lib(iw2d_type_model.obj)
1>Build log written to "file://C:\Source\repos\oneAPI_2022_1_5\infoworks_2d\iw_2d_sim\iw2d_sim1D\Debug.x64\BuildLog.htm"
1>iw2d_sim1D - 2 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 2 up-to-date, 0 skipped =========

  1. I can't share the solution as is, because the code is commercial confidential and could do with much more cutting down to submit as a reproducer, in my opinion.
  2. (DLL) iw2d_sim1D depends on (static libraries) iw2d_simlib & simlib.
  3. simlib is compiled with IFORT, the other two projects with IFX.
  4. Both iw2d_sim1D & iw2d_simlib contain OpenMP directives and are compiled with /Qiopenmp (Fortran->Language->OpenMP support->Generate Parallel Code).
  5. The solution links successfully if I disable OpenMP support in iw2d_sim1D , but this would lead to degraded performance, so wouldn't be desirable.  In any case, see point 6 below.
  6. The original full solution uses IFORT and links/works fine with 2021.3 and 2021.5.

Has anybody encountered anything similar and submitted it to Intel Support?  If not, I'll try to produce a more suitable reproducer.

As an aside, my experience so far with IFX has not been positive: I have 4 open support cases and there are at least another couple of issues I could report, if I get time to create reproducers.  To be honest, I have only got round to testing IFX with our code in 2022.1 because the previous versions didn't support the subset of Fortran 2003 and 2008 we use.  I don't think our code is particularly esoteric and compiles fine with GNU Fortran as well as IFORT.

0 Kudos
5 Replies

The Intel OpenMP developers told us to use /Qopenmp, instead of, /Qiopenmp. You'll get some Intel secret sauce for better performance especially for using the GPU.

I have no idea if that relates to your issue though.


0 Kudos
Valued Contributor I

@Barbara_P_Intel I don't think the use of /Qopenmp v /Qiopenmp is relevant here.  The original ifort project used /Qopenmp and had the same duplicate symbol issue when I changed the relevant projects to use IFX.

I switched to using /Qiopenmp to see if that would help and also because the release notes suggest "-fopenmp is deprecated and may be removed in a future release.  Use -fiopenmp instead."

0 Kudos

I don't know that /Qopenmp v /Qiopenmp matters either. I just thought to add a bit of info.

According to the Fortran Developer Guide and Reference

  • Option -fopenmp has been deprecated, use option -qopenmp or option -fiopenmp

I'll clarify that in the Release Notes. (Glad to know that SOMEONE read those. )

0 Kudos

It's awkward, but try compiling iw2d_sim1D with ifort and Qopenmp and use that object file in your final executable or library to avoid the performance issues.

Now to the underlying problem.

These entry points are created for FIRSTPRIVATE variables when the datatype of the variable is a derived type with allocatable components.

Does this describe some of your data usage?

If so, what is the usage-pattern of this data; is it defined in a module that is USEd by both a iw2d_sim1D and by iw2d_simd.lib?  Or, are there multiple instances of the derived type,  separately defined/declared between the two entities?

I'm really just trying to find the "pattern" to make it easier to create a reproducer.

          Thanks --



0 Kudos
Valued Contributor I

@Lorri_M_Intel Thanks for the suggestions.   My current (still huge) reproducer no longer has any FIRSTPRIVATE variables, but it looks like the PRIVATE clause also creates those entry points if the variable is a derived type with allocatable components.

An example in the module iw2d_type_model that produces these entry points is

    subroutine model_apply_elem2_sub1(sub, array_size, aux, this)
    !$ use omp_lib
    procedure(elem_model_sub) :: sub
    integer, intent(in) :: array_size
    integer, dimension(array_size), intent(in) :: aux   
    type(model), intent(inout) :: this
    type(element), pointer :: elem_
    integer :: i
    integer :: threads
    logical :: do_in_parallel
    integer, parameter :: parallel_threshold = generic_parallel_threshold ! Enable parallel code path with > 1 thread
    integer :: num_data
        ! The following statement is only compiled when OpenMP directives are processed
        !$ threads = omp_get_max_threads()
        num_data = array_size
        do_in_parallel = (threads > 1) .and. (num_data > parallel_threshold)
        if (.not.do_in_parallel) threads = 1
        call iw2d_set_in_parallel(.true.)
        !$OMP parallel &
        !$OMP & if(do_in_parallel) &
        !$OMP & private (elem_) &
        !$OMP & shared (array_size, this)
        !$OMP do schedule(guided,128)
        do i = 1, array_size
            elem_ => model_get_elem(this, aux(i))
            call sub(elem_, this)
        end do
        !$OMP end do
        !$OMP end parallel
        call iw2d_set_in_parallel(.false.)        
    end subroutine

Type element contains allocatable components.  One way to eliminate the private clause and the generation of the entry points is to put the contents of the loop inside a contained subroutine with elem_ declared as a local variable in the contained subroutine instead of the host.  There is code in iw2d_sim1D that uses a similar idiom, but with a different derived type that contains allocatable components.

Another workaround would be to just call sub(model_get_elem(this, aux(i)), this) and eliminate the need for elem_.

I'll see if I can come up with a small reproducer based on this and submit it to support.

0 Kudos