10
0
mirror of https://gitlab.com/scemama/irpf90.git synced 2024-10-12 19:11:39 +02:00
irpf90/src/variable.py

703 lines
25 KiB
Python

#!/usr/bin/env python2
# IRPF90 is a Fortran90 preprocessor written in Python for programming using
# the Implicit Reference to Parameters (IRP) method.
# Copyright (C) 2009 Anthony SCEMAMA
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Anthony Scemama
# LCPQ - IRSAMC - CNRS
# Universite Paul Sabatier
# 118, route de Narbonne
# 31062 Toulouse Cedex 4
# scemama@irsamc.ups-tlse.fr
from irpf90_t import *
from util import *
import error
from command_line import command_line
class Variable(object):
############################################################
def __init__(self,text,label,name = None):
assert type(text) == list
assert len(text) > 0
assert type(text[0]) == Begin_provider
self.label = label
self.text = text
if name is not None:
self._name = name
############################################################
def is_touched(self):
if '_is_touched' not in self.__dict__:
from variables import variables
result = self.is_read
for i in self.children:
if variables[i].is_touched:
result = True
break
self._is_touched = result
return self._is_touched
is_touched = property(is_touched)
############################################################
def has_openmp(self):
if '_has_openmp' not in self.__dict__:
self._has_openmp = False
self.builder()
return self._has_openmp
has_openmp = property(has_openmp)
############################################################
def is_written(self):
if '_is_written' not in self.__dict__:
from variables import variables
result = False
for i in self.parents:
if variables[i].is_written:
result = True
break
self._is_written = result
return self._is_written
is_written = property(is_written)
############################################################
def is_read(self):
if '_is_read' not in self.__dict__:
from variables import variables
result = False
for i in self.parents:
if variables[i].is_read:
result = True
break
self._is_read = result
return self._is_read
is_read = property(is_read)
############################################################
def is_main(self):
if '_is_main' not in self.__dict__:
self._is_main = (self.name == self.same_as)
return self._is_main
is_main = property(is_main)
############################################################
def name(self):
'''Name is lowercase'''
if '_name' not in self.__dict__:
buffer = None
text = self.text
for line in text:
if type(line) == Begin_provider:
self._name = line.filename[1]
if "(" in self._name:
error.fail(line,"""'(' not allowed in variable name.
A ',' is probably missing between the name of the variable and the
dimension.
""")
break
return self._name
name = property(name)
############################################################
def doc(self):
if '_doc' not in self.__dict__:
text = self.text
buffer = filter(lambda l:type(l) == Doc, text)
self._doc = map(lambda l: l.text.lstrip()[1:], buffer)
if buffer == []:
error.warn(None,"Variable %s is not documented"%(self.name))
return self._doc
doc = property(doc)
############################################################
def documented(self):
if '_documented' not in self.__dict__:
self._documented = (self.doc != [])
return self._documented
documented = property(documented)
############################################################
def includes(self):
if '_includes' not in self.__dict__:
self._includes = []
text = self.text
for line in filter(lambda x: type(x) == Include,text):
self._includes.append(line.filename)
make_single(self._includes)
return self._includes
includes = property(includes)
############################################################
def calls(self):
if '_calls' not in self.__dict__:
self._calls = []
text = self.text
for line in filter(lambda x: type(x) == Call,text):
sub = line.text.split('(',1)[0].split()[1].lower()
self._calls.append(sub)
make_single(self._calls)
return self._calls
calls=property(fget=calls)
############################################################
def others(self):
if '_others' not in self.__dict__:
result = []
append = result.append
f = lambda l: type(l) in [Begin_provider, Cont_provider]
text = self.text
lines = filter(f, text)
for line in lines:
append(line.filename[1])
result.remove(self.name)
self._others = result
return self._others
others = property(others)
############################################################
def same_as(self):
if '_same_as' not in self.__dict__:
if type(self.line) == Begin_provider:
result = self.name
else:
result = self.text[0].filename[1]
self._same_as = result
return self._same_as
same_as = property(same_as)
############################################################
def allocate(self):
if '_allocate' not in self.__dict__:
if not self.is_main:
self._allocate = []
else:
from variables import variables
def f(var):
return variables[var].dim != []
self._allocate = filter ( f, self.others + [self.name] )
return self._allocate
allocate = property(allocate)
############################################################
def dim(self):
if '_dim' not in self.__dict__:
line = self.line.text.split('!')[0]
buffer = line.replace(']','').split(',',2)
if len(buffer) == 2:
self._dim = []
else:
buffer = buffer[2].strip()[1:-1].split(',')
self._dim = map(strip,buffer)
return self._dim
dim = property(dim)
############################################################
def type(self):
if '_type' not in self.__dict__:
line = self.line.text
buffer = line.split(',')[0]
try:
buffer = buffer.split('[')[1].strip()
except IndexError:
error.fail(None,"Error in definition of %s."%(self.name))
if self.dim != []:
buffer = "%s, allocatable"%(buffer)
self._type = buffer
return self._type
type = property(type)
############################################################
def fmodule(self):
if '_fmodule' not in self.__dict__:
self._fmodule = self.line.filename[0].replace('/','__').split('.irp.f')[0]+'_mod'
return self._fmodule
fmodule = property(fmodule)
############################################################
def regexp(self):
if '_regexp' not in self.__dict__:
import re
self._regexp = re.compile( \
r"([^a-z0-9'\"_]|^)%s([^a-z0-9_]|$)"%(self.name),re.I)
return self._regexp
regexp = property(regexp)
############################################################
def line(self):
if '_line' not in self.__dict__:
f = lambda l: type(l) in [Begin_provider, Cont_provider]
text = self.text
lines = filter(f, text)
for line in lines:
buffer = line.filename[1]
if self._name == buffer:
self._line = line
break
assert '_line' in self.__dict__
return self._line
line = property(line)
############################################################
def header(self):
if '_header' not in self.__dict__:
name = self.name
self._header = [ " %s :: %s %s"%(self.type, name, build_dim_colons(self) ) ]
if command_line.coarray:
if self.dim == []:
self._header[0] += " [*]"
else:
self._header[0] += " [:]"
if self.dim != [] and command_line.align != '1':
self._header += [" !DIR$ ATTRIBUTES ALIGN: %s :: %s"%(command_line.align,name)]
if self.is_main:
self._header += [ " logical :: %s_is_built = .False."%(name) ]
return self._header
header = property(header)
############################################################
def toucher(self):
if '_toucher' not in self.__dict__:
if not self.is_main:
self._toucher = []
else:
from modules import modules
from variables import variables
if '_needed_by' not in self.__dict__:
import parsed_text
parents = self.parents
parents.sort()
mods = map(lambda x: variables[x].fmodule, parents)
mods = make_single(mods)+[self.fmodule]
name = self.name
result = [ "subroutine touch_%s"%(name) ]
result += map(lambda x: " Use %s"%(x),mods)
result.append(" implicit none")
if command_line.do_debug:
length = str(len("touch_%s"%(name)))
result += [ " character*(%s) :: irp_here = 'touch_%s'"%(length,name) ]
if command_line.do_debug:
result += [ " call irp_enter(irp_here)" ]
result += map( lambda x: " %s_is_built = .False."%(x), parents)
result.append(" %s_is_built = .True."%(name))
if command_line.do_debug:
result.append(" call irp_leave(irp_here)")
result.append("end subroutine touch_%s"%(name))
result.append("")
self._toucher = result
return self._toucher
toucher = property(toucher)
##########################################################
def locker(self):
if '_locker' not in self.__dict__:
if not command_line.do_openmp:
self._locker = []
else:
from modules import modules
from variables import variables
name = self.name
result = [ "subroutine irp_lock_%s(set)"%(name) ]
result += [ " use omp_lib",
" implicit none",
" logical, intent(in) :: set",
" integer(kind=omp_nest_lock_kind),save :: %s_lock"%(name),
" integer,save :: ifirst",
]
if command_line.do_debug:
length = str(len("irp_lock_%s"%(name)))
result += [ " character*(%s) :: irp_here = 'irp_lock_%s'"%(length,name),
" call irp_enter(irp_here)" ]
result += [ " if (ifirst == 0) then",
" ifirst = 1",
" call omp_init_nest_lock(%s_lock)"%(name),
" endif",
" if (set) then",
" call omp_set_nest_lock(%s_lock)"%(name),
" else",
" call omp_unset_nest_lock(%s_lock)"%(name),
" endif",
]
if command_line.do_debug:
result.append(" call irp_leave(irp_here)")
result.append("end subroutine irp_lock_%s"%(name))
result.append("")
self._locker = result
return self._locker
locker = property(locker)
##########################################################
def reader(self):
if '_reader' not in self.__dict__:
if not self.is_main:
self._reader = []
else:
if '_needs' not in self.__dict__:
import parsed_text
from variables import variables
name = self.name
result = [ \
"subroutine reader_%s(irp_num)"%(name),
" use %s"%(self.fmodule),
" implicit none",
" character*(*), intent(in) :: irp_num",
" logical :: irp_is_open",
" integer :: irp_iunit" ]
if command_line.do_debug:
length = len("reader_%s"%(self.name))
result += [\
" character*(%d) :: irp_here = 'reader_%s'"%(length,name),
" call irp_enter(irp_here)" ]
result += map(lambda x: " call reader_%s(irp_num)"%(x),self.needs)
result += [ \
" irp_is_open = .True.",
" irp_iunit = 9",
" do while (irp_is_open)",
" irp_iunit = irp_iunit+1",
" inquire(unit=irp_iunit,opened=irp_is_open)",
" enddo"]
for n in [ name ]+self.others:
result += [\
" open(unit=irp_iunit,file='irpf90_%s_'//trim(irp_num),form='FORMATTED',status='OLD',action='READ')"%(n),
" read(irp_iunit,*) %s%s"%(n,build_dim_colons(variables[n])),
" close(irp_iunit)" ]
result += [ \
" call touch_%s"%(name),
" %s_is_built = .True."%(name) ]
if command_line.do_debug:
result.append(" call irp_leave(irp_here)")
result.append("end subroutine reader_%s"%(name))
result.append("")
self._reader = result
return self._reader
reader = property(reader)
##########################################################
def writer(self):
if '_writer' not in self.__dict__:
if not self.is_main:
self._writer = []
else:
from variables import variables
if '_needs' not in self.__dict__:
import parsed_text
name = self.name
result = [ \
"subroutine writer_%s(irp_num)"%(name),
" use %s"%(self.fmodule),
" implicit none",
" character*(*), intent(in) :: irp_num",
" logical :: irp_is_open",
" integer :: irp_iunit" ]
if command_line.do_debug:
length = len("writer_%s"%(self.name))
result += [\
" character*(%d) :: irp_here = 'writer_%s'"%(length,name),
" call irp_enter(irp_here)" ]
result += [ \
" if (.not.%s_is_built) then"%(self.same_as),
" call provide_%s"%(self.same_as),
" endif" ]
result += map(lambda x: " call writer_%s(irp_num)"%(x),self.needs)
result += [ \
" irp_is_open = .True.",
" irp_iunit = 9",
" do while (irp_is_open)",
" irp_iunit = irp_iunit+1",
" inquire(unit=irp_iunit,opened=irp_is_open)",
" enddo" ]
for n in [ name ] + self.others:
result += [\
" open(unit=irp_iunit,file='irpf90_%s_'//trim(irp_num),form='FORMATTED',status='UNKNOWN',action='WRITE')"%(n),
" write(irp_iunit,*) %s%s"%(n,build_dim_colons(variables[n])),
" close(irp_iunit)" ]
if command_line.do_debug:
result.append(" call irp_leave(irp_here)")
result.append("end subroutine writer_%s"%(name))
result.append("")
self._writer = result
return self._writer
writer = property(writer)
##########################################################
def free(self):
if '_free' not in self.__dict__:
name = self.name
result = [ "!","! >>> FREE %s"%(name),
" %s_is_built = .False."%(self.same_as) ]
if self.dim != []:
if command_line.do_memory:
result += [ \
" if (allocated(%s)) then"%(name),
" deallocate (%s)"%(name),
" print *, 'Deallocating %s'"%(name),
" endif" ]
else:
result += [ \
" if (allocated(%s)) then"%(name),
" deallocate (%s)"%(name),
" endif" ]
result.append("! <<< END FREE")
self._free = result
return self._free
free = property(free)
##########################################################
def provider(self):
if '_provider' not in self.__dict__:
if not self.is_main:
self._provider = []
else:
if '_to_provide' not in self.__dict__:
import parsed_text
from variables import variables, build_use, call_provides
name = self.name
same_as = self.same_as
def build_alloc(name):
self = variables[name]
if self.dim == []:
return []
def do_size():
result = " print *, ' size: ("
result += ','.join(self.dim)
return result+")'"
def check_dimensions():
result = map(lambda x: "(%s>0)"%(dimsize(x)), self.dim)
result = ".and.".join(result)
result = " if (%s) then"%(result)
return result
def dimensions_OK():
result = [ " irp_dimensions_OK = .True." ]
for i,k in enumerate(self.dim):
result.append(" irp_dimensions_OK = irp_dimensions_OK.AND.(SIZE(%s,%d)==(%s))"%(name,i+1,dimsize(k)))
return result
def do_allocate():
if command_line.coarray:
result = " allocate(%s(%s)[*],stat=irp_err)"
else:
result = " allocate(%s(%s),stat=irp_err)"
result = result%(name,','.join(self.dim))
if command_line.do_memory:
tmp = "\n print *, %s, 'Allocating %s(%s)'"
d = ','.join(self.dim)
result += tmp%('size('+name+')',name,d)
return result
result = [ " if (allocated (%s) ) then"%(name) ]
result += dimensions_OK()
result += [\
" if (.not.irp_dimensions_OK) then",
" deallocate(%s,stat=irp_err)"%(name),
" if (irp_err /= 0) then",
" print *, irp_here//': Deallocation failed: %s'"%(name),
do_size(),
" endif"]
if command_line.do_memory:
result += [\
" print *, 'Deallocating %s'"%(name) ]
result.append(check_dimensions())
result.append(do_allocate())
result += [\
" if (irp_err /= 0) then",
" print *, irp_here//': Allocation failed: %s'"%(name),
do_size(),
" endif",
" endif",
" endif",
" else" ]
result.append(check_dimensions())
result.append(do_allocate())
result += [\
" if (irp_err /= 0) then",
" print *, irp_here//': Allocation failed: %s'"%(name),
do_size(),
" endif",
" endif",
" endif" ]
return result
result = []
if command_line.directives and command_line.inline in ["all","providers"]:
result += [ "!DEC$ ATTRIBUTES FORCEINLINE :: provide_%s"%(name) ]
result += [ "subroutine provide_%s"%(name) ]
result += build_use( [same_as]+self.to_provide )
if command_line.do_openmp:
result += [" use omp_lib"]
result.append(" implicit none")
length = len("provide_%s"%(name))
result += [\
" character*(%d) :: irp_here = 'provide_%s'"%(length,name),
" integer :: irp_err ",
" logical :: irp_dimensions_OK",
"!$ integer :: nthreads"]
if command_line.do_openmp:
result.append(" call irp_lock_%s(.True.)"%(same_as))
if command_line.do_debug:
result.append(" call irp_enter(irp_here)")
result += call_provides(self.to_provide)
result += flatten( map(build_alloc,[self.same_as]+self.others) )
result += [ " if (.not.%s_is_built) then"%(same_as),
" call bld_%s"%(same_as),
" %s_is_built = .True."%(same_as), "" ]
result += [ " endif" ]
if command_line.do_debug:
result.append(" call irp_leave(irp_here)")
if command_line.do_openmp:
result.append(" call irp_lock_%s(.False.)"%(same_as))
result.append("end subroutine provide_%s"%(name) )
result.append("")
self._provider = result
return self._provider
provider = property(provider)
##########################################################
def builder(self):
if '_builder' not in self.__dict__:
if not self.is_main:
self._builder = []
else:
import parsed_text
from variables import build_use, call_provides
for filename,buffer in parsed_text.parsed_text:
if self.line.filename[0].startswith(filename):
break
text = []
same_as = self.same_as
inside = False
for vars,line in buffer:
if type(line) == Begin_provider:
if line.filename[1] == same_as:
inside = True
vars = []
if inside:
text.append( (vars,line) )
text += map( lambda x: ([],Simple_line(line.i,x,line.filename)), call_provides(vars) )
if command_line.do_profile and type(line) == Begin_provider:
text.append( ( [], Declaration(line.i," double precision :: irp_rdtsc, irp_rdtsc1, irp_rdtsc2",line.filename) ) )
text.append( ( [], Simple_line(line.i," irp_rdtsc1 = irp_rdtsc()",line.filename) ) )
if type(line) == End_provider:
if inside:
break
name = self.name
text = parsed_text.move_to_top(text,Declaration)
text = parsed_text.move_to_top(text,Implicit)
text = parsed_text.move_to_top(text,Use)
text = map(lambda x: x[1], text)
# inside_omp = False
for line in filter(lambda x: type(x) not in [ Begin_doc, End_doc, Doc], text):
if type(line) == Begin_provider:
result = []
if command_line.directives and command_line.inline in ["all","builders"]:
result += [ "!DEC$ ATTRIBUTES INLINE :: bld_%s"%(same_as) ]
result += [ "subroutine bld_%s"%(name) ]
result += build_use([name]+self.needs)
elif type(line) == Cont_provider:
pass
elif type(line) == End_provider:
if command_line.do_profile:
result += [ " irp_rdtsc2 = irp_rdtsc()" ,
" call irp_set_timer(%d,(irp_rdtsc2-irp_rdtsc1))"%self.label ]
result.append( "end subroutine bld_%s"%(name) )
break
elif type(line) == Openmp:
# Detect OpenMP blocks
buffer = line.text.lower().split()
if buffer[1] == "parallel":
# inside_omp = True
self._has_openmp = True
# if buffer[1] == "end" and buffer[2] == "parallel":
# inside_omp = False
result.append(line.text)
else:
# if inside_omp:
# if type(line) in [ Provide_all, Provide, Touch, SoftTouch ]:
# error.fail(line,str(type(line))+"is not allowed in an OpenMP block.")
result.append(line.text)
self._builder = result
return self._builder
builder = property(builder)
##########################################################
def children(self):
if '_children' not in self.__dict__:
if not self.is_main:
self._children = []
from variables import variables
if '_needs' not in self.__dict__:
import parsed_text
result = []
for x in self.needs:
result.append(x)
try:
result += variables[x].children
except RuntimeError:
pass # Exception will be checked after
self._children = make_single(result)
if self.name in result:
error.fail(self.line,"Cyclic dependencies:\n%s"%(str(self._children)))
return self._children
children = property(children)
##########################################################
def parents(self):
if '_parents' not in self.__dict__:
if not self.is_main:
self._parents = []
else:
from variables import variables
if '_needed_by' not in self.__dict__:
import parsed_text
result = []
for x in self.needed_by:
result.append(x)
try:
result += variables[x].parents
except RuntimeError:
pass # Exception will be checked after
self._parents = make_single(result)
if self.name in result:
error.fail(self.line,"Cyclic dependencies:\n%s"%(str(self._parents)))
return self._parents
parents = property(parents)
######################################################################
if __name__ == '__main__':
from preprocessed_text import preprocessed_text
from variables import variables
#for v in variables.keys():
# print v
def print_dot(x,done):
if x.children == []:
return
for i in x.needs:
pair = (x.name, i)
if pair not in done:
print "%s -> %s"%( x.name, i )
done[pair] = None
print_dot(variables[i],done)
print "digraph G { "
# print_dot(variables['e_loc'], {})
print_dot(variables['psi_value'], {})
print "}"