Skip to content

Commit

Permalink
Merge pull request #17 from naromero77/develop
Browse files Browse the repository at this point in the history
Production Release 0.2.3
  • Loading branch information
naromero77 authored May 18, 2021
2 parents b3e030c + 10c2335 commit dfca2ba
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 34 deletions.
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,41 @@

Notable changes to ACC2OMP are documented in this file

## [0.2.3] - 2021-05-18

Minor enhancements:
- Support additional common OpenACC directives: atomic, serial, declare, update host/device
- Improve internal code documentation

Bug fix:
- keepOpenACC codepath was not working
- Issue with line continue symbols for multi-line directives

## [0.2.2] - 2020-03-10

Minor enhancements:
- Support OpenACC present directive
- More robust handling of directives that translates into nothing

Bug fix:
- Arguements to directives were being forced into lowercase

## [0.2.1] - 2020-01-23

Minor enhancements:
- Original OpenACC directives can be maintained in output.
- Prettify formatting when commas are present.

## [0.2] - 2020-01-10

Production Release

Conversion tool hardened on at least one real science code.
Previously, lines with non-supported directives where completely
deleted from source-2-source translation. Instead we now detect
lines that are only partially translated and retain the original
OpenACC.

## [0.1] - 2019-08-01

Initial Release
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ A number of diagnostic output is written to the standard out. It can be surpress

# Known limitations
- OpenACC -> OpenMP directive mapping must be explicitly available in a dictionary
- OpenACC async is not handled properly because of fundamental differences between OpenACC and OpenMP
- OpenACC async is not handled properly because of fundamental differences between OpenACC and OpenMP.
- Only handles Fortran
- No unit tests
- No enforcement of PEP8 formating on source code
- No enforcement of PEP8 formating on source code via CI

# Funding
This research was supported by the Exascale Computing Project (17-SC-20-SC), a joint project of the U.S. Department of Energy’s
Expand Down
158 changes: 126 additions & 32 deletions acc2omp_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,59 @@
# 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.

import fileinput
from shutil import copyfile

ompDir = '!$omp'
accDir = '!$acc'
ompDirContinue = '!$omp&'
accDirContinue = '!$acc&'
nextLineContinue = '&'

emptyString = ''
singleSpaceString = ' '
transitionArrow = ' -> '
backupExtString = '.bak'

# no arguements
# directives without arguements
singleDirDict = {
'loop': 'parallel do',
'gang': '',
'gang': emptyString,
'independent': emptyString,
'parallel': 'target teams distribute',
'vector': 'simd',
'routine': 'declare target',
'seq': '',
'seq': emptyString,
'data': 'data',
'end': 'end',
'enter': 'target enter',
'exit': 'target exit',
'atomic': 'atomic',
'serial': 'target',
'declare': 'declare target',
}

dualDirDict = {
'atomic update': 'atomic update',
}
dualDirDict = {}

# with arguements
# directives with arguements
singleDirwargsDict = {
'attach': 'map(to:',
'detach': 'map(from:',
'copy': 'map(tofrom:',
'copyin': 'map(to:',
'copyout': 'map(from:',
Expand All @@ -51,15 +66,50 @@
'collapse': 'collapse(',
'private': 'private(',
'vector_length': 'simd simdlen(',
'num_gangs': 'num_teams('
'num_gangs': 'num_teams(',
'present': emptyString,
}

dualDirwargsDict = {
'update device': 'target update(',
'update host': 'target update from(',
'update device': 'target update to(',
}

# Set to 1 for debugging and development purposes
debug = 1

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
Expand All @@ -69,7 +119,14 @@
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:
Expand All @@ -82,7 +139,7 @@
if len(line) == 0:
if debug:
print 'Carriage return only'
entries.append(line)
entries.append(origLine)
continue

# As long as the line is not empty (case #1), it can be
Expand All @@ -101,7 +158,7 @@
if lenDirs == 0:
if debug:
print 'Blank line'
entries.append(line)
entries.append(origLine)
continue

# Third case is a line that contains no directive
Expand All @@ -116,7 +173,7 @@
(dirs[0].lower() != accDirContinue)):
if debug:
print 'No OpenACC directive on this line'
entries.append(line)
entries.append(origLine)
continue

# Fourth case contains some OpenACC directive
Expand Down Expand Up @@ -160,54 +217,64 @@
# directives but we do pairs first because there is overlap between
# keywords among the different directive categories.

# Booleans to keep track of what directive type has been found:
# 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:
# Convert directive to lowercase for pattern matching purpose,
# later on the output line will be written out in the native
# capitalization of the source code (determined on a per line
# basis)
dir = dir.lower()
# 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
else: # accDirLowerCase is True
ompDir = ompDir.lower()
ompDirContinue = ompDirContinue.lower()
if accDirFound:
newLine = newLine + ompDir
else: # accDirContinueFound is True
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 + ' ' + dir
# prevprevdir = prevdir
# 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]
dualDir = prevdir + ' ' + currentDir
currentDir = dirwargs[0].lower()
dualDir = prevdir.lower() + singleSpaceString + currentDir

if lenDirwargs > 1: dirwargsFound = True # Boolean unused for now
if debug:
Expand Down Expand Up @@ -250,46 +317,54 @@

# (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 + singleSpaceString + 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 + singleSpaceString + newDir
newLine = newLine + newDir

# reset booleans for next iteration
singleDirFound = False
Expand All @@ -298,10 +373,29 @@
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
newLine = newLine + '\n'
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.
Expand Down

0 comments on commit dfca2ba

Please sign in to comment.