Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overwrite frontend options on a per-file basis in Scheduler config #233

Merged
merged 5 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions loki/bulk/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,23 @@ class SchedulerConfig:
visualisation. These are intended for utility routines that
pop up in many routines but can be ignored in terms of program
control flow, like ``flush`` or ``abort``.
transformation_configs : dict
Dicts with transformation-specific options
frontend_args : dict
Dicts with file-specific frontend options
"""

def __init__(
self, default, routines, disable=None, dimensions=None,
transformation_configs=None
transformation_configs=None, frontend_args=None
):
self.default = default
self.disable = as_tuple(disable)
self.dimensions = dimensions

self.routines = CaseInsensitiveDict(routines)
self.transformation_configs = transformation_configs
self.frontend_args = frontend_args

# Resolve the dimensions for trafo configurations
for cfg in self.transformation_configs.values():
Expand Down Expand Up @@ -82,10 +87,11 @@ def from_dict(cls, config):
name: TransformationConfig(name=name, **cfg)
for name, cfg in transformation_configs.items()
}
frontend_args = config.get('frontend_args', {})

return cls(
default=default, routines=routines, disable=disable, dimensions=dimensions,
transformation_configs=transformation_configs
transformation_configs=transformation_configs, frontend_args=frontend_args
)

@classmethod
Expand Down Expand Up @@ -179,6 +185,39 @@ def create_item_config(self, name):
item_conf.update(self.routines[key])
return item_conf

def create_frontend_args(self, path, default_args):
"""
Create bespoke ``frontend_args`` to pass to the constructor
or ``make_complete`` method for a file

The resulting `dict` contains overwrites that have been provided
in the :attr:`frontend_args` of the config.

Parameters
----------
path : str or pathlib.Path
The file path for which to create the frontend arguments. This
can be a fully-qualified path or include :any:`fnmatch`-compatible
patterns.
default_args : dict
The default options to use. Only keys that are explicitly overriden
for the file in the scheduler config are updated.

Returns
-------
dict
The frontend arguments, with file-specific overrides of
:data:`default_args` if specified in the Scheduler config.
"""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be useful to list arguments here. Was briefly wondering here what path pertained to.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, good point. I've commited an updated docstring

path = str(path).lower()
frontend_args = default_args.copy()
for key, args in (self.frontend_args or {}).items():
pattern = key.lower() if key[0] == '/' else f'*{key}'.lower()
if fnmatch(path, pattern):
frontend_args.update(args)
return frontend_args
return frontend_args

def is_disabled(self, name):
"""
Check if the item with the given :data:`name` is marked as `disabled`
Expand Down
2 changes: 2 additions & 0 deletions loki/bulk/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,8 @@ def get_or_create_file_item_from_path(self, path, config, frontend_args=None):

if not frontend_args:
frontend_args = {}
if config:
frontend_args = config.create_frontend_args(path, frontend_args)

source = Sourcefile.from_file(path, **frontend_args)
item_conf = config.create_item_config(item_name) if config else None
Expand Down
14 changes: 8 additions & 6 deletions loki/bulk/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
# nor does it submit to any jurisdiction.

from collections import deque, defaultdict
from pathlib import Path
from os.path import commonpath
from pathlib import Path
import networkx as nx
from codetiming import Timer

from loki.bulk.configure import SchedulerConfig
from loki.bulk.item import (
Item, FileItem, ModuleItem, ProcedureItem, ProcedureBindingItem,
InterfaceItem, TypeDefItem, ItemFactory
)
from loki.bulk.configure import SchedulerConfig
from loki.frontend import FP, REGEX, RegexParserClass
from loki.tools import as_tuple, CaseInsensitiveDict, flatten
from loki.logging import info, perf, warning, debug
Expand Down Expand Up @@ -265,10 +265,11 @@ def _parse_items(self):
the execution plan and enriching subroutine calls.
"""
# Force the parsing of the routines
build_args = self.build_args.copy()
build_args['definitions'] = as_tuple(build_args['definitions']) + self.definitions
default_frontend_args = self.build_args.copy()
default_frontend_args['definitions'] = as_tuple(default_frontend_args['definitions']) + self.definitions
for item in SFilter(self.sgraph.as_filegraph(self.item_factory, self.config), reverse=True):
item.source.make_complete(**build_args)
frontend_args = self.config.create_frontend_args(item.name, default_frontend_args)
item.source.make_complete(**frontend_args)

@Timer(logger=info, text='[Loki::Scheduler] Enriched call tree in {:.2f}s')
def _enrich(self):
Expand All @@ -285,7 +286,8 @@ def _enrich(self):
self.sgraph._create_item(name, item_factory=self.item_factory, config=self.config)
)
for enrich_item in enrich_items:
enrich_item.source.make_complete(**self.build_args)
frontend_args = self.config.create_frontend_args(enrich_item.source.path, self.build_args)
enrich_item.source.make_complete(**frontend_args)
enrich_definitions += tuple(item_.ir for item_ in enrich_items)
item.ir.enrich(enrich_definitions, recurse=True)

Expand Down
5 changes: 5 additions & 0 deletions loki/program_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ def from_source(cls, source, definitions=None, preprocess=False,
parent : :any:`Scope`, optional
The parent scope this module or subroutine is nested into
"""
if isinstance(frontend, str):
frontend = Frontend[frontend.upper()]

if preprocess:
# Trigger CPP-preprocessing explicitly, as includes and
# defines can also be used by our OMNI frontend
Expand Down Expand Up @@ -278,6 +281,8 @@ def make_complete(self, **frontend_args):
if not self._incomplete:
return
frontend = frontend_args.pop('frontend', Frontend.FP)
if isinstance(frontend, str):
frontend = Frontend[frontend.upper()]
definitions = frontend_args.get('definitions')
xmods = frontend_args.get('xmods')
parser_classes = frontend_args.get('parser_classes', RegexParserClass.AllClasses)
Expand Down
9 changes: 8 additions & 1 deletion loki/sourcefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from loki.backend.fgen import fgen
from loki.backend.cufgen import cufgen
from loki.frontend import (
OMNI, OFP, FP, REGEX, sanitize_input, Source, read_file, preprocess_cpp,
Frontend, OMNI, OFP, FP, REGEX, sanitize_input, Source, read_file, preprocess_cpp,
parse_omni_source, parse_ofp_source, parse_fparser_source,
parse_omni_ast, parse_ofp_ast, parse_fparser_ast, parse_regex_source,
RegexParserClass
Expand Down Expand Up @@ -107,6 +107,8 @@ def from_file(cls, filename, definitions=None, preprocess=False,
frontend : :any:`Frontend`, optional
Frontend to use for producing the AST (default :any:`FP`).
"""
if isinstance(frontend, str):
frontend = Frontend[frontend.upper()]

# Log full parses at INFO and regex scans at PERF level
log = f'[Loki::Sourcefile] Constructed from {filename}' + ' in {:.2f}s'
Expand Down Expand Up @@ -323,6 +325,9 @@ def from_source(cls, source, definitions=None, preprocess=False,
frontend : :any:`Frontend`, optional
Frontend to use for producing the AST (default :any:`FP`).
"""
if isinstance(frontend, str):
frontend = Frontend[frontend.upper()]

if preprocess:
# Trigger CPP-preprocessing explicitly, as includes and
# defines can also be used by our OMNI frontend
Expand Down Expand Up @@ -362,6 +367,8 @@ def make_complete(self, **frontend_args):

# Sanitize frontend_args
frontend = frontend_args.pop('frontend', FP)
if isinstance(frontend, str):
frontend = Frontend[frontend.upper()]
if frontend == REGEX:
frontend_argnames = ['parser_classes']
elif frontend == OMNI:
Expand Down
Loading
Loading