""" Defines CPU Options for use in the CPU target """ from abc import ABCMeta, abstractmethod class AbstractOptionValue(metaclass=ABCMeta): """Abstract base class for custom option values. """ @abstractmethod def encode(self) -> str: """Returns an encoding of the values """ ... def __repr__(self) -> str: return f"{self.__class__.__name__}({self.encode()})" class FastMathOptions(AbstractOptionValue): """ Options for controlling fast math optimization. """ def __init__(self, value): # https://releases.llvm.org/7.0.0/docs/LangRef.html#fast-math-flags valid_flags = { 'fast', 'nnan', 'ninf', 'nsz', 'arcp', 'contract', 'afn', 'reassoc', } if isinstance(value, FastMathOptions): self.flags = value.flags.copy() elif value is True: self.flags = {'fast'} elif value is False: self.flags = set() elif isinstance(value, set): invalid = value - valid_flags if invalid: raise ValueError("Unrecognized fastmath flags: %s" % invalid) self.flags = value elif isinstance(value, dict): invalid = set(value.keys()) - valid_flags if invalid: raise ValueError("Unrecognized fastmath flags: %s" % invalid) self.flags = {v for v, enable in value.items() if enable} else: msg = "Expected fastmath option(s) to be either a bool, dict or set" raise ValueError(msg) def __bool__(self): return bool(self.flags) __nonzero__ = __bool__ def encode(self) -> str: return str(self.flags) def __eq__(self, other): if type(other) is type(self): return self.flags == other.flags return NotImplemented class ParallelOptions(AbstractOptionValue): """ Options for controlling auto parallelization. """ __slots__ = ("enabled", "comprehension", "reduction", "inplace_binop", "setitem", "numpy", "stencil", "fusion", "prange") def __init__(self, value): if isinstance(value, bool): self.enabled = value self.comprehension = value self.reduction = value self.inplace_binop = value self.setitem = value self.numpy = value self.stencil = value self.fusion = value self.prange = value elif isinstance(value, dict): self.enabled = True self.comprehension = value.pop('comprehension', True) self.reduction = value.pop('reduction', True) self.inplace_binop = value.pop('inplace_binop', True) self.setitem = value.pop('setitem', True) self.numpy = value.pop('numpy', True) self.stencil = value.pop('stencil', True) self.fusion = value.pop('fusion', True) self.prange = value.pop('prange', True) if value: msg = "Unrecognized parallel options: %s" % value.keys() raise NameError(msg) elif isinstance(value, ParallelOptions): self.enabled = value.enabled self.comprehension = value.comprehension self.reduction = value.reduction self.inplace_binop = value.inplace_binop self.setitem = value.setitem self.numpy = value.numpy self.stencil = value.stencil self.fusion = value.fusion self.prange = value.prange else: msg = "Expect parallel option to be either a bool or a dict" raise ValueError(msg) def _get_values(self): """Get values as dictionary. """ return {k: getattr(self, k) for k in self.__slots__} def __eq__(self, other): if type(other) is type(self): return self._get_values() == other._get_values() return NotImplemented def encode(self) -> str: return ", ".join(f"{k}={v}" for k, v in self._get_values().items()) class InlineOptions(AbstractOptionValue): """ Options for controlling inlining """ def __init__(self, value): ok = False if isinstance(value, str): if value in ('always', 'never'): ok = True else: ok = hasattr(value, '__call__') if ok: self._inline = value else: msg = ("kwarg 'inline' must be one of the strings 'always' or " "'never', or it can be a callable that returns True/False. " "Found value %s" % value) raise ValueError(msg) @property def is_never_inline(self): """ True if never inline """ return self._inline == 'never' @property def is_always_inline(self): """ True if always inline """ return self._inline == 'always' @property def has_cost_model(self): """ True if a cost model is provided """ return not (self.is_always_inline or self.is_never_inline) @property def value(self): """ The raw value """ return self._inline def __eq__(self, other): if type(other) is type(self): return self.value == other.value return NotImplemented def encode(self) -> str: return repr(self._inline)