-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathacc2omp_converter.py
executable file
·420 lines (359 loc) · 15.8 KB
/
acc2omp_converter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
#!/usr/bin/python
# Author: Nichols A. Romero
# e-mail: [email protected]
# Argonne National Laboratory
# Python imports
import fileinput
import re
from shutil import copyfile
# Most common user configurable parameters
# Set to True for debugging and development purposes
debug = True
# Set to True to retain OpenACC directives in output
keepOpenACC = True
# Lists, dicts, and strings to aid in translation of OpenACC to OpenMP
# Note that the other way would be more difficult since OpenMP tends to
# be more verbose than OpenACC.
# In the variable names in the program, Dir stands for directive
# not directory.
ompDir = '!$omp'
accDir = '!$acc'
ompDirContinue = '!$omp&'
accDirContinue = '!$acc&'
nextLineContinue = '&'
emptyString = ''
singleSpaceString = ' '
transitionArrow = ' -> '
backupExtString = '.bak'
# directives without arguements
singleDirDict = {
'loop': 'parallel do',
'gang': emptyString,
'independent': emptyString,
'parallel': 'target teams distribute',
'vector': 'simd',
'routine': 'declare target',
'seq': emptyString,
'data': 'data',
'end': 'end',
'enter': 'target enter',
'exit': 'target exit',
'atomic': 'atomic',
'serial': 'target',
'declare': 'declare target',
}
dualDirDict = {}
# directives with arguements
singleDirwargsDict = {
'attach': 'map(to:',
'detach': 'map(from:',
'copy': 'map(tofrom:',
'copyin': 'map(to:',
'copyout': 'map(from:',
'create': 'map(alloc:',
'delete': 'map(release:',
'async': 'depend(out:',
'wait': 'task depend(in:',
'collapse': 'collapse(',
'private': 'private(',
'vector_length': 'simd simdlen(',
'num_gangs': 'num_teams(',
'present': emptyString,
}
dualDirwargsDict = {
'update host': 'target update from(',
'update device': 'target update to(',
}
def remove_extra_spaces(origString):
"""
Converter needs extra spaces before and after commas and parenthesis
removed in order work properly.
"""
# Space before and after a comma
newString = re.sub(' *, *', ',', origString)
# Space before and after left parenthesis
newString = re.sub(' *\( *', '(', newString)
# Space before and after right parenthesis
newString = re.sub(' *\) *', ')', newString)
# Add space back in for continuation symbol
newString = re.sub('\)&', ') &', newString)
# Add space back when newString is adjacent to another variable or
# directive
# \w means any single letter, digit or underscore
newString = re.sub('(\))(\w)', r'\1 \2', newString)
return newString
def add_space_after_commas(origString):
"""
Directives with arguements need spaces insert after commas.
"""
# space after a comma
newString = re.sub(',', ', ', origString)
return newString
if __name__ == "__main__":
# This list will contain the output buffer in a line-by-line breakup
entries = []
# Translate source file one line at a time
lines = fileinput.input()
for line in lines:
# Remove extraneous spaces, but we only use
# parsedLine for lines that actually contain directives
origLine = line
parsedLine = remove_extra_spaces(line)
line = parsedLine
if debug:
print "extra spaces extracted below:"
print line
# Four cases to consider when parsing a line:
# 1. Carriage return only
# 2. White space only
# 3. No OpenACC directive
# 4. Line containing an OpenACC directive
# First case is a line with only a CR
if len(line) == 0:
if debug:
print 'Carriage return only'
entries.append(origLine)
continue
# As long as the line is not empty (case #1), it can be
# parsed. We need an iterable object and enumerate object
# to aid in search for directives. We keep track of the
# length of the line as well as its left justification,
# but only use this when we are actually translating
# directives (case #4)
lenLine = len(line)
numLeftSpaces = lenLine - len(line.lstrip(singleSpaceString))
dirs = line.split()
lenDirs = len(dirs)
enumDirs = enumerate(dirs)
# Second case is a line with only a whitespace
if lenDirs == 0:
if debug:
print 'Blank line'
entries.append(origLine)
continue
# Third case is a line that contains no directive
# Use Booleans to keep track of when an OpenACC directive
# has been found, by default we assume there is no
# ACC directive present. Also allow for the possibility
# that uppercase is used for the OpenACC directive, though
# most people will use lowercase.
accDirFound = False
accDirContinueFound = False
if ((dirs[0].lower() != accDir) and
(dirs[0].lower() != accDirContinue)):
if debug:
print 'No OpenACC directive on this line'
entries.append(origLine)
continue
# Fourth case contains some OpenACC directive
# From this point forward, we assume that a directive has
# been found and we try to do a translation.
# We will either find an OpenACC directive or a continuation
# of an OpenACC directive. Check for both, but only one
# must be found.
if dirs[0].lower() == accDir: accDirFound = True
if dirs[0].lower() == accDirContinue: accDirContinueFound = True
# Detect whether they are using upper or lower case for the OpenACC
# directive. Depending on the capitalization of the first instance
# of an OpenACC pragma on that line will determine the
# capitalization of the rest of the line. Mixed capitalization will
# throw off this detection.
accDirUpperCase = dirs[0].isupper()
accDirLowerCase = dirs[0].islower()
if debug:
print "accDirUpperCase = ", accDirUpperCase
print "accDirLowerCase = ", accDirLowerCase
# Booleans cannot be both True or both False
assert (accDirFound != accDirContinueFound)
assert (accDirUpperCase != accDirLowerCase)
if debug:
print 'OpenACC directive present. Translating.'
print dirs
# These are the cases we consider
# 1. Directive pairs. These are pairs of directives that only have
# meaning in combinations. Thus, they must be translated in pairs.
# 2. Directive pairs with arguements.
# 3. Directive single with no arguements.
# 4. Directive single with scalar arguments.
# 5. Directive single with multi-dimensional array arguements.
# First find directive pairs, this is kludgy way to search through
# directives but we do pairs first because there is overlap between
# keywords among the different directive categories.
# Counters which are only reset at each iteration of outer loop
# NOTE: If present, totalDirsFound will count nextLineContinue symbol
dualDir = None
totalDirsFound = 0
# Booleans to keep track of what directive type has been found
# Need to be reset at each iteration of inner loop
singleDirFound = False
singleDirwargsFound = False
dualDirFound = False
dualDirwargsFound = False
dirwargsFound = False
for i, dir in enumDirs:
# first iteration just put the OMP directive or continuation
# version of it into a string and go to the next iteration
if i == 0:
newLine = singleSpaceString * numLeftSpaces
if accDirUpperCase:
ompDir = ompDir.upper()
ompDirContinue = ompDirContinue.upper()
else: # accDirLowerCase is True
ompDir = ompDir.lower()
ompDirContinue = ompDirContinue.lower()
if accDirFound:
newLine = newLine + ompDir
else: # accDirContinueFound is True
newLine = newLine + ompDirContinue
continue
# second iteration store the first pragma in the pair
if i == 1:
prevdir = dir
# Special detection needed for line continuation
if dir == nextLineContinue:
totalDirsFound = totalDirsFound + 1
newLine = newLine + singleSpaceString + nextLineContinue
# Additional logic would be necessary if examining
# triplets of directives
# take adjacent directives and create new key
# store previous two directives for next iteration
# if i > 1:
# dualDir = prevdir + singleSpaceString + currentDir
# prevdrevdir = prevdir
# prevdir = dir
# Some directives will have arguements, so we need to identify
# those. The maxsplit arguement to the split method in dirwards
# is needed to identify arrays properly. We split *only* on the
# first parenthesis from the left hand side.
#
# Note that currentDir and dualDir must be in lowercase for pattern
# matching purposes.
dirwargs = dir.split('(', 1)
lenDirwargs = len(dirwargs)
currentDir = dirwargs[0].lower()
dualDir = prevdir.lower() + singleSpaceString + currentDir
if lenDirwargs > 1: dirwargsFound = True # Boolean unused for now
if debug:
print 'dirwargs = ', dirwargs
print 'dirwargs[0] = currentDir = ', currentDir
print 'lenDirswargs = ', lenDirwargs
print 'dualDir =', dualDir
# identify which case we are in, only one can be true at any time
# Need the check on dualDir equal None, because it will not exist
# on iteration = 1.
if dualDir is not None:
if dualDir in dualDirDict:
print 'OpenACC Directive Dual with no argument found'
dualDirFound = True
if dualDir in dualDirwargsDict:
print 'OpenACC Directive Dual with argument found'
dualDirwargsFound = True
if currentDir in singleDirDict:
print 'OpenACC Directive Single with no argument found'
singleDirFound = True
if currentDir in singleDirwargsDict:
print 'OpenACC Directive Single with argument found'
singleDirwargsFound = True
# Tests that only one is true with XOR, if not, skip this iteration
# and look for a pair, otherwise, Continue
if not (singleDirFound ^ singleDirwargsFound ^
dualDirFound ^ dualDirwargsFound):
if debug:
print "Next Iteration will check for Dual Directives Found"
continue
else:
assert(singleDirFound ^ singleDirwargsFound ^
dualDirFound ^ dualDirwargsFound)
# Code below generates the new directives depending on the value
# of the boolean, probably need a function instead.
# (single) directive with no arguements
if singleDirFound:
totalDirsFound = totalDirsFound + 1
if debug:
print 'OpenACC Directive Single with no argument found'
newDir = singleDirDict[currentDir]
if newDir == emptyString: continue
if accDirUpperCase: newDir = newDir.upper()
if debug: print currentDir + transitionArrow + newDir
newLine = newLine + singleSpaceString + newDir
# (single) directive with an arguement
if (lenDirwargs > 1) and singleDirwargsFound:
totalDirsFound = totalDirsFound + 1
if debug: print 'OpenACC Directive Single with argument found'
newDir = singleDirwargsDict[currentDir]
if newDir == emptyString: continue
if accDirUpperCase: newDir = newDir.upper()
newLine = newLine + singleSpaceString + newDir
# for-loop handles the arguement component
for j in range(1, lenDirwargs):
newDir = dirwargs[j]
if debug: print currentDir + transitionArrow + newDir
newLine = newLine + newDir
# (pair) directive with no arguement
if dualDirFound:
totalDirsFound = totalDirsFound + 2
if debug:
print 'OpenACC Directive Dual with no arguement found'
newDir = dualDirDict[dualDir]
if newDir == emptyString: continue
if accDirUpperCase: newDir = newDir.upper()
if debug:
print dualDir + transitionArrow + newDir
newLine = newLine + singleSpaceString + newDir
# (pair) directive with an arguement
if (lenDirwargs > 1) and dualDirwargsFound:
totalDirsFound = totalDirsFound + 2
if debug: print 'OpenACC Directive Dual with an argument'
newDir = dualDirwargsDict[dualDir]
if newDir == emptyString: continue
if accDirUpperCase: newDir = newDir.upper()
newLine = newLine + singleSpaceString + newDir
# for-loop handles the arguement component
for j in range(1, lenDirwargs):
newDir = dirwargs[j]
if debug: print dualDir + transitionArrow + newDir
newLine = newLine + newDir
# reset booleans for next iteration
singleDirFound = False
singleDirwargsFound = False
dualDirFound = False
dualDirwargsFound = False
dirwargsFound = False
# End of inner loop on `i`
# On last Loop iteration, check that you were able to translate
# all directives. The minus one in the first conditional takes into
# account the initial `!$acc` or `!$acc&` which is not counted.
# If the directive cannot be translated, keep line AS IS and
# output original line containing OpenACC.
if (totalDirsFound < (lenDirs - 1)):
if debug:
print 'lenDirs=', lenDirs
print 'totalDirsFound=', totalDirsFound
print 'OpenACC directive could not be translated.'
newLine = origLine
else:
if keepOpenACC: # append original line into the buffer
entries.append(origLine)
newLine = add_space_after_commas(newLine) + '\n'
# Finally we add the new line into the buffer
entries.append(newLine)
# End of outer loop on `line`
# We intentionally wait until the entire file is read because
# fileinput module will return None only after the entire file
# has been read.
# First we backup the file, we have to resort to using the sys
# module to get the filename because the filename() method
# returns None unless the entire file has been read.
currentFilename = lines.filename()
backupFilename = currentFilename + backupExtString
copyfile(currentFilename, backupFilename)
if debug:
print 'Current Filename: ', currentFilename
print 'Backup Filename: ', backupFilename
# Close the current open file
lines.close()
# Open a new file to write to that has the same source filename.
# Looks like the file is modified in-place, but this is not the
# case. Write the translated file to disk with the original filename
with open(currentFilename, 'w') as theFile:
theFile.write(''.join(entries))