【问题标题】:Are there some tools to check if a fortran procedure modifies its argument?是否有一些工具可以检查 fortran 过程是否修改了它的参数?
【发布时间】:2016-08-11 21:06:00
【问题描述】:

是否有可用于检查 fortran 过程的哪些参数在过程中定义或未定义的工具?我的意思是类似于词法分析器的东西,它只是检查变量是否在赋值(或等效)语句的左侧使用。 类似于为参数指定 intent(in) 时编译器所做的检查。

我遇到了一个主要用 fortran 77 标准(未指定意图)编写的代码,其子例程具有数百个参数,其中一些子例程每个扩展超过 5000 行代码。我想修改部分代码并重写长子程序。我认为,如果我能够追踪正在改变或没有改变的论点,那将很容易。

欢迎提出任何建议。

只是为了定义我的问题的限制并避免无用的讨论: 我知道可以通过调用其他子例程来修改变量。如果有一个工具可以检查给定过程中的直接修改,我可以手动处理。

【问题讨论】:

  • 我不知道有这样的工具,但你能在每个工具上敲下intent(in) 看看编译器抱怨的地方吗?是的,它不是万无一失或可自动化的,但如果你只需要快速破解......
  • 我担心如果它们在不同的源文件中,许多编译器可能无法捕获它。
  • 与现代代码相比,F77 中变量定义上下文的数量很少。那么,你关心所有代码中的所有定义可能性,还是只关心 LHS 或那些 F77?
  • 我重新标记了这个问题。词法分析是词法分析器所做的,从字符创建标记流。
  • IF 您的来源符合 F2008(包括大部分 F77)并且 IF 您可以访问 Fortran 2003 编译器(不包括当前的 gfortran)然后 here is the source for a little parsing utility 我把它放在一起(大约 - 正确地做到这一点需要对代码进行完整的语义分析)。其他 Fortran 解析工具集应该能够进行修改以执行类似的操作。关于我的例子的进一步讨论最好在其他地方完成 - c.l.f 或电子邮件。

标签: function parsing fortran arguments lexical-analysis


【解决方案1】:

为方便起见,这里有一个用于编译 Ian 的 VariableDefinitionContext 包的脚本。使用 -standard-semantics 使用 ifort-16.0 成功编译(gfortran-6.1 和 ifort-14 无法在语法支持不足的情况下编译...)

#!/usr/bin/env python
from __future__ import print_function
import os

with open( "compile-order.txt", "r" ) as f:
    tmp = f.read()
allsrc = tmp.split()

#cmd = "ifort -standard-semantics -warn -check all"
cmd = "ifort -standard-semantics"

obj = ""
for src in allsrc:
    print( "compiling", src )
    os.system( ( cmd + " -c %s" ) % ( src ) )
    obj += src[ :-4 ]+ ".o "
os.system( ( cmd + " %s" ) % ( obj ) )
# Usage: ./a.out test.f90

...但事实证明,下面的命令可以做同样的工作!! (感谢@IanH)

$ ifort -standard-semantics @compile-order.txt

FWIW,这是另一个用于打印(可能)修改变量的 Python 脚本。此脚本在 gfortran 转储文件中搜索各种符号。与 Ian 的包相比,只考虑了最小的 Fortran 语法集(直接赋值加上基本的读/写语句等)。

这种脚本的一个潜在用途是查找可能被修改的 COMMON 变量。以前,我在修改带有大量 COMMON 块的遗留 Fortran 程序方面有过一段艰难的经历,所以它可能对这种情况很有用......

#!/usr/bin/env python

from __future__ import print_function
import os, sys

def pushuniq( coll, item ):
    if not ( item in coll ): coll.append( item )

def getvarname( s, proc ):
    try:
        return s.split( proc + ":" )[ 1 ].split("(")[ 0 ].split("%")[ 0 ]
    except:  # ad-hoc!
        return s.split("(")[ 0 ].split("%")[ 0 ]

#------------------------------------------------------------------------
def varcheck( filename, Qwritedump=False ):
    """
    checks and prints potentially modified variables.
    Usage: varcheck.py <filenames>
    Set Qwritedump=True to write dump files.
    """
    #.........................................................
    # Generate gfortran dump file

    cmd = "gfortran -fdump-parse-tree -c %s"          # gfort >=4.7
    # cmd = "gfortran -fdump-fortran-original -c %s"  # gfort >=5

    with os.popen( cmd % ( filename ) ) as p:
        lines = p.readlines()

    base = '.'.join( filename.split('.')[:-1] )
    os.system( "rm -f %s.{o,mod}" % ( base ) )   # remove .o and .mod

    if Qwritedump:
        with open( "%s.dump" % ( filename ), "w" ) as f:
            f.write( ''.join( lines ) )
    #/

    #.........................................................
    # List of variables

    varlist = {}    # (potentially) modified variables
    arglist = {}    # dummy arguments
    comlist = {}    # common variables
    modlist = {}    # module variables
    reslist = {}    # result variables
    sublist = {}    # child subroutines
    namlist = {}    # namelists

    #.........................................................
    # Scan the dump file

    Qread = False
    Qgetarg = False

    for line in lines:

        word = line.split()
        if len( word ) == 0 : continue                # skip blank lines
        if word[ 0 ].isdigit() : word = word[ 1: ]    # remove line numbers

        key = word[ 0 ]

        if key == "Namespace:" : continue

        if key == "procedure":
            proc = word[ -1 ]

            varlist[ proc ] = []
            arglist[ proc ] = []
            comlist[ proc ] = []
            modlist[ proc ] = []
            reslist[ proc ] = []
            namlist[ proc ] = []
            sublist[ proc ] = []
            continue

        if key == "common:": continue
        if key == "symtree:": sym = word[ 1 ].strip("'").lower()

        # result variable
        if ( sym == proc ) and ( key == "result:" ):
            reslist[ proc ].append( word[ 1 ] )

        # dummy arguments
        if "DUMMY" in line:
            arglist[ proc ].append( sym )

        # common variables
        if "IN-COMMON" in line:
            comlist[ proc ].append( sym )

        # module variables
        if ( "VARIABLE" in line ) and ( "USE-ASSOC" in line ):
            modlist[ proc ].append( sym )

        # child subroutines
        if key == "CALL":
            pushuniq( sublist[ proc ], word[ 1 ] )

        # namelists
        if ( key == "READ" ) and ( "NML=" in line ):
            namlist[ proc ].append( word[ -1 ].split("NML=")[ -1 ] )

        # iostat
        if "IOSTAT=" in line:
            tmp = line.split("IOSTAT=")[ 1 ].split()[ 0 ]
            sym = getvarname( tmp, proc )
            pushuniq( varlist[ proc ], (sym, "iostat") )
        #/

        def addmemvar( op ):
            for v in word[ 1: ]:
                if proc in v:
                    sym = getvarname( v, proc )
                    pushuniq( varlist[ proc ], (sym, op) )

        # allocation
        if key == "ALLOCATE"    : addmemvar( "alloc" )
        if key == "DEALLOCATE"  : addmemvar( "dealloc" )
        if "move_alloc" in line : addmemvar( "move_alloc" )

        # search for modified variables
        if key == "READ"   : Qread = True
        if key == "DT_END" : Qread = False

        if ( key == "ASSIGN" ) or \
           ( Qread and ( key == "TRANSFER" ) ) or \
           ( key == "WRITE" and ( proc in word[ 1 ] ) ):

            if key == "ASSIGN"   : code = "assign"
            if key == "WRITE"    : code = "write"
            if key == "TRANSFER" : code = "read"

            sym = getvarname( word[ 1 ], proc )
            pushuniq( varlist[ proc ], (sym, code) )
        #/
    #/

    all_lists = { "var": varlist, "arg": arglist, "com": comlist,
                  "mod": modlist, "res": reslist, "sub": sublist,
                  "nam": namlist }

    #.........................................................
    # Print results

    for proc in varlist.keys():
        print( "-" * 60 )
        print( proc + ":" )

        for tag in [ "arg", "com", "mod", "res" ]:

            if tag == "arg":
                print( "    " + tag + ":", arglist[ proc ] )
            else:
                print( "    " + tag + ":" )

            for (sym, code) in varlist[ proc ]:
                if sym in all_lists[ tag ][ proc ]:
                    print( "        %-10s  (%s)" % (sym, code) )
            #/
        #/

        print( "    misc:" )
        for (sym, code) in varlist[ proc ]:
            if ":" in sym:
                print( "        %-10s  (%s)" % (sym, code) )
        #/

        print( "    call:", sublist[ proc ] )

        if len( namlist[ proc ] ) > 0:
            print( "    namelist:", namlist[ proc ] )
    #/

    return all_lists
#/

#------------------------------------------------------------------------
if __name__ == "__main__":

    if len( sys.argv ) == 1:
        sys.exit( "Usage: varcheck.py <filenames>" )
    else:
        filenames = sys.argv[ 1: ]

    for filename in filenames:
        varcheck( filename )
#/

示例 1:LAPACK zheev

$ ./varcheck.py zheev.f
------------------------------------------------------------
zheev:
    arg: ['jobz', 'uplo', 'n', 'a', 'lda', 'w', 'work', 'lwork', 'rwork', 'info']
        info        (assign)
        work        (assign)
        w           (assign)
        a           (assign)
    com:
    mod:
    res:
    call: ['xerbla', 'zlascl', 'zhetrd', 'dsterf', 'zungtr', 'zsteqr', 'dscal']

示例 2:简单的测试程序

!--------------------------------------------------------
module myvar
    character(50) :: str
    type mytype
        integer :: n
    endtype
    type(mytype) :: obj
    integer :: foo
end

!--------------------------------------------------------
subroutine mysub( a, b, c, ios, n, p, q, r )
    use myvar
    dimension b(10)
    common /com1/ dat( 50 ), x, y, z, wtf(1000)
    common /com2/ dat2( 50 )
    integer inp, out
    namelist /list/ str
    namelist /list2/ p, q
    inp = 10 ; out = 20

    open( inp, file="test.dat", status="old", iostat=ios10 )
    read( inp, *, iostat=ios ) a, ( b(i), i=3,5 )
    write( out, * ) "hello"
    read( inp, * ) c
    read( inp, list )
    close( inp, iostat=ios30 )

    write( str, "(f8.3)" ) a + c

    do i = 1, n
        dat( i ) = b( i )
    enddo
    x = p + q
    y = x * 2
100 c = dat( 1 ) + x + y
end

!--------------------------------------------------------
subroutine mysub2( &
        a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, &
        b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, &
        c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 )
    a3 = 3.0
    b5 = 5.0
end

!--------------------------------------------------------
function myfunc( x, m )
    common /com2/ dat2(50)
    common /com3/ dat3(50)
100 myfunc = x + dat2( m )
200 m = 5
    where( dat2 < 1.0 ) dat2 = 500.0
end

!--------------------------------------------------------
function myfunc2() result( res )
    use myvar
    implicit none
    integer :: res
    obj % n = 500
    res = obj % n
    call sub2( res )
end

!--------------------------------------------------------
subroutine myalloc( a, ier )
    implicit none
    integer, allocatable :: a(:), b(:)
    integer ier
    allocate( a( 10 ), b( 20 ), source=0, stat=ier )
end

!--------------------------------------------------------
subroutine mydealloc( a, b, ier )
    implicit none
    integer, allocatable :: a(:), b(:)
    integer ier
    deallocate( a, b, stat=ier )
end

!--------------------------------------------------------
subroutine mymovealloc( a, b )
    implicit none
    integer, allocatable :: a(:), b(:)
    call move_alloc( a, b )
end

!--------------------------------------------------------
program main
    use myvar
    implicit none
    integer a, dat
    common /com/ dat

    call mymain_int
    print *, a, dat, foo
contains
    subroutine mymain_int
        integer b
        a = 1
        b = 2
        dat = 100
        foo = 200
    end subroutine
end program

!--------------------------------------------------------
module mymod
    use myvar
    implicit none
    integer bar
contains
    subroutine mymod_sub
        use myvar
        integer a, dat
        common /com/ dat

        call mymod_sub_int
        bar = 300
        print *, a, dat, foo, bar
    contains
        subroutine mymod_sub_int
            integer b
            a = 1
            b = 2
            dat = 100
            foo = 200
        end subroutine
    end subroutine
end module

结果:

------------------------------------------------------------
mysub:
    arg: ['a', 'b', 'c', 'ios', 'n', 'p', 'q', 'r']
        ios         (iostat)
        a           (read)
        b           (read)
        c           (read)
        c           (assign)
    com:
        dat         (assign)
        x           (assign)
        y           (assign)
    mod:
        str         (write)
    res:
    call: []
    namelist: ['list']
------------------------------------------------------------
mysub2:
    arg: ['a1', 'a10', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'b1', 'b10', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'c1', 'c10', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9']
        a3          (assign)
        b5          (assign)
    com:
    mod:
    res:
    call: []
------------------------------------------------------------
myfunc:
    arg: ['m', 'x']
        m           (assign)
    com:
        dat2        (assign)
    mod:
    res:
        myfunc      (assign)
    call: []
------------------------------------------------------------
myfunc2:
    arg: []
    com:
    mod:
        obj         (assign)
    res:
        res         (assign)
    call: ['sub2']
------------------------------------------------------------
myalloc:
    arg: ['a', 'ier']
        ier         (alloc)
        a           (alloc)
    com:
    mod:
    res:
    call: []
------------------------------------------------------------
mydealloc:
    arg: ['a', 'b', 'ier']
        ier         (dealloc)
        a           (dealloc)
        b           (dealloc)
    com:
    mod:
    res:
    call: []
------------------------------------------------------------
mymovealloc:
    arg: ['a', 'b']
        a           (move_alloc)
        b           (move_alloc)
    com:
    mod:
    res:
    call: ['_gfortran_move_alloc']
------------------------------------------------------------
main:
    arg: []
    com:
    mod:
    res:
    misc:
    call: ['mymain_int']
------------------------------------------------------------
mymain_int:
    arg: []
    com:
    mod:
    res:
    misc:
        main:a      (assign)
        main:dat    (assign)
        main:foo    (assign)
    call: []
------------------------------------------------------------
mymod_sub:
    arg: []
    com:
    mod:
    res:
    misc:
        mymod:bar   (assign)
    call: ['mymod_sub_int']
------------------------------------------------------------
mymod_sub_int:
    arg: []
    com:
    mod:
    res:
    misc:
        mymod_sub:a    (assign)
        mymod_sub:dat  (assign)
        mymod_sub:foo  (assign)
    call: []

【讨论】:

  • python 脚本很棒。我已经接受了答案,因为这是我所要求的。我只是在一些测试程序上尝试它并且它正在工作。您实际上提供了比我预期更有价值的工具,例如常见的 bloc 变量、调用函数列表。我一定会在我的实际工作中使用它并给你反馈。我今天会做更多的测试,然后回来回答问题。
  • 没问题,其实我也一直在找这种工具(因为之前被一些大包严重困扰过)。我制作了这个社区维基,所以我希望任何人在必要时添加任何东西(例如,错误!!!)
  • 如果您不介意的话,您是否容易考虑全局变量?目前,当访问全局变量时程序会崩溃。
  • 你是对的。大多数全局变量在公共或模块中。有些可以在主程序中,并由主程序的包含部分的子程序使用;这些是我遇到的问题。
  • 现在脚本也适用于内部子程序的情况(以一种相当特别的方式!),但报告的“主机”名称可能不正确......在这种情况下,请不要介意:)
猜你喜欢
  • 1970-01-01
  • 2020-05-13
  • 1970-01-01
  • 2013-02-07
  • 2014-01-20
  • 1970-01-01
  • 1970-01-01
  • 2012-08-26
  • 1970-01-01
相关资源
最近更新 更多