def parseDataObjectsAndRemainder(specRightHandSide): dataObjects, remainder = splitIntoComponentsAndRemainder(specRightHandSide) if len(dataObjects) == 0: raise UsageError("no data objects defined on this line") if len(dataObjects) > 1 and remainder.strip() != "": raise UsageError("invalid right hand side specification in '%s'" %(specRightHandSide)) parsedDataObjects = [] for dataObject in dataObjects: parsedDataObjects.append(getComponentNameAndBracketContent(dataObject)) return tuple(parsedDataObjects), remainder
def implemented(self): purgedRoutineElements = [] try: self._updateSymbolState() self.checkSymbols() implementedRoutineElements = [ self._implementHeader(), self._implementAdditionalImports() ] implementedRoutineElements += [ region.implemented(parentRoutine=self) for region in self._regions ] implementedRoutineElements += [self._implementFooter()] purgedRoutineElements = [ (index, text) for index, text in enumerate(implementedRoutineElements) if text != "" ] except UsageError as e: raise UsageError("In %s: %s" % (self.name, str(e))) except ScopeError as e: raise ScopeError("In %s: %s;\nTraceback: %s" % (self.name, str(e), traceback.format_exc())) except Exception as e: raise ScopeError("In %s: %s;\nTraceback: %s" % (self.name, str(e), traceback.format_exc())) return "\n".join([text for (index, text) in purgedRoutineElements])
def getUpperBound(domainSizeSpec): boundaries = domainSizeSpec.split(':') if len(boundaries) == 1: return boundaries[0].strip() if len(boundaries) == 2: return boundaries[1].strip() raise UsageError("Unexpected domain size specification: %s" %(domainSizeSpec))
def __init__(self, doc): def getCallIndexByAttribute(attributeName): index = {} for call in doc.getElementsByTagName("call"): identifier = call.getAttribute(attributeName) callList = index.get(identifier, []) callList.append(call) index[identifier] = callList return index routinesByName = {} for routine in doc.getElementsByTagName("routine"): routineName = routine.getAttribute("name") if routineName in routinesByName: raise UsageError( "Duplicate subroutines found with name %s. Subroutines need to have unique names in Hybrid Fortran." % (routineName)) routinesByName[routineName] = routine callsByCalleeName = getCallIndexByAttribute("callee") callsByCallerName = getCallIndexByAttribute("caller") callGraphEdgesByCallerName = {} callGraphEdgesByCalleeName = {} def addCallees(routineName): for call in callsByCallerName.get(routineName, []): callerName = call.getAttribute("caller") if callerName != routineName: raise Exception( "unexpected error when constructing callgraph") calleeName = call.getAttribute("callee") edgeList = callGraphEdgesByCallerName.get(routineName, []) edgeList.append((callerName, calleeName)) callGraphEdgesByCallerName[routineName] = edgeList edgeList = callGraphEdgesByCalleeName.get(calleeName, []) edgeList.append((callerName, calleeName)) callGraphEdgesByCalleeName[calleeName] = edgeList addCallees(calleeName) callGraphRootRoutineNames = [ routineName for routineName in routinesByName.keys() if len(callsByCalleeName.get(routineName, [])) == 0 ] for routineName in callGraphRootRoutineNames: addCallees(routineName) #at this point we should have the complete callgraph self.callGraphEdgesByCallerName = callGraphEdgesByCallerName self.routinesByName = routinesByName self.callsByCalleeName = callsByCalleeName self.callsByCallerName = callsByCallerName self.doc = doc self.symbolsNode = createOrGetFirstNodeWithName('symbols', doc)
def parseSpecification(line, keepComponentsAsList=False): def parseDataObjectsAndRemainder(specRightHandSide): dataObjects, remainder = splitIntoComponentsAndRemainder(specRightHandSide) if len(dataObjects) == 0: raise UsageError("no data objects defined on this line") if len(dataObjects) > 1 and remainder.strip() != "": raise UsageError("invalid right hand side specification in '%s'" %(specRightHandSide)) parsedDataObjects = [] for dataObject in dataObjects: parsedDataObjects.append(getComponentNameAndBracketContent(dataObject)) return tuple(parsedDataObjects), remainder patterns = regexPatterns multiSpecMatch = patterns.multiSpecPattern.match(line) declarationComponents, remainder = None, None if multiSpecMatch: doublePrecisionMatch = patterns.doublePrecisionPattern.match(multiSpecMatch.group(1)) if doublePrecisionMatch: #double precision type, because of the space in the type name, doesn't work with the default method :( declarationComponents, _ = splitIntoComponentsAndRemainder(doublePrecisionMatch.group(1)) declarationComponents.insert(0, "double precision") else: declarationComponents, _ = splitIntoComponentsAndRemainder(multiSpecMatch.group(1)) if len(declarationComponents) == 0 or not patterns.standardTypePattern.match(declarationComponents[0]): return None, None, None parsedDataObjects, remainder = parseDataObjectsAndRemainder(multiSpecMatch.group(2)) if keepComponentsAsList: return declarationComponents, parsedDataObjects, remainder return ", ".join(declarationComponents), parsedDataObjects, remainder doublePrecisionMatch = patterns.doublePrecisionPattern.match(line) if doublePrecisionMatch: #double precision type, because of the space in the type name, doesn't work with the default method :( declarationComponents, remainder = splitIntoComponentsAndRemainder(doublePrecisionMatch.group(1)) remainder = remainder.strip() if remainder == "" and len(declarationComponents) > 0: remainder = declarationComponents[-1] declarationComponents = declarationComponents[:-1] declarationComponents.insert(0, "double precision") else: declarationComponents, remainder = splitIntoComponentsAndRemainder(line) if remainder.strip() == "" or len(declarationComponents) == 0: return None, None, None if len(declarationComponents) == 0 or not patterns.standardTypePattern.match(declarationComponents[0]): return None, None, None parsedDataObjects, remainder = parseDataObjectsAndRemainder(remainder) if len(parsedDataObjects) != 1: raise UsageError("invalid number of data objects specified on this declaration line") if keepComponentsAsList: return declarationComponents, parsedDataObjects, remainder return ", ".join(declarationComponents), parsedDataObjects, remainder
def implementSymbolAccessStringAndRemainder(line, suffix, symbol, iterators=[], parallelRegionTemplate=None, callee=None): isPointerAssignment = RegExPatterns.Instance( ).pointerAssignmentPattern.match(line) != None try: symbolAccessString, remainder = getSymbolAccessStringAndRemainder( symbol, iterators, parallelRegionTemplate, suffix, callee, isPointerAssignment) except UsageError as e: raise UsageError("%s; Print of Line: %s" % (str(e), line)) return symbolAccessString, remainder
def implementSymbolAccessStringAndRemainder( line, suffix, symbol, iterators=[], parallelRegionTemplate=None, callee=None, useDeviceVersionIfAvailable=True ): isPointerAssignment = regexPatterns.pointerAssignmentPattern.match(line) != None try: symbolAccessString, remainder = getSymbolAccessStringAndRemainder( symbol, iterators, parallelRegionTemplate, suffix, callee, isPointerAssignment, useDeviceVersionIfAvailable=useDeviceVersionIfAvailable ) except UsageError as e: raise UsageError("%s; Print of Line: %s" %(str(e), line)) return symbolAccessString, remainder
def processInsideDeclarationsState(self, line): '''process everything that happens per h90 declaration line''' subProcCallMatch = self.patterns.subprocCallPattern.match(line) parallelRegionMatch = self.patterns.parallelRegionPattern.match(line) domainDependantMatch = self.patterns.domainDependantPattern.match(line) subProcEndMatch = self.patterns.subprocEndPattern.match(line) templateMatch = self.patterns.templatePattern.match(line) templateEndMatch = self.patterns.templateEndPattern.match(line) branchMatch = self.patterns.branchPattern.match(line) dataStatementMatch = self.patterns.dataStatementPattern.match(line) if dataStatementMatch: self.processDataStatementMatch(dataStatementMatch) return if branchMatch: self.processBranchMatch(branchMatch) return if subProcCallMatch: self.switchToNewRegion("CallRegion") self.processCallMatch(subProcCallMatch) self.switchToNewRegion() return if subProcEndMatch: self.processProcEndMatch(subProcEndMatch) if self.state == "inside_branch": self.stateBeforeBranch = 'inside_module_body' else: self.state = 'inside_module_body' return if parallelRegionMatch: raise UsageError("parallel region without parallel dependants") if self.patterns.subprocBeginPattern.match(line): raise UsageError("subprocedure within subprocedure not allowed") if templateMatch: raise UsageError( "template directives are only allowed outside of subroutines") if templateEndMatch: raise UsageError( "template directives are only allowed outside of subroutines") if domainDependantMatch: if self.state == "inside_branch": self.stateBeforeBranch = 'inside_domainDependantRegion' else: self.state = 'inside_domainDependantRegion' self.switchToNewRegion() self.processDomainDependantMatch(domainDependantMatch) return importMatch1 = self.patterns.importPattern.match(line) importMatch2 = self.patterns.singleMappedImportPattern.match(line) importMatch3 = self.patterns.importAllPattern.match(line) specTuple = parseSpecification(line) specificationStatementMatch = self.patterns.specificationStatementPattern.match( line) if not ( \ line.strip() == "" \ or importMatch1 \ or importMatch2 \ or importMatch3 \ or specTuple[0] \ or specificationStatementMatch \ ): if self.state == "inside_branch": self.stateBeforeBranch = "inside_subroutine_body" else: self.state = "inside_subroutine_body" self.switchToNewRegion() self.processInsideSubroutineBodyState(line) return self.analyseSymbolInformationOnCurrentLine(line) #we are never calling super and every match that would have prepared a line, would already have been covered #with a return -> safe to call prepareLine here. self.prepareLine(line, self.tab_insideSub)
def implemented(self, skipDebugPrint=False): def getImportLine(importedSymbols, parentRoutine): return parentRoutine.implementation.getImportSpecification( importedSymbols, RegionType.KERNEL_CALLER_DECLARATION if parentRoutine.isCallingKernel else RegionType.OTHER, parentRoutine.node.getAttribute('parallelRegionPosition'), parentRoutine.parallelRegionTemplates) parentRoutine = self._routineRef() declarationRegionType = RegionType.OTHER if parentRoutine.isCallingKernel: declarationRegionType = RegionType.KERNEL_CALLER_DECLARATION if self._additionalParametersByKernelName == None \ or self._symbolsToAdd == None \ or self._compactionDeclarationPrefixByCalleeName == None \ or self._currAdditionalCompactedSubroutineParameters == None: raise Exception( "additional context not properly loaded for routine specification region in %s" % (parentRoutine.name)) importsFound = False declaredSymbolsByScopedName = OrderedDict() textForKeywords = "" textBeforeDeclarations = "" textAfterDeclarations = "" symbolsToAddByScopedName = dict( (symbol.nameInScope(), symbol) for symbol in self._symbolsToAdd) for (line, symbols) in self._linesAndSymbols: if not symbols or len(symbols) == 0: allImportMatch = RegExPatterns.Instance( ).importAllPattern.match(line) if allImportMatch: importsFound = True elif not importsFound: textForKeywords += line.strip() + "\n" elif len(declaredSymbolsByScopedName.keys()) == 0: textBeforeDeclarations += line.strip() + "\n" else: textAfterDeclarations += line.strip() + "\n" continue for symbol in symbols: if symbol.nameInScope() in symbolsToAddByScopedName: continue if symbol.isCompacted: continue #compacted symbols are handled as part of symbolsToAdd if symbol.getSpecificationTuple(line)[0]: declaredSymbolsByScopedName[symbol.nameInScope()] = symbol continue match = symbol.importPattern.match(line) if not match: match = symbol.importMapPattern.match(line) if match: importsFound = True continue raise Exception( "symbol %s expected to be referenced in line '%s', but all matchings have failed" % (symbol.name, line)) text = "" try: moduleNamesCompletelyImported = [ sourceModule for (sourceModule, nameInScope) in self._allImports if nameInScope == None ] if self._allImports else [] if len(self._typeParameterSymbolsByName.keys()) > 0 \ and ConversionOptions.Instance().debugPrint \ and not skipDebugPrint: text += "!<----- type parameters --\n" for typeParameterSymbol in self._typeParameterSymbolsByName.values( ): typeParameterSymbol.updateNameInScope(forceAutomaticName=True) if typeParameterSymbol.sourceModule in moduleNamesCompletelyImported \ and not "hfauto" in typeParameterSymbol.nameInScope(): continue text += getImportLine([typeParameterSymbol], parentRoutine) if self._allImports: if len(self. _allImports.keys()) > 0 and ConversionOptions.Instance( ).debugPrint and not skipDebugPrint: text += "!<----- synthesized imports --\n" for (sourceModule, nameInScope) in self._allImports: if not nameInScope: text += getImportLine(sourceModule, parentRoutine) continue if sourceModule in moduleNamesCompletelyImported: continue sourceName = self._allImports[(sourceModule, nameInScope)] symbol = parentRoutine.symbolsByName.get(sourceName) if symbol != None and symbol.sourceModule == parentRoutine._parentModule( ).name: continue if symbol != None: text += getImportLine([symbol], parentRoutine) else: text += "use %s, only: %s => %s" %(sourceModule, nameInScope, sourceName) \ if nameInScope != sourceName \ else "use %s, only: %s" %(sourceModule, nameInScope) if ConversionOptions.Instance( ).debugPrint and not skipDebugPrint: text += " ! resynthesizing user input - no associated HF aware symbol found" text += "\n" if textForKeywords != "" and ConversionOptions.Instance( ).debugPrint and not skipDebugPrint: text += "!<----- other imports and specs: ------\n" text += textForKeywords if textBeforeDeclarations != "" and ConversionOptions.Instance( ).debugPrint and not skipDebugPrint: text += "!<----- before declarations: --\n" text += textBeforeDeclarations if len(declaredSymbolsByScopedName.keys()) > 0: if ConversionOptions.Instance( ).debugPrint and not skipDebugPrint: text += "!<----- declarations: -------\n" text += "\n".join([ parentRoutine.implementation.adjustDeclarationForDevice( symbol.getDeclarationLine(purgeList=[]), [symbol], declarationRegionType, parentRoutine.node.getAttribute( 'parallelRegionPosition')).strip() for symbol in declaredSymbolsByScopedName.values() ]).strip() + "\n" if len(self. _dataSpecificationLines) > 0 and ConversionOptions.Instance( ).debugPrint and not skipDebugPrint: text += "!<----- data specifications: --\n" if len(self._dataSpecificationLines) > 0: text += "\n".join(self._dataSpecificationLines) + "\n" if textAfterDeclarations != "" and ConversionOptions.Instance( ).debugPrint and not skipDebugPrint: text += "!<----- after declarations: --\n" text += textAfterDeclarations numberOfAdditionalDeclarations = ( len( sum([ self._additionalParametersByKernelName[kname][1] for kname in self._additionalParametersByKernelName ], [])) + len(self._symbolsToAdd) + len(parentRoutine._packedRealSymbolsByCalleeName.keys())) if numberOfAdditionalDeclarations > 0 and ConversionOptions.Instance( ).debugPrint and not skipDebugPrint: text += "!<----- auto emul symbols : --\n" defaultPurgeList = ['intent', 'public', 'parameter', 'allocatable'] for symbol in self._symbolsToAdd: purgeList = defaultPurgeList if not symbol.isCompacted: purgeList = ['public', 'parameter', 'allocatable'] text += parentRoutine.implementation.adjustDeclarationForDevice( symbol.getDeclarationLine(purgeList).strip(), [symbol], declarationRegionType, parentRoutine.node.getAttribute( 'parallelRegionPosition')).rstrip( ) + " ! type %i symbol added for this subroutine\n" % ( symbol.declarationType) for callee in parentRoutine.callees: #this hasattr is used to test the callee for analyzability without circular imports if not hasattr(callee, "implementation"): continue additionalImports, additionalDeclarations = self._additionalParametersByKernelName.get( callee.name, ([], [])) additionalImportSymbolsByName = {} for symbol in additionalImports: additionalImportSymbolsByName[symbol.name] = symbol implementation = callee.implementation for symbol in parentRoutine.filterOutSymbolsAlreadyAliveInCurrentScope( additionalDeclarations): if symbol.declarationType not in [ DeclarationType.LOCAL_ARRAY, DeclarationType.LOCAL_SCALAR ]: # only symbols that are local to the kernel actually need to be declared here. # Everything else we should have in our own scope already, either through additional imports or # through module association (we assume the kernel and its wrapper reside in the same module) continue #in case the array uses domain sizes in the declaration that are additional symbols themselves #we need to fix them. adjustedDomains = [] for (domName, domSize) in symbol.domains: domSizeSymbol = additionalImportSymbolsByName.get( domSize) if domSizeSymbol is None: adjustedDomains.append((domName, domSize)) continue adjustedDomains.append( (domName, domSizeSymbol.nameInScope())) symbol.domains = adjustedDomains text += implementation.adjustDeclarationForDevice( symbol.getDeclarationLine(defaultPurgeList).strip(), [symbol], declarationRegionType, parentRoutine.node.getAttribute( 'parallelRegionPosition')).rstrip( ) + " ! type %i symbol added for callee %s\n" % ( symbol.declarationType, callee.name) toBeCompacted = parentRoutine._packedRealSymbolsByCalleeName.get( callee.name, []) if len(toBeCompacted) > 0: #TODO: generalize for cases where we don't want this to be on the device #(e.g. put this into Implementation class) compactedArray = FrameworkArray( callee.name, self._compactionDeclarationPrefixByCalleeName[ callee.name], domains=[("hfauto", str(len(toBeCompacted)))], isOnDevice=True) text += implementation.adjustDeclarationForDevice( compactedArray.getDeclarationLine().strip(), [compactedArray], declarationRegionType, parentRoutine.node.getAttribute( 'parallelRegionPosition')).rstrip( ) + " ! compaction array added for callee %s\n" % ( callee.name) declarationEndText = parentRoutine.implementation.declarationEnd( parentRoutine.symbolsByName.values() + parentRoutine.additionalImports, parentRoutine.isCallingKernel, parentRoutine.node, parentRoutine.parallelRegionTemplates) if len(declarationEndText) > 0: text += "!<----- impl. specific decl end : --\n" text += declarationEndText for idx, symbol in enumerate( self._currAdditionalCompactedSubroutineParameters): text += "%s = %s(%i)" % ( symbol.nameInScope(), limitLength(frameworkArrayName(parentRoutine.name)), idx + 1) + " ! additional type %i symbol compaction\n" % ( symbol.declarationType) if ConversionOptions.Instance().debugPrint and not skipDebugPrint: text += "! HF is aware of the following symbols at this point: %s" % ( parentRoutine.symbolsByName.values()) except UsageError as e: raise UsageError("Implementing %s: %s" % (parentRoutine.name, str(e))) except Exception as e: raise Exception("Implementing %s: %s" % (parentRoutine.name, str(e))) return self._sanitize(text, skipDebugPrint)