Nvlink error: Undefined reference

When I compile the PGI fortran, I met this error, and I don’t konw how to solve it. What is wrong with it?

pgf90 -DP3D_SINGLE -DPG -DDBLE_PRECSN  -fast -g  -r8  -acc -ta=tesla -Minfo=accel -c setslave.F
pgf90 -DP3D_SINGLE -DPG -DDBLE_PRECSN  -fast -g  -r8  -acc -ta=tesla -Minfo=accel -c umalloc.F
pgf90 -DP3D_SINGLE -DPG -DDBLE_PRECSN  -fast -g -r8  -acc -ta=tesla -Minfo=accel -c fake.F
ar rusc libdist.a bc_blkint.o findmin_new.o plot3d.o rrest.o bc_patch.o forceout.o plot3t.o rrestg.o calyplus.o pointers.o setup.o writ_buf.o mgblk.o qinter.o prntcp
.o newalpha.o cputim.o patcher.o qout.o termn8.o dynptch.o plot3c.o resp.o usrint.o wrest.o wrestg.o pre_bc.o bc_embed.o updateg.o compg2n.o resetg.o bc_period.o yplusout.o sizer.o cfl3d.o trnsfr_vals.o updatedg.o ae_corr.o mgbl.o setslave.o umalloc.o fake.opgf90 -DP3D_SINGLE -DPG -DDBLE_PRECSN   -acc -ta=tesla -Minfo=accel -o cfl3d_seq ccomplex.o development.o main.o \
	libdist.a libcommon.a 
nvlink error   : Undefined reference to 'pre_blockbc_' in 'libdist.a:pre_bc.o'
nvlink error   : Undefined reference to 'pre_period_' in 'libdist.a:pre_bc.o'
nvlink error   : Undefined reference to 'pre_embed_' in 'libdist.a:pre_bc.o'

And the makefile is:

FSRC_SPEC = fake.F

FOBJ_DIST = $(FSRC_DIST:.F=.o)

FOBJ_SPEC = $(FSRC_SPEC:.F=.o)

DISTLIB = libdist.a

$(DISTLIB): $(FSRC_DIST) $(FOBJ_DIST) $(FSRC_SPEC) $(FOBJ_SPEC)
	ar $(AROPT) $(DISTLIB) $(FOBJ_DIST) $(FOBJ_SPEC)
	@$(RANLIB) $(DISTLIB)

I have add some OpenACC directives in pre_blockbc.F and compiled it with -acc option,but I don’t know how to let libdist.a identified it. The libdist.a was generated by the upper sentence.

Hi xll_bit,

The error is indicating that the device linker can’t find these device symbols. I’m assuming that these are subroutines? If so, can you check if you decorated them with the “routine” directive so the compiler know to create device versions of these routines?

If you do have the “routine” directive, are you naming the routine? If this is a pure Fortran program, you shouldn’t need to name the routine, but if it’s mixed C/Fortran, you may need to include a “bind” clause to get the name mangling between the languages correct.

Can you post some example code which shows how you’re declaring these routines as well as how you’re calling them from an OpenACC compute region?

-Mat

Hi, Mat
Thinks for your reply.
This is a pure Fortran program. And I have a clipped code of my projection.

      subroutine pre_bc(***)
!$acc routine(pre_patch,pre_blockbc,pre_period,pre_embed) seq
!$acc kernels
      do levl = 1,levt
         do 6909 nbl=1,nblock
            if (levl.ne.levelg(nbl)) go to 6909

            call pre_patch(***)
            call pre_blockbc(***)
            call pre_period(***)
            call pre_embed(***)
 
 6909    continue
      end do
!$acc end kernels

I have added some directives in pre_bc.F. And this subroutine call pre_patch, pre_blockbc, pre_period, pre_embed. Take pre_blockbc as a example. The following are some invocation relationships.

      subroutine pre_blockbc(***)
!$acc routine(termn8) seq

      do 100 n=1,abs(nbli)
         if (nblon(n) .ge. 0) then
            if (nbl.eq.nblk(1,n) .or. nbl.eq.nblk(2,n)) then
               do 101 iti = 1,itime
                  if (isva(ir,1,n)+isva(ir,2,n) .eq. 5) then
                  end if

                  if (icount.gt.2*mxbli) then
                     call termn8(myid,ierrflg,ibufdim,nbuf,bou,nou)
                  end if

  101          continue
            end if
         end if
  100 continue

      end if
      return
      end



      subroutine termn8(myid_stop,ierrflg,ibufdim,nbuf,bou,nou)
!$acc routine(outbuf,my_flush) seq

      if (ierrflg.eq.0) then
            write(99,1)
    1       format(/,' execution terminated normally')
      else 
         if (ierrflg.ne.-999) then

         else

         end if

         do nn=1,nbuf
            if (nou(nn).gt.0) then
               do kou = 1,nou(nn)
                  call outbuf(bou(kou,nn),99)
               end do
            end if
         end do
c
      end if
c
      call my_flush(3)

      stop
      end



      subroutine outbuf(str,iunit)
!$acc routine(my_flush) seq

      call my_flush(iunit)
      return
      end



      subroutine my_flush(iunit)
!$acc routine(flush) seq

#   ifdef PG
      call flush(iunit)
#   endif

      return
      end

flush seems to be a library function. Should I need to deal with flush?

Hi xll_blt,

      subroutine pre_blockbc(***) 
!$acc routine(termn8) seq

With this code, you’re declaring that “termn8” has a device callable version. But what you’re missing is a routine directive to tell the compiler to create a device routine for “pre_blockbc” itself. Try adding something like the following:

      subroutine pre_blockbc(***) 
!$acc routine seq

This is why you’re getting the undefined references.

Should I need to deal with flush?

In general, formatted stdout, stdin, and file I/O is not available on the device. So here, you will want to add some conditional compilation to avoid the call to flush as well as formatted write.

Though, I’m thinking that you’ll want to avoid calling “temn8” from the device at all. From what I can tell, it looks to end execution of the program and output results to either stdout or a file. You’ll want to rethink your algorithm to see if you can move the termination and output after the kernels region so it can be performed on the host.

Hope this helps,
Mat

Hi Mat,
I have added some routine directive as your said.

subroutine pre_blockbc(***)
!$acc routine(termn8) seq

But now, I have meet some other mistakes.

PGF90-S-0155-Common block variables are not supported in acc routine - is (pre_embed.F: 177)

and the program has lots of common variables. How can I modify it quickly?

Unfortunately, as far as I’m aware, the most straightforward way to remedy this is to change the common blocks into a module variable. Limitations in the device linker make it such that we don’t support common blocks in device code.