diff --git a/src/pystencils/backend/jit/__init__.py b/src/pystencils/backend/jit/__init__.py
index 6953f8daffe1d7cfb12e10e9e22bda7b4838f038..b3340a9ab7ffb0be2ea9812b0684178102105e20 100644
--- a/src/pystencils/backend/jit/__init__.py
+++ b/src/pystencils/backend/jit/__init__.py
@@ -29,15 +29,11 @@ Both are available here through `LegacyCpuJit` and `LegacyGpuJit`.
 from .jit import JitBase, NoJit, LegacyCpuJit, LegacyGpuJit
 
 no_jit = NoJit()
-legacy_cpu = LegacyCpuJit()
-legacy_gpu = LegacyGpuJit()
 
 __all__ = [
     "JitBase",
     "LegacyCpuJit",
-    "legacy_cpu",
     "NoJit",
     "no_jit",
     "LegacyGpuJit",
-    "legacy_gpu",
 ]
diff --git a/src/pystencils/backend/jit/cpu_extension_module.py b/src/pystencils/backend/jit/cpu_extension_module.py
index f2c0d3ff98ee5e762cee8419b46d6a4b64b1bb61..739aefb272a1a018db4609852561fcdf2f48bf5c 100644
--- a/src/pystencils/backend/jit/cpu_extension_module.py
+++ b/src/pystencils/backend/jit/cpu_extension_module.py
@@ -112,8 +112,6 @@ class PsKernelExtensioNModule:
             "mod_" + hashlib.sha256(code.encode() + header_hash).hexdigest()
         )
 
-        from ...cpu.cpujit import create_module_boilerplate_code
-
         code += create_module_boilerplate_code(self._code_hash, self._kernels.keys())
 
         self._code_string = code
@@ -142,6 +140,35 @@ def emit_call_wrapper(function_name: str, kernel: PsKernelFunction) -> str:
     return builder.resolve(function_name)
 
 
+template_module_boilerplate = """
+static PyMethodDef method_definitions[] = {{
+    {method_definitions}
+    {{NULL, NULL, 0, NULL}}
+}};
+
+static struct PyModuleDef module_definition = {{
+    PyModuleDef_HEAD_INIT,
+    "{module_name}",   /* name of module */
+    NULL,     /* module documentation, may be NULL */
+    -1,       /* size of per-interpreter state of the module,
+                 or -1 if the module keeps state in global variables. */
+    method_definitions
+}};
+
+PyMODINIT_FUNC
+PyInit_{module_name}(void)
+{{
+    return PyModule_Create(&module_definition);
+}}
+"""
+
+
+def create_module_boilerplate_code(module_name, names):
+    method_definition = '{{"{name}", (PyCFunction){name}, METH_VARARGS | METH_KEYWORDS, ""}},'
+    method_definitions = "\n".join([method_definition.format(name=name) for name in names])
+    return template_module_boilerplate.format(module_name=module_name, method_definitions=method_definitions)
+
+
 class CallWrapperBuilder:
     TMPL_EXTRACT_SCALAR = """
 PyObject * obj_{name} = PyDict_GetItemString(kwargs, "{name}");
diff --git a/src/pystencils/backend/jit/jit.py b/src/pystencils/backend/jit/jit.py
index 4e9ae46b61f7018485976f3cfebcbce6e46641c3..842a2f8da52bb13436e469491291fd6b99439581 100644
--- a/src/pystencils/backend/jit/jit.py
+++ b/src/pystencils/backend/jit/jit.py
@@ -31,7 +31,7 @@ class LegacyCpuJit(JitBase):
     """Wrapper around ``pystencils.cpu.cpujit``"""
 
     def compile(self, kernel: PsKernelFunction) -> Callable[..., None]:
-        from ...cpu.cpujit import compile_and_load
+        from .legacy_cpu import compile_and_load
 
         return compile_and_load(kernel)
 
@@ -40,6 +40,6 @@ class LegacyGpuJit(JitBase):
     """Wrapper around ``pystencils.gpu.gpujit``"""
 
     def compile(self, kernel: PsKernelFunction) -> Callable[..., None]:
-        from ...gpu.gpujit import make_python_function
+        from ...old.gpu.gpujit import make_python_function
 
         return make_python_function(kernel)
diff --git a/src/pystencils/backend/jit/legacy_cpu.py b/src/pystencils/backend/jit/legacy_cpu.py
new file mode 100644
index 0000000000000000000000000000000000000000..7906bb5e59bc9f45bd2dcbcc6e2c0eadd4b1a3c1
--- /dev/null
+++ b/src/pystencils/backend/jit/legacy_cpu.py
@@ -0,0 +1,380 @@
+r"""
+
+*pystencils* automatically searches for a compiler, so in most cases no explicit configuration is required.
+On Linux make sure that 'gcc' and 'g++' are installed and in your path.
+On Windows a recent Visual Studio installation is required.
+In case anything does not work as expected or a special compiler should be used, changes can be specified
+in a configuration file.
+
+*pystencils* looks for a configuration file in JSON format at the following locations in the listed order.
+
+1. at the path specified in the environment variable ``PYSTENCILS_CONFIG``
+2. in the current working direction for a file named ``pystencils.json``
+3. or in your home directory at ``~/.config/pystencils/config.json`` (Linux) or
+   ``%HOMEPATH%\.pystencils\config.json`` (Windows)
+
+If no configuration file is found, a default configuration is created at the above-mentioned location in your home.
+So run *pystencils* once, then edit the created configuration file.
+
+
+Compiler Config (Linux)
+-----------------------
+
+- **'os'**: should be detected automatically as 'linux'
+- **'command'**: path to C++ compiler (defaults to 'g++')
+- **'flags'**: space separated list of compiler flags. Make sure to activate OpenMP in your compiler
+- **'restrict_qualifier'**: the 'restrict' qualifier is not standardized across compilers.
+  For most Linux compilers the qualifier is ``__restrict__``
+
+
+Compiler Config (Windows)
+-------------------------
+
+*pystencils* uses the mechanism of *setuptools.msvc* to search for a compilation environment.
+Then 'cl.exe' is used to compile.
+
+- **'os'**: should be detected automatically as 'windows'
+- **'msvc_version'**:  either a version number, year number, 'auto' or 'latest' for automatic detection of latest
+  installed version or 'setuptools' for setuptools-based detection. Alternatively path to folder
+  where Visual Studio is installed. This path has to contain a file called 'vcvarsall.bat'
+- **'arch'**: 'x86' or 'x64'
+- **'flags'**: flags passed to 'cl.exe', make sure OpenMP is activated
+- **'restrict_qualifier'**: the 'restrict' qualifier is not standardized across compilers.
+  For Windows compilers the qualifier should be ``__restrict``
+
+"""
+from appdirs import user_cache_dir, user_config_dir
+from collections import OrderedDict
+import importlib.util
+import json
+import os
+import platform
+import shutil
+import subprocess
+import sysconfig
+import tempfile
+import time
+import warnings
+
+
+from ..ast import PsKernelFunction
+from .cpu_extension_module import PsKernelExtensioNModule
+
+from .msvc_detection import get_environment
+from pystencils.include import get_pystencils_include_path
+from pystencils.kernel_wrapper import KernelWrapper
+from pystencils.utils import atomic_file_write, recursive_dict_update
+
+
+def make_python_function(kernel_function_node, custom_backend=None):
+    """
+    Creates C code from the abstract syntax tree, compiles it and makes it accessible as Python function
+
+    The parameters of the kernel are:
+        - numpy arrays for each field used in the kernel. The keyword argument name is the name of the field
+        - all symbols which are not defined in the kernel itself are expected as parameters
+
+    :param kernel_function_node: the abstract syntax tree
+    :param custom_backend: use own custom printer for code generation
+    :return: kernel functor
+    """
+    result = compile_and_load(kernel_function_node, custom_backend)
+    return result
+
+
+def set_config(config):
+    """
+    Override the configuration provided in config file
+
+    Configuration of compiler parameters:
+    If this function is not called the configuration is taken from a config file in JSON format which
+    is searched in the following locations in the order specified:
+        - at location provided in environment variable PYSTENCILS_CONFIG (if this variable exists)
+        - a file called ".pystencils.json" in the current working directory
+        - ~/.pystencils.json in your home
+    If none of these files exist a file ~/.pystencils.json is created with a default configuration using
+    the GNU 'g++'
+
+    An example JSON file with all possible keys. If not all keys are specified, default values are used
+    ``
+    {
+        'compiler' :
+        {
+            "command": "/software/intel/2017/bin/icpc",
+            "flags": "-Ofast -DNDEBUG -fPIC -march=native -fopenmp",
+            "env": {
+                "LM_PROJECT": "iwia",
+            }
+        }
+    }
+    ``
+    """
+    global _config
+    _config = config.copy()
+
+
+def get_configuration_file_path():
+    config_path_in_home = os.path.join(user_config_dir('pystencils'), 'config.json')
+
+    # 1) Read path from environment variable if found
+    if 'PYSTENCILS_CONFIG' in os.environ:
+        return os.environ['PYSTENCILS_CONFIG'], True
+    # 2) Look in current directory for pystencils.json
+    elif os.path.exists("pystencils.json"):
+        return "pystencils.json", True
+    # 3) Try ~/.pystencils.json
+    elif os.path.exists(config_path_in_home):
+        return config_path_in_home, True
+    else:
+        return config_path_in_home, False
+
+
+def create_folder(path, is_file):
+    if is_file:
+        path = os.path.split(path)[0]
+    try:
+        os.makedirs(path)
+    except os.error:
+        pass
+
+
+def read_config():
+    if platform.system().lower() == 'linux':
+        default_compiler_config = OrderedDict([
+            ('os', 'linux'),
+            ('command', 'g++'),
+            ('flags', '-Ofast -DNDEBUG -fPIC -march=native -fopenmp -std=c++11'),
+            ('restrict_qualifier', '__restrict__')
+        ])
+        if platform.machine().startswith('ppc64') or platform.machine() == 'arm64':
+            default_compiler_config['flags'] = default_compiler_config['flags'].replace('-march=native',
+                                                                                        '-mcpu=native')
+    elif platform.system().lower() == 'windows':
+        default_compiler_config = OrderedDict([
+            ('os', 'windows'),
+            ('msvc_version', 'latest'),
+            ('arch', 'x64'),
+            ('flags', '/Ox /fp:fast /OpenMP /arch:avx'),
+            ('restrict_qualifier', '__restrict')
+        ])
+        if platform.machine() == 'ARM64':
+            default_compiler_config['arch'] = 'ARM64'
+            default_compiler_config['flags'] = default_compiler_config['flags'].replace(' /arch:avx', '')
+    elif platform.system().lower() == 'darwin':
+        default_compiler_config = OrderedDict([
+            ('os', 'darwin'),
+            ('command', 'clang++'),
+            ('flags', '-Ofast -DNDEBUG -fPIC -march=native -Xclang -fopenmp -std=c++11'),
+            ('restrict_qualifier', '__restrict__')
+        ])
+        if platform.machine() == 'arm64':
+            default_compiler_config['flags'] = default_compiler_config['flags'].replace('-march=native ', '')
+        for libomp in ['/opt/local/lib/libomp/libomp.dylib', '/usr/local/lib/libomp.dylib',
+                       '/opt/homebrew/lib/libomp.dylib']:
+            if os.path.exists(libomp):
+                default_compiler_config['flags'] += ' ' + libomp
+                break
+    else:
+        raise NotImplementedError('Generation of default compiler flags for %s is not implemented' %
+                                  (platform.system(),))
+
+    default_cache_config = OrderedDict([
+        ('object_cache', os.path.join(user_cache_dir('pystencils'), 'objectcache')),
+        ('clear_cache_on_start', False),
+    ])
+
+    default_config = OrderedDict([('compiler', default_compiler_config),
+                                  ('cache', default_cache_config)])
+
+    config_path, config_exists = get_configuration_file_path()
+    config = default_config.copy()
+    if config_exists:
+        with open(config_path, 'r') as json_config_file:
+            loaded_config = json.load(json_config_file)
+        config = recursive_dict_update(config, loaded_config)
+    else:
+        create_folder(config_path, True)
+        with open(config_path, 'w') as f:
+            json.dump(config, f, indent=4)
+
+    if config['cache']['object_cache'] is not False:
+        config['cache']['object_cache'] = os.path.expanduser(config['cache']['object_cache']).format(pid=os.getpid())
+
+        clear_cache_on_start = False
+        cache_status_file = os.path.join(config['cache']['object_cache'], 'last_config.json')
+        if os.path.exists(cache_status_file):
+            # check if compiler config has changed
+            last_config = json.load(open(cache_status_file, 'r'))
+            if set(last_config.items()) != set(config['compiler'].items()):
+                clear_cache_on_start = True
+            else:
+                for key in last_config.keys():
+                    if last_config[key] != config['compiler'][key]:
+                        clear_cache_on_start = True
+
+        if config['cache']['clear_cache_on_start'] or clear_cache_on_start:
+            shutil.rmtree(config['cache']['object_cache'], ignore_errors=True)
+
+        create_folder(config['cache']['object_cache'], False)
+        with tempfile.NamedTemporaryFile('w', dir=os.path.dirname(cache_status_file), delete=False) as f:
+            json.dump(config['compiler'], f, indent=4)
+        os.replace(f.name, cache_status_file)
+
+    if config['compiler']['os'] == 'windows':
+        msvc_env = get_environment(config['compiler']['msvc_version'], config['compiler']['arch'])
+        if 'env' not in config['compiler']:
+            config['compiler']['env'] = {}
+        config['compiler']['env'].update(msvc_env)
+
+    return config
+
+
+_config = read_config()
+
+
+def get_compiler_config():
+    return _config['compiler']
+
+
+def get_cache_config():
+    return _config['cache']
+
+
+def add_or_change_compiler_flags(flags):
+    if not isinstance(flags, list) and not isinstance(flags, tuple):
+        flags = [flags]
+
+    compiler_config = get_compiler_config()
+    cache_config = get_cache_config()
+    cache_config['object_cache'] = False  # disable cache
+
+    for flag in flags:
+        flag = flag.strip()
+        if '=' in flag:
+            base = flag.split('=')[0].strip()
+        else:
+            base = flag
+
+        new_flags = [c for c in compiler_config['flags'].split() if not c.startswith(base)]
+        new_flags.append(flag)
+        compiler_config['flags'] = ' '.join(new_flags)
+
+
+def clear_cache():
+    cache_config = get_cache_config()
+    if cache_config['object_cache'] is not False:
+        shutil.rmtree(cache_config['object_cache'], ignore_errors=True)
+        create_folder(cache_config['object_cache'], False)
+
+
+def load_kernel_from_file(module_name, function_name, path):
+    try:
+        spec = importlib.util.spec_from_file_location(name=module_name, location=path)
+        mod = importlib.util.module_from_spec(spec)
+        spec.loader.exec_module(mod)
+    except ImportError:
+        warnings.warn(f"Could not load {path}, trying on more time in 5 seconds ...")
+        time.sleep(5)
+        spec = importlib.util.spec_from_file_location(name=module_name, location=path)
+        mod = importlib.util.module_from_spec(spec)
+        spec.loader.exec_module(mod)
+
+    return getattr(mod, function_name)
+
+
+def run_compile_step(command):
+    compiler_config = get_compiler_config()
+    config_env = compiler_config['env'] if 'env' in compiler_config else {}
+    compile_environment = os.environ.copy()
+    compile_environment.update(config_env)
+    try:
+        shell = True if compiler_config['os'].lower() == 'windows' else False
+        subprocess.check_output(command, env=compile_environment, stderr=subprocess.STDOUT, shell=shell)
+    except subprocess.CalledProcessError as e:
+        print(" ".join(command))
+        print(e.output.decode('utf8'))
+        raise e
+
+
+def compile_module(code, code_hash, base_dir, compile_flags=None):
+    if compile_flags is None:
+        compile_flags = []
+
+    compiler_config = get_compiler_config()
+    extra_flags = ['-I' + sysconfig.get_paths()['include'], '-I' + get_pystencils_include_path()] + compile_flags
+
+    if compiler_config['os'].lower() == 'windows':
+        lib_suffix = '.pyd'
+        object_suffix = '.obj'
+        windows = True
+    else:
+        lib_suffix = '.so'
+        object_suffix = '.o'
+        windows = False
+
+    src_file = os.path.join(base_dir, code_hash + ".cpp")
+    lib_file = os.path.join(base_dir, code_hash + lib_suffix)
+    object_file = os.path.join(base_dir, code_hash + object_suffix)
+
+    if not os.path.exists(object_file):
+        try:
+            with open(src_file, 'x') as f:
+                code.write_to_file(f)
+        except FileExistsError:
+            pass
+
+        if windows:
+            compile_cmd = ['cl.exe', '/c', '/EHsc'] + compiler_config['flags'].split()
+            compile_cmd += [*extra_flags, src_file, '/Fo' + object_file]
+            run_compile_step(compile_cmd)
+        else:
+            with atomic_file_write(object_file) as file_name:
+                compile_cmd = [compiler_config['command'], '-c'] + compiler_config['flags'].split()
+                compile_cmd += [*extra_flags, '-o', file_name, src_file]
+                run_compile_step(compile_cmd)
+
+        # Linking
+        if windows:
+            config_vars = sysconfig.get_config_vars()
+            py_lib = os.path.join(config_vars["installed_base"], "libs",
+                                  f"python{config_vars['py_version_nodot']}.lib")
+            run_compile_step(['link.exe', py_lib, '/DLL', '/out:' + lib_file, object_file])
+        elif platform.system().lower() == 'darwin':
+            with atomic_file_write(lib_file) as file_name:
+                run_compile_step([compiler_config['command'], '-shared', object_file, '-o', file_name, '-undefined',
+                                  'dynamic_lookup']
+                                 + compiler_config['flags'].split())
+        else:
+            with atomic_file_write(lib_file) as file_name:
+                run_compile_step([compiler_config['command'], '-shared', object_file, '-o', file_name]
+                                 + compiler_config['flags'].split())
+    return lib_file
+
+
+def compile_and_load(ast: PsKernelFunction, custom_backend=None):
+    cache_config = get_cache_config()
+
+    compiler_config = get_compiler_config()
+    function_prefix = '__declspec(dllexport)' if compiler_config['os'].lower() == 'windows' else ''
+
+    code = PsKernelExtensioNModule()
+
+    code.add_function(ast, ast.function_name)
+
+    code.create_code_string(compiler_config['restrict_qualifier'], function_prefix)
+    code_hash_str = code.get_hash_of_code()
+
+    compile_flags = []
+    if ast.instruction_set and 'compile_flags' in ast.instruction_set:
+        compile_flags = ast.instruction_set['compile_flags']
+
+    if cache_config['object_cache'] is False:
+        with tempfile.TemporaryDirectory() as base_dir:
+            lib_file = compile_module(code, code_hash_str, base_dir, compile_flags=compile_flags)
+            result = load_kernel_from_file(code_hash_str, ast.function_name, lib_file)
+    else:
+        lib_file = compile_module(code, code_hash_str, base_dir=cache_config['object_cache'],
+                                  compile_flags=compile_flags)
+        result = load_kernel_from_file(code_hash_str, ast.function_name, lib_file)
+
+    return KernelWrapper(result, ast.get_parameters(), ast)
diff --git a/src/pystencils/backend/jit/msvc_detection.py b/src/pystencils/backend/jit/msvc_detection.py
new file mode 100644
index 0000000000000000000000000000000000000000..9cc1fc5ad64a9e50d24788f6e4eea8135cbf7a2c
--- /dev/null
+++ b/src/pystencils/backend/jit/msvc_detection.py
@@ -0,0 +1,105 @@
+import os
+import subprocess
+
+
+def get_environment(version_specifier, arch='x64'):
+    """Returns an environment dictionary, for activating the Visual Studio compiler.
+
+    Args:
+        version_specifier: either a version number, year number, 'auto' or 'latest' for automatic detection of latest
+                          installed version or 'setuptools' for setuptools-based detection
+        arch: x86 or x64
+    """
+    if version_specifier == 'setuptools':
+        return get_environment_from_setup_tools(arch)
+    elif '\\' in version_specifier:
+        vc_vars_path = find_vc_vars_all_via_filesystem_search(version_specifier)
+        return get_environment_from_vc_vars_file(vc_vars_path, arch)
+    else:
+        try:
+            if version_specifier in ('auto', 'latest'):
+                version_nr = find_latest_msvc_version_using_environment_variables()
+            else:
+                version_nr = normalize_msvc_version(version_specifier)
+            vc_vars_path = get_vc_vars_path_via_environment_variable(version_nr)
+        except ValueError:
+            vc_vars_path = find_vc_vars_all_via_filesystem_search("C:\\Program Files (x86)\\Microsoft Visual Studio")
+            if vc_vars_path is None:
+                vc_vars_path = find_vc_vars_all_via_filesystem_search("C:\\Program Files\\Microsoft Visual Studio")
+            if vc_vars_path is None:
+                raise ValueError("Visual Studio not found. Write path to VS folder in pystencils config")
+
+        return get_environment_from_vc_vars_file(vc_vars_path, arch)
+
+
+def find_latest_msvc_version_using_environment_variables():
+    import re
+    # noinspection SpellCheckingInspection
+    regex = re.compile(r'VS(\d\d)\dCOMNTOOLS')
+    versions = []
+    for key, value in os.environ.items():
+        match = regex.match(key)
+        if match:
+            versions.append(int(match.group(1)))
+    if len(versions) == 0:
+        raise ValueError("Visual Studio not found.")
+    versions.sort()
+    return versions[-1]
+
+
+def normalize_msvc_version(version):
+    """
+    Takes version specifiers in the following form:
+        - year: 2012, 2013, 2015, either as int or string
+        - version numbers with or without dot i.e. 11.0 or 11
+    :return: integer version number
+    """
+    if isinstance(version, str) and '.' in version:
+        version = version.split('.')[0]
+
+    version = int(version)
+    mapping = {
+        2015: 14,
+        2013: 12,
+        2012: 11
+    }
+    if version in mapping:
+        return mapping[version]
+    else:
+        return version
+
+
+def get_environment_from_vc_vars_file(vc_vars_file, arch):
+    out = subprocess.check_output(
+        f'cmd /u /c "{vc_vars_file}" {arch} && set',
+        stderr=subprocess.STDOUT,
+    ).decode('utf-16le', errors='replace')
+
+    env = {key.upper(): value for key, _, value in (line.partition('=') for line in out.splitlines()) if key and value}
+    return env
+
+
+def get_vc_vars_path_via_environment_variable(version_nr):
+    # noinspection SpellCheckingInspection
+    environment_var_name = 'VS%d0COMNTOOLS' % (version_nr,)
+    vc_path = os.environ[environment_var_name]
+    path = os.path.join(vc_path, '..', '..', 'VC', 'vcvarsall.bat')
+    return os.path.abspath(path)
+
+
+def get_environment_from_setup_tools(arch):
+    from setuptools.msvc import msvc14_get_vc_env
+    msvc_env = msvc14_get_vc_env(arch)
+    return {k.upper(): v for k, v in msvc_env.items()}
+
+
+def find_vc_vars_all_via_filesystem_search(base_path):
+    matches = []
+    for root, dir_names, file_names in os.walk(base_path):
+        for filename in file_names:
+            if filename == 'vcvarsall.bat':
+                matches.append(os.path.join(root, filename))
+
+    matches.sort(reverse=True)
+    if matches:
+        return matches[0]
diff --git a/src/pystencils/backend/kernelcreation/__init__.py b/src/pystencils/backend/kernelcreation/__init__.py
index 6ac30e9eed597e7d3ae2d9421fea3156a4d5de33..c04cab827a7c28fc38a04bed7bffd749b334e79b 100644
--- a/src/pystencils/backend/kernelcreation/__init__.py
+++ b/src/pystencils/backend/kernelcreation/__init__.py
@@ -98,8 +98,6 @@ It is furthermore annotated with constraints collected during the translation, a
 
 """
 
-from .config import CreateKernelConfig
-
 from .context import KernelCreationContext
 from .analysis import KernelAnalysis
 from .freeze import FreezeExpressions
@@ -113,7 +111,6 @@ from .iteration_space import (
 )
 
 __all__ = [
-    "CreateKernelConfig",
     "KernelCreationContext",
     "KernelAnalysis",
     "FreezeExpressions",
diff --git a/src/pystencils/backend/kernelcreation/analysis.py b/src/pystencils/backend/kernelcreation/analysis.py
index b0267a3d6950d1218e4976ff109d62c065f2520a..c43452cc42c620363a4428ed065a6387577da347 100644
--- a/src/pystencils/backend/kernelcreation/analysis.py
+++ b/src/pystencils/backend/kernelcreation/analysis.py
@@ -207,4 +207,4 @@ class NestedScopes:
 
     @property
     def depth(self):
-        return len(self._defined)
\ No newline at end of file
+        return len(self._defined)
diff --git a/src/pystencils/datahandling/parallel_datahandling.py b/src/pystencils/datahandling/parallel_datahandling.py
index d9d91cd6c364469ae574ed9a3b5322e44977f6f1..c0ddc9a464f4d2ebded2e0530100c098ea6728da 100644
--- a/src/pystencils/datahandling/parallel_datahandling.py
+++ b/src/pystencils/datahandling/parallel_datahandling.py
@@ -9,7 +9,7 @@ from pystencils.datahandling.blockiteration import block_iteration, sliced_block
 from pystencils.datahandling.datahandling_interface import DataHandling
 from pystencils.enums import Backend
 from pystencils.field import Field, FieldType
-from pystencils.typing.typed_sympy import FieldPointerSymbol
+from pystencils.sympyextensions.typed_sympy import FieldPointerSymbol
 from pystencils.utils import DotDict
 from pystencils import Target
 
diff --git a/src/pystencils/kernelcreation.py b/src/pystencils/kernelcreation.py
index e12381068b101e36ff41dc01fcc63edd4ad0919b..954eb2e07aa35c7d0f7cd43af6aca52968f01def 100644
--- a/src/pystencils/kernelcreation.py
+++ b/src/pystencils/kernelcreation.py
@@ -112,9 +112,9 @@ class CreateKernelConfig:
         if self.jit is None:
             match self.target:
                 case Target.CPU:
-                    from .backend.jit import legacy_cpu
+                    from .backend.jit import LegacyCpuJit
 
-                    self.jit = legacy_cpu
+                    self.jit = LegacyCpuJit()
                 case _:
                     raise NotImplementedError(
                         f"No default JIT compiler implemented yet for target {self.target}"
diff --git a/src/pystencils/old/cpu/__init__.py b/src/pystencils/old/cpu/__init__.py
index ba0b57da21322f36f91cdd4cf6d2a64ad485ee41..244df5a8691836d8c1ed431826eeffbae70af977 100644
--- a/src/pystencils/old/cpu/__init__.py
+++ b/src/pystencils/old/cpu/__init__.py
@@ -1,4 +1,4 @@
-from pystencils.cpu.cpujit import make_python_function
-from pystencils.cpu.kernelcreation import add_openmp, create_indexed_kernel, create_kernel, add_pragmas
+from .cpujit import make_python_function
+from .kernelcreation import add_openmp, create_indexed_kernel, create_kernel, add_pragmas
 
 __all__ = ['create_kernel', 'create_indexed_kernel', 'add_openmp', 'add_pragmas', 'make_python_function']
diff --git a/src/pystencils/old/gpu/__init__.py b/src/pystencils/old/gpu/__init__.py
index c0d1fd34d7d8028469d68aaa694f88c3e607e9ee..0ee6f02aef6383d570f93d3841ede5359aa7c86c 100644
--- a/src/pystencils/old/gpu/__init__.py
+++ b/src/pystencils/old/gpu/__init__.py
@@ -1,6 +1,6 @@
-from pystencils.gpu.gpu_array_handler import GPUArrayHandler, GPUNotAvailableHandler
-from pystencils.gpu.gpujit import make_python_function
-from pystencils.gpu.kernelcreation import create_cuda_kernel, created_indexed_cuda_kernel
+from .gpu_array_handler import GPUArrayHandler, GPUNotAvailableHandler
+from .gpujit import make_python_function
+from .kernelcreation import create_cuda_kernel, created_indexed_cuda_kernel
 
 from .indexing import AbstractIndexing, BlockIndexing, LineIndexing
 
diff --git a/tests/nbackend/kernelcreation/platform/test_basic_cpu.py b/tests/nbackend/kernelcreation/platform/test_basic_cpu.py
index 540985a2a80fa1f5f5ddbd1f7dd0c2519f59da21..d42981f8ca2e95bee2bdff7d1baf8062bb1fbd75 100644
--- a/tests/nbackend/kernelcreation/platform/test_basic_cpu.py
+++ b/tests/nbackend/kernelcreation/platform/test_basic_cpu.py
@@ -2,19 +2,18 @@ import pytest
 
 from pystencils.field import Field
 
-from pystencils.nbackend.kernelcreation import (
+from pystencils.backend.kernelcreation import (
     KernelCreationContext,
-    CreateKernelConfig,
     FullIterationSpace
 )
 
-from pystencils.nbackend.ast import PsBlock, PsLoop, PsComment, dfs_preorder
+from pystencils.backend.ast import PsBlock, PsLoop, PsComment, dfs_preorder
 
-from pystencils.nbackend.kernelcreation.platform import BasicCpu
+from pystencils.backend.platforms import BasicCpu
 
 @pytest.mark.parametrize("layout", ["fzyx", "zyxf", "c", "f"])
 def test_loop_nest(layout):
-    ctx = KernelCreationContext(CreateKernelConfig())
+    ctx = KernelCreationContext()
 
     body = PsBlock([PsComment("Loop body goes here")])
     platform = BasicCpu(ctx)
diff --git a/tests/nbackend/kernelcreation/test_domain_kernels.py b/tests/nbackend/kernelcreation/test_domain_kernels.py
index 4221bbad18849b1148139aa350d9db999f511f9d..9ce2f661d840641d28774134070fc7050e90e6d1 100644
--- a/tests/nbackend/kernelcreation/test_domain_kernels.py
+++ b/tests/nbackend/kernelcreation/test_domain_kernels.py
@@ -2,9 +2,9 @@ import sympy as sp
 import numpy as np
 
 from pystencils import fields, Field, AssignmentCollection
-from pystencils.sympyextensions.assignmentcollection.assignment import assignment_from_stencil
+from pystencils.sympyextensions.astnodes import assignment_from_stencil
 
-from pystencils.nbackend.kernelcreation import create_kernel
+from pystencils.kernelcreation import create_kernel
 
 
 def test_filter_kernel():
diff --git a/tests/nbackend/kernelcreation/test_freeze.py b/tests/nbackend/kernelcreation/test_freeze.py
index ca3a470a5fce49a1d45171c50ee62e88d263e051..acecc6503add4a2f858b696886a30a8dbe7fdd98 100644
--- a/tests/nbackend/kernelcreation/test_freeze.py
+++ b/tests/nbackend/kernelcreation/test_freeze.py
@@ -3,17 +3,16 @@ import pymbolic.primitives as pb
 
 from pystencils import Assignment, fields
 
-from pystencils.nbackend.ast import (
+from pystencils.backend.ast import (
     PsAssignment,
     PsDeclaration,
     PsExpression,
     PsSymbolExpr,
     PsLvalueExpr,
 )
-from pystencils.nbackend.typed_expressions import PsTypedConstant, PsTypedVariable
-from pystencils.nbackend.arrays import PsArrayAccess
-from pystencils.nbackend.kernelcreation import (
-    CreateKernelConfig,
+from pystencils.backend.typed_expressions import PsTypedConstant, PsTypedVariable
+from pystencils.backend.arrays import PsArrayAccess
+from pystencils.backend.kernelcreation import (
     KernelCreationContext,
     FreezeExpressions,
     FullIterationSpace,
@@ -21,8 +20,7 @@ from pystencils.nbackend.kernelcreation import (
 
 
 def test_freeze_simple():
-    options = CreateKernelConfig()
-    ctx = KernelCreationContext(options)
+    ctx = KernelCreationContext()
     freeze = FreezeExpressions(ctx)
 
     x, y, z = sp.symbols("x, y, z")
@@ -37,8 +35,7 @@ def test_freeze_simple():
 
 
 def test_freeze_fields():
-    options = CreateKernelConfig()
-    ctx = KernelCreationContext(options)
+    ctx = KernelCreationContext()
 
     zero = PsTypedConstant(0, ctx.index_dtype)
     forty_two = PsTypedConstant(42, ctx.index_dtype)
diff --git a/tests/nbackend/kernelcreation/test_iteration_space.py b/tests/nbackend/kernelcreation/test_iteration_space.py
index e785b64a27d84dc9e2699b9e3c60fd5593a81455..6fe905f73bcbd8ebd65bf0013240325aeb109b04 100644
--- a/tests/nbackend/kernelcreation/test_iteration_space.py
+++ b/tests/nbackend/kernelcreation/test_iteration_space.py
@@ -1,16 +1,15 @@
 from pystencils.field import Field
 
-from pystencils.nbackend.kernelcreation import (
+from pystencils.backend.kernelcreation import (
     KernelCreationContext,
-    CreateKernelConfig,
     FullIterationSpace
 )
 
-from pystencils.nbackend.kernelcreation.defaults import Pymbolic as PbDefaults
+from pystencils.backend.kernelcreation.defaults import Pymbolic as PbDefaults
 
 
 def test_loop_order():
-    ctx = KernelCreationContext(CreateKernelConfig())
+    ctx = KernelCreationContext()
     ctr_symbols = PbDefaults.spatial_counters
 
     #   FZYX Order
diff --git a/tests/nbackend/kernelcreation/test_options.py b/tests/nbackend/kernelcreation/test_options.py
index 77b13da0c94416884adaff4c11ae53aa18fcfb8c..726ee8def24ee3735a6a95f62ab364414024c1a6 100644
--- a/tests/nbackend/kernelcreation/test_options.py
+++ b/tests/nbackend/kernelcreation/test_options.py
@@ -1,8 +1,8 @@
 import pytest
 
 from pystencils.field import Field, FieldType
-from pystencils.nbackend.types.quick import *
-from pystencils.nbackend.kernelcreation.config import (
+from pystencils.backend.types.quick import *
+from pystencils.kernelcreation import (
     CreateKernelConfig,
     PsOptionsError,
 )
diff --git a/tests/nbackend/kernelcreation/test_typification.py b/tests/nbackend/kernelcreation/test_typification.py
index 26d702b2306c11aa370420c1e96e93085922373b..6e24a876f221a2880a8dd71958973d75e1b59a58 100644
--- a/tests/nbackend/kernelcreation/test_typification.py
+++ b/tests/nbackend/kernelcreation/test_typification.py
@@ -5,19 +5,17 @@ import pymbolic.primitives as pb
 
 from pystencils import Assignment, TypedSymbol, Field, FieldType
 
-from pystencils.nbackend.ast import PsDeclaration
-from pystencils.nbackend.types import constify
-from pystencils.nbackend.types.quick import *
-from pystencils.nbackend.typed_expressions import PsTypedConstant, PsTypedVariable
-from pystencils.nbackend.kernelcreation.config import CreateKernelConfig
-from pystencils.nbackend.kernelcreation.context import KernelCreationContext
-from pystencils.nbackend.kernelcreation.freeze import FreezeExpressions
-from pystencils.nbackend.kernelcreation.typification import Typifier, TypificationError
+from pystencils.backend.ast import PsDeclaration
+from pystencils.backend.types import constify
+from pystencils.backend.types.quick import *
+from pystencils.backend.typed_expressions import PsTypedConstant, PsTypedVariable
+from pystencils.backend.kernelcreation.context import KernelCreationContext
+from pystencils.backend.kernelcreation.freeze import FreezeExpressions
+from pystencils.backend.kernelcreation.typification import Typifier, TypificationError
 
 
 def test_typify_simple():
-    options = CreateKernelConfig()
-    ctx = KernelCreationContext(options)
+    ctx = KernelCreationContext()
     freeze = FreezeExpressions(ctx)
     typify = Typifier(ctx)
 
@@ -33,10 +31,10 @@ def test_typify_simple():
         match expr:
             case PsTypedConstant(value, dtype):
                 assert value == 2
-                assert dtype == constify(ctx.options.default_dtype)
+                assert dtype == constify(ctx.default_dtype)
             case PsTypedVariable(name, dtype):
                 assert name in "xyz"
-                assert dtype == ctx.options.default_dtype
+                assert dtype == ctx.default_dtype
             case pb.Sum(cs) | pb.Product(cs):
                 [check(c) for c in cs]
             case _:
@@ -47,8 +45,7 @@ def test_typify_simple():
 
 
 def test_typify_structs():
-    options = CreateKernelConfig(default_dtype=Fp(32))
-    ctx = KernelCreationContext(options)
+    ctx = KernelCreationContext(default_dtype=Fp(32))
     freeze = FreezeExpressions(ctx)
     typify = Typifier(ctx)
 
@@ -69,8 +66,7 @@ def test_typify_structs():
 
 
 def test_contextual_typing():
-    options = CreateKernelConfig()
-    ctx = KernelCreationContext(options)
+    ctx = KernelCreationContext()
     freeze = FreezeExpressions(ctx)
     typify = Typifier(ctx)
 
@@ -82,10 +78,10 @@ def test_contextual_typing():
         match expr:
             case PsTypedConstant(value, dtype):
                 assert value in (2, 3, -4)
-                assert dtype == constify(ctx.options.default_dtype)
+                assert dtype == constify(ctx.default_dtype)
             case PsTypedVariable(name, dtype):
                 assert name in "xyz"
-                assert dtype == ctx.options.default_dtype
+                assert dtype == ctx.default_dtype
             case pb.Sum(cs) | pb.Product(cs):
                 [check(c) for c in cs]
             case _:
@@ -95,8 +91,7 @@ def test_contextual_typing():
 
 
 def test_erronous_typing():
-    options = CreateKernelConfig(default_dtype=make_numeric_type(np.float64))
-    ctx = KernelCreationContext(options)
+    ctx = KernelCreationContext(default_dtype=make_numeric_type(np.float64))
     freeze = FreezeExpressions(ctx)
     typify = Typifier(ctx)
 
diff --git a/tests/nbackend/test_constant_folding.py b/tests/nbackend/test_constant_folding.py
index c297534b4ea1cc2e545d2f631f7857614f5bfddb..686a4346f1935a7c7b1dd795f6f672352d138df6 100644
--- a/tests/nbackend/test_constant_folding.py
+++ b/tests/nbackend/test_constant_folding.py
@@ -3,8 +3,8 @@ import pytest
 import pymbolic.primitives as pb
 from pymbolic.mapper.constant_folder import ConstantFoldingMapper
 
-from pystencils.nbackend.types.quick import *
-from pystencils.nbackend.typed_expressions import PsTypedConstant
+from pystencils.backend.types.quick import *
+from pystencils.backend.typed_expressions import PsTypedConstant
 
 
 @pytest.mark.parametrize("width", (8, 16, 32, 64))
diff --git a/tests/nbackend/test_cpujit.py b/tests/nbackend/test_cpujit.py
index b93f7a1e22c94b9cb6fa6443be4690581c66193c..865b698c4fd8df1f7a073ae923930fb90f469802 100644
--- a/tests/nbackend/test_cpujit.py
+++ b/tests/nbackend/test_cpujit.py
@@ -2,16 +2,15 @@ import pytest
 
 from pystencils import Target
 
-from pystencils.nbackend.ast import *
-from pystencils.nbackend.constraints import PsKernelConstraint
-from pystencils.nbackend.typed_expressions import *
-from pystencils.nbackend.arrays import PsLinearizedArray, PsArrayBasePointer, PsArrayAccess
-from pystencils.nbackend.types.quick import *
+from pystencils.backend.ast import *
+from pystencils.backend.constraints import PsKernelConstraint
+from pystencils.backend.typed_expressions import *
+from pystencils.backend.arrays import PsLinearizedArray, PsArrayBasePointer, PsArrayAccess
+from pystencils.backend.types.quick import *
+from pystencils.backend.jit import LegacyCpuJit
 
 import numpy as np
 
-from pystencils.cpu.cpujit import compile_and_load
-
 def test_pairwise_addition():
     idx_type = SInt(64)
 
@@ -49,7 +48,8 @@ def test_pairwise_addition():
 
     func.add_constraints(sizes_constraint)
 
-    kernel = compile_and_load(func)
+    jit = LegacyCpuJit()
+    kernel = jit.compile(func)
 
     #   Positive case
     N = 21
diff --git a/tests/nbackend/test_expressions.py b/tests/nbackend/test_expressions.py
index 6c24a6442bd5899b4221b795be2c65cf89982331..65a0a4e1ecc9573fa7df26f7251fe9aa300b6953 100644
--- a/tests/nbackend/test_expressions.py
+++ b/tests/nbackend/test_expressions.py
@@ -1,7 +1,6 @@
-from pystencils.nbackend.typed_expressions import PsTypedVariable
-from pystencils.nbackend.arrays import PsLinearizedArray, PsArrayBasePointer, PsArrayShapeVar, PsArrayStrideVar
-
-from pystencils.nbackend.types.quick import *
+from pystencils.backend.typed_expressions import PsTypedVariable
+from pystencils.backend.arrays import PsLinearizedArray, PsArrayBasePointer, PsArrayShapeVar, PsArrayStrideVar
+from pystencils.backend.types.quick import *
 
 def test_variable_equality():
     var1 = PsTypedVariable("x", Fp(32))
diff --git a/tests/nbackend/types/test_constants.py b/tests/nbackend/types/test_constants.py
index 4eaf4060a4182a78232660fc2285caaa8047ff3c..a5399c66e2a08bf10cd1829bd8a0707baa0f0bcd 100644
--- a/tests/nbackend/types/test_constants.py
+++ b/tests/nbackend/types/test_constants.py
@@ -1,8 +1,8 @@
 import pytest
 
-from pystencils.nbackend.types.quick import *
-from pystencils.nbackend.types import PsTypeError
-from pystencils.nbackend.typed_expressions import PsTypedConstant
+from pystencils.backend.types.quick import *
+from pystencils.backend.types import PsTypeError
+from pystencils.backend.typed_expressions import PsTypedConstant
 
 
 @pytest.mark.parametrize("width", (8, 16, 32, 64))
diff --git a/tests/nbackend/types/test_types.py b/tests/nbackend/types/test_types.py
index 06ec7db163b4c096591da4bdb6d67bcc27513e8b..9726fac47cfb38b61903e518b04c86ea2b69bc80 100644
--- a/tests/nbackend/types/test_types.py
+++ b/tests/nbackend/types/test_types.py
@@ -1,9 +1,9 @@
 import pytest
 import numpy as np
 
-from pystencils.nbackend.exceptions import PsInternalCompilerError
-from pystencils.nbackend.types import *
-from pystencils.nbackend.types.quick import *
+from pystencils.backend.exceptions import PsInternalCompilerError
+from pystencils.backend.types import *
+from pystencils.backend.types.quick import *
 
 
 @pytest.mark.parametrize(