PyForTool
Python-fortran-tool
Loading...
Searching...
No Matches
openacc.py
1"""
2This module implements the Openacc class containing the methods relative to openacc
3"""
4
5import re
6
7from pyfortool.util import debugDecor, n2name, alltext, tag
8from pyfortool.expressions import createElem, createExpr
9
10
11class Openacc():
12 """
13 Methods relative to openacc
14 """
15 @debugDecor
16 def removeACC(self):
17 """
18 Remove openACC directives
19 """
20 self.removeComments(exclDirectives=[],
21 pattern=re.compile(r'^\!\$ACC ', re.IGNORECASE))
22
23 @debugDecor
25 """
26 Remove macro !$mnh_(un)def(OPENACC) and !$mnh_(un)def(LOOP) directives
27 for other compiler than Cray
28 """
29
30 self.removeComments(exclDirectives=[],
31 pattern=re.compile(r'^\!\$mnh_undef\‍(LOOP\‍)'))
32 self.removeComments(exclDirectives=[],
33 pattern=re.compile(r'^\!\$mnh_undef\‍(OPENACC\‍)'))
34 self.removeComments(exclDirectives=[],
35 pattern=re.compile(r'^\!\$mnh_define\‍(LOOP\‍)'))
36 self.removeComments(exclDirectives=[],
37 pattern=re.compile(r'^\!\$mnh_define\‍(OPENACC\‍)'))
38
39 @debugDecor
41 """
42 By pass a bug of the CRAY compiler in which the vectorisation is not done with
43 BR_ fonctions use or locally.
44 On all expanded compute kernels with !$acc loop independent collapse(X) placed:
45 - if BR_ fonction is used : !$acc loop independent collapse(X) is removed and
46 the nested DO loops are factorised into DO CONCURRENT
47 - if a mnh_undef(OPENACC) macro is in place, !$acc loop collapse independant(X)
48 is removed
49 - if a mnh_undef(LOOP) macro is in place the nested DO loops are factorised into
50 DO CONCURRENT
51 """
52 def checkPresenceofBR(node):
53 """Return True if a BR_ (math BIT-REPRODUCTIBILITY) function is present in the node"""
54 mathBRList = ['ALOG', 'LOG', 'EXP', 'COS', 'SIN', 'ASIN', 'ATAN', 'ATAN2',
55 'P2', 'P3', 'P4']
56 namedE = node.findall('.//{*}named-E/{*}N/{*}n')
57 for el in namedE:
58 if alltext(el) in ['BR_' + e for e in mathBRList]:
59 return True
60 return False
61
62 def getStatementsInDoConstruct(node, savelist):
63 for sNode in node:
64 if 'do-construct' in tag(sNode):
65 getStatementsInDoConstruct(sNode, savelist)
66 elif 'do-stmt' not in tag(sNode):
67 savelist.append(sNode)
68
69 useNestedLoops = True # Cray compiler needs nested loops by default
70 useAccLoopIndependent = True # It needs nested !$acc loop independent collapse(X)
71 toremove = [] # list of nodes to remove
72 comments = self.findall('.//{*}C')
73
74 for comment in comments:
75 if comment.text.startswith('!$mnh_undef(LOOP)'):
76 useNestedLoops = False # Use DO CONCURRENT
77 if comment.text.startswith('!$mnh_undef(OPENACC)'):
78 useAccLoopIndependent = False
79
80 if comment.text.startswith('!$mnh_define(LOOP)'):
81 useNestedLoops = True # Use DO CONCURRENT
82 if comment.text.startswith('!$mnh_define(OPENACC)'):
83 useAccLoopIndependent = True
84
85 if comment.text.startswith('!$acc loop independent collapse('):
86 # Get the statements content in the DO-construct
87 par = self.getParent(comment)
88 ind = list(par).index(comment)
89 nestedLoop = par[ind + 1]
90 statements = []
91 getStatementsInDoConstruct(nestedLoop, statements)
92
93 # Check presence of BR_ within the statements
94 isBRPresent = False
95 for stmt in statements:
96 if checkPresenceofBR(stmt):
97 isBRPresent = True
98 break
99
100 # Remove !$acc loop independent collapse
101 # if BR_ is present or if !$mnh_undef(OPENACC)
102 if not useAccLoopIndependent or isBRPresent:
103 toremove.append((self, comment))
104
105 # Use DO CONCURRENT instead of nested-loop if BR_ is present or if !$mnh_undef(LOOP)
106 if not useNestedLoops or isBRPresent:
107 # Determine the table of indices
108 doStmt = nestedLoop.findall('.//{*}do-stmt')
109 table = {}
110 for do in reversed(doStmt):
111 table[do.find('.//{*}do-V/{*}named-E/{*}N/{*}n').text] = \
112 [alltext(do.find('.//{*}lower-bound')),
113 alltext(do.find('.//{*}upper-bound'))]
114
115 # Create the do-construct
116 inner, outer, _ = self.createDoConstruct(table,
117 indent=len(nestedLoop.tail),
118 concurrent=True)
119
120 # Insert the statements in the new do-construct
121 for stmt in statements:
122 inner.insert(-1, stmt)
123
124 # Insert the new do-construct and delete all the old do-construct
125 par.insert(ind, outer)
126 toremove.append((par, nestedLoop))
127
128 # Suppression of nodes
129 for parent, elem in toremove:
130 parent.remove(elem)
131
132 @debugDecor
133 def allocatetoHIP(self):
134 """
135 Convert (DE)ALLOCATE to (DE)ALLOCATE_HIP on variables only sent to the GPU via
136 !$acc enter data copyin
137 or
138 !$acc enter data create
139 This is necessary for using the managed memory with GPU AMD MI250X (on Adastra)
140 """
141 scopes = self.getScopes()
142 for scope in scopes:
143 varsToChange = []
144 comments = scope.findall('.//{*}C')
145 pointers = scope.findall('.//{*}pointer-a-stmt')
146
147 for coms in comments:
148 if ('!$acc enter data' in coms.text or '!$acc exit data' in coms.text) \
149 and coms.text.count('!') == 1:
150 if coms.text.count('(') == 1:
151 # !$acc enter data copyin( XRRS, XRRS_CLD ) ==> [' XRRS, XRRS_CLD ']
152 varsToChange.extend(coms.text.split(')')[0].split('(')[1:][0].split(','))
153 else:
154 # $acc exit data delete(xb_mg(level,m)%st) ==> xb_mg(level,m)%st
155 variableName = re.search(r'\‍(.*\‍)', coms.text).group(0)[1:-1]
156 varsToChange.insert(0, variableName)
157
158 if len(pointers) > 0:
159 for i, var in enumerate(varsToChange):
160 for pointer in pointers:
161 if alltext(pointer.find('.//{*}E-1/{*}named-E')) == var:
162 varsToChange[i] = alltext(pointer.find('.//{*}E-2/{*}named-E'))
163
164 for i, var in enumerate(varsToChange):
165 varsToChange[i] = var.replace(' ', '')
166
167 if len(varsToChange) > 0:
168 scope.addModuleVar([(scope.path, 'MODE_MNH_HIPFORT', None)])
169
170 allocateStmts = scope.findall('.//{*}allocate-stmt')
171 allocateStmts.extend(scope.findall('.//{*}deallocate-stmt'))
172
173 for stmt in allocateStmts:
174 varsChecking = ''
175 allocateArg = stmt.find('.//{*}arg-spec')
176 arrayR = allocateArg.find('.//{*}array-R')
177 if arrayR is None:
178 # Particular case of multiple parensR such as Tjacobi(level)%r(nz)
179 parensR = allocateArg.findall('.//{*}parens-R')
180 if len(parensR) == 2:
181 # It's a component case
182 varsChecking = alltext(allocateArg).split('%')[0]+'%' + \
183 alltext(allocateArg).split('%')[1].split('(')[0]
184 elif len(parensR) == 1:
185 # Tjacobi(level)%Sr case where Sr is a type itself
186 if allocateArg.find('.//{*}component-R'):
187 varsChecking = alltext(allocateArg)
188 else:
189 varsChecking = alltext(allocateArg).split('(')[0]
190 elif len(parensR) == 0:
191 varsChecking = alltext(allocateArg).split('(')[0]
192 else:
193 varsChecking = alltext(allocateArg).split('(')[0]
194
195 if varsChecking in varsToChange:
196 removeLastComma = True
197 stmt.text = "CALL MNH_HIP" + stmt.text # For allocate/deallocate statements
198 if tag(stmt) == 'allocate-stmt':
199 # Replace upper:lower into upper,lower
200 lowerBounds = allocateArg.findall('.//{*}lower-bound')
201 if lowerBounds is not None:
202 for bounds in lowerBounds:
203 bounds.tail = ','
204 arrayR = allocateArg.find('.//{*}array-R')
205 if arrayR is None:
206 # Particular case of multiple parensR such as Tjacobi(level)%r(nz)
207 parensR = allocateArg.findall('.//{*}parens-R')
208 if len(parensR) > 1:
209 parensR[-1].text = ','
210 else:
211 if allocateArg.find('.//{*}component-R'):
212 removeLastComma = False
213 else:
214 parensR[0].text = ','
215 else:
216 arrayR.text = ','
217 if removeLastComma:
218 allocateArg.tail = ''
219
220 @debugDecor
221 def addACCData(self):
222 """
223 1) Add after declaration:
224 !$acc data present ( list of intent arrays)
225 2) Add at the end of the routine
226 !$acc end data
227 """
228 scopes = self.getScopes()
229 if scopes[0].path.split('/')[-1].split(':')[1][:4] == 'MODD':
230 return
231 for scope in scopes:
232 # Do not add !$acc data directives to :
233 # - MODULE or FUNCTION object,
234 # - interface subroutine from a MODI
235 # but only to SUBROUTINES
236 if 'sub:' in scope.path and 'func' not in scope.path and 'interface' not in scope.path:
237 # Look for all intent arrays only
238 arraysIntent = []
239 for var in scope.varList:
240 # intent arrays, not of type TYPE (only REAL, INTEGER, CHARACTER)
241 if var['arg'] and var['as'] and 'TYPE' not in var['t'] and \
242 var['scopePath'] == scope.path:
243 arraysIntent.append(var['n'])
244 # Check if there is any intent variables
245 if len(arraysIntent) == 0:
246 break
247
248 # 1) First !$acc data present()
249 listVar = "!$acc data present ( "
250 count = 0
251 for var in arraysIntent:
252 if count > 6:
253 listVar = listVar + '\n!$acc & '
254 count = 0
255 listVar = listVar + var + ", &"
256 count += 1
257 listVarEnd = listVar[:-3] # remove last comma and &
258 accAddMultipleLines = createExpr(listVarEnd + ')')
259 idx = scope.insertStatement(scope.indent(accAddMultipleLines[0]), first=True)
260
261 # 2) multi-lines !$acc &
262 for iLine, line in enumerate(accAddMultipleLines[1:]):
263 scope.insert(idx + 1 + iLine, line)
264
265 # 3) !$acc end data
266 comment = createElem('C', text='!$acc end data', tail='\n')
267 scope.insertStatement(scope.indent(comment), first=False)
268
269 @debugDecor
270 def addACCRoutineSeq(self, stopScopes):
271 """
272 Adds the '!$acc routine (<name>) seq' directive
273 :param stopScopes: scope paths where we stop to add the directive
274 """
275 for scope in self.getScopes():
276 if self.tree.isUnderStopScopes(scope.path, stopScopes,
277 includeInterfaces=True,
278 includeStopScopes=True):
279 name = n2name(scope[0].find('.//{*}N')).upper()
280 acc = createElem('C', text=f'!$acc routine ({name}) seq',
281 tail=scope[0].tail)
282 scope[0].tail = '\n'
283 scope.insert(1, acc)
addACCRoutineSeq(self, stopScopes)
Definition openacc.py:270