diff --git a/devito/core/cpu.py b/devito/core/cpu.py index 81611da9b4..1d34314697 100644 --- a/devito/core/cpu.py +++ b/devito/core/cpu.py @@ -6,8 +6,9 @@ from devito.passes.clusters import (Lift, blocking, buffering, cire, cse, factorize, fission, fuse, optimize_pows, optimize_hyperplanes) -from devito.passes.iet import (CTarget, OmpTarget, avoid_denormals, linearize, mpiize, - hoist_prodders, relax_incr_dimensions) +from devito.passes.iet import (CTarget, OmpTarget, avoid_denormals, linearize, + mpiize, hoist_prodders, relax_incr_dimensions, + check_stability) from devito.tools import timed_pass __all__ = ['Cpu64NoopCOperator', 'Cpu64NoopOmpOperator', 'Cpu64AdvCOperator', @@ -76,6 +77,7 @@ def _normalize_kwargs(cls, **kwargs): o['mapify-reduce'] = oo.pop('mapify-reduce', cls.MAPIFY_REDUCE) o['index-mode'] = oo.pop('index-mode', cls.INDEX_MODE) o['place-transfers'] = oo.pop('place-transfers', True) + o['errctl'] = oo.pop('errctl', cls.ERRCTL) # Recognised but unused by the CPU backend oo.pop('par-disabled', None) @@ -189,6 +191,9 @@ def _specialize_iet(cls, graph, **kwargs): # Misc optimizations hoist_prodders(graph) + # Perform error checking + check_stability(graph, **kwargs) + # Symbol definitions cls._Target.DataManager(**kwargs).process(graph) diff --git a/devito/core/gpu.py b/devito/core/gpu.py index de070c6890..a0c2da774a 100644 --- a/devito/core/gpu.py +++ b/devito/core/gpu.py @@ -10,8 +10,9 @@ from devito.passes.clusters import (Lift, Streaming, Tasker, blocking, buffering, cire, cse, factorize, fission, fuse, optimize_pows) -from devito.passes.iet import (DeviceOmpTarget, DeviceAccTarget, mpiize, hoist_prodders, - linearize, pthreadify, relax_incr_dimensions) +from devito.passes.iet import (DeviceOmpTarget, DeviceAccTarget, mpiize, + hoist_prodders, linearize, pthreadify, + relax_incr_dimensions, check_stability) from devito.tools import as_tuple, timed_pass __all__ = ['DeviceNoopOperator', 'DeviceAdvOperator', 'DeviceCustomOperator', @@ -91,6 +92,7 @@ def _normalize_kwargs(cls, **kwargs): o['mapify-reduce'] = oo.pop('mapify-reduce', cls.MAPIFY_REDUCE) o['index-mode'] = oo.pop('index-mode', cls.INDEX_MODE) o['place-transfers'] = oo.pop('place-transfers', True) + o['errctl'] = oo.pop('errctl', cls.ERRCTL) if oo: raise InvalidOperator("Unsupported optimization options: [%s]" @@ -226,6 +228,9 @@ def _specialize_iet(cls, graph, **kwargs): # Misc optimizations hoist_prodders(graph) + # Perform error checking + check_stability(graph, **kwargs) + # Symbol definitions cls._Target.DataManager(**kwargs).process(graph) diff --git a/devito/core/operator.py b/devito/core/operator.py index 1ba976d3b6..e61ff2c806 100644 --- a/devito/core/operator.py +++ b/devito/core/operator.py @@ -119,6 +119,13 @@ class BasicOperator(Operator): (default) or `int32`. """ + ERRCTL = None + """ + Runtime error checking. If this option is enabled, the generated code will + include runtime checks for various things that might go south, such as + instability (e.g., NaNs), failed library calls (e.g., kernel launches). + """ + _Target = None """ The target language constructor, to be specified by subclasses. @@ -155,6 +162,9 @@ def _check_kwargs(cls, **kwargs): if oo['deriv-unroll'] not in (False, 'inner', 'full'): raise InvalidArgument("Illegal `deriv-unroll` value") + if oo['errctl'] not in (None, False, 'basic', 'max'): + raise InvalidArgument("Illegal `errctl` value") + def _autotune(self, args, setup): if setup in [False, 'off']: return args diff --git a/devito/exceptions.py b/devito/exceptions.py index f03b0720ec..fa5619d4ca 100644 --- a/devito/exceptions.py +++ b/devito/exceptions.py @@ -14,5 +14,9 @@ class InvalidOperator(DevitoError): pass +class ExecutionError(DevitoError): + pass + + class VisitorException(DevitoError): pass diff --git a/devito/ir/iet/nodes.py b/devito/ir/iet/nodes.py index 72307c5ac5..36a7a2f836 100644 --- a/devito/ir/iet/nodes.py +++ b/devito/ir/iet/nodes.py @@ -794,17 +794,19 @@ class CallableBody(MultiTraversable): Data unbundling for `body`. frees : list of Calls, optional Data deallocations for `body`. + errors : list of Nodes, optional + Error handling for `body`. """ is_CallableBody = True _traversable = ['unpacks', 'init', 'standalones', 'allocs', 'stacks', 'casts', 'bundles', 'maps', 'strides', 'objs', 'body', - 'unmaps', 'unbundles', 'frees'] + 'unmaps', 'unbundles', 'frees', 'errors'] def __init__(self, body, init=(), standalones=(), unpacks=(), strides=(), allocs=(), stacks=(), casts=(), bundles=(), objs=(), maps=(), - unmaps=(), unbundles=(), frees=()): + unmaps=(), unbundles=(), frees=(), errors=()): # Sanity check assert not isinstance(body, CallableBody), "CallableBody's cannot be nested" @@ -823,6 +825,7 @@ def __init__(self, body, init=(), standalones=(), unpacks=(), strides=(), self.unmaps = as_tuple(unmaps) self.unbundles = as_tuple(unbundles) self.frees = as_tuple(frees) + self.errors = as_tuple(errors) def __repr__(self): return ("